Index: ELF/InputFiles.h =================================================================== --- ELF/InputFiles.h +++ ELF/InputFiles.h @@ -94,6 +94,7 @@ typedef typename llvm::object::ELFFile::Elf_Shdr Elf_Shdr; typedef typename llvm::object::ELFFile::Elf_Sym_Range Elf_Sym_Range; typedef typename llvm::object::ELFFile::Elf_Word Elf_Word; + typedef typename llvm::object::ELFFile::uintX_t uintX_t; typedef llvm::support::detail::packed_endian_specific_integral< uint32_t, ELFT::TargetEndianness, 2> GroupEntryType; @@ -110,8 +111,8 @@ explicit ObjectFile(MemoryBufferRef M); void parse(llvm::DenseSet &Comdats); - ArrayRef *> getSections() const { return Sections; } - InputSection *getSection(const Elf_Sym &Sym) const; + ArrayRef *> getSections() const { return Sections; } + InputSectionBase *getSection(const Elf_Sym &Sym) const; SymbolBody *getSymbolBody(uint32_t SymbolIndex) const { uint32_t FirstNonLocal = this->Symtab->sh_info; @@ -132,7 +133,7 @@ SymbolBody *createSymbolBody(StringRef StringTable, const Elf_Sym *Sym); // List of all sections defined by this file. - std::vector *> Sections; + std::vector *> Sections; ArrayRef SymtabSHNDX; Index: ELF/InputFiles.cpp =================================================================== --- ELF/InputFiles.cpp +++ ELF/InputFiles.cpp @@ -150,16 +150,28 @@ uint32_t RelocatedSectionIndex = Sec.sh_info; if (RelocatedSectionIndex >= Size) error("Invalid relocated section index"); - InputSection *RelocatedSection = Sections[RelocatedSectionIndex]; + InputSectionBase *RelocatedSection = + Sections[RelocatedSectionIndex]; if (!RelocatedSection) error("Unsupported relocation reference"); - RelocatedSection->RelocSections.push_back(&Sec); + if (auto *S = dyn_cast>(RelocatedSection)) + S->RelocSections.push_back(&Sec); + else + error("Relocations pointing to SHF_MERGE are not supported"); break; } - default: - Sections[I] = new (this->Alloc) InputSection(this, &Sec); + default: { + uintX_t Flags = Sec.sh_flags; + if (Flags & SHF_MERGE && !(Flags & SHF_STRINGS)) { + if (Flags & SHF_WRITE) + error("Writable SHF_MERGE sections are not supported"); + Sections[I] = new (this->Alloc) MergeInputSection(this, &Sec); + } else { + Sections[I] = new (this->Alloc) InputSection(this, &Sec); + } break; } + } } } @@ -173,7 +185,7 @@ } template -InputSection * +InputSectionBase * elf2::ObjectFile::getSection(const Elf_Sym &Sym) const { uint32_t Index = Sym.st_shndx; if (Index == ELF::SHN_XINDEX) @@ -209,7 +221,7 @@ case STB_GLOBAL: case STB_WEAK: case STB_GNU_UNIQUE: { - InputSection *Sec = getSection(*Sym); + InputSectionBase *Sec = getSection(*Sym); if (Sec == &InputSection::Discarded) return new (this->Alloc) Undefined(Name, *Sym); return new (this->Alloc) DefinedRegular(Name, *Sym, *Sec); Index: ELF/InputSection.h =================================================================== --- ELF/InputSection.h +++ ELF/InputSection.h @@ -21,22 +21,28 @@ template class OutputSectionBase; // This corresponds to a section of an input file. -template class InputSection { +template class InputSectionBase { +protected: typedef typename llvm::object::ELFFile::Elf_Shdr Elf_Shdr; - typedef typename llvm::object::ELFFile::Elf_Rela Elf_Rela; - typedef typename llvm::object::ELFFile::Elf_Rel Elf_Rel; typedef typename llvm::object::ELFFile::Elf_Sym Elf_Sym; typedef typename llvm::object::ELFFile::uintX_t uintX_t; + const Elf_Shdr *Header; + + // The file this section is from. + ObjectFile *File; public: - InputSection(ObjectFile *F, const Elf_Shdr *Header); + enum Kind { Regular, Merge }; + Kind SectionKind; + + InputSectionBase(ObjectFile *File, const Elf_Shdr *Header, + Kind SectionKind); + OutputSectionBase *OutSec = nullptr; // Returns the size of this section (even if this is a common or BSS.) size_t getSize() const { return Header->sh_size; } - // Write this section to a mmap'ed file, assuming Buf is pointing to - // beginning of the output section. - void writeTo(uint8_t *Buf); + static InputSectionBase Discarded; StringRef getSectionName() const; const Elf_Shdr *getSectionHdr() const { return Header; } @@ -49,15 +55,52 @@ return std::max(Header->sh_addralign, 1); } + uintX_t getOffset(const Elf_Sym &Sym) const; + ArrayRef getSectionData() const; +}; + +template +InputSectionBase + InputSectionBase::Discarded(nullptr, nullptr, + InputSectionBase::Regular); + +// This corresponds to a SHF_MERGE section of an input file. +template class MergeInputSection : public InputSectionBase { + typedef InputSectionBase Base; + typedef typename llvm::object::ELFFile::uintX_t uintX_t; + typedef typename llvm::object::ELFFile::Elf_Sym Elf_Sym; + typedef typename llvm::object::ELFFile::Elf_Shdr Elf_Shdr; + +public: + MergeInputSection(ObjectFile *F, const Elf_Shdr *Header); + static bool classof(const InputSectionBase *S); + uintX_t getOffset(uintX_t Offset) const; +}; + +// This corresponds to a non SHF_MERGE section of an input file. +template class InputSection : public InputSectionBase { + typedef InputSectionBase Base; + typedef typename llvm::object::ELFFile::Elf_Shdr Elf_Shdr; + typedef typename llvm::object::ELFFile::Elf_Rela Elf_Rela; + typedef typename llvm::object::ELFFile::Elf_Rel Elf_Rel; + typedef typename llvm::object::ELFFile::Elf_Sym Elf_Sym; + typedef typename llvm::object::ELFFile::uintX_t uintX_t; + +public: + InputSection(ObjectFile *F, const Elf_Shdr *Header); + + // Write this section to a mmap'ed file, assuming Buf is pointing to + // beginning of the output section. + void writeTo(uint8_t *Buf); + // Relocation sections that refer to this one. SmallVector RelocSections; // The offset from beginning of the output sections this section was assigned // to. The writer sets a value. uint64_t OutSecOff = 0; - OutputSectionBase *OutSec = nullptr; - static InputSection Discarded; + static bool classof(const InputSectionBase *S); private: template @@ -65,16 +108,8 @@ llvm::iterator_range< const llvm::object::Elf_Rel_Impl *> Rels, const ObjectFile &File, uintX_t BaseAddr); - - // The file this section is from. - ObjectFile *File; - - const Elf_Shdr *Header; }; -template -InputSection InputSection::Discarded(nullptr, nullptr); - } // namespace elf2 } // namespace lld Index: ELF/InputSection.cpp =================================================================== --- ELF/InputSection.cpp +++ ELF/InputSection.cpp @@ -22,8 +22,41 @@ using namespace lld::elf2; template +InputSectionBase::InputSectionBase(ObjectFile *File, + const Elf_Shdr *Header, + Kind SectionKind) + : Header(Header), File(File), SectionKind(SectionKind) {} + +template StringRef InputSectionBase::getSectionName() const { + ErrorOr Name = File->getObj().getSectionName(this->Header); + error(Name); + return *Name; +} + +template +ArrayRef InputSectionBase::getSectionData() const { + ErrorOr> Ret = + this->File->getObj().getSectionContents(this->Header); + error(Ret); + return *Ret; +} + +template +typename ELFFile::uintX_t +InputSectionBase::getOffset(const Elf_Sym &Sym) const { + if (auto *S = dyn_cast>(this)) + return S->OutSecOff + Sym.st_value; + return cast>(this)->getOffset(Sym.st_value); +} + +template InputSection::InputSection(ObjectFile *F, const Elf_Shdr *Header) - : File(F), Header(Header) {} + : InputSectionBase(F, Header, Base::Regular) {} + +template +bool InputSection::classof(const InputSectionBase *S) { + return S->SectionKind == Base::Regular; +} template template @@ -65,35 +98,63 @@ } template void InputSection::writeTo(uint8_t *Buf) { - if (Header->sh_type == SHT_NOBITS) + if (this->Header->sh_type == SHT_NOBITS) return; // Copy section contents from source object file to output file. - ArrayRef Data = *File->getObj().getSectionContents(Header); + ArrayRef Data = this->getSectionData(); memcpy(Buf + OutSecOff, Data.data(), Data.size()); - ELFFile &EObj = File->getObj(); + ELFFile &EObj = this->File->getObj(); uint8_t *Base = Buf + OutSecOff; - uintX_t BaseAddr = OutSec->getVA() + OutSecOff; + uintX_t BaseAddr = this->OutSec->getVA() + OutSecOff; // Iterate over all relocation sections that apply to this section. for (const Elf_Shdr *RelSec : RelocSections) { if (RelSec->sh_type == SHT_RELA) - relocate(Base, Base + Data.size(), EObj.relas(RelSec), *File, BaseAddr); + relocate(Base, Base + Data.size(), EObj.relas(RelSec), *this->File, + BaseAddr); else - relocate(Base, Base + Data.size(), EObj.rels(RelSec), *File, BaseAddr); + relocate(Base, Base + Data.size(), EObj.rels(RelSec), *this->File, + BaseAddr); } } -template StringRef InputSection::getSectionName() const { - ErrorOr Name = File->getObj().getSectionName(Header); - error(Name); - return *Name; +template +MergeInputSection::MergeInputSection(ObjectFile *F, + const Elf_Shdr *Header) + : InputSectionBase(F, Header, Base::Merge) {} + +template +bool MergeInputSection::classof(const InputSectionBase *S) { + return S->SectionKind == Base::Merge; +} + +// FIXME: Optimize this by keeping an offset for each element. +template +typename MergeInputSection::uintX_t +MergeInputSection::getOffset(uintX_t Offset) const { + ArrayRef Data = this->getSectionData(); + uintX_t EntSize = this->Header->sh_entsize; + if (Offset + EntSize > Data.size()) + error("Entry is past the end of the section"); + Data = Data.slice(Offset, EntSize); + return static_cast *>(this->OutSec)->getOffset(Data); } namespace lld { namespace elf2 { +template class InputSectionBase; +template class InputSectionBase; +template class InputSectionBase; +template class InputSectionBase; + template class InputSection; template class InputSection; template class InputSection; template class InputSection; + +template class MergeInputSection; +template class MergeInputSection; +template class MergeInputSection; +template class MergeInputSection; } } Index: ELF/OutputSections.h =================================================================== --- ELF/OutputSections.h +++ ELF/OutputSections.h @@ -12,6 +12,7 @@ #include "lld/Core/LLVM.h" +#include "llvm/ADT/MapVector.h" #include "llvm/MC/StringTableBuilder.h" #include "llvm/Object/ELF.h" @@ -27,6 +28,7 @@ template class SymbolTableSection; template class StringTableSection; template class InputSection; +template class MergeInputSection; template class OutputSection; template class ObjectFile; template class DefinedRegular; @@ -204,6 +206,23 @@ }; template +class MergeOutputSection final : public OutputSectionBase { + typedef typename OutputSectionBase::uintX_t uintX_t; + +public: + MergeOutputSection(StringRef Name, uint32_t sh_type, uintX_t sh_flags); + void addSection(MergeInputSection *S); + void writeTo(uint8_t *Buf) override; + + unsigned getOffset(ArrayRef Val); + +private: + // This map is used to find if we already have an entry for a given value and, + // if so, at what offset it is. + llvm::MapVector, uintX_t> Offsets; +}; + +template class InterpSection final : public OutputSectionBase { public: InterpSection(); Index: ELF/OutputSections.cpp =================================================================== --- ELF/OutputSections.cpp +++ ELF/OutputSections.cpp @@ -410,8 +410,8 @@ return cast>(S).Sym.st_value; case SymbolBody::DefinedRegularKind: { const auto &DR = cast>(S); - const InputSection &SC = DR.Section; - return SC.OutSec->getVA() + SC.OutSecOff + DR.Sym.st_value; + const InputSectionBase &SC = DR.Section; + return SC.OutSec->getVA() + SC.getOffset(DR.Sym); } case SymbolBody::DefinedCommonKind: return Out::Bss->getVA() + cast>(S).OffsetInBSS; @@ -451,11 +451,22 @@ // the group are not allowed. Unfortunately .eh_frame breaks that rule // and must be treated specially. For now we just replace the symbol with // 0. - InputSection *Section = File.getSection(*Sym); + InputSectionBase *Section = File.getSection(*Sym); if (Section == &InputSection::Discarded) return Addend; - return Section->OutSec->getVA() + Section->OutSecOff + Sym->st_value + Addend; + uintX_t VA = Section->OutSec->getVA(); + if (isa>(Section)) + return VA + Section->getOffset(*Sym) + Addend; + + uintX_t Offset = Sym->st_value; + if (Sym->getType() == STT_SECTION) { + Offset += Addend; + Addend = Offset % Section->getSectionHdr()->sh_entsize; + Offset -= Addend; + } + return VA + cast>(Section)->getOffset(Offset) + + Addend; } // Returns true if a symbol can be replaced at load-time by a symbol @@ -502,6 +513,44 @@ } template +MergeOutputSection::MergeOutputSection(StringRef Name, uint32_t sh_type, + uintX_t sh_flags) + : OutputSectionBase(Name, sh_type, sh_flags) {} + +template void MergeOutputSection::writeTo(uint8_t *Buf) { + for (const std::pair, uintX_t> &P : Offsets) { + ArrayRef Data = P.first; + memcpy(Buf, Data.data(), Data.size()); + Buf += Data.size(); + } +} + +template +void MergeOutputSection::addSection(MergeInputSection *S) { + S->OutSec = this; + uint32_t Align = S->getAlign(); + if (Align > this->Header.sh_addralign) + this->Header.sh_addralign = Align; + + uintX_t Off = this->Header.sh_size; + ArrayRef Data = S->getSectionData(); + uintX_t EntSize = S->getSectionHdr()->sh_entsize; + if (Data.size() % EntSize) + error("SHF_MERGE section size must be a multiple of sh_entsize"); + for (unsigned I = 0, N = Data.size(); I != N; I += EntSize) { + auto P = Offsets.insert(std::make_pair(Data.slice(I, EntSize), Off)); + if (P.second) + Off += EntSize; + } + this->Header.sh_size = Off; +} + +template +unsigned MergeOutputSection::getOffset(ArrayRef Val) { + return Offsets.find(Val)->second; +} + +template StringTableSection::StringTableSection(bool Dynamic) : OutputSectionBase(Dynamic ? ".dynstr" : ".strtab", llvm::ELF::SHT_STRTAB, @@ -613,13 +662,15 @@ ESym->st_name = StrTabSec.getFileOff(SymName); ESym->st_size = Sym.st_size; ESym->setBindingAndType(Sym.getBinding(), Sym.getType()); - uintX_t VA = Sym.st_value; + uintX_t VA = 0; if (Sym.st_shndx == SHN_ABS) { ESym->st_shndx = SHN_ABS; + VA = Sym.st_value; } else { - const InputSection *Sec = File->getSection(Sym); - ESym->st_shndx = Sec->OutSec->SectionIndex; - VA += Sec->OutSec->getVA() + Sec->OutSecOff; + const InputSectionBase *Section = File->getSection(Sym); + const OutputSectionBase *OutSec = Section->OutSec; + ESym->st_shndx = OutSec->SectionIndex; + VA += OutSec->getVA() + Section->getOffset(Sym); } ESym->st_value = VA; } @@ -646,7 +697,7 @@ ESym->st_name = StrTabSec.getFileOff(Name); const OutputSectionBase *OutSec = nullptr; - const InputSection *Section = nullptr; + const InputSectionBase *Section = nullptr; switch (Body->kind()) { case SymbolBody::DefinedSyntheticKind: @@ -742,6 +793,11 @@ template class OutputSection; template class OutputSection; +template class MergeOutputSection; +template class MergeOutputSection; +template class MergeOutputSection; +template class MergeOutputSection; + template class StringTableSection; template class StringTableSection; template class StringTableSection; @@ -760,19 +816,29 @@ template ELFFile::uintX_t getLocalRelTarget(const ObjectFile &, const ELFFile::Elf_Rel &); - template ELFFile::uintX_t getLocalRelTarget(const ObjectFile &, const ELFFile::Elf_Rel &); - template ELFFile::uintX_t getLocalRelTarget(const ObjectFile &, const ELFFile::Elf_Rel &); - template ELFFile::uintX_t getLocalRelTarget(const ObjectFile &, const ELFFile::Elf_Rel &); +template ELFFile::uintX_t +getLocalRelTarget(const ObjectFile &, + const ELFFile::Elf_Rela &); +template ELFFile::uintX_t +getLocalRelTarget(const ObjectFile &, + const ELFFile::Elf_Rela &); +template ELFFile::uintX_t +getLocalRelTarget(const ObjectFile &, + const ELFFile::Elf_Rela &); +template ELFFile::uintX_t +getLocalRelTarget(const ObjectFile &, + const ELFFile::Elf_Rela &); + template bool includeInSymtab(const SymbolBody &); template bool includeInSymtab(const SymbolBody &); template bool includeInSymtab(const SymbolBody &); Index: ELF/Symbols.h =================================================================== --- ELF/Symbols.h +++ ELF/Symbols.h @@ -217,14 +217,15 @@ typedef typename Base::Elf_Sym Elf_Sym; public: - DefinedRegular(StringRef N, const Elf_Sym &Sym, InputSection &Section) + DefinedRegular(StringRef N, const Elf_Sym &Sym, + InputSectionBase &Section) : Defined(Base::DefinedRegularKind, N, Sym), Section(Section) {} static bool classof(const SymbolBody *S) { return S->kind() == Base::DefinedRegularKind; } - const InputSection &Section; + const InputSectionBase &Section; }; template class DefinedSynthetic : public Defined { Index: ELF/Writer.cpp =================================================================== --- ELF/Writer.cpp +++ ELF/Writer.cpp @@ -71,6 +71,7 @@ std::unique_ptr Buffer; SpecificBumpPtrAllocator> SecAlloc; + SpecificBumpPtrAllocator> MSecAlloc; BumpPtrAllocator Alloc; std::vector *> OutputSections; unsigned getNumSections() const { return OutputSections.size() + 1; } @@ -135,24 +136,27 @@ StringRef Name; uint32_t Type; uintX_t Flags; + uintX_t EntSize; }; } namespace llvm { template struct DenseMapInfo> { static SectionKey getEmptyKey() { - return SectionKey{DenseMapInfo::getEmptyKey(), 0, 0}; + return SectionKey{DenseMapInfo::getEmptyKey(), 0, 0, + 0}; } static SectionKey getTombstoneKey() { return SectionKey{DenseMapInfo::getTombstoneKey(), 0, - 0}; + 0, 0}; } static unsigned getHashValue(const SectionKey &Val) { - return hash_combine(Val.Name, Val.Type, Val.Flags); + return hash_combine(Val.Name, Val.Type, Val.Flags, Val.EntSize); } static bool isEqual(const SectionKey &LHS, const SectionKey &RHS) { return DenseMapInfo::isEqual(LHS.Name, RHS.Name) && - LHS.Type == RHS.Type && LHS.Flags == RHS.Flags; + LHS.Type == RHS.Type && LHS.Flags == RHS.Flags && + LHS.EntSize == RHS.EntSize; } }; } @@ -390,39 +394,52 @@ if (needsInterpSection()) OutputSections.push_back(Out::Interp); - SmallDenseMap, OutputSection *> Map; + SmallDenseMap, OutputSectionBase *> Map; OutputSections.push_back(Out::Bss); Map[{Out::Bss->getName(), Out::Bss->getType(), - Out::Bss->getFlags()}] = Out::Bss; + Out::Bss->getFlags(), 0}] = Out::Bss; std::vector *> RegularSections; for (const std::unique_ptr> &F : Symtab.getObjectFiles()) { - for (InputSection *C : F->getSections()) { + for (InputSectionBase *C : F->getSections()) { if (!C || C == &InputSection::Discarded) continue; const Elf_Shdr *H = C->getSectionHdr(); uintX_t OutFlags = H->sh_flags & ~SHF_GROUP; + // For SHF_MERGE we create different output sections for each sh_entsize. + // This makes each output section simple and keeps a single level + // mapping from input to output. + auto *IS = dyn_cast>(C); + uintX_t EntSize = IS ? 0 : H->sh_entsize; SectionKey Key{getOutputName(C->getSectionName()), - H->sh_type, OutFlags}; - OutputSection *&Sec = Map[Key]; + H->sh_type, OutFlags, EntSize}; + OutputSectionBase *&Sec = Map[Key]; if (!Sec) { - Sec = new (SecAlloc.Allocate()) - OutputSection(Key.Name, Key.Type, Key.Flags); + if (IS) + Sec = new (SecAlloc.Allocate()) + OutputSection(Key.Name, Key.Type, Key.Flags); + else + Sec = new (MSecAlloc.Allocate()) + MergeOutputSection(Key.Name, Key.Type, Key.Flags); OutputSections.push_back(Sec); RegularSections.push_back(Sec); } - Sec->addSection(C); + if (IS) + static_cast *>(Sec)->addSection(IS); + else + static_cast *>(Sec) + ->addSection(cast>(C)); } } - Out::Dynamic->PreInitArraySec = - Map.lookup({".preinit_array", SHT_PREINIT_ARRAY, SHF_WRITE | SHF_ALLOC}); + Out::Dynamic->PreInitArraySec = Map.lookup( + {".preinit_array", SHT_PREINIT_ARRAY, SHF_WRITE | SHF_ALLOC, 0}); Out::Dynamic->InitArraySec = - Map.lookup({".init_array", SHT_INIT_ARRAY, SHF_WRITE | SHF_ALLOC}); + Map.lookup({".init_array", SHT_INIT_ARRAY, SHF_WRITE | SHF_ALLOC, 0}); Out::Dynamic->FiniArraySec = - Map.lookup({".fini_array", SHT_FINI_ARRAY, SHF_WRITE | SHF_ALLOC}); + Map.lookup({".fini_array", SHT_FINI_ARRAY, SHF_WRITE | SHF_ALLOC, 0}); auto AddStartEnd = [&](StringRef Start, StringRef End, OutputSectionBase *OS) { @@ -455,9 +472,10 @@ // Scan relocations. This must be done after every symbol is declared so that // we can correctly decide if a dynamic relocation is needed. for (const std::unique_ptr> &F : Symtab.getObjectFiles()) - for (InputSection *S : F->getSections()) - if (S && S != &InputSection::Discarded) - scanRelocs(*S); + for (InputSectionBase *B : F->getSections()) + if (auto *S = dyn_cast_or_null>(B)) + if (S != &InputSection::Discarded) + scanRelocs(*S); // FIXME: Try to avoid the extra walk over all global symbols. std::vector *> CommonSymbols; @@ -516,7 +534,7 @@ // If we have a .opd section (used under PPC64 for function descriptors), // store a pointer to it here so that we can use it later when processing // relocations. - Out::Opd = Map.lookup({".opd", SHT_PROGBITS, SHF_WRITE | SHF_ALLOC}); + Out::Opd = Map.lookup({".opd", SHT_PROGBITS, SHF_WRITE | SHF_ALLOC, 0}); } static bool isAlpha(char C) { Index: test/elf2/Inputs/merge.s =================================================================== --- /dev/null +++ test/elf2/Inputs/merge.s @@ -0,0 +1,6 @@ + .section .mysec,"aM",@progbits,4 + .align 4 + .long 0x42 + + .text + movl .mysec, %eax Index: test/elf2/merge-invalid-size.s =================================================================== --- /dev/null +++ test/elf2/merge-invalid-size.s @@ -0,0 +1,7 @@ +// REQUIRES: x86 +// RUN: llvm-mc %s -o %t.o -filetype=obj -triple=x86_64-pc-linux +// RUN: not ld.lld2 %t.o -o %t.so 2>&1 | FileCheck %s +// CHECK: SHF_MERGE section size must be a multiple of sh_entsize + + .section .foo,"aM",@progbits,4 + .short 42 Index: test/elf2/merge-shared.s =================================================================== --- /dev/null +++ test/elf2/merge-shared.s @@ -0,0 +1,26 @@ +// REQUIRES: x86 +// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o +// RUN: ld.lld2 %t.o -o %t.so -shared +// RUN: llvm-readobj -r -s %t.so | FileCheck %s + + .section foo,"aM",@progbits,4 + .long 42 + .long 42 + + .text + .quad foo + 6 + + +// CHECK: Name: foo (20) +// CHECK-NEXT: Type: SHT_PROGBITS +// CHECK-NEXT: Flags [ +// CHECK-NEXT: SHF_ALLOC +// CHECK-NEXT: SHF_MERGE +// CHECK-NEXT: ] +// CHECK-NEXT: Address: 0x158 + +// CHECK: Relocations [ +// CHECK-NEXT: Section ({{.*}}) .rela.dyn { +// CHECK-NEXT: 0x{{.*}} R_X86_64_RELATIVE - 0x15A +// CHECK-NEXT: } +// CHECK-NEXT: ] Index: test/elf2/merge.s =================================================================== --- /dev/null +++ test/elf2/merge.s @@ -0,0 +1,109 @@ +// REQUIRES: x86 +// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o +// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %p/Inputs/merge.s -o %t2.o +// RUN: ld.lld2 %t.o %t2.o -o %t +// RUN: llvm-readobj -s -section-data -t %t | FileCheck %s +// RUN: llvm-objdump -d %t | FileCheck --check-prefix=DISASM %s + + .section .mysec,"aM",@progbits,4 + .align 4 + .global foo + .hidden foo + .long 0x10 +foo: + .long 0x42 +bar: + .long 0x42 +zed: + .long 0x42 + +// CHECK: Name: .mysec +// CHECK-NEXT: Type: SHT_PROGBITS +// CHECK-NEXT: Flags [ +// CHECK-NEXT: SHF_ALLOC +// CHECK-NEXT: SHF_MERGE +// CHECK-NEXT: ] +// CHECK-NEXT: Address: 0x100E8 +// CHECK-NEXT: Offset: 0xE8 +// CHECK-NEXT: Size: 8 +// CHECK-NEXT: Link: 0 +// CHECK-NEXT: Info: 0 +// CHECK-NEXT: AddressAlignment: 4 +// CHECK-NEXT: EntrySize: 0 +// CHECK-NEXT: SectionData ( +// CHECK-NEXT: 0000: 10000000 42000000 +// CHECK-NEXT: ) + + +// Address of the constant 0x10 = 0x100E8 = 65768 +// Address of the constant 0x42 = 0x100EC = 65772 + +// CHECK: Symbols [ + +// CHECK: Name: bar +// CHECK-NEXT: Value: 0x100EC +// CHECK-NEXT: Size: 0 +// CHECK-NEXT: Binding: Loca +// CHECK-NEXT: Type: None +// CHECK-NEXT: Other: 0 +// CHECK-NEXT: Section: .mysec + +// CHECK: Name: zed +// CHECK-NEXT: Value: 0x100EC +// CHECK-NEXT: Size: 0 +// CHECK-NEXT: Binding: Local +// CHECK-NEXT: Type: None +// CHECK-NEXT: Other: 0 +// CHECK-NEXT: Section: .mysec + +// CHECK: Name: foo +// CHECK-NEXT: Value: 0x100EC +// CHECK-NEXT: Size: 0 +// CHECK-NEXT: Binding: Local +// CHECK-NEXT: Type: None +// CHECK-NEXT: Other: 2 +// CHECK-NEXT: Section: .mysec + + // CHECK: ] + + .text + .globl _start +_start: +// DISASM: Disassembly of section .text: +// DISASM-NEXT: _start: + + movl .mysec, %eax +// addr(0x10) = 65768 +// DISASM-NEXT: movl 65768, %eax + + movl .mysec+7, %eax +// addr(0x42) + 3 = 65772 + 3 = 65775 +// DISASM-NEXT: movl 65775, %eax + + movl .mysec+8, %eax +// addr(0x42) = 65772 +// DISASM-NEXT: movl 65772, %eax + + movl bar+7, %eax +// addr(0x42) + 7 = 65772 + 7 = 65779 +// DISASM-NEXT: movl 65779, %eax + + movl bar+8, %eax +// addr(0x42) + 8 = 65772 + 8 = 65780 +// DISASM-NEXT: movl 65780, %eax + + movl foo, %eax +// addr(0x42) = 65772 +// DISASM-NEXT: movl 65772, %eax + + movl foo+7, %eax +// addr(0x42) + 7 = = 65772 + 7 = 65779 +// DISASM-NEXT: movl 65779, %eax + + movl foo+8, %eax +// addr(0x42) + 8 = = 65772 + 8 = 65780 +// DISASM-NEXT: movl 65780, %eax + +// From the other file: movl .mysec, %eax +// addr(0x42) = 65772 +// DISASM-NEXT: movl 65772, %eax Index: test/elf2/relocation-in-merge.s =================================================================== --- /dev/null +++ test/elf2/relocation-in-merge.s @@ -0,0 +1,7 @@ +// REQUIRES: x86 +// RUN: llvm-mc %s -o %t.o -filetype=obj -triple=x86_64-pc-linux +// RUN: not ld.lld2 %t.o -o %t 2>&1 | FileCheck %s +// CHECK: Relocations pointing to SHF_MERGE are not supported + + .section .foo,"aM",@progbits,4 + .long bar Index: test/elf2/relocation-past-merge-end.s =================================================================== --- /dev/null +++ test/elf2/relocation-past-merge-end.s @@ -0,0 +1,7 @@ +// REQUIRES: x86 +// RUN: llvm-mc %s -o %t.o -filetype=obj -triple=x86_64-pc-linux +// RUN: not ld.lld2 %t.o -o %t.so -shared 2>&1 | FileCheck %s +// CHECK: Entry is past the end of the section + + .long .foo + 1 + .section .foo,"aM",@progbits,4 Index: test/elf2/writable-merge.s =================================================================== --- /dev/null +++ test/elf2/writable-merge.s @@ -0,0 +1,6 @@ +// REQUIRES: x86 +// RUN: llvm-mc %s -o %t.o -filetype=obj -triple=x86_64-pc-linux +// RUN: not ld.lld2 %t.o -o %t 2>&1 | FileCheck %s +// CHECK: Writable SHF_MERGE sections are not supported + + .section .foo,"awM",@progbits,4