loader: Build the preloader for ARM

André Hentschel nerv at dawncrow.de
Mon Jan 31 16:13:51 CST 2011


it took me long, it was hard work, but here it is :)
i know the modulo stuff is not the nicest way, but there's no other chance...
---
 configure.ac       |    1 +
 loader/main.c      |    4 +-
 loader/preloader.c |  147 ++++++++++++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 147 insertions(+), 5 deletions(-)

diff --git a/configure.ac b/configure.ac
index 1a8c5b0..967ee76 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1887,6 +1887,7 @@ case $host_os in
   linux*)
     case $host_cpu in
       *i[[3456789]]86*) AC_SUBST(EXTRA_BINARIES,"wine-preloader") ;;
+      arm*) AC_SUBST(EXTRA_BINARIES,"wine-preloader") ;;
       x86_64*) AC_SUBST(EXTRA_BINARIES,"wine64-preloader") ;;
     esac
     ;;
diff --git a/loader/main.c b/loader/main.c
index 8434d81..daf302b 100644
--- a/loader/main.c
+++ b/loader/main.c
@@ -167,11 +167,11 @@ static int pre_exec(void)
     return 1;
 }
 
-#elif defined(__linux__) && defined(__x86_64__)
+#elif defined(__linux__) && (defined(__x86_64__) || defined(__ARM_EABI__))
 
 static int pre_exec(void)
 {
-    return 1;  /* we have a preloader on x86-64 */
+    return 1;  /* we have a preloader */
 }
 
 #elif (defined(__FreeBSD__) || defined (__FreeBSD_kernel__)) && defined(__i386__)
diff --git a/loader/preloader.c b/loader/preloader.c
index e1c2276..412bd2f 100644
--- a/loader/preloader.c
+++ b/loader/preloader.c
@@ -108,7 +108,7 @@
 
 static struct wine_preload_info preload_info[] =
 {
-#ifdef __i386__
+#if defined(__i386__) || defined(__ARM_EABI__)
     { (void *)0x00000000, 0x00010000 },  /* low 64k */
     { (void *)0x00010000, 0x00100000 },  /* DOS area */
     { (void *)0x00110000, 0x67ef0000 },  /* low memory area */
@@ -430,6 +430,125 @@ SYSCALL_NOERR( wld_geteuid, 107 /* SYS_geteuid */ );
 gid_t wld_getegid(void);
 SYSCALL_NOERR( wld_getegid, 108 /* SYS_getegid */ );
 
+#elif defined(__ARM_EABI__)
+
+/*
+ * 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, #136\n\t"    /* allocate some space for extra aux values */
+                  "push {r0}\n\t"       /* orig stack pointer */
+                  "mov r0, sp\n\t"      /* ptr to orig stack pointer */
+                  "mov r6, r0\n\t"
+                  "bl wld_start\n\t"
+                  "mov sp, r6\n\t"
+                  "pop {r1}\n\t"
+                  "mov sp, r1\n\t"
+                  "push {r0}\n\t"
+                  "mov r0, #0\n\t"
+                  "mov r1, #0\n\t"
+                  "mov r2, #0\n\t"
+                  "mov r3, #0\n\t"
+                  "mov r4, #0\n\t"
+                  "mov r5, #0\n\t"
+                  "mov r6, #0\n\t"
+                  "mov r7, #0\n\t"
+                  "mov r8, #0\n\t"
+                  "mov r9, #0\n\t"
+                  "mov r10, #0\n\t"
+                  "mov r11, #0\n\t"
+                  "mov r12, #0\n\t"
+                  "pop {PC}\n\t")
+
+#define SYSCALL_FUNC( name, nr ) \
+    __ASM_GLOBAL_FUNC( name, \
+                       "push {LR}\n\t" \
+                       "push {r7}\n\t" \
+                       "mov r7, #" #nr "\n\t" \
+                       "swi #0\n\t" \
+                       "pop {r7}\n\t" \
+                       "cmp r0, #0\n\t" \
+                       "bge.n 1f\n\t" \
+                       "mov r1, #0x1000\n\t" \
+                       "sub r1, r1, #2\n\t" \
+                       "eor r1, r1, #-1\n\t" \
+                       "cmp r0, r1\n\t" \
+                       "blt.n 1f\n\t" \
+                       "mov.w r0, #-1\n\t" \
+                       "1: pop {PC}\n\t" )
+
+#define SYSCALL_NOERR( name, nr ) \
+    __ASM_GLOBAL_FUNC( name, \
+                       "push {r7,LR}\n\t" \
+                       "mov r7, #" #nr "\n\t" \
+                       "swi #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_open( const char *name, int flags );
+SYSCALL_FUNC( wld_open, 5 /* SYS_open */ );
+
+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, off_t pgoffset );
+    __ASM_GLOBAL_FUNC( wld_mmap2,
+                       "mov ip, sp\n\t"
+                       "stmfd sp!, {r4, r5, r6, r7, LR}\n\t"
+                       "mov r7, #192\n\t"       /* SYS_mmap2 */
+                       "ldmfd ip, {r4, r5, r6}\n\t"
+                       "mov     r5, r6\n\t"
+                       "swi 0x0\n\t"
+                       "cmp r0, #0\n\t"
+                       "bge.n 1f\n\t"
+                       "mov r1, #0x1000\n\t"
+                       "sub r1, r1, #2\n\t"
+                       "eor r1, r1, #-1\n\t"
+                       "cmp r0, r1\n\t"
+                       "blt.n 1f\n\t"
+                       "mov.w r0, #-1\n\t"
+                       "1: ldmfd sp!, {r4, r5, r6, r7, PC}\n\t" )
+
+void *wld_mmap( void *start, size_t len, int prot, int flags, int fd, off_t offset )
+{
+    /* On ARM EABI we don't have mmap, so recalculate the offset into pages and use mmap2 */
+    offset /= 0x1000;
+    return wld_mmap2(start, len, prot, flags, fd, offset);
+}
+
+int wld_mprotect( const void *addr, size_t len, int prot );
+SYSCALL_FUNC( wld_mprotect, 125 /* SYS_mprotect */ );
+
+int wld_prctl( int code, int 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 */ );
+
 #else
 #error preloader not implemented for this CPU
 #endif
@@ -457,6 +576,25 @@ static inline void *wld_memset( void *dest, int val, size_t len )
 }
 
 /*
+ * wld_modulo
+ *
+ *  on ARM, gcc tries to link to __aeabi_uidivmod for modulo
+ *  that function is a workaround for that problem
+ */
+unsigned int wld_modulo( const unsigned int a, const Elf32_Word b )
+{
+#ifdef __ARM_EABI__
+    unsigned int i;
+    if(b > a) return a;
+    for(i = 0; i * b < a + b; i++)
+        if(i * b > a - b) return (a - i * b);
+    return 0;
+#else
+    return a % b;
+#endif
+}
+
+/*
  * wld_printf - just the basics
  *
  *  %x prints a hex number
@@ -700,6 +838,9 @@ static void map_so_lib( const char *name, struct wld_link_map *l)
 #elif defined(__x86_64__)
     if( header->e_machine != EM_X86_64 )
         fatal_error("%s: not an x86-64 ELF binary... don't know how to load it\n", name );
+#elif defined(__ARM_EABI__)
+    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]))
@@ -985,7 +1126,7 @@ static void *find_symbol( const ElfW(Phdr) *phdr, int num, const char *var, int
         const Elf32_Word *buckets = (const Elf32_Word *)(bitmask + nwords);
         const Elf32_Word *chains  = buckets + nbuckets - symbias;
 
-        if (!(idx = buckets[hash % nbuckets])) return NULL;
+        if (!(idx = buckets[wld_modulo(hash, nbuckets)])) return NULL;
         do
         {
             if ((chains[idx] & ~1u) == (hash & ~1u) &&
@@ -1001,7 +1142,7 @@ static void *find_symbol( const ElfW(Phdr) *phdr, int num, const char *var, int
         const Elf_Symndx *buckets = hashtab + 2;
         const Elf_Symndx *chains  = buckets + nbuckets;
 
-        for (idx = buckets[hash % nbuckets]; idx != STN_UNDEF; idx = chains[idx])
+        for (idx = buckets[wld_modulo(hash, nbuckets)]; idx != STN_UNDEF; idx = chains[idx])
         {
             if (symtab[idx].st_info == ELF32_ST_INFO( STB_GLOBAL, type ) &&
                 !wld_strcmp( strings + symtab[idx].st_name, var ))
-- 

Best Regards, André Hentschel



More information about the wine-patches mailing list