Bug 18027 - dwarf2 debug info after rebasing DLLs unusable
Summary: dwarf2 debug info after rebasing DLLs unusable
Status: NEW
Alias: None
Product: gdb
Classification: Unclassified
Component: win32 (show other bugs)
Version: HEAD
: P2 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2015-02-25 15:54 UTC by Corinna Vinschen
Modified: 2023-04-24 15:54 UTC (History)
8 users (show)

See Also:
Host:
Target:
Build:
Last reconfirmed:


Attachments
rebased DLL per discussion (16.63 KB, application/octet-stream)
2023-03-30 09:24 UTC, Corinna Vinschen
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Corinna Vinschen 2015-02-25 15:54:26 UTC
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
Comment 1 Tom Tromey 2023-02-11 17:48:56 UTC
Is this still an issue?
If so could you say what effect rebase has?
Like, after rebase, what does 'objdump -h' say?
Comment 2 Corinna Vinschen 2023-02-13 10:31:40 UTC
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
Comment 3 Corinna Vinschen 2023-02-13 10:36:34 UTC
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
Comment 4 Simon Marchi 2023-02-13 14:01:01 UTC
(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.
Comment 5 Corinna Vinschen 2023-02-13 16:17:54 UTC
(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
Comment 6 Simon Marchi 2023-02-13 17:13:41 UTC
> 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?
Comment 7 Hannes Domani 2023-02-13 17:24:01 UTC
(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.
Comment 8 Tom Tromey 2023-02-13 19:23:22 UTC
> 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.
Comment 9 Hannes Domani 2023-02-13 19:46:04 UTC
(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.
Comment 10 Corinna Vinschen 2023-02-13 19:48:17 UTC
(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
Comment 11 Tom Tromey 2023-02-13 22:08:26 UTC
(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.
Comment 12 Corinna Vinschen 2023-02-15 10:22:03 UTC
(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
Comment 13 Tom Tromey 2023-02-17 02:15:14 UTC
(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.
Comment 14 Tom Tromey 2023-02-28 14:43:59 UTC
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.
Comment 15 Corinna Vinschen 2023-02-28 15:20:10 UTC
(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
Comment 16 Corinna Vinschen 2023-02-28 15:22:43 UTC
(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?
Comment 17 Tom Tromey 2023-03-24 17:31:00 UTC
(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?
Comment 18 Corinna Vinschen 2023-03-24 19:00:03 UTC
[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
Comment 19 Nick Clifton 2023-03-27 09:19:30 UTC
(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 ?
Comment 20 Corinna Vinschen 2023-03-28 08:58:05 UTC
(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
Comment 21 Nick Clifton 2023-03-28 14:01:04 UTC
(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
Comment 22 Corinna Vinschen 2023-03-28 18:10:51 UTC
[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
Comment 23 Tom Tromey 2023-03-29 12:46:29 UTC
(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.
Comment 24 Corinna Vinschen 2023-03-30 09:23:27 UTC
(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
Comment 25 Corinna Vinschen 2023-03-30 09:24:16 UTC
Created attachment 14789 [details]
rebased DLL per discussion
Comment 26 Tom Tromey 2023-04-21 16:38:19 UTC
(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.
Comment 27 Tom Tromey 2023-04-21 16:54:10 UTC
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.
Comment 28 Tom Tromey 2023-04-21 17:11:03 UTC
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.
Comment 29 Corinna Vinschen 2023-04-21 19:48:01 UTC
(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
Comment 30 Tom Tromey 2023-04-24 15:54:58 UTC
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.