mirror of https://github.com/upx/upx.git
Unify 64-bit upx_main() stub for shlib with 32-bit i386
modified: stub/src/amd64-linux.elf-so_main.c
This commit is contained in:
parent
93f420939e
commit
a2fa417af9
|
@ -40,7 +40,7 @@ extern size_t Pwrite(unsigned, void const *, size_t);
|
|||
|
||||
extern void f_int3(int arg);
|
||||
|
||||
#define DEBUG 0
|
||||
#define DEBUG 1
|
||||
|
||||
#ifndef DEBUG //{
|
||||
#define DEBUG 0
|
||||
|
@ -242,33 +242,51 @@ ERR_LAB
|
|||
|
||||
extern int memfd_create(const char *name, unsigned int flags);
|
||||
|
||||
#define ElfW(sym) Elf64_ ## sym
|
||||
|
||||
#define nullptr (void *)0
|
||||
|
||||
extern char *upx_mmap_and_fd( // x86_64 Android emulator of i386 is not faithful
|
||||
void *ptr // desired address
|
||||
, unsigned len // also pre-allocate space in file
|
||||
, char *pathname // 0 ==> call get_upxfn_path, which stores if 1st time
|
||||
);
|
||||
|
||||
#if defined(__x86_64__) //{
|
||||
static char *
|
||||
make_hatch_x86_64(
|
||||
Elf64_Phdr const *const phdr,
|
||||
char *
|
||||
make_hatch(
|
||||
ElfW(Phdr) const *const phdr,
|
||||
char *next_unc,
|
||||
unsigned frag_mask
|
||||
)
|
||||
{
|
||||
char *hatch = 0;
|
||||
unsigned *hatch = 0;
|
||||
unsigned code[3] = {
|
||||
0x5e5f050f, // syscall; pop %arg1{%rdi}; pop %arg2{%rsi}
|
||||
0xff3e585a, // pop %arg3{%rdx}; pop %rax; notrack jmp
|
||||
0x909090e0 // *%rax; nop; nop; nop
|
||||
};
|
||||
DPRINTF("make_hatch %%p %%p %%x\\n", phdr, next_unc, frag_mask);
|
||||
if (phdr->p_type==PT_LOAD && phdr->p_flags & PF_X) {
|
||||
next_unc += phdr->p_memsz - phdr->p_filesz; // Skip over local .bss
|
||||
frag_mask &= -(long)next_unc; // bytes left on page
|
||||
if (6 <= frag_mask) {
|
||||
hatch = next_unc;
|
||||
(( long *)hatch)[0] = 0x5e5f050f; // syscall; pop %arg1{%rdi}; pop %arg2{%rsi}
|
||||
((short *)hatch)[2] = 0xc35a; // pop %arg3{%rdx}; ret
|
||||
if (sizeof(code) <= frag_mask) {
|
||||
hatch = (unsigned *)next_unc;
|
||||
hatch[0] = code[0];
|
||||
hatch[1] = code[1];
|
||||
hatch[2] = code[2];
|
||||
}
|
||||
else { // Does not fit at hi end of .text, so must use a new page "permanently"
|
||||
int mfd = memfd_create(addr_string("upx"), 0); // the directory entry
|
||||
Pwrite(mfd, addr_string("\x0f\x05\x5f\x5e\x5a\xc3"), 6);
|
||||
hatch = Pmap(0, 6, PROT_READ|PROT_EXEC, MAP_PRIVATE, mfd, 0);
|
||||
unsigned long fdmap = (long)upx_mmap_and_fd((void *)0, sizeof(code), nullptr);
|
||||
unsigned mfd = -1+ (0xfff& fdmap);
|
||||
write(mfd, &code, sizeof(code));
|
||||
hatch = mmap((void *)(fdmap & ~0xffful), sizeof(code),
|
||||
PROT_READ|PROT_EXEC, MAP_PRIVATE, mfd, 0);
|
||||
close(mfd);
|
||||
}
|
||||
}
|
||||
DPRINTF("hatch=%%p\\n", hatch);
|
||||
return hatch;
|
||||
return (char *)hatch;
|
||||
}
|
||||
#elif defined(__powerpc64__) //}{
|
||||
static unsigned
|
||||
|
@ -278,8 +296,8 @@ ORRX(unsigned ra, unsigned rs, unsigned rb) // or ra,rs,rb
|
|||
}
|
||||
|
||||
static char *
|
||||
make_hatch_ppc64(
|
||||
Elf64_Phdr const *const phdr,
|
||||
make_hatch(
|
||||
ElfW(Phdr) const *const phdr,
|
||||
char *next_unc,
|
||||
unsigned frag_mask
|
||||
)
|
||||
|
@ -314,8 +332,8 @@ make_hatch_ppc64(
|
|||
}
|
||||
#elif defined(__aarch64__) //{
|
||||
static char *
|
||||
make_hatch_arm64(
|
||||
Elf64_Phdr const *const phdr,
|
||||
make_hatch(
|
||||
ElfW(Phdr) const *const phdr,
|
||||
char *next_unc,
|
||||
unsigned frag_mask
|
||||
)
|
||||
|
@ -365,26 +383,24 @@ make_hatch_arm64(
|
|||
|(REP8(PROT_WRITE) & EXP8(PF_W)) \
|
||||
) >> ((pf & (PF_R|PF_W|PF_X))<<2) ))
|
||||
|
||||
#define nullptr (void *)0
|
||||
|
||||
#undef PAGE_MASK
|
||||
static unsigned long
|
||||
get_PAGE_MASK(void) // the mask which KEEPS the page, discards the offset
|
||||
get_page_mask(void) // the mask which KEEPS the page, discards the offset
|
||||
{
|
||||
unsigned long rv = ~0xffful; // default to (PAGE_SIZE == 4KiB)
|
||||
int fd = openat(0, addr_string("/proc/self/auxv"), O_RDONLY, 0);
|
||||
if (0 <= fd) {
|
||||
Elf64_auxv_t data[40];
|
||||
Elf64_auxv_t *end = &data[read(fd, data, sizeof(data)) / sizeof(data[0])];
|
||||
ElfW(auxv_t) data[40];
|
||||
ElfW(auxv_t) *end = &data[read(fd, data, sizeof(data)) / sizeof(data[0])];
|
||||
close(fd);
|
||||
Elf64_auxv_t *ptr; for (ptr = &data[0]; ptr < end ; ++ptr) {
|
||||
ElfW(auxv_t) *ptr; for (ptr = &data[0]; ptr < end ; ++ptr) {
|
||||
if (AT_PAGESZ == ptr->a_type) {
|
||||
rv = (0u - ptr->a_un.a_val);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
DPRINTF("get_PAGE_MASK= %%p\\n", rv);
|
||||
DPRINTF("get_page_mask= %%p\\n", rv);
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
@ -410,12 +426,47 @@ underlay(unsigned size, char *ptr, unsigned len);
|
|||
|
||||
// Exchange the bits with values 4 (PF_R, PROT_EXEC) and 1 (PF_X, PROT_READ)
|
||||
// Use table lookup into a PIC-string that pre-computes the result.
|
||||
unsigned PF_to_PROT(Elf64_Phdr const *phdr)
|
||||
unsigned PF_to_PROT(ElfW(Phdr) const *phdr)
|
||||
{
|
||||
return 7& addr_string("@\x04\x02\x06\x01\x05\x03\x07")
|
||||
[phdr->p_flags & (PF_R|PF_W|PF_X)];
|
||||
}
|
||||
|
||||
unsigned
|
||||
fini_SELinux(
|
||||
unsigned size,
|
||||
char *ptr,
|
||||
ElfW(Phdr) const *phdr,
|
||||
unsigned mfd,
|
||||
ElfW(Addr) base
|
||||
)
|
||||
{
|
||||
if (phdr->p_flags & PF_X) {
|
||||
// Map the contents of mfd as per *phdr.
|
||||
Punmap(ptr, size);
|
||||
Pmap(ptr, size, PF_to_PROT(phdr), MAP_FIXED|MAP_PRIVATE, mfd, 0);
|
||||
close(mfd);
|
||||
}
|
||||
else { // easy
|
||||
Pprotect( (char *)(phdr->p_vaddr + base), phdr->p_memsz, PF_to_PROT(phdr));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned
|
||||
prep_SELinux(unsigned size, char *ptr, unsigned len) // returns mfd
|
||||
{
|
||||
// Cannot set PROT_EXEC except via mmap() into a region (Linux "vma")
|
||||
// that has never had PROT_WRITE. So use a Linux-only "memory file"
|
||||
// to hold the contents.
|
||||
char *val = upx_mmap_and_fd(ptr, size, nullptr);
|
||||
unsigned mfd = 0xfff & (unsigned)(long)val;
|
||||
val -= mfd; --mfd;
|
||||
if (len)
|
||||
Pwrite(mfd, ptr, len); // Save lo fragment of contents on first page.
|
||||
return mfd;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
long argc;
|
||||
char **argv;
|
||||
|
@ -423,7 +474,7 @@ typedef struct {
|
|||
} So_args;
|
||||
|
||||
typedef struct {
|
||||
unsigned off_reloc; // distance back to &Elf64_Ehdr
|
||||
unsigned off_reloc; // distance back to &ElfW(Ehdr)
|
||||
unsigned off_user_DT_INIT;
|
||||
unsigned off_xct_off; // where un-compressed bytes end
|
||||
unsigned off_info; // xct_off: {l_info; p_info; b_info; compressed data)
|
||||
|
@ -437,10 +488,10 @@ void *
|
|||
upx_so_main( // returns &escape_hatch
|
||||
So_info *so_info,
|
||||
So_args *so_args,
|
||||
Elf64_Ehdr *elf_tmp // scratch for Elf64_Ehdr and Elf64_Phdrs
|
||||
ElfW(Ehdr) *elf_tmp // scratch for ElfW(Ehdr) and ElfW(Phdrs)
|
||||
)
|
||||
{
|
||||
unsigned long const PAGE_MASK = get_PAGE_MASK();
|
||||
unsigned long const page_mask = get_page_mask();
|
||||
char *const va_load = (char *)&so_info->off_reloc - so_info->off_reloc;
|
||||
So_info so_infc; // So_info Copy
|
||||
memcpy(&so_infc, so_info, sizeof(so_infc)); // before de-compression overwrites
|
||||
|
@ -457,7 +508,7 @@ upx_so_main( // returns &escape_hatch
|
|||
// Copy compressed data before de-compression overwrites it.
|
||||
char *const sideaddr = mmap(nullptr, cpr_len, PROT_WRITE|PROT_READ,
|
||||
MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
|
||||
DPRINTF("sideaddr=%%p\\n", sideaddr);
|
||||
DPRINTF("&sideaddr=%%p\\n", &sideaddr);
|
||||
memcpy(sideaddr, cpr_ptr, cpr_len);
|
||||
|
||||
// Transition to copied data
|
||||
|
@ -479,110 +530,82 @@ upx_so_main( // returns &escape_hatch
|
|||
// De-compress from remaining [sideaddr, +sidelen).
|
||||
// Pprotect(,, PF_TO_PROT(.p_flags));
|
||||
|
||||
// Get the uncompressed Elf64_Ehdr and Elf64_Phdr
|
||||
// Get the uncompressed ElfW(Ehdr) and ElfW(Phdr)
|
||||
// The first b_info is aligned, so direct access to fields is OK.
|
||||
Extent x1 = {binfo->sz_unc, (char *)elf_tmp}; // destination
|
||||
Extent x0 = {binfo->sz_cpr + sizeof(*binfo), (char *)binfo}; // source
|
||||
unpackExtent(&x0, &x1); // de-compress _Ehdr and _Phdrs; x0.buf is updated
|
||||
|
||||
Elf64_Phdr const *phdr = (Elf64_Phdr *)(1+ elf_tmp);
|
||||
Elf64_Phdr const *const phdrN = &phdr[elf_tmp->e_phnum];
|
||||
while (phdr->p_type != PT_LOAD) ++phdr; // skip PT_PHDR if any
|
||||
Elf64_Addr const base = (Elf64_Addr)va_load - phdr->p_vaddr;
|
||||
DPRINTF("base=%%p\\n", base);
|
||||
|
||||
if (phdr->p_flags & PF_X) {
|
||||
int mfd = memfd_create(addr_string("upx"), 0);
|
||||
unsigned mfd_len = 0ul - PAGE_MASK;
|
||||
Pwrite(mfd, elf_tmp, binfo->sz_unc); // de-compressed Elf_Ehdr and Elf_Phdrs
|
||||
Pwrite(mfd, binfo->sz_unc + va_load, mfd_len - binfo->sz_unc); // rest of 1st page
|
||||
|
||||
Punmap(va_load, mfd_len); // make SELinux forget any previous protection
|
||||
Elf64_Addr va_mfd = (Elf64_Addr)Pmap(va_load, mfd_len, PF_to_PROT(phdr),
|
||||
MAP_FIXED|MAP_PRIVATE, mfd, 0); (void)va_mfd;
|
||||
close(mfd);
|
||||
}
|
||||
ElfW(Phdr) const *phdr = (ElfW(Phdr) *)(1+ elf_tmp);
|
||||
ElfW(Phdr) const *const phdrN = &phdr[elf_tmp->e_phnum];
|
||||
|
||||
// Process each read-only PT_LOAD.
|
||||
// A read+write PT_LOAD might be relocated by rtld before de-compression,
|
||||
// so it cannot be compressed.
|
||||
struct b_info al_bi; // for aligned data from binfo
|
||||
void *hatch = nullptr;
|
||||
ElfW(Addr) base = 0;
|
||||
int n_load = 0;
|
||||
|
||||
for (; phdr < phdrN; ++phdr)
|
||||
if ( phdr->p_type == PT_LOAD && !(phdr->p_flags & PF_W)) {
|
||||
DPRINTF("phdr@%%p p_offset=%%p p_vaddr=%%p p_filesz=%%p p_memsz=%%p binfo=%%p\\n",
|
||||
phdr, phdr->p_offset, phdr->p_vaddr, phdr->p_filesz, phdr->p_memsz, x0.buf);
|
||||
|
||||
if ((phdr->p_filesz + phdr->p_offset) <= so_infc.off_xct_off) {
|
||||
continue; // below compressed region
|
||||
}
|
||||
Elf64_Addr const pfx = (so_infc.off_xct_off < phdr->p_offset)
|
||||
? 0 // entire PT_LOAD is compressed
|
||||
: so_infc.off_xct_off - phdr->p_offset ; // below xct_off is not
|
||||
if (phdr->p_type == PT_LOAD && !(phdr->p_flags & PF_W)) {
|
||||
unsigned hi_offset = phdr->p_filesz + phdr->p_offset;
|
||||
struct b_info al_bi; // for aligned data from binfo
|
||||
// Need un-aligned read of b_info to determine compression sizes.
|
||||
x0.size = sizeof(struct b_info);
|
||||
xread(&x0, (char *)&al_bi, x0.size); // aligned binfo
|
||||
x0.buf -= sizeof(al_bi); // back up (the xread() was a peek)
|
||||
DPRINTF("next1 pfx=%%x binfo@%%p (%%p %%p %%p)\\n", pfx, x0.buf,
|
||||
al_bi.sz_unc, al_bi.sz_cpr, *(unsigned *)(void *)&al_bi.b_method);
|
||||
x1.size = hi_offset - xct_off;
|
||||
x1.buf = (void *)(hi_offset + base - al_bi.sz_unc);
|
||||
x0.size = al_bi.sz_cpr;
|
||||
|
||||
// Using .p_memsz implicitly handles .bss via MAP_ANONYMOUS.
|
||||
// Omit any non-tcompressed prefix (below xct_off)
|
||||
x1.buf = (char *)(pfx + phdr->p_vaddr + base);
|
||||
x1.size = phdr->p_memsz - pfx;
|
||||
|
||||
unsigned const frag = (phdr->p_vaddr + pfx) & ~PAGE_MASK; // lo fragment on page
|
||||
x1.buf -= frag;
|
||||
x1.size += frag;
|
||||
DPRINTF("phdr(%%p %%p) xct_off=%%x frag=%%x\\n", x1.buf, x1.size, xct_off, frag);
|
||||
|
||||
int mfd = 0;
|
||||
char *mfd_addr = 0;
|
||||
if (phdr->p_flags & PF_X) { // SELinux
|
||||
// Cannot set PROT_EXEC except via mmap() into a region (Linux "vma")
|
||||
// that has never had PROT_WRITE. So use a Linux-only "memory file"
|
||||
// to hold the contents.
|
||||
mfd = memfd_create(addr_string("upx"), 0); // the directory entry
|
||||
ftruncate(mfd, x1.size); // Allocate the pages in the file.
|
||||
Pwrite(mfd, x1.buf, frag); // Save lo fragment of contents on first page.
|
||||
Punmap(x1.buf, x1.size);
|
||||
mfd_addr = Pmap(x1.buf, x1.size, PROT_READ|PROT_WRITE, MAP_FIXED|MAP_SHARED, mfd, 0);
|
||||
DPRINTF("mfd_addr= %%p\\n", mfd_addr); // Re-use the address space
|
||||
}
|
||||
else {
|
||||
underlay(x1.size, x1.buf, frag); // also makes PROT_WRITE
|
||||
if (!base) {
|
||||
base = (ElfW(Addr))va_load - phdr->p_vaddr;
|
||||
DPRINTF("base=%%p\\n", base);
|
||||
}
|
||||
DPRINTF("phdr@%%p p_offset=%%p p_vaddr=%%p p_filesz=%%p p_memsz=%%p\\n",
|
||||
phdr, phdr->p_offset, phdr->p_vaddr, phdr->p_filesz, phdr->p_memsz);
|
||||
DPRINTF("x0=%%p x1=%%p\\n", &x0, &x1);
|
||||
//my_bkpt((void const *)0x1230, phdr, &x0, &x1);
|
||||
|
||||
x1.buf += frag;
|
||||
x1.size = al_bi.sz_unc;
|
||||
x0.size = al_bi.sz_cpr + sizeof(struct b_info);
|
||||
DPRINTF("before unpack x0=(%%p %%p x1=(%%p %%p)\\n", x0.size, x0.buf, x1.size, x1.buf);
|
||||
unpackExtent(&x0, &x1); // updates x0 and x1
|
||||
DPRINTF(" after unpack x0=(%%p %%p x1=(%%p %%p)\\n", x0.size, x0.buf, x1.size, x1.buf);
|
||||
|
||||
if (!hatch && phdr->p_flags & PF_X) {
|
||||
#if defined(__x86_64) //{
|
||||
hatch = make_hatch_x86_64(phdr, x1.buf, ~PAGE_MASK);
|
||||
#elif defined(__powerpc64__) //}{
|
||||
hatch = make_hatch_ppc64(phdr, x1.buf, ~PAGE_MASK);
|
||||
#elif defined(__aarch64__) //}{
|
||||
hatch = make_hatch_arm64(phdr, x1.buf, ~PAGE_MASK);
|
||||
#endif //}
|
||||
unsigned frag = ~page_mask & (unsigned)(long)x1.buf;
|
||||
unsigned mfd = 0;
|
||||
if (!n_load) { // 1st PT_LOAD is special.
|
||||
// Already ELF headers are in place, perhaps already followed
|
||||
// by non-compressed loader tables below xct_off.
|
||||
if (xct_off < hi_offset) { // 1st PT_LOAD also has compressed code, too
|
||||
if (phdr->p_flags & PF_X) {
|
||||
mfd = prep_SELinux(x1.size, x1.buf, frag);
|
||||
}
|
||||
else {
|
||||
underlay(x1.size, x1.buf, page_mask); // also makes PROT_WRITE
|
||||
}
|
||||
unpackExtent(&x0, &x1);
|
||||
if (!hatch && phdr->p_flags & PF_X) {
|
||||
hatch = make_hatch(phdr, x1.buf, ~page_mask);
|
||||
}
|
||||
my_bkpt((void const *)0x1235, &x1);
|
||||
fini_SELinux(x1.size, x1.buf, phdr, mfd, base); // FIXME: x1 changed!
|
||||
}
|
||||
}
|
||||
|
||||
if (phdr->p_flags & PF_X) { // SELinux
|
||||
// Map the contents of mfd as per *phdr.
|
||||
DPRINTF("mfd mmap addr=%%p len=%%p\\n", (phdr->p_vaddr + base + pfx), al_bi.sz_unc);
|
||||
Punmap(mfd_addr, frag + al_bi.sz_unc); // Discard RW mapping; mfd has the bytes
|
||||
Pmap((char *)(phdr->p_vaddr + base + pfx), al_bi.sz_unc, PF_to_PROT(phdr),
|
||||
MAP_FIXED|MAP_PRIVATE, mfd, 0);
|
||||
close(mfd);
|
||||
}
|
||||
else { // easy
|
||||
Pprotect( (char *)(phdr->p_vaddr + base), phdr->p_memsz, PF_to_PROT(phdr));
|
||||
else { // 2nd and later PT_LOADs
|
||||
x1.buf = (void *)(phdr->p_vaddr + base);
|
||||
x1.size = phdr->p_filesz;
|
||||
if (phdr->p_flags & PF_X) {
|
||||
mfd = prep_SELinux(x1.size, x1.buf, frag);
|
||||
}
|
||||
else {
|
||||
underlay(x1.size, x1.buf, page_mask); // also makes PROT_WRITE
|
||||
}
|
||||
unpackExtent(&x0, &x1);
|
||||
if (!hatch && phdr->p_flags &PF_X) {
|
||||
hatch = make_hatch(phdr, x1.buf, ~page_mask);
|
||||
}
|
||||
fini_SELinux(al_bi.sz_unc, (void *)(phdr->p_vaddr + base), phdr, mfd, base);
|
||||
}
|
||||
++n_load;
|
||||
}
|
||||
|
||||
DPRINTF("Punmap sideaddr=%%p cpr_len=%%p\\n", sideaddr, cpr_len);
|
||||
Punmap(sideaddr, cpr_len);
|
||||
DPRINTF("calling user DT_INIT %%p\\n", dt_init);
|
||||
dt_init(so_args->argc, so_args->argv, so_args->envp);
|
||||
|
|
Loading…
Reference in New Issue