[Bug 49139] Regression: Wine crashes on startup on FreeBSD >= 5.7

WineHQ Bugzilla wine-bugs at winehq.org
Sat May 16 13:12:41 CDT 2020


https://bugs.winehq.org/show_bug.cgi?id=49139

--- Comment #6 from Damjan Jovanovic <damjan.jov at gmail.com> ---
dlinfo() is implemented in FreeBSD's run-time dynamic linker, rtld-elf.

/usr/src/libexec/rtld-elf/rtld.h:

    /* These items are computed by map_object() or by digest_phdr(). */
    caddr_t mapbase;            /* Base address of mapped region */
    size_t mapsize;             /* Size of mapped region in bytes */
    size_t textsize;            /* Size of text segment in bytes */
    Elf_Addr vaddrbase;         /* Base address in shared object file */
    caddr_t relocbase;          /* Relocation constant = mapbase - vaddrbase */

/usr/src/libexec/rtld-elf/rtld.c, function linkmap_add():

    obj->linkmap.l_addr = obj->mapbase;

    obj->linkmap.l_ld = obj->dynamic;
#ifdef __mips__
    /* GDB needs load offset on MIPS to use the symbols */
    obj->linkmap.l_offs = obj->relocbase;
#endif

/usr/src/libexec/rtld-elf/rtld.c, function digest_dynamic1():

        case DT_INIT:
            obj->init = (Elf_Addr)(obj->relocbase + dynp->d_un.d_ptr);
            break;

So what is returned in link_map.l_addr is obj->mapbase, even though when the
dynamic linker calls initializers by itself it uses obj->relocbase instead as
the offset.

As per the comments, and as confirmed by map_object() in map_object.c:
    obj->relocbase = mapbase – base_vaddr;

we have, through maths:
relocbase = mapbase – vaddrbase
mapbase = relocbase + vaddrbase

Thus the value being returned to Wine via dlinfo() has l_addr = mapbase =
relocbase + vaddrbase, whereas internally the dynamic linker only uses
relocbase. Thus the value being given to Wine is already too high by vaddrbase
bytes, even before adding dyn->d_un.d_val.

How does Wine not crash on Linux? On
http://man7.org/linux/man-pages/man3/dlinfo.3.html the description for l_addr
in GNU's dlinfo() is:

ElfW(Addr) l_addr;  /* Difference between the
                       address in the ELF file and
                       the address in memory */

ie. GNU's l_addr is FreeBSD's l_offs – and only available on MIPS.

Thus the bug could be fixed in 2 ways:
1. Wine could use link_map.l_offs instead of link_map.l_addr, but that would
only work on MIPS until FreeBSD's rtld-elf is patched to support other
platforms.
2. FreeBSD's rtld-elf could be changed to return obj->relocbase in
link_map.l_addr instead of obj->mapbase.

What is the right approach?

On http://man7.org/linux/man-pages/man3/dlinfo.3.html it states: “This function
is a nonstandard GNU extension,” and adds “This function derives from the
Solaris function of the same name and also appears on some other systems.  The
sets of requests supported by the various implementations overlaps only
partially.”

NetBSD does what GNU does, returning relocbase, so Wine should work there:
https://github.com/NetBSD/src/blob/72845ff20a2fedf73f626b82df2f34a63bb17b77/libexec/ld.elf_so/rtld.c#L1587
    obj->linkmap.l_addr = obj->relocbase;
which was my option 2 above, which they implemented in 2002
(https://github.com/NetBSD/src/commit/d1351c627c5f4d5ac41a3f680243d57293e0ce1f).

OpenBSD doesn't even seem to have dlinfo().

Solaris's man page sounds the same as FreeBSD's
(https://docs.oracle.com/cd/E36784_01/html/E36874/dlinfo-3c.html), and I
haven't been able to find the source for OpenIndiana's dynamic linker yet to
prove otherwise.

Couldn't find anything about MacOS's dlinfo() either, but I assume it works as
per ccce5f769dbbab92b2fa872c27f1d36343a77afe.

Thus this regression seems to break many operating systems; only Linux, Mac and
NetBSD work for sure. If Wine plans to remain portable, it might also consider
option 3: implement its own portable ELF loader, and use in place of the native
one whose dlinfo() implementation (if any) doesn't always allow us to calculate
correct initializer addresses.

As it stands now, Wine > 5.6 is permanently unusable on FreeBSD and other
platforms that won't provide relocbase in dlinfo()'s l_addr. Gen's patch, which
ignores l_addr, will only work when relocbase is 0, which only happens when the
library is loaded at the address it wanted, ie. when it hasn't been relocated –
and ELF libraries are relocatable, so crashes will still happen every now and
then. Reverting the commit that caused this “regression” will get Wine running,
but (AFAIK) only because initializers are running in the dynamic linker instead
of NTDLL, which means things are getting initialized in the wrong order (ie.
Windows code might run before Wine is initialized?), with unpredictable
consequences.

-- 
Do not reply to this email, post in Bugzilla using the
above URL to reply.
You are receiving this mail because:
You are watching all bug changes.


More information about the wine-bugs mailing list