Thread overview
[Issue 20460] [OSX] DMD writes the same address everywhere in DWARF debug infos
[Issue 20460] Stack traces on OSX show wrong file / line
Dec 21, 2019
Jacob Carlborg
Jan 10, 2020
Mathias LANG
[Issue 20460] [OSX] Stack traces involving extern(C++) can show wrong file / line
Jan 10, 2020
Mathias LANG
Jan 13, 2020
Dlang Bot
December 21, 2019
https://issues.dlang.org/show_bug.cgi?id=20460

Jacob Carlborg <doob@me.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |doob@me.com

--- Comment #1 from Jacob Carlborg <doob@me.com> ---
Hasn't this already been reported?

Should probably be connected to the bounty: https://www.flipcause.com/secure/cause_pdetails/NjI2NjQ=

--
January 10, 2020
https://issues.dlang.org/show_bug.cgi?id=20460

--- Comment #2 from Mathias LANG <pro.mathias.lang@gmail.com> ---
Found a way to reproduce:

--- a.d
module a;

import b;
import c;

void main ()
{
    auto o1 = new Foo();
    o1.bar(5);
}
--- b.d
module b;

extern(C++) void func() {}
--- c.d
module c;

extern(C++) class Foo
{
    void bar(int rec)
    {
        assert(0);
    }
}

Compile and run:
$ dmd -g b.d c.d -run a.d
core.exception.AssertError@c.d(9): Assertion failure
----------------
??:? _d_assertp [0x10916d89d]
b.d:3 _ZN3Foo3barEi [0x1091608d7]
a.d:9 _Dmain [0x109160881]


The file/line points to `b.d:3` (the first function in that file) while it should be `c.d:7`. Note that even if `bar` calls other functions, all their file/line will point to the same entry.

--
January 10, 2020
https://issues.dlang.org/show_bug.cgi?id=20460

Mathias LANG <pro.mathias.lang@gmail.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
            Summary|Stack traces on OSX show    |[OSX] Stack traces
                   |wrong file / line           |involving extern(C++) can
                   |                            |show wrong file / line

--
January 13, 2020
https://issues.dlang.org/show_bug.cgi?id=20460

Dlang Bot <dlang-bot@dlang.rocks> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
           Keywords|                            |pull

--- Comment #3 from Dlang Bot <dlang-bot@dlang.rocks> ---
@Geod24 created dlang/dmd pull request #10722 "Fix issue 20460: Stack traces involving extern(C++) can show wrong file/line" fixing this issue:

- Fix issue 20460: Stack traces involving extern(C++) can show wrong file/line

  The comment mentions writing the 'function prologue',
  but this seems to be a long-gone heritage (perhaps from DM exceptions?)
  Before this change, some functions would have their 'end' address
  set at the end of the next file's last function,
  which in practice would mean that the last function in the module
  would 'eat' the whole file, and all the stack traces would point
  to the same line.
  This was witnessed with extern(C++) and the _Dmain function.

https://github.com/dlang/dmd/pull/10722

--
January 19
https://issues.dlang.org/show_bug.cgi?id=20460

Mathias LANG <pro.mathias.lang@gmail.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
           Keywords|pull                        |backend, industry
            Summary|[OSX] Stack traces          |[OSX] DMD writes the same
                   |involving extern(C++) can   |address everywhere in DWARF
                   |show wrong file / line      |debug infos

--- Comment #4 from Mathias LANG <pro.mathias.lang@gmail.com> ---
Did some more investigation on this. Turns out the issue is that DMD writes complete nonsense in the debug infos!

Take the following example, in C:
```C
#include <stdio.h>

void callB ()
{
    printf("Hello World\n");
}

int main (void)
{
    callB();
    return 0;
}
```

Compiled with `gcc -g -c ca.c`.
Using `dwarfdump` on it gives a pretty standard output:
```
ca.o:   file format Mach-O 64-bit x86-64

.debug_info contents:
0x00000000: Compile Unit: length = 0x00000064 version = 0x0004 abbr_offset =
0x0000 addr_size = 0x08 (next unit at 0x00000068)

0x0000000b: DW_TAG_compile_unit
              DW_AT_producer    ("Apple clang version 12.0.0
(clang-1200.0.32.28)")
              DW_AT_language    (DW_LANG_C99)
              DW_AT_name        ("ca.c")
              DW_AT_LLVM_sysroot
("/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk")
              DW_AT_APPLE_sdk   ("MacOSX.sdk")
              DW_AT_stmt_list   (0x00000000)
              DW_AT_comp_dir    ("/Users/geod24/projects/dlang/druntime")
              DW_AT_low_pc      (0x0000000000000000)
              DW_AT_high_pc     (0x000000000000003c)

0x00000032:   DW_TAG_subprogram
                DW_AT_low_pc    (0x0000000000000000)
                DW_AT_high_pc   (0x0000000000000014)
                DW_AT_frame_base        (DW_OP_reg6 RBP)
                DW_AT_name      ("callB")
                DW_AT_decl_file ("/Users/geod24/projects/dlang/druntime/ca.c")
                DW_AT_decl_line (3)
                DW_AT_external  (true)

0x00000047:   DW_TAG_subprogram
                DW_AT_low_pc    (0x0000000000000020)
                DW_AT_high_pc   (0x000000000000003c)
                DW_AT_frame_base        (DW_OP_reg6 RBP)
                DW_AT_name      ("main")
                DW_AT_decl_file ("/Users/geod24/projects/dlang/druntime/ca.c")
                DW_AT_decl_line (8)
                DW_AT_prototyped        (true)
                DW_AT_type      (0x00000060 "int")
                DW_AT_external  (true)

0x00000060:   DW_TAG_base_type
                DW_AT_name      ("int")
                DW_AT_encoding  (DW_ATE_signed)
                DW_AT_byte_size (0x04)

0x00000067:   NULL
```

We can see that the DW_TAG_compile_unit contain a DW_AT_low_pc which is the DW_AT_low_pc of callB, and a DW_AT_high_pc which is that of main. In other words, the two functions are stored contiguously in the binary.

We can verify this using `nm`:
```
0000000000000000 T _callB
0000000000000020 T _main <== Value is DW_AT_low_pc, as expected
                 U _printf
```

Now what does DMD produce for equivalent code:
```D
import core.stdc.stdio;

void callB ()
{
    printf("Hello World\n");
}

int main ()
{
    callB();
    return 0;
}
```

Compiled with `dmd -g -c ca.d`, using v2.095.0, and using dwarfdump:
```
ca.o:   file format Mach-O 64-bit x86-64

.debug_info contents:
0x00000000: Compile Unit: length = 0x00000135 version = 0x0003 abbr_offset =
0x0000 addr_size = 0x08 (next unit at 0x00000139)

0x0000000b: DW_TAG_compile_unit
              DW_AT_producer    ("Digital Mars D v2.095.0\n")
              DW_AT_language    (DW_LANG_D)
              DW_AT_name        ("ca.d")
              DW_AT_comp_dir    ("/Users/geod24/projects/dlang/druntime")
              DW_AT_low_pc      (0x0000000000000000)
              DW_AT_entry_pc    (0x0000000000000000)
              DW_AT_ranges      (0x00000000
                 [0x0000000000000500, 0x0000000000000500))
              DW_AT_stmt_list   (0x00000000)

0x00000069:   DW_TAG_module
                DW_AT_name      ("ca")

0x0000006d:   DW_TAG_subprogram
                DW_AT_name      ("ca.callB")
                DW_AT_MIPS_linkage_name ("_D2ca5callBFZv")
                DW_AT_decl_file ("/Users/geod24/projects/dlang/druntime/ca.d")
                DW_AT_decl_line (3)
                DW_AT_low_pc    (0x0000000000000500)
                DW_AT_high_pc   (0x0000000000000500)
                DW_AT_frame_base        (0x00000000:
                   [0x0000000000000500, 0x0000000000000500): DW_OP_breg7 RSP+8
                   [0x0000000000000500, 0x0000000000000500): DW_OP_breg7 RSP+16
                   [0x0000000000000500, 0x0000000000000500): DW_OP_breg6
RBP+16)

0x0000009d:   DW_TAG_base_type
                DW_AT_name      ("int")
                DW_AT_byte_size (0x04)
                DW_AT_encoding  (DW_ATE_signed)

0x000000a4:   DW_TAG_subprogram
                DW_AT_name      ("D main")
                DW_AT_MIPS_linkage_name ("_Dmain")
                DW_AT_decl_file ("/Users/geod24/projects/dlang/druntime/ca.d")
                DW_AT_decl_line (8)
                DW_AT_type      (0x0000009d "int")
                DW_AT_external  (0x01)
                DW_AT_low_pc    (0x0000000000000000)
                DW_AT_high_pc   (0x0000000000000000)
                DW_AT_frame_base        (0x0000004c: )

0x000000cf:   DW_TAG_base_type
                DW_AT_name      ("char")
                DW_AT_byte_size (0x01)
                DW_AT_encoding  (DW_ATE_unsigned_char)

0x000000d7:   DW_TAG_pointer_type
                DW_AT_type      (0x000000cf "char")

0x000000dc:   DW_TAG_pointer_type
                DW_AT_type      (0x000000d7 "char*")

0x000000e1:   DW_TAG_subprogram
                DW_AT_sibling   (0x00000138)
                DW_AT_name      ("ca._d_cmain!().main")
                DW_AT_MIPS_linkage_name ("main")
                DW_AT_decl_file
("/usr/local/opt/dmd/include/dlang/dmd/core/internal/entrypoint.d")
                DW_AT_decl_line (27)
                DW_AT_type      (0x0000009d "int")
                DW_AT_external  (0x01)
                DW_AT_low_pc    (0x0000000000000000)
                DW_AT_high_pc   (0x0000000000000000)
                DW_AT_frame_base        (0x00000098: )

0x0000011b:     DW_TAG_formal_parameter
                  DW_AT_name    ("argc")
                  DW_AT_type    (0x0000009d "int")
                  DW_AT_artificial      (0x00)
                  DW_AT_location        (DW_OP_fbreg -32)

0x00000129:     DW_TAG_formal_parameter
                  DW_AT_name    ("argv")
                  DW_AT_type    (0x000000dc "char**")
                  DW_AT_artificial      (0x00)
                  DW_AT_location        (DW_OP_fbreg -24)

0x00000137:     NULL

0x00000138:   NULL
```

And here we have complete nonsense in terms of address. Either we get 0 - 0, or
0x0000000000000500 - 0x0000000000000500. Neither of those is correct.
Using `nm` on the binary outputs:
```
0000000000000060 s EH_frame0
00000000000000a0 S _D main.eh
0000000000000050 S __D2ca12__ModuleInfoZ
0000000000000500 S __D2ca5callBFZv
0000000000000000 T __Dmain
                 U __Dmain
                 U __d_run_main
00000000000000c8 S _ca._d_cmain!().main.eh
0000000000000078 S _ca.callB.eh
                 U _main
0000000000000010 T _main
                 U _printf
```

Here we can see that 0x0000000000000500 is the start address of callB, and so
the DW_AT_low_pc for "D main" and "callB" are the only two things correct with
this.
I took a look at the backend, and somehow it seems that all relocations end up
with the same addresses.

For reference, LDC's output here is much saner, and as expected:
```
.debug_info contents:
0x00000000: Compile Unit: length = 0x000000d4 version = 0x0004 abbr_offset =
0x0000 addr_size = 0x08 (next unit at 0x000000d8)

0x0000000b: DW_TAG_compile_unit
              DW_AT_producer    ("LDC 1.24.0 (LLVM 9.0.1)")
              DW_AT_language    (DW_LANG_D)
              DW_AT_name        ("ca.d")
              DW_AT_stmt_list   (0x00000000)
              DW_AT_comp_dir    ("/Users/geod24/projects/dlang/druntime")
              DW_AT_APPLE_major_runtime_vers    (0x01)
              DW_AT_low_pc      (0x0000000000000000)
              DW_AT_high_pc     (0x000000000000005b)

0x0000002b:   DW_TAG_module
                DW_AT_name      ("ca")

0x00000030:     DW_TAG_imported_module
                  DW_AT_import  (0x000000ad)

0x00000035:     DW_TAG_imported_module
                  DW_AT_decl_file
("/Users/geod24/projects/dlang/druntime/ca.d")
                  DW_AT_decl_line       (1)
                  DW_AT_import  (0x000000b2)

0x0000003c:     DW_TAG_subprogram
                  DW_AT_low_pc  (0x0000000000000000)
                  DW_AT_high_pc (0x0000000000000014)
                  DW_AT_frame_base      (DW_OP_reg6 RBP)
                  DW_AT_linkage_name    ("_D2ca5callBFZv")
                  DW_AT_name    ("callB")
                  DW_AT_decl_file
("/Users/geod24/projects/dlang/druntime/ca.d")
                  DW_AT_decl_line       (3)
                  DW_AT_external        (true)

0x00000055:     DW_TAG_subprogram
                  DW_AT_low_pc  (0x0000000000000020)
                  DW_AT_high_pc (0x000000000000002d)
                  DW_AT_frame_base      (DW_OP_reg6 RBP)
                  DW_AT_linkage_name    ("_Dmain")
                  DW_AT_name    ("D main")
                  DW_AT_decl_file
("/Users/geod24/projects/dlang/druntime/ca.d")
                  DW_AT_decl_line       (8)
                  DW_AT_type    (0x000000b7 "int")
                  DW_AT_external        (true)

0x00000072:     DW_TAG_subprogram
                  DW_AT_low_pc  (0x0000000000000030)
                  DW_AT_high_pc (0x000000000000005b)
                  DW_AT_frame_base      (DW_OP_reg6 RBP)
                  DW_AT_linkage_name    ("main")
                  DW_AT_name    ("main")
                  DW_AT_decl_file
("/usr/local/Cellar/ldc/1.24.0/include/dlang/ldc/core/internal/entrypoint.d")
                  DW_AT_decl_line       (39)
                  DW_AT_type    (0x000000b7 "int")
                  DW_AT_external        (true)
```

--
January 19
https://issues.dlang.org/show_bug.cgi?id=20460

Mathias LANG <pro.mathias.lang@gmail.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
             Blocks|                            |20510

--- Comment #5 from Mathias LANG <pro.mathias.lang@gmail.com> ---
This is also blocking me from fixing 20510.


Referenced Issues:

https://issues.dlang.org/show_bug.cgi?id=20510
[Issue 20510] Make backtrace code read the dSYM data
--