Skip to content

Commit ef043b0

Browse files
MaskRaykbluck
authored andcommitted
[MC,llvm-readobj,yaml2obj] Support CREL relocation format
CREL is a compact relocation format for the ELF object file format. This patch adds integrated assembler support (using the RELA form) available with `llvm-mc -filetype=obj -crel a.s -o a.o`. A dependent patch will add `clang -c -Wa,--crel,--allow-experimental-crel`. Also add llvm-readobj support (for both REL and RELA forms) to facilitate testing the assembler. Additionally, yaml2obj gains support for the RELA form to aid testing with llvm-readobj. We temporarily assign the section type code 0x40000020 from the generic range to `SHT_CREL`. We avoided using `SHT_LLVM_` or `SHT_GNU_` to avoid code churn and maintain broader applicability for interested psABIs. Similarly, `DT_CREL` is temporarily 0x40000026. LLVM will change the code and break compatibility. This is not an issue if all relocatable files using CREL are regenerated (aka no prebuilt relocatable files). Link: https://discourse.llvm.org/t/rfc-crel-a-compact-relocation-format-for-elf/77600 Pull Request: llvm#91280
1 parent 1ef9182 commit ef043b0

24 files changed

+876
-60
lines changed

llvm/include/llvm/BinaryFormat/DynamicTags.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,10 @@ DYNAMIC_TAG(RELRSZ, 35) // Size of Relr relocation table.
8686
DYNAMIC_TAG(RELR, 36) // Address of relocation table (Relr entries).
8787
DYNAMIC_TAG(RELRENT, 37) // Size of a Relr relocation entry.
8888

89+
// TODO: Experimental CREL relocations. LLVM will change the value and
90+
// break compatibility in the future.
91+
DYNAMIC_TAG(CREL, 0x40000026) // CREL relocation table
92+
8993
DYNAMIC_TAG_MARKER(LOOS, 0x60000000) // Start of environment specific tags.
9094
DYNAMIC_TAG_MARKER(HIOS, 0x6FFFFFFF) // End of environment specific tags.
9195
DYNAMIC_TAG_MARKER(LOPROC, 0x70000000) // Start of processor specific tags.

llvm/include/llvm/BinaryFormat/ELF.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1082,7 +1082,10 @@ enum : unsigned {
10821082
SHT_SYMTAB_SHNDX = 18, // Indices for SHN_XINDEX entries.
10831083
// Experimental support for SHT_RELR sections. For details, see proposal
10841084
// at https://groups.google.com/forum/#!topic/generic-abi/bX460iggiKg
1085-
SHT_RELR = 19, // Relocation entries; only offsets.
1085+
SHT_RELR = 19, // Relocation entries; only offsets.
1086+
// TODO: Experimental CREL relocations. LLVM will change the value and
1087+
// break compatibility in the future.
1088+
SHT_CREL = 0x40000014,
10861089
SHT_LOOS = 0x60000000, // Lowest operating system-specific type.
10871090
// Android packed relocation section types.
10881091
// https://android.googlesource.com/platform/bionic/+/6f12bfece5dcc01325e0abba56a46b1bcf991c69/tools/relocation_packer/src/elf_file.cc#37
@@ -1937,6 +1940,8 @@ enum {
19371940
ELFCOMPRESS_HIPROC = 0x7fffffff // End of processor-specific.
19381941
};
19391942

1943+
constexpr unsigned CREL_HDR_ADDEND = 4;
1944+
19401945
/// Convert an architecture name into ELF's e_machine value.
19411946
uint16_t convertArchNameToEMachine(StringRef Arch);
19421947

llvm/include/llvm/MC/MCTargetOptions.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,9 @@ class MCTargetOptions {
6161

6262
bool Dwarf64 : 1;
6363

64+
// Use CREL relocation format for ELF.
65+
bool Crel = false;
66+
6467
// If true, prefer R_X86_64_[REX_]GOTPCRELX to R_X86_64_GOTPCREL on x86-64
6568
// ELF.
6669
bool X86RelaxRelocations = true;

llvm/include/llvm/MC/MCTargetOptionsCommandFlags.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ bool getNoTypeCheck();
5151

5252
bool getSaveTempLabels();
5353

54+
bool getCrel();
55+
5456
bool getX86RelaxRelocations();
5557

5658
std::string getABIName();

llvm/include/llvm/Object/ELF.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,11 @@ class ELFFile {
321321

322322
std::vector<Elf_Rel> decode_relrs(Elf_Relr_Range relrs) const;
323323

324+
Expected<uint64_t> getCrelHeader(ArrayRef<uint8_t> Content) const;
325+
using RelsOrRelas = std::pair<std::vector<Elf_Rel>, std::vector<Elf_Rela>>;
326+
Expected<RelsOrRelas> decodeCrel(ArrayRef<uint8_t> Content) const;
327+
Expected<RelsOrRelas> crels(const Elf_Shdr &Sec) const;
328+
324329
Expected<std::vector<Elf_Rela>> android_relas(const Elf_Shdr &Sec) const;
325330

326331
/// Iterate over program header table.

llvm/include/llvm/Object/ELFTypes.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ template <class ELFT> struct Elf_Sym_Impl;
3232
template <class ELFT> struct Elf_Dyn_Impl;
3333
template <class ELFT> struct Elf_Phdr_Impl;
3434
template <class ELFT, bool isRela> struct Elf_Rel_Impl;
35+
template <bool Is64> struct Elf_Crel_Impl;
3536
template <class ELFT> struct Elf_Verdef_Impl;
3637
template <class ELFT> struct Elf_Verdaux_Impl;
3738
template <class ELFT> struct Elf_Verneed_Impl;
@@ -62,6 +63,7 @@ template <endianness E, bool Is64> struct ELFType {
6263
using Phdr = Elf_Phdr_Impl<ELFType<E, Is64>>;
6364
using Rel = Elf_Rel_Impl<ELFType<E, Is64>, false>;
6465
using Rela = Elf_Rel_Impl<ELFType<E, Is64>, true>;
66+
using Crel = Elf_Crel_Impl<Is64>;
6567
using Relr = packed<uint>;
6668
using Verdef = Elf_Verdef_Impl<ELFType<E, Is64>>;
6769
using Verdaux = Elf_Verdaux_Impl<ELFType<E, Is64>>;
@@ -117,6 +119,7 @@ using ELF64BE = ELFType<llvm::endianness::big, true>;
117119
using Elf_Phdr = typename ELFT::Phdr; \
118120
using Elf_Rel = typename ELFT::Rel; \
119121
using Elf_Rela = typename ELFT::Rela; \
122+
using Elf_Crel = typename ELFT::Crel; \
120123
using Elf_Relr = typename ELFT::Relr; \
121124
using Elf_Verdef = typename ELFT::Verdef; \
122125
using Elf_Verdaux = typename ELFT::Verdaux; \
@@ -385,6 +388,7 @@ template <endianness Endianness>
385388
struct Elf_Rel_Impl<ELFType<Endianness, false>, false> {
386389
LLVM_ELF_IMPORT_TYPES(Endianness, false)
387390
static const bool HasAddend = false;
391+
static const bool IsCrel = false;
388392
Elf_Addr r_offset; // Location (file byte offset, or program virtual addr)
389393
Elf_Word r_info; // Symbol table index and type of relocation to apply
390394

@@ -421,13 +425,15 @@ struct Elf_Rel_Impl<ELFType<Endianness, false>, true>
421425
: public Elf_Rel_Impl<ELFType<Endianness, false>, false> {
422426
LLVM_ELF_IMPORT_TYPES(Endianness, false)
423427
static const bool HasAddend = true;
428+
static const bool IsCrel = false;
424429
Elf_Sword r_addend; // Compute value for relocatable field by adding this
425430
};
426431

427432
template <endianness Endianness>
428433
struct Elf_Rel_Impl<ELFType<Endianness, true>, false> {
429434
LLVM_ELF_IMPORT_TYPES(Endianness, true)
430435
static const bool HasAddend = false;
436+
static const bool IsCrel = false;
431437
Elf_Addr r_offset; // Location (file byte offset, or program virtual addr)
432438
Elf_Xword r_info; // Symbol table index and type of relocation to apply
433439

@@ -474,9 +480,29 @@ struct Elf_Rel_Impl<ELFType<Endianness, true>, true>
474480
: public Elf_Rel_Impl<ELFType<Endianness, true>, false> {
475481
LLVM_ELF_IMPORT_TYPES(Endianness, true)
476482
static const bool HasAddend = true;
483+
static const bool IsCrel = false;
477484
Elf_Sxword r_addend; // Compute value for relocatable field by adding this.
478485
};
479486

487+
// In-memory representation. The serialized representation uses LEB128.
488+
template <bool Is64> struct Elf_Crel_Impl {
489+
using uint = std::conditional_t<Is64, uint64_t, uint32_t>;
490+
static const bool IsRela = true;
491+
static const bool IsCrel = true;
492+
uint r_offset;
493+
uint32_t r_symidx;
494+
uint32_t r_type;
495+
std::conditional_t<Is64, int64_t, int32_t> r_addend;
496+
497+
// Dummy bool parameter is for compatibility with Elf_Rel_Impl.
498+
uint32_t getType(bool) const { return r_type; }
499+
uint32_t getSymbol(bool) const { return r_symidx; }
500+
void setSymbolAndType(uint32_t s, unsigned char t, bool) {
501+
r_symidx = s;
502+
r_type = t;
503+
}
504+
};
505+
480506
template <class ELFT>
481507
struct Elf_Ehdr_Impl {
482508
LLVM_ELF_IMPORT_TYPES_ELFT(ELFT)

llvm/lib/MC/ELFObjectWriter.cpp

Lines changed: 88 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
#include "llvm/MC/StringTableBuilder.h"
4040
#include "llvm/Support/Alignment.h"
4141
#include "llvm/Support/Casting.h"
42+
#include "llvm/Support/CommandLine.h"
4243
#include "llvm/Support/Compression.h"
4344
#include "llvm/Support/Endian.h"
4445
#include "llvm/Support/EndianStream.h"
@@ -256,7 +257,7 @@ class ELFObjectWriter : public MCObjectWriter {
256257
void recordRelocation(MCAssembler &Asm, const MCFragment *Fragment,
257258
const MCFixup &Fixup, MCValue Target,
258259
uint64_t &FixedValue) override;
259-
bool usesRela(const MCSectionELF &Sec) const;
260+
bool usesRela(const MCTargetOptions *TO, const MCSectionELF &Sec) const;
260261

261262
void executePostLayoutBinding(MCAssembler &Asm,
262263
const MCAsmLayout &Layout) override;
@@ -810,7 +811,15 @@ MCSectionELF *ELFWriter::createRelocationSection(MCContext &Ctx,
810811
Flags = ELF::SHF_GROUP;
811812

812813
const StringRef SectionName = Sec.getName();
813-
const bool Rela = OWriter.usesRela(Sec);
814+
const MCTargetOptions *TO = Ctx.getTargetOptions();
815+
if (TO && TO->Crel) {
816+
MCSectionELF *RelaSection =
817+
Ctx.createELFRelSection(".crel" + SectionName, ELF::SHT_CREL, Flags,
818+
/*EntrySize=*/1, Sec.getGroup(), &Sec);
819+
return RelaSection;
820+
}
821+
822+
const bool Rela = OWriter.usesRela(TO, Sec);
814823
unsigned EntrySize;
815824
if (Rela)
816825
EntrySize = is64Bit() ? sizeof(ELF::Elf64_Rela) : sizeof(ELF::Elf32_Rela);
@@ -912,20 +921,61 @@ void ELFWriter::WriteSecHdrEntry(uint32_t Name, uint32_t Type, uint64_t Flags,
912921
WriteWord(EntrySize); // sh_entsize
913922
}
914923

924+
template <class uint>
925+
static void encodeCrel(ArrayRef<ELFRelocationEntry> Relocs, raw_ostream &OS) {
926+
uint OffsetMask = 8, Offset = 0, Addend = 0;
927+
uint32_t SymIdx = 0, Type = 0;
928+
// hdr & 4 indicates 3 flag bits in delta offset and flags members.
929+
for (const ELFRelocationEntry &Entry : Relocs)
930+
OffsetMask |= Entry.Offset;
931+
const int Shift = llvm::countr_zero(OffsetMask);
932+
encodeULEB128(Relocs.size() * 8 + ELF::CREL_HDR_ADDEND + Shift, OS);
933+
for (const ELFRelocationEntry &Entry : Relocs) {
934+
// The delta offset and flags member may be larger than uint64_t. Special
935+
// case the first byte (3 flag bits and 4 offset bits). Other ULEB128 bytes
936+
// encode the remaining delta offset bits.
937+
auto DeltaOffset = static_cast<uint>((Entry.Offset - Offset) >> Shift);
938+
Offset = Entry.Offset;
939+
uint32_t CurSymIdx = Entry.Symbol ? Entry.Symbol->getIndex() : 0;
940+
uint8_t B = (DeltaOffset << 3) + (SymIdx != CurSymIdx) +
941+
(Type != Entry.Type ? 2 : 0) + (Addend != Entry.Addend ? 4 : 0);
942+
if (DeltaOffset < 0x10) {
943+
OS << char(B);
944+
} else {
945+
OS << char(B | 0x80);
946+
encodeULEB128(DeltaOffset >> 4, OS);
947+
}
948+
// Delta symidx/type/addend members (SLEB128).
949+
if (B & 1) {
950+
encodeSLEB128(static_cast<int32_t>(CurSymIdx - SymIdx), OS);
951+
SymIdx = CurSymIdx;
952+
}
953+
if (B & 2) {
954+
encodeSLEB128(static_cast<int32_t>(Entry.Type - Type), OS);
955+
Type = Entry.Type;
956+
}
957+
if (B & 4) {
958+
encodeSLEB128(std::make_signed_t<uint>(Entry.Addend - Addend), OS);
959+
Addend = Entry.Addend;
960+
}
961+
}
962+
}
963+
915964
void ELFWriter::writeRelocations(const MCAssembler &Asm,
916965
const MCSectionELF &Sec) {
917966
std::vector<ELFRelocationEntry> &Relocs = OWriter.Relocations[&Sec];
918-
const bool Rela = OWriter.usesRela(Sec);
967+
const MCTargetOptions *TO = Asm.getContext().getTargetOptions();
968+
const bool Rela = OWriter.usesRela(TO, Sec);
919969

920970
// Sort the relocation entries. MIPS needs this.
921971
OWriter.TargetObjectWriter->sortRelocs(Asm, Relocs);
922972

923973
if (OWriter.TargetObjectWriter->getEMachine() == ELF::EM_MIPS) {
924974
for (const ELFRelocationEntry &Entry : Relocs) {
925-
uint32_t Symidx = Entry.Symbol ? Entry.Symbol->getIndex() : 0;
975+
uint32_t SymIdx = Entry.Symbol ? Entry.Symbol->getIndex() : 0;
926976
if (is64Bit()) {
927977
write(Entry.Offset);
928-
write(uint32_t(Symidx));
978+
write(uint32_t(SymIdx));
929979
write(OWriter.TargetObjectWriter->getRSsym(Entry.Type));
930980
write(OWriter.TargetObjectWriter->getRType3(Entry.Type));
931981
write(OWriter.TargetObjectWriter->getRType2(Entry.Type));
@@ -935,7 +985,7 @@ void ELFWriter::writeRelocations(const MCAssembler &Asm,
935985
} else {
936986
write(uint32_t(Entry.Offset));
937987
ELF::Elf32_Rela ERE32;
938-
ERE32.setSymbolAndType(Symidx, Entry.Type);
988+
ERE32.setSymbolAndType(SymIdx, Entry.Type);
939989
write(ERE32.r_info);
940990
if (Rela)
941991
write(uint32_t(Entry.Addend));
@@ -955,24 +1005,29 @@ void ELFWriter::writeRelocations(const MCAssembler &Asm,
9551005
}
9561006
}
9571007
}
958-
return;
959-
}
960-
for (const ELFRelocationEntry &Entry : Relocs) {
961-
uint32_t Symidx = Entry.Symbol ? Entry.Symbol->getIndex() : 0;
962-
if (is64Bit()) {
963-
write(Entry.Offset);
964-
ELF::Elf64_Rela ERE;
965-
ERE.setSymbolAndType(Symidx, Entry.Type);
966-
write(ERE.r_info);
967-
if (Rela)
968-
write(Entry.Addend);
969-
} else {
970-
write(uint32_t(Entry.Offset));
971-
ELF::Elf32_Rela ERE;
972-
ERE.setSymbolAndType(Symidx, Entry.Type);
973-
write(ERE.r_info);
974-
if (Rela)
975-
write(uint32_t(Entry.Addend));
1008+
} else if (TO && TO->Crel) {
1009+
if (is64Bit())
1010+
encodeCrel<uint64_t>(Relocs, W.OS);
1011+
else
1012+
encodeCrel<uint32_t>(Relocs, W.OS);
1013+
} else {
1014+
for (const ELFRelocationEntry &Entry : Relocs) {
1015+
uint32_t Symidx = Entry.Symbol ? Entry.Symbol->getIndex() : 0;
1016+
if (is64Bit()) {
1017+
write(Entry.Offset);
1018+
ELF::Elf64_Rela ERE;
1019+
ERE.setSymbolAndType(Symidx, Entry.Type);
1020+
write(ERE.r_info);
1021+
if (Rela)
1022+
write(Entry.Addend);
1023+
} else {
1024+
write(uint32_t(Entry.Offset));
1025+
ELF::Elf32_Rela ERE;
1026+
ERE.setSymbolAndType(Symidx, Entry.Type);
1027+
write(ERE.r_info);
1028+
if (Rela)
1029+
write(uint32_t(Entry.Addend));
1030+
}
9761031
}
9771032
}
9781033
}
@@ -992,7 +1047,8 @@ void ELFWriter::writeSection(const SectionIndexMapTy &SectionIndexMap,
9921047
llvm_unreachable("SHT_DYNAMIC in a relocatable object");
9931048

9941049
case ELF::SHT_REL:
995-
case ELF::SHT_RELA: {
1050+
case ELF::SHT_RELA:
1051+
case ELF::SHT_CREL: {
9961052
sh_link = SymbolTableIndex;
9971053
assert(sh_link && ".symtab not found");
9981054
const MCSection *InfoSection = Section.getLinkedToSection();
@@ -1417,6 +1473,7 @@ void ELFObjectWriter::recordRelocation(MCAssembler &Asm,
14171473
uint64_t C = Target.getConstant();
14181474
uint64_t FixupOffset = Asm.getFragmentOffset(*Fragment) + Fixup.getOffset();
14191475
MCContext &Ctx = Asm.getContext();
1476+
const MCTargetOptions *TO = Ctx.getTargetOptions();
14201477

14211478
if (const MCSymbolRefExpr *RefB = Target.getSymB()) {
14221479
const auto &SymB = cast<MCSymbolELF>(RefB->getSymbol());
@@ -1472,7 +1529,7 @@ void ELFObjectWriter::recordRelocation(MCAssembler &Asm,
14721529
FixedValue = !RelocateWithSymbol && SymA && !SymA->isUndefined()
14731530
? C + Asm.getSymbolOffset(*SymA)
14741531
: C;
1475-
if (usesRela(FixupSection)) {
1532+
if (usesRela(TO, FixupSection)) {
14761533
Addend = FixedValue;
14771534
FixedValue = 0;
14781535
}
@@ -1501,9 +1558,11 @@ void ELFObjectWriter::recordRelocation(MCAssembler &Asm,
15011558
Relocations[&FixupSection].push_back(Rec);
15021559
}
15031560

1504-
bool ELFObjectWriter::usesRela(const MCSectionELF &Sec) const {
1505-
return hasRelocationAddend() &&
1506-
Sec.getType() != ELF::SHT_LLVM_CALL_GRAPH_PROFILE;
1561+
bool ELFObjectWriter::usesRela(const MCTargetOptions *TO,
1562+
const MCSectionELF &Sec) const {
1563+
return (hasRelocationAddend() &&
1564+
Sec.getType() != ELF::SHT_LLVM_CALL_GRAPH_PROFILE) ||
1565+
(TO && TO->Crel);
15071566
}
15081567

15091568
bool ELFObjectWriter::isSymbolRefDifferenceFullyResolvedImpl(

llvm/lib/MC/MCTargetOptionsCommandFlags.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ MCOPT(bool, NoWarn)
4747
MCOPT(bool, NoDeprecatedWarn)
4848
MCOPT(bool, NoTypeCheck)
4949
MCOPT(bool, SaveTempLabels)
50+
MCOPT(bool, Crel)
5051
MCOPT(bool, X86RelaxRelocations)
5152
MCOPT(std::string, ABIName)
5253
MCOPT(std::string, AsSecureLogFile)
@@ -128,6 +129,10 @@ llvm::mc::RegisterMCTargetOptionsFlags::RegisterMCTargetOptionsFlags() {
128129
"save-temp-labels", cl::desc("Don't discard temporary labels"));
129130
MCBINDOPT(SaveTempLabels);
130131

132+
static cl::opt<bool> Crel("crel",
133+
cl::desc("Use CREL relocation format for ELF"));
134+
MCBINDOPT(Crel);
135+
131136
static cl::opt<bool> X86RelaxRelocations(
132137
"x86-relax-relocations",
133138
cl::desc(
@@ -162,6 +167,7 @@ MCTargetOptions llvm::mc::InitMCTargetOptionsFromFlags() {
162167
Options.MCNoDeprecatedWarn = getNoDeprecatedWarn();
163168
Options.MCNoTypeCheck = getNoTypeCheck();
164169
Options.MCSaveTempLabels = getSaveTempLabels();
170+
Options.Crel = getCrel();
165171
Options.X86RelaxRelocations = getX86RelaxRelocations();
166172
Options.EmitDwarfUnwind = getEmitDwarfUnwind();
167173
Options.EmitCompactUnwindNonCanonical = getEmitCompactUnwindNonCanonical();

0 commit comments

Comments
 (0)