Damjan Jovanovic : ntdll: Fix ELF initializer address calculations on FreeBSD.

Alexandre Julliard julliard at winehq.org
Mon May 18 15:00:13 CDT 2020


Module: wine
Branch: master
Commit: 0fd3f0266e05f6afa710fa2b5a254b0ed88bac0f
URL:    https://source.winehq.org/git/wine.git/?a=commit;h=0fd3f0266e05f6afa710fa2b5a254b0ed88bac0f

Author: Damjan Jovanovic <damjan.jov at gmail.com>
Date:   Sun May 17 12:49:08 2020 +0200

ntdll: Fix ELF initializer address calculations on FreeBSD.

dlinfo() doesn't conform to a standard, each platform implements in differently,
if at all. On GNU and NetBSD its l_addr field returned is the "relocbase",
the relative offset between addresses wanted by the file and addresses obtained
in memory (==0 when no relocation occurred), which we add to d_un.d_ptr to
obtain the memory address where the initializer is. On FreeBSD (and possibly
Solaris) this won't work, as l_addr is the "mapbase" instead, the absolute
starting memory address where the binary was loaded, resulting in wrong
calculations and crashes on startup as we call into wrong addresses.

Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=49139
Signed-off-by: Damjan Jovanovic <damjan.jov at gmail.com>
Signed-off-by: Alexandre Julliard <julliard at winehq.org>

---

 dlls/ntdll/loader.c | 40 ++++++++++++++++++++++++++++++++++++----
 1 file changed, 36 insertions(+), 4 deletions(-)

diff --git a/dlls/ntdll/loader.c b/dlls/ntdll/loader.c
index fa65ae1e9f..cdb25fc78c 100644
--- a/dlls/ntdll/loader.c
+++ b/dlls/ntdll/loader.c
@@ -1317,6 +1317,35 @@ static void call_tls_callbacks( HMODULE module, UINT reason )
     }
 }
 
+#ifdef __FreeBSD__
+/* The PT_LOAD segments are sorted in increasing order, and the first
+ * starts at the beginning of the ELF file. By parsing the file, we can
+ * find that first PT_LOAD segment, from which we can find the base
+ * address it wanted, and knowing mapbase where the binary was actually
+ * loaded, use them to work out the relocbase offset. */
+static BOOL get_relocbase(caddr_t mapbase, caddr_t *relocbase)
+{
+    Elf_Half i;
+#ifdef _WIN64
+    const Elf64_Ehdr *elf_header = (Elf64_Ehdr*) mapbase;
+#else
+    const Elf32_Ehdr *elf_header = (Elf32_Ehdr*) mapbase;
+#endif
+    const Elf_Phdr *prog_header = (const Elf_Phdr *)(mapbase + elf_header->e_phoff);
+
+    for (i = 0; i < elf_header->e_phnum; i++)
+    {
+         if (prog_header->p_type == PT_LOAD)
+         {
+             caddr_t desired_base = (caddr_t)((prog_header->p_vaddr / prog_header->p_align) * prog_header->p_align);
+             *relocbase = (caddr_t) (mapbase - desired_base);
+             return TRUE;
+         }
+         prog_header++;
+    }
+    return FALSE;
+}
+#endif
 
 /*************************************************************************
  *              call_constructors
@@ -1339,14 +1368,17 @@ static void call_constructors( WINE_MODREF *wm )
     if (dlinfo( wm->so_handle, RTLD_DI_LINKMAP, &map ) == -1) return;
     for (dyn = map->l_ld; dyn->d_tag; dyn++)
     {
-#define GET_PTR(base,ptr)  ((ptr) > (base) ? (ptr) : (base) + (ptr))
+        caddr_t relocbase = (caddr_t)map->l_addr;
+
+#ifdef __FreeBSD__  /* FreeBSD doesn't relocate l_addr */
+        if (!get_relocbase(map->l_addr, &relocbase)) return;
+#endif
         switch (dyn->d_tag)
         {
-        case 0x60009990: init_array = (void *)GET_PTR( map->l_addr, dyn->d_un.d_ptr ); break;
+        case 0x60009990: init_array = (void *)(relocbase + dyn->d_un.d_val); break;
         case 0x60009991: init_arraysz = dyn->d_un.d_val; break;
-        case 0x60009992: init_func = (void *)GET_PTR( map->l_addr, dyn->d_un.d_ptr ); break;
+        case 0x60009992: init_func = (void *)(relocbase + dyn->d_un.d_val); break;
         }
-#undef GET_PTR
     }
 
     TRACE( "%s: got init_func %p init_array %p %lu\n", debugstr_us( &wm->ldr.BaseDllName ),




More information about the wine-cvs mailing list