Index: ELF/Config.h =================================================================== --- ELF/Config.h +++ ELF/Config.h @@ -67,6 +67,7 @@ bool NoInhibitExec; bool NoUndefined; bool PrintGcSections; + bool Relocatable; bool Shared; bool Static = false; bool StripAll; Index: ELF/Driver.cpp =================================================================== --- ELF/Driver.cpp +++ ELF/Driver.cpp @@ -111,6 +111,10 @@ Files.push_back(make_unique(MBRef)); return; case file_magic::elf_shared_object: + if (Config->Relocatable) { + error("Attempted static link of dynamic object " + Path); + return; + } Files.push_back(createSharedFile(MBRef)); return; default: @@ -130,12 +134,6 @@ // Some command line options or some combinations of them are not allowed. // This function checks for such errors. static void checkOptions(opt::InputArgList &Args) { - // Traditional linkers can generate re-linkable object files instead - // of executables or DSOs. We don't support that since the feature - // does not seem to provide more value than the static archiver. - if (Args.hasArg(OPT_relocatable)) - error("-r option is not supported. Use 'ar' command instead."); - // The MIPS ABI as of 2016 does not support the GNU-style symbol lookup // table which is a relatively new feature. if (Config->EMachine == EM_MIPS && Config->GnuHash) @@ -143,6 +141,9 @@ if (Config->EMachine == EM_AMDGPU && !Config->Entry.empty()) error("-e option is not valid for AMDGPU."); + + if (Config->Relocatable && Config->Shared) + error("-r and -shared may not be used together"); } static StringRef @@ -219,6 +220,7 @@ Config->NoInhibitExec = Args.hasArg(OPT_noinhibit_exec); Config->NoUndefined = Args.hasArg(OPT_no_undefined); Config->PrintGcSections = Args.hasArg(OPT_print_gc_sections); + Config->Relocatable = Args.hasArg(OPT_relocatable); Config->Shared = Args.hasArg(OPT_shared); Config->StripAll = Args.hasArg(OPT_strip_all); Config->Verbose = Args.hasArg(OPT_verbose); @@ -237,6 +239,9 @@ Config->ZOrigin = hasZOption(Args, "origin"); Config->ZRelro = !hasZOption(Args, "norelro"); + if (Config->Relocatable) + Config->StripAll = false; + if (auto *Arg = Args.getLastArg(OPT_O)) { StringRef Val = Arg->getValue(); if (Val.getAsInteger(10, Config->Optimize)) @@ -303,7 +308,7 @@ std::unique_ptr TI(createTarget()); Target = TI.get(); - if (!Config->Shared) { + if (!Config->Shared && !Config->Relocatable) { // Add entry symbol. // // There is no entry symbol for AMDGPU binaries, so skip adding one to avoid Index: ELF/InputFiles.cpp =================================================================== --- ELF/InputFiles.cpp +++ ELF/InputFiles.cpp @@ -226,7 +226,10 @@ continue; if (!RelocatedSection) fatal("Unsupported relocation reference"); - if (auto *S = dyn_cast>(RelocatedSection)) { + if (Config->Relocatable) { + // For -r, relocation sections are handled as regular input sections. + Sections[I] = new (Alloc) InputSection(this, &Sec); + } else if (auto *S = dyn_cast>(RelocatedSection)) { S->RelocSections.push_back(&Sec); } else if (auto *S = dyn_cast>(RelocatedSection)) { if (S->RelocSection) Index: ELF/InputSection.h =================================================================== --- ELF/InputSection.h +++ ELF/InputSection.h @@ -159,6 +159,16 @@ uint64_t OutSecOff = 0; static bool classof(const InputSectionBase *S); + + InputSectionBase *getRelocatedSection(); + +private: + template + using RelIteratorRange = + llvm::iterator_range *>; + + template + void copyRelocations(uint8_t *Buf, RelIteratorRange Rels); }; // MIPS .reginfo section provides information on the registers used by the code Index: ELF/InputSection.cpp =================================================================== --- ELF/InputSection.cpp +++ ELF/InputSection.cpp @@ -108,6 +108,43 @@ } template +InputSectionBase *InputSection::getRelocatedSection() { + assert(this->Header->sh_type == SHT_RELA || this->Header->sh_type == SHT_REL); + ArrayRef *> Sections = this->File->getSections(); + return Sections[this->Header->sh_info]; +} + +// This is used for -r. We can't use memcpy to copy relocations because we need +// to update symbol table offset and section index for each relocation. So we +// copy relocations one by one. +template +template +void InputSection::copyRelocations(uint8_t *Buf, + RelIteratorRange Rels) { + typedef Elf_Rel_Impl RelType; + InputSectionBase *RelocatedSection = getRelocatedSection(); + + for (const RelType &Rel : Rels) { + uint32_t SymIndex = Rel.getSymbol(Config->Mips64EL); + uint32_t Type = Rel.getType(Config->Mips64EL); + const Elf_Shdr *SymTab = this->File->getSymbolTable(); + + RelType *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) + fatal("Relocation against local symbols is not supported yet"); + + SymbolBody *Body = this->File->getSymbolBody(SymIndex)->repl(); + P->r_offset = RelocatedSection->getOffset(Rel.r_offset); + P->setSymbolAndType(Body->DynsymIndex, Type, Config->Mips64EL); + } +} + +template template uint8_t * InputSectionBase::findMipsPairedReloc(uint8_t *Buf, uint32_t SymIndex, @@ -259,9 +296,21 @@ return; // Copy section contents from source object file to output file. ArrayRef Data = this->getSectionData(); + ELFFile &EObj = this->File->getObj(); + + // That happens with -r. In that case we need fix the relocation position and + // target. No relocations are applied. + if (this->Header->sh_type == SHT_RELA) { + this->copyRelocations(Buf + OutSecOff, EObj.relas(this->Header)); + return; + } + if (this->Header->sh_type == SHT_REL) { + this->copyRelocations(Buf + OutSecOff, EObj.rels(this->Header)); + return; + } + memcpy(Buf + OutSecOff, Data.data(), Data.size()); - ELFFile &EObj = this->File->getObj(); uint8_t *BufEnd = Buf + OutSecOff + Data.size(); // Iterate over all relocation sections that apply to this section. for (const Elf_Shdr *RelSec : this->RelocSections) { Index: ELF/Options.td =================================================================== --- ELF/Options.td +++ ELF/Options.td @@ -94,7 +94,8 @@ def rpath : Separate<["-"], "rpath">, HelpText<"Add a DT_RUNPATH to the output">; -def relocatable : Flag<["--"], "relocatable">; +def relocatable : Flag<["--"], "relocatable">, + HelpText<"Create relocatable object file">; def script : Separate<["--"], "script">, HelpText<"Read linker script">; Index: ELF/OutputSections.h =================================================================== --- ELF/OutputSections.h +++ ELF/OutputSections.h @@ -287,6 +287,7 @@ void sortInitFini(); void sortCtorsDtors(); void writeTo(uint8_t *Buf) override; + void finalize() override; private: void reassignOffsets(); Index: ELF/OutputSections.cpp =================================================================== --- ELF/OutputSections.cpp +++ ELF/OutputSections.cpp @@ -728,9 +728,24 @@ } template -OutputSection::OutputSection(StringRef Name, uint32_t Type, - uintX_t Flags) - : OutputSectionBase(Name, Type, Flags) {} +OutputSection::OutputSection(StringRef Name, uint32_t Type, uintX_t Flags) + : OutputSectionBase(Name, Type, Flags) { + if (Type == SHT_RELA) + this->Header.sh_entsize = sizeof(Elf_Rela); + else if (Type == SHT_REL) + this->Header.sh_entsize = sizeof(Elf_Rel); +} + +template void OutputSection::finalize() { + uint32_t Type = this->Header.sh_type; + if (Type != SHT_RELA && Type != SHT_REL) + return; + this->Header.sh_link = Out::SymTab->SectionIndex; + // sh_info for SHT_REL[A] sections should contain the section header index of + // the section to which the relocation applies. + InputSectionBase *RelocatedSection = Sections[0]->getRelocatedSection(); + this->Header.sh_info = RelocatedSection->OutSec->SectionIndex; +} template void OutputSection::addSection(InputSectionBase *C) { @@ -1368,6 +1383,13 @@ this->Header.sh_link = StrTabSec.SectionIndex; this->Header.sh_info = NumLocals + 1; + if (Config->Relocatable) { + size_t I = NumLocals; + for (const std::pair &P : Symbols) + P.first->DynsymIndex = ++I; + return; + } + if (!StrTabSec.isDynamic()) { std::stable_sort(Symbols.begin(), Symbols.end(), [](const std::pair &L, Index: ELF/Writer.cpp =================================================================== --- ELF/Writer.cpp +++ ELF/Writer.cpp @@ -69,6 +69,7 @@ void scanRelocs(InputSectionBase &S, const Elf_Shdr &RelSec); void createPhdrs(); void assignAddresses(); + void assignAddressesRelocatable(); void fixAbsoluteSymbols(); bool openFile(); void writeHeader(); @@ -93,13 +94,15 @@ std::vector>> OwningSections; // We create a section for the ELF header and one for the program headers. - const unsigned NumDummySections = 2; ArrayRef *> getSections() const { - return makeArrayRef(OutputSections).slice(NumDummySections); + return makeArrayRef(OutputSections).slice(dummySectionsNum()); } unsigned getNumSections() const { - return OutputSections.size() + 1 - NumDummySections; + return OutputSections.size() + 1 - dummySectionsNum(); } + // Usually there are 2 dummies sections: ELF header and program header. + // Relocatable output does not require program headers to be created. + unsigned dummySectionsNum() const { return Config->Relocatable ? 1 : 2; } void addRelIpltSymbols(); void addStartEndSymbols(); @@ -193,8 +196,12 @@ addReservedSymbols(); if (!createSections()) return; - createPhdrs(); - assignAddresses(); + if (!Config->Relocatable) { + createPhdrs(); + assignAddresses(); + } else { + assignAddressesRelocatable(); + } fixAbsoluteSymbols(); if (!openFile()) return; @@ -495,7 +502,7 @@ template static void reportUndefined(SymbolTable &Symtab, SymbolBody *Sym) { - if (Config->Shared && !Config->NoUndefined) + if ((Config->Relocatable || Config->Shared) && !Config->NoUndefined) return; std::string Msg = "undefined symbol: " + Sym->getName().str(); @@ -928,7 +935,8 @@ // Create output section objects and add them to OutputSections. template bool Writer::createSections() { OutputSections.push_back(Out::ElfHeader); - OutputSections.push_back(Out::ProgramHeaders); + if (!Config->Relocatable) + OutputSections.push_back(Out::ProgramHeaders); // Add .interp first because some loaders want to see that section // on the first page of the executable file when loaded into memory. @@ -1040,8 +1048,8 @@ std::stable_sort(OutputSections.begin(), OutputSections.end(), compareSections); - for (unsigned I = NumDummySections, N = OutputSections.size(); I < N; ++I) - OutputSections[I]->SectionIndex = I + 1 - NumDummySections; + for (unsigned I = dummySectionsNum(), N = OutputSections.size(); I < N; ++I) + OutputSections[I]->SectionIndex = I + 1 - dummySectionsNum(); for (OutputSectionBase *Sec : getSections()) Sec->setSHName(Out::ShStrTab->addString(Sec->getName())); @@ -1304,6 +1312,21 @@ AddHdr(PT_GNU_STACK, PF_R | PF_W); } +// Used for relocatable output (-r). In this case we create only ELF file +// header, do not create program headers. Also assign of section addresses +// is very straightforward: we just put all sections sequentually to the file. +template void Writer::assignAddressesRelocatable() { + Out::ElfHeader->setSize(sizeof(Elf_Ehdr)); + uintX_t FileOff = 0; + for (OutputSectionBase *Sec : OutputSections) { + FileOff = alignTo(FileOff, Sec->getAlign()); + Sec->setFileOffset(FileOff); + FileOff += Sec->getSize(); + } + SectionHeaderOff = alignTo(FileOff, sizeof(uintX_t)); + FileSize = SectionHeaderOff + getNumSections() * sizeof(Elf_Shdr); +} + // Visits all headers in PhdrTable and assigns the adresses to // the output sections. Also creates common and special headers. template void Writer::assignAddresses() { @@ -1445,20 +1468,29 @@ auto &FirstObj = cast>(*Config->FirstElf); EHdr->e_ident[EI_OSABI] = FirstObj.getOSABI(); - EHdr->e_type = Config->Shared ? ET_DYN : ET_EXEC; + if (Config->Shared) + EHdr->e_type = ET_DYN; + else if (Config->Relocatable) + EHdr->e_type = ET_REL; + else + EHdr->e_type = ET_EXEC; + EHdr->e_machine = FirstObj.getEMachine(); EHdr->e_version = EV_CURRENT; EHdr->e_entry = getEntryAddr(); - EHdr->e_phoff = sizeof(Elf_Ehdr); EHdr->e_shoff = SectionHeaderOff; EHdr->e_flags = getELFFlags(); EHdr->e_ehsize = sizeof(Elf_Ehdr); - EHdr->e_phentsize = sizeof(Elf_Phdr); EHdr->e_phnum = Phdrs.size(); EHdr->e_shentsize = sizeof(Elf_Shdr); EHdr->e_shnum = getNumSections(); EHdr->e_shstrndx = Out::ShStrTab->SectionIndex; + if (!Config->Relocatable) { + EHdr->e_phoff = sizeof(Elf_Ehdr); + EHdr->e_phentsize = sizeof(Elf_Phdr); + } + // Write the program header table. auto *HBuf = reinterpret_cast(Buf + EHdr->e_phoff); for (Phdr &P : Phdrs) Index: test/ELF/Inputs/relocatable.s =================================================================== --- test/ELF/Inputs/relocatable.s +++ test/ELF/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/ELF/Inputs/relocatable2.s =================================================================== --- test/ELF/Inputs/relocatable2.s +++ test/ELF/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/ELF/relocatable.s =================================================================== --- test/ELF/relocatable.s +++ test/ELF/relocatable.s @@ -1,9 +1,129 @@ # 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.lld -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 -# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t -# RUN: not ld.lld -r %t -o %t2 2>&1 | FileCheck %s +## Test --relocatable alias +# RUN: ld.lld --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 -# CHECK: -r option is not supported. Use 'ar' command instead. +## Verify that we can use our relocation output as input to produce executable +# RUN: ld.lld -e main %t -o %texec +# RUN: llvm-readobj -file-headers %texec | FileCheck -check-prefix=CHECKEXE %s -.globl _start; -_start: +## Attempt to link DSO with -r +# RUN: ld.lld -shared %t1.o -o %t.so +# RUN: not ld.lld -r %t.so %t2.o -o %tfail 2>&1 | FileCheck -check-prefix=ERR %s +# ERR: Attempted static link of dynamic object + +## Attempt to use -r and shared together +# RUN: not ld.lld -r -shared %t2.o -o %tfail 2>&1 | FileCheck -check-prefix=ERR2 %s +# ERR2: -r and -shared may not be used together + +# 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: 0x2D8 +# 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: 0x11E8 +# CHECKEXE-NEXT: Flags [ +# CHECKEXE-NEXT: ] +# CHECKEXE-NEXT: HeaderSize: 64 +# CHECKEXE-NEXT: ProgramHeaderEntrySize: 56 +# CHECKEXE-NEXT: ProgramHeaderCount: 5 +# 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 + +blah: +goo: +abs = 42 Index: test/ELF/strip-all.s =================================================================== --- test/ELF/strip-all.s +++ test/ELF/strip-all.s @@ -13,6 +13,10 @@ #AFTER: .shstrtab #AFTER-NOT: .strtab +# Ignore --strip-all if -r is specified +#RUN: ld.lld %t.o --strip-all -r -o %t1 +#RUN: llvm-objdump -section-headers %t1 | FileCheck %s -check-prefix BEFORE + # Test alias -s #RUN: ld.lld %t.o -s -o %t1 #RUN: llvm-objdump -section-headers %t1 | FileCheck %s -check-prefix AFTER