From 29922c22765bb65352d48a3922c8fc07eebe7353 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Storsj=C3=B6?= Date: Tue, 3 Aug 2021 23:24:12 +0300 Subject: [PATCH] loader: Add support for ARM linux in the preloader. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since 28fe84da45bea7de56539b50eac8ebcec54342de, the main exe image must be mappable at its desired base address, which essentially requires the preloader. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=51539 Signed-off-by: Martin Storsjö Signed-off-by: Alexandre Julliard --- configure | 4 +- configure.ac | 4 +- dlls/ntdll/unix/loader.c | 6 +- loader/preloader.c | 124 ++++++++++++++++++++++++++++++++++++++- 4 files changed, 128 insertions(+), 10 deletions(-) diff --git a/configure b/configure index d3c60b4ff4c..1be0aa9d217 100755 --- a/configure +++ b/configure @@ -9473,7 +9473,7 @@ fi 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*) { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports -Wl,-Ttext-segment=0x7bc00000" >&5 $as_echo_n "checking whether the compiler supports -Wl,-Ttext-segment=0x7bc00000... " >&6; } if ${ac_cv_cflags__Wl__Ttext_segment_0x7bc00000+:} false; then : @@ -18025,7 +18025,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_fn_append_file CONFIGURE_TARGETS "loader/wine-preloader" WINELOADER_PROGRAMS="$WINELOADER_PROGRAMS $wine_binary-preloader" ;; diff --git a/configure.ac b/configure.ac index 5d9ca31099e..69e27147088 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 8888376f2f0..55fc73d2d06 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]))