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.
However PT_LOAD segments are in increasing order of their start address, and the first segment starts at the beginning of the ELF file. Thus we can convert mapbase into relocbase by finding that segment and working out the difference between mapbase and the address it wanted, then use that to work out the correct initializer address. This gets Wine working on FreeBSD again.
Note how commit 6a12d93b88145cb1f31e89c6faca39184fb819ca broke both Linux and FreeBSD for relocated binaries. Linux broke because GNU's l_addr is the relocbase, which has already taken relocation into account, thus adding l_addr to d_un.d_ptr should always be performed, no matter what, but that commit sometimes won't. On FreeBSD l_addr is mapbase, and we can never add it to d_un.d_ptr as both are absolute addresses; the reason the commit appeared to fix FreeBSD is because relocbase is 0 in the common case when no relocation happens, so adding nothing to d_un.d_ptr is usually the right thing to do, but when relocation does happen, we have to add relocbase, which is neither detected nor handled correctly, as it isn't l_addr nor does it have anything to do with l_addr's value alone.
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=49139 Signed-off-by: Damjan Jovanovic damjan.jov@gmail.com --- dlls/ntdll/loader.c | 51 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 47 insertions(+), 4 deletions(-)