Revamp --android-shlib

Android "Oreo" wants to deal with ElfXX_Shdr[].  So splice 4KiB into PT_LOAD[0]
with an "extra" copy of them.  This requires careful relocation processing.
It also costs a page at run time, and an average of a page in the file
(depending on fragmentation at the boundary between r-x and rw- .)

https://github.com/upx/upx/issues/124
https://github.com/upx/upx/issues/139
https://github.com/upx/upx/issues/142

	modified:   p_elf_enum.h
	modified:   p_lx_elf.cpp
	modified:   p_lx_elf.h
	modified:   stub/src/arm.v4a-linux.shlib-init.S
	modified:   stub/src/arm.v4t-linux.shlib-init.S
	modified:   stub/src/arm64-linux.shlib-init.S

	modified:   stub/arm.v5a-linux.shlib-init.h
	modified:   stub/arm.v5t-linux.shlib-init.h
	modified:   stub/arm64-linux.shlib-init.h

	modified:   ../.github/travis_testsuite_1.sh
This commit is contained in:
John Reiser
2018-01-15 16:08:34 -08:00
parent f3a7620ae8
commit c34fe09a79
12 changed files with 1497 additions and 1283 deletions
+421 -245
View File
@@ -87,6 +87,7 @@ fpad4(OutputFile *fo)
return d + len;
}
#if 0 //{ unused
static off_t
fpad8(OutputFile *fo)
{
@@ -96,6 +97,7 @@ fpad8(OutputFile *fo)
fo->write(&zero, d);
return d + len;
}
#endif //}
static unsigned
funpad4(InputFile *fi)
@@ -268,8 +270,8 @@ PackLinuxElf32::PackLinuxElf32help1(InputFile *f)
alloc_file_image(file_image, file_size);
f->seek(0, SEEK_SET);
f->readx(file_image, file_size);
phdri= (Elf32_Phdr *)(e_phoff + file_image); // do not free() !!
shdri= (Elf32_Shdr const *)(e_shoff + file_image); // do not free() !!
phdri= (Elf32_Phdr *)(e_phoff + file_image); // do not free() !!
shdri= (Elf32_Shdr *)(e_shoff + file_image); // do not free() !!
sec_dynsym = elf_find_section_type(Elf32_Shdr::SHT_DYNSYM);
if (sec_dynsym) {
unsigned t = get_te32(&sec_dynsym->sh_link);
@@ -307,8 +309,8 @@ off_t PackLinuxElf::pack3(OutputFile *fo, Filter &ft) // return length of output
fo->write(&zero, t);
len += t;
set_te32(&disp, 2*sizeof(disp) + len - (sz_elf_hdrs + sizeof(p_info) + sizeof(l_info)));
fo->write(&disp, sizeof(disp)); // .e_entry - &first_b_info
set_te32(&disp, sz_elf_hdrs + sizeof(p_info) + sizeof(l_info));
fo->write(&disp, sizeof(disp)); // offset(b_info)
len += sizeof(disp);
set_te32(&disp, len); // distance back to beginning (detect dynamic reloc)
fo->write(&disp, sizeof(disp));
@@ -319,15 +321,21 @@ off_t PackLinuxElf::pack3(OutputFile *fo, Filter &ft) // return length of output
? jni_onload_va
: elf_unsigned_dynamic(Elf32_Dyn::DT_INIT) );
set_te32(&disp, firstpc_va - load_va);
fo->write(&disp, sizeof(disp));
if (opt->o_unix.android_shlib) { // the extra page
set_te32(&disp, asl_delta + firstpc_va - load_va);
}
fo->write(&disp, sizeof(disp)); // DT_INIT.d_val
len += sizeof(disp);
set_te32(&disp, hatch_off);
fo->write(&disp, sizeof(disp));
fo->write(&disp, sizeof(disp)); // offset(hatch)
len += sizeof(disp);
if (opt->o_unix.android_shlib) {
xct_off += asl_delta; // the extra page
}
set_te32(&disp, xct_off);
fo->write(&disp, sizeof(disp));
fo->write(&disp, sizeof(disp)); // offset(dst for f_exp)
len += sizeof(disp);
}
sz_pack2 = len; // 0 mod 8
@@ -362,6 +370,8 @@ off_t PackLinuxElf32::pack3(OutputFile *fo, Filter &ft)
fo->write(&hdr, sizeof(hdr));
flen = fpad4(fo);
set_te32(&elfout.phdr[0].p_filesz, sz_pack2 + lsize);
set_te32(&elfout.phdr[0].p_memsz, sz_pack2 + lsize);
if (0==xct_off) { // not shared library; adjust PT_LOAD
v_hole = page_mask & (~page_mask + v_hole + get_te32(&elfout.phdr[0].p_vaddr));
set_te32(&elfout.phdr[1].p_vaddr, v_hole);
@@ -398,8 +408,8 @@ off_t PackLinuxElf32::pack3(OutputFile *fo, Filter &ft)
set_te32(&phdr->p_align, align);
}
off += (align-1) & (ioff - off);
fi->seek(ioff, SEEK_SET); fi->readx(ibuf, len);
fo->seek( off, SEEK_SET); fo->write(ibuf, len);
fo->seek( off, SEEK_SET);
fo->write(ioff + file_image, len);
so_slide = off - ioff;
set_te32(&phdr->p_offset, so_slide + ioff);
}
@@ -436,9 +446,25 @@ off_t PackLinuxElf32::pack3(OutputFile *fo, Filter &ft)
fo->rewrite(&word, sizeof(word));
flen = fo->seek(0, SEEK_END);
}
ehdri.e_shnum = 0;
ehdri.e_shoff = 0;
ehdri.e_shstrndx = 0;
if (opt->o_unix.android_shlib) {
// Update {DYNAMIC}.sh_offset by so_slide.
Elf32_Shdr *shdr = shdri;
for (unsigned j = 0; j < e_shnum; ++shdr, ++j) {
if (Elf32_Shdr::SHT_DYNAMIC == get_te32(&shdr->sh_type)) {
upx_uint32_t offset = get_te32(&shdr->sh_offset);
set_te32(&shdr->sh_offset, so_slide - asl_delta + offset );
fo->seek(j * sizeof(Elf32_Shdr) - asl_delta + xct_off, SEEK_SET);
fo->rewrite(shdr, sizeof(*shdr));
fo->seek(0, SEEK_END);
break;
}
}
}
else { // !opt->o_unix.android_shlib)
ehdri.e_shnum = 0;
ehdri.e_shoff = 0;
ehdri.e_shstrndx = 0;
}
}
return flen;
}
@@ -522,8 +548,8 @@ off_t PackLinuxElf64::pack3(OutputFile *fo, Filter &ft)
set_te64(&phdr->p_align, align);
}
off += (align-1) & (ioff - off);
fi->seek(ioff, SEEK_SET); fi->readx(ibuf, len);
fo->seek( off, SEEK_SET); fo->write(ibuf, len);
fo->seek( off, SEEK_SET);
fo->write(ioff + file_image, len);
so_slide = off - ioff;
set_te64(&phdr->p_offset, so_slide + ioff);
}
@@ -553,15 +579,40 @@ off_t PackLinuxElf64::pack3(OutputFile *fo, Filter &ft)
if (xct_off < ioff)
set_te64(&phdr->p_offset, so_slide + ioff);
} // end each Phdr
if (off_init) { // change DT_INIT.d_val
fo->seek(off_init, SEEK_SET);
upx_uint64_t word; set_te64(&word, va_init);
fo->rewrite(&word, sizeof(word));
flen = fo->seek(0, SEEK_END);
}
ehdri.e_shnum = 0;
ehdri.e_shoff = 0;
ehdri.e_shstrndx = 0;
if (opt->o_unix.android_shlib) {
// Update {DYNAMIC}.sh_offset by so_slide.
Elf64_Shdr *shdr = shdri;
for (unsigned j = 0; j < e_shnum; ++shdr, ++j) {
unsigned sh_type = get_te32(&shdr->sh_type);
if (Elf64_Shdr::SHT_DYNAMIC == sh_type) {
upx_uint64_t offset = get_te64(&shdr->sh_offset);
set_te64(&shdr->sh_offset, so_slide - asl_delta + offset );
fo->seek(j * sizeof(Elf64_Shdr) - asl_delta + xct_off, SEEK_SET);
fo->rewrite(shdr, sizeof(*shdr));
fo->seek(0, SEEK_END);
break;
}
if (Elf64_Shdr::SHT_RELA == sh_type
&& n_jmp_slot
&& !strcmp(".rela.plt", get_te32(&shdr->sh_name) + shstrtab)) {
upx_uint64_t f_off = elf_get_offset_from_address(plt_off);
fo->seek(f_off, SEEK_SET);
fo->rewrite(&file_image[f_off - so_slide], n_jmp_slot * sizeof(void *));
}
}
}
else { // !opt->o_unix.android_shlib)
ehdri.e_shnum = 0;
ehdri.e_shoff = 0;
ehdri.e_shstrndx = 0;
}
}
return flen;
}
@@ -703,8 +754,8 @@ PackLinuxElf64::PackLinuxElf64help1(InputFile *f)
alloc_file_image(file_image, file_size);
f->seek(0, SEEK_SET);
f->readx(file_image, file_size);
phdri= (Elf64_Phdr *)(e_phoff + file_image); // do not free() !!
shdri= (Elf64_Shdr const *)(e_shoff + file_image); // do not free() !!
phdri= (Elf64_Phdr *)(e_phoff + file_image); // do not free() !!
shdri= (Elf64_Shdr *)(e_shoff + file_image); // do not free() !!
sec_dynsym = elf_find_section_type(Elf64_Shdr::SHT_DYNSYM);
if (sec_dynsym) {
unsigned t = get_te32(&sec_dynsym->sh_link);
@@ -1718,7 +1769,37 @@ bool PackLinuxElf32::canPack()
// Also allow __uClibc_main and __uClibc_start_main .
if (Elf32_Ehdr::ET_DYN==get_te16(&ehdr->e_type)) {
// The DT_SYMTAB has no designated length. Read the whole file.
alloc_file_image(file_image, file_size);
fi->seek(0, SEEK_SET);
fi->readx(file_image, file_size);
memcpy(&ehdri, ehdr, sizeof(Elf32_Ehdr));
phdri= (Elf32_Phdr *)((size_t)e_phoff + file_image); // do not free() !!
shdri= (Elf32_Shdr *)((size_t)e_shoff + file_image); // do not free() !!
sec_strndx = &shdri[get_te16(&ehdr->e_shstrndx)];
shstrtab = (char const *)(get_te32(&sec_strndx->sh_offset) + file_image);
sec_dynsym = elf_find_section_type(Elf32_Shdr::SHT_DYNSYM);
if (sec_dynsym)
sec_dynstr = get_te32(&sec_dynsym->sh_link) + shdri;
sec_strndx = &shdri[get_te16(&ehdr->e_shstrndx)];
shstrtab = (char const *)(get_te32(&sec_strndx->sh_offset) + file_image);
if (Elf32_Shdr::SHT_STRTAB != get_te32(&sec_strndx->sh_type)
|| 0!=strcmp((char const *)".shstrtab",
&shstrtab[get_te32(&sec_strndx->sh_name)]) ) {
throwCantPack("bad e_shstrndx");
}
phdr= phdri;
for (int j= e_phnum; --j>=0; ++phdr)
if (Elf32_Phdr::PT_DYNAMIC==get_te32(&phdr->p_type)) {
dynseg= (Elf32_Dyn const *)(check_pt_dynamic(phdr) + file_image);
break;
}
// elf_find_dynamic() returns 0 if 0==dynseg.
dynstr= (char const *)elf_find_dynamic(Elf32_Dyn::DT_STRTAB);
dynsym= (Elf32_Sym const *)elf_find_dynamic(Elf32_Dyn::DT_SYMTAB);
if (Elf32_Dyn::DF_1_PIE & elf_unsigned_dynamic(Elf32_Dyn::DT_FLAGS_1)
|| calls_crt1((Elf32_Rel const *)elf_find_dynamic(Elf32_Dyn::DT_REL),
@@ -1898,14 +1979,20 @@ PackLinuxElf64::canPack()
fi->seek(0, SEEK_SET);
fi->readx(file_image, file_size);
memcpy(&ehdri, ehdr, sizeof(Elf64_Ehdr));
phdri= (Elf64_Phdr *)((size_t)e_phoff + file_image); // do not free() !!
shdri= (Elf64_Shdr const *)((size_t)e_shoff + file_image); // do not free() !!
phdri= (Elf64_Phdr *)((size_t)e_phoff + file_image); // do not free() !!
shdri= (Elf64_Shdr *)((size_t)e_shoff + file_image); // do not free() !!
//sec_strndx = &shdri[ehdr->e_shstrndx];
//shstrtab = (char const *)(sec_strndx->sh_offset + file_image);
sec_dynsym = elf_find_section_type(Elf64_Shdr::SHT_DYNSYM);
if (sec_dynsym)
sec_dynstr = get_te64(&sec_dynsym->sh_link) + shdri;
sec_dynstr = get_te32(&sec_dynsym->sh_link) + shdri;
sec_strndx = &shdri[get_te16(&ehdr->e_shstrndx)];
shstrtab = (char const *)(get_te64(&sec_strndx->sh_offset) + file_image);
if (Elf64_Shdr::SHT_STRTAB != get_te32(&sec_strndx->sh_type)
|| 0!=strcmp((char const *)".shstrtab",
&shstrtab[get_te32(&sec_strndx->sh_name)]) ) {
throwCantPack("bad e_shstrndx");
}
phdr= phdri;
for (int j= e_phnum; --j>=0; ++phdr)
@@ -1945,9 +2032,23 @@ PackLinuxElf64::canPack()
}
Elf64_Shdr const *shdr = shdri;
xct_va = ~0ull;
for (int j= e_shnum; --j>=0; ++shdr) {
if (Elf64_Shdr::SHF_EXECINSTR & get_te32(&shdr->sh_flags)) {
xct_va = umin64(xct_va, get_te64(&shdr->sh_addr));
if (e_shnum) {
for (int j= e_shnum; --j>=0; ++shdr) {
if (Elf64_Shdr::SHF_EXECINSTR & get_te32(&shdr->sh_flags)) {
xct_va = umin64(xct_va, get_te64(&shdr->sh_addr));
}
}
}
else { // no Sections; use heuristics
upx_uint64_t const strsz = elf_unsigned_dynamic(Elf64_Dyn::DT_STRSZ);
upx_uint64_t const strtab = elf_unsigned_dynamic(Elf64_Dyn::DT_STRTAB);
upx_uint64_t const relsz = elf_unsigned_dynamic(Elf64_Dyn::DT_RELSZ);
upx_uint64_t const rel = elf_unsigned_dynamic(Elf64_Dyn::DT_REL);
upx_uint64_t const init = elf_unsigned_dynamic(Elf64_Dyn::DT_INIT);
if ((init == (relsz + rel ) && rel == (strsz + strtab))
|| (init == (strsz + strtab) && strtab == (relsz + rel ))
) {
xct_va = init;
}
}
// Rely on 0==elf_unsigned_dynamic(tag) if no such tag.
@@ -2404,6 +2505,10 @@ PackLinuxElf64::generateElfHdr(
}
}
#define WANT_REL_ENUM
#include "p_elf_enum.h"
#undef WANT_REL_ENUM
void PackLinuxElf32::pack1(OutputFile *fo, Filter & /*ft*/)
{
fi->seek(0, SEEK_SET);
@@ -2412,7 +2517,7 @@ void PackLinuxElf32::pack1(OutputFile *fo, Filter & /*ft*/)
sz_phdrs = e_phnum * get_te16(&ehdri.e_phentsize);
// Remember all PT_NOTE, and find lg2_page from PT_LOAD.
Elf32_Phdr const *phdr = phdri;
Elf32_Phdr *phdr = phdri;
note_size = 0;
for (unsigned j=0; j < e_phnum; ++phdr, ++j) {
if (phdr->PT_NOTE32 == get_te32(&phdr->p_type)) {
@@ -2450,34 +2555,143 @@ void PackLinuxElf32::pack1(OutputFile *fo, Filter & /*ft*/)
progid = 0; // getRandomId(); not useful, so do not clutter
if (0!=xct_off) { // shared library
fi->seek(0, SEEK_SET);
fi->readx(ibuf, xct_off);
sz_elf_hdrs = xct_off;
fo->write(ibuf, xct_off);
fo->write(file_image, xct_off); // before the first SHF_EXECINSTR (typ ".plt")
if (opt->o_unix.android_shlib) {
// In order to pacify the runtime linker on Android "O" ("Oreo"),
// we will splice-in a 4KiB page that contains an "extra" copy
// of the Shdr and shstrtab.
xct_va += asl_delta;
//xct_off += asl_delta; // not yet
// Relocate PT_DYNAMIC (in 2nd PT_LOAD)
Elf32_Dyn *dyn = const_cast<Elf32_Dyn *>(dynseg);
for (; dyn->d_tag; ++dyn) {
unsigned d_tag = get_te32(&dyn->d_tag);
if (Elf32_Dyn::DT_FINI == d_tag
|| Elf32_Dyn::DT_FINI_ARRAY == d_tag
|| Elf32_Dyn::DT_INIT_ARRAY == d_tag
|| Elf32_Dyn::DT_PREINIT_ARRAY == d_tag
|| Elf32_Dyn::DT_PLTGOT == d_tag) {
unsigned d_val = get_te32(&dyn->d_val);
set_te32(&dyn->d_val, asl_delta + d_val);
}
}
// Relocate dynsym (DT_SYMTAB) which is below xct_va
Elf32_Sym *sym = const_cast<Elf32_Sym *>(dynsym);
unsigned off_dynsym = get_te32(&sec_dynsym->sh_offset);
unsigned sz_dynsym = get_te32(&sec_dynsym->sh_size);
for (int j = sz_dynsym / sizeof(Elf32_Sym); --j>=0; ++sym) {
unsigned symval = get_te32(&sym->st_value);
unsigned symsec = get_te16(&sym->st_shndx);
if (Elf32_Sym::SHN_UNDEF != symsec
&& Elf32_Sym::SHN_ABS != symsec
&& xct_off <= symval) {
set_te32(&sym->st_value, asl_delta + symval);
}
}
fo->seek(off_dynsym, SEEK_SET); fo->rewrite(dynsym, sz_dynsym);
set_te32(&ehdri.e_shoff, sz_elf_hdrs);
fo->seek(0, SEEK_SET); fo->rewrite(&ehdri, sizeof(ehdri));
// Relocate Phdr virtual addresses, but not physical offsets and sizes
/* Elf32_Phdr * */ phdr = phdri;
for (int j = e_phnum; --j>=0; ++phdr) {
unsigned offset = get_te32(&phdr->p_offset);
if (xct_off <= offset) { // above the extra page
//set_te32(&phdr->p_offset, asl_delta + offset); // physical
unsigned addr = get_te32(&phdr->p_paddr);
set_te32(&phdr->p_paddr, asl_delta + addr);
addr = get_te32(&phdr->p_vaddr);
set_te32(&phdr->p_vaddr, asl_delta + addr);
}
// .p_filesz,.p_memsz are updated in ::pack3
}
fo->rewrite(phdri, e_phnum * sizeof(Elf32_Phdr)); // adjacent to Ehdr
// Relocate Shdr; and Rela, Rel (below xct_off)
Elf32_Shdr *shdr = shdri;
unsigned sz_shstrtab = get_te32(&sec_strndx->sh_size);
for (int j = e_shnum; --j>=0; ++shdr) {
unsigned sh_type = get_te32(&shdr->sh_type);
unsigned sh_size = get_te32(&shdr->sh_size);
unsigned sh_offset = get_te32(&shdr->sh_offset);
unsigned sh_entsize = get_te32(&shdr->sh_entsize);
if (xct_off <= sh_offset) {
set_te32(&shdr->sh_offset, asl_delta + sh_offset);
uint32_t addr = get_te32(&shdr->sh_addr);
set_te32(&shdr->sh_addr, asl_delta + addr);
}
if (Elf32_Shdr::SHT_RELA== sh_type) {
if (sizeof(Elf32_Rela) != sh_entsize) {
char msg[50];
snprintf(msg, sizeof(msg), "bad Rela.sh_entsize %u", sh_entsize);
throwCantPack(msg);
}
Elf32_Rela *rela = (Elf32_Rela *)(sh_offset + file_image);
for (int k = sh_size / sh_entsize; --k >= 0; ++rela) {
unsigned r_offset = get_te32(&rela->r_offset);
if (xct_off <= r_offset) {
set_te32(&rela->r_offset, asl_delta + r_offset);
}
}
fo->seek(sh_offset, SEEK_SET);
fo->rewrite(sh_offset + file_image, sh_size);
}
if (Elf32_Shdr::SHT_REL == sh_type) {
if (sizeof(Elf32_Rel) != sh_entsize) {
char msg[50];
snprintf(msg, sizeof(msg), "bad Rel.sh_entsize %u", sh_entsize);
throwCantPack(msg);
}
Elf32_Rel *rel = (Elf32_Rel *)(sh_offset + file_image);
for (int k = sh_size / sh_entsize; --k >= 0; ++rel) {
unsigned r_offset = get_te32(&rel->r_offset);
if (xct_off <= r_offset) {
set_te32(&rel->r_offset, asl_delta + r_offset);
}
// r_offset must be in 2nd PT_LOAD; .p_vaddr was already relocated
unsigned d = elf_get_offset_from_address(asl_delta + r_offset);
unsigned w = get_te32(&file_image[d]);
unsigned r_info = get_te32(&rel->r_info);
unsigned r_type = ELF32_R_TYPE(r_info);
if (xct_off <= w
&& Elf32_Ehdr::EM_ARM == e_machine
&& ( R_ARM_RELATIVE == r_type
|| R_ARM_JUMP_SLOT == r_type)) {
set_te32(&file_image[d], asl_delta + w);
}
}
fo->seek(sh_offset, SEEK_SET);
fo->rewrite(sh_offset + file_image, sh_size);
}
}
// New copy of Shdr
set_te32(&sec_strndx->sh_offset, (e_shnum * sizeof(Elf32_Shdr)) + sz_elf_hdrs);
fo->seek(xct_off, SEEK_SET); fo->write(shdri, e_shnum * sizeof(Elf32_Shdr));
// New copy of Shdr[.e_shstrndx].[ sh_offset, +.sh_size )
fo->write(shstrtab, sz_shstrtab);
sz_elf_hdrs = fpad4(fo);
//xct_off += asl_delta; // wait until ::pack3
}
memset(&linfo, 0, sizeof(linfo));
fo->write(&linfo, sizeof(linfo));
}
// if the preserve build-id option was specified
if (opt->o_unix.preserve_build_id) {
Elf32_Shdr *shdr = NULL;
Elf32_Shdr const *tmp = shdri;
if (! shdri) {
shdr = New(Elf32_Shdr, e_shnum);
fi->seek(0,SEEK_SET);
fi->seek(ehdri.e_shoff,SEEK_SET);
fi->readx((void*)shdr,ehdri.e_shentsize*ehdri.e_shnum);
// set this so we can use elf_find_section_name
shdri = (Elf32_Shdr *)shdr;
// set this so we can use elf_find_section_name
e_shnum = get_te16(&ehdri.e_shnum);
if (!shdri) {
shdri = (Elf32_Shdr *)&file_image[get_te32(&ehdri.e_shoff)];
}
//set the shstrtab
sec_strndx = &shdri[ehdri.e_shstrndx];
sec_strndx = &shdri[get_te16(&ehdri.e_shstrndx)];
char *strtab = New(char, sec_strndx->sh_size);
fi->seek(0,SEEK_SET);
@@ -2500,22 +2714,14 @@ void PackLinuxElf32::pack1(OutputFile *fo, Filter & /*ft*/)
memset(&shdrout,0,sizeof(shdrout));
//setup the build-id
memcpy(&shdrout.shdr[1],buildid, sizeof(shdrout.shdr[1]));
memcpy(&shdrout.shdr[1], buildid, sizeof(shdrout.shdr[1]));
shdrout.shdr[1].sh_name = 1;
//setup the shstrtab
memcpy(&shdrout.shdr[2],sec_strndx, sizeof(shdrout.shdr[2]));
memcpy(&shdrout.shdr[2], sec_strndx, sizeof(shdrout.shdr[2]));
shdrout.shdr[2].sh_name = 20;
shdrout.shdr[2].sh_size = 29; //size of our static shstrtab
}
// repoint shdr in case it is used by code some where else
if (shdr) {
shdri = tmp;
delete [] shdr;
shdr = NULL;
sec_strndx = NULL;
}
}
}
@@ -2620,7 +2826,7 @@ void PackLinuxElf64::pack1(OutputFile *fo, Filter & /*ft*/)
assert(e_phoff == sizeof(Elf64_Ehdr)); // checked by canPack()
sz_phdrs = e_phnum * get_te16(&ehdri.e_phentsize);
Elf64_Phdr const *phdr = phdri;
Elf64_Phdr *phdr = phdri;
note_size = 0;
for (unsigned j=0; j < e_phnum; ++phdr, ++j) {
if (phdr->PT_NOTE64 == get_te32(&phdr->p_type)) {
@@ -2655,11 +2861,154 @@ void PackLinuxElf64::pack1(OutputFile *fo, Filter & /*ft*/)
progid = 0; // getRandomId(); not useful, so do not clutter
if (0!=xct_off) { // shared library
fi->seek(0, SEEK_SET);
fi->readx(ibuf, xct_off);
sz_elf_hdrs = xct_off;
fo->write(ibuf, xct_off);
fo->write(file_image, xct_off); // before the first SHF_EXECINSTR (typ ".plt")
if (opt->o_unix.android_shlib) {
// In order to pacify the runtime linker on Android "O" ("Oreo"),
// we will splice-in a 4KiB page that contains an "extra" copy
// of the Shdr and shstrtab.
xct_va += asl_delta;
//xct_off += asl_delta; // not yet
// Relocate PT_DYNAMIC (in 2nd PT_LOAD)
Elf64_Dyn *dyn = const_cast<Elf64_Dyn *>(dynseg);
for (; dyn->d_tag; ++dyn) {
upx_uint64_t d_tag = get_te64(&dyn->d_tag);
if (Elf64_Dyn::DT_FINI == d_tag
|| Elf64_Dyn::DT_FINI_ARRAY == d_tag
|| Elf64_Dyn::DT_INIT_ARRAY == d_tag
|| Elf64_Dyn::DT_PREINIT_ARRAY == d_tag
|| Elf64_Dyn::DT_PLTGOT == d_tag) {
upx_uint64_t d_val = get_te64(&dyn->d_val);
set_te64(&dyn->d_val, asl_delta + d_val);
}
}
// Relocate dynsym (DT_SYMTAB) which is below xct_va
Elf64_Sym *sym = const_cast<Elf64_Sym *>(dynsym);
upx_uint64_t off_dynsym = get_te64(&sec_dynsym->sh_offset);
upx_uint64_t sz_dynsym = get_te64(&sec_dynsym->sh_size);
for (int j = sz_dynsym / sizeof(Elf64_Sym); --j>=0; ++sym) {
upx_uint64_t symval = get_te64(&sym->st_value);
unsigned symsec = get_te16(&sym->st_shndx);
if (Elf64_Sym::SHN_UNDEF != symsec
&& Elf64_Sym::SHN_ABS != symsec
&& xct_off <= symval) {
set_te64(&sym->st_value, asl_delta + symval);
}
}
fo->seek(off_dynsym, SEEK_SET); fo->rewrite(dynsym, sz_dynsym);
set_te64(&ehdri.e_shoff, sz_elf_hdrs);
fo->seek(0, SEEK_SET); fo->rewrite(&ehdri, sizeof(ehdri));
// Relocate Phdr virtual addresses, but not physical offsets and sizes
/* Elf64_Phdr * */ phdr = phdri;
for (int j = e_phnum; --j>=0; ++phdr) {
upx_uint64_t offset = get_te64(&phdr->p_offset);
if (xct_off <= offset) { // above the extra page
//set_te64(&phdr->p_offset, asl_delta + offset); // physical
upx_uint64_t addr = get_te64(&phdr->p_paddr);
set_te64(&phdr->p_paddr, asl_delta + addr);
addr = get_te64(&phdr->p_vaddr);
set_te64(&phdr->p_vaddr, asl_delta + addr);
}
// .p_filesz,.p_memsz are updated in ::pack3
}
fo->rewrite(phdri, e_phnum * sizeof(Elf64_Phdr)); // adjacent to Ehdr
// Relocate Shdr; and Rela, Rel (below xct_off)
Elf64_Shdr *shdr = shdri;
upx_uint64_t sz_shstrtab = get_te64(&sec_strndx->sh_size);
for (int j = e_shnum; --j>=0; ++shdr) {
unsigned sh_type = get_te32(&shdr->sh_type);
upx_uint64_t sh_size = get_te64(&shdr->sh_size);
upx_uint64_t sh_offset = get_te64(&shdr->sh_offset);
upx_uint64_t sh_entsize = get_te64(&shdr->sh_entsize);
if (xct_off <= sh_offset) {
set_te64(&shdr->sh_offset, asl_delta + sh_offset);
upx_uint64_t addr = get_te64(&shdr->sh_addr);
set_te64(&shdr->sh_addr, asl_delta + addr);
}
if (Elf64_Shdr::SHT_RELA == sh_type) {
if (sizeof(Elf64_Rela) != sh_entsize) {
char msg[50];
snprintf(msg, sizeof(msg), "bad Rela.sh_entsize %lu", (long)sh_entsize);
throwCantPack(msg);
}
n_jmp_slot = 0;
plt_off = ~0ull;
Elf64_Rela *rela = (Elf64_Rela *)(sh_offset + file_image);
for (int k = sh_size / sh_entsize; --k >= 0; ++rela) {
upx_uint64_t r_addend = get_te64(&rela->r_addend);
upx_uint64_t r_offset = get_te64(&rela->r_offset);
upx_uint64_t r_info = get_te64(&rela->r_info);
unsigned r_type = ELF64_R_TYPE(r_info);
if (xct_off <= r_offset) {
set_te64(&rela->r_offset, asl_delta + r_offset);
}
if (Elf64_Ehdr::EM_AARCH64 == e_machine) {
if (R_AARCH64_RELATIVE == r_type) {
if (xct_off <= r_addend) {
set_te64(&rela->r_addend, asl_delta + r_addend);
}
}
if (R_AARCH64_JUMP_SLOT == r_type) {
// .rela.plt contains offset of the "first time" target
if (plt_off > (asl_delta + r_offset)) {
plt_off = (asl_delta + r_offset);
}
upx_uint64_t d = elf_get_offset_from_address(asl_delta + r_offset);
upx_uint64_t w = get_te64(&file_image[d]);
if (xct_off <= w) {
set_te64(&file_image[d], asl_delta + w);
}
++n_jmp_slot;
}
}
}
fo->seek(sh_offset, SEEK_SET);
fo->rewrite(sh_offset + file_image, sh_size);
}
if (Elf64_Shdr::SHT_REL == sh_type) {
if (sizeof(Elf64_Rel) != sh_entsize) {
char msg[50];
snprintf(msg, sizeof(msg), "bad Rel.sh_entsize %lu", (long)sh_entsize);
throwCantPack(msg);
}
Elf64_Rel *rel = (Elf64_Rel *)(sh_offset + file_image);
for (int k = sh_size / sh_entsize; --k >= 0; ++rel) {
upx_uint64_t r_offset = get_te64(&rel->r_offset);
if (xct_off <= r_offset) {
set_te64(&rel->r_offset, asl_delta + r_offset);
}
// r_offset must be in 2nd PT_LOAD; .p_vaddr was already relocated
upx_uint64_t d = elf_get_offset_from_address(asl_delta + r_offset);
upx_uint64_t w = get_te64(&file_image[d]);
upx_uint64_t r_info = get_te32(&rel->r_info);
unsigned r_type = ELF64_R_TYPE(r_info);
if (xct_off <= w
&& Elf64_Ehdr::EM_AARCH64 == e_machine
&& ( R_AARCH64_RELATIVE == r_type
|| R_AARCH64_JUMP_SLOT == r_type)) {
set_te64(&file_image[d], asl_delta + w);
}
}
fo->seek(sh_offset, SEEK_SET);
fo->rewrite(sh_offset + file_image, sh_size);
}
}
// New copy of Shdr
set_te64(&sec_strndx->sh_offset, (e_shnum * sizeof(Elf64_Shdr)) + sz_elf_hdrs);
fo->seek(xct_off, SEEK_SET); fo->write(shdri, e_shnum * sizeof(Elf64_Shdr));
// New copy of Shdr[.e_shstrndx].[ sh_offset, +.sh_size )
fo->write(shstrtab, sz_shstrtab);
sz_elf_hdrs = fpad4(fo);
//xct_off += asl_delta; // wait until ::pack3
}
memset(&linfo, 0, sizeof(linfo));
fo->write(&linfo, sizeof(linfo));
}
@@ -2667,27 +3016,12 @@ void PackLinuxElf64::pack1(OutputFile *fo, Filter & /*ft*/)
// only execute if option present
if (opt->o_unix.preserve_build_id) {
// set this so we can use elf_find_section_name
e_shnum = ehdri.e_shnum;
// there is a class member similar to this, but I did not
// want to assume it would be available
Elf64_Shdr const *tmp = shdri;
Elf64_Shdr *shdr = NULL;
if (! shdri) {
shdr = New(Elf64_Shdr, e_shnum);
fi->seek(0,SEEK_SET);
fi->seek(ehdri.e_shoff,SEEK_SET);
fi->readx((void*)shdr,ehdri.e_shentsize*ehdri.e_shnum);
// set this so we can use elf_find_section_name
shdri = (Elf64_Shdr *)shdr;
e_shnum = get_te16(&ehdri.e_shnum);
if (!shdri) {
shdri = (Elf64_Shdr *)&file_image[get_te32(&ehdri.e_shoff)];
}
//set the shstrtab
sec_strndx = &shdri[ehdri.e_shstrndx];
sec_strndx = &shdri[get_te16(&ehdri.e_shstrndx)];
char *strtab = New(char, sec_strndx->sh_size);
fi->seek(0,SEEK_SET);
@@ -2710,20 +3044,14 @@ void PackLinuxElf64::pack1(OutputFile *fo, Filter & /*ft*/)
memset(&shdrout,0,sizeof(shdrout));
//setup the build-id
memcpy(&shdrout.shdr[1],buildid, sizeof(shdrout.shdr[1]));
memcpy(&shdrout.shdr[1], buildid, sizeof(shdrout.shdr[1]));
shdrout.shdr[1].sh_name = 1;
//setup the shstrtab
memcpy(&shdrout.shdr[2],sec_strndx, sizeof(shdrout.shdr[2]));
memcpy(&shdrout.shdr[2], sec_strndx, sizeof(shdrout.shdr[2]));
shdrout.shdr[2].sh_name = 20;
shdrout.shdr[2].sh_size = 29; //size of our static shstrtab
}
if (shdr) {
shdri = tmp;
delete [] shdr;
shdr = NULL;
}
}
}
@@ -2822,7 +3150,7 @@ int PackLinuxElf32::pack2(OutputFile *fo, Filter &ft)
? (sizeof(Elf32_Ehdr) + sz_phdrs) // main executable
: xct_off; // shared library
if (ft.id < 0x40) {
// FIXME: ?? ft.addvalue += delta;
// FIXME: ?? ft.addvalue += asl_delta;
}
x.offset += delta;
x.size -= delta;
@@ -2931,7 +3259,7 @@ int PackLinuxElf64::pack2(OutputFile *fo, Filter &ft)
? (sizeof(Elf64_Ehdr) + sz_phdrs) // main executable
: xct_off; // shared library
if (ft.id < 0x40) {
// FIXME: ?? ft.addvalue += delta;
// FIXME: ?? ft.addvalue += asl_delta;
}
x.offset += delta;
x.size -= delta;
@@ -3096,82 +3424,6 @@ void PackLinuxElf32::pack4(OutputFile *fo, Filter &ft)
fo->write(buildid_data,shdrout.shdr[1].sh_size);
}
if (0!=xct_off) { // shared library
if (opt->o_unix.android_shlib && shdri) { // dlopen() checks Elf32_Shdr vs Elf32_Phdr
unsigned load0_hi = ~0u;
Elf32_Phdr const *phdr = phdri;
for (unsigned j = 0; j < e_phnum; ++j, ++phdr) {
unsigned load0_lo = get_te32(&phdr->p_vaddr);
unsigned load0_sz = get_te32(&phdr->p_memsz);
if (PT_LOAD32==get_te32(&phdr->p_type)
&& (xct_off - load0_lo) < load0_sz) {
load0_hi = load0_lo + load0_sz;
break;
}
}
MemBuffer smap(e_shnum); smap.clear(); // smap[0] = 0; // SHN_UNDEF
MemBuffer snew(e_shnum * sizeof(*shdri));
Elf32_Shdr const *shdr = shdri;
unsigned k = 0;
for (unsigned j = 0; j < e_shnum; ++j, ++shdr) {
// Select some Elf32_Shdr by .sh_type
unsigned const type = get_te32(&shdr->sh_type);
unsigned const flags = get_te32(&shdr->sh_flags);
if (((Elf32_Shdr::SHT_STRTAB == type) && (Elf32_Shdr::SHF_ALLOC & flags))
|| ((Elf32_Shdr::SHT_DYNAMIC == type) && (Elf32_Shdr::SHF_ALLOC & flags))
|| ((Elf32_Shdr::SHT_ARM_ATTRIBUTES == type))
|| ( ((1+ Elf32_Shdr::SHT_LOOS) <= type) // SHT_ANDROID_REL
&& ((2+ Elf32_Shdr::SHT_LOOS) >= type)) // SHT_ANDROID_RELA
) {
Elf32_Shdr *const sptr = k + (Elf32_Shdr *)(void *)snew;
unsigned va = get_te32(&shdr->sh_addr);
if (xct_off <= va && va <= load0_hi) {
throwCantPack("Android-required Shdr in packed region");
}
*sptr = *shdr; // copy old Elf32_Shdr
smap[j] = 1+ k++; // for later forwarding
}
}
if (k && fo) {
unsigned const new_shoff = fpad4(fo);
unsigned xtra_off = ((1+ k) * sizeof(*shdri)) + new_shoff; // 1+: shdr_undef
set_te32(&ehdri.e_shoff, new_shoff);
set_te16(&ehdri.e_shentsize, sizeof(*shdri));
set_te16(&ehdri.e_shnum, 1+ k);
Elf32_Shdr shdr_undef; memset(&shdr_undef, 0, sizeof(shdr_undef));
fo->write(&shdr_undef, sizeof(shdr_undef));
unsigned arm_attr_offset = 0;
unsigned arm_attr_size = 0;
for (unsigned j = 0; j < k; ++j) { // forward .sh_link
Elf32_Shdr *const sptr = j + (Elf32_Shdr *)(void *)snew;
unsigned const type = get_te32(&sptr->sh_type);
// work-around for https://bugs.launchpad.net/bugs/1712938
if (Elf32_Shdr::SHT_ARM_ATTRIBUTES == type) {
arm_attr_offset = get_te32(&sptr->sh_offset);
arm_attr_size = get_te32(&sptr->sh_size);
set_te32(&sptr->sh_offset, xtra_off);
xtra_off += get_te32(&sptr->sh_size);
}
if (Elf32_Shdr::SHT_STRTAB == type) { // any SHT_STRTAB should work
set_te16(&elfout.ehdr.e_shstrndx, 1+ j); // 1+: shdr_undef
set_te16( &ehdri.e_shstrndx, 1+ j); // 1+: shdr_undef
}
unsigned sh_offset = get_te32(&sptr->sh_offset);
if (xct_off <= sh_offset) {
set_te32(&sptr->sh_offset, so_slide + sh_offset);
}
sptr->sh_name = 0; // we flushed .e_shstrndx
set_te16(&sptr->sh_link, smap[sptr->sh_link]);
set_te16(&sptr->sh_info, smap[sptr->sh_info]); // ?
}
fo->write(snew, k * sizeof(*shdri));
if (arm_attr_offset) {
fo->write(&file_image[arm_attr_offset], arm_attr_size);
}
}
}
}
// Cannot pre-round .p_memsz. If .p_filesz < .p_memsz, then kernel
// tries to make .bss, which requires PF_W.
// But strict SELinux (or PaX, grSecurity) disallows PF_W with PF_X.
@@ -3234,82 +3486,6 @@ void PackLinuxElf64::pack4(OutputFile *fo, Filter &ft)
fo->write(buildid_data,shdrout.shdr[1].sh_size);
}
if (0!=xct_off) { // shared library
if (opt->o_unix.android_shlib && shdri) { // dlopen() checks Elf64_Shdr vs Elf64_Phdr
unsigned load0_hi = ~0u;
Elf64_Phdr const *phdr = phdri;
for (unsigned j = 0; j < e_phnum; ++j, ++phdr) {
upx_uint64_t load0_lo = get_te64(&phdr->p_vaddr);
upx_uint64_t load0_sz = get_te64(&phdr->p_memsz);
if (PT_LOAD64==get_te32(&phdr->p_type)
&& (xct_off - load0_lo) < load0_sz) {
load0_hi = load0_lo + load0_sz;
break;
}
}
MemBuffer smap(e_shnum); smap.clear(); // smap[0] = 0; // SHN_UNDEF
MemBuffer snew(e_shnum * sizeof(*shdri));
Elf64_Shdr const *shdr = shdri;
unsigned k = 0;
for (unsigned j = 0; j < e_shnum; ++j, ++shdr) {
// Select some Elf64_Shdr by .sh_type
unsigned const type = get_te32(&shdr->sh_type);
unsigned const flags = get_te32(&shdr->sh_flags);
if (((Elf64_Shdr::SHT_STRTAB == type) && (Elf64_Shdr::SHF_ALLOC & flags))
|| ((Elf64_Shdr::SHT_DYNAMIC == type) && (Elf64_Shdr::SHF_ALLOC & flags))
|| ((Elf64_Shdr::SHT_ARM_ATTRIBUTES == type))
|| ( ((1+ Elf64_Shdr::SHT_LOOS) <= type) // SHT_ANDROID_REL
&& ((2+ Elf64_Shdr::SHT_LOOS) >= type)) // SHT_ANDROID_RELA
) {
Elf64_Shdr *const sptr = k + (Elf64_Shdr *)(void *)snew;
upx_uint64_t va = get_te64(&shdr->sh_addr);
if (xct_off <= va && va <= load0_hi) {
throwCantPack("Android-required Shdr in packed region");
}
*sptr = *shdr; // copy old Elf64_Shdr
smap[j] = 1+ k++; // for later forwarding
}
}
if (k && fo) {
unsigned long const new_shoff = fpad8(fo);
unsigned long xtra_off = ((1+ k) * sizeof(*shdri)) + new_shoff; // 1+: shdr_undef
set_te64(&ehdri.e_shoff, new_shoff);
set_te16(&ehdri.e_shentsize, sizeof(*shdri));
set_te16(&ehdri.e_shnum, 1+ k);
Elf64_Shdr shdr_undef; memset(&shdr_undef, 0, sizeof(shdr_undef));
fo->write(&shdr_undef, sizeof(shdr_undef));
unsigned long arm_attr_offset = 0;
unsigned long arm_attr_size = 0;
for (unsigned j = 0; j < k; ++j) { // forward .sh_link
Elf64_Shdr *const sptr = j + (Elf64_Shdr *)(void *)snew;
unsigned const type = get_te32(&sptr->sh_type);
// work-around for https://bugs.launchpad.net/bugs/1712938
if (Elf64_Shdr::SHT_ARM_ATTRIBUTES == type) {
arm_attr_offset = get_te64(&sptr->sh_offset);
arm_attr_size = get_te64(&sptr->sh_size);
set_te64(&sptr->sh_offset, xtra_off);
xtra_off += get_te64(&sptr->sh_size);
}
if (Elf64_Shdr::SHT_STRTAB == type) { // any SHT_STRTAB should work
set_te16(&elfout.ehdr.e_shstrndx, 1+ j); // 1+: shdr_undef
set_te16( &ehdri.e_shstrndx, 1+ j); // 1+: shdr_undef
}
upx_uint64_t sh_offset = get_te64(&sptr->sh_offset);
if (xct_off <= sh_offset) {
set_te64(&sptr->sh_offset, so_slide + sh_offset);
}
sptr->sh_name = 0; // we flushed .e_shstrndx
set_te16(&sptr->sh_link, smap[sptr->sh_link]);
set_te16(&sptr->sh_info, smap[sptr->sh_info]); // ?
}
fo->write(snew, k * sizeof(*shdri));
if (arm_attr_offset) {
fo->write(&file_image[arm_attr_offset], arm_attr_size);
}
}
}
}
// Cannot pre-round .p_memsz. If .p_filesz < .p_memsz, then kernel
// tries to make .bss, which requires PF_W.
// But strict SELinux (or PaX, grSecurity) disallows PF_W with PF_X.
@@ -3321,7 +3497,7 @@ void PackLinuxElf64::pack4(OutputFile *fo, Filter &ft)
if (0!=xct_off) { // shared library
fo->rewrite(&ehdri, sizeof(ehdri));
fo->rewrite(phdri, e_phnum * sizeof(*phdri));
fo->seek(xct_off, SEEK_SET);
fo->seek(sz_elf_hdrs, SEEK_SET);
fo->rewrite(&linfo, sizeof(linfo));
}
else {
@@ -3865,7 +4041,7 @@ PackLinuxElf64::elf_find_dynamic(unsigned int key) const
&& Elf64_Dyn::DT_NULL!=dynp->d_tag; ++dynp) if (get_te64(&dynp->d_tag)==key) {
upx_uint64_t const t= elf_get_offset_from_address(get_te64(&dynp->d_val));
if (t) {
return (size_t)t + file_image;
return &((unsigned char const *)file_image)[(size_t)t];
}
break;
}