Summary: | [PATCH] rtld-elf: dlinfo() returns wrong address in RTLD_DI_LINKMAP's l_addr (breaking Wine, gdb, etc.) | ||
---|---|---|---|
Product: | Base System | Reporter: | Damjan Jovanovic <damjan.jov> |
Component: | bin | Assignee: | Konstantin Belousov <kib> |
Status: | Closed FIXED | ||
Severity: | Affects Some People | CC: | cem, emaste, iwtcex, jhb, kib, markj |
Priority: | --- | Flags: | linimon:
mfc-stable12+
|
Version: | 12.1-STABLE | ||
Hardware: | Any | ||
OS: | Any |
Description
Damjan Jovanovic
2020-05-19 03:12:34 UTC
I do not think we can break link_map ABI by changing the meaning of l_addr. https://reviews.freebsd.org/D24918 (In reply to Konstantin Belousov from comment #1) It's not clear if there exists any code at all which expects mapbase in l_addr. Would you at least consider renaming l_addr to something else then? That way existing code won't compile until it's patched to use the correct field (whatever it is). (In reply to Alex S from comment #2) Aren't the gdb binaries expect it (adopted to it) ? GDB doesn't use the special field for MIPS (it ignores it). GDB also uses the structure embedded in the rtld itself, it does not call dlinfo(). GDB does seem to expect l_addr + offset of ".dynamic" in the binary == l_ld. I think what we have now only works because all of our shared libraries are linked at a virtual address of 0. If we did "pre-linking" to set preferred addresses for shared libraries GDB would stop working. Here's stepping through the function (gdb/solib-svr4.c:lm_addr_check()) that finds the base address of a shared library (libutil.so in this case): (top-gdb) p *li $10 = {<lm_info_base> = {<No data fields>}, l_addr = 0x0, l_addr_inferior = 0x800250000, l_addr_p = false, lm_addr = 0x800232638, l_ld = 0x800263360, l_next = 0x800232a38, l_prev = 0x800232238, l_name = 0x800230260} (top-gdb) n 225 l_addr = li->l_addr_inferior; (top-gdb) n 227 if (! abfd || ! has_lm_dynamic_from_link_map ()) (top-gdb) 230 l_dynaddr = li->l_ld; (top-gdb) 232 dyninfo_sect = bfd_get_section_by_name (abfd, ".dynamic"); (top-gdb) 233 if (dyninfo_sect == NULL) (top-gdb) 236 dynaddr = bfd_section_vma (abfd, dyninfo_sect); (top-gdb) 238 if (dynaddr + l_addr != l_dynaddr) (top-gdb) p dynaddr $11 = 0x13360 (top-gdb) p l_addr $12 = 0x800250000 (top-gdb) p l_dynaddr $13 = 0x800263360 li->l_addr_inferior is the value of 'l_addr' from the linkmap, li->l_ld is 'l_ld'. readelf -t libutil.so: [20] .dynamic DYNAMIC 0000000000013360 0000000000013360 6 0000000000000160 0000000000000010 0 8 [0000000000000003]: WRITE, ALLOC readelf -l shows first PT_LOAD with an offset of 0: Program Headers: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align PHDR 0x0000000000000040 0x0000000000000040 0x0000000000000040 0x00000000000001f8 0x00000000000001f8 R 0x8 LOAD 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x00000000000075cc 0x00000000000075cc R 0x1000 LOAD 0x0000000000008000 0x0000000000008000 0x0000000000008000 0x000000000000a5d0 0x000000000000a5d0 R E 0x1000 A commit references this bug: Author: kib Date: Wed May 20 22:08:27 UTC 2020 New revision: 361303 URL: https://svnweb.freebsd.org/changeset/base/361303 Log: Change the samantic of struct link_map l_addr member. It previously returned the object map base address, while all other ELF operating systems return load offset, i.e. the difference between map base and the link base. Explain the meaning of the field in the man page. Stop filling the mips-only l_offs member, which is apparently unused. PR: 246561 Requested by: Damjan Jovanovic <damjan.jov@gmail.com> Reviewed by: emaste, jhb, cem (previous version) Sponsored by: The FreeBSD Foundation MFC after: 1 week Differential revision: https://reviews.freebsd.org/D24918 Changes: head/lib/libc/gen/dlinfo.3 head/libexec/rtld-elf/rtld.c head/sys/sys/link_elf.h MFCed in r361564. |