Index: ELF/Config.h =================================================================== --- ELF/Config.h +++ ELF/Config.h @@ -56,6 +56,7 @@ bool Mips64EL = false; bool NoInhibitExec; bool NoUndefined; + bool Relocatable = false; bool Shared; bool Static = false; bool StripAll; Index: ELF/Driver.cpp =================================================================== --- ELF/Driver.cpp +++ ELF/Driver.cpp @@ -80,6 +80,8 @@ Files.push_back(make_unique(MBRef)); return; case file_magic::elf_shared_object: + if (Config->Relocatable) + error("attempted static link of dynamic object " + Path); Files.push_back(createELFFile(MBRef)); return; default: @@ -150,6 +152,15 @@ Config->StripAll = Args.hasArg(OPT_strip_all); Config->Verbose = Args.hasArg(OPT_verbose); + Config->Relocatable = Args.hasArg(OPT_relocatable); + if (Config->Relocatable) { + // -strip-all is silently ignored. + Config->StripAll = false; + Config->Static = true; + if (Config->Shared) + error("-r and -shared may not be used together"); + } + Config->DynamicLinker = getString(Args, OPT_dynamic_linker); Config->Entry = getString(Args, OPT_entry); Config->Fini = getString(Args, OPT_fini, "_fini"); @@ -226,7 +237,7 @@ SymbolTable Symtab; Target.reset(createTarget()); - if (!Config->Shared) { + if (!Config->Shared && !Config->Relocatable) { // Add entry symbol. if (Config->Entry.empty()) Config->Entry = (Config->EMachine == EM_MIPS) ? "__start" : "_start"; Index: ELF/InputFiles.cpp =================================================================== --- ELF/InputFiles.cpp +++ ELF/InputFiles.cpp @@ -205,6 +205,16 @@ Sections[RelocatedSectionIndex]; if (!RelocatedSection) error("Unsupported relocation reference"); + + if (Config->Relocatable) { + InputSection *S = new (this->Alloc) InputSection(this, &Sec); + S->RelocatingSection = dyn_cast>(RelocatedSection); + if (!S->RelocatingSection) + error("Unsupported relocating section"); + Sections[I] = S; + break; + } + if (auto *S = dyn_cast>(RelocatedSection)) S->RelocSections.push_back(&Sec); else Index: ELF/InputSection.h =================================================================== --- ELF/InputSection.h +++ ELF/InputSection.h @@ -117,6 +117,9 @@ static bool classof(const InputSectionBase *S); + // Section to which the relocation applies if this is a SHT_REL[A] section. + InputSection *RelocatingSection = nullptr; + private: template void relocate(uint8_t *Buf, uint8_t *BufEnd, Index: ELF/InputSection.cpp =================================================================== --- ELF/InputSection.cpp +++ ELF/InputSection.cpp @@ -90,10 +90,24 @@ for (const RelType &RI : Rels) { uint32_t SymIndex = RI.getSymbol(Config->Mips64EL); uint32_t Type = RI.getType(Config->Mips64EL); + const Elf_Shdr *SymTab = File.getSymbolTable(); + + if (RelocatingSection) { + auto *P = reinterpret_cast(Buf); + Buf += sizeof(RelType); + // Relocation for local symbol here means that it is probably + // rel[a].eh_frame section which has references to + // sections in r_info field. Also needs fix for addend. + if (SymIndex < SymTab->sh_info) + error("Not implemented relocation section type"); + SymbolBody &Body = *File.getSymbolBody(SymIndex)->repl(); + P->r_offset += RelocatingSection->OutSecOff; + P->setSymbolAndType(Body.getSymbolTableIndex(), Type, Config->Mips64EL); + continue; + } // Handle relocations for local symbols -- they never get // resolved so we don't allocate a SymbolBody. - const Elf_Shdr *SymTab = File.getSymbolTable(); if (SymIndex < SymTab->sh_info) { uintX_t SymVA = getLocalRelTarget(File, RI); Target->relocateOne(Buf + RI.r_offset, BufEnd, Type, @@ -131,8 +145,18 @@ ELFFile &EObj = this->File->getObj(); uint8_t *Base = Buf + OutSecOff; uintX_t BaseAddr = this->OutSec->getVA() + OutSecOff; + + // If it is SHT_REL[A] section then RelocatingSection will be available. + // That happens if relocatable output was choosen. In that case we should fix + // the symbol indices within symtab table, fix addends in some cases and also + // other possible data. + SmallVector Info; + if (RelocatingSection) + Info.emplace_back(Header); + // Iterate over all relocation sections that apply to this section. - for (const Elf_Shdr *RelSec : RelocSections) { + SmallVector &Relocs = RelocatingSection ? Info : RelocSections; + for (const Elf_Shdr *RelSec : Relocs) { if (RelSec->sh_type == SHT_RELA) relocate(Base, Base + Data.size(), EObj.relas(RelSec), *this->File, BaseAddr); Index: ELF/Options.td =================================================================== --- ELF/Options.td +++ ELF/Options.td @@ -82,6 +82,9 @@ def rpath : Separate<["-"], "rpath">, HelpText<"Add a DT_RUNPATH to the output">; +def relocatable : Flag<["--"], "relocatable">, + HelpText<"Create relocatable object file">; + def script : Separate<["--"], "script">, HelpText<"Read linker script">; def shared : Flag<["-"], "shared">, @@ -122,6 +125,7 @@ def alias_init_init : Joined<["-"], "init=">, Alias; def alias_l__library : Joined<["--"], "library=">, Alias; def alias_o_output : Joined<["--"], "output=">, Alias; +def alias_relocatable : Flag<["-"], "r">, Alias; def alias_rpath_rpath : Joined<["-"], "rpath=">, Alias; def alias_shared_Bshareable : Flag<["-"], "Bshareable">, Alias; def alias_soname_h : Separate<["-"], "h">, Alias; Index: ELF/OutputSections.h =================================================================== --- ELF/OutputSections.h +++ ELF/OutputSections.h @@ -28,8 +28,10 @@ template class SymbolTableSection; template class StringTableSection; template class InputSection; +template class InputSectionBase; template class MergeInputSection; template class OutputSection; +template class StaticRelocSection; template class ObjectFile; template class DefinedRegular; template class ELFSymbolBody; @@ -215,7 +217,7 @@ }; template -class OutputSection final : public OutputSectionBase { +class OutputSection : public OutputSectionBase { public: typedef typename llvm::object::ELFFile::Elf_Shdr Elf_Shdr; typedef typename llvm::object::ELFFile::Elf_Sym Elf_Sym; @@ -223,7 +225,7 @@ typedef typename llvm::object::ELFFile::Elf_Rela Elf_Rela; typedef typename llvm::object::ELFFile::uintX_t uintX_t; OutputSection(StringRef Name, uint32_t sh_type, uintX_t sh_flags); - void addSection(InputSection *C); + virtual void addSection(InputSection *C); void writeTo(uint8_t *Buf) override; private: @@ -231,6 +233,17 @@ }; template +class StaticRelocSection final : public OutputSection { +public: + StaticRelocSection(StringRef Name, uint32_t sh_type, uintX_t sh_flags); + void addSection(InputSection *C) override; + void finalize() override; + +private: + OutputSectionBase *LinkedOut = nullptr; +}; + +template class MergeOutputSection final : public OutputSectionBase { typedef typename OutputSectionBase::uintX_t uintX_t; Index: ELF/OutputSections.cpp =================================================================== --- ELF/OutputSections.cpp +++ ELF/OutputSections.cpp @@ -171,12 +171,12 @@ if (CanBePreempted) { if (NeedsGot) - P->setSymbolAndType(Body->getDynamicSymbolTableIndex(), + P->setSymbolAndType(Body->getSymbolTableIndex(), LazyReloc ? Target->getPltReloc() : Target->getGotReloc(), Config->Mips64EL); else - P->setSymbolAndType(Body->getDynamicSymbolTableIndex(), + P->setSymbolAndType(Body->getSymbolTableIndex(), NeedsCopy ? Target->getCopyReloc() : Type, Config->Mips64EL); } else { @@ -283,7 +283,7 @@ for (SymbolBody *Body : Out::DynSymTab->getSymbols()) { StringRef Name = Body->getName(); - unsigned I = Body->getDynamicSymbolTableIndex(); + unsigned I = Body->getSymbolTableIndex(); uint32_t Hash = hashSysv(Name) % NumSymbols; Chains[I] = Buckets[Hash]; Buckets[Hash] = I; @@ -396,7 +396,7 @@ int Bucket = Item.Hash % NBuckets; assert(PrevBucket <= Bucket); if (Bucket != PrevBucket) { - Buckets[Bucket] = Item.Body->getDynamicSymbolTableIndex(); + Buckets[Bucket] = Item.Body->getSymbolTableIndex(); PrevBucket = Bucket; if (I > 0) Values[I - 1] |= 1; @@ -633,6 +633,30 @@ } template +StaticRelocSection::StaticRelocSection(StringRef Name, uint32_t sh_type, + uintX_t sh_flags) + : OutputSection(Name, sh_type, sh_flags) { + Elf_Shdr &Header = this->Header; + Header.sh_addralign = ELFT::Is64Bits ? 8 : 4; + Header.sh_entsize = true ? sizeof(Elf_Rela) : sizeof(Elf_Rel); +} + +template +void StaticRelocSection::addSection(InputSection *C) { + if (!LinkedOut) + LinkedOut = C->RelocatingSection->OutSec; + else if (LinkedOut != C->RelocatingSection->OutSec) + error("Sections to which the relocations applies doesn`t match"); + OutputSection::addSection(C); +} + +template void StaticRelocSection::finalize() { + if (Out::SymTab) + Header.sh_link = Out::SymTab->SectionIndex; + Header.sh_info = LinkedOut->SectionIndex; +} + +template typename ELFFile::uintX_t lld::elf2::getSymVA(const SymbolBody &S) { switch (S.kind()) { case SymbolBody::DefinedSyntheticKind: { @@ -915,14 +939,15 @@ return getSymbolBinding(L) == STB_LOCAL && getSymbolBinding(R) != STB_LOCAL; }); - return; + if (!Config->Relocatable) + return; } if (Out::GnuHashTab) // NB: It also sorts Symbols to meet the GNU hash table requirements. Out::GnuHashTab->addSymbols(Symbols); - size_t I = 0; + size_t I = NumLocals; for (SymbolBody *B : Symbols) - B->setDynamicSymbolTableIndex(++I); + B->setSymbolTableIndex(++I); } template @@ -1105,6 +1130,11 @@ template class OutputSection; template class OutputSection; +template class StaticRelocSection; +template class StaticRelocSection; +template class StaticRelocSection; +template class StaticRelocSection; + template class MergeOutputSection; template class MergeOutputSection; template class MergeOutputSection; Index: ELF/Symbols.h =================================================================== --- ELF/Symbols.h +++ ELF/Symbols.h @@ -85,10 +85,10 @@ uint8_t getVisibility() const { return Visibility; } - unsigned getDynamicSymbolTableIndex() const { - return DynamicSymbolTableIndex; + unsigned getSymbolTableIndex() const { + return SymbolTableIndex; } - void setDynamicSymbolTableIndex(unsigned V) { DynamicSymbolTableIndex = V; } + void setSymbolTableIndex(unsigned V) { SymbolTableIndex = V; } uint32_t GotIndex = -1; uint32_t GotPltIndex = -1; @@ -126,7 +126,7 @@ unsigned IsUsedInRegularObj : 1; unsigned IsUsedInDynamicReloc : 1; unsigned IsTLS : 1; - unsigned DynamicSymbolTableIndex = 0; + unsigned SymbolTableIndex = 0; StringRef Name; Symbol *Backref = nullptr; }; Index: ELF/Writer.cpp =================================================================== --- ELF/Writer.cpp +++ ELF/Writer.cpp @@ -46,6 +46,7 @@ iterator_range *> Rels); void scanRelocs(const InputSection &C); void assignAddresses(); + void assignAddressesRel(); void openFile(StringRef OutputPath); void writeHeader(); void writeSections(); @@ -66,6 +67,7 @@ std::unique_ptr Buffer; SpecificBumpPtrAllocator> SecAlloc; + SpecificBumpPtrAllocator> RelSecAlloc; SpecificBumpPtrAllocator> MSecAlloc; BumpPtrAllocator Alloc; std::vector *> OutputSections; @@ -133,7 +135,10 @@ if (!Config->DiscardAll) copyLocalSymbols(); createSections(); - assignAddresses(); + if (!Config->Relocatable) + assignAddresses(); + else + assignAddressesRel(); openFile(Config->OutputFile); writeHeader(); writeSections(); @@ -258,7 +263,7 @@ typedef typename ELFFile::Elf_Sym Elf_Sym; typedef typename ELFFile::Elf_Sym_Range Elf_Sym_Range; - if (Config->Shared && !Config->NoUndefined) + if ((Config->Relocatable || Config->Shared) && !Config->NoUndefined) return; const Elf_Sym &SymE = cast>(Sym).Sym; @@ -477,12 +482,17 @@ H->sh_type, OutFlags, EntSize}; OutputSectionBase *&Sec = Map[Key]; if (!Sec) { - if (IS) - Sec = new (SecAlloc.Allocate()) - OutputSection(Key.Name, Key.Type, Key.Flags); - else + if (IS) { + if (IS->RelocatingSection) + Sec = new (RelSecAlloc.Allocate()) + StaticRelocSection(Key.Name, Key.Type, Key.Flags); + else + 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); } @@ -665,6 +675,18 @@ return Ret; } +template void Writer::assignAddressesRel() { + uintX_t FileOff = sizeof(Elf_Ehdr); + for (OutputSectionBase *Sec : OutputSections) { + auto Name = Sec->getName(); + FileOff = RoundUpToAlignment(FileOff, Sec->getAlign()); + Sec->setFileOffset(FileOff); + FileOff += Sec->getSize(); + } + SectionHeaderOff = RoundUpToAlignment(FileOff, ELFT::Is64Bits ? 8 : 4); + FileSize = SectionHeaderOff + getNumSections() * sizeof(Elf_Shdr); +} + // Visits all sections to create PHDRs and to assign incremental, // non-overlapping addresses to output sections. template void Writer::assignAddresses() { @@ -804,21 +826,26 @@ auto &FirstObj = cast>(*Config->FirstElf); EHdr->e_ident[EI_OSABI] = FirstObj.getOSABI(); - EHdr->e_type = Config->Shared ? ET_DYN : ET_EXEC; + if (Config->Relocatable) + EHdr->e_type = ET_REL; + else + EHdr->e_type = Config->Shared ? ET_DYN : ET_EXEC; + EHdr->e_machine = FirstObj.getEMachine(); EHdr->e_version = EV_CURRENT; EHdr->e_entry = getEntryAddr(); - EHdr->e_phoff = sizeof(Elf_Ehdr); + EHdr->e_phoff = EHdr->e_type == ET_REL ? 0 : sizeof(Elf_Ehdr); EHdr->e_shoff = SectionHeaderOff; EHdr->e_ehsize = sizeof(Elf_Ehdr); - EHdr->e_phentsize = sizeof(Elf_Phdr); + EHdr->e_phentsize = EHdr->e_type == ET_REL ? 0 : sizeof(Elf_Phdr); EHdr->e_phnum = Phdrs.size(); EHdr->e_shentsize = sizeof(Elf_Shdr); EHdr->e_shnum = getNumSections(); EHdr->e_shstrndx = Out::ShStrTab->SectionIndex; // Write the program header table. - memcpy(Buf + EHdr->e_phoff, &Phdrs[0], Phdrs.size() * sizeof(Phdrs[0])); + if (!Phdrs.empty()) + memcpy(Buf + EHdr->e_phoff, &Phdrs[0], Phdrs.size() * sizeof(Phdrs[0])); // Write the section header table. Note that the first table entry is null. auto SHdrs = reinterpret_cast(Buf + EHdr->e_shoff); Index: test/elf2/Inputs/relocatable.s =================================================================== --- test/elf2/Inputs/relocatable.s +++ test/elf2/Inputs/relocatable.s @@ -0,0 +1,22 @@ +.text +.type xx,@object +.bss +.globl xx +.align 4 +xx: +.long 0 +.size xx, 4 +.type yy,@object +.globl yy +.align 4 +yy: +.long 0 +.size yy, 4 + +.text +.globl foo +.align 16, 0x90 +.type foo,@function +foo: +movl $1, xx +movl $2, yy Index: test/elf2/Inputs/relocatable2.s =================================================================== --- test/elf2/Inputs/relocatable2.s +++ test/elf2/Inputs/relocatable2.s @@ -0,0 +1,22 @@ +.text +.type xxx,@object +.bss +.globl xxx +.align 4 +xxx: +.long 0 +.size xxx, 4 +.type yyy,@object +.globl yyy +.align 4 +yyy: +.long 0 +.size yyy, 4 + +.text +.globl bar +.align 16, 0x90 +.type bar,@function +bar: +movl $8, xxx +movl $9, yyy Index: test/elf2/relocatable.s =================================================================== --- test/elf2/relocatable.s +++ test/elf2/relocatable.s @@ -0,0 +1,116 @@ +// REQUIRES: x86 +// RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t1.o +// RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/relocatable.s -o %t2.o +// RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/relocatable2.s -o %t3.o +// RUN: ld.lld2 -r %t1.o %t2.o %t3.o -o %t +// RUN: llvm-readobj -file-headers -sections -program-headers -symbols -r %t | FileCheck %s +// RUN: llvm-objdump -s -d %t | FileCheck -check-prefix=CHECKTEXT %s + +// Test --relocatable alias +// RUN: ld.lld2 --relocatable %t1.o %t2.o %t3.o -o %t +// RUN: llvm-readobj -file-headers -sections -program-headers -symbols -r %t | FileCheck %s +// RUN: llvm-objdump -s -d %t | FileCheck -check-prefix=CHECKTEXT %s + +// Verify that we can use our relocation output as input to produce executable +// RUN: ld.lld2 -e main %t -o %texec +// RUN: llvm-readobj -file-headers %texec | FileCheck -check-prefix=CHECKEXE %s + +# CHECK: ElfHeader { +# CHECK-NEXT: Ident { +# CHECK-NEXT: Magic: (7F 45 4C 46) +# CHECK-NEXT: Class: 64-bit +# CHECK-NEXT: DataEncoding: LittleEndian +# CHECK-NEXT: FileVersion: 1 +# CHECK-NEXT: OS/ABI: SystemV +# CHECK-NEXT: ABIVersion: 0 +# CHECK-NEXT: Unused: (00 00 00 00 00 00 00) +# CHECK-NEXT: } +# CHECK-NEXT: Type: Relocatable +# CHECK-NEXT: Machine: EM_X86_64 +# CHECK-NEXT: Version: 1 +# CHECK-NEXT: Entry: 0x0 +# CHECK-NEXT: ProgramHeaderOffset: 0x0 +# CHECK-NEXT: SectionHeaderOffset: 0x278 +# CHECK-NEXT: Flags [ +# CHECK-NEXT: ] +# CHECK-NEXT: HeaderSize: 64 +# CHECK-NEXT: ProgramHeaderEntrySize: 0 +# CHECK-NEXT: ProgramHeaderCount: 0 +# CHECK-NEXT: SectionHeaderEntrySize: 64 +# CHECK-NEXT: SectionHeaderCount: 7 +# CHECK-NEXT: StringTableSectionIndex: 5 +# CHECK-NEXT: } + +# CHECK: Relocations [ +# CHECK-NEXT: Section (3) .rela.text { +# CHECK-NEXT: 0x3 R_X86_64_32S x 0x0 +# CHECK-NEXT: 0xE R_X86_64_32S y 0x0 +# CHECK-NEXT: 0x23 R_X86_64_32S xx 0x0 +# CHECK-NEXT: 0x2E R_X86_64_32S yy 0x0 +# CHECK-NEXT: 0x43 R_X86_64_32S xxx 0x0 +# CHECK-NEXT: 0x4E R_X86_64_32S yyy 0x0 +# CHECK-NEXT: } + +# CHECKTEXT: Disassembly of section .text: +# CHECKTEXT-NEXT: main: +# CHECKTEXT-NEXT: 0: c7 04 25 00 00 00 00 05 00 00 00 movl $5, 0 +# CHECKTEXT-NEXT: b: c7 04 25 00 00 00 00 07 00 00 00 movl $7, 0 +# CHECKTEXT: foo: +# CHECKTEXT-NEXT: 20: c7 04 25 00 00 00 00 01 00 00 00 movl $1, 0 +# CHECKTEXT-NEXT: 2b: c7 04 25 00 00 00 00 02 00 00 00 movl $2, 0 +# CHECKTEXT: bar: +# CHECKTEXT-NEXT: 40: c7 04 25 00 00 00 00 08 00 00 00 movl $8, 0 +# CHECKTEXT-NEXT: 4b: c7 04 25 00 00 00 00 09 00 00 00 movl $9, 0 + +# CHECKEXE: Format: ELF64-x86-64 +# CHECKEXE-NEXT: Arch: x86_64 +# CHECKEXE-NEXT: AddressSize: 64bit +# CHECKEXE-NEXT: LoadName: +# CHECKEXE-NEXT: ElfHeader { +# CHECKEXE-NEXT: Ident { +# CHECKEXE-NEXT: Magic: (7F 45 4C 46) +# CHECKEXE-NEXT: Class: 64-bit +# CHECKEXE-NEXT: DataEncoding: LittleEndian +# CHECKEXE-NEXT: FileVersion: 1 +# CHECKEXE-NEXT: OS/ABI: SystemV (0x0) +# CHECKEXE-NEXT: ABIVersion: 0 +# CHECKEXE-NEXT: Unused: (00 00 00 00 00 00 00) +# CHECKEXE-NEXT: } +# CHECKEXE-NEXT: Type: Executable +# CHECKEXE-NEXT: Machine: EM_X86_64 +# CHECKEXE-NEXT: Version: 1 +# CHECKEXE-NEXT: Entry: 0x11000 +# CHECKEXE-NEXT: ProgramHeaderOffset: 0x40 +# CHECKEXE-NEXT: SectionHeaderOffset: 0x2130 +# CHECKEXE-NEXT: Flags [ +# CHECKEXE-NEXT: ] +# CHECKEXE-NEXT: HeaderSize: 64 +# CHECKEXE-NEXT: ProgramHeaderEntrySize: 56 +# CHECKEXE-NEXT: ProgramHeaderCount: 4 +# CHECKEXE-NEXT: SectionHeaderEntrySize: 64 +# CHECKEXE-NEXT: SectionHeaderCount: 6 +# CHECKEXE-NEXT: StringTableSectionIndex: 4 +# CHECKEXE-NEXT: } + +.text +.type x,@object +.bss +.globl x +.align 4 +x: +.long 0 +.size x, 4 +.type y,@object +.globl y +.align 4 +y: +.long 0 +.size y, 4 + +.text +.globl main +.align 16, 0x90 +.type main,@function +main: +movl $5, x +movl $7, y Index: test/elf2/strip-all.s =================================================================== --- test/elf2/strip-all.s +++ test/elf2/strip-all.s @@ -13,6 +13,10 @@ #AFTER: .shstrtab #AFTER-NOT: .strtab +// Ignore --strip-all if -r is specified +#RUN: ld.lld2 %t.o --strip-all -r -o %t1 +#RUN: llvm-objdump -section-headers %t1 | FileCheck %s -check-prefix BEFORE + # Test alias -s #RUN: ld.lld2 %t.o -s -o %t1 #RUN: llvm-objdump -section-headers %t1 | FileCheck %s -check-prefix AFTER