Bug 1388713 - Change how elfhack looks for the bss section. r=froydnj

In bug 635961, elfhack was made to (ab)use the bss section as a
temporary space for a pointer. To find it, it scanned writable PT_LOAD
segments to find one that has a different file and memory size,
indicating the presence of .bss. This usually works fine, but when
the binary is linked with lld and relro is enabled, the end of the
file-backed part of the PT_LOAD segment containing the .bss section
ends up in the RELRO segment, making that location read-only and
subsequently making the elfhacked binary crash when it tries to restore
the .bss to a clean state, because it's not actually writing in the .bss
section: lld page aligns it after the RELRO segment.

So instead of scanning PT_LOAD segments, we scan for SHT_NOBITS
sections that are not SHF_TLS (i.e. not .tbss).

--HG--
extra : rebase_source : f18c43897fd0139aa8535f983e13eb785088cb18
This commit is contained in:
Mike Hommey 2017-08-10 07:55:55 +09:00
parent f41361f1d2
commit ccd43013f6

View File

@ -729,15 +729,15 @@ int do_relocation_section(Elf *elf, unsigned int rel_type, unsigned int rel_type
// Find the beginning of the bss section, and use an aligned location in there
// for the relocation.
for (ElfSegment *segment = elf->getSegmentByType(PT_LOAD); segment;
segment = elf->getSegmentByType(PT_LOAD, segment)) {
if ((segment->getFlags() & PF_W) == 0)
for (ElfSection *s = elf->getSection(1); s != nullptr; s = s->getNext()) {
if (s->getType() != SHT_NOBITS || (s->getFlags() & (SHF_TLS | SHF_WRITE)) != SHF_WRITE) {
continue;
}
size_t ptr_size = Elf_Addr::size(elf->getClass());
size_t aligned_mem_end = (segment->getAddr() + segment->getMemSize() + ptr_size - 1) & ~(ptr_size - 1);
size_t aligned_file_end = (segment->getAddr() + segment->getFileSize() + ptr_size - 1) & ~(ptr_size - 1);
if (aligned_mem_end - aligned_file_end >= Elf_Addr::size(elf->getClass())) {
mprotect_cb = rel.r_offset = aligned_file_end;
size_t usable_start = (s->getAddr() + ptr_size - 1) & ~(ptr_size - 1);
size_t usable_end = (s->getAddr() + s->getSize()) & ~(ptr_size - 1);
if (usable_end - usable_start >= ptr_size) {
mprotect_cb = rel.r_offset = usable_start;
break;
}
}