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,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)) @@ -303,7 +310,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 @@ -219,6 +219,7 @@ fatal("Invalid relocated section index"); InputSectionBase *RelocatedSection = Sections[RelocatedSectionIndex]; + // Strictly speaking, a relocation section must be included in the // group of the section it relocates. However, LLVM 3.3 and earlier // would fail to do so, so we gracefully handle that case. @@ -226,6 +227,15 @@ continue; if (!RelocatedSection) fatal("Unsupported relocation reference"); + + // If -r option is given, relocation sections are handled as + // input sections, because no relocations are applied during link. + if (Config->Relocatable) { + Sections[I] = + new (Alloc) RelocInputSection(this, &Sec, RelocatedSection); + break; + } + if (auto *S = dyn_cast>(RelocatedSection)) { S->RelocSections.push_back(&Sec); } else if (auto *S = dyn_cast>(RelocatedSection)) { Index: ELF/InputSection.h =================================================================== --- ELF/InputSection.h +++ ELF/InputSection.h @@ -36,7 +36,7 @@ ObjectFile *File; public: - enum Kind { Regular, EHFrame, Merge, MipsReginfo }; + enum Kind { Regular, EHFrame, Merge, Reloc, MipsReginfo }; Kind SectionKind; InputSectionBase(ObjectFile *File, const Elf_Shdr *Header, @@ -170,6 +170,42 @@ static bool classof(const InputSectionBase *S); }; +// This class represents relocation sections that are to be copied to output. +// Used only for -r (or --relocatable), so if you are not interested how +// we produce relocatable object files, you can ignore this class. +// +// If a relocatable output is requested, we will create a regular object file +// instead of an executable or a shared object file. Becaues the output will +// be processed by a static linker again, we do not have to resolve any +// relocations. This class represents such relocation sections that are just +// copied to output. +template class RelocInputSection : public InputSectionBase { + typedef InputSectionBase Base; + typedef typename llvm::object::ELFFile::Elf_Shdr Elf_Shdr; + typedef typename llvm::object::ELFFile::uintX_t uintX_t; + + template + using RelIteratorRange = + llvm::iterator_range *>; + +public: + RelocInputSection(ObjectFile *F, const Elf_Shdr *Header, + InputSectionBase *Target); + + static bool classof(const InputSectionBase *S); + + void writeTo(uint8_t *Buf); + + // The section this section's relocations would be applied to. + InputSectionBase *Target; + + uint64_t OutSecOff = 0; + +private: + template + void copyRelocations(uint8_t *Buf, RelIteratorRange Rels); +}; + // MIPS .reginfo section provides information on the registers used by the code // in the object file. Linker should collect this information and write a single // .reginfo section in the output file. The output section contains a union of Index: ELF/InputSection.cpp =================================================================== --- ELF/InputSection.cpp +++ ELF/InputSection.cpp @@ -51,6 +51,8 @@ return cast>(this)->getOffset(Offset); case Merge: return cast>(this)->getOffset(Offset); + case Reloc: + return cast>(this)->OutSecOff + Offset; case MipsReginfo: // MIPS .reginfo sections are consumed by the linker, // so it should never be copied to output. @@ -252,6 +254,7 @@ 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) { if (RelSec->sh_type == SHT_RELA) @@ -356,6 +359,57 @@ } template +RelocInputSection::RelocInputSection(ObjectFile *F, + const Elf_Shdr *H, + InputSectionBase *T) + : InputSectionBase(F, H, Base::Reloc), Target(T) {} + +template +bool RelocInputSection::classof(const InputSectionBase *S) { + return S->SectionKind == Base::Reloc; +} + +template void RelocInputSection::writeTo(uint8_t *Buf) { + ELFFile &EObj = this->File->getObj(); + if (this->Header->sh_type == SHT_RELA) + this->copyRelocations(Buf + OutSecOff, EObj.relas(this->Header)); + else + this->copyRelocations(Buf + OutSecOff, EObj.rels(this->Header)); +} + +// This function copies section contents to Buf, assuming that the current +// section contains relocations. If -r is specified, we are a producer of +// an object file, so instead of applying, we copy all relocations to a +// resulting object file and let the final static linker resolve all +// relocations. 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 RelocInputSection::copyRelocations(uint8_t *Buf, + RelIteratorRange Rels) { + typedef Elf_Rel_Impl RelTy; + for (const RelTy &Rel : Rels) { + uint32_t SymIndex = Rel.getSymbol(Config->Mips64EL); + uint32_t Type = Rel.getType(Config->Mips64EL); + const Elf_Shdr *SymTab = this->File->getSymbolTable(); + + RelTy *P = reinterpret_cast(Buf); + Buf += sizeof(RelTy); + + // 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 = Target->getOffset(Rel.r_offset); + P->setSymbolAndType(Body->DynsymIndex, Type, Config->Mips64EL); + } +} + +template MipsReginfoInputSection::MipsReginfoInputSection(ObjectFile *F, const Elf_Shdr *Hdr) : InputSectionBase(F, Hdr, InputSectionBase::MipsReginfo) { @@ -381,6 +435,11 @@ template class elf2::InputSection; template class elf2::InputSection; +template class elf2::RelocInputSection; +template class elf2::RelocInputSection; +template class elf2::RelocInputSection; +template class elf2::RelocInputSection; + template class elf2::EHInputSection; template class elf2::EHInputSection; template class elf2::EHInputSection; 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 @@ -23,17 +23,18 @@ namespace elf2 { class SymbolBody; -template class SymbolTable; -template class SymbolTableSection; -template class StringTableSection; +template class DefinedRegular; template class EHInputSection; template class InputSection; template class InputSectionBase; template class MergeInputSection; template class MipsReginfoInputSection; -template class OutputSection; template class ObjectFile; -template class DefinedRegular; +template class OutputSection; +template class RelocInputSection; +template class StringTableSection; +template class SymbolTable; +template class SymbolTableSection; template static inline typename llvm::object::ELFFile::uintX_t @@ -293,6 +294,27 @@ 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 OutputSectionBase { +public: + typedef typename llvm::object::ELFFile::Elf_Rel Elf_Rel; + typedef typename llvm::object::ELFFile::Elf_Rela Elf_Rela; + typedef typename llvm::object::ELFFile::uintX_t uintX_t; + + StaticRelocSection(StringRef Name, uint32_t Type, uintX_t Flags, bool IsRela); + void addSection(InputSectionBase *S) override; + void finalize() override; + void writeTo(uint8_t *Buf) override; + +private: + std::vector *> Sections; +}; + 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,42 @@ this->Header.sh_size = Off; } +template +StaticRelocSection::StaticRelocSection(StringRef Name, uint32_t Type, + uintX_t Flags, bool IsRela) + : OutputSectionBase(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 *Sec) { + auto *S = cast>(Sec); + Sections.push_back(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; + Off = alignTo(Off, Align); + S->OutSecOff = Off; + Off += S->getSize(); + this->Header.sh_size = Off; +} + +template void StaticRelocSection::writeTo(uint8_t *Buf) { + for (RelocInputSection *S : Sections) + S->writeTo(Buf); +} + +template void StaticRelocSection::finalize() { + 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. + this->Header.sh_info = Sections[0]->Target->OutSec->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. @@ -1369,6 +1405,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, @@ -1598,6 +1641,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,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(); @@ -837,6 +844,10 @@ case InputSectionBase::Regular: Sec = new OutputSection(Key.Name, Key.Type, Key.Flags); break; + case InputSectionBase::Reloc: + Sec = new StaticRelocSection(Key.Name, Key.Type, Key.Flags, + shouldUseRela()); + break; case InputSectionBase::EHFrame: Sec = new EHOutputSection(Key.Name, Key.Type, Key.Flags); break; @@ -928,7 +939,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 +1052,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 +1316,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 +1472,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/relocatable.s =================================================================== --- test/ELF/relocatable.s +++ /dev/null @@ -1,9 +0,0 @@ -# REQUIRES: x86 - -# 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 - -# CHECK: -r option is not supported. Use 'ar' command instead. - -.globl _start; -_start: 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