Skip to content

Commit 5c54f15

Browse files
committed
ELF: Add support for emitting dynamic relocations in the Android relocation packing format.
The Android relocation packing format is a more compact format for dynamic relocations in executables and DSOs that is based on delta encoding and SLEBs. An overview of the format can be found in the Android source code: https://android.googlesource.com/platform/bionic/+/refs/heads/master/tools/relocation_packer/src/delta_encoder.h This patch implements relocation packing using that format. This implementation uses a more intelligent algorithm for compressing relative relocations than Android's own relocation packer. As a result it can generally create smaller relocation sections than that packer. If I link Chromium for Android targeting ARM32 I get a .rel.dyn of size 174693 bytes, as compared to 371832 bytes with gold and the Android packer. Differential Revision: https://reviews.llvm.org/D39152 llvm-svn: 316775
1 parent 1d05379 commit 5c54f15

File tree

9 files changed

+539
-53
lines changed

9 files changed

+539
-53
lines changed

Diff for: lld/ELF/Config.h

+1
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ struct Configuration {
105105
std::vector<SymbolVersion> VersionScriptLocals;
106106
std::vector<uint8_t> BuildIdVector;
107107
bool AllowMultipleDefinition;
108+
bool AndroidPackDynRelocs = false;
108109
bool AsNeeded = false;
109110
bool Bsymbolic;
110111
bool BsymbolicFunctions;

Diff for: lld/ELF/Driver.cpp

+8
Original file line numberDiff line numberDiff line change
@@ -775,6 +775,14 @@ void LinkerDriver::readConfigs(opt::InputArgList &Args) {
775775

776776
std::tie(Config->BuildId, Config->BuildIdVector) = getBuildId(Args);
777777

778+
if (auto *Arg = Args.getLastArg(OPT_pack_dyn_relocs_eq)) {
779+
StringRef S = Arg->getValue();
780+
if (S == "android")
781+
Config->AndroidPackDynRelocs = true;
782+
else if (S != "none")
783+
error("unknown -pack-dyn-relocs format: " + S);
784+
}
785+
778786
if (auto *Arg = Args.getLastArg(OPT_symbol_ordering_file))
779787
if (Optional<MemoryBufferRef> Buffer = readFile(Arg->getValue()))
780788
Config->SymbolOrderingFile = getLines(*Buffer);

Diff for: lld/ELF/Options.td

+3
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,9 @@ def omagic: Flag<["--"], "omagic">, MetaVarName<"<magic>">,
222222
defm orphan_handling: Eq<"orphan-handling">,
223223
HelpText<"Control how orphan sections are handled when linker script used">;
224224

225+
def pack_dyn_relocs_eq: J<"pack-dyn-relocs=">, MetaVarName<"<format>">,
226+
HelpText<"Pack dynamic relocations in the given format (none or android)">;
227+
225228
def pie: F<"pie">, HelpText<"Create a position independent executable">;
226229

227230
def print_gc_sections: F<"print-gc-sections">,

Diff for: lld/ELF/SyntheticSections.cpp

+244-31
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
#include "llvm/Object/Decompressor.h"
3434
#include "llvm/Object/ELFObjectFile.h"
3535
#include "llvm/Support/Endian.h"
36+
#include "llvm/Support/LEB128.h"
3637
#include "llvm/Support/MD5.h"
3738
#include "llvm/Support/RandomNumberGenerator.h"
3839
#include "llvm/Support/SHA1.h"
@@ -817,7 +818,7 @@ unsigned MipsGotSection::getLocalEntriesNum() const {
817818

818819
void MipsGotSection::finalizeContents() { updateAllocSize(); }
819820

820-
void MipsGotSection::updateAllocSize() {
821+
bool MipsGotSection::updateAllocSize() {
821822
PageEntriesNum = 0;
822823
for (std::pair<const OutputSection *, size_t> &P : PageIndexMap) {
823824
// For each output section referenced by GOT page relocations calculate
@@ -831,6 +832,7 @@ void MipsGotSection::updateAllocSize() {
831832
}
832833
Size = (getLocalEntriesNum() + GlobalEntries.size() + TlsEntries.size()) *
833834
Config->Wordsize;
835+
return false;
834836
}
835837

836838
bool MipsGotSection::empty() const {
@@ -1063,10 +1065,11 @@ template <class ELFT> void DynamicSection<ELFT>::finalizeContents() {
10631065

10641066
this->Link = InX::DynStrTab->getParent()->SectionIndex;
10651067
if (In<ELFT>::RelaDyn->getParent() && !In<ELFT>::RelaDyn->empty()) {
1066-
bool IsRela = Config->IsRela;
1067-
add({IsRela ? DT_RELA : DT_REL, In<ELFT>::RelaDyn});
1068-
add({IsRela ? DT_RELASZ : DT_RELSZ, In<ELFT>::RelaDyn->getParent(),
1068+
add({In<ELFT>::RelaDyn->DynamicTag, In<ELFT>::RelaDyn});
1069+
add({In<ELFT>::RelaDyn->SizeDynamicTag, In<ELFT>::RelaDyn->getParent(),
10691070
Entry::SecSize});
1071+
1072+
bool IsRela = Config->IsRela;
10701073
add({IsRela ? DT_RELAENT : DT_RELENT,
10711074
uint64_t(IsRela ? sizeof(Elf_Rela) : sizeof(Elf_Rel))});
10721075

@@ -1202,21 +1205,57 @@ uint32_t DynamicReloc::getSymIndex() const {
12021205
return 0;
12031206
}
12041207

1205-
template <class ELFT>
1206-
RelocationSection<ELFT>::RelocationSection(StringRef Name, bool Sort)
1207-
: SyntheticSection(SHF_ALLOC, Config->IsRela ? SHT_RELA : SHT_REL,
1208-
Config->Wordsize, Name),
1209-
Sort(Sort) {
1210-
this->Entsize = Config->IsRela ? sizeof(Elf_Rela) : sizeof(Elf_Rel);
1211-
}
1208+
RelocationBaseSection::RelocationBaseSection(StringRef Name, uint32_t Type,
1209+
int32_t DynamicTag,
1210+
int32_t SizeDynamicTag)
1211+
: SyntheticSection(SHF_ALLOC, Type, Config->Wordsize, Name),
1212+
DynamicTag(DynamicTag), SizeDynamicTag(SizeDynamicTag) {}
12121213

1213-
template <class ELFT>
1214-
void RelocationSection<ELFT>::addReloc(const DynamicReloc &Reloc) {
1214+
void RelocationBaseSection::addReloc(const DynamicReloc &Reloc) {
12151215
if (Reloc.Type == Target->RelativeRel)
12161216
++NumRelativeRelocs;
12171217
Relocs.push_back(Reloc);
12181218
}
12191219

1220+
void RelocationBaseSection::finalizeContents() {
1221+
// If all relocations are R_*_RELATIVE they don't refer to any
1222+
// dynamic symbol and we don't need a dynamic symbol table. If that
1223+
// is the case, just use 0 as the link.
1224+
this->Link = InX::DynSymTab ? InX::DynSymTab->getParent()->SectionIndex : 0;
1225+
1226+
// Set required output section properties.
1227+
getParent()->Link = this->Link;
1228+
}
1229+
1230+
template <class ELFT>
1231+
static void encodeDynamicReloc(typename ELFT::Rela *P,
1232+
const DynamicReloc &Rel) {
1233+
if (Config->IsRela)
1234+
P->r_addend = Rel.getAddend();
1235+
P->r_offset = Rel.getOffset();
1236+
if (Config->EMachine == EM_MIPS && Rel.getInputSec() == InX::MipsGot)
1237+
// The MIPS GOT section contains dynamic relocations that correspond to TLS
1238+
// entries. These entries are placed after the global and local sections of
1239+
// the GOT. At the point when we create these relocations, the size of the
1240+
// global and local sections is unknown, so the offset that we store in the
1241+
// TLS entry's DynamicReloc is relative to the start of the TLS section of
1242+
// the GOT, rather than being relative to the start of the GOT. This line of
1243+
// code adds the size of the global and local sections to the virtual
1244+
// address computed by getOffset() in order to adjust it into the TLS
1245+
// section.
1246+
P->r_offset += InX::MipsGot->getTlsOffset();
1247+
P->setSymbolAndType(Rel.getSymIndex(), Rel.Type, Config->IsMips64EL);
1248+
}
1249+
1250+
template <class ELFT>
1251+
RelocationSection<ELFT>::RelocationSection(StringRef Name, bool Sort)
1252+
: RelocationBaseSection(Name, Config->IsRela ? SHT_RELA : SHT_REL,
1253+
Config->IsRela ? DT_RELA : DT_REL,
1254+
Config->IsRela ? DT_RELASZ : DT_RELSZ),
1255+
Sort(Sort) {
1256+
this->Entsize = Config->IsRela ? sizeof(Elf_Rela) : sizeof(Elf_Rel);
1257+
}
1258+
12201259
template <class ELFT, class RelTy>
12211260
static bool compRelocations(const RelTy &A, const RelTy &B) {
12221261
bool AIsRel = A.getType(Config->IsMips64EL) == Target->RelativeRel;
@@ -1230,18 +1269,8 @@ static bool compRelocations(const RelTy &A, const RelTy &B) {
12301269
template <class ELFT> void RelocationSection<ELFT>::writeTo(uint8_t *Buf) {
12311270
uint8_t *BufBegin = Buf;
12321271
for (const DynamicReloc &Rel : Relocs) {
1233-
auto *P = reinterpret_cast<Elf_Rela *>(Buf);
1272+
encodeDynamicReloc<ELFT>(reinterpret_cast<Elf_Rela *>(Buf), Rel);
12341273
Buf += Config->IsRela ? sizeof(Elf_Rela) : sizeof(Elf_Rel);
1235-
1236-
if (Config->IsRela)
1237-
P->r_addend = Rel.getAddend();
1238-
P->r_offset = Rel.getOffset();
1239-
if (Config->EMachine == EM_MIPS && Rel.getInputSec() == InX::MipsGot)
1240-
// Dynamic relocation against MIPS GOT section make deal TLS entries
1241-
// allocated in the end of the GOT. We need to adjust the offset to take
1242-
// in account 'local' and 'global' GOT entries.
1243-
P->r_offset += InX::MipsGot->getTlsOffset();
1244-
P->setSymbolAndType(Rel.getSymIndex(), Rel.Type, Config->IsMips64EL);
12451274
}
12461275

12471276
if (Sort) {
@@ -1259,14 +1288,193 @@ template <class ELFT> unsigned RelocationSection<ELFT>::getRelocOffset() {
12591288
return this->Entsize * Relocs.size();
12601289
}
12611290

1262-
template <class ELFT> void RelocationSection<ELFT>::finalizeContents() {
1263-
// If all relocations are *RELATIVE they don't refer to any
1264-
// dynamic symbol and we don't need a dynamic symbol table. If that
1265-
// is the case, just use 0 as the link.
1266-
this->Link = InX::DynSymTab ? InX::DynSymTab->getParent()->SectionIndex : 0;
1291+
template <class ELFT>
1292+
AndroidPackedRelocationSection<ELFT>::AndroidPackedRelocationSection(
1293+
StringRef Name)
1294+
: RelocationBaseSection(
1295+
Name, Config->IsRela ? SHT_ANDROID_RELA : SHT_ANDROID_REL,
1296+
Config->IsRela ? DT_ANDROID_RELA : DT_ANDROID_REL,
1297+
Config->IsRela ? DT_ANDROID_RELASZ : DT_ANDROID_RELSZ) {
1298+
this->Entsize = 1;
1299+
}
12671300

1268-
// Set required output section properties.
1269-
getParent()->Link = this->Link;
1301+
template <class ELFT>
1302+
bool AndroidPackedRelocationSection<ELFT>::updateAllocSize() {
1303+
// This function computes the contents of an Android-format packed relocation
1304+
// section.
1305+
//
1306+
// This format compresses relocations by using relocation groups to factor out
1307+
// fields that are common between relocations and storing deltas from previous
1308+
// relocations in SLEB128 format (which has a short representation for small
1309+
// numbers). A good example of a relocation type with common fields is
1310+
// R_*_RELATIVE, which is normally used to represent function pointers in
1311+
// vtables. In the REL format, each relative relocation has the same r_info
1312+
// field, and is only different from other relative relocations in terms of
1313+
// the r_offset field. By sorting relocations by offset, grouping them by
1314+
// r_info and representing each relocation with only the delta from the
1315+
// previous offset, each 8-byte relocation can be compressed to as little as 1
1316+
// byte (or less with run-length encoding). This relocation packer was able to
1317+
// reduce the size of the relocation section in an Android Chromium DSO from
1318+
// 2,911,184 bytes to 174,693 bytes, or 6% of the original size.
1319+
//
1320+
// A relocation section consists of a header containing the literal bytes
1321+
// 'APS2' followed by a sequence of SLEB128-encoded integers. The first two
1322+
// elements are the total number of relocations in the section and an initial
1323+
// r_offset value. The remaining elements define a sequence of relocation
1324+
// groups. Each relocation group starts with a header consisting of the
1325+
// following elements:
1326+
//
1327+
// - the number of relocations in the relocation group
1328+
// - flags for the relocation group
1329+
// - (if RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG is set) the r_offset delta
1330+
// for each relocation in the group.
1331+
// - (if RELOCATION_GROUPED_BY_INFO_FLAG is set) the value of the r_info
1332+
// field for each relocation in the group.
1333+
// - (if RELOCATION_GROUP_HAS_ADDEND_FLAG and
1334+
// RELOCATION_GROUPED_BY_ADDEND_FLAG are set) the r_addend delta for
1335+
// each relocation in the group.
1336+
//
1337+
// Following the relocation group header are descriptions of each of the
1338+
// relocations in the group. They consist of the following elements:
1339+
//
1340+
// - (if RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG is not set) the r_offset
1341+
// delta for this relocation.
1342+
// - (if RELOCATION_GROUPED_BY_INFO_FLAG is not set) the value of the r_info
1343+
// field for this relocation.
1344+
// - (if RELOCATION_GROUP_HAS_ADDEND_FLAG is set and
1345+
// RELOCATION_GROUPED_BY_ADDEND_FLAG is not set) the r_addend delta for
1346+
// this relocation.
1347+
1348+
size_t OldSize = RelocData.size();
1349+
1350+
RelocData = {'A', 'P', 'S', '2'};
1351+
raw_svector_ostream OS(RelocData);
1352+
1353+
// The format header includes the number of relocations and the initial
1354+
// offset (we set this to zero because the first relocation group will
1355+
// perform the initial adjustment).
1356+
encodeSLEB128(Relocs.size(), OS);
1357+
encodeSLEB128(0, OS);
1358+
1359+
std::vector<Elf_Rela> Relatives, NonRelatives;
1360+
1361+
for (const DynamicReloc &Rel : Relocs) {
1362+
Elf_Rela R;
1363+
encodeDynamicReloc<ELFT>(&R, Rel);
1364+
1365+
if (R.getType(Config->IsMips64EL) == Target->RelativeRel)
1366+
Relatives.push_back(R);
1367+
else
1368+
NonRelatives.push_back(R);
1369+
}
1370+
1371+
std::sort(Relatives.begin(), Relatives.end(),
1372+
[](const Elf_Rel &A, const Elf_Rel &B) {
1373+
return A.r_offset < B.r_offset;
1374+
});
1375+
1376+
// Try to find groups of relative relocations which are spaced one word
1377+
// apart from one another. These generally correspond to vtable entries. The
1378+
// format allows these groups to be encoded using a sort of run-length
1379+
// encoding, but each group will cost 7 bytes in addition to the offset from
1380+
// the previous group, so it is only profitable to do this for groups of
1381+
// size 8 or larger.
1382+
std::vector<Elf_Rela> UngroupedRelatives;
1383+
std::vector<std::vector<Elf_Rela>> RelativeGroups;
1384+
for (auto I = Relatives.begin(), E = Relatives.end(); I != E;) {
1385+
std::vector<Elf_Rela> Group;
1386+
do {
1387+
Group.push_back(*I++);
1388+
} while (I != E && (I - 1)->r_offset + Config->Wordsize == I->r_offset);
1389+
1390+
if (Group.size() < 8)
1391+
UngroupedRelatives.insert(UngroupedRelatives.end(), Group.begin(),
1392+
Group.end());
1393+
else
1394+
RelativeGroups.emplace_back(std::move(Group));
1395+
}
1396+
1397+
unsigned HasAddendIfRela =
1398+
Config->IsRela ? RELOCATION_GROUP_HAS_ADDEND_FLAG : 0;
1399+
1400+
uint64_t Offset = 0;
1401+
uint64_t Addend = 0;
1402+
1403+
// Emit the run-length encoding for the groups of adjacent relative
1404+
// relocations. Each group is represented using two groups in the packed
1405+
// format. The first is used to set the current offset to the start of the
1406+
// group (and also encodes the first relocation), and the second encodes the
1407+
// remaining relocations.
1408+
for (std::vector<Elf_Rela> &G : RelativeGroups) {
1409+
// The first relocation in the group.
1410+
encodeSLEB128(1, OS);
1411+
encodeSLEB128(RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG |
1412+
RELOCATION_GROUPED_BY_INFO_FLAG | HasAddendIfRela,
1413+
OS);
1414+
encodeSLEB128(G[0].r_offset - Offset, OS);
1415+
encodeSLEB128(Target->RelativeRel, OS);
1416+
if (Config->IsRela) {
1417+
encodeSLEB128(G[0].r_addend - Addend, OS);
1418+
Addend = G[0].r_addend;
1419+
}
1420+
1421+
// The remaining relocations.
1422+
encodeSLEB128(G.size() - 1, OS);
1423+
encodeSLEB128(RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG |
1424+
RELOCATION_GROUPED_BY_INFO_FLAG | HasAddendIfRela,
1425+
OS);
1426+
encodeSLEB128(Config->Wordsize, OS);
1427+
encodeSLEB128(Target->RelativeRel, OS);
1428+
if (Config->IsRela) {
1429+
for (auto I = G.begin() + 1, E = G.end(); I != E; ++I) {
1430+
encodeSLEB128(I->r_addend - Addend, OS);
1431+
Addend = I->r_addend;
1432+
}
1433+
}
1434+
1435+
Offset = G.back().r_offset;
1436+
}
1437+
1438+
// Now the ungrouped relatives.
1439+
if (!UngroupedRelatives.empty()) {
1440+
encodeSLEB128(UngroupedRelatives.size(), OS);
1441+
encodeSLEB128(RELOCATION_GROUPED_BY_INFO_FLAG | HasAddendIfRela, OS);
1442+
encodeSLEB128(Target->RelativeRel, OS);
1443+
for (Elf_Rela &R : UngroupedRelatives) {
1444+
encodeSLEB128(R.r_offset - Offset, OS);
1445+
Offset = R.r_offset;
1446+
if (Config->IsRela) {
1447+
encodeSLEB128(R.r_addend - Addend, OS);
1448+
Addend = R.r_addend;
1449+
}
1450+
}
1451+
}
1452+
1453+
// Finally the non-relative relocations.
1454+
std::sort(NonRelatives.begin(), NonRelatives.end(),
1455+
[](const Elf_Rela &A, const Elf_Rela &B) {
1456+
return A.r_offset < B.r_offset;
1457+
});
1458+
if (!NonRelatives.empty()) {
1459+
encodeSLEB128(NonRelatives.size(), OS);
1460+
encodeSLEB128(HasAddendIfRela, OS);
1461+
for (Elf_Rela &R : NonRelatives) {
1462+
encodeSLEB128(R.r_offset - Offset, OS);
1463+
Offset = R.r_offset;
1464+
encodeSLEB128(R.r_info, OS);
1465+
if (Config->IsRela) {
1466+
encodeSLEB128(R.r_addend - Addend, OS);
1467+
Addend = R.r_addend;
1468+
}
1469+
}
1470+
}
1471+
1472+
// Returns whether the section size changed. We need to keep recomputing both
1473+
// section layout and the contents of this section until the size converges
1474+
// because changing this section's size can affect section layout, which in
1475+
// turn can affect the sizes of the LEB-encoded integers stored in this
1476+
// section.
1477+
return RelocData.size() != OldSize;
12701478
}
12711479

12721480
SymbolTableBaseSection::SymbolTableBaseSection(StringTableSection &StrTabSec)
@@ -2471,6 +2679,11 @@ template class elf::RelocationSection<ELF32BE>;
24712679
template class elf::RelocationSection<ELF64LE>;
24722680
template class elf::RelocationSection<ELF64BE>;
24732681

2682+
template class elf::AndroidPackedRelocationSection<ELF32LE>;
2683+
template class elf::AndroidPackedRelocationSection<ELF32BE>;
2684+
template class elf::AndroidPackedRelocationSection<ELF64LE>;
2685+
template class elf::AndroidPackedRelocationSection<ELF64BE>;
2686+
24742687
template class elf::SymbolTableSection<ELF32LE>;
24752688
template class elf::SymbolTableSection<ELF32BE>;
24762689
template class elf::SymbolTableSection<ELF64LE>;

0 commit comments

Comments
 (0)