Index: docs/Extensions.rst =================================================================== --- docs/Extensions.rst +++ docs/Extensions.rst @@ -221,6 +221,22 @@ .section .foo,"a",@progbits .section .bar,"ao",@progbits,.foo +``.linker-options`` Section (linker options) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +In order to support passing linker options from the frontend to the linker, a +special section ``.linker-options`` is emitted when necessary. The contents of +this section is a simple pair-wise encoding of options for consideration by the +linker. The strings are encoded as standard null-terminated C-strings. They +are emitted inline to avoid having the linker to traverse the object file for +retrieving the value. The linker is permitted to not honour the option and +instead provide a warning/error to the user that the requested option was not +honoured. + +The section is marked as ``SHF_PROGBITS`` and has the ``SHF_EXCLUDE`` flag to +ensure that the section is treated as opaque by linkers which do not support the +feature and will not be emitted into the final linked binary. + Target Specific Behaviour ========================= Index: lib/CodeGen/TargetLoweringObjectFileImpl.cpp =================================================================== --- lib/CodeGen/TargetLoweringObjectFileImpl.cpp +++ lib/CodeGen/TargetLoweringObjectFileImpl.cpp @@ -93,6 +93,24 @@ void TargetLoweringObjectFileELF::emitModuleMetadata( MCStreamer &Streamer, Module &M, const TargetMachine &TM) const { + auto &C = getContext(); + + if (NamedMDNode *LinkerOptions = M.getNamedMetadata("llvm.linker.options")) { + auto *S = + C.getELFSection(".linker-options", ELF::SHT_PROGBITS, ELF::SHF_EXCLUDE); + + Streamer.SwitchSection(S); + + for (const auto &Operand : LinkerOptions->operands()) { + if (cast(Operand)->getNumOperands() != 2) + report_fatal_error("invalid llvm.linker.options"); + for (const auto &Option : cast(Operand)->operands()) { + Streamer.EmitBytes(cast(Option)->getString()); + Streamer.EmitIntValue(0, 1); + } + } + } + unsigned Version = 0; unsigned Flags = 0; StringRef Section; @@ -101,7 +119,6 @@ if (Section.empty()) return; - auto &C = getContext(); auto *S = C.getELFSection(Section, ELF::SHT_PROGBITS, ELF::SHF_ALLOC); Streamer.SwitchSection(S); Streamer.EmitLabel(C.getOrCreateSymbol(StringRef("OBJC_IMAGE_INFO"))); Index: test/Feature/elf-linker-options.ll =================================================================== --- /dev/null +++ test/Feature/elf-linker-options.ll @@ -0,0 +1,16 @@ +; RUN: llc -mtriple x86_64-elf -filetype asm -o - %s | FileCheck %s + +!llvm.linker.options = !{!0, !1} + +!0 = !{!"option 0", !"value 0"} +!1 = !{!"option 1", !"value 1"} + +; CHECK: .section ".linker-options","e",@progbits +; CHECK-NEXT: .ascii "option 0" +; CHECK-NEXT: .byte 0 +; CHECK-NEXT: .ascii "value 0" +; CHECK-NEXT: .byte 0 +; CHECK-NEXT: .ascii "option 1" +; CHECK-NEXT: .byte 0 +; CHECK-NEXT: .ascii "value 1" +; CHECK-NEXT: .byte 0 Index: test/tools/llvm-readobj/elf-linker-options.ll =================================================================== --- /dev/null +++ test/tools/llvm-readobj/elf-linker-options.ll @@ -0,0 +1,11 @@ +; RUN: llc -mtriple x86_64-elf -filetype obj -o - %s | llvm-readobj -elf-linker-options - | FileCheck %s + +!llvm.linker.options = !{!0, !1} + +!0 = !{!"option 0", !"value 0"} +!1 = !{!"option 1", !"value 1"} + +; CHECK: LinkerOptions [ +; CHECK: option 0: value 0 +; CHECK: option 1: value 1 +; CHECK: ] Index: tools/llvm-readobj/ELFDumper.cpp =================================================================== --- tools/llvm-readobj/ELFDumper.cpp +++ tools/llvm-readobj/ELFDumper.cpp @@ -164,6 +164,8 @@ void printNotes() override; + void printELFLinkerOptions() override; + private: std::unique_ptr> ELFDumperStyle; @@ -316,6 +318,7 @@ virtual void printProgramHeaders(const ELFFile *Obj) = 0; virtual void printHashHistogram(const ELFFile *Obj) = 0; virtual void printNotes(const ELFFile *Obj) = 0; + virtual void printELFLinkerOptions(const ELFFile *Obj) = 0; virtual void printMipsGOT(const MipsGOTParser &Parser) = 0; virtual void printMipsPLT(const MipsGOTParser &Parser) = 0; const ELFDumper *dumper() const { return Dumper; } @@ -345,6 +348,7 @@ void printProgramHeaders(const ELFO *Obj) override; void printHashHistogram(const ELFFile *Obj) override; void printNotes(const ELFFile *Obj) override; + void printELFLinkerOptions(const ELFFile *Obj) override; void printMipsGOT(const MipsGOTParser &Parser) override; void printMipsPLT(const MipsGOTParser &Parser) override; @@ -405,6 +409,7 @@ void printProgramHeaders(const ELFO *Obj) override; void printHashHistogram(const ELFFile *Obj) override; void printNotes(const ELFFile *Obj) override; + void printELFLinkerOptions(const ELFFile *Obj) override; void printMipsGOT(const MipsGOTParser &Parser) override; void printMipsPLT(const MipsGOTParser &Parser) override; @@ -1501,6 +1506,10 @@ ELFDumperStyle->printNotes(Obj); } +template void ELFDumper::printELFLinkerOptions() { + ELFDumperStyle->printELFLinkerOptions(Obj); +} + #define LLVM_READOBJ_TYPE_CASE(name) \ case DT_##name: return #name @@ -3554,6 +3563,11 @@ } } +template +void GNUStyle::printELFLinkerOptions(const ELFFile *Obj) { + OS << "printELFLinkerOptions not implemented!\n"; +} + template void GNUStyle::printMipsGOT(const MipsGOTParser &Parser) { size_t Bias = ELFT::Is64Bits ? 8 : 0; @@ -4062,6 +4076,28 @@ W.startLine() << "printNotes not implemented!\n"; } +template +void LLVMStyle::printELFLinkerOptions(const ELFFile *Obj) { + ListScope L(W, "LinkerOptions"); + + for (const Elf_Shdr &Shdr : unwrapOrError(Obj->sections())) { + StringRef Name = unwrapOrError(Obj->getSectionName(&Shdr)); + if (Name != ".linker-options") + continue; + + ArrayRef Contents = unwrapOrError(Obj->getSectionContents(&Shdr)); + for (const uint8_t *P = Contents.begin(), *E = Contents.end(); P < E; ) { + StringRef Key = StringRef(reinterpret_cast(P)); + StringRef Value = + StringRef(reinterpret_cast(P) + Key.size() + 1); + + W.printString(Key, Value); + + P = P + Key.size() + Value.size() + 2; + } + } +} + template void LLVMStyle::printMipsGOT(const MipsGOTParser &Parser) { auto PrintEntry = [&](const Elf_Addr *E) { Index: tools/llvm-readobj/ObjDumper.h =================================================================== --- tools/llvm-readobj/ObjDumper.h +++ tools/llvm-readobj/ObjDumper.h @@ -48,6 +48,7 @@ virtual void printGroupSections() {} virtual void printHashHistogram() {} virtual void printNotes() {} + virtual void printELFLinkerOptions() {} // Only implemented for ARM ELF at this time. virtual void printAttributes() { } Index: tools/llvm-readobj/llvm-readobj.cpp =================================================================== --- tools/llvm-readobj/llvm-readobj.cpp +++ tools/llvm-readobj/llvm-readobj.cpp @@ -229,6 +229,11 @@ COFFLoadConfig("coff-load-config", cl::desc("Display the PE/COFF load config")); + // -elf-linker-options + cl::opt + ELFLinkerOptions("elf-linker-options", + cl::desc("Display the ELF .linker-options section")); + // -macho-data-in-code cl::opt MachODataInCode("macho-data-in-code", @@ -419,6 +424,8 @@ if (opts::VersionInfo) Dumper->printVersionInfo(); if (Obj->isELF()) { + if (opts::ELFLinkerOptions) + Dumper->printELFLinkerOptions(); if (Obj->getArch() == llvm::Triple::arm) if (opts::ARMAttributes) Dumper->printAttributes();