Index: llvm/docs/CommandGuide/dsymutil.rst =================================================================== --- llvm/docs/CommandGuide/dsymutil.rst +++ llvm/docs/CommandGuide/dsymutil.rst @@ -32,11 +32,19 @@ architectures will be linked by default and any architectures that can't be properly linked will cause :program:`dsymutil` to return an error. +.. option:: --build-variant-suffix + + Specify the build variant suffix used to build the executabe file. + .. option:: --dump-debug-map Dump the *executable*'s debug-map (the list of the object files containing the debug information) in YAML format and exit. No DWARF link will take place. + .. option:: -D + + Specify a directory that contain dSYM files to search for. + .. option:: --fat64 Use a 64-bit header when emitting universal binaries. Index: llvm/include/llvm/BinaryFormat/Dwarf.def =================================================================== --- llvm/include/llvm/BinaryFormat/Dwarf.def +++ llvm/include/llvm/BinaryFormat/Dwarf.def @@ -629,6 +629,7 @@ HANDLE_DW_AT(0x3fed, APPLE_property, 0, APPLE) HANDLE_DW_AT(0x3fee, APPLE_objc_direct, 0, APPLE) HANDLE_DW_AT(0x3fef, APPLE_sdk, 0, APPLE) +HANDLE_DW_AT(0x3ff0, APPLE_origin, 0, APPLE) // Attribute form encodings. HANDLE_DW_FORM(0x01, addr, 2, DWARF) Index: llvm/include/llvm/BinaryFormat/MachO.h =================================================================== --- llvm/include/llvm/BinaryFormat/MachO.h +++ llvm/include/llvm/BinaryFormat/MachO.h @@ -373,6 +373,7 @@ N_SSYM = 0x60u, N_SO = 0x64u, N_OSO = 0x66u, + N_LIB = 0x68u, N_LSYM = 0x80u, N_BINCL = 0x82u, N_SOL = 0x84u, Index: llvm/include/llvm/DWARFLinker/DWARFLinker.h =================================================================== --- llvm/include/llvm/DWARFLinker/DWARFLinker.h +++ llvm/include/llvm/DWARFLinker/DWARFLinker.h @@ -14,6 +14,7 @@ #include "llvm/CodeGen/AccelTable.h" #include "llvm/CodeGen/NonRelocatableStringpool.h" #include "llvm/DWARFLinker/DWARFLinkerCompileUnit.h" +#include "llvm/DWARFLinker/DWARFLinkerRelocs.h" #include "llvm/DebugInfo/DWARF/DWARFContext.h" #include "llvm/DebugInfo/DWARF/DWARFDebugLine.h" #include "llvm/DebugInfo/DWARF/DWARFDebugRangeList.h" @@ -23,9 +24,11 @@ namespace llvm { class DWARFExpression; +class DWARFLinker; class DWARFUnit; class DataExtractor; class DeclContextTree; + template class SmallVectorImpl; enum class DwarfLinkerClient { Dsymutil, LLD, General }; @@ -62,12 +65,19 @@ virtual std::optional getSubprogramRelocAdjustment(const DWARFDie &DIE) = 0; + /// Returns the file name associated to the AddessesMap + virtual std::optional getFileName() = 0; + /// Apply the valid relocations to the buffer \p Data, taking into /// account that Data is at \p BaseOffset in the .debug_info section. /// /// \returns true whether any reloc has been applied. virtual bool applyValidRelocs(MutableArrayRef Data, uint64_t BaseOffset, - bool IsLittleEndian) = 0; + bool IsLittleEndian, uint64_t OutOffset, + CompileUnit *CU) = 0; + + /// Add the valid relocations to be serialized to the relocation map + virtual void addValidRelocs(RelocMap *RM) = 0; /// Erases all data. virtual void clear() = 0; @@ -638,6 +648,9 @@ CompileUnit::DIEInfo &MyInfo, unsigned Flags); + void visitCompileUnitDIE(AddressesMap &RelocMgr, const DWARFDie &DIE, + CompileUnit::DIEInfo &MyInfo); + /// Resolve the DIE attribute reference that has been extracted in \p /// RefValue. The resulting DIE might be in another CompileUnit which is /// stored into \p ReferencedCU. \returns null if resolving fails for any Index: llvm/include/llvm/DWARFLinker/DWARFLinkerCompileUnit.h =================================================================== --- llvm/include/llvm/DWARFLinker/DWARFLinkerCompileUnit.h +++ llvm/include/llvm/DWARFLinker/DWARFLinkerCompileUnit.h @@ -74,6 +74,9 @@ /// The index of this DIE's parent. uint32_t ParentIdx; + /// Name of the file where the described entity is from. + std::optional FileName; + /// Is the DIE part of the linked output? bool Keep : 1; @@ -105,7 +108,8 @@ CompileUnit(DWARFUnit &OrigUnit, unsigned ID, bool CanUseODR, StringRef ClangModuleName) - : OrigUnit(OrigUnit), ID(ID), ClangModuleName(ClangModuleName) { + : OrigUnit(OrigUnit), ID(ID), AddedOriginObject(false), + ClangModuleName(ClangModuleName) { Info.resize(OrigUnit.getNumDIEs()); auto CUDie = OrigUnit.getUnitDIE(false); @@ -143,6 +147,9 @@ const std::string &getClangModuleName() const { return ClangModuleName; } + void setOriginObject() { AddedOriginObject = true; } + bool addedOriginObject() const { return AddedOriginObject; } + DIEInfo &getInfo(unsigned Idx) { return Info[Idx]; } const DIEInfo &getInfo(unsigned Idx) const { return Info[Idx]; } @@ -320,6 +327,9 @@ /// The DW_AT_language of this unit. uint16_t Language = 0; + /// The DW_AT_APPLE_origin attribute was just added. + bool AddedOriginObject; + /// The DW_AT_LLVM_sysroot of this unit. std::string SysRoot; Index: llvm/include/llvm/DWARFLinkerParallel/AddressesMap.h =================================================================== --- llvm/include/llvm/DWARFLinkerParallel/AddressesMap.h +++ llvm/include/llvm/DWARFLinkerParallel/AddressesMap.h @@ -15,6 +15,10 @@ #include namespace llvm { + +class RelocMap; +class CompileUnit; + namespace dwarflinker_parallel { /// Mapped value in the address map is the offset to apply to the @@ -53,12 +57,19 @@ virtual std::optional getSubprogramRelocAdjustment(const DWARFDie &DIE) = 0; + // Returns the file name associated to the AddessesMap. + virtual std::optional getFileName() = 0; + /// Apply the valid relocations to the buffer \p Data, taking into /// account that Data is at \p BaseOffset in the .debug_info section. /// /// \returns true whether any reloc has been applied. virtual bool applyValidRelocs(MutableArrayRef Data, uint64_t BaseOffset, - bool IsLittleEndian) = 0; + bool IsLittleEndian, uint64_t OutOffset, + llvm::CompileUnit *CU) = 0; + + /// Add the valid relocatios to be serialized to the relocation map. + virtual void addValidRelocs(RelocMap *RM) = 0; /// Erases all data. virtual void clear() = 0; Index: llvm/include/llvm/TargetParser/Triple.h =================================================================== --- llvm/include/llvm/TargetParser/Triple.h +++ llvm/include/llvm/TargetParser/Triple.h @@ -420,9 +420,6 @@ /// Get the architecture (first) component of the triple. StringRef getArchName() const; - /// Get the architecture name based on Kind and SubArch. - StringRef getArchName(ArchType Kind, SubArchType SubArch = NoSubArch) const; - /// Get the vendor (second) component of the triple. StringRef getVendorName() const; @@ -1102,6 +1099,9 @@ /// Get the canonical name for the \p Kind architecture. static StringRef getArchTypeName(ArchType Kind); + /// Get the architecture name based on \p Kind and \p SubArch. + static StringRef getArchName(ArchType Kind, SubArchType SubArch = NoSubArch); + /// Get the "prefix" canonical name for the \p Kind architecture. This is the /// prefix used by the architecture specific builtins, and is suitable for /// passing to \see Intrinsic::getIntrinsicForClangBuiltin(). Index: llvm/lib/DWARFLinker/DWARFLinker.cpp =================================================================== --- llvm/lib/DWARFLinker/DWARFLinker.cpp +++ llvm/lib/DWARFLinker/DWARFLinker.cpp @@ -515,6 +515,7 @@ MyInfo.AddrAdjust = *LocExprAddrAndRelocAdjustment.second; MyInfo.InDebugMap = true; + MyInfo.FileName = RelocMgr.getFileName(); if (((Flags & TF_InFunctionScope) && !LLVM_UNLIKELY(Options.KeepFunctionForStatic))) @@ -550,6 +551,7 @@ MyInfo.AddrAdjust = *RelocAdjustment; MyInfo.InDebugMap = true; + MyInfo.FileName = RelocMgr.getFileName(); if (Options.Verbose) { outs() << "Keeping subprogram DIE:"; @@ -594,6 +596,12 @@ return Flags; } +/// Get information about a compile unit that needs to be updated. +void DWARFLinker::visitCompileUnitDIE(AddressesMap &RelocMgr, + const DWARFDie &DIE, + CompileUnit::DIEInfo &MyInfo) { + MyInfo.FileName = RelocMgr.getFileName(); +} /// Check if a DIE should be kept. /// \returns updated TraversalFlags. unsigned DWARFLinker::shouldKeepDIE(AddressesMap &RelocMgr, const DWARFDie &DIE, @@ -615,6 +623,9 @@ case dwarf::DW_TAG_imported_unit: // We always want to keep these. return Flags | TF_Keep; + case dwarf::DW_TAG_compile_unit: + visitCompileUnitDIE(RelocMgr, DIE, MyInfo); + break; default: break; } @@ -1655,6 +1666,9 @@ // Since DW_AT_loclists_base is used for only DW_FORM_loclistx the // DW_AT_loclists_base is removed. return !Update; + case dwarf::DW_AT_APPLE_origin: + // Always recreate the DW_AT_APPLE_origin attribute. + return true; case dwarf::DW_AT_location: case dwarf::DW_AT_frame_base: return !Update && SkipPC; @@ -1714,7 +1728,8 @@ DWARFDataExtractor(DIECopy, Data.isLittleEndian(), Data.getAddressSize()); // Modify the copy with relocated addresses. - ObjFile.Addresses->applyValidRelocs(DIECopy, Offset, Data.isLittleEndian()); + ObjFile.Addresses->applyValidRelocs(DIECopy, Offset, Data.isLittleEndian(), + OutOffset, &Unit); // Reset the Offset to 0 as we will be working on the local copy of // the data. @@ -1744,8 +1759,10 @@ Flags |= TF_SkipPC; } + bool hadOriginAttribute = false; for (const auto &AttrSpec : Abbrev->attributes()) { if (shouldSkipAttribute(Update, AttrSpec, Flags & TF_SkipPC)) { + hadOriginAttribute = AttrSpec.Attr == dwarf::DW_AT_APPLE_origin; DWARFFormValue::skipValue(AttrSpec.Form, Data, &Offset, U.getFormParams()); continue; @@ -1760,8 +1777,21 @@ AttrSize, AttrInfo, IsLittleEndian); } - // Look for accelerator entries. uint16_t Tag = InputDIE.getTag(); + // Add origin attribute to Compile Unit die. + if (Tag == dwarf::DW_TAG_compile_unit) { + if (Info.FileName) { + auto StringEntry = DebugStrPool.getEntry(Info.FileName.value()); + Die->addValue(DIEAlloc, dwarf::Attribute(dwarf::DW_AT_APPLE_origin), + dwarf::DW_FORM_strp, DIEInteger(StringEntry.getOffset())); + AttrInfo.Name = StringEntry; + OutOffset += 4; + if (!hadOriginAttribute) + Unit.setOriginObject(); + } + } + + // Look for accelerator entries. // FIXME: This is slightly wrong. An inline_subroutine without a // low_pc, but with AT_ranges might be interesting to get into the // accelerator tables too. For now stick with dsymutil's behavior. Index: llvm/lib/TargetParser/Triple.cpp =================================================================== --- llvm/lib/TargetParser/Triple.cpp +++ llvm/lib/TargetParser/Triple.cpp @@ -89,6 +89,36 @@ llvm_unreachable("Invalid ArchType!"); } +StringRef Triple::getArchName(ArchType Kind, SubArchType SubArch) { + switch (Kind) { + case Triple::mips: + if (SubArch == MipsSubArch_r6) + return "mipsisa32r6"; + break; + case Triple::mipsel: + if (SubArch == MipsSubArch_r6) + return "mipsisa32r6el"; + break; + case Triple::mips64: + if (SubArch == MipsSubArch_r6) + return "mipsisa64r6"; + break; + case Triple::mips64el: + if (SubArch == MipsSubArch_r6) + return "mipsisa64r6el"; + break; + case Triple::aarch64: + if (SubArch == AArch64SubArch_arm64ec) + return "arm64ec"; + if (SubArch == AArch64SubArch_arm64e) + return "arm64e"; + break; + default: + break; + } + return getArchTypeName(Kind); +} + StringRef Triple::getArchTypePrefix(ArchType Kind) { switch (Kind) { default: @@ -1145,34 +1175,6 @@ return StringRef(Data).split('-').first; // Isolate first component } -StringRef Triple::getArchName(ArchType Kind, SubArchType SubArch) const { - switch (Kind) { - case Triple::mips: - if (SubArch == MipsSubArch_r6) - return "mipsisa32r6"; - break; - case Triple::mipsel: - if (SubArch == MipsSubArch_r6) - return "mipsisa32r6el"; - break; - case Triple::mips64: - if (SubArch == MipsSubArch_r6) - return "mipsisa64r6"; - break; - case Triple::mips64el: - if (SubArch == MipsSubArch_r6) - return "mipsisa64r6el"; - break; - case Triple::aarch64: - if (SubArch == AArch64SubArch_arm64ec) - return "arm64ec"; - break; - default: - break; - } - return getArchTypeName(Kind); -} - StringRef Triple::getVendorName() const { StringRef Tmp = StringRef(Data).split('-').second; // Strip first component return Tmp.split('-').first; // Isolate second component Index: llvm/test/tools/dsymutil/basic-linking.test =================================================================== --- llvm/test/tools/dsymutil/basic-linking.test +++ llvm/test/tools/dsymutil/basic-linking.test @@ -2,6 +2,9 @@ RUN: dsymutil -no-output -verbose -oso-prepend-path=%p %p/Inputs/basic-lto.macho.x86_64 | FileCheck %s --check-prefix=CHECK-LTO RUN: dsymutil -no-output -verbose -oso-prepend-path=%p %p/Inputs/basic-archive.macho.x86_64 | FileCheck %s --check-prefix=CHECK-ARCHIVE RUN: dsymutil -no-output -verbose -oso-prepend-path=%p %p/Inputs/basic.macho.x86_64 %p/Inputs/basic-lto.macho.x86_64 %p/Inputs/basic-archive.macho.x86_64 | FileCheck %s --check-prefixes=CHECK,CHECK-LTO,CHECK-ARCHIVE +RUN: dsymutil -no-output -verbose -oso-prepend-path=%p -D %p/Inputs %p/Inputs/basic-relink.macho.arm64.dylib | FileCheck %s --check-prefix=CHECK-RELINK +RUN: dsymutil -no-output -verbose -oso-prepend-path=%p -D %p/Inputs %p/Inputs/two-level-relink.macho.arm64.dylib | FileCheck %s --check-prefix=CHECK-RELINK-TWO +RUN: dsymutil -no-output -verbose -oso-prepend-path=%p -build-variant-suffix=_debug -D WrongPath -D %p/Inputs %p/Inputs/variant-relink.macho.arm64.dylib | FileCheck %s --check-prefix=CHECK-RELINK-VARIANT This test check the basic Dwarf linking process through the debug dumps. @@ -175,3 +178,116 @@ CHECK-ARCHIVE-NEXT: Keeping subprogram DIE: CHECK-ARCHIVE-NEXT: DW_TAG_subprogram CHECK-ARCHIVE-NEXT: DW_AT_name {{.*}}"inc") + + +================================= Simple relink ================================ +CHECK-RELINK: DEBUG MAP OBJECT: {{.*}}basic-relink.macho.arm64.o +CHECK-RELINK: Input compilation unit: +CHECK-RELINK-NEXT: TAG_compile_unit +CHECK-RELINK-NOT: TAG +CHECK-RELINK: AT_name {{.*}}basic-relink.macho.arm64.c + +CHECK-RELINK: DEBUG MAP OBJECT: {{.*}}foo-relink.dylib +CHECK-RELINK: Input compilation unit: +CHECK-RELINK-NEXT: TAG_compile_unit +CHECK-RELINK-NOT: TAG +CHECK-RELINK: AT_name {{.*}}foo-relink.c + +CHECK-RELINK: DEBUG MAP OBJECT: {{.*}}bar-relink.dylib +CHECK-RELINK: Input compilation unit: +CHECK-RELINK-NEXT: TAG_compile_unit +CHECK-RELINK-NOT: TAG +CHECK-RELINK: AT_name {{.*}}bar-relink.c + +CHECK-RELINK-NOT: Found valid debug map entry +CHECK-RELINK: Found valid debug map entry: _display 0x0000000000000000 => 0x0000000000003f20 +CHECK-RELINK-NEXT: Keeping subprogram DIE: +CHECK-RELINK-NEXT: DW_TAG_subprogram +CHECK-RELINK: DW_AT_name{{.*}}"display" + +CHECK-RELINK: Found valid debug map entry: _foo 0x0000000000003f60 => 0x0000000000003f3c +CHECK-RELINK-NEXT: Keeping subprogram DIE: +CHECK-RELINK-NEXT: DW_TAG_subprogram +CHECK-RELINK: DW_AT_name {{.*}}"foo" + +CHECK-RELINK-NOT: Found valid debug map entry +CHECK-RELINK: Found valid debug map entry: _foo_unused 0x0000000000003f80 => 0x0000000000003f5c +CHECK-RELINK-NEXT: Keeping subprogram DIE: +CHECK-RELINK-NEXT: DW_TAG_subprogram +CHECK-RELINK: DW_AT_name {{.*}}"foo_unused" + +CHECK-RELINK-NOT: Found valid debug map entry +CHECK-RELINK: Found valid debug map entry: _altfoo 0x0000000000003f88 => 0x0000000000003f64 +CHECK-RELINK-NEXT: Keeping subprogram DIE: +CHECK-RELINK-NEXT: DW_TAG_subprogram +CHECK-RELINK: DW_AT_name {{.*}}"altfoo" + +CHECK-RELINK-NOT: Found valid debug map entry +CHECK-RELINK: Found valid debug map entry: _baz 0x0000000000004000 => 0x0000000000008000 +CHECK-RELINK-NEXT: Keeping variable DIE: +CHECK-RELINK-NEXT: DW_TAG_variable +CHECK-RELINK-NEXT: DW_AT_name {{.*}}"baz" + +CHECK-RELINK-NOT: Found valid debug map entry +CHECK-RELINK: Found valid debug map entry: _bar 0x0000000000003fb4 => 0x0000000000003f88 +CHECK-RELINK-NEXT: Keeping subprogram DIE: +CHECK-RELINK-NEXT: DW_TAG_subprogram +CHECK-RELINK: DW_AT_name {{.*}}"bar" + +================================= Two level relink ================================ +CHECK-RELINK-TWO: DEBUG MAP OBJECT: {{.*}}proxy-relink.dylib +CHECK-RELINK-TWO: Input compilation unit: +CHECK-RELINK-TWO-NEXT: TAG_compile_unit +CHECK-RELINK-TWO-NOT: TAG +CHECK-RELINK-TWO: AT_name {{.*}}two-level-relink.macho.arm64.c + +CHECK-RELINK-TWO: Input compilation unit: +CHECK-RELINK-TWO-NEXT: TAG_compile_unit +CHECK-RELINK-TWO-NOT: TAG +CHECK-RELINK-TWO: AT_name {{.*}}bar-relink.c +CHECK-RELINK-TWO: DW_AT_APPLE_origin {{.*}}/path/to/bar-relink.dylib + +CHECK-RELINK-TWO: Input compilation unit: +CHECK-RELINK-TWO-NEXT: TAG_compile_unit +CHECK-RELINK-TWO-NOT: TAG +CHECK-RELINK-TWO: AT_name {{.*}}foo-relink.c +CHECK-RELINK-TWO: DW_AT_APPLE_origin {{.*}}/path/to/foo-relink.dylib + +CHECK-RELINK-TWO: Input compilation unit: +CHECK-RELINK-TWO-NEXT: TAG_compile_unit +CHECK-RELINK-TWO-NOT: TAG +CHECK-RELINK-TWO: AT_name {{.*}}altfoo-relink.c + +CHECK-RELINK-TWO-NOT: Found valid debug map entry +CHECK-RELINK-TWO: Found valid debug map entry: _display 0x0000000000003f2c => 0x0000000000003f2c +CHECK-RELINK-TWO-NEXT: Keeping subprogram DIE: +CHECK-RELINK-TWO-NEXT: DW_TAG_subprogram +CHECK-RELINK-TWO: DW_AT_name{{.*}}"display" + +CHECK-RELINK-TWO-NOT: Found valid debug map entry +CHECK-RELINK-TWO: Found valid debug map entry: _baz 0x0000000000008000 => 0x0000000000008000 +CHECK-RELINK-TWO-NEXT: Keeping variable DIE: +CHECK-RELINK-TWO-NEXT: DW_TAG_variable +CHECK-RELINK-TWO-NEXT: DW_AT_name {{.*}}"baz" + +CHECK-RELINK-TWO-NOT: Found valid debug map entry +CHECK-RELINK-TWO: Found valid debug map entry: _bar 0x0000000000003f48 => 0x0000000000003f48 +CHECK-RELINK-TWO-NEXT: Keeping subprogram DIE: +CHECK-RELINK-TWO-NEXT: DW_TAG_subprogram +CHECK-RELINK-TWO: DW_AT_name {{.*}}"bar" + +CHECK-RELINK-TWO: Found valid debug map entry: _foo 0x0000000000003f50 => 0x0000000000003f50 +CHECK-RELINK-TWO-NEXT: Keeping subprogram DIE: +CHECK-RELINK-TWO-NEXT: DW_TAG_subprogram +CHECK-RELINK-TWO: DW_AT_name {{.*}}"foo" + +CHECK-RELINK-TWO-NOT: Found valid debug map entry +CHECK-RELINK-TWO: Found valid debug map entry: _altfoo 0x0000000000003f74 => 0x0000000000003f74 +CHECK-RELINK-TWO-NEXT: Keeping subprogram DIE: +CHECK-RELINK-TWO-NEXT: DW_TAG_subprogram +CHECK-RELINK-TWO: DW_AT_name {{.*}}"altfoo" + +================================= Build variants relink ================================ +CHECK-RELINK-VARIANT: DEBUG MAP OBJECT: {{.*}}basic-relink.macho.arm64.o +CHECK-RELINK-VARIANT: DEBUG MAP OBJECT: {{.*}}foo-relink-variant_debug.dylib +CHECK-RELINK-VARIANT: DEBUG MAP OBJECT: {{.*}}bar-relink-variant.dylib Index: llvm/test/tools/dsymutil/cmdline.test =================================================================== --- llvm/test/tools/dsymutil/cmdline.test +++ llvm/test/tools/dsymutil/cmdline.test @@ -7,7 +7,9 @@ HELP: Dsymutil Options: CHECK: -accelerator CHECK: -arch +CHECK: -build-variant-suffix CHECK: -dump-debug-map +CHECK: -D CHECK: -fat64 CHECK: -flat CHECK: -gen-reproducer Index: llvm/tools/dsymutil/CMakeLists.txt =================================================================== --- llvm/tools/dsymutil/CMakeLists.txt +++ llvm/tools/dsymutil/CMakeLists.txt @@ -30,6 +30,7 @@ MachODebugMapParser.cpp MachOUtils.cpp Reproducer.cpp + RelocationMap.cpp SymbolMap.cpp DEPENDS Index: llvm/tools/dsymutil/DebugMap.h =================================================================== --- llvm/tools/dsymutil/DebugMap.h +++ llvm/tools/dsymutil/DebugMap.h @@ -21,6 +21,7 @@ #ifndef LLVM_TOOLS_DSYMUTIL_DEBUGMAP_H #define LLVM_TOOLS_DSYMUTIL_DEBUGMAP_H +#include "RelocationMap.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" @@ -134,22 +135,6 @@ /// linked binary for all the linked atoms in this object file. class DebugMapObject { public: - struct SymbolMapping { - std::optional ObjectAddress; - yaml::Hex64 BinaryAddress; - yaml::Hex32 Size; - - SymbolMapping(std::optional ObjectAddr, uint64_t BinaryAddress, - uint32_t Size) - : BinaryAddress(BinaryAddress), Size(Size) { - if (ObjectAddr) - ObjectAddress = *ObjectAddr; - } - - /// For YAML IO support - SymbolMapping() = default; - }; - using YAMLSymbolMapping = std::pair; using DebugMapEntry = StringMapEntry; @@ -182,6 +167,16 @@ } const std::vector &getWarnings() const { return Warnings; } + const std::optional &getRelocationMap() const { + return RelocationMap; + } + void setRelocationMap(dsymutil::RelocationMap &RM); + + const std::optional &getInstallName() const { + return InstallName; + } + void setInstallName(StringRef IN); + void print(raw_ostream &OS) const; #ifndef NDEBUG void dump() const; @@ -200,6 +195,9 @@ DenseMap AddressToMapping; uint8_t Type; + std::optional RelocationMap; + std::optional InstallName; + std::vector Warnings; /// For YAMLIO support. @@ -225,10 +223,8 @@ using namespace llvm::dsymutil; -template <> -struct MappingTraits> { - static void mapping(IO &io, - std::pair &s); +template <> struct MappingTraits> { + static void mapping(IO &io, std::pair &s); static const bool flow = true; }; @@ -237,12 +233,6 @@ static void mapping(IO &io, dsymutil::DebugMapObject &DMO); }; -template <> struct ScalarTraits { - static void output(const Triple &val, void *, raw_ostream &out); - static StringRef input(StringRef scalar, void *, Triple &value); - static QuotingType mustQuote(StringRef) { return QuotingType::Single; } -}; - template <> struct SequenceTraits>> { static size_t Index: llvm/tools/dsymutil/DebugMap.cpp =================================================================== --- llvm/tools/dsymutil/DebugMap.cpp +++ llvm/tools/dsymutil/DebugMap.cpp @@ -46,6 +46,11 @@ bool DebugMapObject::addSymbol(StringRef Name, std::optional ObjectAddress, uint64_t LinkedAddress, uint32_t Size) { + if (Symbols.count(Name)) { + // Symbol was previously added. + return true; + } + auto InsertResult = Symbols.insert( std::make_pair(Name, SymbolMapping(ObjectAddress, LinkedAddress, Size))); @@ -54,6 +59,12 @@ return InsertResult.second; } +void DebugMapObject::setRelocationMap(dsymutil::RelocationMap &RM) { + RelocationMap.emplace(RM); +} + +void DebugMapObject::setInstallName(StringRef IN) { InstallName.emplace(IN); } + void DebugMapObject::print(raw_ostream &OS) const { OS << getObjectFilename() << ":\n"; // Sort the symbols in alphabetical order, like llvm-nm (and to get @@ -159,8 +170,8 @@ std::vector Entries; }; -void MappingTraits>:: - mapping(IO &io, std::pair &s) { +void MappingTraits>::mapping( + IO &io, std::pair &s) { io.mapRequired("sym", s.first); io.mapOptional("objAddr", s.second.ObjectAddress); io.mapRequired("binAddr", s.second.BinaryAddress); @@ -276,7 +287,13 @@ } } - dsymutil::DebugMapObject Res(Path, sys::toTimePoint(Timestamp), MachO::N_OSO); + uint8_t Type = MachO::N_OSO; + if (Path.endswith(".dylib")) { + // FIXME: find a more resilient way + Type = MachO::N_LIB; + } + dsymutil::DebugMapObject Res(Path, sys::toTimePoint(Timestamp), Type); + for (auto &Entry : Entries) { auto &Mapping = Entry.second; std::optional ObjAddress; Index: llvm/tools/dsymutil/DwarfLinkerForBinary.h =================================================================== --- llvm/tools/dsymutil/DwarfLinkerForBinary.h +++ llvm/tools/dsymutil/DwarfLinkerForBinary.h @@ -13,6 +13,7 @@ #include "DebugMap.h" #include "LinkUtils.h" #include "MachOUtils.h" +#include "RelocationMap.h" #include "llvm/DWARFLinker/DWARFLinker.h" #include "llvm/DWARFLinker/DWARFLinkerCompileUnit.h" #include "llvm/DWARFLinker/DWARFLinkerDeclContext.h" @@ -21,6 +22,7 @@ #include "llvm/Remarks/RemarkFormat.h" #include "llvm/Remarks/RemarkLinker.h" #include +#include namespace llvm { namespace dsymutil { @@ -67,33 +69,29 @@ /// Keeps track of relocations. template class AddressManager : public AddressesMapBase { - struct ValidReloc { - uint64_t Offset; - uint32_t Size; - uint64_t Addend; - const DebugMapObject::DebugMapEntry *Mapping; - - ValidReloc(uint64_t Offset, uint32_t Size, uint64_t Addend, - const DebugMapObject::DebugMapEntry *Mapping) - : Offset(Offset), Size(Size), Addend(Addend), Mapping(Mapping) {} - - bool operator<(const ValidReloc &RHS) const { - return Offset < RHS.Offset; - } - bool operator<(uint64_t RHS) const { return Offset < RHS; } - }; const DwarfLinkerForBinary &Linker; /// The valid relocations for the current DebugMapObject. - /// This vector is sorted by relocation offset. + /// These vectors are sorted by relocation offset. + /// When auxiliary data is cleaned up in order to work on the next + /// objects, their content is stored in different permanent vectors. + /// Those additional vectors contain the relocations for + /// all the objects, and will be serialied into the RelocationMap. /// { std::vector ValidDebugInfoRelocs; std::vector ValidDebugAddrRelocs; + + std::vector StoredValidDebugInfoRelocs; + std::vector StoredValidDebugAddrRelocs; /// } StringRef SrcFileName; + uint8_t DebugMapObjectType; + + std::optional LibInstallName; + /// Returns list of valid relocations from \p Relocs, /// between \p StartOffset and \p NextOffset. /// @@ -116,8 +114,29 @@ public: AddressManager(DwarfLinkerForBinary &Linker, const object::ObjectFile &Obj, const DebugMapObject &DMO) - : Linker(Linker), SrcFileName(DMO.getObjectFilename()) { - findValidRelocsInDebugSections(Obj, DMO); + : Linker(Linker), SrcFileName(DMO.getObjectFilename()), + DebugMapObjectType(MachO::N_OSO) { + if (DMO.getRelocationMap().has_value()) { + DebugMapObjectType = MachO::N_LIB; + LibInstallName.emplace(DMO.getInstallName().value()); + const RelocationMap &RM = DMO.getRelocationMap().value(); + for (const auto &Reloc : RM.relocations()) { + const auto *DebugMapEntry = DMO.lookupSymbol(Reloc.SymbolName); + if (!DebugMapEntry) + continue; + std::optional ObjAddress; + ObjAddress.emplace( + (uint64_t)DebugMapEntry->getValue().ObjectAddress.value()); + ValidDebugInfoRelocs.emplace_back( + Reloc.OffsetInCU, Reloc.Offset, Reloc.Size, Reloc.Addend, + Reloc.SymbolName, + SymbolMapping(ObjAddress, DebugMapEntry->getValue().BinaryAddress, + DebugMapEntry->getValue().Size)); + // FIXME: Support relocations debug_addr. + } + } else { + findValidRelocsInDebugSections(Obj, DMO); + } } ~AddressManager() override { clear(); } @@ -158,8 +177,13 @@ std::optional getSubprogramRelocAdjustment(const DWARFDie &DIE) override; + std::optional getFileName() override; + bool applyValidRelocs(MutableArrayRef Data, uint64_t BaseOffset, - bool IsLittleEndian) override; + bool IsLittleEndian, uint64_t OutOffset, + CompileUnit *CU) override; + + void addValidRelocs(RelocMap *RM) override; void clear() override { ValidDebugInfoRelocs.clear(); @@ -180,7 +204,6 @@ /// Attempt to load a debug object from disk. ErrorOr loadObject(const DebugMapObject &Obj, const Triple &triple); - template ErrorOr> loadObject(const DebugMapObject &Obj, const DebugMap &DebugMap, @@ -207,6 +230,11 @@ bool linkImpl(const DebugMap &Map, typename Linker::OutputFileType ObjectType); + template + Error emitRelocations( + const DebugMap &DM, + std::vector> &ObjectsForLinking); + raw_fd_ostream &OutFile; BinaryHolder &BinHolder; LinkOptions Options; Index: llvm/tools/dsymutil/DwarfLinkerForBinary.cpp =================================================================== --- llvm/tools/dsymutil/DwarfLinkerForBinary.cpp +++ llvm/tools/dsymutil/DwarfLinkerForBinary.cpp @@ -184,6 +184,44 @@ return createFileError(FE->getFileName(), std::move(NewE)); } +template +Error DwarfLinkerForBinary::emitRelocations( + const DebugMap &DM, + std::vector> &ObjectsForLinking) { + // Return early if the "Resources" directory is not being written to. + if (!Options.ResourceDir) + return Error::success(); + + RelocationMap RM(DM.getTriple(), DM.getBinaryPath()); + for (const auto &Obj : ObjectsForLinking) { + if (!Obj->Addresses) + continue; + Obj->Addresses->addValidRelocs(static_cast(&RM)); + } + + SmallString<128> InputPath; + SmallString<128> Path; + // Create the "Relocations" directory in the "Resources" directory, and + // create an architecture-specific directory in the "Relocations" directory. + StringRef ArchName = Triple::getArchName(RM.getTriple().getArch(), + RM.getTriple().getSubArch()); + sys::path::append(Path, *Options.ResourceDir, "Relocations", ArchName); + if (std::error_code EC = sys::fs::create_directories(Path.str(), true, + sys::fs::perms::all_all)) + return errorCodeToError(EC); + + // Append the file name. + sys::path::append(Path, sys::path::filename(DM.getBinaryPath())); + Path.append(".yml"); + + std::error_code EC; + raw_fd_ostream OS(Path.str(), EC, sys::fs::OF_Text); + if (EC) + return errorCodeToError(EC); + + RM.print(OS); + return Error::success(); +} static Error emitRemarks(const LinkOptions &Options, StringRef BinaryPath, StringRef ArchName, const remarks::RemarkLinker &RL) { @@ -795,6 +833,10 @@ if (Options.NoOutput) return true; + if (Error E = + emitRelocations(Map, ObjectsForLinking)) + return error(toString(std::move(E))); + if (Options.ResourceDir && !ParseableSwiftInterfaces.empty()) { StringRef ArchName = Triple::getArchTypeName(Map.getTriple().getArch()); if (auto E = copySwiftInterfaces(ArchName)) @@ -883,12 +925,14 @@ continue; } if (const auto *Mapping = DMO.lookupSymbol(*SymbolName)) - ValidRelocs.emplace_back(Offset64, RelocSize, Addend, Mapping); + ValidRelocs.emplace_back(Offset64, Offset64, RelocSize, Addend, + Mapping->getKey(), Mapping->getValue()); } else if (const auto *Mapping = DMO.lookupObjectAddress(SymAddress)) { // Do not store the addend. The addend was the address of the symbol in // the object file, the address in the binary that is stored in the debug // map doesn't need to be offset. - ValidRelocs.emplace_back(Offset64, RelocSize, SymOffset, Mapping); + ValidRelocs.emplace_back(Offset64, Offset64, RelocSize, SymOffset, + Mapping->getKey(), Mapping->getValue()); } } } @@ -946,20 +990,17 @@ } template -std::vector< - typename DwarfLinkerForBinary::AddressManager::ValidReloc> +std::vector DwarfLinkerForBinary::AddressManager::getRelocations( const std::vector &Relocs, uint64_t StartPos, uint64_t EndPos) { - std::vector< - DwarfLinkerForBinary::AddressManager::ValidReloc> - Res; + std::vector Res; auto CurReloc = partition_point(Relocs, [StartPos](const ValidReloc &Reloc) { - return Reloc.Offset < StartPos; + return (uint64_t)Reloc.Offset < StartPos; }); while (CurReloc != Relocs.end() && CurReloc->Offset >= StartPos && - CurReloc->Offset < EndPos) { + (uint64_t)CurReloc->Offset < EndPos) { Res.push_back(*CurReloc); CurReloc++; } @@ -970,12 +1011,12 @@ template void DwarfLinkerForBinary::AddressManager::printReloc( const ValidReloc &Reloc) { - const auto &Mapping = Reloc.Mapping->getValue(); + const auto &Mapping = Reloc.SymbolMapping; const uint64_t ObjectAddress = Mapping.ObjectAddress ? uint64_t(*Mapping.ObjectAddress) : std::numeric_limits::max(); - outs() << "Found valid debug map entry: " << Reloc.Mapping->getKey() << "\t" + outs() << "Found valid debug map entry: " << Reloc.SymbolName << "\t" << format("0x%016" PRIx64 " => 0x%016" PRIx64 "\n", ObjectAddress, uint64_t(Mapping.BinaryAddress)); } @@ -984,8 +1025,8 @@ int64_t DwarfLinkerForBinary::AddressManager::getRelocValue( const ValidReloc &Reloc) { int64_t AddrAdjust = relocate(Reloc); - if (Reloc.Mapping->getValue().ObjectAddress) - AddrAdjust -= uint64_t(*Reloc.Mapping->getValue().ObjectAddress); + if (Reloc.SymbolMapping.ObjectAddress) + AddrAdjust -= uint64_t(*Reloc.SymbolMapping.ObjectAddress); return AddrAdjust; } @@ -1094,10 +1135,24 @@ } } +template +std::optional +DwarfLinkerForBinary::AddressManager::getFileName() { + return LibInstallName; +} + template uint64_t DwarfLinkerForBinary::AddressManager::relocate( const ValidReloc &Reloc) const { - return Reloc.Mapping->getValue().BinaryAddress + Reloc.Addend; + return Reloc.SymbolMapping.BinaryAddress + Reloc.Addend; +} +template +void DwarfLinkerForBinary::AddressManager::addValidRelocs( + RelocMap *RM) { + for (const auto &InfoReloc : StoredValidDebugInfoRelocs) { + RM->addRelocationMapEntry(dyn_cast(&InfoReloc)); + } + // FIXME: Support relocations debug_addr (DWARF5). } /// Apply the valid relocations found by findValidRelocs() to @@ -1110,7 +1165,15 @@ /// \returns whether any reloc has been applied. template bool DwarfLinkerForBinary::AddressManager::applyValidRelocs( - MutableArrayRef Data, uint64_t BaseOffset, bool IsLittleEndian) { + MutableArrayRef Data, uint64_t BaseOffset, bool IsLittleEndian, + uint64_t OutOffset, llvm::CompileUnit *CU) { + + // Reflect DIEs removed during transformation from .o to .dSYM with a slide. + int64_t RelocSlide = + LibInstallName->empty() ? (int64_t)OutOffset - (int64_t)BaseOffset : 0; + // Reflect displacement due to DW_AT_APPLE_origin attribute in output file. + RelocSlide += CU->addedOriginObject() ? 4 : 0; + std::vector Relocs = getRelocations( ValidDebugInfoRelocs, BaseOffset, BaseOffset + Data.size()); @@ -1125,8 +1188,13 @@ } assert(CurReloc.Size <= sizeof(Buf)); memcpy(&Data[CurReloc.Offset - BaseOffset], Buf, CurReloc.Size); - } + // store relocation values to be serialized + uint64_t SlidOffset = (int64_t)CurReloc.OffsetInCU + RelocSlide; + StoredValidDebugInfoRelocs.emplace_back( + SlidOffset, CU->getStartOffset() + SlidOffset, CurReloc.Size, + CurReloc.Addend, CurReloc.SymbolName, CurReloc.SymbolMapping); + } return Relocs.size() > 0; } Index: llvm/tools/dsymutil/LinkUtils.h =================================================================== --- llvm/tools/dsymutil/LinkUtils.h +++ llvm/tools/dsymutil/LinkUtils.h @@ -93,6 +93,12 @@ llvm::IntrusiveRefCntPtr VFS = vfs::getRealFileSystem(); + /// -build-variant-suffix. + std::string BuildVariantSuffix; + + /// Paths where to search for the .dSYM files of merged libraries. + std::vector DSYMSearchPaths; + /// Fields used for linking and placing remarks into the .dSYM bundle. /// @{ Index: llvm/tools/dsymutil/MachODebugMapParser.cpp =================================================================== --- llvm/tools/dsymutil/MachODebugMapParser.cpp +++ llvm/tools/dsymutil/MachODebugMapParser.cpp @@ -9,6 +9,7 @@ #include "BinaryHolder.h" #include "DebugMap.h" #include "MachOUtils.h" +#include "RelocationMap.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/SmallSet.h" #include "llvm/Object/MachO.h" @@ -28,10 +29,13 @@ public: MachODebugMapParser(llvm::IntrusiveRefCntPtr VFS, StringRef BinaryPath, ArrayRef Archs, - StringRef PathPrefix = "", + ArrayRef DSYMSearchPaths, + StringRef PathPrefix = "", StringRef VariantSuffix = "", bool PaperTrailWarnings = false, bool Verbose = false) : BinaryPath(std::string(BinaryPath)), Archs(Archs.begin(), Archs.end()), + DSYMSearchPaths(DSYMSearchPaths.begin(), DSYMSearchPaths.end()), PathPrefix(std::string(PathPrefix)), + VariantSuffix(std::string(VariantSuffix)), PaperTrailWarnings(PaperTrailWarnings), BinHolder(VFS, Verbose), CurrentDebugMapObject(nullptr), SkipDebugMapObject(false) {} @@ -49,7 +53,9 @@ private: std::string BinaryPath; SmallVector Archs; + SmallVector DSYMSearchPaths; std::string PathPrefix; + std::string VariantSuffix; bool PaperTrailWarnings; /// Owns the MemoryBuffer for the main binary. @@ -90,6 +96,9 @@ void switchToNewDebugMapObject(StringRef Filename, sys::TimePoint Timestamp); + void + switchToNewLibDebugMapObject(StringRef Filename, + sys::TimePoint Timestamp); void resetParserState(); uint64_t getMainBinarySymbolAddress(StringRef Name); std::vector getMainBinarySymbolNames(uint64_t Value); @@ -186,8 +195,6 @@ /// everything up to add symbols to the new one. void MachODebugMapParser::switchToNewDebugMapObject( StringRef Filename, sys::TimePoint Timestamp) { - addCommonSymbols(); - resetParserState(); SmallString<80> Path(PathPrefix); sys::path::append(Path, Filename); @@ -208,11 +215,138 @@ return; } + addCommonSymbols(); + resetParserState(); + CurrentDebugMapObject = &Result->addDebugMapObject(Path, Timestamp, MachO::N_OSO); + loadCurrentObjectFileSymbols(*Object); } +/// Create a new DebugMapObject of type MachO::N_LIB. +/// This function resets the state of the +/// parser that was referring to the last object file and sets +/// everything up to add symbols to the new one. +void MachODebugMapParser::switchToNewLibDebugMapObject( + StringRef Filename, sys::TimePoint Timestamp) { + + if (DSYMSearchPaths.empty()) { + Warning("no dSYM search path was specified"); + return; + } + + StringRef LeafName = sys::path::filename(Filename); + SmallString<128> VariantLeafName; + SmallString<128> ProductName(LeafName); + + // For Framework.framework/Framework and -build-variant-suffix=_debug, + // look in the following order: + // 1) Framework.framework.dSYM/Contents/Resources/DWARF/Framework_debug + // 2) Framework.framework.dSYM/Contents/Resources/DWARF/Framework + // + // For libName.dylib and -build-variant-suffix=_debug, + // look in the following order: + // 1) libName.dylib.dSYM/Contents/Resources/DWARF/libName_debug.dylib + // 2) libName.dylib.dSYM/Contents/Resources/DWARF/libName.dylib + + size_t libExt = LeafName.rfind(".dylib"); + if (libExt != StringRef::npos) { + if (!VariantSuffix.empty()) { + VariantLeafName.append(LeafName.substr(0, libExt)); + VariantLeafName.append(VariantSuffix); + VariantLeafName.append(".dylib"); + } + } else { + // Expected to be a framework + ProductName.append(".framework"); + if (!VariantSuffix.empty()) { + VariantLeafName.append(LeafName); + VariantLeafName.append(VariantSuffix); + } + } + + for (auto DSYMSearchPath : DSYMSearchPaths) { + SmallString<256> Path(DSYMSearchPath); + SmallString<256> FallbackPath(Path); + + SmallString<256> DSYMPath(ProductName); + DSYMPath.append(".dSYM"); + sys::path::append(DSYMPath, "Contents", "Resources", "DWARF"); + + if (!VariantSuffix.empty()) { + sys::path::append(Path, DSYMPath, VariantLeafName); + sys::path::append(FallbackPath, DSYMPath, LeafName); + } else { + sys::path::append(Path, DSYMPath, LeafName); + } + + auto ObjectEntry = BinHolder.getObjectEntry(Path, Timestamp); + if (!ObjectEntry) { + auto Err = ObjectEntry.takeError(); + Warning("unable to open object file: " + toString(std::move(Err)), + Path.str()); + if (!VariantSuffix.empty()) { + ObjectEntry = BinHolder.getObjectEntry(FallbackPath, Timestamp); + if (!ObjectEntry) { + auto Err = ObjectEntry.takeError(); + Warning("unable to open object file: " + toString(std::move(Err)), + FallbackPath.str()); + continue; + } + Path.assign(FallbackPath); + } else { + continue; + } + } + + auto Object = + ObjectEntry->getObjectAs(Result->getTriple()); + if (!Object) { + auto Err = Object.takeError(); + Warning("unable to open object file: " + toString(std::move(Err)), + Path.str()); + continue; + } + + if (CurrentDebugMapObject && + CurrentDebugMapObject->getType() == MachO::N_LIB && + CurrentDebugMapObject->getObjectFilename().compare(Path.str()) == 0) { + return; + } + + addCommonSymbols(); + resetParserState(); + + CurrentDebugMapObject = + &Result->addDebugMapObject(Path, Timestamp, MachO::N_LIB); + + CurrentDebugMapObject->setInstallName(Filename); + + SmallString<256> RMPath(DSYMSearchPath); + sys::path::append(RMPath, ProductName); + RMPath.append(".dSYM"); + StringRef ArchName = Triple::getArchName(Result->getTriple().getArch(), + Result->getTriple().getSubArch()); + sys::path::append(RMPath, "Contents", "Resources", "Relocations", ArchName); + sys::path::append(RMPath, LeafName); + RMPath.append(".yml"); + const auto &RelocMapPtrOrErr = + RelocationMap::parseYAMLRelocationMap(RMPath, PathPrefix); + if (auto EC = RelocMapPtrOrErr.getError()) { + Warning("cannot parse relocation map file: " + EC.message(), + RMPath.str()); + return; + } + CurrentDebugMapObject->setRelocationMap(*RelocMapPtrOrErr->get()); + + loadCurrentObjectFileSymbols(*Object); + + // Found and loaded new dSYM file + return; + } +} + static std::string getArchName(const object::MachOObjectFile &Obj) { Triple T = Obj.getArchTriple(); return std::string(T.getArchName()); @@ -285,23 +419,39 @@ const char *Name; }; -const struct DarwinStabName DarwinStabNames[] = { - {MachO::N_GSYM, "N_GSYM"}, {MachO::N_FNAME, "N_FNAME"}, - {MachO::N_FUN, "N_FUN"}, {MachO::N_STSYM, "N_STSYM"}, - {MachO::N_LCSYM, "N_LCSYM"}, {MachO::N_BNSYM, "N_BNSYM"}, - {MachO::N_PC, "N_PC"}, {MachO::N_AST, "N_AST"}, - {MachO::N_OPT, "N_OPT"}, {MachO::N_RSYM, "N_RSYM"}, - {MachO::N_SLINE, "N_SLINE"}, {MachO::N_ENSYM, "N_ENSYM"}, - {MachO::N_SSYM, "N_SSYM"}, {MachO::N_SO, "N_SO"}, - {MachO::N_OSO, "N_OSO"}, {MachO::N_LSYM, "N_LSYM"}, - {MachO::N_BINCL, "N_BINCL"}, {MachO::N_SOL, "N_SOL"}, - {MachO::N_PARAMS, "N_PARAM"}, {MachO::N_VERSION, "N_VERS"}, - {MachO::N_OLEVEL, "N_OLEV"}, {MachO::N_PSYM, "N_PSYM"}, - {MachO::N_EINCL, "N_EINCL"}, {MachO::N_ENTRY, "N_ENTRY"}, - {MachO::N_LBRAC, "N_LBRAC"}, {MachO::N_EXCL, "N_EXCL"}, - {MachO::N_RBRAC, "N_RBRAC"}, {MachO::N_BCOMM, "N_BCOMM"}, - {MachO::N_ECOMM, "N_ECOMM"}, {MachO::N_ECOML, "N_ECOML"}, - {MachO::N_LENG, "N_LENG"}, {0, nullptr}}; +const struct DarwinStabName DarwinStabNames[] = {{MachO::N_GSYM, "N_GSYM"}, + {MachO::N_FNAME, "N_FNAME"}, + {MachO::N_FUN, "N_FUN"}, + {MachO::N_STSYM, "N_STSYM"}, + {MachO::N_LCSYM, "N_LCSYM"}, + {MachO::N_BNSYM, "N_BNSYM"}, + {MachO::N_PC, "N_PC"}, + {MachO::N_AST, "N_AST"}, + {MachO::N_OPT, "N_OPT"}, + {MachO::N_RSYM, "N_RSYM"}, + {MachO::N_SLINE, "N_SLINE"}, + {MachO::N_ENSYM, "N_ENSYM"}, + {MachO::N_SSYM, "N_SSYM"}, + {MachO::N_SO, "N_SO"}, + {MachO::N_OSO, "N_OSO"}, + {MachO::N_LIB, "N_LIB"}, + {MachO::N_LSYM, "N_LSYM"}, + {MachO::N_BINCL, "N_BINCL"}, + {MachO::N_SOL, "N_SOL"}, + {MachO::N_PARAMS, "N_PARAM"}, + {MachO::N_VERSION, "N_VERS"}, + {MachO::N_OLEVEL, "N_OLEV"}, + {MachO::N_PSYM, "N_PSYM"}, + {MachO::N_EINCL, "N_EINCL"}, + {MachO::N_ENTRY, "N_ENTRY"}, + {MachO::N_LBRAC, "N_LBRAC"}, + {MachO::N_EXCL, "N_EXCL"}, + {MachO::N_RBRAC, "N_RBRAC"}, + {MachO::N_BCOMM, "N_BCOMM"}, + {MachO::N_ECOMM, "N_ECOMM"}, + {MachO::N_ECOML, "N_ECOML"}, + {MachO::N_LENG, "N_LENG"}, + {0, nullptr}}; static const char *getDarwinStabString(uint8_t NType) { for (unsigned i = 0; DarwinStabNames[i].Name; i++) { @@ -487,13 +637,25 @@ const char *Name = &MainBinaryStrings.data()[StringIndex]; + // An N_LIB entry represents the start of a new library file description. + if (Type == MachO::N_LIB) { + switchToNewLibDebugMapObject(Name, sys::toTimePoint(Value)); + return; + } + // An N_OSO entry represents the start of a new object file description. + // If an N_LIB entry was present, this is parsed only if the library + // dSYM file could not be found. if (Type == MachO::N_OSO) { - if (Duplicates.count(OSO(Name, Value))) { - SkipDebugMapObject = true; - return; + if (!CurrentDebugMapObject || + CurrentDebugMapObject->getType() != MachO::N_LIB) { + if (Duplicates.count(OSO(Name, Value))) { + SkipDebugMapObject = true; + return; + } + switchToNewDebugMapObject(Name, sys::toTimePoint(Value)); } - return switchToNewDebugMapObject(Name, sys::toTimePoint(Value)); + return; } if (SkipDebugMapObject) @@ -704,20 +866,24 @@ llvm::ErrorOr>> parseDebugMap(llvm::IntrusiveRefCntPtr VFS, StringRef InputFile, ArrayRef Archs, - StringRef PrependPath, bool PaperTrailWarnings, bool Verbose, + ArrayRef DSYMSearchPaths, StringRef PrependPath, + StringRef VariantSuffix, bool PaperTrailWarnings, bool Verbose, bool InputIsYAML) { if (InputIsYAML) return DebugMap::parseYAMLDebugMap(InputFile, PrependPath, Verbose); - MachODebugMapParser Parser(VFS, InputFile, Archs, PrependPath, - PaperTrailWarnings, Verbose); + MachODebugMapParser Parser(VFS, InputFile, Archs, DSYMSearchPaths, + PrependPath, VariantSuffix, PaperTrailWarnings, + Verbose); return Parser.parse(); } bool dumpStab(llvm::IntrusiveRefCntPtr VFS, StringRef InputFile, ArrayRef Archs, - StringRef PrependPath) { - MachODebugMapParser Parser(VFS, InputFile, Archs, PrependPath, false); + ArrayRef DSYMSearchPaths, StringRef PrependPath, + StringRef VariantSuffix) { + MachODebugMapParser Parser(VFS, InputFile, Archs, DSYMSearchPaths, + PrependPath, VariantSuffix, false); return Parser.dumpStab(); } } // namespace dsymutil Index: llvm/tools/dsymutil/Options.td =================================================================== --- llvm/tools/dsymutil/Options.td +++ llvm/tools/dsymutil/Options.td @@ -205,3 +205,14 @@ HelpText<"Specify the desired type of DWARF linker. Defaults to 'apple'">, Group; def: Joined<["--", "-"], "linker=">, Alias; + +def build_variant_suffix: Separate<["--", "-"], "build-variant-suffix">, + MetaVarName<"">, + HelpText<"Specify the build variant suffix used to build the executabe file.">, + Group; +def: Joined<["--", "-"], "build-variant-suffix=">, Alias; + +def dsym_search_path: Separate<["-", "--"], "D">, + MetaVarName<"">, + HelpText<"Specify a directory that contain dSYM files to search for.">, + Group; Index: llvm/tools/dsymutil/dsymutil.h =================================================================== --- llvm/tools/dsymutil/dsymutil.h +++ llvm/tools/dsymutil/dsymutil.h @@ -35,13 +35,15 @@ ErrorOr>> parseDebugMap(llvm::IntrusiveRefCntPtr VFS, StringRef InputFile, ArrayRef Archs, - StringRef PrependPath, bool PaperTrailWarnings, bool Verbose, + ArrayRef DSYMSearchPaths, StringRef PrependPath, + StringRef VariantSuffix, bool PaperTrailWarnings, bool Verbose, bool InputIsYAML); /// Dump the symbol table. bool dumpStab(llvm::IntrusiveRefCntPtr VFS, StringRef InputFile, ArrayRef Archs, - StringRef PrependPath = ""); + ArrayRef DSYMSearchPaths, StringRef PrependPath = "", + StringRef VariantSuffix = ""); } // end namespace dsymutil } // end namespace llvm Index: llvm/tools/dsymutil/dsymutil.cpp =================================================================== --- llvm/tools/dsymutil/dsymutil.cpp +++ llvm/tools/dsymutil/dsymutil.cpp @@ -408,6 +408,12 @@ Options.LinkOpts.RemarksKeepAll = !Args.hasArg(OPT_remarks_drop_without_debug); + if (opt::Arg *BuildVariantSuffix = Args.getLastArg(OPT_build_variant_suffix)) + Options.LinkOpts.BuildVariantSuffix = BuildVariantSuffix->getValue(); + + for (auto *SearchPath : Args.filtered(OPT_dsym_search_path)) + Options.LinkOpts.DSYMSearchPaths.push_back(SearchPath->getValue()); + if (Error E = verifyOptions(Options)) return std::move(E); return Options; @@ -665,15 +671,18 @@ // Dump the symbol table for each input file and requested arch if (Options.DumpStab) { if (!dumpStab(Options.LinkOpts.VFS, InputFile, Options.Archs, - Options.LinkOpts.PrependPath)) + Options.LinkOpts.DSYMSearchPaths, + Options.LinkOpts.PrependPath, + Options.LinkOpts.BuildVariantSuffix)) return EXIT_FAILURE; continue; } - auto DebugMapPtrsOrErr = - parseDebugMap(Options.LinkOpts.VFS, InputFile, Options.Archs, - Options.LinkOpts.PrependPath, Options.PaperTrailWarnings, - Options.LinkOpts.Verbose, Options.InputIsYAMLDebugMap); + auto DebugMapPtrsOrErr = parseDebugMap( + Options.LinkOpts.VFS, InputFile, Options.Archs, + Options.LinkOpts.DSYMSearchPaths, Options.LinkOpts.PrependPath, + Options.LinkOpts.BuildVariantSuffix, Options.PaperTrailWarnings, + Options.LinkOpts.Verbose, Options.InputIsYAMLDebugMap); if (auto EC = DebugMapPtrsOrErr.getError()) { WithColor::error() << "cannot parse the debug map for '" << InputFile Index: llvm/tools/llvm-dwarfutil/DebugInfoLinker.cpp =================================================================== --- llvm/tools/llvm-dwarfutil/DebugInfoLinker.cpp +++ llvm/tools/llvm-dwarfutil/DebugInfoLinker.cpp @@ -130,11 +130,16 @@ return std::nullopt; } - bool applyValidRelocs(MutableArrayRef, uint64_t, bool) override { + std::optional getFileName() override { return std::nullopt; } + + bool applyValidRelocs(MutableArrayRef, uint64_t, bool, uint64_t, + CompileUnit *) override { // no need to apply relocations to the linked binary. return false; } + void addValidRelocs(RelocMap *RM) override {} + void clear() override {} protected: Index: llvm/tools/llvm-nm/llvm-nm.cpp =================================================================== --- llvm/tools/llvm-nm/llvm-nm.cpp +++ llvm/tools/llvm-nm/llvm-nm.cpp @@ -561,37 +561,22 @@ const char *Name; }; const struct DarwinStabName DarwinStabNames[] = { - {MachO::N_GSYM, "GSYM"}, - {MachO::N_FNAME, "FNAME"}, - {MachO::N_FUN, "FUN"}, - {MachO::N_STSYM, "STSYM"}, - {MachO::N_LCSYM, "LCSYM"}, - {MachO::N_BNSYM, "BNSYM"}, - {MachO::N_PC, "PC"}, - {MachO::N_AST, "AST"}, - {MachO::N_OPT, "OPT"}, - {MachO::N_RSYM, "RSYM"}, - {MachO::N_SLINE, "SLINE"}, - {MachO::N_ENSYM, "ENSYM"}, - {MachO::N_SSYM, "SSYM"}, - {MachO::N_SO, "SO"}, - {MachO::N_OSO, "OSO"}, - {MachO::N_LSYM, "LSYM"}, - {MachO::N_BINCL, "BINCL"}, - {MachO::N_SOL, "SOL"}, - {MachO::N_PARAMS, "PARAM"}, - {MachO::N_VERSION, "VERS"}, - {MachO::N_OLEVEL, "OLEV"}, - {MachO::N_PSYM, "PSYM"}, - {MachO::N_EINCL, "EINCL"}, - {MachO::N_ENTRY, "ENTRY"}, - {MachO::N_LBRAC, "LBRAC"}, - {MachO::N_EXCL, "EXCL"}, - {MachO::N_RBRAC, "RBRAC"}, - {MachO::N_BCOMM, "BCOMM"}, - {MachO::N_ECOMM, "ECOMM"}, - {MachO::N_ECOML, "ECOML"}, - {MachO::N_LENG, "LENG"}, + {MachO::N_GSYM, "GSYM"}, {MachO::N_FNAME, "FNAME"}, + {MachO::N_FUN, "FUN"}, {MachO::N_STSYM, "STSYM"}, + {MachO::N_LCSYM, "LCSYM"}, {MachO::N_BNSYM, "BNSYM"}, + {MachO::N_PC, "PC"}, {MachO::N_AST, "AST"}, + {MachO::N_OPT, "OPT"}, {MachO::N_RSYM, "RSYM"}, + {MachO::N_SLINE, "SLINE"}, {MachO::N_ENSYM, "ENSYM"}, + {MachO::N_SSYM, "SSYM"}, {MachO::N_SO, "SO"}, + {MachO::N_OSO, "OSO"}, {MachO::N_LIB, "LIB"}, + {MachO::N_LSYM, "LSYM"}, {MachO::N_BINCL, "BINCL"}, + {MachO::N_SOL, "SOL"}, {MachO::N_PARAMS, "PARAM"}, + {MachO::N_VERSION, "VERS"}, {MachO::N_OLEVEL, "OLEV"}, + {MachO::N_PSYM, "PSYM"}, {MachO::N_EINCL, "EINCL"}, + {MachO::N_ENTRY, "ENTRY"}, {MachO::N_LBRAC, "LBRAC"}, + {MachO::N_EXCL, "EXCL"}, {MachO::N_RBRAC, "RBRAC"}, + {MachO::N_BCOMM, "BCOMM"}, {MachO::N_ECOMM, "ECOMM"}, + {MachO::N_ECOML, "ECOML"}, {MachO::N_LENG, "LENG"}, }; static const char *getDarwinStabString(uint8_t NType) {