diff --git a/lld/ELF/InputSection.cpp b/lld/ELF/InputSection.cpp --- a/lld/ELF/InputSection.cpp +++ b/lld/ELF/InputSection.cpp @@ -852,6 +852,9 @@ template void InputSection::relocateNonAlloc(uint8_t *buf, ArrayRef rels) { const unsigned bits = sizeof(typename ELFT::uint) * 8; + const bool isDebug = isDebugSection(*this); + const bool isDebugLocOrRanges = + isDebug && (name == ".debug_loc" || name == ".debug_ranges"); for (const RelTy &rel : rels) { RelType type = rel.getType(config->isMips64EL); @@ -902,11 +905,38 @@ continue; } - if (sym.isTls() && !Out::tlsPhdr) + if (sym.isTls() && !Out::tlsPhdr) { target->relocateNoSym(bufLoc, type, 0); - else - target->relocateNoSym(bufLoc, type, - SignExtend64(sym.getVA(addend))); + continue; + } + + if (isDebug && type == target->symbolicRel) { + // Resolve relocations in .debug_* referencing (discarded symbols or ICF + // folded section symbols) to a tombstone value. Resolving to addend is + // unsatisfactory because the result address range may collide with a + // valid range of low address, or leave multiple CUs claiming ownership of + // the same range of code, which may confuse consumers. + // + // To address the problems, we use -1 as a tombstone value for most + // .debug_* sections. We have to ignore the addend because we don't want + // to resolve an address attribute (which may have a non-zero addend) to + // -1+addend (wrap around to a low address). + // + // If the referenced symbol is discarded (made Undefined), or the + // section defining the referenced symbol is garbage collected, + // sym.getOutputSection() is nullptr. `ds->section->repl != ds->section` + // catches the ICF folded case. + // + // For pre-DWARF-v5 .debug_loc and .debug_ranges, -1 is a reserved value + // (base address selection entry), so -2 is used. + auto *ds = dyn_cast(&sym); + if (!sym.getOutputSection() || (ds && ds->section->repl != ds->section)) { + target->relocateNoSym(bufLoc, type, + isDebugLocOrRanges ? UINT64_MAX - 1 : UINT64_MAX); + continue; + } + } + target->relocateNoSym(bufLoc, type, SignExtend64(sym.getVA(addend))); } } diff --git a/lld/test/ELF/debug-dead-reloc-32.s b/lld/test/ELF/debug-dead-reloc-32.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/debug-dead-reloc-32.s @@ -0,0 +1,29 @@ +# REQUIRES: x86 +## Test we resolve symbolic relocations in .debug_* sections to a tombstone +## value if the referenced symbol is discarded (--gc-sections, non-prevailing +## section group, SHF_EXCLUDE, /DISCARD/, etc). + +# RUN: llvm-mc -filetype=obj -triple=i386 %s -o %t.o +# RUN: ld.lld %t.o -o %t +# RUN: llvm-objdump -s %t | FileCheck %s + +# CHECK: Contents of section .debug_loc: +# CHECK-NEXT: 0000 feffffff +# CHECK-NEXT: Contents of section .debug_ranges: +# CHECK-NEXT: 0000 feffffff +# CHECK-NEXT: Contents of section .debug_addr: +# CHECK-NEXT: 0000 ffffffff + +.section .text.1,"axe" + .byte 0 + +## Resolved to UINT32_C(-2), with the addend ignored. +## UINT32_C(-1) is a reserved value (base address selection entry) which can't be used. +.section .debug_loc + .long .text.1+8 +.section .debug_ranges + .long .text.1+16 + +## Resolved to UINT32_C(-1), with the addend ignored. +.section .debug_addr + .long .text.1+8 diff --git a/lld/test/ELF/debug-dead-reloc-icf.s b/lld/test/ELF/debug-dead-reloc-icf.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/debug-dead-reloc-icf.s @@ -0,0 +1,24 @@ +# REQUIRES: x86 +## Test we resolve symbolic relocations in .debug_* sections to a tombstone +## value if the referenced section symbol is folded into another section by ICF. +## Otherwise, we would leave entries in multiple CUs claiming ownership of the +## same range of code, which can confuse consumers. + +# RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t.o +# RUN: ld.lld --icf=all %t.o -o %t +# RUN: llvm-objdump -s %t | FileCheck %s + +# CHECK: Contents of section .debug_info: +# CHECK-NEXT: 0000 {{[0-9a-f]+}}000 00000000 ffffffff ffffffff + +.globl _start +_start: + ret + +## .text.1 will be folded by ICF. +.section .text.1,"ax" + ret + +.section .debug_info + .quad .text+8 + .quad .text.1+8 diff --git a/lld/test/ELF/debug-dead-reloc.s b/lld/test/ELF/debug-dead-reloc.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/debug-dead-reloc.s @@ -0,0 +1,53 @@ +# REQUIRES: x86 +## Test we resolve symbolic relocations in .debug_* sections to a tombstone +## value if the referenced symbol is discarded (--gc-sections, non-prevailing +## section group, SHF_EXCLUDE, /DISCARD/, etc). + +# RUN: echo '.globl _start; _start: call group' | llvm-mc -filetype=obj -triple=x86_64 - -o %t.o +# RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t1.o +# RUN: ld.lld --gc-sections %t.o %t1.o %t1.o -o %t +# RUN: llvm-objdump -s %t | FileCheck %s + +# CHECK: Contents of section .debug_loc: +# CHECK-NEXT: 0000 feffffff ffffffff feffffff ffffffff +# CHECK-NEXT: Contents of section .debug_ranges: +# CHECK-NEXT: 0000 feffffff ffffffff feffffff ffffffff +# CHECK-NEXT: Contents of section .debug_addr: +# CHECK-NEXT: 0000 {{.*}}000 00000000 {{.*}}000 00000000 +# CHECK-NEXT: 0010 ffffffff ffffffff {{.*}}000 00000000 +# CHECK-NEXT: Contents of section .debug_foo: +# CHECK-NEXT: 0000 ffffffff ffffffff 08000000 00000000 +# CHECK-NEXT: 0010 ffffffff ffffffff 08000000 00000000 + +.section .text.1,"ax" + .byte 0 +.section .text.2,"axe" + .byte 0 +.section .text.3,"axG",@progbits,group,comdat +.globl group +group: + .byte 0 + +## Resolved to UINT64_C(-2), with the addend ignored. +## UINT64_C(-1) is a reserved value (base address selection entry) which can't be used. +.section .debug_loc + .quad .text.1+8 +.section .debug_ranges + .quad .text.2+16 + +.section .debug_addr +## .text.3 is a local symbol. The symbol defined in a non-prevailing group is +## discarded. Resolved to UINT64_C(-1). + .quad .text.3+24 +## group is a non-local symbol. The relocation from the second %t1.o gets +## resolved to the prevailing copy. + .quad group+32 + +.section .debug_foo + .quad .text.1+8 + +## We only deal with DW_FORM_addr. Don't special case short-range absolute +## relocations. Treat them like regular absolute relocations referencing +## discarded symbols, which are resolved to the addend. + .long .text.1+8 + .long 0