diff --git a/lld/MachO/Dwarf.cpp b/lld/MachO/Dwarf.cpp index 47dc51e6196d1..bcabff3d51f3e 100644 --- a/lld/MachO/Dwarf.cpp +++ b/lld/MachO/Dwarf.cpp @@ -25,19 +25,22 @@ std::unique_ptr 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(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(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; diff --git a/lld/MachO/InputFiles.cpp b/lld/MachO/InputFiles.cpp index d0128d03a9eab..f9fc7aa97f343 100644 --- a/lld/MachO/InputFiles.cpp +++ b/lld/MachO/InputFiles.cpp @@ -409,15 +409,14 @@ void ObjFile::parseSections(ArrayRef sectionHeaders) { addrSigSection = sections.back(); auto *isec = make(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}); } } } diff --git a/lld/MachO/InputFiles.h b/lld/MachO/InputFiles.h index 2d5bceb160445..92da714027e8b 100644 --- a/lld/MachO/InputFiles.h +++ b/lld/MachO/InputFiles.h @@ -181,7 +181,6 @@ class ObjFile final : public InputFile { const uint32_t modTime; bool forceHidden; bool builtFromBitcode; - std::vector debugSections; std::vector callGraph; llvm::DenseMap fdes; std::vector aliases; diff --git a/lld/test/MachO/dwarf-strp-relocations.s b/lld/test/MachO/dwarf-strp-relocations.s new file mode 100644 index 0000000000000..bc873d6c50a0c --- /dev/null +++ b/lld/test/MachO/dwarf-strp-relocations.s @@ -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"