Prepare to emulate memfd_create() on 32-bit Android (ARM and i386)

modified:   stub/src/include/linux.h
	new file:   stub/src/upxfd_android.c
	new file:   stub/src/upxfd_create.c
This commit is contained in:
John Reiser 2023-09-28 12:44:49 -07:00
parent 290dd0c5d2
commit f63a673a8b
3 changed files with 157 additions and 0 deletions

View File

@ -554,6 +554,7 @@ int mprotect(void const *, size_t, int);
int open(char const *, unsigned, unsigned);
int openat(int fd, char const *, unsigned, unsigned);
ssize_t read(int, void *, size_t);
int unlink(char const *);
ssize_t write(int, void const *, size_t);
#endif /*}*/

View File

@ -0,0 +1,104 @@
/* upxfd_android.c -- workaround memfd_create for 32-bit Android
This file is part of the UPX executable compressor.
Copyright (C) 2023 John F. Reiser
All Rights Reserved.
*/
#if defined(__i386__) //}{
#define addr_string(string) ({ \
char const *str; \
asm("call 0f; .asciz \"" string "\"; 0: pop %0" \
/*out*/ : "=r"(str) ); \
str; \
})
#elif defined(__arm__) //}{
#define addr_string(string) ({ \
char const *str; \
asm("bl 0f; .string \"" string "\"; .balign 4; 0: mov %0,lr" \
/*out*/ : "=r"(str) \
/* in*/ : \
/*und*/ : "lr"); \
str; \
})
#else //}{
# error; // only: __arm__ or __i386__ (for ARM emulator)
#endif //}
// Upper half of ASCII (chars 0100-0177) are all legal in a Linux filename.
// So the shortest code is " return 0100 + (077 & x); "
// But the 5 chars which follow 'Z' ("[\\]^_") or 'z' ("{|}~\x7F")
// look ugly, so substitute digits.
static unsigned sixbit(unsigned x)
{
unsigned y = 037 & x; // "upper case"
x &= 077;
if (033 <= y) { // last 5 chars in each 32 are ugly
if (040 & x) // "lower case"
x += '4' - 'z'; // "56789" follows 'z';
else
x += '/' - 'Z'; // "01234" follows 'Z';
}
x += 0100; // upper half ASCII: "@ABC"...
return x;
}
#define S_IRWXU 00700 /* rwx------ User Read-Write-eXecute */
extern void *alloca(unsigned size);
#include "include/linux.h" // syscalls; i386 inlines via "int 0x80"
extern void *mempcpy(void *dst, void const *src, unsigned len);
// Early 32-bit Android lacks memfd_create.
// Try /data/data/$APP_NAME/cache/upxAAA
// where APP_NAME is discovered as basename(argv[0])
// and argv[0] is guessed from /proc/self/cmdline.
// Also 32-bit Android has inconsistent __NR_ftruncate,
// so use direct write()
//
#define BUFLEN 0x2000 /* 4KiB for PATH_MAX; 8KiB for zeroing */
#define ENOSPC 28 /* no space left on device */
int upxfd_android(unsigned len) // returns fd with (.st_size = len); else -ENOSPC
{
char *buf = alloca(BUFLEN);
char *p = mempcpy(&buf[0], addr_string("/data/data/"), 11); // '/' sentinel at end
int fd = open(addr_string("/proc/self/cmdline"), O_RDONLY, 0);
int rlen = read(fd, p, BUFLEN);
if (rlen < 0) {
return -1;
}
while (*p) { // advance to end of argv[0]
if (' '==*p) break;
++p;
}
{
char *app_end = p;
while ('/' != *--p) ; // last component of argv[0]
p = mempcpy(&buf[10], p, app_end - p);
}
p = mempcpy(p, addr_string("/cache/upx"), 10);
pid_t pid = getpid();
p[0] = sixbit(pid >> 0*6);
p[1] = sixbit(pid >> 1*6);
p[2] = sixbit(pid >> 2*6);
p[3]='\0';
fd = open(&buf[0], O_CREAT|O_EXCL|O_RDWR, S_IRWXU);
if (fd < 0) {
return -1;
}
unlink(&buf[0]);
extern void *memset(void *dst, int c, unsigned);
memset(buf, 0, BUFLEN);
while (0 < len) {
int x = (len < BUFLEN) ? len : BUFLEN;
if (x != write(fd, buf, BUFLEN)) {
return -ENOSPC;
}
len -= x;
}
lseek(fd, 0, SEEK_SET);
return fd;
}

View File

@ -0,0 +1,52 @@
/* upxfd_create.c -- workaround for 32-bit Android
This file is part of the UPX executable compressor.
Copyright (C) 2023 John F. Reiser
All Rights Reserved.
*/
// Like memfd_create, but early 32-bit Android lacks.
// Also 32-bit Android has inconsistent __NR_ftruncate,
// so use direct write().
extern void *alloca(unsigned long size);
#include "include/linux.h" // syscalls; i386 inlines via "int 0x80"
// 1. Try memfd_create
// 2. If Android or emulator, try /data/data/$APP_NAME/cache/upxAAA
// where APP_NAME is discovered as basename($(< /proc/self/cmdline))
// If Android, then write zeroes to desired length, then lseek back to offset 0.
// (Except make_hatch() just writes the actual data, and closes the file.)
// If not Android then ftruncate() to desired length.
// Return (mapped_addr | (1+ fd))
//
extern int upxfd_android(int len);
void *upxfd_create(void *addr, unsigned len)
{
char str_upx[] = "upx";
int fd = memfd_create(str_upx, 0);
#if defined(__arm__) || defined(__i386__) //{ workaround: android & emulators
if (fd < 0) {
fd = upxfd_android(len); // also emulates ftruncate(fd, len)
}
#else //}{
if (0 > ftruncate(fd, len)) {
return (void *)-1ul;
}
#endif //}
addr = mmap(addr, len, PROT_READ|PROT_WRITE,
(addr ? MAP_FIXED : 0)|MAP_SHARED, fd, 0);
if (PAGE_MASK <= (unsigned long)addr) {
return addr;
}
return (void *)((long)addr + (1+ (unsigned)fd));
}
#if 0 //{ test
int main(int argc, char *argv[])
{
upxfd_create(0, 100);
return 0;
}
#endif //}