106f32e7eSjoerg //===-- MachOUtils.cpp - Mach-o specific helpers for dsymutil ------------===//
206f32e7eSjoerg //
306f32e7eSjoerg // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
406f32e7eSjoerg // See https://llvm.org/LICENSE.txt for license information.
506f32e7eSjoerg // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
606f32e7eSjoerg //
706f32e7eSjoerg //===----------------------------------------------------------------------===//
806f32e7eSjoerg
906f32e7eSjoerg #include "MachOUtils.h"
1006f32e7eSjoerg #include "BinaryHolder.h"
1106f32e7eSjoerg #include "DebugMap.h"
1206f32e7eSjoerg #include "LinkUtils.h"
13*da58b97aSjoerg #include "llvm/CodeGen/NonRelocatableStringpool.h"
1406f32e7eSjoerg #include "llvm/MC/MCAsmLayout.h"
1506f32e7eSjoerg #include "llvm/MC/MCMachObjectWriter.h"
1606f32e7eSjoerg #include "llvm/MC/MCObjectStreamer.h"
1706f32e7eSjoerg #include "llvm/MC/MCSectionMachO.h"
1806f32e7eSjoerg #include "llvm/MC/MCStreamer.h"
1906f32e7eSjoerg #include "llvm/Object/MachO.h"
2006f32e7eSjoerg #include "llvm/Support/FileUtilities.h"
2106f32e7eSjoerg #include "llvm/Support/Program.h"
2206f32e7eSjoerg #include "llvm/Support/WithColor.h"
2306f32e7eSjoerg #include "llvm/Support/raw_ostream.h"
2406f32e7eSjoerg
2506f32e7eSjoerg namespace llvm {
2606f32e7eSjoerg namespace dsymutil {
2706f32e7eSjoerg namespace MachOUtils {
2806f32e7eSjoerg
createTempFile()2906f32e7eSjoerg llvm::Error ArchAndFile::createTempFile() {
3006f32e7eSjoerg llvm::SmallString<128> TmpModel;
3106f32e7eSjoerg llvm::sys::path::system_temp_directory(true, TmpModel);
3206f32e7eSjoerg llvm::sys::path::append(TmpModel, "dsym.tmp%%%%%.dwarf");
3306f32e7eSjoerg Expected<sys::fs::TempFile> T = sys::fs::TempFile::create(TmpModel);
3406f32e7eSjoerg
3506f32e7eSjoerg if (!T)
3606f32e7eSjoerg return T.takeError();
3706f32e7eSjoerg
3806f32e7eSjoerg File = std::make_unique<sys::fs::TempFile>(std::move(*T));
3906f32e7eSjoerg return Error::success();
4006f32e7eSjoerg }
4106f32e7eSjoerg
path() const4206f32e7eSjoerg llvm::StringRef ArchAndFile::path() const { return File->TmpName; }
4306f32e7eSjoerg
~ArchAndFile()4406f32e7eSjoerg ArchAndFile::~ArchAndFile() {
4506f32e7eSjoerg if (File)
4606f32e7eSjoerg if (auto E = File->discard())
4706f32e7eSjoerg llvm::consumeError(std::move(E));
4806f32e7eSjoerg }
4906f32e7eSjoerg
getArchName(StringRef Arch)5006f32e7eSjoerg std::string getArchName(StringRef Arch) {
5106f32e7eSjoerg if (Arch.startswith("thumb"))
5206f32e7eSjoerg return (llvm::Twine("arm") + Arch.drop_front(5)).str();
53*da58b97aSjoerg return std::string(Arch);
5406f32e7eSjoerg }
5506f32e7eSjoerg
runLipo(StringRef SDKPath,SmallVectorImpl<StringRef> & Args)5606f32e7eSjoerg static bool runLipo(StringRef SDKPath, SmallVectorImpl<StringRef> &Args) {
5706f32e7eSjoerg auto Path = sys::findProgramByName("lipo", makeArrayRef(SDKPath));
5806f32e7eSjoerg if (!Path)
5906f32e7eSjoerg Path = sys::findProgramByName("lipo");
6006f32e7eSjoerg
6106f32e7eSjoerg if (!Path) {
6206f32e7eSjoerg WithColor::error() << "lipo: " << Path.getError().message() << "\n";
6306f32e7eSjoerg return false;
6406f32e7eSjoerg }
6506f32e7eSjoerg
6606f32e7eSjoerg std::string ErrMsg;
6706f32e7eSjoerg int result = sys::ExecuteAndWait(*Path, Args, None, {}, 0, 0, &ErrMsg);
6806f32e7eSjoerg if (result) {
6906f32e7eSjoerg WithColor::error() << "lipo: " << ErrMsg << "\n";
7006f32e7eSjoerg return false;
7106f32e7eSjoerg }
7206f32e7eSjoerg
7306f32e7eSjoerg return true;
7406f32e7eSjoerg }
7506f32e7eSjoerg
generateUniversalBinary(SmallVectorImpl<ArchAndFile> & ArchFiles,StringRef OutputFileName,const LinkOptions & Options,StringRef SDKPath)7606f32e7eSjoerg bool generateUniversalBinary(SmallVectorImpl<ArchAndFile> &ArchFiles,
7706f32e7eSjoerg StringRef OutputFileName,
7806f32e7eSjoerg const LinkOptions &Options, StringRef SDKPath) {
7906f32e7eSjoerg // No need to merge one file into a universal fat binary.
8006f32e7eSjoerg if (ArchFiles.size() == 1) {
8106f32e7eSjoerg if (auto E = ArchFiles.front().File->keep(OutputFileName)) {
8206f32e7eSjoerg WithColor::error() << "while keeping " << ArchFiles.front().path()
8306f32e7eSjoerg << " as " << OutputFileName << ": "
8406f32e7eSjoerg << toString(std::move(E)) << "\n";
8506f32e7eSjoerg return false;
8606f32e7eSjoerg }
8706f32e7eSjoerg return true;
8806f32e7eSjoerg }
8906f32e7eSjoerg
9006f32e7eSjoerg SmallVector<StringRef, 8> Args;
9106f32e7eSjoerg Args.push_back("lipo");
9206f32e7eSjoerg Args.push_back("-create");
9306f32e7eSjoerg
9406f32e7eSjoerg for (auto &Thin : ArchFiles)
9506f32e7eSjoerg Args.push_back(Thin.path());
9606f32e7eSjoerg
9706f32e7eSjoerg // Align segments to match dsymutil-classic alignment
9806f32e7eSjoerg for (auto &Thin : ArchFiles) {
9906f32e7eSjoerg Thin.Arch = getArchName(Thin.Arch);
10006f32e7eSjoerg Args.push_back("-segalign");
10106f32e7eSjoerg Args.push_back(Thin.Arch);
10206f32e7eSjoerg Args.push_back("20");
10306f32e7eSjoerg }
10406f32e7eSjoerg
10506f32e7eSjoerg Args.push_back("-output");
10606f32e7eSjoerg Args.push_back(OutputFileName.data());
10706f32e7eSjoerg
10806f32e7eSjoerg if (Options.Verbose) {
10906f32e7eSjoerg outs() << "Running lipo\n";
11006f32e7eSjoerg for (auto Arg : Args)
11106f32e7eSjoerg outs() << ' ' << Arg;
11206f32e7eSjoerg outs() << "\n";
11306f32e7eSjoerg }
11406f32e7eSjoerg
11506f32e7eSjoerg return Options.NoOutput ? true : runLipo(SDKPath, Args);
11606f32e7eSjoerg }
11706f32e7eSjoerg
11806f32e7eSjoerg // Return a MachO::segment_command_64 that holds the same values as the passed
11906f32e7eSjoerg // MachO::segment_command. We do that to avoid having to duplicate the logic
12006f32e7eSjoerg // for 32bits and 64bits segments.
adaptFrom32bits(MachO::segment_command Seg)12106f32e7eSjoerg struct MachO::segment_command_64 adaptFrom32bits(MachO::segment_command Seg) {
12206f32e7eSjoerg MachO::segment_command_64 Seg64;
12306f32e7eSjoerg Seg64.cmd = Seg.cmd;
12406f32e7eSjoerg Seg64.cmdsize = Seg.cmdsize;
12506f32e7eSjoerg memcpy(Seg64.segname, Seg.segname, sizeof(Seg.segname));
12606f32e7eSjoerg Seg64.vmaddr = Seg.vmaddr;
12706f32e7eSjoerg Seg64.vmsize = Seg.vmsize;
12806f32e7eSjoerg Seg64.fileoff = Seg.fileoff;
12906f32e7eSjoerg Seg64.filesize = Seg.filesize;
13006f32e7eSjoerg Seg64.maxprot = Seg.maxprot;
13106f32e7eSjoerg Seg64.initprot = Seg.initprot;
13206f32e7eSjoerg Seg64.nsects = Seg.nsects;
13306f32e7eSjoerg Seg64.flags = Seg.flags;
13406f32e7eSjoerg return Seg64;
13506f32e7eSjoerg }
13606f32e7eSjoerg
13706f32e7eSjoerg // Iterate on all \a Obj segments, and apply \a Handler to them.
13806f32e7eSjoerg template <typename FunctionTy>
iterateOnSegments(const object::MachOObjectFile & Obj,FunctionTy Handler)13906f32e7eSjoerg static void iterateOnSegments(const object::MachOObjectFile &Obj,
14006f32e7eSjoerg FunctionTy Handler) {
14106f32e7eSjoerg for (const auto &LCI : Obj.load_commands()) {
14206f32e7eSjoerg MachO::segment_command_64 Segment;
14306f32e7eSjoerg if (LCI.C.cmd == MachO::LC_SEGMENT)
14406f32e7eSjoerg Segment = adaptFrom32bits(Obj.getSegmentLoadCommand(LCI));
14506f32e7eSjoerg else if (LCI.C.cmd == MachO::LC_SEGMENT_64)
14606f32e7eSjoerg Segment = Obj.getSegment64LoadCommand(LCI);
14706f32e7eSjoerg else
14806f32e7eSjoerg continue;
14906f32e7eSjoerg
15006f32e7eSjoerg Handler(Segment);
15106f32e7eSjoerg }
15206f32e7eSjoerg }
15306f32e7eSjoerg
15406f32e7eSjoerg // Transfer the symbols described by \a NList to \a NewSymtab which is just the
15506f32e7eSjoerg // raw contents of the symbol table for the dSYM companion file. \returns
15606f32e7eSjoerg // whether the symbol was transferred or not.
15706f32e7eSjoerg template <typename NListTy>
transferSymbol(NListTy NList,bool IsLittleEndian,StringRef Strings,SmallVectorImpl<char> & NewSymtab,NonRelocatableStringpool & NewStrings,bool & InDebugNote)15806f32e7eSjoerg static bool transferSymbol(NListTy NList, bool IsLittleEndian,
15906f32e7eSjoerg StringRef Strings, SmallVectorImpl<char> &NewSymtab,
16006f32e7eSjoerg NonRelocatableStringpool &NewStrings,
16106f32e7eSjoerg bool &InDebugNote) {
16206f32e7eSjoerg // Do not transfer undefined symbols, we want real addresses.
16306f32e7eSjoerg if ((NList.n_type & MachO::N_TYPE) == MachO::N_UNDF)
16406f32e7eSjoerg return false;
16506f32e7eSjoerg
166*da58b97aSjoerg // Do not transfer N_AST symbols as their content is copied into a section of
167*da58b97aSjoerg // the Mach-O companion file.
168*da58b97aSjoerg if (NList.n_type == MachO::N_AST)
169*da58b97aSjoerg return false;
170*da58b97aSjoerg
17106f32e7eSjoerg StringRef Name = StringRef(Strings.begin() + NList.n_strx);
172*da58b97aSjoerg
173*da58b97aSjoerg // An N_SO with a filename opens a debugging scope and another one without a
174*da58b97aSjoerg // name closes it. Don't transfer anything in the debugging scope.
17506f32e7eSjoerg if (InDebugNote) {
17606f32e7eSjoerg InDebugNote =
17706f32e7eSjoerg (NList.n_type != MachO::N_SO) || (!Name.empty() && Name[0] != '\0');
17806f32e7eSjoerg return false;
17906f32e7eSjoerg } else if (NList.n_type == MachO::N_SO) {
18006f32e7eSjoerg InDebugNote = true;
18106f32e7eSjoerg return false;
18206f32e7eSjoerg }
18306f32e7eSjoerg
18406f32e7eSjoerg // FIXME: The + 1 is here to mimic dsymutil-classic that has 2 empty
18506f32e7eSjoerg // strings at the start of the generated string table (There is
18606f32e7eSjoerg // corresponding code in the string table emission).
18706f32e7eSjoerg NList.n_strx = NewStrings.getStringOffset(Name) + 1;
18806f32e7eSjoerg if (IsLittleEndian != sys::IsLittleEndianHost)
18906f32e7eSjoerg MachO::swapStruct(NList);
19006f32e7eSjoerg
19106f32e7eSjoerg NewSymtab.append(reinterpret_cast<char *>(&NList),
19206f32e7eSjoerg reinterpret_cast<char *>(&NList + 1));
19306f32e7eSjoerg return true;
19406f32e7eSjoerg }
19506f32e7eSjoerg
19606f32e7eSjoerg // Wrapper around transferSymbol to transfer all of \a Obj symbols
19706f32e7eSjoerg // to \a NewSymtab. This function does not write in the output file.
19806f32e7eSjoerg // \returns the number of symbols in \a NewSymtab.
transferSymbols(const object::MachOObjectFile & Obj,SmallVectorImpl<char> & NewSymtab,NonRelocatableStringpool & NewStrings)19906f32e7eSjoerg static unsigned transferSymbols(const object::MachOObjectFile &Obj,
20006f32e7eSjoerg SmallVectorImpl<char> &NewSymtab,
20106f32e7eSjoerg NonRelocatableStringpool &NewStrings) {
20206f32e7eSjoerg unsigned Syms = 0;
20306f32e7eSjoerg StringRef Strings = Obj.getStringTableData();
20406f32e7eSjoerg bool IsLittleEndian = Obj.isLittleEndian();
20506f32e7eSjoerg bool InDebugNote = false;
20606f32e7eSjoerg
20706f32e7eSjoerg if (Obj.is64Bit()) {
20806f32e7eSjoerg for (const object::SymbolRef &Symbol : Obj.symbols()) {
20906f32e7eSjoerg object::DataRefImpl DRI = Symbol.getRawDataRefImpl();
21006f32e7eSjoerg if (transferSymbol(Obj.getSymbol64TableEntry(DRI), IsLittleEndian,
21106f32e7eSjoerg Strings, NewSymtab, NewStrings, InDebugNote))
21206f32e7eSjoerg ++Syms;
21306f32e7eSjoerg }
21406f32e7eSjoerg } else {
21506f32e7eSjoerg for (const object::SymbolRef &Symbol : Obj.symbols()) {
21606f32e7eSjoerg object::DataRefImpl DRI = Symbol.getRawDataRefImpl();
21706f32e7eSjoerg if (transferSymbol(Obj.getSymbolTableEntry(DRI), IsLittleEndian, Strings,
21806f32e7eSjoerg NewSymtab, NewStrings, InDebugNote))
21906f32e7eSjoerg ++Syms;
22006f32e7eSjoerg }
22106f32e7eSjoerg }
22206f32e7eSjoerg return Syms;
22306f32e7eSjoerg }
22406f32e7eSjoerg
22506f32e7eSjoerg static MachO::section
getSection(const object::MachOObjectFile & Obj,const MachO::segment_command & Seg,const object::MachOObjectFile::LoadCommandInfo & LCI,unsigned Idx)22606f32e7eSjoerg getSection(const object::MachOObjectFile &Obj,
22706f32e7eSjoerg const MachO::segment_command &Seg,
22806f32e7eSjoerg const object::MachOObjectFile::LoadCommandInfo &LCI, unsigned Idx) {
22906f32e7eSjoerg return Obj.getSection(LCI, Idx);
23006f32e7eSjoerg }
23106f32e7eSjoerg
23206f32e7eSjoerg static MachO::section_64
getSection(const object::MachOObjectFile & Obj,const MachO::segment_command_64 & Seg,const object::MachOObjectFile::LoadCommandInfo & LCI,unsigned Idx)23306f32e7eSjoerg getSection(const object::MachOObjectFile &Obj,
23406f32e7eSjoerg const MachO::segment_command_64 &Seg,
23506f32e7eSjoerg const object::MachOObjectFile::LoadCommandInfo &LCI, unsigned Idx) {
23606f32e7eSjoerg return Obj.getSection64(LCI, Idx);
23706f32e7eSjoerg }
23806f32e7eSjoerg
23906f32e7eSjoerg // Transfer \a Segment from \a Obj to the output file. This calls into \a Writer
24006f32e7eSjoerg // to write these load commands directly in the output file at the current
24106f32e7eSjoerg // position.
242*da58b97aSjoerg //
24306f32e7eSjoerg // The function also tries to find a hole in the address map to fit the __DWARF
24406f32e7eSjoerg // segment of \a DwarfSegmentSize size. \a EndAddress is updated to point at the
24506f32e7eSjoerg // highest segment address.
246*da58b97aSjoerg //
24706f32e7eSjoerg // When the __LINKEDIT segment is transferred, its offset and size are set resp.
24806f32e7eSjoerg // to \a LinkeditOffset and \a LinkeditSize.
249*da58b97aSjoerg //
250*da58b97aSjoerg // When the eh_frame section is transferred, its offset and size are set resp.
251*da58b97aSjoerg // to \a EHFrameOffset and \a EHFrameSize.
25206f32e7eSjoerg template <typename SegmentTy>
transferSegmentAndSections(const object::MachOObjectFile::LoadCommandInfo & LCI,SegmentTy Segment,const object::MachOObjectFile & Obj,MachObjectWriter & Writer,uint64_t LinkeditOffset,uint64_t LinkeditSize,uint64_t EHFrameOffset,uint64_t EHFrameSize,uint64_t DwarfSegmentSize,uint64_t & GapForDwarf,uint64_t & EndAddress)25306f32e7eSjoerg static void transferSegmentAndSections(
25406f32e7eSjoerg const object::MachOObjectFile::LoadCommandInfo &LCI, SegmentTy Segment,
25506f32e7eSjoerg const object::MachOObjectFile &Obj, MachObjectWriter &Writer,
256*da58b97aSjoerg uint64_t LinkeditOffset, uint64_t LinkeditSize, uint64_t EHFrameOffset,
257*da58b97aSjoerg uint64_t EHFrameSize, uint64_t DwarfSegmentSize, uint64_t &GapForDwarf,
258*da58b97aSjoerg uint64_t &EndAddress) {
25906f32e7eSjoerg if (StringRef("__DWARF") == Segment.segname)
26006f32e7eSjoerg return;
26106f32e7eSjoerg
262*da58b97aSjoerg if (StringRef("__TEXT") == Segment.segname && EHFrameSize > 0) {
263*da58b97aSjoerg Segment.fileoff = EHFrameOffset;
264*da58b97aSjoerg Segment.filesize = EHFrameSize;
265*da58b97aSjoerg } else if (StringRef("__LINKEDIT") == Segment.segname) {
26606f32e7eSjoerg Segment.fileoff = LinkeditOffset;
26706f32e7eSjoerg Segment.filesize = LinkeditSize;
26806f32e7eSjoerg // Resize vmsize by rounding to the page size.
26906f32e7eSjoerg Segment.vmsize = alignTo(LinkeditSize, 0x1000);
270*da58b97aSjoerg } else {
271*da58b97aSjoerg Segment.fileoff = Segment.filesize = 0;
27206f32e7eSjoerg }
27306f32e7eSjoerg
27406f32e7eSjoerg // Check if the end address of the last segment and our current
27506f32e7eSjoerg // start address leave a sufficient gap to store the __DWARF
27606f32e7eSjoerg // segment.
27706f32e7eSjoerg uint64_t PrevEndAddress = EndAddress;
27806f32e7eSjoerg EndAddress = alignTo(EndAddress, 0x1000);
27906f32e7eSjoerg if (GapForDwarf == UINT64_MAX && Segment.vmaddr > EndAddress &&
28006f32e7eSjoerg Segment.vmaddr - EndAddress >= DwarfSegmentSize)
28106f32e7eSjoerg GapForDwarf = EndAddress;
28206f32e7eSjoerg
28306f32e7eSjoerg // The segments are not necessarily sorted by their vmaddr.
28406f32e7eSjoerg EndAddress =
28506f32e7eSjoerg std::max<uint64_t>(PrevEndAddress, Segment.vmaddr + Segment.vmsize);
28606f32e7eSjoerg unsigned nsects = Segment.nsects;
28706f32e7eSjoerg if (Obj.isLittleEndian() != sys::IsLittleEndianHost)
28806f32e7eSjoerg MachO::swapStruct(Segment);
28906f32e7eSjoerg Writer.W.OS.write(reinterpret_cast<char *>(&Segment), sizeof(Segment));
29006f32e7eSjoerg for (unsigned i = 0; i < nsects; ++i) {
29106f32e7eSjoerg auto Sect = getSection(Obj, Segment, LCI, i);
292*da58b97aSjoerg if (StringRef("__eh_frame") == Sect.sectname) {
293*da58b97aSjoerg Sect.offset = EHFrameOffset;
294*da58b97aSjoerg Sect.reloff = Sect.nreloc = 0;
295*da58b97aSjoerg } else {
29606f32e7eSjoerg Sect.offset = Sect.reloff = Sect.nreloc = 0;
297*da58b97aSjoerg }
29806f32e7eSjoerg if (Obj.isLittleEndian() != sys::IsLittleEndianHost)
29906f32e7eSjoerg MachO::swapStruct(Sect);
30006f32e7eSjoerg Writer.W.OS.write(reinterpret_cast<char *>(&Sect), sizeof(Sect));
30106f32e7eSjoerg }
30206f32e7eSjoerg }
30306f32e7eSjoerg
30406f32e7eSjoerg // Write the __DWARF segment load command to the output file.
createDwarfSegment(uint64_t VMAddr,uint64_t FileOffset,uint64_t FileSize,unsigned NumSections,MCAsmLayout & Layout,MachObjectWriter & Writer)30506f32e7eSjoerg static void createDwarfSegment(uint64_t VMAddr, uint64_t FileOffset,
30606f32e7eSjoerg uint64_t FileSize, unsigned NumSections,
30706f32e7eSjoerg MCAsmLayout &Layout, MachObjectWriter &Writer) {
30806f32e7eSjoerg Writer.writeSegmentLoadCommand("__DWARF", NumSections, VMAddr,
30906f32e7eSjoerg alignTo(FileSize, 0x1000), FileOffset,
31006f32e7eSjoerg FileSize, /* MaxProt */ 7,
31106f32e7eSjoerg /* InitProt =*/3);
31206f32e7eSjoerg
31306f32e7eSjoerg for (unsigned int i = 0, n = Layout.getSectionOrder().size(); i != n; ++i) {
31406f32e7eSjoerg MCSection *Sec = Layout.getSectionOrder()[i];
31506f32e7eSjoerg if (Sec->begin() == Sec->end() || !Layout.getSectionFileSize(Sec))
31606f32e7eSjoerg continue;
31706f32e7eSjoerg
31806f32e7eSjoerg unsigned Align = Sec->getAlignment();
31906f32e7eSjoerg if (Align > 1) {
32006f32e7eSjoerg VMAddr = alignTo(VMAddr, Align);
32106f32e7eSjoerg FileOffset = alignTo(FileOffset, Align);
32206f32e7eSjoerg }
32306f32e7eSjoerg Writer.writeSection(Layout, *Sec, VMAddr, FileOffset, 0, 0, 0);
32406f32e7eSjoerg
32506f32e7eSjoerg FileOffset += Layout.getSectionAddressSize(Sec);
32606f32e7eSjoerg VMAddr += Layout.getSectionAddressSize(Sec);
32706f32e7eSjoerg }
32806f32e7eSjoerg }
32906f32e7eSjoerg
isExecutable(const object::MachOObjectFile & Obj)33006f32e7eSjoerg static bool isExecutable(const object::MachOObjectFile &Obj) {
33106f32e7eSjoerg if (Obj.is64Bit())
33206f32e7eSjoerg return Obj.getHeader64().filetype != MachO::MH_OBJECT;
33306f32e7eSjoerg else
33406f32e7eSjoerg return Obj.getHeader().filetype != MachO::MH_OBJECT;
33506f32e7eSjoerg }
33606f32e7eSjoerg
hasLinkEditSegment(const object::MachOObjectFile & Obj)33706f32e7eSjoerg static bool hasLinkEditSegment(const object::MachOObjectFile &Obj) {
33806f32e7eSjoerg bool HasLinkEditSegment = false;
33906f32e7eSjoerg iterateOnSegments(Obj, [&](const MachO::segment_command_64 &Segment) {
34006f32e7eSjoerg if (StringRef("__LINKEDIT") == Segment.segname)
34106f32e7eSjoerg HasLinkEditSegment = true;
34206f32e7eSjoerg });
34306f32e7eSjoerg return HasLinkEditSegment;
34406f32e7eSjoerg }
34506f32e7eSjoerg
segmentLoadCommandSize(bool Is64Bit,unsigned NumSections)34606f32e7eSjoerg static unsigned segmentLoadCommandSize(bool Is64Bit, unsigned NumSections) {
34706f32e7eSjoerg if (Is64Bit)
34806f32e7eSjoerg return sizeof(MachO::segment_command_64) +
34906f32e7eSjoerg NumSections * sizeof(MachO::section_64);
35006f32e7eSjoerg
35106f32e7eSjoerg return sizeof(MachO::segment_command) + NumSections * sizeof(MachO::section);
35206f32e7eSjoerg }
35306f32e7eSjoerg
35406f32e7eSjoerg // Stream a dSYM companion binary file corresponding to the binary referenced
35506f32e7eSjoerg // by \a DM to \a OutFile. The passed \a MS MCStreamer is setup to write to
35606f32e7eSjoerg // \a OutFile and it must be using a MachObjectWriter object to do so.
generateDsymCompanion(llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS,const DebugMap & DM,SymbolMapTranslator & Translator,MCStreamer & MS,raw_fd_ostream & OutFile)357*da58b97aSjoerg bool generateDsymCompanion(llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS,
358*da58b97aSjoerg const DebugMap &DM, SymbolMapTranslator &Translator,
35906f32e7eSjoerg MCStreamer &MS, raw_fd_ostream &OutFile) {
36006f32e7eSjoerg auto &ObjectStreamer = static_cast<MCObjectStreamer &>(MS);
36106f32e7eSjoerg MCAssembler &MCAsm = ObjectStreamer.getAssembler();
36206f32e7eSjoerg auto &Writer = static_cast<MachObjectWriter &>(MCAsm.getWriter());
36306f32e7eSjoerg
36406f32e7eSjoerg // Layout but don't emit.
36506f32e7eSjoerg ObjectStreamer.flushPendingLabels();
36606f32e7eSjoerg MCAsmLayout Layout(MCAsm);
36706f32e7eSjoerg MCAsm.layout(Layout);
36806f32e7eSjoerg
369*da58b97aSjoerg BinaryHolder InputBinaryHolder(VFS, false);
37006f32e7eSjoerg
37106f32e7eSjoerg auto ObjectEntry = InputBinaryHolder.getObjectEntry(DM.getBinaryPath());
37206f32e7eSjoerg if (!ObjectEntry) {
37306f32e7eSjoerg auto Err = ObjectEntry.takeError();
37406f32e7eSjoerg return error(Twine("opening ") + DM.getBinaryPath() + ": " +
37506f32e7eSjoerg toString(std::move(Err)),
37606f32e7eSjoerg "output file streaming");
37706f32e7eSjoerg }
37806f32e7eSjoerg
37906f32e7eSjoerg auto Object =
38006f32e7eSjoerg ObjectEntry->getObjectAs<object::MachOObjectFile>(DM.getTriple());
38106f32e7eSjoerg if (!Object) {
38206f32e7eSjoerg auto Err = Object.takeError();
38306f32e7eSjoerg return error(Twine("opening ") + DM.getBinaryPath() + ": " +
38406f32e7eSjoerg toString(std::move(Err)),
38506f32e7eSjoerg "output file streaming");
38606f32e7eSjoerg }
38706f32e7eSjoerg
38806f32e7eSjoerg auto &InputBinary = *Object;
38906f32e7eSjoerg
39006f32e7eSjoerg bool Is64Bit = Writer.is64Bit();
39106f32e7eSjoerg MachO::symtab_command SymtabCmd = InputBinary.getSymtabLoadCommand();
39206f32e7eSjoerg
39306f32e7eSjoerg // Compute the number of load commands we will need.
39406f32e7eSjoerg unsigned LoadCommandSize = 0;
39506f32e7eSjoerg unsigned NumLoadCommands = 0;
39606f32e7eSjoerg
39706f32e7eSjoerg // Get LC_UUID and LC_BUILD_VERSION.
39806f32e7eSjoerg MachO::uuid_command UUIDCmd;
39906f32e7eSjoerg SmallVector<MachO::build_version_command, 2> BuildVersionCmd;
40006f32e7eSjoerg memset(&UUIDCmd, 0, sizeof(UUIDCmd));
40106f32e7eSjoerg for (auto &LCI : InputBinary.load_commands()) {
40206f32e7eSjoerg switch (LCI.C.cmd) {
40306f32e7eSjoerg case MachO::LC_UUID:
40406f32e7eSjoerg if (UUIDCmd.cmd)
40506f32e7eSjoerg return error("Binary contains more than one UUID");
40606f32e7eSjoerg UUIDCmd = InputBinary.getUuidCommand(LCI);
40706f32e7eSjoerg ++NumLoadCommands;
40806f32e7eSjoerg LoadCommandSize += sizeof(UUIDCmd);
40906f32e7eSjoerg break;
41006f32e7eSjoerg case MachO::LC_BUILD_VERSION: {
41106f32e7eSjoerg MachO::build_version_command Cmd;
41206f32e7eSjoerg memset(&Cmd, 0, sizeof(Cmd));
41306f32e7eSjoerg Cmd = InputBinary.getBuildVersionLoadCommand(LCI);
41406f32e7eSjoerg ++NumLoadCommands;
41506f32e7eSjoerg LoadCommandSize += sizeof(Cmd);
41606f32e7eSjoerg // LLDB doesn't care about the build tools for now.
41706f32e7eSjoerg Cmd.ntools = 0;
41806f32e7eSjoerg BuildVersionCmd.push_back(Cmd);
41906f32e7eSjoerg break;
42006f32e7eSjoerg }
42106f32e7eSjoerg default:
42206f32e7eSjoerg break;
42306f32e7eSjoerg }
42406f32e7eSjoerg }
42506f32e7eSjoerg
42606f32e7eSjoerg // If we have a valid symtab to copy, do it.
42706f32e7eSjoerg bool ShouldEmitSymtab =
42806f32e7eSjoerg isExecutable(InputBinary) && hasLinkEditSegment(InputBinary);
42906f32e7eSjoerg if (ShouldEmitSymtab) {
43006f32e7eSjoerg LoadCommandSize += sizeof(MachO::symtab_command);
43106f32e7eSjoerg ++NumLoadCommands;
43206f32e7eSjoerg }
43306f32e7eSjoerg
434*da58b97aSjoerg // If we have a valid eh_frame to copy, do it.
435*da58b97aSjoerg uint64_t EHFrameSize = 0;
436*da58b97aSjoerg StringRef EHFrameData;
437*da58b97aSjoerg for (const object::SectionRef &Section : InputBinary.sections()) {
438*da58b97aSjoerg Expected<StringRef> NameOrErr = Section.getName();
439*da58b97aSjoerg if (!NameOrErr) {
440*da58b97aSjoerg consumeError(NameOrErr.takeError());
441*da58b97aSjoerg continue;
442*da58b97aSjoerg }
443*da58b97aSjoerg StringRef SectionName = *NameOrErr;
444*da58b97aSjoerg SectionName = SectionName.substr(SectionName.find_first_not_of("._"));
445*da58b97aSjoerg if (SectionName == "eh_frame") {
446*da58b97aSjoerg if (Expected<StringRef> ContentsOrErr = Section.getContents()) {
447*da58b97aSjoerg EHFrameData = *ContentsOrErr;
448*da58b97aSjoerg EHFrameSize = Section.getSize();
449*da58b97aSjoerg } else {
450*da58b97aSjoerg consumeError(ContentsOrErr.takeError());
451*da58b97aSjoerg }
452*da58b97aSjoerg }
453*da58b97aSjoerg }
454*da58b97aSjoerg
45506f32e7eSjoerg unsigned HeaderSize =
45606f32e7eSjoerg Is64Bit ? sizeof(MachO::mach_header_64) : sizeof(MachO::mach_header);
45706f32e7eSjoerg // We will copy every segment that isn't __DWARF.
45806f32e7eSjoerg iterateOnSegments(InputBinary, [&](const MachO::segment_command_64 &Segment) {
45906f32e7eSjoerg if (StringRef("__DWARF") == Segment.segname)
46006f32e7eSjoerg return;
46106f32e7eSjoerg
46206f32e7eSjoerg ++NumLoadCommands;
46306f32e7eSjoerg LoadCommandSize += segmentLoadCommandSize(Is64Bit, Segment.nsects);
46406f32e7eSjoerg });
46506f32e7eSjoerg
46606f32e7eSjoerg // We will add our own brand new __DWARF segment if we have debug
46706f32e7eSjoerg // info.
46806f32e7eSjoerg unsigned NumDwarfSections = 0;
46906f32e7eSjoerg uint64_t DwarfSegmentSize = 0;
47006f32e7eSjoerg
47106f32e7eSjoerg for (unsigned int i = 0, n = Layout.getSectionOrder().size(); i != n; ++i) {
47206f32e7eSjoerg MCSection *Sec = Layout.getSectionOrder()[i];
47306f32e7eSjoerg if (Sec->begin() == Sec->end())
47406f32e7eSjoerg continue;
47506f32e7eSjoerg
47606f32e7eSjoerg if (uint64_t Size = Layout.getSectionFileSize(Sec)) {
47706f32e7eSjoerg DwarfSegmentSize = alignTo(DwarfSegmentSize, Sec->getAlignment());
47806f32e7eSjoerg DwarfSegmentSize += Size;
47906f32e7eSjoerg ++NumDwarfSections;
48006f32e7eSjoerg }
48106f32e7eSjoerg }
48206f32e7eSjoerg
48306f32e7eSjoerg if (NumDwarfSections) {
48406f32e7eSjoerg ++NumLoadCommands;
48506f32e7eSjoerg LoadCommandSize += segmentLoadCommandSize(Is64Bit, NumDwarfSections);
48606f32e7eSjoerg }
48706f32e7eSjoerg
48806f32e7eSjoerg SmallString<0> NewSymtab;
489*da58b97aSjoerg std::function<StringRef(StringRef)> TranslationLambda =
490*da58b97aSjoerg Translator ? [&](StringRef Input) { return Translator(Input); }
491*da58b97aSjoerg : static_cast<std::function<StringRef(StringRef)>>(nullptr);
492*da58b97aSjoerg // Legacy dsymutil puts an empty string at the start of the line table.
493*da58b97aSjoerg // thus we set NonRelocatableStringpool(,PutEmptyString=true)
494*da58b97aSjoerg NonRelocatableStringpool NewStrings(TranslationLambda, true);
49506f32e7eSjoerg unsigned NListSize = Is64Bit ? sizeof(MachO::nlist_64) : sizeof(MachO::nlist);
49606f32e7eSjoerg unsigned NumSyms = 0;
49706f32e7eSjoerg uint64_t NewStringsSize = 0;
49806f32e7eSjoerg if (ShouldEmitSymtab) {
49906f32e7eSjoerg NewSymtab.reserve(SymtabCmd.nsyms * NListSize / 2);
50006f32e7eSjoerg NumSyms = transferSymbols(InputBinary, NewSymtab, NewStrings);
50106f32e7eSjoerg NewStringsSize = NewStrings.getSize() + 1;
50206f32e7eSjoerg }
50306f32e7eSjoerg
50406f32e7eSjoerg uint64_t SymtabStart = LoadCommandSize;
50506f32e7eSjoerg SymtabStart += HeaderSize;
50606f32e7eSjoerg SymtabStart = alignTo(SymtabStart, 0x1000);
50706f32e7eSjoerg
50806f32e7eSjoerg // We gathered all the information we need, start emitting the output file.
50906f32e7eSjoerg Writer.writeHeader(MachO::MH_DSYM, NumLoadCommands, LoadCommandSize, false);
51006f32e7eSjoerg
51106f32e7eSjoerg // Write the load commands.
51206f32e7eSjoerg assert(OutFile.tell() == HeaderSize);
51306f32e7eSjoerg if (UUIDCmd.cmd != 0) {
51406f32e7eSjoerg Writer.W.write<uint32_t>(UUIDCmd.cmd);
51506f32e7eSjoerg Writer.W.write<uint32_t>(sizeof(UUIDCmd));
51606f32e7eSjoerg OutFile.write(reinterpret_cast<const char *>(UUIDCmd.uuid), 16);
51706f32e7eSjoerg assert(OutFile.tell() == HeaderSize + sizeof(UUIDCmd));
51806f32e7eSjoerg }
51906f32e7eSjoerg for (auto Cmd : BuildVersionCmd) {
52006f32e7eSjoerg Writer.W.write<uint32_t>(Cmd.cmd);
52106f32e7eSjoerg Writer.W.write<uint32_t>(sizeof(Cmd));
52206f32e7eSjoerg Writer.W.write<uint32_t>(Cmd.platform);
52306f32e7eSjoerg Writer.W.write<uint32_t>(Cmd.minos);
52406f32e7eSjoerg Writer.W.write<uint32_t>(Cmd.sdk);
52506f32e7eSjoerg Writer.W.write<uint32_t>(Cmd.ntools);
52606f32e7eSjoerg }
52706f32e7eSjoerg
52806f32e7eSjoerg assert(SymtabCmd.cmd && "No symbol table.");
52906f32e7eSjoerg uint64_t StringStart = SymtabStart + NumSyms * NListSize;
53006f32e7eSjoerg if (ShouldEmitSymtab)
53106f32e7eSjoerg Writer.writeSymtabLoadCommand(SymtabStart, NumSyms, StringStart,
53206f32e7eSjoerg NewStringsSize);
53306f32e7eSjoerg
534*da58b97aSjoerg uint64_t EHFrameStart = StringStart + NewStringsSize;
535*da58b97aSjoerg EHFrameStart = alignTo(EHFrameStart, 0x1000);
536*da58b97aSjoerg
537*da58b97aSjoerg uint64_t DwarfSegmentStart = EHFrameStart + EHFrameSize;
53806f32e7eSjoerg DwarfSegmentStart = alignTo(DwarfSegmentStart, 0x1000);
53906f32e7eSjoerg
54006f32e7eSjoerg // Write the load commands for the segments and sections we 'import' from
54106f32e7eSjoerg // the original binary.
54206f32e7eSjoerg uint64_t EndAddress = 0;
54306f32e7eSjoerg uint64_t GapForDwarf = UINT64_MAX;
54406f32e7eSjoerg for (auto &LCI : InputBinary.load_commands()) {
54506f32e7eSjoerg if (LCI.C.cmd == MachO::LC_SEGMENT)
546*da58b97aSjoerg transferSegmentAndSections(
547*da58b97aSjoerg LCI, InputBinary.getSegmentLoadCommand(LCI), InputBinary, Writer,
548*da58b97aSjoerg SymtabStart, StringStart + NewStringsSize - SymtabStart, EHFrameStart,
549*da58b97aSjoerg EHFrameSize, DwarfSegmentSize, GapForDwarf, EndAddress);
55006f32e7eSjoerg else if (LCI.C.cmd == MachO::LC_SEGMENT_64)
551*da58b97aSjoerg transferSegmentAndSections(
552*da58b97aSjoerg LCI, InputBinary.getSegment64LoadCommand(LCI), InputBinary, Writer,
553*da58b97aSjoerg SymtabStart, StringStart + NewStringsSize - SymtabStart, EHFrameStart,
554*da58b97aSjoerg EHFrameSize, DwarfSegmentSize, GapForDwarf, EndAddress);
55506f32e7eSjoerg }
55606f32e7eSjoerg
55706f32e7eSjoerg uint64_t DwarfVMAddr = alignTo(EndAddress, 0x1000);
55806f32e7eSjoerg uint64_t DwarfVMMax = Is64Bit ? UINT64_MAX : UINT32_MAX;
55906f32e7eSjoerg if (DwarfVMAddr + DwarfSegmentSize > DwarfVMMax ||
56006f32e7eSjoerg DwarfVMAddr + DwarfSegmentSize < DwarfVMAddr /* Overflow */) {
56106f32e7eSjoerg // There is no room for the __DWARF segment at the end of the
56206f32e7eSjoerg // address space. Look through segments to find a gap.
56306f32e7eSjoerg DwarfVMAddr = GapForDwarf;
56406f32e7eSjoerg if (DwarfVMAddr == UINT64_MAX)
56506f32e7eSjoerg warn("not enough VM space for the __DWARF segment.",
56606f32e7eSjoerg "output file streaming");
56706f32e7eSjoerg }
56806f32e7eSjoerg
56906f32e7eSjoerg // Write the load command for the __DWARF segment.
57006f32e7eSjoerg createDwarfSegment(DwarfVMAddr, DwarfSegmentStart, DwarfSegmentSize,
57106f32e7eSjoerg NumDwarfSections, Layout, Writer);
57206f32e7eSjoerg
57306f32e7eSjoerg assert(OutFile.tell() == LoadCommandSize + HeaderSize);
57406f32e7eSjoerg OutFile.write_zeros(SymtabStart - (LoadCommandSize + HeaderSize));
57506f32e7eSjoerg assert(OutFile.tell() == SymtabStart);
57606f32e7eSjoerg
57706f32e7eSjoerg // Transfer symbols.
57806f32e7eSjoerg if (ShouldEmitSymtab) {
57906f32e7eSjoerg OutFile << NewSymtab.str();
58006f32e7eSjoerg assert(OutFile.tell() == StringStart);
58106f32e7eSjoerg
58206f32e7eSjoerg // Transfer string table.
58306f32e7eSjoerg // FIXME: The NonRelocatableStringpool starts with an empty string, but
58406f32e7eSjoerg // dsymutil-classic starts the reconstructed string table with 2 of these.
58506f32e7eSjoerg // Reproduce that behavior for now (there is corresponding code in
58606f32e7eSjoerg // transferSymbol).
58706f32e7eSjoerg OutFile << '\0';
58806f32e7eSjoerg std::vector<DwarfStringPoolEntryRef> Strings =
58906f32e7eSjoerg NewStrings.getEntriesForEmission();
59006f32e7eSjoerg for (auto EntryRef : Strings) {
59106f32e7eSjoerg OutFile.write(EntryRef.getString().data(),
59206f32e7eSjoerg EntryRef.getString().size() + 1);
59306f32e7eSjoerg }
59406f32e7eSjoerg }
59506f32e7eSjoerg assert(OutFile.tell() == StringStart + NewStringsSize);
59606f32e7eSjoerg
597*da58b97aSjoerg // Pad till the EH frame start.
598*da58b97aSjoerg OutFile.write_zeros(EHFrameStart - (StringStart + NewStringsSize));
599*da58b97aSjoerg assert(OutFile.tell() == EHFrameStart);
600*da58b97aSjoerg
601*da58b97aSjoerg // Transfer eh_frame.
602*da58b97aSjoerg if (EHFrameSize > 0)
603*da58b97aSjoerg OutFile << EHFrameData;
604*da58b97aSjoerg assert(OutFile.tell() == EHFrameStart + EHFrameSize);
605*da58b97aSjoerg
60606f32e7eSjoerg // Pad till the Dwarf segment start.
607*da58b97aSjoerg OutFile.write_zeros(DwarfSegmentStart - (EHFrameStart + EHFrameSize));
60806f32e7eSjoerg assert(OutFile.tell() == DwarfSegmentStart);
60906f32e7eSjoerg
61006f32e7eSjoerg // Emit the Dwarf sections contents.
61106f32e7eSjoerg for (const MCSection &Sec : MCAsm) {
61206f32e7eSjoerg if (Sec.begin() == Sec.end())
61306f32e7eSjoerg continue;
61406f32e7eSjoerg
61506f32e7eSjoerg uint64_t Pos = OutFile.tell();
61606f32e7eSjoerg OutFile.write_zeros(alignTo(Pos, Sec.getAlignment()) - Pos);
61706f32e7eSjoerg MCAsm.writeSectionData(OutFile, &Sec, Layout);
61806f32e7eSjoerg }
61906f32e7eSjoerg
62006f32e7eSjoerg return true;
62106f32e7eSjoerg }
62206f32e7eSjoerg } // namespace MachOUtils
62306f32e7eSjoerg } // namespace dsymutil
62406f32e7eSjoerg } // namespace llvm
625