Index: ELF/Config.h =================================================================== --- ELF/Config.h +++ ELF/Config.h @@ -67,6 +67,7 @@ bool NoInhibitExec; bool NoUndefined; bool PrintGcSections; + bool Relocatable = false; 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; case sys::fs::file_magic::bitcode: @@ -133,12 +137,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) @@ -146,6 +144,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 @@ -222,6 +223,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); @@ -240,6 +242,11 @@ Config->ZOrigin = hasZOption(Args, "origin"); Config->ZRelro = !hasZOption(Args, "norelro"); + if (Config->Relocatable) { + Config->StripAll = false; + Config->Static = true; + } + if (auto *Arg = Args.getLastArg(OPT_O)) { StringRef Val = Arg->getValue(); if (Val.getAsInteger(10, Config->Optimize)) @@ -306,7 +313,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,13 @@ continue; if (!RelocatedSection) fatal("Unsupported relocation reference"); - if (auto *S = dyn_cast>(RelocatedSection)) { + if (Config->Relocatable) { + // If relocatable output is choosen, relocation sections are handled as + // regular input sections, because no relocations are applied during link. + InputSection *S = new (Alloc) InputSection(this, &Sec); + S->RelocatingSection = RelocatedSection; + Sections[I] = S; + } 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 @@ -67,7 +67,7 @@ // Translate an offset in the input section to an offset in the output // section. - uintX_t getOffset(uintX_t Offset); + uintX_t getOffset(uintX_t Offset = 0); ArrayRef getSectionData() const; @@ -82,6 +82,18 @@ template void relocate(uint8_t *Buf, uint8_t *BufEnd, RelIteratorRange Rels); + template + void fixupRelocations(uint8_t *Buf, uint8_t *BufEnd, + RelIteratorRange Rels); + + // If -r (or --relocatable) option is given, this member may have a non-null + // value. During partial linking, we do not apply any relocations but just + // leave them as is, expecting the final static linking applies them all at + // once. In that case, relocation sections are handled as regular input + // sections, and their RelocatingSection pointers point to their target + // sections. + InputSectionBase *RelocatingSection = nullptr; + private: template uint8_t *findMipsPairedReloc(uint8_t *Buf, uint32_t SymIndex, uint32_t Type, Index: ELF/InputSection.cpp =================================================================== --- ELF/InputSection.cpp +++ ELF/InputSection.cpp @@ -98,6 +98,33 @@ template template +void InputSectionBase::fixupRelocations(uint8_t *Buf, uint8_t *BufEnd, + RelIteratorRange Rels) { + typedef Elf_Rel_Impl RelType; + size_t Num = Rels.end() - Rels.begin(); + for (size_t I = 0; I < Num; ++I) { + const RelType &RI = *(Rels.begin() + I); + uint32_t SymIndex = RI.getSymbol(Config->Mips64EL); + uint32_t Type = RI.getType(Config->Mips64EL); + const Elf_Shdr *SymTab = 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("Not implemented relocation section type"); + + SymbolBody *Body = File->getSymbolBody(SymIndex)->repl(); + P->r_offset += RelocatingSection->getOffset(); + P->setSymbolAndType(Body->DynsymIndex, Type, Config->Mips64EL); + } +} + +template +template uint8_t * InputSectionBase::findMipsPairedReloc(uint8_t *Buf, uint32_t SymIndex, uint32_t Type, @@ -252,6 +279,19 @@ ELFFile &EObj = this->File->getObj(); uint8_t *BufEnd = Buf + OutSecOff + Data.size(); + + // If it is SHT_REL[A] section then RelocatingSection may 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. No relocations are applied. + if (RelocatingSection) { + if (Header->sh_type == SHT_RELA) + this->fixupRelocations(Buf + OutSecOff, BufEnd, EObj.relas(Header)); + else + this->fixupRelocations(Buf + OutSecOff, BufEnd, EObj.rels(Header)); + return; + } + // Iterate over all relocation sections that apply to this section. for (const Elf_Shdr *RelSec : this->RelocSections) { if (RelSec->sh_type == SHT_RELA) 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 @@ -275,7 +275,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; @@ -293,6 +293,23 @@ std::vector *> Sections; }; +// This section contains input relocation sections so that they are copied +// to the output. Used only when -r is specified (-r lets the linker do partial +// linking, which combines multiple .o files into single .o file). +// Normally, we don't need this class because we interpret and consume +// relocations ourselves instead of copying. +template +class StaticRelocSection final : public OutputSection { +public: + StaticRelocSection(StringRef Name, uint32_t Type, uintX_t Flags, bool IsRela); + void addSection(InputSectionBase *C) override; + void finalize() override; + +private: + // Output section of section to which relocation applies. + OutputSectionBase *RelocOutSec = nullptr; +}; + template class MergeOutputSection final : public OutputSectionBase { typedef typename OutputSectionBase::uintX_t uintX_t; Index: ELF/OutputSections.cpp =================================================================== --- ELF/OutputSections.cpp +++ ELF/OutputSections.cpp @@ -747,6 +747,30 @@ this->Header.sh_size = Off; } +template +StaticRelocSection::StaticRelocSection(StringRef Name, uint32_t Type, + uintX_t Flags, bool IsRela) + : OutputSection(Name, Type, Flags) { + this->Header.sh_addralign = sizeof(uintX_t); + this->Header.sh_entsize = IsRela ? sizeof(Elf_Rela) : sizeof(Elf_Rel); +} + +template +void StaticRelocSection::addSection(InputSectionBase *C) { + if (!RelocOutSec) + RelocOutSec = C->RelocatingSection->OutSec; + else if (RelocOutSec != C->RelocatingSection->OutSec) + error("Sections to which the relocations applies doesn`t match"); + OutputSection::addSection(C); +} + +template void StaticRelocSection::finalize() { + 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. + Header.sh_info = RelocOutSec->SectionIndex; +} + // If an input string is in the form of "foo.N" where N is a number, // return N. Otherwise, returns 65536, which is one greater than the // lowest priority. @@ -1375,14 +1399,15 @@ return getSymbolBinding(L.first) == STB_LOCAL && getSymbolBinding(R.first) != 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); else if (Config->EMachine == EM_MIPS) std::stable_sort(Symbols.begin(), Symbols.end(), sortMipsSymbols); - size_t I = 0; + size_t I = NumLocals; for (const std::pair &P : Symbols) P.first->DynsymIndex = ++I; } @@ -1597,6 +1622,11 @@ template class OutputSection; template class OutputSection; +template class StaticRelocSection; +template class StaticRelocSection; +template class StaticRelocSection; +template class StaticRelocSection; + template class EHOutputSection; template class EHOutputSection; template class EHOutputSection; 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,13 @@ 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(); } + unsigned dummySectionsNum() const { return Config->Relocatable ? 1 : 2; } void addRelIpltSymbols(); void addStartEndSymbols(); @@ -193,8 +194,12 @@ addReservedSymbols(); if (!createSections()) return; - createPhdrs(); - assignAddresses(); + if (!Config->Relocatable) { + createPhdrs(); + assignAddresses(); + } else { + assignAddressesRelocatable(); + } fixAbsoluteSymbols(); if (!openFile()) return; @@ -494,7 +499,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(); @@ -834,7 +839,11 @@ switch (C->SectionKind) { case InputSectionBase::Regular: - Sec = new OutputSection(Key.Name, Key.Type, Key.Flags); + if (C->RelocatingSection) + Sec = new StaticRelocSection(Key.Name, Key.Type, Key.Flags, + shouldUseRela()); + else + Sec = new OutputSection(Key.Name, Key.Type, Key.Flags); break; case InputSectionBase::EHFrame: Sec = new EHOutputSection(Key.Name, Key.Type, Key.Flags); @@ -921,7 +930,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. @@ -1033,8 +1043,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())); @@ -1297,6 +1307,18 @@ AddHdr(PT_GNU_STACK, PF_R | PF_W); } +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() { @@ -1437,15 +1459,21 @@ 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_phoff = Config->Relocatable ? 0 : 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_phentsize = Config->Relocatable ? 0 : sizeof(Elf_Phdr); EHdr->e_phnum = Phdrs.size(); EHdr->e_shentsize = sizeof(Elf_Shdr); EHdr->e_shnum = getNumSections(); 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,130 @@ # 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: 0x2190 +# 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