Placate Android dlopen() some more.

https://github.com/upx/upx/issues/680
	modified:   p_lx_elf.cpp
This commit is contained in:
John Reiser 2023-07-04 11:28:00 -07:00 committed by Markus F.X.J. Oberhumer
parent dc76b2af97
commit c429859cbf

View File

@ -2712,6 +2712,7 @@ bool PackLinuxElf32::canPack()
// not explicitly PIE main program
if (Elf32_Ehdr::EM_ARM == e_machine // Android is common
&& !opt->o_unix.android_shlib // but not explicit
&& !saved_opt_android_shlib
) {
opt->info_mode++;
info("note: use --android-shlib if appropriate");
@ -2899,7 +2900,9 @@ bad:
throwCantPack(buf);
goto abandon;
}
if (!opt->o_unix.android_shlib) {
if (!opt->o_unix.android_shlib
&& !saved_opt_android_shlib
) {
phdr = phdri;
for (unsigned j= 0; j < e_phnum; ++phdr, ++j) {
unsigned const vaddr = get_te32(&phdr->p_vaddr);
@ -3135,6 +3138,7 @@ PackLinuxElf64::canPack()
// not explicitly PIE main program
if (Elf64_Ehdr::EM_AARCH64 == e_machine // Android is common
&& !opt->o_unix.android_shlib // but not explicit
&& !saved_opt_android_shlib
) {
opt->info_mode++;
info("note: use --android-shlib if appropriate");
@ -3315,7 +3319,9 @@ PackLinuxElf64::canPack()
throwCantPack(buf);
goto abandon;
}
if (!opt->o_unix.android_shlib) {
if (!opt->o_unix.android_shlib
&& !saved_opt_android_shlib
) {
phdr = phdri;
for (unsigned j= 0; j < e_phnum; ++phdr, ++j) {
upx_uint64_t const vaddr = get_te64(&phdr->p_vaddr);
@ -5560,7 +5566,7 @@ void PackLinuxElf32::forward_Shdrs(OutputFile *fo, Elf32_Ehdr *const eho)
if (saved_opt_android_shlib) { // Forward select _Shdr
unsigned penalty = total_out;
// Keep _Shdr for rtld data (below xct_off).
// Discard _Shdr for compressed regions.
// Discard _Shdr for compressed regions, except ".text" for gdb
// Keep _Shdr for SHF_WRITE.
// Discard _Shdr with (0==sh_addr), except _Shdr[0]
// Keep ARM_ATTRIBUTES
@ -5581,6 +5587,20 @@ void PackLinuxElf32::forward_Shdrs(OutputFile *fo, Elf32_Ehdr *const eho)
| 1u<<(0x1f & SHT_GNU_verneed)
| 1u<<(0x1f & SHT_GNU_verdef)
| 1u<<(0x1f & SHT_GNU_HASH);
u32_t xct_off_hi = 0;
Elf32_Phdr const *ptr = phdri, *ptr_end = &phdri[e_phnum];
for (; ptr < ptr_end; ++ptr) {
if (PT_LOAD32 == get_te32(&ptr->p_type)) {
u32_t hi = get_te32(&ptr->p_filesz)
+ get_te32(&ptr->p_offset);
if (xct_off < hi) {
xct_off_hi = hi;
break;
}
}
}
MemBuffer mb_ask_for(e_shnum * sizeof(eho->e_shnum));
memset(mb_ask_for, 0, mb_ask_for.getSize());
unsigned short *const ask_for = (unsigned short *)mb_ask_for.getVoidPtr();
@ -5595,11 +5615,11 @@ void PackLinuxElf32::forward_Shdrs(OutputFile *fo, Elf32_Ehdr *const eho)
for (unsigned j = 1; j < e_shnum; ++j, ++sh_in) {
unsigned sh_type = get_te32(&sh_in->sh_type);
unsigned sh_info = get_te32(&sh_in->sh_info);
unsigned sh_flags = get_te32(&sh_in->sh_flags);
unsigned sh_addr = get_te32(&sh_in->sh_addr);
unsigned sh_offset = get_te32(&sh_in->sh_offset);
unsigned sh_size = get_te32(&sh_in->sh_size);
unsigned sh_info = get_te32(&sh_in->sh_info);
if (ask_for[j]) { // Some previous _Shdr requested me
// Tell them my new index
set_te32(&sh_out0[ask_for[j]].sh_info, n_sh_out); // sh_info vs st_shndx
@ -5614,17 +5634,27 @@ void PackLinuxElf32::forward_Shdrs(OutputFile *fo, Elf32_Ehdr *const eho)
|| (want_types_mask & (1<<(0x1f & sh_type)))
) {
*sh_out = *sh_in;
if (sh_offset > xct_off) { // default: so_slide down
set_te32(&sh_out->sh_offset, so_slide + sh_offset);
}
if (sh_addr > xct_off) { // default: so_slide down
if (!(SHF_WRITE & sh_flags)) {
set_te32(&sh_out->sh_addr, so_slide + sh_addr);
if (sh_offset > xct_off) { // may slide down: earlier compression
if (sh_offset >= xct_off_hi) { // easy: so_slide down
if (sh_out->sh_addr) // change only if non-zero
set_te32(&sh_out->sh_addr, so_slide + sh_addr);
set_te32(&sh_out->sh_offset, so_slide + sh_offset);
}
else { // somewhere in compressed; try proportional (aligned)
u32_t const slice = xct_off + (~0xFu & (unsigned)(
(sh_offset - xct_off) *
((sh_offset - xct_off) / (float)(xct_off_hi - xct_off))));
set_te32(&sh_out->sh_addr, slice);
set_te32(&sh_out->sh_offset, slice);
}
u32_t const max_sz = total_out - get_te32(&sh_out->sh_offset);
if (sh_size > max_sz) { // avoid complaint "extends beyond EOF"
set_te32(&sh_out->sh_size, max_sz);
}
// BEWARE: .p_vaddr does not slide
}
if (j == e_shstrndx) { // changes Elf32_Ehdr itself
set_te16(&eho->e_shstrndx, sh_out - (Elf32_Shdr *)mb_shdro.getVoidPtr());
set_te16(&eho->e_shstrndx, sh_out -
(Elf32_Shdr *)mb_shdro.getVoidPtr());
}
if (j == e_shstrndx
|| sec_arm_attr == sh_in
@ -5704,7 +5734,7 @@ void PackLinuxElf64::forward_Shdrs(OutputFile *fo, Elf64_Ehdr *const eho)
if (saved_opt_android_shlib) { // Forward select _Shdr
unsigned penalty = total_out;
// Keep _Shdr for rtld data (below xct_off).
// Discard _Shdr for compressed regions.
// Discard _Shdr for compressed regions, except ".text" for gdb.
// Keep _Shdr for SHF_WRITE.
// Discard _Shdr with (0==sh_addr), except _Shdr[0]
// Keep ARM_ATTRIBUTES
@ -5725,6 +5755,20 @@ void PackLinuxElf64::forward_Shdrs(OutputFile *fo, Elf64_Ehdr *const eho)
| 1u<<(0x1f & SHT_GNU_verneed)
| 1u<<(0x1f & SHT_GNU_verdef)
| 1u<<(0x1f & SHT_GNU_HASH);
upx_uint64_t xct_off_hi = 0;
Elf64_Phdr const *ptr = phdri, *ptr_end = &phdri[e_phnum];
for (; ptr < ptr_end; ++ptr) {
if (PT_LOAD64 == get_te32(&ptr->p_type)) {
upx_uint64_t hi = get_te64(&ptr->p_filesz)
+ get_te64(&ptr->p_offset);
if (xct_off < hi) {
xct_off_hi = hi;
break;
}
}
}
MemBuffer mb_ask_for(e_shnum * sizeof(eho->e_shnum));
memset(mb_ask_for, 0, mb_ask_for.getSize());
unsigned short *const ask_for = (unsigned short *)mb_ask_for.getVoidPtr();
@ -5758,17 +5802,27 @@ void PackLinuxElf64::forward_Shdrs(OutputFile *fo, Elf64_Ehdr *const eho)
|| (want_types_mask & (1<<(0x1f & sh_type)))
) {
*sh_out = *sh_in;
if (sh_offset > xct_off) { // default: so_slide down
set_te64(&sh_out->sh_offset, so_slide + sh_offset);
}
if (sh_addr > xct_off) { // default: so_slide down
if (!(SHF_WRITE & sh_flags)) {
set_te64(&sh_out->sh_addr, so_slide + sh_addr);
if (sh_offset > xct_off) { // may slide down: earlier compression
if (sh_offset >= xct_off_hi) { // easy: so_slide down
if (sh_out->sh_addr) // change only if non-zero
set_te64(&sh_out->sh_addr, so_slide + sh_addr);
set_te64(&sh_out->sh_offset, so_slide + sh_offset);
}
else { // somewhere in compressed; try proportional (aligned)
u64_t const slice = xct_off + (~0xFu & (unsigned)(
(sh_offset - xct_off) *
((sh_offset - xct_off) / (float)(xct_off_hi - xct_off))));
set_te64(&sh_out->sh_addr, slice);
set_te64(&sh_out->sh_offset, slice);
}
u64_t const max_sz = total_out - get_te64(&sh_out->sh_offset);
if (sh_size > max_sz) { // avoid complaint "extends beyond EOF"
set_te64(&sh_out->sh_size, max_sz);
}
// BEWARE: .p_vaddr does not slide
}
if (j == e_shstrndx) { // changes Elf64_Ehdr itself
set_te16(&eho->e_shstrndx, sh_out - (Elf64_Shdr *)mb_shdro.getVoidPtr());
set_te16(&eho->e_shstrndx, sh_out -
(Elf64_Shdr *)mb_shdro.getVoidPtr());
}
if (j == e_shstrndx
|| sec_arm_attr == sh_in