Hi, I reported the analog problem to binutils, see https://sourceware.org/bugzilla/show_bug.cgi?id=18025 but GDB is affected as well. We're encountering a problem evaluating Dwarf2 debug info in DLLs after rebasing the DLL. Rebasing, that is, moving the image base address of a DLL and adjusting the relocation information, is an essential part of DLL handling in the Cygwin distro, required for smooth operation of the fork emulation. Consider a DLL built with debug info, unstripped. As an example, I'm using the latest file(1) package which comes with a DLL called cygmagic-1.dll. The output of objdump -h looks like this: $ objdump -h cygmagic-1.dll. cygmagic-1.dll: file format pei-x86-64 Sections: Idx Name Size VMA LMA File off Algn 0 .text 00013618 00000004d9221000 00000004d9221000 00000600 2**4 CONTENTS, ALLOC, LOAD, READONLY, CODE, DATA 1 .data 00000068 00000004d9235000 00000004d9235000 00013e00 2**5 CONTENTS, ALLOC, LOAD, DATA 2 .rdata 00005258 00000004d9236000 00000004d9236000 00014000 2**6 CONTENTS, ALLOC, LOAD, READONLY, DATA [...] 10 .debug_aranges 00000510 00000004d9243000 00000004d9243000 0001c400 2**4 CONTENTS, READONLY, DEBUGGING 11 .debug_info 0002b4ab 00000004d9244000 00000004d9244000 0001ca00 2**0 CONTENTS, READONLY, DEBUGGING 12 .debug_abbrev 00003d2b 00000004d9270000 00000004d9270000 00048000 2**0 CONTENTS, READONLY, DEBUGGING 13 .debug_line 00006046 00000004d9274000 00000004d9274000 0004be00 2**0 CONTENTS, READONLY, DEBUGGING 14 .debug_frame 00002b68 00000004d927b000 00000004d927b000 00052000 2**3 CONTENTS, READONLY, DEBUGGING 15 .debug_str 00000302 00000004d927e000 00000004d927e000 00054c00 2**0 CONTENTS, READONLY, DEBUGGING 16 .debug_loc 000259a2 00000004d927f000 00000004d927f000 00055000 2**0 CONTENTS, READONLY, DEBUGGING 17 .debug_ranges 00003230 00000004d92a5000 00000004d92a5000 0007aa00 2**0 CONTENTS, READONLY, DEBUGGING Notice the VMA addresses. The DLL is based at 0x4d9220000. This DLL works and evaluating the debug info works nicely. In GDB you can set a breakpoint on this function and it's loaded to the same address. When breaking, GDB shows the function, its arguments, and the source line: Breakpoint 1, file_fsmagic (ms=ms@entry=0x6000394f0, fn=fn@entry=0x23cb75 "./file", sb=sb@entry=0x23c8f0) at /usr/src/debug/file-5.22-1/src/fsmagic.c:104 104 { After rebasing to, e.g., 0x300000000 as above, it looks like this: $ rebase -b 0x300000000 cygmagic-1.dll $ nm cygmagic-1.dll | grep file_fsmagic 000000030000c520 T file_fsmagic (gdb) r ./file Starting program: /usr/bin/file ./file [New Thread 2696.0xecc] Warning: Cannot insert breakpoint 1. Cannot access memory at address 0x4d922c520 So, apparently the debug info uses absolute addresses, rather than image base relative or section relative addresses. After rebasing, the info points to invalid addresses. However, when splitting off the debug info into an external *.dbg file and stripping the debug info from the original DLL, then debugging works fine even after rebsing the original DLL. So apparently GDB knows how to handle this situation, but it doesn't take it into account when the debug info is still present in the DLL itself. Can this be fixed, please? Ultimately I think the right thing to do would be to change the Dwarf2 debug info to be defined image base relative rather than using absolute addressing, to make sure it still contains valid information even after rebasing a DLL. At least on PE/COFF targets. I mentioned this in the binutls bugreport https://sourceware.org/bugzilla/show_bug.cgi?id=18025 as well. Thanks, Corinna
Is this still an issue? If so could you say what effect rebase has? Like, after rebase, what does 'objdump -h' say?
Yes, this is still an issue, and, as I wrote originally, it's still restricted to DLLs with integrated dwarf2 debug sections. If the debug sections are only present in an external debuginfo file, like, in the example of the `file' package I used for this case, /usr/lib/debug/usr/bin/cygmagic-1.dll.dbg, rebasing the DLL is no problem and resolving debug symbols in GDB continues to work as desired. So, what happens when rebasing the DLL? Lets start with how PE/COFF sections are organized. Windows stores a load address in the file header. The header has a size of 4K. all other sections following the header must start at the next 4K-aligned address without leaving holes, even NOLOAD sections! When rebase runs, it changes the load address in the PE/COFF header, and it changes all start addresses of all sections according to the above rule. So when rebasing a DLL to, say, 0x30000000, the load address in the header will be 0x300000000, the start address of the next section 0x300001000. If the section has a size of 10K, the next section will start at 0x300001000 + 10K 4K-aligened) == 0x300004000, and so on. Let's make an example with the most recent file package anew: The current cygmagic-1.dll, freshly built, starts at load address 0x4d9220000. So the objdump output of the unstripped DLL looks like this: ------------------------------------------------------------------------------- $ objdump -h cygmagic-1.dll cygmagic-1.dll: file format pei-x86-64 Sections: Idx Name Size VMA LMA File off Algn 0 .text 0001d5e8 00000004d9221000 00000004d9221000 00000600 2**4 CONTENTS, ALLOC, LOAD, READONLY, CODE, DATA 1 .data 000001c0 00000004d923f000 00000004d923f000 0001dc00 2**4 CONTENTS, ALLOC, LOAD, DATA 2 .rdata 000065d8 00000004d9240000 00000004d9240000 0001de00 2**4 CONTENTS, ALLOC, LOAD, READONLY, DATA 3 .buildid 00000035 00000004d9247000 00000004d9247000 00024400 2**2 CONTENTS, ALLOC, LOAD, READONLY, DATA 4 .pdata 000009e4 00000004d9248000 00000004d9248000 00024600 2**2 CONTENTS, ALLOC, LOAD, READONLY, DATA 5 .xdata 00000ab4 00000004d9249000 00000004d9249000 00025000 2**2 CONTENTS, ALLOC, LOAD, READONLY, DATA 6 .bss 000004d0 00000004d924a000 00000004d924a000 00000000 2**4 ALLOC 7 .edata 00000c75 00000004d924b000 00000004d924b000 00025c00 2**2 CONTENTS, ALLOC, LOAD, READONLY, DATA 8 .idata 00001214 00000004d924c000 00000004d924c000 00026a00 2**2 CONTENTS, ALLOC, LOAD, DATA 9 .reloc 000001f4 00000004d924e000 00000004d924e000 00027e00 2**2 CONTENTS, ALLOC, LOAD, READONLY, DATA 10 .debug_aranges 000005d0 00000004d924f000 00000004d924f000 00028000 2**0 CONTENTS, READONLY, DEBUGGING 11 .debug_info 0004186a 00000004d9250000 00000004d9250000 00028600 2**0 CONTENTS, READONLY, DEBUGGING 12 .debug_abbrev 00005acf 00000004d9292000 00000004d9292000 0006a000 2**0 CONTENTS, READONLY, DEBUGGING 13 .debug_line 000150e4 00000004d9298000 00000004d9298000 0006fc00 2**0 CONTENTS, READONLY, DEBUGGING 14 .debug_frame 000056a0 00000004d92ae000 00000004d92ae000 00084e00 2**0 CONTENTS, READONLY, DEBUGGING 15 .debug_str 0000055b 00000004d92b4000 00000004d92b4000 0008a600 2**0 CONTENTS, READONLY, DEBUGGING 16 .debug_line_str 00003431 00000004d92b5000 00000004d92b5000 0008ac00 2**0 CONTENTS, READONLY, DEBUGGING 17 .debug_loclists 000211c5 00000004d92b9000 00000004d92b9000 0008e200 2**0 CONTENTS, READONLY, DEBUGGING 18 .debug_rnglists 000025a9 00000004d92db000 00000004d92db000 000af400 2**0 CONTENTS, READONLY, DEBUGGING ------------------------------------------------------------------------------- If I use this DLL for debugging I get the expected result: ------------------------------------------------------------------------------- $ gdb ./file.exe GNU gdb (GDB) (Cygwin 12.1-1) 12.1 [...] (gdb) start file.exe Temporary breakpoint 1 at 0x100402400: file /usr/src/debug/file-5.44-1/src/file.c, line 192. Starting program: /home/corinna/tmp/file/file-5.44-1.x86_64/build/src/.libs/file.exe file.exe [...] (gdb) (gdb) br file_fsmagic Breakpoint 2 at 0x4d92373b0: file /usr/src/debug/file-5.44-1/src/fsmagic.c, line 107. (gdb) c Continuing. Thread 1 "file" hit Breakpoint 2, file_fsmagic (ms=ms@entry=0xa00001770, fn=fn@entry=0x7ffffcc7e "file.exe", sb=sb@entry=0x7ffffc950) at /usr/src/debug/file-5.44-1/src/fsmagic.c:107 107 { (gdb) ------------------------------------------------------------------------------- Now I rebase the DLL to a new load address 0x300000000. As I wrote above, this will change the load address in the header as well as the load addresses of all sections accordingly: ------------------------------------------------------------------------------- $ rebase -b 0x300000000 cygmagic-1.dll $ objdump -h cygmagic-1.dll cygmagic-1.dll: file format pei-x86-64 Sections: Idx Name Size VMA LMA File off Algn 0 .text 0001d5e8 0000000300001000 0000000300001000 00000600 2**4 CONTENTS, ALLOC, LOAD, READONLY, CODE, DATA 1 .data 000001c0 000000030001f000 000000030001f000 0001dc00 2**4 CONTENTS, ALLOC, LOAD, DATA 2 .rdata 000065d8 0000000300020000 0000000300020000 0001de00 2**4 CONTENTS, ALLOC, LOAD, READONLY, DATA 3 .buildid 00000035 0000000300027000 0000000300027000 00024400 2**2 CONTENTS, ALLOC, LOAD, READONLY, DATA 4 .pdata 000009e4 0000000300028000 0000000300028000 00024600 2**2 CONTENTS, ALLOC, LOAD, READONLY, DATA 5 .xdata 00000ab4 0000000300029000 0000000300029000 00025000 2**2 CONTENTS, ALLOC, LOAD, READONLY, DATA 6 .bss 000004d0 000000030002a000 000000030002a000 00000000 2**4 ALLOC 7 .edata 00000c75 000000030002b000 000000030002b000 00025c00 2**2 CONTENTS, ALLOC, LOAD, READONLY, DATA 8 .idata 00001214 000000030002c000 000000030002c000 00026a00 2**2 CONTENTS, ALLOC, LOAD, DATA 9 .reloc 000001f4 000000030002e000 000000030002e000 00027e00 2**2 CONTENTS, ALLOC, LOAD, READONLY, DATA 10 .debug_aranges 000005d0 000000030002f000 000000030002f000 00028000 2**0 CONTENTS, READONLY, DEBUGGING 11 .debug_info 0004186a 0000000300030000 0000000300030000 00028600 2**0 CONTENTS, READONLY, DEBUGGING 12 .debug_abbrev 00005acf 0000000300072000 0000000300072000 0006a000 2**0 CONTENTS, READONLY, DEBUGGING 13 .debug_line 000150e4 0000000300078000 0000000300078000 0006fc00 2**0 CONTENTS, READONLY, DEBUGGING 14 .debug_frame 000056a0 000000030008e000 000000030008e000 00084e00 2**0 CONTENTS, READONLY, DEBUGGING 15 .debug_str 0000055b 0000000300094000 0000000300094000 0008a600 2**0 CONTENTS, READONLY, DEBUGGING 16 .debug_line_str 00003431 0000000300095000 0000000300095000 0008ac00 2**0 CONTENTS, READONLY, DEBUGGING 17 .debug_loclists 000211c5 0000000300099000 0000000300099000 0008e200 2**0 CONTENTS, READONLY, DEBUGGING 18 .debug_rnglists 000025a9 00000003000bb000 00000003000bb000 000af400 2**0 CONTENTS, READONLY, DEBUGGING ------------------------------------------------------------------------------- Now I try to debug this DLL: ------------------------------------------------------------------------------- $ gdb ./file.exe GNU gdb (GDB) (Cygwin 12.1-1) 12.1 [...] Temporary breakpoint 1 at 0x100402400: file /usr/src/debug/file-5.44-1/src/file.c, line 192. Starting program: /home/corinna/tmp/file/file-5.44-1.x86_64/build/src/.libs/file.exe file.exe [...] (gdb) br file_fsmagic Breakpoint 2 at 0x3000173c3 (2 locations) ------------------------------------------------------------------------------- Compare that with setting the breakpoint while debugging the un-rebased DLL. Next I try to continue: ------------------------------------------------------------------------------- (gdb) c Continuing. Warning: Cannot insert breakpoint 2. Cannot access memory at address 0x4d92373b0 Command aborted. (gdb) ------------------------------------------------------------------------------- Observe the address GDB is trying to set a breakpoint to! It's still an address which only makes sense if the DLL hasn't been rebased. The address 0x4d92373b0 in the non-rebased DLL would actually be equivalent to the address 0x3000173B0 in the rebased DLL. Still, and just to be sure, if I create the DLL, split off the debuginfo file and then rebase the DLL sans its debug sections, debuging works nicely, albeit the debuginfo file continues to maintain the original addresses. I. e., the load address of the debuginfo file is still 0x4d9220000. Thanks, Corinna
Oh, btw. the core of the rebase functionality has actually been written by Microsoft. The heart of our rebase tool is actually a call to the Windows function ReBaseImage64(). For info on that function, see https://learn.microsoft.com/en-us/windows/win32/api/imagehlp/nf-imagehlp-rebaseimage64 HTH, Corinna
(In reply to Corinna Vinschen from comment #2) > Still, and just to be sure, if I create the DLL, split off the debuginfo > file and then rebase the DLL sans its debug sections, debuging works > nicely, albeit the debuginfo file continues to maintain the original > addresses. I. e., the load address of the debuginfo file is still > 0x4d9220000. Can you post a session of this working? I don't understand how the debug info being separate changes this behavior. I'd be interested in seeing the output of "info breakpoint", to see how GDB placed the same breakpoints as you placed in other sessions.
(In reply to Simon Marchi from comment #4) > Can you post a session of this working? I don't understand how the debug > info being separate changes this behavior. I'd be interested in seeing the > output of "info breakpoint", to see how GDB placed the same breakpoints as > you placed in other sessions. Ok, so here's the same DLL, stripped at package creation time, installed into /usr/bin, and rebased to 0x300000000: ------------------------------------------------------------------------------ $ objdump -h /usr/bin/cygmagic-1.dll /bin/cygmagic-1.dll: file format pei-x86-64 Sections: Idx Name Size VMA LMA File off Algn 0 .text 0001d5e8 0000000300001000 0000000300001000 00000400 2**4 CONTENTS, ALLOC, LOAD, READONLY, CODE, DATA 1 .data 000001c0 000000030001f000 000000030001f000 0001da00 2**4 CONTENTS, ALLOC, LOAD, DATA 2 .rdata 000065f8 0000000300020000 0000000300020000 0001dc00 2**4 CONTENTS, ALLOC, LOAD, READONLY, DATA 3 .buildid 00000035 0000000300027000 0000000300027000 00024200 2**2 CONTENTS, ALLOC, LOAD, READONLY, DATA 4 .pdata 000009e4 0000000300028000 0000000300028000 00024400 2**2 CONTENTS, ALLOC, LOAD, READONLY, DATA 5 .xdata 00000ab4 0000000300029000 0000000300029000 00024e00 2**2 CONTENTS, ALLOC, LOAD, READONLY, DATA 6 .bss 000004d0 000000030002a000 000000030002a000 00000000 2**4 ALLOC 7 .edata 00000c75 000000030002b000 000000030002b000 00025a00 2**2 CONTENTS, ALLOC, LOAD, READONLY, DATA 8 .idata 00001214 000000030002c000 000000030002c000 00026800 2**2 CONTENTS, ALLOC, LOAD, DATA 9 .reloc 000001f4 000000030002e000 000000030002e000 00027c00 2**2 CONTENTS, ALLOC, LOAD, READONLY, DATA 10 .gnu_debuglink 00000018 000000030002f000 000000030002f000 00027e00 2**2 CONTENTS, READONLY, DEBUGGING ------------------------------------------------------------------------------ Debuginfo split off at package creation time with default addresses for this DLL intact: ------------------------------------------------------------------------------ $ $ objdump -h /usr/lib/debug/usr/bin/cygmagic-1.dll.dbg /usr/lib/debug/usr/bin/cygmagic-1.dll.dbg: file format pei-x86-64 Sections: Idx Name Size VMA LMA File off Algn 0 .text 0001d5e8 00000004d9221000 00000004d9221000 00000000 2**4 ALLOC, LOAD, READONLY, CODE, DATA 1 .data 000001c0 00000004d923f000 00000004d923f000 00000000 2**4 ALLOC, LOAD, DATA 2 .rdata 000065f8 00000004d9240000 00000004d9240000 00000000 2**4 ALLOC, LOAD, READONLY, DATA 3 .buildid 00000035 00000004d9247000 00000004d9247000 00000600 2**2 CONTENTS, ALLOC, LOAD, READONLY, DATA 4 .pdata 000009e4 00000004d9248000 00000004d9248000 00000000 2**2 ALLOC, LOAD, READONLY, DATA 5 .xdata 00000ab4 00000004d9249000 00000004d9249000 00000000 2**2 ALLOC, LOAD, READONLY, DATA 6 .bss 000004d0 00000004d924a000 00000004d924a000 00000000 2**4 ALLOC 7 .edata 00000c75 00000004d924b000 00000004d924b000 00000000 2**2 ALLOC, LOAD, READONLY, DATA 8 .idata 00001214 00000004d924c000 00000004d924c000 00000000 2**2 ALLOC, LOAD, DATA 9 .reloc 000001f4 00000004d924e000 00000004d924e000 00000000 2**2 ALLOC, LOAD, READONLY, DATA 10 .debug_aranges 000005d0 00000004d924f000 00000004d924f000 00000800 2**0 CONTENTS, READONLY, DEBUGGING 11 .debug_info 0004186a 00000004d9250000 00000004d9250000 00000e00 2**0 CONTENTS, READONLY, DEBUGGING 12 .debug_abbrev 00005acf 00000004d9292000 00000004d9292000 00042800 2**0 CONTENTS, READONLY, DEBUGGING 13 .debug_line 000150e4 00000004d9298000 00000004d9298000 00048400 2**0 CONTENTS, READONLY, DEBUGGING 14 .debug_frame 000056a0 00000004d92ae000 00000004d92ae000 0005d600 2**0 CONTENTS, READONLY, DEBUGGING 15 .debug_str 0000055b 00000004d92b4000 00000004d92b4000 00062e00 2**0 CONTENTS, READONLY, DEBUGGING 16 .debug_line_str 00003107 00000004d92b5000 00000004d92b5000 00063400 2**0 CONTENTS, READONLY, DEBUGGING 17 .debug_loclists 000211c5 00000004d92b9000 00000004d92b9000 00066600 2**0 CONTENTS, READONLY, DEBUGGING 18 .debug_rnglists 000025a9 00000004d92db000 00000004d92db000 00087800 2**0 CONTENTS, READONLY, DEBUGGING 19 .gnu_debuglink 0000000c 00000004d92de000 00000004d92de000 00089e00 2**2 CONTENTS, READONLY, DEBUGGING ------------------------------------------------------------------------------ Now let's debug it: ------------------------------------------------------------------------------ $ gdb /usr/bin/file GNU gdb (GDB) (Cygwin 12.1-1) 12.1 [...] Reading symbols from /usr/bin/file... Reading symbols from /usr/lib/debug//usr/bin/file.exe.dbg... (gdb) sta /usr/bin/file.exe Temporary breakpoint 1 at 0x100402400: file /usr/src/debug/file-5.44-1/src/file.c, line 192. Starting program: /usr/bin/file /usr/bin/file.exe [...] Thread 1 "file" hit Temporary breakpoint 1, main (argc=2, argv=0x7ffffcc40) at /usr/src/debug/file-5.44-1/src/file.c:192 192 { (gdb) br file_fsmagic Breakpoint 2 at 0x3000173b0: file /usr/src/debug/file-5.44-1/src/fsmagic.c, line 107. (gdb) c Continuing. [New Thread 2960.0x2164] [New Thread 2960.0xbe8] Thread 1 "file" hit Breakpoint 2, file_fsmagic (ms=ms@entry=0xa00001710, fn=fn@entry=0x7ffffcc79 "/usr/bin/file.exe", sb=sb@entry=0x7ffffc980) at /usr/src/debug/file-5.44-1/src/fsmagic.c:107 107 { (gdb) info br info br Num Type Disp Enb Address What 2 breakpoint keep y 0x00000003000173b0 in file_fsmagic at /usr/src/debug/file-5.44-1/src/fsmagic.c:107 breakpoint already hit 1 time (gdb) ------------------------------------------------------------------------------ As an extra datapoint, if ASLR is active for this DLL, it can be moved to any arbitrary free address by the OS, without GDB choking on that load address. For instance: ------------------------------------------------------------------------------ (gdb) br file_fsmagic Breakpoint 2 at 0x7fff658e73b0: file /usr/src/debug/file-5.44-1/src/fsmagic.c, line 107. (gdb) c Continuing. [New Thread 5988.0xe40] [New Thread 5988.0xe80] Thread 1 "file" hit Breakpoint 2, file_fsmagic (ms=ms@entry=0xa00001770, fn=fn@entry=0x7ffffcc7e "file.exe", sb=sb@entry=0x7ffffc950) at /usr/src/debug/file-5.44-1/src/fsmagic.c:107 107 { (gdb) info br Num Type Disp Enb Address What 2 breakpoint keep y 0x00007fff658e73b0 in file_fsmagic at /usr/src/debug/file-5.44-1/src/fsmagic.c:107 breakpoint already hit 1 time ------------------------------------------------------------------------------ For the unstripped DLL, this is also true... as long as it hasn't been rebased! If the unstripped DLL got rebased, the same problem occurs for the ASLR'ed address: ------------------------------------------------------------------------------ (gdb) br file_fsmagic Breakpoint 2 at 0x7fff658e73c3 (2 locations) (gdb) c Continuing. Warning: Cannot insert breakpoint 2. Cannot access memory at address 0x80013eb073b0 Command aborted. (gdb) ------------------------------------------------------------------------------ Does that help? I'm pretty sure the problem is that the dwarf2 debug info contains absolute addresses which don't match the load address of the DLL after rebase. Being loaded to other addresses (ASLR) is taken into account, GDB computes the offset between load address in the file header and actual load address in VM and utilizes it throughout. However, as soon as the DLL gets rebased, the addresses in the debug info and the load address in the DLL header are "off", so the offset computation doesn't work anymore. Just guessing here, of course. Thanks, Corinna
> I'm pretty sure the problem is that the dwarf2 debug info contains > absolute addresses which don't match the load address of the DLL after > rebase. This does make sense, but the part that I don't understand is: why would a rebased executable work if the debug info has been split. Or maybe I don't understand the problem correctly?
(In reply to Simon Marchi from comment #6) > > I'm pretty sure the problem is that the dwarf2 debug info contains > > absolute addresses which don't match the load address of the DLL after > > rebase. > > This does make sense, but the part that I don't understand is: why would a > rebased executable work if the debug info has been split. Or maybe I don't > understand the problem correctly? Because when it's split, then the debug info itself also has image base address in its own header, which is matching the debug info. So when the executable is loaded, the debug info is automatically rebased to the difference of both image base addresses (exe->base - dbg->base). When it's not split, there is only one base address, and it's matching the executable, and no longer the debug info.
> I'm pretty sure the problem is that the dwarf2 debug info contains > absolute addresses which don't match the load address of the DLL after > rebase. Ok... so if I'm understanding correctly, this might not completely be a gdb bug, since what's happening is that the DLL is rebased but some addresses in the DWARF are not updated. However, maybe gdb could work around this some way, if we had a way to determine the original base? Like, for DWARF symbols, gdb could do ADDR - ORIG + NEW to compute the address? My apologies if this is misguided, I'm still not sure I understand what is going on.
(In reply to Tom Tromey from comment #8) > > I'm pretty sure the problem is that the dwarf2 debug info contains > > absolute addresses which don't match the load address of the DLL after > > rebase. > > Ok... so if I'm understanding correctly, this might not completely > be a gdb bug, since what's happening is that the DLL is rebased but > some addresses in the DWARF are not updated. I also think that this isn't really a gdb bug. > However, maybe gdb could work around this some way, if we had a > way to determine the original base? Like, for DWARF symbols, > gdb could do ADDR - ORIG + NEW to compute the address? > > My apologies if this is misguided, I'm still not sure I understand > what is going on. I looked into this problem a few years ago, but I couldn't figure out a way to determine the original base from the debug symbols.
(In reply to Tom Tromey from comment #8) > > I'm pretty sure the problem is that the dwarf2 debug info contains > > absolute addresses which don't match the load address of the DLL after > > rebase. > > Ok... so if I'm understanding correctly, this might not completely > be a gdb bug, since what's happening is that the DLL is rebased but > some addresses in the DWARF are not updated. Indeed, this is not necessarily a GDB bug. Apologies, but 8 years ago it very much looked like a combined ld/GDB problem, that's why I created https://sourceware.org/bugzilla/show_bug.cgi?id=18025 and this bug here at the same time. In fact, the first question is probably if the dwarf2 info couldn't by imagebase-relative, rather than absolute. If that's possible (and maybe even already handled in GDB?) this could be done by tweaking the linker. If that's not possible because of how the dwarf info is defined... > However, maybe gdb could work around this some way, if we had a > way to determine the original base? Like, for DWARF symbols, > gdb could do ADDR - ORIG + NEW to compute the address? ...then it would be great if GDB could workaround this somehow. The problem here is, I have no clue if there's anything in the DLL which would be indicative of the original image base address after rebasing. Unless, maybe, some of the dwarf2 info itself can be used to reconstruct the originl address? Thanks, Corinna
(In reply to Corinna Vinschen from comment #10) > Indeed, this is not necessarily a GDB bug. Apologies, but 8 years ago > it very much looked like a combined ld/GDB problem, that's why I created > https://sourceware.org/bugzilla/show_bug.cgi?id=18025 and this bug here > at the same time. Completely understandable, no need to apologize :) > In fact, the first question is probably if the dwarf2 info couldn't > by imagebase-relative, rather than absolute. If that's possible > (and maybe even already handled in GDB?) this could be done by tweaking > the linker. I think this would be better, but won't it cause issues if we then use gdb to debug existing executables? Since those will have the offsets already in the DWARF? > ...then it would be great if GDB could workaround this somehow. > The problem here is, I have no clue if there's anything in the DLL > which would be indicative of the original image base address after > rebasing. Unless, maybe, some of the dwarf2 info itself can be > used to reconstruct the originl address? binutils has a workaround based on _bfd_dwarf2_find_symbol_bias (see bfd/dwarf2.c). What this does is look for a function in the DWARF that has an address, then tries to find that same function in the linker symbols. When it finds a match, it uses those addresses to compute the delta. This could work in gdb as well, it's just very unfortunate to have to do it. However it's hard to see another way. One idea I had is to assume that some function lives in the first text page and try to use the lowest address seen (this is easier to get just from scanning the DWARF) to compute the offset, but this fails for cases like a DLL with a mix of -g / no-g objects.
(In reply to Tom Tromey from comment #11) > (In reply to Corinna Vinschen from comment #10) > [...] > > In fact, the first question is probably if the dwarf2 info couldn't > > by imagebase-relative, rather than absolute. If that's possible > > (and maybe even already handled in GDB?) this could be done by tweaking > > the linker. > > I think this would be better, but won't it cause issues if we then > use gdb to debug existing executables? Since those will have the > offsets already in the DWARF? Isn't there some kind of version information in the DWARF sections somewhere? GDB could use that to distinguish absolute vs. load-address relative DWARF info. > > ...then it would be great if GDB could workaround this somehow. > > The problem here is, I have no clue if there's anything in the DLL > > which would be indicative of the original image base address after > > rebasing. Unless, maybe, some of the dwarf2 info itself can be > > used to reconstruct the originl address? > > binutils has a workaround based on _bfd_dwarf2_find_symbol_bias > (see bfd/dwarf2.c). > > What this does is look for a function in the DWARF that has an address, > then tries to find that same function in the linker symbols. When > it finds a match, it uses those addresses to compute the delta. Yeah, that actually sounds very simple: load address in header: 0x300000000 func foo in .text: 0x300021300 func foo in DWARF: 0x4d9241300 --> original load address at the time the file was created: 0x300000000 + (0x4d9241300 - 0x300021300) => 0x4D9220000 > This could work in gdb as well, it's just very unfortunate to have to do it. > However it's hard to see another way. > > One idea I had is to assume that some function lives in the first text page > and try to use the lowest address seen (this is easier to get just from > scanning > the DWARF) to compute the offset, but this fails for cases like a DLL with > a mix of -g / no-g objects. We have to assume that the DLL has been built with debugging symbols. SO we can safely assume that the DllMain function is always present. As symbol to look for, one could use DllMain for DLLs, and WinMainCRTStartup otherwise, they are always present in Cygwin executables. Alternatively, we know exactly where the .text section starts in a Windows executable. .text is at load address + 0x1000. And there's always *some* symbol at this address. So it would be possible to figure out that symbol, fetch it from DWARF and use the above offset computation, no? Corinna
(In reply to Corinna Vinschen from comment #12) > Isn't there some kind of version information in the DWARF sections somewhere? > GDB could use that to distinguish absolute vs. load-address relative DWARF > info. There is, but it's not useful for this. There's no way to tell which linker was in use. > We have to assume that the DLL has been built with debugging symbols. SO > we can safely assume that the DllMain function is always present. It's not unheard of for programs to have some compilations with debuginfo and some without. I tend to think we'll have to do this the way BFD does: just keep trying symbols until we find one that works.
I have been thinking about this again (but not working on it, sorry). It seems to me that the detection should probably be done in coffread somewhere. Another thought I had is if the linker could emit a section or note that held the original offset, then gdb could simply read that to find the correct delta.
(In reply to Tom Tromey from comment #14) > Another thought I had is if the linker could emit a section or note > that held the original offset, then gdb could simply read that to > find the correct delta. That sounds like a really nice and sinmple idea. Maybe we should add Nick Clifton to CC? Thanks, Corinna
(In reply to Corinna Vinschen from comment #15) > (In reply to Tom Tromey from comment #14) > > Another thought I had is if the linker could emit a section or note > > that held the original offset, then gdb could simply read that to > > find the correct delta. > > That sounds like a really nice and sinmple idea. Maybe we should > add Nick Clifton to CC? Oh and, btw., we could even add this section manually, could we not? Something like .debug_pe_defaddr with a 32 or 64 bit address as hex string?
(In reply to Corinna Vinschen from comment #15) > (In reply to Tom Tromey from comment #14) > > Another thought I had is if the linker could emit a section or note > > that held the original offset, then gdb could simply read that to > > find the correct delta. > > That sounds like a really nice and sinmple idea. Maybe we should > add Nick Clifton to CC? Would you mind asking him?
[Adding nickc to CC] Hi Nick, what do you think about Tom's idea in comment 14? Would it be possible to add something like that? Maybe like I suggest in comment 16? Thanks, Corinna
(In reply to Corinna Vinschen from comment #18) > what do you think about Tom's idea in comment 14? To be honest, I do not get what Tom is suggesting. :-( What is that coffread.c should be detecting and then doing ? > Would it be possible to add something like that? > Maybe like I suggest in comment 16? It certainly should be possible for the linker to add an extra section. There is already a precedence for this, for example when it creates the .reloc section. In fact it may even be possible to create the section and its contents using the default linker script, so that there is no need to change the linker at all. For example adding (untested): .debug_pe_defaddr 0 { .long DLLMain } Cheers Nick PS. I assume that the rebase program cannot update the addresses in the debug information itself ? PPS. Would the __pei386_runtime_relocator symbol be of any use in addressing this problem ?
(In reply to Nick Clifton from comment #19) > (In reply to Corinna Vinschen from comment #18) > > > what do you think about Tom's idea in comment 14? > > To be honest, I do not get what Tom is suggesting. :-( What is that > coffread.c should be detecting and then doing ? That's what GDB would be able to do if the extra section with the original address is available. There would be an offset between original address and rebased address which could be taken into account. > > Would it be possible to add something like that? > > Maybe like I suggest in comment 16? > > It certainly should be possible for the linker to add an extra section. > There is already a precedence for this, for example when it creates the > .reloc section. > > In fact it may even be possible to create the section and its contents using > the > default linker script, so that there is no need to change the linker at all. > For example adding (untested): > > .debug_pe_defaddr 0 { .long DLLMain } Aha, nice. That should ideally be the load address of the DLL, so I tried this with the Cygwin DLL which provides its own linker script, and I came up with: .debug_pe_defaddr ALIGN(__section_alignment__) (NOLOAD) : { QUAD (__image_base__) } which appears to work. > Cheers > Nick > > PS. I assume that the rebase program cannot update the addresses in the > debug information itself ? Rebasing is actually not performed by some code we have under control. Rather, we're calling a function Rebase64(), which is part of Windows itself. Given that Windows has no idea what Dwarf-2 is, it doesn't even touch these sections. > PPS. Would the __pei386_runtime_relocator symbol be of any use in addressing > this problem ? I have no idea. How so? Thanks, Corinna
(In reply to Corinna Vinschen from comment #20) Hi Corinna, > > In fact it may even be possible to create the section and its contents using > > the default linker script, so that there is no need to change the linker at all. > > For example adding (untested): > > > > .debug_pe_defaddr 0 { .long DLLMain } > > Aha, nice. That should ideally be the load address of the DLL, so I tried > this with the Cygwin DLL which provides its own linker script, and I came up > with: > > .debug_pe_defaddr ALIGN(__section_alignment__) (NOLOAD) : > { > QUAD (__image_base__) > } > > which appears to work. Excellent. Do you want me to add that to the default linker scripts for PE and PEP format targets ? Of course this will only help if the user then builds with a linker containing this extra text in its default linker script. But you can also provide that script fragment as an extra input file to any version of the linker, and it should work. So you would not need a new linker at all, just a modified build process. > > PPS. Would the __pei386_runtime_relocator symbol be of any use in addressing > > this problem ? > > I have no idea. How so? It was just a guess. I thought that the name of the symbol meant that it would contain an address bias of some sort. Something that might be able to help fix the addresses in the DWARF debug information. Cheers Nick
[adding Jon and Achim, our GDB and binutils maintainers] Hi Nick, (In reply to Nick Clifton from comment #21) > (In reply to Corinna Vinschen from comment #20) > Hi Corinna, > > > > In fact it may even be possible to create the section and its contents using > > > the default linker script, so that there is no need to change the linker at all. > > > For example adding (untested): > > > > > > .debug_pe_defaddr 0 { .long DLLMain } > > > > Aha, nice. That should ideally be the load address of the DLL, so I tried > > this with the Cygwin DLL which provides its own linker script, and I came up > > with: > > > > .debug_pe_defaddr ALIGN(__section_alignment__) (NOLOAD) : > > { > > QUAD (__image_base__) > > } > > > > which appears to work. > > Excellent. Do you want me to add that to the default linker scripts for PE > and PEP format targets ? Well... I guess this only makese sense on Cygwin. I'm not sure if anybody outside of Cygwin actually still rebases DLLs, given the default is ASLR these days... Tom? If the original image base is represented in binary form as above, is that ok for GDB's offset calculation? > Of course this will only help if the user then builds with a linker > containing this extra text in its default linker script. But you can also > provide that script fragment as an extra input file to any version of the > linker, and it should work. So you would not need a new linker at all, just > a modified build process. Yeah, it would be great if the info would be created automatically by binutils ld scripts. In the meantime we could add this to the release version 2.40. Take care, Corinna
(In reply to Corinna Vinschen from comment #22) > Tom? If the original image base is represented in binary form as above, > is that ok for GDB's offset calculation? I'm not totally sure, since I don't really know anything about linker scripts. Could you attach a minimal DLL that was built with the patch and that was relocated? That would answer for sure. Don't do it if it's a lot of work or something though.
(In reply to Tom Tromey from comment #23) > (In reply to Corinna Vinschen from comment #22) > > > Tom? If the original image base is represented in binary form as above, > > is that ok for GDB's offset calculation? > > I'm not totally sure, since I don't really know anything about > linker scripts. Could you attach a minimal DLL that was built > with the patch and that was relocated? That would answer for sure. > Don't do it if it's a lot of work or something though. I'm attaching a simple x86_64 DLL with a single function called foo(). I built it with the extra .debug_pe_defaddr section and the original __image_base__ value is 0x4b10c0000. I rebased the DLL to 0x300000000. I also inspected the debug_pe_defaddr section and it contains the 64 bit value 0x4b10c0000. Thanks, Corinna
Created attachment 14789 [details] rebased DLL per discussion
(In reply to Corinna Vinschen from comment #24) > I'm attaching a simple x86_64 DLL with a single function called foo(). > > I built it with the extra .debug_pe_defaddr section and the original > __image_base__ value is 0x4b10c0000. I rebased the DLL to 0x300000000. > I also inspected the debug_pe_defaddr section and it contains the > 64 bit value 0x4b10c0000. I finally got back to looking at this. I don't see 'foo' in there: murgatroyd. nm dll.dll | grep foo murgatroyd. objdump -Wi ./dll.dll | grep foo murgatroyd. I'm not super sure how to test this (not done with a patch but close). I may have to ask you to.
Another oddity is that the .text section says: Idx Name Size VMA LMA File off Algn 0 .text 00000648 0000000300001000 0000000300001000 00000600 2**4 CONTENTS, ALLOC, LOAD, READONLY, CODE, DATA The VMA is offset by 0x1000. So my function to compute the offset also says that: (top-gdb) print/x pe_rebase_offset(abfd) $12 = 0x300001000 Does this matter? I don't know... but each section has a different offset here, so if it does matter, then we'd have to know which section each DWARF entity comes from. Maybe the un-relocated dll already has those offsets in it and then the only mystery would be how to remove the extra 0x1000. Hopefully this makes sense, I can't really tell.
I pushed my patches to my github, see branch win32-18027-dll-rebase. I am going to pause work here for a bit again. I think my overall approach isn't quite good enough due to the competing problems of not wanting to pass in custom section offsets (supporting a long term goal of objfile independence) and of not wanting to add the offset in twice -- meaning the earlier refactoring on that branch needs to be a bit more sophisticated.
(In reply to Tom Tromey from comment #27) > Another oddity is that the .text section says: > > Idx Name Size VMA LMA File off > Algn > 0 .text 00000648 0000000300001000 0000000300001000 00000600 > 2**4 > CONTENTS, ALLOC, LOAD, READONLY, CODE, DATA > > The VMA is offset by 0x1000. No, the VMA is correct. Please keep in mind that the PE/COFF file has a leading 4K block, the file header. The file header is loaded into the address space together with all other sections. ImageBase is the address of the PE/COFF file header, NOT the address of the .text section. > So my function to compute the offset also says that: > > (top-gdb) print/x pe_rebase_offset(abfd) > $12 = 0x300001000 > > Does this matter? I don't know... but each section has a different offset > here, so if it does matter, then we'd have to know which section each > DWARF entity comes from. Using the .text address is wrong. The address where the image gets loaded to is in the PE header header and the entry is called image_base. It should be available via bfd, shouldn't it? If not, assuming you map the file into memory at address ADDR, then you can use Windows definitions to access ImageBase (example for x86_64): IMAGE_DOS_HEADER *dos_hdr = (IMAGE_DOS_HEADER *) ADDR; IMAGE_NT_HEADERS64 *nt_hdr = (BYTE *) dos_hdr + dos-hdr->e_lfanew; ULONGLONG image_base = nt_hdr->OptionalHeader.ImageBase; The orginal, pre-rebase image_base value is a 32 or 64 bit value in the newly defined .debug_pe_defaddr section. So the offset is just the difference between the above current image_base from the file header and the value stored in the .debug_pe_defaddr HTH, Corinna
Thanks for the note. I see windows-tdep.c reaches into BFD to find the image base, so presumably we can just use that here as well.