From a94c33942373cb504b6e64c95415165907a89d34 Mon Sep 17 00:00:00 2001 From: Daniel Dunbar Date: Fri, 18 Jan 2013 01:26:07 +0000 Subject: [MC/Mach-O] Add support for linker options in Mach-O files. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@172779 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/llvm/MC/MCAssembler.h | 12 ++++++++ include/llvm/MC/MCMachObjectWriter.h | 2 ++ include/llvm/Object/MachOFormat.h | 11 ++++++- include/llvm/Object/MachOObject.h | 3 ++ lib/MC/MCMachOStreamer.cpp | 5 ++++ lib/MC/MachObjectWriter.cpp | 56 ++++++++++++++++++++++++++++++++---- lib/Object/MachOObject.cpp | 11 +++++++ test/MC/MachO/linker-option-2.s | 25 ++++++++++++++++ tools/macho-dump/macho-dump.cpp | 30 ++++++++++++++++++- 9 files changed, 148 insertions(+), 7 deletions(-) create mode 100644 test/MC/MachO/linker-option-2.s diff --git a/include/llvm/MC/MCAssembler.h b/include/llvm/MC/MCAssembler.h index 981002f798..2edd956108 100644 --- a/include/llvm/MC/MCAssembler.h +++ b/include/llvm/MC/MCAssembler.h @@ -849,6 +849,10 @@ private: std::vector IndirectSymbols; std::vector DataRegions; + + /// The list of linker options to propagate into the object file. + std::vector > LinkerOptions; + /// The set of function symbols for which a .thumb_func directive has /// been seen. // @@ -1059,6 +1063,14 @@ public: size_t indirect_symbol_size() const { return IndirectSymbols.size(); } + /// @} + /// @name Linker Option List Access + /// @{ + + std::vector > &getLinkerOptions() { + return LinkerOptions; + } + /// @} /// @name Data Region List Access /// @{ diff --git a/include/llvm/MC/MCMachObjectWriter.h b/include/llvm/MC/MCMachObjectWriter.h index 3cd278e7c0..3c9a588d04 100644 --- a/include/llvm/MC/MCMachObjectWriter.h +++ b/include/llvm/MC/MCMachObjectWriter.h @@ -196,6 +196,8 @@ public: void WriteLinkeditLoadCommand(uint32_t Type, uint32_t DataOffset, uint32_t DataSize); + void WriteLinkerOptionsLoadCommand(const std::vector &Options); + // FIXME: We really need to improve the relocation validation. Basically, we // want to implement a separate computation which evaluates the relocation // entry as the linker would, and verifies that the resultant fixup value is diff --git a/include/llvm/Object/MachOFormat.h b/include/llvm/Object/MachOFormat.h index 001cb6540a..ffca391ea2 100644 --- a/include/llvm/Object/MachOFormat.h +++ b/include/llvm/Object/MachOFormat.h @@ -148,7 +148,8 @@ namespace macho { LCT_CodeSignature = 0x1d, LCT_SegmentSplitInfo = 0x1e, LCT_FunctionStarts = 0x26, - LCT_DataInCode = 0x29 + LCT_DataInCode = 0x29, + LCT_LinkerOptions = 0x2D }; /// \brief Load command structure. @@ -236,6 +237,14 @@ namespace macho { uint32_t DataSize; }; + struct LinkerOptionsLoadCommand { + uint32_t Type; + uint32_t Size; + uint32_t Count; + // Load command is followed by Count number of zero-terminated UTF8 strings, + // and then zero-filled to be 4-byte aligned. + }; + /// @} /// @name Section Data /// @{ diff --git a/include/llvm/Object/MachOObject.h b/include/llvm/Object/MachOObject.h index e32a85d140..9e4ab199f5 100644 --- a/include/llvm/Object/MachOObject.h +++ b/include/llvm/Object/MachOObject.h @@ -153,6 +153,9 @@ public: void ReadLinkeditDataLoadCommand( const LoadCommandInfo &LCI, InMemoryStruct &Res) const; + void ReadLinkerOptionsLoadCommand( + const LoadCommandInfo &LCI, + InMemoryStruct &Res) const; void ReadIndirectSymbolTableEntry( const macho::DysymtabLoadCommand &DLC, unsigned Index, diff --git a/lib/MC/MCMachOStreamer.cpp b/lib/MC/MCMachOStreamer.cpp index f947dda83d..2c0c97af47 100644 --- a/lib/MC/MCMachOStreamer.cpp +++ b/lib/MC/MCMachOStreamer.cpp @@ -48,6 +48,7 @@ public: virtual void EmitEHSymAttributes(const MCSymbol *Symbol, MCSymbol *EHSymbol); virtual void EmitAssemblerFlag(MCAssemblerFlag Flag); + virtual void EmitLinkerOptions(ArrayRef Options); virtual void EmitDataRegion(MCDataRegionType Kind); virtual void EmitThumbFunc(MCSymbol *Func); virtual void EmitSymbolAttribute(MCSymbol *Symbol, MCSymbolAttr Attribute); @@ -178,6 +179,10 @@ void MCMachOStreamer::EmitAssemblerFlag(MCAssemblerFlag Flag) { } } +void MCMachOStreamer::EmitLinkerOptions(ArrayRef Options) { + getAssembler().getLinkerOptions().push_back(Options); +} + void MCMachOStreamer::EmitDataRegion(MCDataRegionType Kind) { switch (Kind) { case MCDR_DataRegion: diff --git a/lib/MC/MachObjectWriter.cpp b/lib/MC/MachObjectWriter.cpp index 0098bead45..d13c178a9c 100644 --- a/lib/MC/MachObjectWriter.cpp +++ b/lib/MC/MachObjectWriter.cpp @@ -376,6 +376,39 @@ void MachObjectWriter::WriteLinkeditLoadCommand(uint32_t Type, assert(OS.tell() - Start == macho::LinkeditLoadCommandSize); } +static unsigned ComputeLinkerOptionsLoadCommandSize( + const std::vector &Options) +{ + unsigned Size = sizeof(macho::LinkerOptionsLoadCommand); + for (unsigned i = 0, e = Options.size(); i != e; ++i) + Size += Options[i].size() + 1; + return RoundUpToAlignment(Size, 4); +} + +void MachObjectWriter::WriteLinkerOptionsLoadCommand( + const std::vector &Options) +{ + unsigned Size = ComputeLinkerOptionsLoadCommandSize(Options); + uint64_t Start = OS.tell(); + (void) Start; + + Write32(macho::LCT_LinkerOptions); + Write32(Size); + Write32(Options.size()); + uint64_t BytesWritten = 0; + for (unsigned i = 0, e = Options.size(); i != e; ++i) { + // Write each string, including the null byte. + const std::string &Option = Options[i]; + WriteBytes(Option.c_str(), Option.size() + 1); + BytesWritten += Option.size() + 1; + } + + // Pad to a multiple of 4. + WriteBytes("", OffsetToAlignment(BytesWritten, 4)); + + assert(OS.tell() - Start == Size); +} + void MachObjectWriter::RecordRelocation(const MCAssembler &Asm, const MCAsmLayout &Layout, @@ -693,6 +726,13 @@ void MachObjectWriter::WriteObject(MCAssembler &Asm, macho::SegmentLoadCommand64Size + NumSections * macho::Section64Size : macho::SegmentLoadCommand32Size + NumSections * macho::Section32Size; + // Add the data-in-code load command size, if used. + unsigned NumDataRegions = Asm.getDataRegions().size(); + if (NumDataRegions) { + ++NumLoadCommands; + LoadCommandsSize += macho::LinkeditLoadCommandSize; + } + // Add the symbol table load command sizes, if used. unsigned NumSymbols = LocalSymbolData.size() + ExternalSymbolData.size() + UndefinedSymbolData.size(); @@ -702,13 +742,14 @@ void MachObjectWriter::WriteObject(MCAssembler &Asm, macho::DysymtabLoadCommandSize); } - // Add the data-in-code load command size, if used. - unsigned NumDataRegions = Asm.getDataRegions().size(); - if (NumDataRegions) { + // Add the linker option load commands sizes. + const std::vector > &LinkerOptions = + Asm.getLinkerOptions(); + for (unsigned i = 0, e = LinkerOptions.size(); i != e; ++i) { ++NumLoadCommands; - LoadCommandsSize += macho::LinkeditLoadCommandSize; + LoadCommandsSize += ComputeLinkerOptionsLoadCommandSize(LinkerOptions[i]); } - + // Compute the total size of the section data, as well as its file size and vm // size. uint64_t SectionDataStart = (is64Bit() ? macho::Header64Size : @@ -799,6 +840,11 @@ void MachObjectWriter::WriteObject(MCAssembler &Asm, IndirectSymbolOffset, NumIndirectSymbols); } + // Write the linker options load commands. + for (unsigned i = 0, e = LinkerOptions.size(); i != e; ++i) { + WriteLinkerOptionsLoadCommand(LinkerOptions[i]); + } + // Write the actual section data. for (MCAssembler::const_iterator it = Asm.begin(), ie = Asm.end(); it != ie; ++it) { diff --git a/lib/Object/MachOObject.cpp b/lib/Object/MachOObject.cpp index 529bdf97e5..c9c341a207 100644 --- a/lib/Object/MachOObject.cpp +++ b/lib/Object/MachOObject.cpp @@ -258,6 +258,17 @@ void MachOObject::ReadLinkeditDataLoadCommand(const LoadCommandInfo &LCI, ReadInMemoryStruct(*this, Buffer->getBuffer(), LCI.Offset, Res); } +template<> +void SwapStruct(macho::LinkerOptionsLoadCommand &Value) { + SwapValue(Value.Type); + SwapValue(Value.Size); + SwapValue(Value.Count); +} +void MachOObject::ReadLinkerOptionsLoadCommand(const LoadCommandInfo &LCI, + InMemoryStruct &Res) const { + ReadInMemoryStruct(*this, Buffer->getBuffer(), LCI.Offset, Res); +} + template<> void SwapStruct(macho::IndirectSymbolTableEntry &Value) { SwapValue(Value.Index); diff --git a/test/MC/MachO/linker-option-2.s b/test/MC/MachO/linker-option-2.s new file mode 100644 index 0000000000..bb5966be27 --- /dev/null +++ b/test/MC/MachO/linker-option-2.s @@ -0,0 +1,25 @@ +// RUN: llvm-mc -n -triple x86_64-apple-darwin10 %s -filetype=obj | macho-dump | FileCheck %s + +// CHECK: ('load_commands_size', 104) +// CHECK: ('load_commands', [ +// CHECK: # Load Command 1 +// CHECK: (('command', 45) +// CHECK: ('size', 16) +// CHECK: ('count', 1) +// CHECK: ('_strings', [ +// CHECK: "a", +// CHECK: ]) +// CHECK: ), +// CHECK: # Load Command 2 +// CHECK: (('command', 45) +// CHECK: ('size', 16) +// CHECK: ('count', 2) +// CHECK: ('_strings', [ +// CHECK: "a", +// CHECK: "b", +// CHECK: ]) +// CHECK: ), +// CHECK: ]) + +.linker_option "a" +.linker_option "a", "b" diff --git a/tools/macho-dump/macho-dump.cpp b/tools/macho-dump/macho-dump.cpp index 20deda9a0c..3bd3ecc8fd 100644 --- a/tools/macho-dump/macho-dump.cpp +++ b/tools/macho-dump/macho-dump.cpp @@ -337,7 +337,7 @@ static int DumpDataInCodeDataCommand(MachOObject &Obj, InMemoryStruct LLC; Obj.ReadLinkeditDataLoadCommand(LCI, LLC); if (!LLC) - return Error("unable to read segment load command"); + return Error("unable to read data-in-code load command"); outs() << " ('dataoff', " << LLC->DataOffset << ")\n" << " ('datasize', " << LLC->DataSize << ")\n" @@ -361,6 +361,31 @@ static int DumpDataInCodeDataCommand(MachOObject &Obj, return 0; } +static int DumpLinkerOptionsCommand(MachOObject &Obj, + const MachOObject::LoadCommandInfo &LCI) { + InMemoryStruct LOLC; + Obj.ReadLinkerOptionsLoadCommand(LCI, LOLC); + if (!LOLC) + return Error("unable to read linker options load command"); + + outs() << " ('count', " << LOLC->Count << ")\n" + << " ('_strings', [\n"; + + uint64_t DataSize = LOLC->Size - sizeof(macho::LinkerOptionsLoadCommand); + StringRef Data = Obj.getData( + LCI.Offset + sizeof(macho::LinkerOptionsLoadCommand), DataSize); + for (unsigned i = 0; i != LOLC->Count; ++i) { + std::pair Split = Data.split('\0'); + outs() << "\t\""; + outs().write_escaped(Split.first); + outs() << "\",\n"; + Data = Split.second; + } + outs() <<" ])\n"; + + return 0; +} + static int DumpLoadCommand(MachOObject &Obj, unsigned Index) { const MachOObject::LoadCommandInfo &LCI = Obj.getLoadCommandInfo(Index); @@ -390,6 +415,9 @@ static int DumpLoadCommand(MachOObject &Obj, unsigned Index) { case macho::LCT_DataInCode: Res = DumpDataInCodeDataCommand(Obj, LCI); break; + case macho::LCT_LinkerOptions: + Res = DumpLinkerOptionsCommand(Obj, LCI); + break; default: Warning("unknown load command: " + Twine(LCI.Command.Type)); break; -- cgit v1.2.3