Bug 1378986 - Avoid elfhack failing on weird DT_INIT_ARRAYs. r?froydnj
Somehow, with the Android toolchain, we end up with
non-empty-but-really-empty DT_INIT_ARRAYs.
In practical terms, they are arrays with no relocations, and content
that is meaningless:
$ objdump -s -j .init_array libnss3.so
libnss3.so: file format elf32-little
Contents of section .init_array:
1086e0 00000000 ....
$ readelf -r libnss3.so | grep 1086e0
$ objdump -s -j .init_array libplugin-container-pie.so
libplugin-container-pie.so: file format elf32-little
Contents of section .init_array:
4479c ffffffff 00000000 ffffffff 00000000 ................
$ readelf -r libplugin-container-pie.so | grep 4479c
Because so far, elfhack expected meaningful DT_INIT_ARRAYs, it bailed out
early in that case.
--- a/build/unix/elfhack/elfhack.cpp
+++ b/build/unix/elfhack/elfhack.cpp
@@ -616,21 +616,46 @@ 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) {
- if (! init_array_reloc) {
+ 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;
+ for (size_t off = 0; off < init_array->getSize(); off += length) {
+ if (memcmp(data + off, &zero, length) &&
+ memcmp(data + off, &all, length)) {
+ empty = false;
+ break;
+ }
+ }
+ // If we encounter such an empty DT_INIT_ARRAY section, we add a
+ // relocation for its first entry to point to our init. Code further
+ // below will take care of actually setting the right r_info and
+ // r_addend for the relocation, as if we had a normal DT_INIT_ARRAY
+ // section.
+ if (empty) {
+ new_rels.emplace_back();
+ init_array_reloc = new_rels.size();
+ Rel_Type *rel = &new_rels[init_array_reloc - 1];
+ rel->r_offset = init_array->getAddr();
+ } else {
fprintf(stderr, "Didn't find relocation for DT_INIT_ARRAY's first entry. Skipping\n");
return -1;
}
+ } else if (init_array) {
Rel_Type *rel = &new_rels[init_array_reloc - 1];
unsigned int addend = get_addend(rel, elf);
// Use relocated value of DT_INIT_ARRAY's first entry for the
// function to be called by the injected code.
if (ELF32_R_TYPE(rel->r_info) == rel_type) {
original_init = addend;
} else if (ELF32_R_TYPE(rel->r_info) == rel_type2) {
ElfSymtab_Section *symtab = (ElfSymtab_Section *)section->getLink();
--- a/build/unix/elfhack/elfxx.h
+++ b/build/unix/elfhack/elfxx.h
@@ -553,26 +553,32 @@ public:
Elf_SymValue *lookup(const char *name, unsigned int type_filter = STT(OBJECT) | STT(FUNC));
//private: // Until we have a real API
std::vector<Elf_SymValue> syms;
};
class Elf_Rel: public serializable<Elf_Rel_Traits> {
public:
+ Elf_Rel()
+ : serializable<Elf_Rel_Traits>() {};
+
Elf_Rel(std::ifstream &file, char ei_class, char ei_data)
: serializable<Elf_Rel_Traits>(file, ei_class, ei_data) {};
static const unsigned int sh_type = SHT_REL;
static const unsigned int d_tag = DT_REL;
static const unsigned int d_tag_count = DT_RELCOUNT;
};
class Elf_Rela: public serializable<Elf_Rela_Traits> {
public:
+ Elf_Rela()
+ : serializable<Elf_Rela_Traits>() {};
+
Elf_Rela(std::ifstream &file, char ei_class, char ei_data)
: serializable<Elf_Rela_Traits>(file, ei_class, ei_data) {};
static const unsigned int sh_type = SHT_RELA;
static const unsigned int d_tag = DT_RELA;
static const unsigned int d_tag_count = DT_RELACOUNT;
};