diff options
Diffstat (limited to 'llvm/tools')
-rw-r--r-- | llvm/tools/dsymutil/DwarfLinker.cpp | 10 | ||||
-rw-r--r-- | llvm/tools/dsymutil/MachOUtils.cpp | 423 | ||||
-rw-r--r-- | llvm/tools/dsymutil/MachOUtils.h | 6 |
3 files changed, 436 insertions, 3 deletions
diff --git a/llvm/tools/dsymutil/DwarfLinker.cpp b/llvm/tools/dsymutil/DwarfLinker.cpp index 0be12375b26..eece3a6d8be 100644 --- a/llvm/tools/dsymutil/DwarfLinker.cpp +++ b/llvm/tools/dsymutil/DwarfLinker.cpp @@ -10,6 +10,7 @@ #include "BinaryHolder.h" #include "DebugMap.h" #include "dsymutil.h" +#include "MachOUtils.h" #include "NonRelocatableStringpool.h" #include "llvm/ADT/IntervalMap.h" #include "llvm/ADT/StringMap.h" @@ -475,7 +476,7 @@ public: bool init(Triple TheTriple, StringRef OutputFilename); /// \brief Dump the file to the disk. - bool finish(); + bool finish(const DebugMap &); AsmPrinter &getAsmPrinter() const { return *Asm; } @@ -617,7 +618,10 @@ bool DwarfStreamer::init(Triple TheTriple, StringRef OutputFilename) { return true; } -bool DwarfStreamer::finish() { +bool DwarfStreamer::finish(const DebugMap &DM) { + if (DM.getTriple().isOSDarwin() && !DM.getBinaryPath().empty()) + return MachOUtils::generateDsymCompanion(DM, *MS, *OutFile); + MS->Finish(); return true; } @@ -3041,7 +3045,7 @@ bool DwarfLinker::link(const DebugMap &Map) { Streamer->emitStrings(StringPool); } - return Options.NoOutput ? true : Streamer->finish(); + return Options.NoOutput ? true : Streamer->finish(Map); } } diff --git a/llvm/tools/dsymutil/MachOUtils.cpp b/llvm/tools/dsymutil/MachOUtils.cpp index 8f4d2610b8f..e46eb7951bc 100644 --- a/llvm/tools/dsymutil/MachOUtils.cpp +++ b/llvm/tools/dsymutil/MachOUtils.cpp @@ -8,7 +8,16 @@ //===----------------------------------------------------------------------===// #include "MachOUtils.h" +#include "BinaryHolder.h" +#include "DebugMap.h" #include "dsymutil.h" +#include "NonRelocatableStringpool.h" +#include "llvm/MC/MCSectionMachO.h" +#include "llvm/MC/MCAsmLayout.h" +#include "llvm/MC/MCSectionMachO.h" +#include "llvm/MC/MCObjectStreamer.h" +#include "llvm/MC/MCStreamer.h" +#include "llvm/Object/MachO.h" #include "llvm/Support/FileUtilities.h" #include "llvm/Support/Program.h" #include "llvm/Support/raw_ostream.h" @@ -88,6 +97,420 @@ bool generateUniversalBinary(SmallVectorImpl<ArchAndFilename> &ArchFiles, return Options.NoOutput ? true : runLipo(Args); } + +// Return a MachO::segment_command_64 that holds the same values as +// the passed MachO::segment_command. We do that to avoid having to +// duplicat the logic for 32bits and 64bits segments. +struct MachO::segment_command_64 adaptFrom32bits(MachO::segment_command Seg) { + MachO::segment_command_64 Seg64; + Seg64.cmd = Seg.cmd; + Seg64.cmdsize = Seg.cmdsize; + memcpy(Seg64.segname, Seg.segname, sizeof(Seg.segname)); + Seg64.vmaddr = Seg.vmaddr; + Seg64.vmsize = Seg.vmsize; + Seg64.fileoff = Seg.fileoff; + Seg64.filesize = Seg.filesize; + Seg64.maxprot = Seg.maxprot; + Seg64.initprot = Seg.initprot; + Seg64.nsects = Seg.nsects; + Seg64.flags = Seg.flags; + return Seg64; +} + +// Iterate on all \a Obj segments, and apply \a Handler to them. +template <typename FunctionTy> +static void iterateOnSegments(const object::MachOObjectFile &Obj, + FunctionTy Handler) { + for (const auto &LCI : Obj.load_commands()) { + MachO::segment_command_64 Segment; + if (LCI.C.cmd == MachO::LC_SEGMENT) + Segment = adaptFrom32bits(Obj.getSegmentLoadCommand(LCI)); + else if (LCI.C.cmd == MachO::LC_SEGMENT_64) + Segment = Obj.getSegment64LoadCommand(LCI); + else + continue; + + Handler(Segment); + } +} + +// Transfer the symbols described by \a NList to \a NewSymtab which is +// just the raw contents of the symbol table for the dSYM companion file. +// \returns whether the symbol was tranfered or not. +template <typename NListTy> +static bool transferSymbol(NListTy NList, bool IsLittleEndian, + StringRef Strings, SmallVectorImpl<char> &NewSymtab, + NonRelocatableStringpool &NewStrings, + bool &InDebugNote) { + // Do not transfer undefined symbols, we want real addresses. + if ((NList.n_type & MachO::N_TYPE) == MachO::N_UNDF) + return false; + + StringRef Name = StringRef(Strings.begin() + NList.n_strx); + if (InDebugNote) { + InDebugNote = + (NList.n_type != MachO::N_SO) || (!Name.empty() && Name[0] != '\0'); + return false; + } else if (NList.n_type == MachO::N_SO) { + InDebugNote = true; + return false; + } + + // FIXME: The + 1 is here to mimic dsymutil-classic that has 2 empty + // strings at the start of the generated string table (There is + // corresponding code in the string table emission). + NList.n_strx = NewStrings.getStringOffset(Name) + 1; + if (IsLittleEndian != sys::IsLittleEndianHost) + MachO::swapStruct(NList); + + NewSymtab.append((char *)&NList, (char *)(&NList + 1)); + return true; +} + +// Wrapper around transferSymbol to transfer all of \a Obj symbols +// to \a NewSymtab. This function does not write in the output file. +// \returns the number of symbols in \a NewSymtab. +static unsigned transferSymbols(const object::MachOObjectFile &Obj, + SmallVectorImpl<char> &NewSymtab, + NonRelocatableStringpool &NewStrings) { + unsigned Syms = 0; + StringRef Strings = Obj.getStringTableData(); + bool IsLittleEndian = Obj.isLittleEndian(); + bool InDebugNote = false; + + if (Obj.is64Bit()) { + for (const object::SymbolRef &Symbol : Obj.symbols()) { + object::DataRefImpl DRI = Symbol.getRawDataRefImpl(); + if (transferSymbol(Obj.getSymbol64TableEntry(DRI), IsLittleEndian, + Strings, NewSymtab, NewStrings, InDebugNote)) + ++Syms; + } + } else { + for (const object::SymbolRef &Symbol : Obj.symbols()) { + object::DataRefImpl DRI = Symbol.getRawDataRefImpl(); + if (transferSymbol(Obj.getSymbolTableEntry(DRI), IsLittleEndian, Strings, + NewSymtab, NewStrings, InDebugNote)) + ++Syms; + } + } + return Syms; +} + +static MachO::section +getSection(const object::MachOObjectFile &Obj, + const MachO::segment_command &Seg, + const object::MachOObjectFile::LoadCommandInfo &LCI, unsigned Idx) { + return Obj.getSection(LCI, Idx); +} + +static MachO::section_64 +getSection(const object::MachOObjectFile &Obj, + const MachO::segment_command_64 &Seg, + const object::MachOObjectFile::LoadCommandInfo &LCI, unsigned Idx) { + return Obj.getSection64(LCI, Idx); +} + +// Transfer \a Segment from \a Obj to the output file. This calls into \a Writer +// to write these load commands directly in the output file at the current +// position. +// The function also tries to find a hole in the address map to fit the __DWARF +// segment of \a DwarfSegmentSize size. \a EndAddress is updated to point at the +// highest segment address. +// When the __LINKEDIT segment is transfered, its offset and size are set resp. +// to \a LinkeditOffset and \a LinkeditSize. +template <typename SegmentTy> +static void transferSegmentAndSections( + const object::MachOObjectFile::LoadCommandInfo &LCI, SegmentTy Segment, + const object::MachOObjectFile &Obj, MCObjectWriter &Writer, + uint64_t LinkeditOffset, uint64_t LinkeditSize, uint64_t DwarfSegmentSize, + uint64_t &GapForDwarf, uint64_t &EndAddress) { + if (StringRef("__DWARF") == Segment.segname) + return; + + Segment.fileoff = Segment.filesize = 0; + + if (StringRef("__LINKEDIT") == Segment.segname) { + Segment.fileoff = LinkeditOffset; + Segment.filesize = LinkeditSize; + } + + // Check if the end address of the last segment and our current + // start address leave a sufficient gap to store the __DWARF + // segment. + uint64_t PrevEndAddress = EndAddress; + EndAddress = RoundUpToAlignment(EndAddress, 0x1000); + if (GapForDwarf == UINT64_MAX && Segment.vmaddr > EndAddress && + Segment.vmaddr - EndAddress >= DwarfSegmentSize) + GapForDwarf = EndAddress; + + // The segments are not necessarily sorted by their vmaddr. + EndAddress = + std::max<uint64_t>(PrevEndAddress, Segment.vmaddr + Segment.vmsize); + if (Obj.isLittleEndian() != sys::IsLittleEndianHost) + MachO::swapStruct(Segment); + Writer.writeBytes( + StringRef(reinterpret_cast<char *>(&Segment), sizeof(Segment))); + for (unsigned i = 0; i < Segment.nsects; ++i) { + auto Sect = getSection(Obj, Segment, LCI, i); + Sect.offset = Sect.reloff = Sect.nreloc = 0; + if (Obj.isLittleEndian() != sys::IsLittleEndianHost) + MachO::swapStruct(Sect); + Writer.writeBytes(StringRef(reinterpret_cast<char *>(&Sect), sizeof(Sect))); + } +} + +// Write the __DWARF segment load command to the output file. +static void createDwarfSegment(uint64_t VMAddr, uint64_t FileOffset, + uint64_t FileSize, unsigned NumSections, + MCAsmLayout &Layout, MachObjectWriter &Writer) { + Writer.writeSegmentLoadCommand("__DWARF", NumSections, VMAddr, + RoundUpToAlignment(FileSize, 0x1000), + FileOffset, FileSize, /* MaxProt */ 7, + /* InitProt =*/3); + + for (unsigned int i = 0, n = Layout.getSectionOrder().size(); i != n; ++i) { + MCSection *Sec = Layout.getSectionOrder()[i]; + if (Sec->begin() == Sec->end() || !Layout.getSectionFileSize(Sec)) + continue; + + unsigned Align = Sec->getAlignment(); + if (Align > 1) { + VMAddr = RoundUpToAlignment(VMAddr, Align); + FileOffset = RoundUpToAlignment(FileOffset, Align); + } + Writer.writeSection(Layout, *Sec, VMAddr, FileOffset, 0, 0, 0); + + FileOffset += Layout.getSectionAddressSize(Sec); + VMAddr += Layout.getSectionAddressSize(Sec); + } +} + +static bool isExecutable(const object::MachOObjectFile &Obj) { + if (Obj.is64Bit()) + return Obj.getHeader64().filetype != MachO::MH_OBJECT; + else + return Obj.getHeader().filetype != MachO::MH_OBJECT; +} + +static bool hasLinkEditSegment(const object::MachOObjectFile &Obj) { + bool HasLinkEditSegment = false; + iterateOnSegments(Obj, [&](const MachO::segment_command_64 &Segment) { + if (StringRef("__LINKEDIT") == Segment.segname) + HasLinkEditSegment = true; + }); + return HasLinkEditSegment; +} + +static unsigned segmentLoadCommandSize(bool Is64Bit, unsigned NumSections) { + if (Is64Bit) + return sizeof(MachO::segment_command_64) + + NumSections * sizeof(MachO::section_64); + + return sizeof(MachO::segment_command) + NumSections * sizeof(MachO::section); +} + +// Stream a dSYM companion binary file corresponding to the binary referenced +// by \a DM to \a OutFile. The passed \a MS MCStreamer is setup to write to +// \a OutFile and it must be using a MachObjectWriter object to do so. +bool generateDsymCompanion(const DebugMap &DM, MCStreamer &MS, + raw_fd_ostream &OutFile) { + auto &ObjectStreamer = static_cast<MCObjectStreamer &>(MS); + MCAssembler &MCAsm = ObjectStreamer.getAssembler(); + auto &Writer = static_cast<MachObjectWriter &>(MCAsm.getWriter()); + MCAsmLayout Layout(MCAsm); + + MCAsm.layout(Layout); + + BinaryHolder InputBinaryHolder(false); + auto ErrOrObjs = InputBinaryHolder.GetObjectFiles(DM.getBinaryPath()); + if (auto Error = ErrOrObjs.getError()) + return error(Twine("opening ") + DM.getBinaryPath() + ": " + + Error.message(), + "output file streaming"); + + auto ErrOrInputBinary = + InputBinaryHolder.GetAs<object::MachOObjectFile>(DM.getTriple()); + if (auto Error = ErrOrInputBinary.getError()) + return error(Twine("opening ") + DM.getBinaryPath() + ": " + + Error.message(), + "output file streaming"); + auto &InputBinary = *ErrOrInputBinary; + + bool Is64Bit = Writer.is64Bit(); + MachO::symtab_command SymtabCmd = InputBinary.getSymtabLoadCommand(); + + // Get UUID. + MachO::uuid_command UUIDCmd; + memset(&UUIDCmd, 0, sizeof(UUIDCmd)); + UUIDCmd.cmd = MachO::LC_UUID; + UUIDCmd.cmdsize = sizeof(MachO::uuid_command); + for (auto &LCI : InputBinary.load_commands()) { + if (LCI.C.cmd == MachO::LC_UUID) { + UUIDCmd = InputBinary.getUuidCommand(LCI); + break; + } + } + + // Compute the number of load commands we will need. + unsigned LoadCommandSize = 0; + unsigned NumLoadCommands = 0; + // We will copy the UUID if there is one. + if (UUIDCmd.cmd != 0) { + ++NumLoadCommands; + LoadCommandSize += sizeof(MachO::uuid_command); + } + + // If we have a valid symtab to copy, do it. + bool ShouldEmitSymtab = + isExecutable(InputBinary) && hasLinkEditSegment(InputBinary); + if (ShouldEmitSymtab) { + LoadCommandSize += sizeof(MachO::symtab_command); + ++NumLoadCommands; + } + + unsigned HeaderSize = + Is64Bit ? sizeof(MachO::mach_header_64) : sizeof(MachO::mach_header); + // We will copy every segment that isn't __DWARF. + iterateOnSegments(InputBinary, [&](const MachO::segment_command_64 &Segment) { + if (StringRef("__DWARF") == Segment.segname) + return; + + ++NumLoadCommands; + LoadCommandSize += segmentLoadCommandSize(Is64Bit, Segment.nsects); + }); + + // We will add our own brand new __DWARF segment if we have debug + // info. + unsigned NumDwarfSections = 0; + uint64_t DwarfSegmentSize = 0; + + for (unsigned int i = 0, n = Layout.getSectionOrder().size(); i != n; ++i) { + MCSection *Sec = Layout.getSectionOrder()[i]; + if (Sec->begin() == Sec->end()) + continue; + + if (uint64_t Size = Layout.getSectionFileSize(Sec)) { + DwarfSegmentSize = + RoundUpToAlignment(DwarfSegmentSize, Sec->getAlignment()); + DwarfSegmentSize += Size; + ++NumDwarfSections; + } + } + + if (NumDwarfSections) { + ++NumLoadCommands; + LoadCommandSize += segmentLoadCommandSize(Is64Bit, NumDwarfSections); + } + + SmallString<0> NewSymtab; + NonRelocatableStringpool NewStrings; + unsigned NListSize = Is64Bit ? sizeof(MachO::nlist_64) : sizeof(MachO::nlist); + unsigned NumSyms = 0; + uint64_t NewStringsSize = 0; + if (ShouldEmitSymtab) { + NewSymtab.reserve(SymtabCmd.nsyms * NListSize / 2); + NumSyms = transferSymbols(InputBinary, NewSymtab, NewStrings); + NewStringsSize = NewStrings.getSize() + 1; + } + + uint64_t SymtabStart = LoadCommandSize; + SymtabStart += HeaderSize; + SymtabStart = RoundUpToAlignment(SymtabStart, 0x1000); + + // We gathered all the information we need, start emitting the output file. + Writer.writeHeader(MachO::MH_DSYM, NumLoadCommands, LoadCommandSize, false); + + // Write the load commands. + assert(OutFile.tell() == HeaderSize); + if (UUIDCmd.cmd != 0) { + Writer.write32(UUIDCmd.cmd); + Writer.write32(UUIDCmd.cmdsize); + Writer.writeBytes(StringRef((const char *)UUIDCmd.uuid, 16)); + assert(OutFile.tell() == HeaderSize + sizeof(UUIDCmd)); + } + + assert(SymtabCmd.cmd && "No symbol table."); + uint64_t StringStart = SymtabStart + NumSyms * NListSize; + if (ShouldEmitSymtab) + Writer.writeSymtabLoadCommand(SymtabStart, NumSyms, StringStart, + NewStringsSize); + + uint64_t DwarfSegmentStart = StringStart + NewStringsSize; + DwarfSegmentStart = RoundUpToAlignment(DwarfSegmentStart, 0x1000); + + // Write the load commands for the segments and sections we 'import' from + // the original binary. + uint64_t EndAddress = 0; + uint64_t GapForDwarf = UINT64_MAX; + for (auto &LCI : InputBinary.load_commands()) { + if (LCI.C.cmd == MachO::LC_SEGMENT) + transferSegmentAndSections(LCI, InputBinary.getSegmentLoadCommand(LCI), + InputBinary, Writer, SymtabStart, + StringStart + NewStringsSize - SymtabStart, + DwarfSegmentSize, GapForDwarf, EndAddress); + else if (LCI.C.cmd == MachO::LC_SEGMENT_64) + transferSegmentAndSections(LCI, InputBinary.getSegment64LoadCommand(LCI), + InputBinary, Writer, SymtabStart, + StringStart + NewStringsSize - SymtabStart, + DwarfSegmentSize, GapForDwarf, EndAddress); + } + + uint64_t DwarfVMAddr = RoundUpToAlignment(EndAddress, 0x1000); + uint64_t DwarfVMMax = Is64Bit ? UINT64_MAX : UINT32_MAX; + if (DwarfVMAddr + DwarfSegmentSize > DwarfVMMax || + DwarfVMAddr + DwarfSegmentSize < DwarfVMAddr /* Overflow */) { + // There is no room for the __DWARF segment at the end of the + // address space. Look trhough segments to find a gap. + DwarfVMAddr = GapForDwarf; + if (DwarfVMAddr == UINT64_MAX) + warn("not enough VM space for the __DWARF segment.", + "output file streaming"); + } + + // Write the load command for the __DWARF segment. + createDwarfSegment(DwarfVMAddr, DwarfSegmentStart, DwarfSegmentSize, + NumDwarfSections, Layout, Writer); + + assert(OutFile.tell() == LoadCommandSize + HeaderSize); + Writer.WriteZeros(SymtabStart - (LoadCommandSize + HeaderSize)); + assert(OutFile.tell() == SymtabStart); + + // Transfer symbols. + if (ShouldEmitSymtab) { + Writer.writeBytes(NewSymtab.str()); + assert(OutFile.tell() == StringStart); + + // Transfer string table. + // FIXME: The NonRelocatableStringpool starts with an empty string, but + // dsymutil-classic starts the reconstructed string table with 2 of these. + // Reproduce that behavior for now (there is corresponding code in + // transferSymbol). + Writer.WriteZeros(1); + typedef NonRelocatableStringpool::MapTy MapTy; + for (auto *Entry = NewStrings.getFirstEntry(); Entry; + Entry = static_cast<MapTy::MapEntryTy *>(Entry->getValue().second)) + Writer.writeBytes( + StringRef(Entry->getKey().data(), Entry->getKey().size() + 1)); + } + + assert(OutFile.tell() == StringStart + NewStringsSize); + + // Pad till the Dwarf segment start. + Writer.WriteZeros(DwarfSegmentStart - (StringStart + NewStringsSize)); + assert(OutFile.tell() == DwarfSegmentStart); + + // Emit the Dwarf sections contents. + for (const MCSection &Sec : MCAsm) { + if (Sec.begin() == Sec.end()) + continue; + + uint64_t Pos = OutFile.tell(); + Writer.WriteZeros(RoundUpToAlignment(Pos, Sec.getAlignment()) - Pos); + MCAsm.writeSectionData(&Sec, Layout); + } + + return true; +} } } } diff --git a/llvm/tools/dsymutil/MachOUtils.h b/llvm/tools/dsymutil/MachOUtils.h index d6b6f3d61c3..9a63645ab4a 100644 --- a/llvm/tools/dsymutil/MachOUtils.h +++ b/llvm/tools/dsymutil/MachOUtils.h @@ -13,7 +13,10 @@ #include "llvm/ADT/StringRef.h" namespace llvm { +class MCStreamer; +class raw_fd_ostream; namespace dsymutil { +class DebugMap; struct LinkOptions; namespace MachOUtils { @@ -25,6 +28,9 @@ struct ArchAndFilename { bool generateUniversalBinary(SmallVectorImpl<ArchAndFilename> &ArchFiles, StringRef OutputFileName, const LinkOptions &); +bool generateDsymCompanion(const DebugMap &DM, MCStreamer &MS, + raw_fd_ostream &OutFile); + std::string getArchName(StringRef Arch); } } |