Index: lld/trunk/ELF/InputSection.h =================================================================== --- lld/trunk/ELF/InputSection.h +++ lld/trunk/ELF/InputSection.h @@ -144,9 +144,22 @@ typename InputSectionBase::Kind SectionKind); // For each piece of data, we maintain the offsets in the input section and - // in the output section. The latter may be -1 if it is not assigned yet. + // in the output section. std::vector> Offsets; + // Merge input sections may use the following special values as the output + // section offset: + enum { + // The piece is dead. + PieceDead = uintX_t(-1), + // The piece is live, and an offset has not yet been assigned. After offsets + // have been assigned, if the output section uses tail merging, the field + // will still have this value and the output section offset is available + // from MergeOutputSection::getOffset(). Otherwise, this value has no + // special significance, it just means that the offset is 0. + PieceLive = uintX_t(0), + }; + std::pair *, uintX_t> getRangeAndSize(uintX_t Offset); }; Index: lld/trunk/ELF/InputSection.cpp =================================================================== --- lld/trunk/ELF/InputSection.cpp +++ lld/trunk/ELF/InputSection.cpp @@ -419,7 +419,8 @@ StringRef Data((const char *)D.data(), D.size()); std::vector> &Offsets = this->Offsets; - uintX_t V = Config->GcSections ? -1 : 0; + uintX_t V = Config->GcSections ? MergeInputSection::PieceDead + : MergeInputSection::PieceLive; if (Header->sh_flags & SHF_STRINGS) { uintX_t Offset = 0; while (!Data.empty()) { @@ -478,15 +479,15 @@ // Compute the Addend and if the Base is cached, return. uintX_t Addend = Offset - Start; uintX_t &Base = I->second; - if (Base != uintX_t(-1)) + auto *MOS = static_cast *>(this->OutSec); + if (!MOS->shouldTailMerge()) return Base + Addend; // Map the base to the offset in the output section and cache it. ArrayRef D = this->getSectionData(); StringRef Data((const char *)D.data(), D.size()); StringRef Entry = Data.substr(Start, End - Start); - Base = - static_cast *>(this->OutSec)->getOffset(Entry); + Base = MOS->getOffset(Entry); return Base + Addend; } Index: lld/trunk/ELF/MarkLive.cpp =================================================================== --- lld/trunk/ELF/MarkLive.cpp +++ lld/trunk/ELF/MarkLive.cpp @@ -143,7 +143,7 @@ if (auto *MS = dyn_cast>(R.Sec)) { std::pair *, uintX_t> T = MS->getRangeAndSize(R.Offset); - T.first->second = 0; + T.first->second = MergeInputSection::PieceLive; } if (R.Sec->Live) return; Index: lld/trunk/ELF/OutputSections.h =================================================================== --- lld/trunk/ELF/OutputSections.h +++ lld/trunk/ELF/OutputSections.h @@ -310,8 +310,6 @@ class MergeOutputSection final : public OutputSectionBase { typedef typename ELFT::uint uintX_t; - bool shouldTailMerge() const; - public: MergeOutputSection(StringRef Name, uint32_t Type, uintX_t Flags, uintX_t Alignment); @@ -319,6 +317,7 @@ void writeTo(uint8_t *Buf) override; unsigned getOffset(StringRef Val); void finalize() override; + bool shouldTailMerge() const; private: llvm::StringTableBuilder Builder; Index: lld/trunk/ELF/OutputSections.cpp =================================================================== --- lld/trunk/ELF/OutputSections.cpp +++ lld/trunk/ELF/OutputSections.cpp @@ -1271,7 +1271,7 @@ if (this->Header.sh_flags & SHF_STRINGS) { for (unsigned I = 0, N = Offsets.size(); I != N; ++I) { auto &P = Offsets[I]; - if (P.second == (uintX_t)-1) + if (P.second == MergeInputSection::PieceDead) continue; uintX_t Start = P.first; @@ -1279,7 +1279,7 @@ StringRef Entry = Data.substr(Start, End - Start); uintX_t OutputOffset = Builder.add(Entry); if (shouldTailMerge()) - OutputOffset = -1; + OutputOffset = MergeInputSection::PieceLive; P.second = OutputOffset; } return; Index: lld/trunk/ELF/Writer.cpp =================================================================== --- lld/trunk/ELF/Writer.cpp +++ lld/trunk/ELF/Writer.cpp @@ -1118,9 +1118,16 @@ return false; if (auto *D = dyn_cast>(&B)) { + // Always include absolute symbols. + if (!D->Section) + return true; // Exclude symbols pointing to garbage-collected sections. - if (D->Section && !D->Section->Live) + if (!D->Section->Live) return false; + if (auto *S = dyn_cast>(D->Section)) + if (S->getRangeAndSize(D->Value).first->second == + MergeInputSection::PieceDead) + return false; } return true; } Index: lld/trunk/test/ELF/string-gc.s =================================================================== --- lld/trunk/test/ELF/string-gc.s +++ lld/trunk/test/ELF/string-gc.s @@ -0,0 +1,54 @@ +// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o +// RUN: ld.lld %t.o -o %t --gc-sections +// RUN: llvm-readobj -symbols %t | FileCheck %s + +// CHECK: Symbols [ +// CHECK-NEXT: Symbol { +// CHECK-NEXT: Name: (0) +// CHECK-NEXT: Value: 0x0 +// CHECK-NEXT: Size: 0 +// CHECK-NEXT: Binding: Local (0x0) +// CHECK-NEXT: Type: None (0x0) +// CHECK-NEXT: Other: 0 +// CHECK-NEXT: Section: Undefined (0x0) +// CHECK-NEXT: } +// CHECK-NEXT: Symbol { +// CHECK-NEXT: Name: s1 (8) +// CHECK-NEXT: Value: 0x10120 +// CHECK-NEXT: Size: 0 +// CHECK-NEXT: Binding: Local (0x0) +// CHECK-NEXT: Type: Object (0x1) +// CHECK-NEXT: Other [ (0x2) +// CHECK-NEXT: STV_HIDDEN (0x2) +// CHECK-NEXT: ] +// CHECK-NEXT: Section: .rodata (0x1) +// CHECK-NEXT: } +// CHECK-NEXT: Symbol { +// CHECK-NEXT: Name: _start (1) +// CHECK-NEXT: Value: 0x11000 +// CHECK-NEXT: Size: 0 +// CHECK-NEXT: Binding: Global (0x1) +// CHECK-NEXT: Type: Function (0x2) +// CHECK-NEXT: Other: 0 +// CHECK-NEXT: Section: .text (0x2) +// CHECK-NEXT: } +// CHECK-NEXT: ] + +.text +.globl _start +.type _start,@function +_start: +movl $s1, %eax + +.hidden s1 +.type s1,@object +.section .rodata.str1.1,"aMS",@progbits,1 +.globl s1 +s1: +.asciz "abcd" + +.hidden s2 +.type s2,@object +.globl s2 +s2: +.asciz "efgh"