Skip to content

Commit 2bf7d0d

Browse files
Preserve the __debug_line section on macOS in the final executable
The linker on macOS will remove any debug info, i.e. every section with the `S_ATTR_DEBUG` flag, this includes everything in the `__DWARF` section. By using the `S_REGULAR` flag the linker will not remove this section. This allows to get the filenames and line numbers for backtraces from the executable. Normally the linker removes all the debug info but adds a reference to the object files. The debugger can then read the object files to get filename and line number information. It's also possible to use an additional tool that generates a separate `.dSYM` file. This file can then later be deployed with the application if debug info is needed when the application is deployed.
1 parent 566dc49 commit 2bf7d0d

File tree

2 files changed

+100
-22
lines changed

2 files changed

+100
-22
lines changed

src/dmd/backend/dwarf.c

Lines changed: 50 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,21 @@
1111

1212
// Emit Dwarf symbolic debug info
1313

14+
/*
15+
Some generic information for debug info on macOS:
16+
17+
The linker on macOS will remove any debug info, i.e. every section with the
18+
`S_ATTR_DEBUG` flag, this includes everything in the `__DWARF` section. By using
19+
the `S_REGULAR` flag the linker will not remove this section. This allows to get
20+
the filenames and line numbers for backtraces from the executable.
21+
22+
Normally the linker removes all the debug info but adds a reference to the
23+
object files. The debugger can then read the object files to get filename and
24+
line number information. It's also possible to use an additional tool that
25+
generates a separate `.dSYM` file. This file can then later be deployed with the
26+
application if debug info is needed when the application is deployed.
27+
*/
28+
1429
#if !SPP
1530
#include <stdio.h>
1631
#include <string.h>
@@ -115,12 +130,12 @@ bool doUnwindEhFrame()
115130

116131
#define OFFSET_FAC REGSIZE
117132

118-
int dwarf_getsegment(const char *name, int align)
133+
int dwarf_getsegment(const char *name, int align, int flags)
119134
{
120135
#if ELFOBJ
121-
return ElfObj::getsegment(name, NULL, SHT_PROGBITS, 0, align * 4);
136+
return ElfObj::getsegment(name, NULL, flags, 0, align * 4);
122137
#elif MACHOBJ
123-
return MachObj::getsegment(name, "__DWARF", align * 2, S_ATTR_DEBUG);
138+
return MachObj::getsegment(name, "__DWARF", align * 2, flags);
124139
#else
125140
assert(0);
126141
return 0;
@@ -458,12 +473,13 @@ struct Section
458473
IDXSEC secidx;
459474
Outbuffer *buf;
460475
const char *name;
476+
int flags;
461477

462478
/* Allocate and initialize Section
463479
*/
464480
void initialize()
465481
{
466-
const segidx_t segi = dwarf_getsegment(name, 0);
482+
const segidx_t segi = dwarf_getsegment(name, 0, flags);
467483
seg = segi;
468484
secidx = SegData[segi]->SDshtidx;
469485
buf = SegData[segi]->SDbuf;
@@ -472,23 +488,25 @@ struct Section
472488
};
473489

474490
#if MACHOBJ
475-
static Section debug_pubnames = { 0,0,0, "__debug_pubnames" };
476-
static Section debug_aranges = { 0,0,0, "__debug_aranges" };
477-
static Section debug_ranges = { 0,0,0, "__debug_ranges" };
478-
static Section debug_loc = { 0,0,0, "__debug_loc" };
479-
static Section debug_abbrev = { 0,0,0, "__debug_abbrev" };
480-
static Section debug_info = { 0,0,0, "__debug_info" };
481-
static Section debug_str = { 0,0,0, "__debug_str" };
482-
static Section debug_line = { 0,0,0, "__debug_line" };
491+
static Section debug_pubnames = { 0,0,0, "__debug_pubnames", S_ATTR_DEBUG };
492+
static Section debug_aranges = { 0,0,0, "__debug_aranges", S_ATTR_DEBUG };
493+
static Section debug_ranges = { 0,0,0, "__debug_ranges", S_ATTR_DEBUG };
494+
static Section debug_loc = { 0,0,0, "__debug_loc", S_ATTR_DEBUG };
495+
static Section debug_abbrev = { 0,0,0, "__debug_abbrev", S_ATTR_DEBUG };
496+
static Section debug_info = { 0,0,0, "__debug_info", S_ATTR_DEBUG };
497+
static Section debug_str = { 0,0,0, "__debug_str", S_ATTR_DEBUG };
498+
// We use S_REGULAR to make sure the linker doesn't remove this section. Needed
499+
// for filenames and line numbers in backtraces.
500+
static Section debug_line = { 0,0,0, "__debug_line", S_REGULAR };
483501
#elif ELFOBJ
484-
static Section debug_pubnames = { 0,0,0, ".debug_pubnames" };
485-
static Section debug_aranges = { 0,0,0, ".debug_aranges" };
486-
static Section debug_ranges = { 0,0,0, ".debug_ranges" };
487-
static Section debug_loc = { 0,0,0, ".debug_loc" };
488-
static Section debug_abbrev = { 0,0,0, ".debug_abbrev" };
489-
static Section debug_info = { 0,0,0, ".debug_info" };
490-
static Section debug_str = { 0,0,0, ".debug_str" };
491-
static Section debug_line = { 0,0,0, ".debug_line" };
502+
static Section debug_pubnames = { 0,0,0, ".debug_pubnames", SHT_PROGBITS };
503+
static Section debug_aranges = { 0,0,0, ".debug_aranges", SHT_PROGBITS };
504+
static Section debug_ranges = { 0,0,0, ".debug_ranges", SHT_PROGBITS };
505+
static Section debug_loc = { 0,0,0, ".debug_loc", SHT_PROGBITS };
506+
static Section debug_abbrev = { 0,0,0, ".debug_abbrev", SHT_PROGBITS };
507+
static Section debug_info = { 0,0,0, ".debug_info", SHT_PROGBITS };
508+
static Section debug_str = { 0,0,0, ".debug_str", SHT_PROGBITS };
509+
static Section debug_line = { 0,0,0, ".debug_line", SHT_PROGBITS };
492510
#endif
493511

494512
#if MACHOBJ
@@ -1012,7 +1030,12 @@ void dwarf_initfile(const char *filename)
10121030
return;
10131031
if (config.ehmethod == EH_DM)
10141032
{
1015-
int seg = dwarf_getsegment(debug_frame_name, 1);
1033+
#if MACHOBJ
1034+
int flags = S_ATTR_DEBUG;
1035+
#elif ELFOBJ
1036+
int flags = SHT_PROGBITS;
1037+
#endif
1038+
int seg = dwarf_getsegment(debug_frame_name, 1, flags);
10161039
Outbuffer *buf = SegData[seg]->SDbuf;
10171040
buf->reserve(1000);
10181041
writeDebugFrameHeader(buf);
@@ -1515,7 +1538,12 @@ void dwarf_func_term(Symbol *sfunc)
15151538

15161539
if (ehmethod(sfunc) == EH_DM)
15171540
{
1518-
IDXSEC dfseg = dwarf_getsegment(debug_frame_name, 1);
1541+
#if MACHOBJ
1542+
int flags = S_ATTR_DEBUG;
1543+
#elif ELFOBJ
1544+
int flags = SHT_PROGBITS;
1545+
#endif
1546+
IDXSEC dfseg = dwarf_getsegment(debug_frame_name, 1, flags);
15191547
writeDebugFrameFDE(dfseg, sfunc);
15201548
}
15211549

test/runnable/debug_info.d

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// REQUIRED_ARGS: -g
2+
3+
void main()
4+
{
5+
version(OSX) testDebugLineMacOS();
6+
}
7+
8+
version (OSX):
9+
10+
struct mach_header;
11+
struct mach_header_64;
12+
struct section;
13+
struct section_64;
14+
15+
version (D_LP64)
16+
{
17+
alias MachHeader = mach_header_64;
18+
alias Section = section_64;
19+
}
20+
21+
else
22+
{
23+
alias MachHeader = mach_header;
24+
alias Section = section;
25+
}
26+
27+
extern (C)
28+
{
29+
MachHeader* _dyld_get_image_header(uint image_index);
30+
const(section)* getsectbynamefromheader(in mach_header* mhp, in char* segname, in char* sectname);
31+
const(section_64)* getsectbynamefromheader_64(in mach_header_64* mhp, in char* segname, in char* sectname);
32+
}
33+
34+
const(Section)* getSectByNameFromHeader(MachHeader* mhp, in char* segname, in char* sectname)
35+
{
36+
version (D_LP64)
37+
return getsectbynamefromheader_64(mhp, segname, sectname);
38+
else
39+
return getsectbynamefromheader(mhp, segname, sectname);
40+
}
41+
42+
void testDebugLineMacOS()
43+
{
44+
auto header = _dyld_get_image_header(0);
45+
assert(header);
46+
47+
auto section = getSectByNameFromHeader(header, "__DWARF", "__debug_line");
48+
// verify that the __debug_line section is present in the final executable
49+
assert(section);
50+
}

0 commit comments

Comments
 (0)