[PATCH v3] loader: Add support for ARM linux in the preloader
Since 28fe84da45bea7de56539b50eac8ebcec54342de, the main exe image must be mappable at its desired base address, which essentially requires the preloader. This fixes the original issue that lead to https://bugs.winehq.org/show_bug.cgi?id=51539. Signed-off-by: Martin Storsjö <martin(a)martin.st> --- v3: Provide a trivial implementation of __aeabi_uidivmod to avoid relying on extra sources. --- configure.ac | 4 +- dlls/ntdll/unix/loader.c | 6 +- loader/preloader.c | 124 ++++++++++++++++++++++++++++++++++++++- 3 files changed, 126 insertions(+), 8 deletions(-) diff --git a/configure.ac b/configure.ac index b5d3217f2a0..c018c89f748 100644 --- a/configure.ac +++ b/configure.ac @@ -940,7 +940,7 @@ case $host_os in WINEPRELOADER_LDFLAGS="-static -nostartfiles -nodefaultlibs -Wl,-Ttext=0x7d400000" case $host_cpu in - *i[[3456789]]86* | x86_64 | *aarch64*) + *i[[3456789]]86* | x86_64 | *aarch64* | arm*) WINE_TRY_CFLAGS([-Wl,-Ttext-segment=0x7bc00000], [case $host_os in freebsd* | kfreebsd*-gnu) WINELOADER_LDFLAGS="$WINELOADER_LDFLAGS -Wl,-Ttext-segment=0x60000000" ;; @@ -2161,7 +2161,7 @@ esac case $host_os in linux*) case $host_cpu in - *i[[3456789]]86*|x86_64*|*aarch64*) + *i[[3456789]]86*|x86_64*|*aarch64*|arm*) test "$wine_binary" = wine || WINE_IGNORE_FILE("loader/wine-preloader") WINELOADER_PROGRAMS="$WINELOADER_PROGRAMS $wine_binary-preloader" ;; diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c index 0e580cd7556..14f26cbd520 100644 --- a/dlls/ntdll/unix/loader.c +++ b/dlls/ntdll/unix/loader.c @@ -2155,11 +2155,7 @@ static int pre_exec(void) int temp; check_vmsplit( &temp ); -#ifdef __i386__ - return 1; /* we have a preloader on x86 */ -#else - return 0; -#endif + return 1; /* we have a preloader on x86/arm */ } #elif defined(__linux__) && (defined(__x86_64__) || defined(__aarch64__)) diff --git a/loader/preloader.c b/loader/preloader.c index bb16ecd96a4..86308484182 100644 --- a/loader/preloader.c +++ b/loader/preloader.c @@ -111,7 +111,7 @@ static struct wine_preload_info preload_info[] = { -#ifdef __i386__ +#if defined(__i386__) || defined(__arm__) { (void *)0x00000000, 0x00010000 }, /* low 64k */ { (void *)0x00010000, 0x00100000 }, /* DOS area */ { (void *)0x00110000, 0x67ef0000 }, /* low memory area */ @@ -536,6 +536,125 @@ SYSCALL_NOERR( wld_geteuid, 175 /* SYS_geteuid */ ); gid_t wld_getegid(void); SYSCALL_NOERR( wld_getegid, 177 /* SYS_getegid */ ); +#elif defined(__arm__) + +void *thread_data[256]; + +/* + * The _start function is the entry and exit point of this program + * + * It calls wld_start, passing a pointer to the args it receives + * then jumps to the address wld_start returns. + */ +void _start(void); +extern char _end[]; +__ASM_GLOBAL_FUNC(_start, + "mov r0, sp\n\t" + "sub sp, sp, #144\n\t" /* allocate some space for extra aux values */ + "str r0, [sp]\n\t" /* orig stack pointer */ + "ldr r0, =thread_data\n\t" + "movw r7, #0x0005\n\t" /* __ARM_NR_set_tls */ + "movt r7, #0xf\n\t" /* __ARM_NR_set_tls */ + "svc #0\n\t" + "mov r0, sp\n\t" /* ptr to orig stack pointer */ + "bl wld_start\n\t" + "ldr r1, [sp]\n\t" /* new stack pointer */ + "mov sp, r1\n\t" + "mov lr, r0\n\t" + "mov r0, #0\n\t" + "mov r1, #0\n\t" + "mov r2, #0\n\t" + "mov r3, #0\n\t" + "mov r12, #0\n\t" + "bx lr\n\t" + ".ltorg\n\t") + +#define SYSCALL_FUNC( name, nr ) \ + __ASM_GLOBAL_FUNC( name, \ + "push {r4-r5,r7,lr}\n\t" \ + "ldr r4, [sp, #16]\n\t" \ + "ldr r5, [sp, #20]\n\t" \ + "mov r7, #" #nr "\n\t" \ + "svc #0\n\t" \ + "cmn r0, #4096\n\t" \ + "it hi\n\t" \ + "movhi r0, #-1\n\t" \ + "pop {r4-r5,r7,pc}\n\t" ) + +#define SYSCALL_NOERR( name, nr ) \ + __ASM_GLOBAL_FUNC( name, \ + "push {r7,lr}\n\t" \ + "mov r7, #" #nr "\n\t" \ + "svc #0\n\t" \ + "pop {r7,pc}\n\t" ) + +void wld_exit( int code ) __attribute__((noreturn)); +SYSCALL_NOERR( wld_exit, 1 /* SYS_exit */ ); + +ssize_t wld_read( int fd, void *buffer, size_t len ); +SYSCALL_FUNC( wld_read, 3 /* SYS_read */ ); + +ssize_t wld_write( int fd, const void *buffer, size_t len ); +SYSCALL_FUNC( wld_write, 4 /* SYS_write */ ); + +int wld_openat( int dirfd, const char *name, int flags ); +SYSCALL_FUNC( wld_openat, 322 /* SYS_openat */ ); + +int wld_open( const char *name, int flags ) +{ + return wld_openat(-100 /* AT_FDCWD */, name, flags); +} + +int wld_close( int fd ); +SYSCALL_FUNC( wld_close, 6 /* SYS_close */ ); + +void *wld_mmap2( void *start, size_t len, int prot, int flags, int fd, int offset ); +SYSCALL_FUNC( wld_mmap2, 192 /* SYS_mmap2 */ ); + +void *wld_mmap( void *start, size_t len, int prot, int flags, int fd, off_t offset ) +{ + return wld_mmap2(start, len, prot, flags, fd, offset >> 12); +} + +int wld_mprotect( const void *addr, size_t len, int prot ); +SYSCALL_FUNC( wld_mprotect, 125 /* SYS_mprotect */ ); + +int wld_prctl( int code, long arg ); +SYSCALL_FUNC( wld_prctl, 172 /* SYS_prctl */ ); + +uid_t wld_getuid(void); +SYSCALL_NOERR( wld_getuid, 24 /* SYS_getuid */ ); + +gid_t wld_getgid(void); +SYSCALL_NOERR( wld_getgid, 47 /* SYS_getgid */ ); + +uid_t wld_geteuid(void); +SYSCALL_NOERR( wld_geteuid, 49 /* SYS_geteuid */ ); + +gid_t wld_getegid(void); +SYSCALL_NOERR( wld_getegid, 50 /* SYS_getegid */ ); + +unsigned long long __aeabi_uidivmod(unsigned int num, unsigned int den) +{ + unsigned int bit = 1; + unsigned int quota = 0; + if (!den) + wld_exit(1); + while (den < num && !(den & 0x80000000)) { + den <<= 1; + bit <<= 1; + } + do { + if (den <= num) { + quota |= bit; + num -= den; + } + bit >>= 1; + den >>= 1; + } while (bit); + return ((unsigned long long)num << 32) | quota; +} + #else #error preloader not implemented for this CPU #endif @@ -809,6 +928,9 @@ static void map_so_lib( const char *name, struct wld_link_map *l) #elif defined(__aarch64__) if( header->e_machine != EM_AARCH64 ) fatal_error("%s: not an aarch64 ELF binary... don't know how to load it\n", name ); +#elif defined(__arm__) + if( header->e_machine != EM_ARM ) + fatal_error("%s: not an arm ELF binary... don't know how to load it\n", name ); #endif if (header->e_phnum > sizeof(loadcmds)/sizeof(loadcmds[0])) -- 2.25.1
participants (1)
-
Martin Storsjö