[Bug 246561] [PATCH] rtld-elf: dlinfo() returns wrong address in RTLD_DI_LINKMAP's l_addr (breaking Wine, gdb, etc.)
bugzilla-noreply at freebsd.org
bugzilla-noreply at freebsd.org
Tue May 19 03:12:36 UTC 2020
https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=246561
Bug ID: 246561
Summary: [PATCH] rtld-elf: dlinfo() returns wrong address in
RTLD_DI_LINKMAP's l_addr (breaking Wine, gdb, etc.)
Product: Base System
Version: 12.1-STABLE
Hardware: Any
OS: Any
Status: New
Severity: Affects Some People
Priority: ---
Component: bin
Assignee: bugs at FreeBSD.org
Reporter: damjan.jov at gmail.com
Several low-level applications need access to ELF internals. For example Wine
recently began calling ELF initializers for Winelibs within itself instead of
having the dynamic linker do it. To do this, it calls the dlinfo() function
with request RTLD_DI_LINKMAP, and searches the .dynamic section pointed to by
link_map.l_ld for a custom tag with Winelib initializers.
However ELF libraries can be loaded at a different address to the one they
expected (relocation), so the pointer stored in the initializer tag is not
necessarily pointing to the correct address. We have to add the offset from the
expected base address to the actual base address to it to determine the
relocated address (in the rtld-elf code, this is the "relocbase" variable).
On most implementations of dlinfo(), the link_map.l_addr field stores this
offset. However on FreeBSD, link_map.l_addr stores the absolute base address
where the library was loaded instead ("mapbase"). A separate l_offs field was
apparently added to return relocbase, but it only exists on MIPS.
Wine recently began crashing on startup because it was adding l_addr to the
pointers in initializers, but since l_addr is mapbase instead of relocbase on
FreeBSD, it was relocating them to wrong addresses.
NetBSD changed their dynamic linker to return relocbase in l_addr in 2002
(https://github.com/NetBSD/src/commit/d1351c627c5f4d5ac41a3f680243d57293e0ce1f).
GNU always returned relocbase. Illumos returns relocbase. Only FreeBSD returns
mapbase.
Can we please change rtld-elf to return relocbase? Either we can change l_addr
itself to return relocbase like NetBSD did:
diff --git a/libexec/rtld-elf/rtld.c b/libexec/rtld-elf/rtld.c
index 2d15560fb908..647626e3a41d 100644
--- a/libexec/rtld-elf/rtld.c
+++ b/libexec/rtld-elf/rtld.c
@@ -3951,7 +3951,7 @@ linkmap_add(Obj_Entry *obj)
struct link_map *prev;
obj->linkmap.l_name = obj->path;
- obj->linkmap.l_addr = obj->mapbase;
+ obj->linkmap.l_addr = obj->relocbase;
obj->linkmap.l_ld = obj->dynamic;
#ifdef __mips__
/* GDB needs load offset on MIPS to use the symbols */
or we can remove that "#ifdef __mips__" further down and return l_offs on all
platforms, and patch applications to use that instead (like was apparently
already done for GDB on MIPS). (Of course, being an ABI-breaking change, it can
only be done in CURRENT.)
I eventually found a way to work out relocbase from mapbase within Wine
(https://source.winehq.org/git/wine.git/blobdiff/0f31e48e9ba28ba8c0aee0678567d6b47e3c63aa..0fd3f0266e05f6afa710fa2b5a254b0ed88bac0f:/dlls/ntdll/loader.c),
and the latest Wine Git is no longer crashing on startup. But I am not
convinced that approach will always work, because it requires the first PT_LOAD
segment to start at address 0 in the file, and I don't see that being required
by the ELF specification.
Detailed discussion: https://bugs.winehq.org/show_bug.cgi?id=49139
--
You are receiving this mail because:
You are the assignee for the bug.
More information about the freebsd-bugs
mailing list