diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h --- a/lld/ELF/Config.h +++ b/lld/ELF/Config.h @@ -147,6 +147,7 @@ bool defineCommon; bool demangle = true; bool dependentLibraries; + bool disableDebugRangesMarking = false; bool disableVerify; bool ehFrameHdr; bool emitLLVM; diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -898,6 +898,9 @@ args.hasFlag(OPT_optimize_bb_jumps, OPT_no_optimize_bb_jumps, false); config->demangle = args.hasFlag(OPT_demangle, OPT_no_demangle, true); config->dependentLibraries = args.hasFlag(OPT_dependent_libraries, OPT_no_dependent_libraries, true); + config->disableDebugRangesMarking = + args.hasFlag(OPT_disable_debug_ranges_marking, + OPT_no_disable_debug_ranges_marking, false); config->disableVerify = args.hasArg(OPT_disable_verify); config->discard = getDiscard(args); config->dwoDir = args.getLastArgValue(OPT_plugin_opt_dwo_dir_eq); diff --git a/lld/ELF/InputSection.h b/lld/ELF/InputSection.h --- a/lld/ELF/InputSection.h +++ b/lld/ELF/InputSection.h @@ -386,6 +386,7 @@ private: template void copyRelocations(uint8_t *buf, llvm::ArrayRef rels); + uint64_t getRelocValueForDeletedCode(RelType type, bool isDebugLocOrRanges); template void copyShtGroup(uint8_t *buf); }; diff --git a/lld/ELF/InputSection.cpp b/lld/ELF/InputSection.cpp --- a/lld/ELF/InputSection.cpp +++ b/lld/ELF/InputSection.cpp @@ -842,6 +842,19 @@ } } +// This function returns special value which is used to indicate reference +// from debug section into the deleted code. +uint64_t InputSection::getRelocValueForDeletedCode(RelType type, + bool isDebugLocOrRanges) { + uint64_t res = + isDebugLocOrRanges ? 0xfffffffffffffffeULL : 0xffffffffffffffffULL; + + if (config->is64 && config->emachine == EM_X86_64 && type == R_X86_64_32) + res &= 0x00000000ffffffffULL; + + return res; +} + // This function applies relocations to sections without SHF_ALLOC bit. // Such sections are never mapped to memory at runtime. Debug sections are // an example. Relocations in non-alloc sections are much easier to @@ -853,6 +866,11 @@ void InputSection::relocateNonAlloc(uint8_t *buf, ArrayRef rels) { const unsigned bits = sizeof(typename ELFT::uint) * 8; + bool isDebug = isDebugSection(*this); + StringRef nameWOPrefix = name.substr(name.find_first_not_of(".z")); + bool isDebugLocOrRanges = + nameWOPrefix == "debug_loc" || nameWOPrefix == "debug_ranges"; + for (const RelTy &rel : rels) { RelType type = rel.getType(config->isMips64EL); @@ -904,9 +922,16 @@ if (sym.isTls() && !Out::tlsPhdr) target->relocateNoSym(bufLoc, type, 0); - else + else if (sym.getOutputSection() || !isDebug || + config->disableDebugRangesMarking) target->relocateNoSym(bufLoc, type, SignExtend64(sym.getVA(addend))); + // If the relocation from .debug* section points to a deleted section, + // e.g. due to --gc-sections, then it is neccessary to resolve such + // relocation to the special value indicating deleted code. + else + target->relocateNoSym( + bufLoc, type, getRelocValueForDeletedCode(type, isDebugLocOrRanges)); } } diff --git a/lld/ELF/Options.td b/lld/ELF/Options.td --- a/lld/ELF/Options.td +++ b/lld/ELF/Options.td @@ -132,6 +132,10 @@ "Demangle symbol names (default)", "Do not demangle symbol names">; +defm disable_debug_ranges_marking: B<"disable-debug-ranges-marking", + "Disable marking of debug address ranges pointing to deleted code", + "Enable marking of debug address ranges pointing to deleted code(default)">; + def disable_new_dtags: F<"disable-new-dtags">, HelpText<"Disable new dynamic tags">; diff --git a/lld/docs/ld.lld.1 b/lld/docs/ld.lld.1 --- a/lld/docs/ld.lld.1 +++ b/lld/docs/ld.lld.1 @@ -142,6 +142,8 @@ .Ql --defsym=foo=bar+0x100 . .It Fl -demangle Demangle symbol names. +.It Fl -disable-debug-ranges-marking +Disable marking of debug address ranges with predefined value. .It Fl -disable-new-dtags Disable new dynamic tags. .It Fl -discard-all , Fl x diff --git a/lld/test/ELF/gc-sections-debuginfo.s b/lld/test/ELF/gc-sections-debuginfo.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/gc-sections-debuginfo.s @@ -0,0 +1,74 @@ +# REQUIRES: x86 + +# RUN: echo '.section .text,"a"; .byte 0; .section .debug_foo,"",@progbits; .quad .text; .section .debug_loc,"",@progbits; .quad .text' \ +# RUN: | llvm-mc --filetype=obj -triple=x86_64-unknown-linux - -o %t +# RUN: ld.lld %t -o %t2 --gc-sections +# RUN: llvm-readobj --sections --section-data %t2 | FileCheck %s --check-prefixes=CHECK,CHECK64 + +# RUN: echo '.section .text,"a"; .byte 0; .section .debug_foo,"",@progbits; .long .text; .section .debug_loc,"",@progbits; .long .text' \ +# RUN: | llvm-mc --filetype=obj -triple=i386-unknown-linux - -o %t +# RUN: ld.lld %t -o %t2 --gc-sections +# RUN: llvm-readobj --sections --section-data %t2 | FileCheck %s --check-prefixes=CHECK,CHECK32 + +# RUN: echo '.section .text,"a"; .byte 0; .section .debug_foo,"",@progbits; .long .text; .section .debug_loc,"",@progbits; .long .text' \ +# RUN: | llvm-mc --filetype=obj -triple=x86_64-unknown-linux32 - -o %t +# RUN: ld.lld %t -o %t2 --gc-sections +# RUN: llvm-readobj --sections --section-data %t2 | FileCheck %s --check-prefixes=CHECK,CHECK32 + +# RUN: echo '.section .text,"a"; .byte 0; .section .debug_foo,"",@progbits; .quad .text; .section .debug_loc,"",@progbits; .quad .text' \ +# RUN: | llvm-mc --filetype=obj -triple=x86_64-unknown-linux - -o %t +# RUN: ld.lld %t -o %t2 --gc-sections --disable-debug-ranges-marking +# RUN: llvm-readobj --sections --section-data %t2 | FileCheck %s --check-prefixes=CHECK,CHECK64ZERO + +# When --gc-sections is used, the linker deletes unused text sections, +# but their debug data is left in the binary. That debug data could have relocations +# pointing to deleted sections. Address ranges of such debug data could overlap +# with other address ranges for correct debug data. To prevent addresses clashing +# it is neccessary to resolve such relocations to an predefined value. +# That test checks that relocations from the .debug_foo +# section pointing to a deleted .text section would be resolved +# to 0xffffffffffffffff. And relocations from the .debug_loc section +# pointing to a deleted .text section would be resolved +# to 0xfffffffffffffffe. + +# CHECK-NOT: Name: .text + +# CHECK: Name: .debug_foo +# CHECK-NEXT: Type: SHT_PROGBITS +# CHECK-NEXT: Flags [ +# CHECK-NEXT: ] +# CHECK-NEXT: Address: +# CHECK-NEXT: Offset: +# CHECK64-NEXT: Size: 8 +# CHECK32-NEXT: Size: 4 +# CHECK64ZERO-NEXT: Size: 8 +# CHECK-NEXT: Link: +# CHECK-NEXT: Info: +# CHECK-NEXT: AddressAlignment: +# CHECK-NEXT: EntrySize: +# CHECK-NEXT: SectionData ( +# CHECK64-NEXT: 0000: FFFFFFFF FFFFFFFF +# CHECK32-NEXT: 0000: FFFFFFFF +# CHECK64ZERO-NEXT: 0000: 00000000 00000000 +# CHECK-NEXT: ) + +# CHECK: Name: .debug_loc +# CHECK-NEXT: Type: SHT_PROGBITS +# CHECK-NEXT: Flags [ +# CHECK-NEXT: ] +# CHECK-NEXT: Address: +# CHECK-NEXT: Offset: +# CHECK64-NEXT: Size: 8 +# CHECK32-NEXT: Size: 4 +# CHECK64ZERO-NEXT: Size: 8 +# CHECK-NEXT: Link: +# CHECK-NEXT: Info: +# CHECK-NEXT: AddressAlignment: +# CHECK-NEXT: EntrySize: +# CHECK-NEXT: SectionData ( +# CHECK64-NEXT: 0000: FEFFFFFF FFFFFFFF +# CHECK32-NEXT: 0000: FEFFFFFF +# CHECK64ZERO-NEXT: 0000: 00000000 00000000 +# CHECK-NEXT: ) + +