Bug 1385783 - Insert the elfhack code before the first executable section. r?froydnj
The lld linker creates separate segments for purely executable sections
(such as .text) and sections preceding those (such as .rel.dyn). Neither
gold nor bfd ld do that, and just put all those sections in the same
executable segment.
Since elfhack is putting its executable code between the two relocation
sections, it ends up in a non-executable segment, leading to a crash
when it's time to run that code.
We thus insert the elfhack code before the first executable section
instead of between the two relocation sections (which is where the
elfhack data lies, and stays).
--- a/build/unix/elfhack/elfhack.cpp
+++ b/build/unix/elfhack/elfhack.cpp
@@ -636,18 +636,16 @@ int do_relocation_section(Elf *elf, unsi
}
}
if (relhack_entry.r_offset)
relhack->push_back(relhack_entry);
// Last entry must be nullptr
relhack_entry.r_offset = relhack_entry.r_info = 0;
relhack->push_back(relhack_entry);
- unsigned int old_end = section->getOffset() + section->getSize();
-
if (init_array && !init_array_reloc) {
// Some linkers create a DT_INIT_ARRAY section that, for all purposes,
// is empty: it only contains 0x0 or 0xffffffff pointers with no relocations.
const size_t zero = 0;
const size_t all = SIZE_MAX;
const char *data = init_array->getData();
size_t length = Elf_Addr::size(elf->getClass());
bool empty = true;
@@ -749,19 +747,37 @@ int do_relocation_section(Elf *elf, unsi
return -1;
}
}
section->rels.assign(new_rels.begin(), new_rels.end());
section->shrink(new_rels.size() * section->getEntSize());
ElfRelHackCode_Section *relhackcode = new ElfRelHackCode_Section(relhackcode_section, *elf, *relhack, original_init, mprotect_cb);
- relhackcode->insertBefore(section);
- relhack->insertAfter(relhackcode);
- if (section->getOffset() + section->getSize() >= old_end) {
+ // Find the first executable section, and insert the relhack code before
+ // that. The relhack data is inserted between .rel.dyn and .rel.plt.
+ ElfSection *first_executable = nullptr;
+ for (ElfSection *s = elf->getSection(1); s != nullptr;
+ s = s->getNext()) {
+ if (s->getFlags() & SHF_EXECINSTR) {
+ first_executable = s;
+ break;
+ }
+ }
+
+ unsigned int old_exec = first_executable->getOffset();
+
+ relhack->insertBefore(section);
+ relhackcode->insertBefore(first_executable);
+
+ // Trying to get first_executable->getOffset() now may throw if the new
+ // layout would require it to move, so we look at the end of the relhack
+ // code section instead, comparing it to where the first executable
+ // section used to start.
+ if (relhackcode->getOffset() + relhackcode->getSize() >= old_exec) {
fprintf(stderr, "No gain. Skipping\n");
return -1;
}
// Adjust PT_LOAD segments
for (ElfSegment *segment = elf->getSegmentByType(PT_LOAD); segment;
segment = elf->getSegmentByType(PT_LOAD, segment)) {
maybe_split_segment(elf, segment, fill);