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