From 0051dfc84f28b225761dfe6ead8529b287547885 Mon Sep 17 00:00:00 2001 From: John Reiser Date: Sun, 16 Sep 2018 15:10:40 -0700 Subject: [PATCH] --android-shlib: Relocate ABS _edata etc. https://github.com/upx/upx/issues/220 modified: p_lx_elf.cpp modified: p_lx_elf.h --- src/p_lx_elf.cpp | 90 +++++++++++++++++++++++++++++++++++++++--------- src/p_lx_elf.h | 2 ++ 2 files changed, 76 insertions(+), 16 deletions(-) diff --git a/src/p_lx_elf.cpp b/src/p_lx_elf.cpp index 73b6f37d..47716856 100644 --- a/src/p_lx_elf.cpp +++ b/src/p_lx_elf.cpp @@ -2651,6 +2651,42 @@ PackLinuxElf64::generateElfHdr( #include "p_elf_enum.h" #undef WANT_REL_ENUM +// Android shlib has ABS symbols that actually are relative. +static char const abs_symbol_names[][14] = { + "__bss_end__" + , "_bss_end__" + , "__bss_start" + , "__bss_start__" + , "_edata" + , "_end" + , "__end__" + , "" +}; + +int +PackLinuxElf32::adjABS(Elf32_Sym *sym, unsigned delta) +{ + for (int j = 0; abs_symbol_names[j][0]; ++j) { + if (!strcmp(abs_symbol_names[j], &dynstr[sym->st_name])) { + sym->st_value += delta; + return 1; + } + } + return 0; +} + +int +PackLinuxElf64::adjABS(Elf64_Sym *sym, unsigned delta) +{ + for (int j = 0; abs_symbol_names[j][0]; ++j) { + if (!strcmp(abs_symbol_names[j], &dynstr[sym->st_name])) { + sym->st_value += delta; + return 1; + } + } + return 0; +} + void PackLinuxElf32::pack1(OutputFile *fo, Filter & /*ft*/) { fi->seek(0, SEEK_SET); @@ -2741,6 +2777,9 @@ void PackLinuxElf32::pack1(OutputFile *fo, Filter & /*ft*/) && xct_off <= symval) { set_te32(&sym->st_value, asl_delta + symval); } + if (Elf32_Sym::SHN_ABS == symsec && xct_off <= symval) { + adjABS(sym, asl_delta); + } } // Relocate Phdr virtual addresses, but not physical offsets and sizes @@ -3135,6 +3174,9 @@ void PackLinuxElf64::pack1(OutputFile *fo, Filter & /*ft*/) && xct_off <= symval) { set_te64(&sym->st_value, asl_delta + symval); } + if (Elf64_Sym::SHN_ABS == symsec && xct_off <= symval) { + adjABS(sym, asl_delta); + } } // Relocate Phdr virtual addresses, but not physical offsets and sizes @@ -3989,7 +4031,8 @@ void PackLinuxElf64::unpack(OutputFile *fo) // Packed ET_EXE has no PT_DYNAMIC. // Packed ET_DYN has original PT_DYNAMIC for info needed by rtld. - bool const is_shlib = !!elf_find_ptype(Elf64_Phdr::PT_DYNAMIC, phdri, c_phnum); + Elf64_Phdr const *const dynhdr = elf_find_ptype(Elf64_Phdr::PT_DYNAMIC, phdri, c_phnum); + bool const is_shlib = !!dynhdr; if (is_shlib) { // Unpack and output the Ehdr and Phdrs for real. // This depends on position within input file fi. @@ -3997,9 +4040,8 @@ void PackLinuxElf64::unpack(OutputFile *fo) c_adler, u_adler, false, szb_info); // The first PT_LOAD. Part is not compressed (for benefit of rtld.) - // Read enough to position the input for next unpackExtent. fi->seek(0, SEEK_SET); - fi->readx(ibuf, overlay_offset + sizeof(hbuf) + szb_info + ph.c_len); + fi->readx(ibuf, get_te64(&dynhdr->p_offset) + get_te64(&dynhdr->p_filesz)); overlay_offset -= sizeof(linfo); xct_off = overlay_offset; e_shoff = get_te64(&ehdri.e_shoff); @@ -4011,6 +4053,9 @@ void PackLinuxElf64::unpack(OutputFile *fo) xct_off = e_shoff; } // un-Relocate dynsym (DT_SYMTAB) which is below xct_off + dynseg = (Elf64_Dyn const *)ibuf.subref( + "bad DYNAMIC", get_te64(&dynhdr->p_offset), get_te64(&dynhdr->p_filesz)); + dynstr = (char const *)elf_find_dynamic(Elf64_Dyn::DT_STRTAB); sec_dynsym = elf_find_section_type(Elf64_Shdr::SHT_DYNSYM); upx_uint64_t const off_dynsym = get_te64(&sec_dynsym->sh_offset); upx_uint64_t const sz_dynsym = get_te64(&sec_dynsym->sh_size); @@ -4025,6 +4070,9 @@ void PackLinuxElf64::unpack(OutputFile *fo) && xct_off <= symval) { set_te64(&sym->st_value, symval - asl_delta); } + if (Elf64_Sym::SHN_ABS == symsec && xct_off <= symval) { + adjABS(sym, -asl_delta); + } } } if (fo) { @@ -4044,6 +4092,8 @@ void PackLinuxElf64::unpack(OutputFile *fo) total_in = xct_off; total_out = xct_off; ph.u_len = 0; + // Position the input for next unpackExtent. + fi->seek(sizeof(linfo) + overlay_offset + sizeof(hbuf) + szb_info + ph.c_len, SEEK_SET); // Decompress and unfilter the tail of first PT_LOAD. phdr = (Elf64_Phdr *) (void *) (1+ ehdr); @@ -4150,13 +4200,13 @@ void PackLinuxElf64::unpack(OutputFile *fo) total_in += old_data_len; total_out += old_data_len; - Elf64_Phdr *dynhdr = (Elf64_Phdr *)&u[sizeof(*ehdr)]; - for (unsigned j3= 0; j3 < u_phnum; ++j3, ++dynhdr) - if (Elf64_Phdr::PT_DYNAMIC==get_te32(&dynhdr->p_type)) { + Elf64_Phdr const *udynhdr = (Elf64_Phdr *)&u[sizeof(*ehdr)]; + for (unsigned j3= 0; j3 < u_phnum; ++j3, ++udynhdr) + if (Elf64_Phdr::PT_DYNAMIC==get_te32(&udynhdr->p_type)) { upx_uint64_t dt_pltrelsz(0), dt_jmprel(0); upx_uint64_t dt_relasz(0), dt_rela(0); - upx_uint64_t const dyn_len = get_te64(&dynhdr->p_filesz); - upx_uint64_t const dyn_off = get_te64(&dynhdr->p_offset); + upx_uint64_t const dyn_len = get_te64(&udynhdr->p_filesz); + upx_uint64_t const dyn_off = get_te64(&udynhdr->p_offset); if (dyn_off < load_off) { continue; // Oops. Not really is_shlib ? [built by 'rust' ?] } @@ -4757,7 +4807,8 @@ void PackLinuxElf32::unpack(OutputFile *fo) // Packed ET_EXE has no PT_DYNAMIC. // Packed ET_DYN has original PT_DYNAMIC for info needed by rtld. - bool const is_shlib = !!elf_find_ptype(Elf32_Phdr::PT_DYNAMIC, phdri, c_phnum); + Elf32_Phdr const *const dynhdr = elf_find_ptype(Elf32_Phdr::PT_DYNAMIC, phdri, c_phnum); + bool const is_shlib = !!dynhdr; if (is_shlib) { // Unpack and output the Ehdr and Phdrs for real. // This depends on position within input file fi. @@ -4765,9 +4816,8 @@ void PackLinuxElf32::unpack(OutputFile *fo) c_adler, u_adler, false, szb_info); // The first PT_LOAD. Part is not compressed (for benefit of rtld.) - // Read enough to position the input for next unpackExtent. fi->seek(0, SEEK_SET); - fi->readx(ibuf, overlay_offset + sizeof(hbuf) + szb_info + ph.c_len); + fi->readx(ibuf, get_te32(&dynhdr->p_offset) + get_te32(&dynhdr->p_filesz)); overlay_offset -= sizeof(linfo); xct_off = overlay_offset; e_shoff = get_te32(&ehdri.e_shoff); @@ -4779,7 +4829,10 @@ void PackLinuxElf32::unpack(OutputFile *fo) xct_off = e_shoff; } // un-Relocate dynsym (DT_SYMTAB) which is below xct_off - sec_dynsym = elf_find_section_type(Elf64_Shdr::SHT_DYNSYM); + dynseg = (Elf32_Dyn const *)ibuf.subref( + "bad DYNAMIC", get_te32(&dynhdr->p_offset), get_te32(&dynhdr->p_filesz)); + dynstr = (char const *)elf_find_dynamic(Elf32_Dyn::DT_STRTAB); + sec_dynsym = elf_find_section_type(Elf32_Shdr::SHT_DYNSYM); unsigned const off_dynsym = get_te32(&sec_dynsym->sh_offset); unsigned const sz_dynsym = get_te32(&sec_dynsym->sh_size); Elf32_Sym *const sym0 = (Elf32_Sym *)ibuf.subref( @@ -4793,6 +4846,9 @@ void PackLinuxElf32::unpack(OutputFile *fo) && xct_off <= symval) { set_te32(&sym->st_value, symval - asl_delta); } + if (Elf32_Sym::SHN_ABS == symsec && xct_off <= symval) { + adjABS(sym, -asl_delta); + } } } if (fo) { @@ -4812,6 +4868,8 @@ void PackLinuxElf32::unpack(OutputFile *fo) total_in = xct_off; total_out = xct_off; ph.u_len = 0; + // Position the input for next unpackExtent. + fi->seek(sizeof(linfo) + overlay_offset + sizeof(hbuf) + szb_info + ph.c_len, SEEK_SET); // Decompress and unfilter the tail of first PT_LOAD. phdr = (Elf32_Phdr *) (void *) (1+ ehdr); @@ -4918,13 +4976,13 @@ void PackLinuxElf32::unpack(OutputFile *fo) total_in += old_data_len; total_out += old_data_len; - Elf32_Phdr *dynhdr = (Elf32_Phdr *)&u[sizeof(*ehdr)]; - for (unsigned j3= 0; j3 < u_phnum; ++j3, ++dynhdr) + Elf32_Phdr const *udynhdr = (Elf32_Phdr *)&u[sizeof(*ehdr)]; + for (unsigned j3= 0; j3 < u_phnum; ++j3, ++udynhdr) if (Elf32_Phdr::PT_DYNAMIC==get_te32(&dynhdr->p_type)) { unsigned dt_pltrelsz(0), dt_jmprel(0); unsigned dt_relsz(0), dt_rel(0); - unsigned const dyn_len = get_te32(&dynhdr->p_filesz); - unsigned const dyn_off = get_te32(&dynhdr->p_offset); + unsigned const dyn_len = get_te32(&udynhdr->p_filesz); + unsigned const dyn_off = get_te32(&udynhdr->p_offset); if (dyn_off < load_off) { continue; // Oops. Not really is_shlib ? [built by 'rust' ?] } diff --git a/src/p_lx_elf.h b/src/p_lx_elf.h index 6cb4be43..580a4db9 100644 --- a/src/p_lx_elf.h +++ b/src/p_lx_elf.h @@ -161,6 +161,7 @@ protected: void const *elf_find_dynamic(unsigned) const; Elf32_Dyn const *elf_has_dynamic(unsigned) const; virtual upx_uint64_t elf_unsigned_dynamic(unsigned) const; + virtual int adjABS(Elf32_Sym *sym, unsigned delta); protected: Elf32_Ehdr ehdri; // from input file @@ -292,6 +293,7 @@ protected: void const *elf_find_dynamic(unsigned) const; Elf64_Dyn const *elf_has_dynamic(unsigned) const; virtual upx_uint64_t elf_unsigned_dynamic(unsigned) const; + virtual int adjABS(Elf64_Sym *sym, unsigned delta); protected: Elf64_Ehdr ehdri; // from input file