Skip to content

Commit f1d5cbb

Browse files
committed
[dsymutil] Add preliminary support for DWARF 5.
Currently dsymutil will silently fail when processing binaries with Dwarf 5 debug info. This patch adds rudimentary support for Dwarf 5 in dsymutil. - Recognize relocations in the debug_addr section. - Recognize (a subset of) Dwarf 5 form values. - Emits valid Dwarf 5 compile unit header chains. To simplify things (and avoid having to emit indexed sections) I decided to emit the relocated addresses directly in the debug info section. - DW_FORM_strx gets relocated and rewritten to DW_FORM_strp - DW_FORM_addrx gets relocated and rewritten to DW_FORM_addr Obviously there's a lot of work left, but this should be a step in the right direction. rdar://62345491 Differential revision: https://reviews.llvm.org/D94323
1 parent 8a20e2b commit f1d5cbb

File tree

11 files changed

+245
-81
lines changed

11 files changed

+245
-81
lines changed

llvm/include/llvm/DWARFLinker/DWARFLinker.h

+5-1
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,9 @@ class AddressesMap {
8585
virtual bool applyValidRelocs(MutableArrayRef<char> Data, uint64_t BaseOffset,
8686
bool IsLittleEndian) = 0;
8787

88+
/// Relocate the given address offset if a valid relocation exists.
89+
virtual llvm::Expected<uint64_t> relocateIndexedAddr(uint64_t Offset) = 0;
90+
8891
/// Returns all valid functions address ranges(i.e., those ranges
8992
/// which points to sections with code).
9093
virtual RangesTy &getValidAddressRanges() = 0;
@@ -183,7 +186,8 @@ class DwarfEmitter {
183186
///
184187
/// As a side effect, this also switches the current Dwarf version
185188
/// of the MC layer to the one of U.getOrigUnit().
186-
virtual void emitCompileUnitHeader(CompileUnit &Unit) = 0;
189+
virtual void emitCompileUnitHeader(CompileUnit &Unit,
190+
unsigned DwarfVersion) = 0;
187191

188192
/// Recursively emit the DIE tree rooted at \p Die.
189193
virtual void emitDIE(DIE &Die) = 0;

llvm/include/llvm/DWARFLinker/DWARFLinkerCompileUnit.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ class CompileUnit {
159159
/// Compute the end offset for this unit. Must be called after the CU's DIEs
160160
/// have been cloned. \returns the next unit offset (which is also the
161161
/// current debug_info section size).
162-
uint64_t computeNextUnitOffset();
162+
uint64_t computeNextUnitOffset(uint16_t DwarfVersion);
163163

164164
/// Keep track of a forward reference to DIE \p Die in \p RefUnit by \p
165165
/// Attr. The attribute should be fixed up later to point to the absolute

llvm/include/llvm/DWARFLinker/DWARFStreamer.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ class DwarfStreamer : public DwarfEmitter {
6464
///
6565
/// As a side effect, this also switches the current Dwarf version
6666
/// of the MC layer to the one of U.getOrigUnit().
67-
void emitCompileUnitHeader(CompileUnit &Unit) override;
67+
void emitCompileUnitHeader(CompileUnit &Unit, unsigned DwarfVersion) override;
6868

6969
/// Recursively emit the DIE tree rooted at \p Die.
7070
void emitDIE(DIE &Die) override;

llvm/lib/DWARFLinker/DWARFLinker.cpp

+36-9
Original file line numberDiff line numberDiff line change
@@ -419,7 +419,6 @@ void DWARFLinker::cleanupAuxiliarryData(LinkContext &Context) {
419419
DIEAlloc.Reset();
420420
}
421421

422-
423422
/// Check if a variable describing DIE should be kept.
424423
/// \returns updated TraversalFlags.
425424
unsigned DWARFLinker::shouldKeepVariableDIE(AddressesMap &RelocMgr,
@@ -845,9 +844,12 @@ void DWARFLinker::assignAbbrev(DIEAbbrev &Abbrev) {
845844
unsigned DWARFLinker::DIECloner::cloneStringAttribute(
846845
DIE &Die, AttributeSpec AttrSpec, const DWARFFormValue &Val,
847846
const DWARFUnit &U, OffsetsStringPool &StringPool, AttributesInfo &Info) {
847+
Optional<const char *> String = Val.getAsCString();
848+
if (!String)
849+
return 0;
850+
848851
// Switch everything to out of line strings.
849-
const char *String = *Val.getAsCString();
850-
auto StringEntry = StringPool.getEntry(String);
852+
auto StringEntry = StringPool.getEntry(*String);
851853

852854
// Update attributes info.
853855
if (AttrSpec.Attr == dwarf::DW_AT_name)
@@ -1056,6 +1058,7 @@ unsigned DWARFLinker::DIECloner::cloneBlockAttribute(
10561058
unsigned DWARFLinker::DIECloner::cloneAddressAttribute(
10571059
DIE &Die, AttributeSpec AttrSpec, const DWARFFormValue &Val,
10581060
const CompileUnit &Unit, AttributesInfo &Info) {
1061+
dwarf::Form Form = AttrSpec.Form;
10591062
uint64_t Addr = *Val.getAsAddress();
10601063

10611064
if (LLVM_UNLIKELY(Linker.Options.Update)) {
@@ -1105,8 +1108,19 @@ unsigned DWARFLinker::DIECloner::cloneAddressAttribute(
11051108
Addr = (Info.OrigCallPc ? Info.OrigCallPc : Addr) + Info.PCOffset;
11061109
}
11071110

1111+
// If this is an indexed address emit the relocated address.
1112+
if (Form == dwarf::DW_FORM_addrx) {
1113+
if (llvm::Expected<uint64_t> RelocAddr =
1114+
ObjFile.Addresses->relocateIndexedAddr(Addr)) {
1115+
Addr = *RelocAddr;
1116+
Form = dwarf::DW_FORM_addr;
1117+
} else {
1118+
Linker.reportWarning(toString(RelocAddr.takeError()), ObjFile);
1119+
}
1120+
}
1121+
11081122
Die.addValue(DIEAlloc, static_cast<dwarf::Attribute>(AttrSpec.Attr),
1109-
static_cast<dwarf::Form>(AttrSpec.Form), DIEInteger(Addr));
1123+
static_cast<dwarf::Form>(Form), DIEInteger(Addr));
11101124
return Unit.getOrigUnit().getAddressByteSize();
11111125
}
11121126

@@ -1188,6 +1202,11 @@ unsigned DWARFLinker::DIECloner::cloneAttribute(
11881202
switch (AttrSpec.Form) {
11891203
case dwarf::DW_FORM_strp:
11901204
case dwarf::DW_FORM_string:
1205+
case dwarf::DW_FORM_strx:
1206+
case dwarf::DW_FORM_strx1:
1207+
case dwarf::DW_FORM_strx2:
1208+
case dwarf::DW_FORM_strx3:
1209+
case dwarf::DW_FORM_strx4:
11911210
return cloneStringAttribute(Die, AttrSpec, Val, U, StringPool, Info);
11921211
case dwarf::DW_FORM_ref_addr:
11931212
case dwarf::DW_FORM_ref1:
@@ -1204,6 +1223,7 @@ unsigned DWARFLinker::DIECloner::cloneAttribute(
12041223
return cloneBlockAttribute(Die, File, Unit, AttrSpec, Val, AttrSize,
12051224
IsLittleEndian);
12061225
case dwarf::DW_FORM_addr:
1226+
case dwarf::DW_FORM_addrx:
12071227
return cloneAddressAttribute(Die, AttrSpec, Val, Unit, Info);
12081228
case dwarf::DW_FORM_data1:
12091229
case dwarf::DW_FORM_data2:
@@ -1284,6 +1304,9 @@ shouldSkipAttribute(DWARFAbbreviationDeclaration::AttributeSpec AttrSpec,
12841304
case dwarf::DW_AT_high_pc:
12851305
case dwarf::DW_AT_ranges:
12861306
return SkipPC;
1307+
case dwarf::DW_AT_str_offsets_base:
1308+
// FIXME: Use the string offset table with Dwarf 5.
1309+
return true;
12871310
case dwarf::DW_AT_location:
12881311
case dwarf::DW_AT_frame_base:
12891312
// FIXME: for some reason dsymutil-classic keeps the location attributes
@@ -2127,22 +2150,24 @@ uint64_t DWARFLinker::DIECloner::cloneAllCompileUnits(
21272150
const uint64_t StartOutputDebugInfoSize = OutputDebugInfoSize;
21282151

21292152
for (auto &CurrentUnit : CompileUnits) {
2153+
const uint16_t DwarfVersion = CurrentUnit->getOrigUnit().getVersion();
2154+
const uint32_t UnitHeaderSize = DwarfVersion >= 5 ? 12 : 11;
21302155
auto InputDIE = CurrentUnit->getOrigUnit().getUnitDIE();
21312156
CurrentUnit->setStartOffset(OutputDebugInfoSize);
21322157
if (!InputDIE) {
2133-
OutputDebugInfoSize = CurrentUnit->computeNextUnitOffset();
2158+
OutputDebugInfoSize = CurrentUnit->computeNextUnitOffset(DwarfVersion);
21342159
continue;
21352160
}
21362161
if (CurrentUnit->getInfo(0).Keep) {
21372162
// Clone the InputDIE into your Unit DIE in our compile unit since it
21382163
// already has a DIE inside of it.
21392164
CurrentUnit->createOutputDIE();
21402165
cloneDIE(InputDIE, File, *CurrentUnit, StringPool, 0 /* PC offset */,
2141-
11 /* Unit Header size */, 0, IsLittleEndian,
2166+
UnitHeaderSize, 0, IsLittleEndian,
21422167
CurrentUnit->getOutputUnitDIE());
21432168
}
21442169

2145-
OutputDebugInfoSize = CurrentUnit->computeNextUnitOffset();
2170+
OutputDebugInfoSize = CurrentUnit->computeNextUnitOffset(DwarfVersion);
21462171

21472172
if (!Linker.Options.NoOutput) {
21482173
assert(Emitter);
@@ -2183,12 +2208,14 @@ uint64_t DWARFLinker::DIECloner::cloneAllCompileUnits(
21832208
if (!CurrentUnit->getOutputUnitDIE())
21842209
continue;
21852210

2211+
unsigned DwarfVersion = CurrentUnit->getOrigUnit().getVersion();
2212+
21862213
assert(Emitter->getDebugInfoSectionSize() ==
21872214
CurrentUnit->getStartOffset());
2188-
Emitter->emitCompileUnitHeader(*CurrentUnit);
2215+
Emitter->emitCompileUnitHeader(*CurrentUnit, DwarfVersion);
21892216
Emitter->emitDIE(*CurrentUnit->getOutputUnitDIE());
21902217
assert(Emitter->getDebugInfoSectionSize() ==
2191-
CurrentUnit->computeNextUnitOffset());
2218+
CurrentUnit->computeNextUnitOffset(DwarfVersion));
21922219
}
21932220
}
21942221

llvm/lib/DWARFLinker/DWARFLinkerCompileUnit.cpp

+3-3
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ StringRef CompileUnit::getSysRoot() {
3636
}
3737
return SysRoot;
3838
}
39-
39+
4040
void CompileUnit::markEverythingAsKept() {
4141
unsigned Idx = 0;
4242

@@ -69,10 +69,10 @@ void CompileUnit::markEverythingAsKept() {
6969
}
7070
}
7171

72-
uint64_t CompileUnit::computeNextUnitOffset() {
72+
uint64_t CompileUnit::computeNextUnitOffset(uint16_t DwarfVersion) {
7373
NextUnitOffset = StartOffset;
7474
if (NewUnit) {
75-
NextUnitOffset += 11 /* Header size */;
75+
NextUnitOffset += (DwarfVersion >= 5) ? 12 : 11; // Header size
7676
NextUnitOffset += NewUnit->getUnitDie().getSize();
7777
}
7878
return NextUnitOffset;

llvm/lib/DWARFLinker/DWARFStreamer.cpp

+38-12
Original file line numberDiff line numberDiff line change
@@ -121,16 +121,23 @@ void DwarfStreamer::switchToDebugInfoSection(unsigned DwarfVersion) {
121121

122122
/// Emit the compilation unit header for \p Unit in the debug_info section.
123123
///
124-
/// A Dwarf section header is encoded as:
124+
/// A Dwarf 4 section header is encoded as:
125125
/// uint32_t Unit length (omitting this field)
126126
/// uint16_t Version
127127
/// uint32_t Abbreviation table offset
128128
/// uint8_t Address size
129-
///
130129
/// Leading to a total of 11 bytes.
131-
void DwarfStreamer::emitCompileUnitHeader(CompileUnit &Unit) {
132-
unsigned Version = Unit.getOrigUnit().getVersion();
133-
switchToDebugInfoSection(Version);
130+
///
131+
/// A Dwarf 5 section header is encoded as:
132+
/// uint32_t Unit length (omitting this field)
133+
/// uint16_t Version
134+
/// uint8_t Unit type
135+
/// uint8_t Address size
136+
/// uint32_t Abbreviation table offset
137+
/// Leading to a total of 12 bytes.
138+
void DwarfStreamer::emitCompileUnitHeader(CompileUnit &Unit,
139+
unsigned DwarfVersion) {
140+
switchToDebugInfoSection(DwarfVersion);
134141

135142
/// The start of the unit within its section.
136143
Unit.setLabelBegin(Asm->createTempSymbol("cu_begin"));
@@ -140,13 +147,22 @@ void DwarfStreamer::emitCompileUnitHeader(CompileUnit &Unit) {
140147
// been computed in CompileUnit::computeOffsets(). Subtract 4 to that size to
141148
// account for the length field.
142149
Asm->emitInt32(Unit.getNextUnitOffset() - Unit.getStartOffset() - 4);
143-
Asm->emitInt16(Version);
144-
145-
// We share one abbreviations table across all units so it's always at the
146-
// start of the section.
147-
Asm->emitInt32(0);
148-
Asm->emitInt8(Unit.getOrigUnit().getAddressByteSize());
149-
DebugInfoSectionSize += 11;
150+
Asm->emitInt16(DwarfVersion);
151+
152+
if (DwarfVersion >= 5) {
153+
Asm->emitInt8(dwarf::DW_UT_compile);
154+
Asm->emitInt8(Unit.getOrigUnit().getAddressByteSize());
155+
// We share one abbreviations table across all units so it's always at the
156+
// start of the section.
157+
Asm->emitInt32(0);
158+
DebugInfoSectionSize += 12;
159+
} else {
160+
// We share one abbreviations table across all units so it's always at the
161+
// start of the section.
162+
Asm->emitInt32(0);
163+
Asm->emitInt8(Unit.getOrigUnit().getAddressByteSize());
164+
DebugInfoSectionSize += 11;
165+
}
150166

151167
// Remember this CU.
152168
EmittedUnits.push_back({Unit.getUniqueID(), Unit.getLabelBegin()});
@@ -211,6 +227,16 @@ void DwarfStreamer::emitStrings(const NonRelocatableStringpool &Pool) {
211227
// Emit a null terminator.
212228
Asm->emitInt8(0);
213229
}
230+
231+
#if 0
232+
if (DwarfVersion >= 5) {
233+
// Emit an empty string offset section.
234+
Asm->OutStreamer->SwitchSection(MOFI->getDwarfStrOffSection());
235+
Asm->emitDwarfUnitLength(4, "Length of String Offsets Set");
236+
Asm->emitInt16(DwarfVersion);
237+
Asm->emitInt16(0);
238+
}
239+
#endif
214240
}
215241

216242
void DwarfStreamer::emitDebugNames(
Binary file not shown.
Binary file not shown.
+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
Test DWARF5 support in dsymutil. Currently this still generates an empty dSYM.
2+
3+
$ cat dwarf5.c
4+
__attribute__ ((optnone))
5+
int foo() {
6+
volatile i;
7+
return i;
8+
}
9+
10+
int main(int argc, char** argv) {
11+
return foo();
12+
}
13+
14+
$ clang -gdwarf-5 dwarf5.c -c -o dwarf5.o
15+
$ clang dwarf5.o -o dwarf5.out
16+
17+
RUN: dsymutil -oso-prepend-path %p/../Inputs %p/../Inputs/private/tmp/dwarf5/dwarf5.out -o %t.dSYM 2>&1 | FileCheck %s --allow-empty
18+
RUN: llvm-dwarfdump --verify %t.dSYM 2>&1 | FileCheck %s
19+
CHECK-NOT: error:
20+
21+
RUN: llvm-dwarfdump %t.dSYM | FileCheck %s --check-prefix DWARF
22+
DWARF: DW_TAG_compile_unit
23+
DWARF: DW_AT_producer ("clang version 12.0.0
24+
DWARF: DW_AT_language (DW_LANG_C99)
25+
DWARF: DW_AT_name ("dwarf5.c")
26+
DWARF: DW_AT_LLVM_sysroot ("/")
27+
DWARF: DW_AT_stmt_list (0x00000000)
28+
DWARF: DW_AT_comp_dir ("/private/tmp/dwarf5")
29+
DWARF: DW_AT_low_pc (0x0000000100003f80)
30+
DWARF: DW_AT_high_pc (0x0000000100003fb1)
31+
DWARF: DW_AT_addr_base (0x00000008)
32+
DWARF: DW_TAG_subprogram
33+
DWARF: DW_AT_name ("foo")
34+
DWARF: DW_AT_decl_file (0x00)
35+
DWARF: DW_AT_decl_line (2)
36+
DWARF: DW_AT_type (0x0000006c "int")
37+
DWARF: DW_AT_external (true)
38+
DWARF: DW_TAG_variable
39+
DWARF: DW_AT_name ("i")
40+
DWARF: DW_AT_decl_file (0x00)
41+
DWARF: DW_AT_decl_line (3)
42+
DWARF: DW_AT_type (0x00000073 "volatile int")
43+
DWARF: DW_TAG_subprogram
44+
DWARF: DW_AT_name ("main")
45+
DWARF: DW_AT_decl_file (0x00)
46+
DWARF: DW_AT_decl_line (7)
47+
DWARF: DW_AT_prototyped (true)
48+
DWARF: DW_AT_type (0x0000006c "int")
49+
DWARF: DW_AT_external (true)
50+
DWARF: DW_TAG_formal_parameter
51+
DWARF: DW_AT_name ("argc")
52+
DWARF: DW_AT_decl_file (0x00)
53+
DWARF: DW_AT_decl_line (7)
54+
DWARF: DW_AT_type (0x0000006c "int")
55+
DWARF: DW_TAG_formal_parameter
56+
DWARF: DW_AT_name ("argv")
57+
DWARF: DW_AT_decl_file (0x00)
58+
DWARF: DW_AT_decl_line (7)
59+
DWARF: DW_AT_type (0x00000078 "char**")

0 commit comments

Comments
 (0)