Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 16 additions & 13 deletions lld/MachO/Dwarf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,22 @@ std::unique_ptr<DwarfObject> DwarfObject::create(ObjFile *obj) {
// to get that path. The debugger will locate the debug info via the object
// file paths that we emit in our STABS symbols, so we don't need to process &
// emit them ourselves.
for (const InputSection *isec : obj->debugSections) {
if (StringRef *s =
StringSwitch<StringRef *>(isec->getName())
.Case(section_names::debugInfo, &dObj->infoSection.Data)
.Case(section_names::debugLine, &dObj->lineSection.Data)
.Case(section_names::debugStrOffs, &dObj->strOffsSection.Data)
.Case(section_names::debugAbbrev, &dObj->abbrevSection)
.Case(section_names::debugStr, &dObj->strSection)
.Default(nullptr)) {
*s = toStringRef(isec->data);
hasDwarfInfo = true;
}
}
for (const Section *sec : obj->sections)
if (sec->segname == segment_names::dwarf)
for (const Subsection &subsec : sec->subsections)
if (const InputSection *isec = subsec.isec)
if (StringRef *s =
StringSwitch<StringRef *>(isec->getName())
.Case(section_names::debugInfo, &dObj->infoSection.Data)
.Case(section_names::debugLine, &dObj->lineSection.Data)
.Case(section_names::debugStrOffs,
&dObj->strOffsSection.Data)
.Case(section_names::debugAbbrev, &dObj->abbrevSection)
.Case(section_names::debugStr, &dObj->strSection)
.Default(nullptr)) {
*s = toStringRef(isec->data);
hasDwarfInfo = true;
}

if (hasDwarfInfo)
return dObj;
Expand Down
15 changes: 7 additions & 8 deletions lld/MachO/InputFiles.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -409,15 +409,14 @@ void ObjFile::parseSections(ArrayRef<SectionHeader> sectionHeaders) {
addrSigSection = sections.back();

auto *isec = make<ConcatInputSection>(section, data, align);
// Keep DWARF sections long enough for relocation rewriting; dropping them
// here leaves section-relative relocations (e.g. DW_FORM_strp into
// __debug_str) dangling and crashes the linker. Mark them dead so they
// are not emitted; Mach-O keeps STABS-only debug output.
if (isDebugSection(isec->getFlags()) &&
isec->getSegName() == segment_names::dwarf) {
// Instead of emitting DWARF sections, we emit STABS symbols to the
// object files that contain them. We filter them out early to avoid
// parsing their relocations unnecessarily.
debugSections.push_back(isec);
} else {
section.subsections.push_back({0, isec});
}
isec->getSegName() == segment_names::dwarf)
isec->live = false;
section.subsections.push_back({0, isec});
}
}
}
Expand Down
1 change: 0 additions & 1 deletion lld/MachO/InputFiles.h
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,6 @@ class ObjFile final : public InputFile {
const uint32_t modTime;
bool forceHidden;
bool builtFromBitcode;
std::vector<ConcatInputSection *> debugSections;
std::vector<CallGraphEntry> callGraph;
llvm::DenseMap<ConcatInputSection *, FDE> fdes;
std::vector<AliasSymbol *> aliases;
Expand Down
63 changes: 63 additions & 0 deletions lld/test/MachO/dwarf-strp-relocations.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# REQUIRES: x86
# RUN: rm -rf %t; split-file %s %t

# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %t/test.s -o %t/test.o
# RUN: %lld -dylib %t/test.o -o %t/test.dylib
# RUN: llvm-objdump --section-headers %t/test.dylib | FileCheck %s

## Test that lld can handle section-relative relocations in DWARF sections,
## specifically DW_FORM_strp which creates X86_64_RELOC_UNSIGNED relocations
## to the __debug_str section. This previously caused linker crashes with
## "malformed relocation" errors on macOS.
##
## The test verifies that:
## 1. The link completes successfully without crashing (key requirement)
## 2. DWARF sections are processed for relocations but not emitted to output
## (MachO traditionally uses STABS, not DWARF, for debug info)
##
## Negative checks ensure DWARF sections are NOT in the final binary, preventing
## regression where they might be accidentally emitted.

# CHECK-NOT: __debug_info
# CHECK-NOT: __debug_abbrev
# CHECK-NOT: __debug_str

#--- test.s
.section __TEXT,__text,regular,pure_instructions
.globl _main
_main:
movl $42, %eax
retq

.section __DWARF,__debug_abbrev,regular,debug
Labbrev_begin:
.byte 1 ## Abbrev code
.byte 17 ## DW_TAG_compile_unit
.byte 1 ## DW_CHILDREN_yes
.byte 37 ## DW_AT_producer
.byte 14 ## DW_FORM_strp (string table pointer!)
.byte 3 ## DW_AT_name
.byte 14 ## DW_FORM_strp
.byte 0 ## End attributes
.byte 0
.byte 0 ## End abbrev table

.section __DWARF,__debug_info,regular,debug
Linfo_begin:
.long Linfo_end - Linfo_begin - 4 ## Length
.short 4 ## DWARF version 4
.long 0 ## Abbrev offset
.byte 8 ## Address size
.byte 1 ## Abbrev code
## These .long directives create section-relative relocations (X86_64_RELOC_UNSIGNED)
## to the __debug_str section. This is the critical test case.
.long Lproducer - Ldebug_str ## DW_AT_producer (section-relative!)
.long Lfilename - Ldebug_str ## DW_AT_name (section-relative!)
Linfo_end:

.section __DWARF,__debug_str,regular,debug
Ldebug_str:
Lproducer:
.asciz "Test Producer 1.0"
Lfilename:
.asciz "test.c"