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,15 @@ if (sym.isTls() && !Out::tlsPhdr) target->relocateNoSym(bufLoc, type, 0); - else + else if (sym.getOutputSection() || !isDebug) 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/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,65 @@ +# 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 + +# 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 +# CHECK-NEXT: Link: +# CHECK-NEXT: Info: +# CHECK-NEXT: AddressAlignment: +# CHECK-NEXT: EntrySize: +# CHECK-NEXT: SectionData ( +# CHECK64-NEXT: 0000: FFFFFFFF FFFFFFFF +# CHECK32-NEXT: 0000: FFFFFFFF +# 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 +# CHECK-NEXT: Link: +# CHECK-NEXT: Info: +# CHECK-NEXT: AddressAlignment: +# CHECK-NEXT: EntrySize: +# CHECK-NEXT: SectionData ( +# CHECK64-NEXT: 0000: FEFFFFFF FFFFFFFF +# CHECK32-NEXT: 0000: FEFFFFFF +# CHECK-NEXT: ) + +