[llvm] 122c89b - [dsymutil] Add support for mergeable libraries

Jonas Devlieghere via llvm-commits llvm-commits at lists.llvm.org
Tue Oct 24 10:39:15 PDT 2023


Author: Alpha Abdoulaye
Date: 2023-10-24T10:39:08-07:00
New Revision: 122c89b271af30b86536cad7bac64ea9c56615ed

URL: https://github.com/llvm/llvm-project/commit/122c89b271af30b86536cad7bac64ea9c56615ed
DIFF: https://github.com/llvm/llvm-project/commit/122c89b271af30b86536cad7bac64ea9c56615ed.diff

LOG: [dsymutil] Add support for mergeable libraries

This adds support in dsymutil for mergeable libraries [1].

dsymutil reads a new stab emitted by ld, allowing it to operate on
dynamic libraries instead of object files. It also now loads the DWARF
files associated to the libraries, and build the debug map for each
binary from the list of symbols exported by the library. For each Debug
Map Object, there is a new associated Relocation Map which is serialized
from the information retrieved in the original debug_info (or
debug_addr) section of the .o file.

The final DWARF file has multiple compile units, so the offsets
information of the relocations are adjusted relatively to the compile
unit they will end up belonging to, inside the final linked DWARF file.

[1] https://developer.apple.com/documentation/xcode/configuring-your-project-to-use-mergeable-libraries

Differential revision: https://reviews.llvm.org/D158124

Added: 
    llvm/test/tools/dsymutil/Inputs/bar-relink-variant.dylib.dSYM/Contents/Info.plist
    llvm/test/tools/dsymutil/Inputs/bar-relink-variant.dylib.dSYM/Contents/Resources/Relocations/aarch64/bar-relink-variant.dylib.yml
    llvm/test/tools/dsymutil/Inputs/bar-relink.dylib.dSYM/Contents/Info.plist
    llvm/test/tools/dsymutil/Inputs/bar-relink.dylib.dSYM/Contents/Resources/Relocations/aarch64/bar-relink.dylib.yml
    llvm/test/tools/dsymutil/Inputs/foo-relink-variant.dylib.dSYM/Contents/Info.plist
    llvm/test/tools/dsymutil/Inputs/foo-relink-variant.dylib.dSYM/Contents/Resources/Relocations/aarch64/foo-relink-variant.dylib.yml
    llvm/test/tools/dsymutil/Inputs/foo-relink.dylib.dSYM/Contents/Info.plist
    llvm/test/tools/dsymutil/Inputs/foo-relink.dylib.dSYM/Contents/Resources/Relocations/aarch64/foo-relink.dylib.yml
    llvm/test/tools/dsymutil/Inputs/proxy-relink.dylib.dSYM/Contents/Info.plist
    llvm/test/tools/dsymutil/Inputs/proxy-relink.dylib.dSYM/Contents/Resources/Relocations/aarch64/proxy-relink.dylib.yml
    llvm/tools/dsymutil/RelocationMap.cpp
    llvm/tools/dsymutil/RelocationMap.h

Modified: 
    llvm/docs/CommandGuide/dsymutil.rst
    llvm/include/llvm/BinaryFormat/Dwarf.def
    llvm/include/llvm/BinaryFormat/MachO.h
    llvm/include/llvm/DWARFLinker/DWARFLinker.h
    llvm/include/llvm/DWARFLinkerParallel/AddressesMap.h
    llvm/include/llvm/TargetParser/Triple.h
    llvm/lib/DWARFLinker/DWARFLinker.cpp
    llvm/lib/TargetParser/Triple.cpp
    llvm/test/tools/dsymutil/basic-linking.test
    llvm/test/tools/dsymutil/cmdline.test
    llvm/tools/dsymutil/CMakeLists.txt
    llvm/tools/dsymutil/DebugMap.cpp
    llvm/tools/dsymutil/DebugMap.h
    llvm/tools/dsymutil/DwarfLinkerForBinary.cpp
    llvm/tools/dsymutil/DwarfLinkerForBinary.h
    llvm/tools/dsymutil/LinkUtils.h
    llvm/tools/dsymutil/MachODebugMapParser.cpp
    llvm/tools/dsymutil/Options.td
    llvm/tools/dsymutil/dsymutil.cpp
    llvm/tools/dsymutil/dsymutil.h
    llvm/tools/llvm-dwarfutil/DebugInfoLinker.cpp
    llvm/tools/llvm-nm/llvm-nm.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/docs/CommandGuide/dsymutil.rst b/llvm/docs/CommandGuide/dsymutil.rst
index 02243e227a24d4a..df621a429bb5c38 100644
--- a/llvm/docs/CommandGuide/dsymutil.rst
+++ b/llvm/docs/CommandGuide/dsymutil.rst
@@ -32,11 +32,26 @@ OPTIONS
  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 <suffix=buildvariant>
+
+ Specify the build variant suffix used to build the executabe file.
+ There can be multiple variants for the binary of a product, each built
+ slightly 
diff erently. The most common build variants are 'debug' and
+ 'profile'. Setting the DYLD_IMAGE_SUFFIX environment variable will
+ cause dyld to load the specified variant at runtime.
+
 .. 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 <path>
+
+ Specify a directory that contain dSYM files to search for.
+ This is used for mergeable libraries, so dsymutil knows where to look
+ for dSYM files with  debug information about symbols present in those
+ libraries.
+
 .. option:: --fat64
 
  Use a 64-bit header when emitting universal binaries.

diff  --git a/llvm/include/llvm/BinaryFormat/Dwarf.def b/llvm/include/llvm/BinaryFormat/Dwarf.def
index fb328a025773252..d1abb1f361d3edf 100644
--- a/llvm/include/llvm/BinaryFormat/Dwarf.def
+++ b/llvm/include/llvm/BinaryFormat/Dwarf.def
@@ -629,6 +629,7 @@ HANDLE_DW_AT(0x3fec, APPLE_objc_complete_type, 0, APPLE)
 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)

diff  --git a/llvm/include/llvm/BinaryFormat/MachO.h b/llvm/include/llvm/BinaryFormat/MachO.h
index 49991ebe7bfaf27..bef70f869520b78 100644
--- a/llvm/include/llvm/BinaryFormat/MachO.h
+++ b/llvm/include/llvm/BinaryFormat/MachO.h
@@ -373,6 +373,7 @@ enum StabType {
   N_SSYM = 0x60u,
   N_SO = 0x64u,
   N_OSO = 0x66u,
+  N_LIB = 0x68u,
   N_LSYM = 0x80u,
   N_BINCL = 0x82u,
   N_SOL = 0x84u,

diff  --git a/llvm/include/llvm/DWARFLinker/DWARFLinker.h b/llvm/include/llvm/DWARFLinker/DWARFLinker.h
index e5797514165a22b..a5721184a6a2f09 100644
--- a/llvm/include/llvm/DWARFLinker/DWARFLinker.h
+++ b/llvm/include/llvm/DWARFLinker/DWARFLinker.h
@@ -62,6 +62,9 @@ class AddressesMap {
   virtual std::optional<int64_t>
   getSubprogramRelocAdjustment(const DWARFDie &DIE) = 0;
 
+  /// Returns the file name associated to the AddessesMap
+  virtual std::optional<StringRef> getLibraryInstallName() = 0;
+
   /// Apply the valid relocations to the buffer \p Data, taking into
   /// account that Data is at \p BaseOffset in the .debug_info section.
   ///
@@ -69,6 +72,23 @@ class AddressesMap {
   virtual bool applyValidRelocs(MutableArrayRef<char> Data, uint64_t BaseOffset,
                                 bool IsLittleEndian) = 0;
 
+  /// Check if the linker needs to gather and save relocation info.
+  virtual bool needToSaveValidRelocs() = 0;
+
+  /// Update and save original relocations located in between StartOffset and
+  /// EndOffset. LinkedOffset is the value which should be added to the original
+  /// relocation offset to get new relocation offset in linked binary.
+  virtual void updateAndSaveValidRelocs(bool IsDWARF5,
+                                        uint64_t OriginalUnitOffset,
+                                        int64_t LinkedOffset,
+                                        uint64_t StartOffset,
+                                        uint64_t EndOffset) = 0;
+
+  /// Update the valid relocations that used OriginalUnitOffset as the compile
+  /// unit offset, and update their values to reflect OutputUnitOffset.
+  virtual void updateRelocationsWithUnitOffset(uint64_t OriginalUnitOffset,
+                                               uint64_t OutputUnitOffset) = 0;
+
   /// Erases all data.
   virtual void clear() = 0;
 };
@@ -752,6 +772,9 @@ class DWARFLinker {
       /// Is there a DW_AT_str_offsets_base in the CU?
       bool AttrStrOffsetBaseSeen = false;
 
+      /// Is there a DW_AT_APPLE_origin in the CU?
+      bool HasAppleOrigin = false;
+
       AttributesInfo() = default;
     };
 

diff  --git a/llvm/include/llvm/DWARFLinkerParallel/AddressesMap.h b/llvm/include/llvm/DWARFLinkerParallel/AddressesMap.h
index 22fbec20d7d3766..b451fee4e0b723a 100644
--- a/llvm/include/llvm/DWARFLinkerParallel/AddressesMap.h
+++ b/llvm/include/llvm/DWARFLinkerParallel/AddressesMap.h
@@ -55,6 +55,9 @@ class AddressesMap {
   virtual std::optional<int64_t>
   getSubprogramRelocAdjustment(const DWARFDie &DIE) = 0;
 
+  // Returns the library install name associated to the AddessesMap.
+  virtual std::optional<StringRef> getLibraryInstallName() = 0;
+
   /// Apply the valid relocations to the buffer \p Data, taking into
   /// account that Data is at \p BaseOffset in the .debug_info section.
   ///
@@ -62,6 +65,21 @@ class AddressesMap {
   virtual bool applyValidRelocs(MutableArrayRef<char> Data, uint64_t BaseOffset,
                                 bool IsLittleEndian) = 0;
 
+  /// Check if the linker needs to gather and save relocation info.
+  virtual bool needToSaveValidRelocs() = 0;
+
+  /// Update and save relocation values to be serialized
+  virtual void updateAndSaveValidRelocs(bool IsDWARF5,
+                                        uint64_t OriginalUnitOffset,
+                                        int64_t LinkedOffset,
+                                        uint64_t StartOffset,
+                                        uint64_t EndOffset) = 0;
+
+  /// Update the valid relocations that used OriginalUnitOffset as the compile
+  /// unit offset, and update their values to reflect OutputUnitOffset.
+  virtual void updateRelocationsWithUnitOffset(uint64_t OriginalUnitOffset,
+                                               uint64_t OutputUnitOffset) = 0;
+
   /// Erases all data.
   virtual void clear() = 0;
 

diff  --git a/llvm/include/llvm/TargetParser/Triple.h b/llvm/include/llvm/TargetParser/Triple.h
index 53cef0abbe0e139..0f56ac68c851f67 100644
--- a/llvm/include/llvm/TargetParser/Triple.h
+++ b/llvm/include/llvm/TargetParser/Triple.h
@@ -418,9 +418,6 @@ class Triple {
   /// 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;
 
@@ -1118,6 +1115,9 @@ class Triple {
   /// 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().

diff  --git a/llvm/lib/DWARFLinker/DWARFLinker.cpp b/llvm/lib/DWARFLinker/DWARFLinker.cpp
index 2d8360f100c1172..80a4e2adefa6cb6 100644
--- a/llvm/lib/DWARFLinker/DWARFLinker.cpp
+++ b/llvm/lib/DWARFLinker/DWARFLinker.cpp
@@ -1026,6 +1026,15 @@ unsigned DWARFLinker::DIECloner::cloneStringAttribute(DIE &Die,
     StringEntry = DebugLineStrPool.getEntry(*String);
   } else {
     StringEntry = DebugStrPool.getEntry(*String);
+
+    if (AttrSpec.Attr == dwarf::DW_AT_APPLE_origin) {
+      Info.HasAppleOrigin = true;
+      if (std::optional<StringRef> FileName =
+              ObjFile.Addresses->getLibraryInstallName()) {
+        StringEntry = DebugStrPool.getEntry(*FileName);
+      }
+    }
+
     // Update attributes info.
     if (AttrSpec.Attr == dwarf::DW_AT_name)
       Info.Name = StringEntry;
@@ -1637,6 +1646,12 @@ shouldSkipAttribute(bool Update,
   }
 }
 
+struct AttributeLinkedOffsetFixup {
+  int64_t LinkedOffsetFixupVal;
+  uint64_t InputAttrStartOffset;
+  uint64_t InputAttrEndOffset;
+};
+
 DIE *DWARFLinker::DIECloner::cloneDIE(const DWARFDie &InputDIE,
                                       const DWARFFile &File, CompileUnit &Unit,
                                       int64_t PCOffset, uint32_t OutOffset,
@@ -1720,6 +1735,9 @@ DIE *DWARFLinker::DIECloner::cloneDIE(const DWARFDie &InputDIE,
       Flags |= TF_SkipPC;
   }
 
+  std::optional<StringRef> LibraryInstallName =
+      ObjFile.Addresses->getLibraryInstallName();
+  SmallVector<AttributeLinkedOffsetFixup> AttributesFixups;
   for (const auto &AttrSpec : Abbrev->attributes()) {
     if (shouldSkipAttribute(Update, AttrSpec, Flags & TF_SkipPC)) {
       DWARFFormValue::skipValue(AttrSpec.Form, Data, &Offset,
@@ -1727,17 +1745,41 @@ DIE *DWARFLinker::DIECloner::cloneDIE(const DWARFDie &InputDIE,
       continue;
     }
 
+    AttributeLinkedOffsetFixup CurAttrFixup;
+    CurAttrFixup.InputAttrStartOffset = InputDIE.getOffset() + Offset;
+    CurAttrFixup.LinkedOffsetFixupVal =
+        Unit.getStartOffset() + OutOffset - CurAttrFixup.InputAttrStartOffset;
+
     DWARFFormValue Val = AttrSpec.getFormValue();
     uint64_t AttrSize = Offset;
     Val.extractValue(Data, &Offset, U.getFormParams(), &U);
+    CurAttrFixup.InputAttrEndOffset = InputDIE.getOffset() + Offset;
     AttrSize = Offset - AttrSize;
 
-    OutOffset += cloneAttribute(*Die, InputDIE, File, Unit, Val, AttrSpec,
-                                AttrSize, AttrInfo, IsLittleEndian);
+    uint64_t FinalAttrSize =
+        cloneAttribute(*Die, InputDIE, File, Unit, Val, AttrSpec, AttrSize,
+                       AttrInfo, IsLittleEndian);
+    if (FinalAttrSize != 0 && ObjFile.Addresses->needToSaveValidRelocs())
+      AttributesFixups.push_back(CurAttrFixup);
+
+    OutOffset += FinalAttrSize;
   }
 
-  // Look for accelerator entries.
   uint16_t Tag = InputDIE.getTag();
+  // Add the DW_AT_APPLE_origin attribute to Compile Unit die if we have
+  // an install name and the DWARF doesn't have the attribute yet.
+  const bool NeedsAppleOrigin = (Tag == dwarf::DW_TAG_compile_unit) &&
+                                LibraryInstallName.has_value() &&
+                                !AttrInfo.HasAppleOrigin;
+  if (NeedsAppleOrigin) {
+    auto StringEntry = DebugStrPool.getEntry(LibraryInstallName.value());
+    Die->addValue(DIEAlloc, dwarf::Attribute(dwarf::DW_AT_APPLE_origin),
+                  dwarf::DW_FORM_strp, DIEInteger(StringEntry.getOffset()));
+    AttrInfo.Name = StringEntry;
+    OutOffset += 4;
+  }
+
+  // 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.
@@ -1806,8 +1848,19 @@ DIE *DWARFLinker::DIECloner::cloneDIE(const DWARFDie &InputDIE,
   Linker.assignAbbrev(NewAbbrev);
   Die->setAbbrevNumber(NewAbbrev.getNumber());
 
+  uint64_t AbbrevNumberSize = getULEB128Size(Die->getAbbrevNumber());
+
   // Add the size of the abbreviation number to the output offset.
-  OutOffset += getULEB128Size(Die->getAbbrevNumber());
+  OutOffset += AbbrevNumberSize;
+
+  // Update fixups with the size of the abbreviation number
+  for (AttributeLinkedOffsetFixup &F : AttributesFixups)
+    F.LinkedOffsetFixupVal += AbbrevNumberSize;
+
+  for (AttributeLinkedOffsetFixup &F : AttributesFixups)
+    ObjFile.Addresses->updateAndSaveValidRelocs(
+        Unit.getOrigUnit().getVersion() >= 5, Unit.getOrigUnit().getOffset(),
+        F.LinkedOffsetFixupVal, F.InputAttrStartOffset, F.InputAttrEndOffset);
 
   if (!HasChildren) {
     // Update our size.

diff  --git a/llvm/lib/TargetParser/Triple.cpp b/llvm/lib/TargetParser/Triple.cpp
index b9fab469f747607..5d4eb79675f8916 100644
--- a/llvm/lib/TargetParser/Triple.cpp
+++ b/llvm/lib/TargetParser/Triple.cpp
@@ -90,6 +90,36 @@ StringRef Triple::getArchTypeName(ArchType Kind) {
   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:
@@ -1143,34 +1173,6 @@ StringRef Triple::getArchName() const {
   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

diff  --git a/llvm/test/tools/dsymutil/Inputs/bar-relink-variant.dylib.dSYM/Contents/Info.plist b/llvm/test/tools/dsymutil/Inputs/bar-relink-variant.dylib.dSYM/Contents/Info.plist
new file mode 100644
index 000000000000000..14d6272d3f8d97f
--- /dev/null
+++ b/llvm/test/tools/dsymutil/Inputs/bar-relink-variant.dylib.dSYM/Contents/Info.plist
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+	<dict>
+		<key>CFBundleDevelopmentRegion</key>
+		<string>English</string>
+		<key>CFBundleIdentifier</key>
+		<string>com.apple.xcode.dsym.bar-relink-variant.dylib</string>
+		<key>CFBundleInfoDictionaryVersion</key>
+		<string>6.0</string>
+		<key>CFBundlePackageType</key>
+		<string>dSYM</string>
+		<key>CFBundleSignature</key>
+		<string>????</string>
+		<key>CFBundleShortVersionString</key>
+		<string>1.0</string>
+		<key>CFBundleVersion</key>
+		<string>1</string>
+	</dict>
+</plist>

diff  --git a/llvm/test/tools/dsymutil/Inputs/bar-relink-variant.dylib.dSYM/Contents/Resources/Relocations/aarch64/bar-relink-variant.dylib.yml b/llvm/test/tools/dsymutil/Inputs/bar-relink-variant.dylib.dSYM/Contents/Resources/Relocations/aarch64/bar-relink-variant.dylib.yml
new file mode 100644
index 000000000000000..68fd250cdf2a8a7
--- /dev/null
+++ b/llvm/test/tools/dsymutil/Inputs/bar-relink-variant.dylib.dSYM/Contents/Resources/Relocations/aarch64/bar-relink-variant.dylib.yml
@@ -0,0 +1,8 @@
+---
+triple:          'arm64-apple-darwin'
+binary-path:     bar-relink-variant.dylib
+relocations:
+  - { offset: 0x26, size: 0x8, addend: 0x0, symName: _bar, symObjAddr: 0x0, symBinAddr: 0x3FA0, symSize: 0x8 }
+  - { offset: 0x3F, size: 0x8, addend: 0x0, symName: _baz, symObjAddr: 0x8, symBinAddr: 0x4000, symSize: 0x0 }
+  - { offset: 0x4F, size: 0x8, addend: 0x0, symName: _bar, symObjAddr: 0x0, symBinAddr: 0x3FA0, symSize: 0x8 }
+...

diff  --git a/llvm/test/tools/dsymutil/Inputs/bar-relink.dylib.dSYM/Contents/Info.plist b/llvm/test/tools/dsymutil/Inputs/bar-relink.dylib.dSYM/Contents/Info.plist
new file mode 100644
index 000000000000000..37c2a8aebe4c6ff
--- /dev/null
+++ b/llvm/test/tools/dsymutil/Inputs/bar-relink.dylib.dSYM/Contents/Info.plist
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+	<dict>
+		<key>CFBundleDevelopmentRegion</key>
+		<string>English</string>
+		<key>CFBundleIdentifier</key>
+		<string>com.apple.xcode.dsym.bar-relink.dylib</string>
+		<key>CFBundleInfoDictionaryVersion</key>
+		<string>6.0</string>
+		<key>CFBundlePackageType</key>
+		<string>dSYM</string>
+		<key>CFBundleSignature</key>
+		<string>????</string>
+		<key>CFBundleShortVersionString</key>
+		<string>1.0</string>
+		<key>CFBundleVersion</key>
+		<string>1</string>
+	</dict>
+</plist>

diff  --git a/llvm/test/tools/dsymutil/Inputs/bar-relink.dylib.dSYM/Contents/Resources/Relocations/aarch64/bar-relink.dylib.yml b/llvm/test/tools/dsymutil/Inputs/bar-relink.dylib.dSYM/Contents/Resources/Relocations/aarch64/bar-relink.dylib.yml
new file mode 100644
index 000000000000000..d47e5f9e2e8d276
--- /dev/null
+++ b/llvm/test/tools/dsymutil/Inputs/bar-relink.dylib.dSYM/Contents/Resources/Relocations/aarch64/bar-relink.dylib.yml
@@ -0,0 +1,8 @@
+---
+triple:          'arm64-apple-darwin'
+binary-path:     bar-relink.dylib
+relocations:
+  - { offset: 0x26, size: 0x8, addend: 0x0, symName: _bar, symObjAddr: 0x0, symBinAddr: 0x3FA0, symSize: 0x8 }
+  - { offset: 0x3F, size: 0x8, addend: 0x0, symName: _baz, symObjAddr: 0x8, symBinAddr: 0x4000, symSize: 0x0 }
+  - { offset: 0x4F, size: 0x8, addend: 0x0, symName: _bar, symObjAddr: 0x0, symBinAddr: 0x3FA0, symSize: 0x8 }
+...

diff  --git a/llvm/test/tools/dsymutil/Inputs/foo-relink-variant.dylib.dSYM/Contents/Info.plist b/llvm/test/tools/dsymutil/Inputs/foo-relink-variant.dylib.dSYM/Contents/Info.plist
new file mode 100644
index 000000000000000..7e84e95bd3f48cc
--- /dev/null
+++ b/llvm/test/tools/dsymutil/Inputs/foo-relink-variant.dylib.dSYM/Contents/Info.plist
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+	<dict>
+		<key>CFBundleDevelopmentRegion</key>
+		<string>English</string>
+		<key>CFBundleIdentifier</key>
+		<string>com.apple.xcode.dsym.foo-relink-variant.dylib</string>
+		<key>CFBundleInfoDictionaryVersion</key>
+		<string>6.0</string>
+		<key>CFBundlePackageType</key>
+		<string>dSYM</string>
+		<key>CFBundleSignature</key>
+		<string>????</string>
+		<key>CFBundleShortVersionString</key>
+		<string>1.0</string>
+		<key>CFBundleVersion</key>
+		<string>1</string>
+	</dict>
+</plist>

diff  --git a/llvm/test/tools/dsymutil/Inputs/foo-relink-variant.dylib.dSYM/Contents/Resources/Relocations/aarch64/foo-relink-variant.dylib.yml b/llvm/test/tools/dsymutil/Inputs/foo-relink-variant.dylib.dSYM/Contents/Resources/Relocations/aarch64/foo-relink-variant.dylib.yml
new file mode 100644
index 000000000000000..0bc06e9a9e857ac
--- /dev/null
+++ b/llvm/test/tools/dsymutil/Inputs/foo-relink-variant.dylib.dSYM/Contents/Resources/Relocations/aarch64/foo-relink-variant.dylib.yml
@@ -0,0 +1,9 @@
+---
+triple:          'arm64-apple-darwin'
+binary-path:     foo-relink-variant.dylib
+relocations:
+  - { offset: 0x26, size: 0x8, addend: 0x0, symName: _foo, symObjAddr: 0x0, symBinAddr: 0x3F64, symSize: 0x20 }
+  - { offset: 0x33, size: 0x8, addend: 0x0, symName: _foo, symObjAddr: 0x0, symBinAddr: 0x3F64, symSize: 0x20 }
+  - { offset: 0x88, size: 0x8, addend: 0x0, symName: _altfoo, symObjAddr: 0x0, symBinAddr: 0x3F84, symSize: 0x24 }
+  - { offset: 0x95, size: 0x8, addend: 0x0, symName: _altfoo, symObjAddr: 0x0, symBinAddr: 0x3F84, symSize: 0x24 }
+...

diff  --git a/llvm/test/tools/dsymutil/Inputs/foo-relink.dylib.dSYM/Contents/Info.plist b/llvm/test/tools/dsymutil/Inputs/foo-relink.dylib.dSYM/Contents/Info.plist
new file mode 100644
index 000000000000000..e91926013155858
--- /dev/null
+++ b/llvm/test/tools/dsymutil/Inputs/foo-relink.dylib.dSYM/Contents/Info.plist
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+	<dict>
+		<key>CFBundleDevelopmentRegion</key>
+		<string>English</string>
+		<key>CFBundleIdentifier</key>
+		<string>com.apple.xcode.dsym.foo-relink.dylib</string>
+		<key>CFBundleInfoDictionaryVersion</key>
+		<string>6.0</string>
+		<key>CFBundlePackageType</key>
+		<string>dSYM</string>
+		<key>CFBundleSignature</key>
+		<string>????</string>
+		<key>CFBundleShortVersionString</key>
+		<string>1.0</string>
+		<key>CFBundleVersion</key>
+		<string>1</string>
+	</dict>
+</plist>

diff  --git a/llvm/test/tools/dsymutil/Inputs/foo-relink.dylib.dSYM/Contents/Resources/Relocations/aarch64/foo-relink.dylib.yml b/llvm/test/tools/dsymutil/Inputs/foo-relink.dylib.dSYM/Contents/Resources/Relocations/aarch64/foo-relink.dylib.yml
new file mode 100644
index 000000000000000..0db1b30e9d126ee
--- /dev/null
+++ b/llvm/test/tools/dsymutil/Inputs/foo-relink.dylib.dSYM/Contents/Resources/Relocations/aarch64/foo-relink.dylib.yml
@@ -0,0 +1,10 @@
+---
+triple:          'arm64-apple-darwin'
+binary-path:     foo-relink.dylib
+relocations:
+  - { offset: 0x26, size: 0x8, addend: 0x0, symName: _foo, symObjAddr: 0x0, symBinAddr: 0x3F54, symSize: 0x20 }
+  - { offset: 0x33, size: 0x8, addend: 0x0, symName: _foo, symObjAddr: 0x0, symBinAddr: 0x3F54, symSize: 0x20 }
+  - { offset: 0x5B, size: 0x8, addend: 0x0, symName: _foo_unused, symObjAddr: 0x20, symBinAddr: 0x3F74, symSize: 0x8 }
+  - { offset: 0xA1, size: 0x8, addend: 0x0, symName: _altfoo, symObjAddr: 0x0, symBinAddr: 0x3F7C, symSize: 0x24 }
+  - { offset: 0xAE, size: 0x8, addend: 0x0, symName: _altfoo, symObjAddr: 0x0, symBinAddr: 0x3F7C, symSize: 0x24 }
+...

diff  --git a/llvm/test/tools/dsymutil/Inputs/proxy-relink.dylib.dSYM/Contents/Info.plist b/llvm/test/tools/dsymutil/Inputs/proxy-relink.dylib.dSYM/Contents/Info.plist
new file mode 100644
index 000000000000000..425df2f6c841d8a
--- /dev/null
+++ b/llvm/test/tools/dsymutil/Inputs/proxy-relink.dylib.dSYM/Contents/Info.plist
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+	<dict>
+		<key>CFBundleDevelopmentRegion</key>
+		<string>English</string>
+		<key>CFBundleIdentifier</key>
+		<string>com.apple.xcode.dsym.proxy-relink.dylib</string>
+		<key>CFBundleInfoDictionaryVersion</key>
+		<string>6.0</string>
+		<key>CFBundlePackageType</key>
+		<string>dSYM</string>
+		<key>CFBundleSignature</key>
+		<string>????</string>
+		<key>CFBundleShortVersionString</key>
+		<string>1.0</string>
+		<key>CFBundleVersion</key>
+		<string>1</string>
+	</dict>
+</plist>

diff  --git a/llvm/test/tools/dsymutil/Inputs/proxy-relink.dylib.dSYM/Contents/Resources/Relocations/aarch64/proxy-relink.dylib.yml b/llvm/test/tools/dsymutil/Inputs/proxy-relink.dylib.dSYM/Contents/Resources/Relocations/aarch64/proxy-relink.dylib.yml
new file mode 100644
index 000000000000000..44dd0a2342da84b
--- /dev/null
+++ b/llvm/test/tools/dsymutil/Inputs/proxy-relink.dylib.dSYM/Contents/Resources/Relocations/aarch64/proxy-relink.dylib.yml
@@ -0,0 +1,14 @@
+---
+triple:          'arm64-apple-darwin'
+binary-path:     proxy-relink.dylib
+relocations:
+  - { offset: 0x26, size: 0x8, addend: 0x0, symName: _display, symObjAddr: 0x0, symBinAddr: 0x3F1C, symSize: 0x1C }
+  - { offset: 0x41, size: 0x8, addend: 0x0, symName: _display, symObjAddr: 0x0, symBinAddr: 0x3F1C, symSize: 0x1C }
+  - { offset: 0x7C, size: 0x8, addend: 0x0, symName: _bar, symObjAddr: 0x3FA0, symBinAddr: 0x3F38, symSize: 0x8 }
+  - { offset: 0x99, size: 0x8, addend: 0x0, symName: _baz, symObjAddr: 0x4000, symBinAddr: 0x8000, symSize: 0x0 }
+  - { offset: 0xA9, size: 0x8, addend: 0x0, symName: _bar, symObjAddr: 0x3FA0, symBinAddr: 0x3F38, symSize: 0x8 }
+  - { offset: 0xE8, size: 0x8, addend: 0x0, symName: _foo, symObjAddr: 0x3F60, symBinAddr: 0x3F40, symSize: 0x24 }
+  - { offset: 0xF9, size: 0x8, addend: 0x0, symName: _foo, symObjAddr: 0x3F60, symBinAddr: 0x3F40, symSize: 0x24 }
+  - { offset: 0x14E, size: 0x8, addend: 0x0, symName: _altfoo, symObjAddr: 0x3F84, symBinAddr: 0x3F64, symSize: 0x24 }
+  - { offset: 0x15F, size: 0x8, addend: 0x0, symName: _altfoo, symObjAddr: 0x3F84, symBinAddr: 0x3F64, symSize: 0x24 }
+...

diff  --git a/llvm/test/tools/dsymutil/basic-linking.test b/llvm/test/tools/dsymutil/basic-linking.test
index cc521430b0c8715..88cd3293efa2427 100644
--- a/llvm/test/tools/dsymutil/basic-linking.test
+++ b/llvm/test/tools/dsymutil/basic-linking.test
@@ -2,6 +2,9 @@ RUN: dsymutil -no-output -verbose -oso-prepend-path=%p %p/Inputs/basic.macho.x86
 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,122 @@ CHECK-ARCHIVE: Found valid debug map entry: _inc	0x0000000000000070 => 0x0000000
 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: Input compilation unit:
+CHECK-RELINK-NEXT: TAG_compile_unit
+CHECK-RELINK-NOT: TAG
+CHECK-RELINK: AT_name {{.*}}altfoo-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 => 0x0000000000003f10
+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	0x0000000000003f54 => 0x0000000000003f2c
+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	0x0000000000003f74 => 0x0000000000003f4c
+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	0x0000000000003f7c => 0x0000000000003f54
+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	0x0000000000003fa0 => 0x0000000000003f78
+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: DW_AT_APPLE_origin {{.*}}/path/to/foo-relink.dylib
+
+CHECK-RELINK-TWO-NOT: Found valid debug map entry
+CHECK-RELINK-TWO: Found valid debug map entry: _display	0x0000000000003f1c => 0x0000000000003f1c
+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	0x0000000000003f38 => 0x0000000000003f38
+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	0x0000000000003f40 => 0x0000000000003f40
+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	0x0000000000003f64 => 0x0000000000003f64
+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

diff  --git a/llvm/test/tools/dsymutil/cmdline.test b/llvm/test/tools/dsymutil/cmdline.test
index 2317852f3c489e2..36cf3f542695ca4 100644
--- a/llvm/test/tools/dsymutil/cmdline.test
+++ b/llvm/test/tools/dsymutil/cmdline.test
@@ -7,7 +7,9 @@ HELP-NOT: -reverse-iterate
 HELP: Dsymutil Options:
 CHECK: -accelerator
 CHECK: -arch <arch>
+CHECK: -build-variant-suffix <suffix=buildvariant>
 CHECK: -dump-debug-map
+CHECK: -D <path>
 CHECK: -fat64
 CHECK: -flat
 CHECK: -gen-reproducer

diff  --git a/llvm/tools/dsymutil/CMakeLists.txt b/llvm/tools/dsymutil/CMakeLists.txt
index 3cb7594d2fd920d..c612bfd9150c47f 100644
--- a/llvm/tools/dsymutil/CMakeLists.txt
+++ b/llvm/tools/dsymutil/CMakeLists.txt
@@ -30,6 +30,7 @@ add_llvm_tool(dsymutil
   MachODebugMapParser.cpp
   MachOUtils.cpp
   Reproducer.cpp
+  RelocationMap.cpp
   SymbolMap.cpp
 
   DEPENDS

diff  --git a/llvm/tools/dsymutil/DebugMap.cpp b/llvm/tools/dsymutil/DebugMap.cpp
index d4e2c2b2cfac7d2..1950258ce722767 100644
--- a/llvm/tools/dsymutil/DebugMap.cpp
+++ b/llvm/tools/dsymutil/DebugMap.cpp
@@ -45,6 +45,11 @@ DebugMapObject::DebugMapObject(StringRef ObjectFilename,
 bool DebugMapObject::addSymbol(StringRef Name,
                                std::optional<uint64_t> 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)));
 
@@ -53,6 +58,12 @@ bool DebugMapObject::addSymbol(StringRef Name,
   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
@@ -158,8 +169,8 @@ struct MappingTraits<dsymutil::DebugMapObject>::YamlDMO {
   std::vector<dsymutil::DebugMapObject::YAMLSymbolMapping> Entries;
 };
 
-void MappingTraits<std::pair<std::string, DebugMapObject::SymbolMapping>>::
-    mapping(IO &io, std::pair<std::string, DebugMapObject::SymbolMapping> &s) {
+void MappingTraits<std::pair<std::string, SymbolMapping>>::mapping(
+    IO &io, std::pair<std::string, SymbolMapping> &s) {
   io.mapRequired("sym", s.first);
   io.mapOptional("objAddr", s.second.ObjectAddress);
   io.mapRequired("binAddr", s.second.BinaryAddress);
@@ -275,7 +286,13 @@ MappingTraits<dsymutil::DebugMapObject>::YamlDMO::denormalize(IO &IO) {
     }
   }
 
-  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<uint64_t> ObjAddress;

diff  --git a/llvm/tools/dsymutil/DebugMap.h b/llvm/tools/dsymutil/DebugMap.h
index 86cb88d32492d3d..0b2f760dc17a352 100644
--- a/llvm/tools/dsymutil/DebugMap.h
+++ b/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 @@ class DebugMap {
 /// linked binary for all the linked atoms in this object file.
 class DebugMapObject {
 public:
-  struct SymbolMapping {
-    std::optional<yaml::Hex64> ObjectAddress;
-    yaml::Hex64 BinaryAddress;
-    yaml::Hex32 Size;
-
-    SymbolMapping(std::optional<uint64_t> 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<std::string, SymbolMapping>;
   using DebugMapEntry = StringMapEntry<SymbolMapping>;
 
@@ -182,6 +167,16 @@ class DebugMapObject {
   }
   const std::vector<std::string> &getWarnings() const { return Warnings; }
 
+  const std::optional<RelocationMap> &getRelocationMap() const {
+    return RelocationMap;
+  }
+  void setRelocationMap(dsymutil::RelocationMap &RM);
+
+  const std::optional<std::string> &getInstallName() const {
+    return InstallName;
+  }
+  void setInstallName(StringRef IN);
+
   void print(raw_ostream &OS) const;
 #ifndef NDEBUG
   void dump() const;
@@ -200,6 +195,9 @@ class DebugMapObject {
   DenseMap<uint64_t, DebugMapEntry *> AddressToMapping;
   uint8_t Type;
 
+  std::optional<RelocationMap> RelocationMap;
+  std::optional<std::string> InstallName;
+
   std::vector<std::string> Warnings;
 
   /// For YAMLIO support.
@@ -225,10 +223,8 @@ namespace yaml {
 
 using namespace llvm::dsymutil;
 
-template <>
-struct MappingTraits<std::pair<std::string, DebugMapObject::SymbolMapping>> {
-  static void mapping(IO &io,
-                      std::pair<std::string, DebugMapObject::SymbolMapping> &s);
+template <> struct MappingTraits<std::pair<std::string, SymbolMapping>> {
+  static void mapping(IO &io, std::pair<std::string, SymbolMapping> &s);
   static const bool flow = true;
 };
 
@@ -237,12 +233,6 @@ template <> struct MappingTraits<dsymutil::DebugMapObject> {
   static void mapping(IO &io, dsymutil::DebugMapObject &DMO);
 };
 
-template <> struct ScalarTraits<Triple> {
-  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<std::vector<std::unique_ptr<dsymutil::DebugMapObject>>> {
   static size_t

diff  --git a/llvm/tools/dsymutil/DwarfLinkerForBinary.cpp b/llvm/tools/dsymutil/DwarfLinkerForBinary.cpp
index 39776ae5a92000b..a8fea1e2712271d 100644
--- a/llvm/tools/dsymutil/DwarfLinkerForBinary.cpp
+++ b/llvm/tools/dsymutil/DwarfLinkerForBinary.cpp
@@ -189,6 +189,44 @@ static Error remarksErrorHandler(const DebugMapObject &DMO,
 
   return createFileError(FE->getFileName(), std::move(NewE));
 }
+template <typename OutDwarfFile, typename AddressMap>
+Error DwarfLinkerForBinary::emitRelocations(
+    const DebugMap &DM,
+    std::vector<ObjectWithRelocMap<OutDwarfFile>> &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 (auto &Obj : ObjectsForLinking) {
+    if (!Obj.OutRelocs->isInitialized())
+      continue;
+    Obj.OutRelocs->addValidRelocs(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) {
@@ -229,30 +267,31 @@ static Error emitRemarks(const LinkOptions &Options, StringRef BinaryPath,
 }
 
 template <typename OutDWARFFile, typename AddressesMap>
-ErrorOr<std::unique_ptr<OutDWARFFile>>
-DwarfLinkerForBinary::loadObject(const DebugMapObject &Obj,
-                                 const DebugMap &DebugMap,
-                                 remarks::RemarkLinker &RL) {
+ErrorOr<std::unique_ptr<OutDWARFFile>> DwarfLinkerForBinary::loadObject(
+    const DebugMapObject &Obj, const DebugMap &DebugMap,
+    remarks::RemarkLinker &RL,
+    std::shared_ptr<DwarfLinkerForBinaryRelocationMap> DLBRM) {
   auto ErrorOrObj = loadObject(Obj, DebugMap.getTriple());
   std::unique_ptr<OutDWARFFile> Res;
 
   if (ErrorOrObj) {
+    auto Context = DWARFContext::create(
+        *ErrorOrObj, DWARFContext::ProcessDebugRelocations::Process, nullptr,
+        "",
+        [&](Error Err) {
+          handleAllErrors(std::move(Err), [&](ErrorInfoBase &Info) {
+            reportError(Info.message());
+          });
+        },
+        [&](Error Warning) {
+          handleAllErrors(std::move(Warning), [&](ErrorInfoBase &Info) {
+            reportWarning(Info.message());
+          });
+        });
+    DLBRM->init(*Context);
     Res = std::make_unique<OutDWARFFile>(
-        Obj.getObjectFilename(),
-        DWARFContext::create(
-            *ErrorOrObj, DWARFContext::ProcessDebugRelocations::Process,
-            nullptr, "",
-            [&](Error Err) {
-              handleAllErrors(std::move(Err), [&](ErrorInfoBase &Info) {
-                reportError(Info.message());
-              });
-            },
-            [&](Error Warning) {
-              handleAllErrors(std::move(Warning), [&](ErrorInfoBase &Info) {
-                reportWarning(Info.message());
-              });
-            }),
-        std::make_unique<AddressesMap>(*this, *ErrorOrObj, Obj),
+        Obj.getObjectFilename(), std::move(Context),
+        std::make_unique<AddressesMap>(*this, *ErrorOrObj, Obj, DLBRM),
         [&](StringRef FileName) { BinHolder.eraseObjectEntry(FileName); });
 
     Error E = RL.link(*ErrorOrObj);
@@ -614,7 +653,7 @@ template <typename Linker, typename OutDwarfFile, typename AddressMap>
 bool DwarfLinkerForBinary::linkImpl(
     const DebugMap &Map, typename Linker::OutputFileType ObjectType) {
 
-  std::vector<std::unique_ptr<OutDwarfFile>> ObjectsForLinking;
+  std::vector<ObjectWithRelocMap<OutDwarfFile>> ObjectsForLinking;
 
   DebugMap DebugMap(Map.getTriple(), Map.getBinaryPath());
 
@@ -668,10 +707,12 @@ bool DwarfLinkerForBinary::linkImpl(
     auto &Obj = DebugMap.addDebugMapObject(
         Path, sys::TimePoint<std::chrono::seconds>(), MachO::N_OSO);
 
+    auto DLBRelocMap = std::make_shared<DwarfLinkerForBinaryRelocationMap>();
     if (ErrorOr<std::unique_ptr<OutDwarfFile>> ErrorOrObj =
-            loadObject<OutDwarfFile, AddressMap>(Obj, DebugMap, RL)) {
-      ObjectsForLinking.emplace_back(std::move(*ErrorOrObj));
-      return *ObjectsForLinking.back();
+            loadObject<OutDwarfFile, AddressMap>(Obj, DebugMap, RL,
+                                                 DLBRelocMap)) {
+      ObjectsForLinking.emplace_back(std::move(*ErrorOrObj), DLBRelocMap);
+      return *ObjectsForLinking.back().Object;
     } else {
       // Try and emit more helpful warnings by applying some heuristics.
       StringRef ObjFile = ContainerName;
@@ -782,15 +823,18 @@ bool DwarfLinkerForBinary::linkImpl(
       continue;
     }
 
+    auto DLBRelocMap = std::make_shared<DwarfLinkerForBinaryRelocationMap>();
     if (ErrorOr<std::unique_ptr<OutDwarfFile>> ErrorOrObj =
-            loadObject<OutDwarfFile, AddressMap>(*Obj, Map, RL)) {
-      ObjectsForLinking.emplace_back(std::move(*ErrorOrObj));
-      GeneralLinker->addObjectFile(*ObjectsForLinking.back(), Loader,
+            loadObject<OutDwarfFile, AddressMap>(*Obj, Map, RL, DLBRelocMap)) {
+      ObjectsForLinking.emplace_back(std::move(*ErrorOrObj), DLBRelocMap);
+      GeneralLinker->addObjectFile(*ObjectsForLinking.back().Object, Loader,
                                    OnCUDieLoaded);
     } else {
-      ObjectsForLinking.push_back(std::make_unique<OutDwarfFile>(
-          Obj->getObjectFilename(), nullptr, nullptr));
-      GeneralLinker->addObjectFile(*ObjectsForLinking.back());
+      ObjectsForLinking.push_back(
+          {std::make_unique<OutDwarfFile>(Obj->getObjectFilename(), nullptr,
+                                          nullptr),
+           DLBRelocMap});
+      GeneralLinker->addObjectFile(*ObjectsForLinking.back().Object);
     }
   }
 
@@ -815,6 +859,10 @@ bool DwarfLinkerForBinary::linkImpl(
   if (Options.NoOutput)
     return true;
 
+  if (Error E =
+          emitRelocations<OutDwarfFile, AddressMap>(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))
@@ -903,12 +951,14 @@ void DwarfLinkerForBinary::AddressManager<AddressesMapBase>::
         continue;
       }
       if (const auto *Mapping = DMO.lookupSymbol(*SymbolName))
-        ValidRelocs.emplace_back(Offset64, RelocSize, Addend, Mapping);
+        ValidRelocs.emplace_back(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, RelocSize, SymOffset,
+                               Mapping->getKey(), Mapping->getValue());
     }
   }
 }
@@ -966,20 +1016,17 @@ bool DwarfLinkerForBinary::AddressManager<AddressesMapBase>::
 }
 
 template <typename AddressesMapBase>
-std::vector<
-    typename DwarfLinkerForBinary::AddressManager<AddressesMapBase>::ValidReloc>
+std::vector<ValidReloc>
 DwarfLinkerForBinary::AddressManager<AddressesMapBase>::getRelocations(
     const std::vector<ValidReloc> &Relocs, uint64_t StartPos, uint64_t EndPos) {
-  std::vector<
-      DwarfLinkerForBinary::AddressManager<AddressesMapBase>::ValidReloc>
-      Res;
+  std::vector<ValidReloc> 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++;
   }
@@ -990,12 +1037,12 @@ DwarfLinkerForBinary::AddressManager<AddressesMapBase>::getRelocations(
 template <typename AddressesMapBase>
 void DwarfLinkerForBinary::AddressManager<AddressesMapBase>::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<uint64_t>::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));
 }
@@ -1004,8 +1051,8 @@ template <typename AddressesMapBase>
 int64_t DwarfLinkerForBinary::AddressManager<AddressesMapBase>::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;
 }
 
@@ -1116,12 +1163,40 @@ std::optional<int64_t> DwarfLinkerForBinary::AddressManager<
   }
 }
 
+template <typename AddressesMapBase>
+std::optional<StringRef> DwarfLinkerForBinary::AddressManager<
+    AddressesMapBase>::getLibraryInstallName() {
+  return LibInstallName;
+}
+
 template <typename AddressesMapBase>
 uint64_t DwarfLinkerForBinary::AddressManager<AddressesMapBase>::relocate(
     const ValidReloc &Reloc) const {
-  return Reloc.Mapping->getValue().BinaryAddress + Reloc.Addend;
+  return Reloc.SymbolMapping.BinaryAddress + Reloc.Addend;
+}
+
+template <typename AddressesMapBase>
+void DwarfLinkerForBinary::AddressManager<
+    AddressesMapBase>::updateAndSaveValidRelocs(bool IsDWARF5,
+                                                uint64_t OriginalUnitOffset,
+                                                int64_t LinkedOffset,
+                                                uint64_t StartOffset,
+                                                uint64_t EndOffset) {
+  std::vector<ValidReloc> InRelocs =
+      getRelocations(ValidDebugInfoRelocs, StartOffset, EndOffset);
+  if (IsDWARF5)
+    InRelocs = getRelocations(ValidDebugAddrRelocs, StartOffset, EndOffset);
+  DwarfLinkerRelocMap->updateAndSaveValidRelocs(
+      IsDWARF5, InRelocs, OriginalUnitOffset, LinkedOffset);
 }
 
+template <typename AddressesMapBase>
+void DwarfLinkerForBinary::AddressManager<AddressesMapBase>::
+    updateRelocationsWithUnitOffset(uint64_t OriginalUnitOffset,
+                                    uint64_t OutputUnitOffset) {
+  DwarfLinkerRelocMap->updateRelocationsWithUnitOffset(OriginalUnitOffset,
+                                                       OutputUnitOffset);
+}
 /// Apply the valid relocations found by findValidRelocs() to
 /// the buffer \p Data, taking into account that Data is at \p BaseOffset
 /// in the debug_info section.
@@ -1133,6 +1208,7 @@ uint64_t DwarfLinkerForBinary::AddressManager<AddressesMapBase>::relocate(
 template <typename AddressesMapBase>
 bool DwarfLinkerForBinary::AddressManager<AddressesMapBase>::applyValidRelocs(
     MutableArrayRef<char> Data, uint64_t BaseOffset, bool IsLittleEndian) {
+
   std::vector<ValidReloc> Relocs = getRelocations(
       ValidDebugInfoRelocs, BaseOffset, BaseOffset + Data.size());
 
@@ -1148,9 +1224,47 @@ bool DwarfLinkerForBinary::AddressManager<AddressesMapBase>::applyValidRelocs(
     assert(CurReloc.Size <= sizeof(Buf));
     memcpy(&Data[CurReloc.Offset - BaseOffset], Buf, CurReloc.Size);
   }
-
   return Relocs.size() > 0;
 }
 
+void DwarfLinkerForBinaryRelocationMap::init(DWARFContext &Context) {
+  for (const std::unique_ptr<DWARFUnit> &CU : Context.compile_units())
+    StoredValidDebugInfoRelocsMap.insert(
+        std::make_pair(CU->getOffset(), std::vector<ValidReloc>()));
+  // FIXME: Support relocations debug_addr (DWARF5).
+}
+
+void DwarfLinkerForBinaryRelocationMap::addValidRelocs(RelocationMap &RM) {
+  for (const auto &DebugInfoRelocs : StoredValidDebugInfoRelocsMap) {
+    for (const auto &InfoReloc : DebugInfoRelocs.second)
+      RM.addRelocationMapEntry(InfoReloc);
+  }
+  // FIXME: Support relocations debug_addr (DWARF5).
+}
+
+void DwarfLinkerForBinaryRelocationMap::updateRelocationsWithUnitOffset(
+    uint64_t OriginalUnitOffset, uint64_t OutputUnitOffset) {
+  std::vector<ValidReloc> &StoredValidDebugInfoRelocs =
+      StoredValidDebugInfoRelocsMap[OriginalUnitOffset];
+  for (ValidReloc &R : StoredValidDebugInfoRelocs) {
+    R.Offset = (uint64_t)R.Offset + OutputUnitOffset;
+  }
+  // FIXME: Support relocations debug_addr (DWARF5).
+}
+
+void DwarfLinkerForBinaryRelocationMap::updateAndSaveValidRelocs(
+    bool IsDWARF5, std::vector<ValidReloc> &InRelocs, uint64_t UnitOffset,
+    int64_t LinkedOffset) {
+  std::vector<ValidReloc> &OutRelocs =
+      StoredValidDebugInfoRelocsMap[UnitOffset];
+  if (IsDWARF5)
+    OutRelocs = StoredValidDebugAddrRelocsMap[UnitOffset];
+
+  for (ValidReloc &R : InRelocs) {
+    OutRelocs.emplace_back(R.Offset + LinkedOffset, R.Size, R.Addend,
+                           R.SymbolName, R.SymbolMapping);
+  }
+}
+
 } // namespace dsymutil
 } // namespace llvm

diff  --git a/llvm/tools/dsymutil/DwarfLinkerForBinary.h b/llvm/tools/dsymutil/DwarfLinkerForBinary.h
index 230f569a6988c32..328cd9197d0d170 100644
--- a/llvm/tools/dsymutil/DwarfLinkerForBinary.h
+++ b/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,10 +22,48 @@
 #include "llvm/Remarks/RemarkFormat.h"
 #include "llvm/Remarks/RemarkLinker.h"
 #include <mutex>
+#include <optional>
 
 namespace llvm {
 namespace dsymutil {
 
+/// DwarfLinkerForBinaryRelocationMap contains the logic to handle the
+/// relocations and to store them inside an associated RelocationMap.
+class DwarfLinkerForBinaryRelocationMap {
+public:
+  void init(DWARFContext &Context);
+
+  bool isInitialized() {
+    return StoredValidDebugInfoRelocsMap.getMemorySize() != 0;
+  }
+
+  void addValidRelocs(RelocationMap &RM);
+
+  void updateAndSaveValidRelocs(bool IsDWARF5,
+                                std::vector<ValidReloc> &InRelocs,
+                                uint64_t UnitOffset, int64_t LinkedOffset);
+
+  void updateRelocationsWithUnitOffset(uint64_t OriginalUnitOffset,
+                                       uint64_t OutputUnitOffset);
+
+  /// Map compilation unit offset to the valid relocations to store
+  /// @{
+  DenseMap<uint64_t, std::vector<ValidReloc>> StoredValidDebugInfoRelocsMap;
+  DenseMap<uint64_t, std::vector<ValidReloc>> StoredValidDebugAddrRelocsMap;
+  /// @}
+
+  DwarfLinkerForBinaryRelocationMap() = default;
+};
+
+template <typename OutDwarfFile> struct ObjectWithRelocMap {
+  ObjectWithRelocMap(
+      std::unique_ptr<OutDwarfFile> Object,
+      std::shared_ptr<DwarfLinkerForBinaryRelocationMap> OutRelocs)
+      : Object(std::move(Object)), OutRelocs(OutRelocs) {}
+  std::unique_ptr<OutDwarfFile> Object;
+  std::shared_ptr<DwarfLinkerForBinaryRelocationMap> OutRelocs;
+};
+
 /// The core of the Dsymutil Dwarf linking logic.
 ///
 /// The link of the dwarf information from the object files will be
@@ -67,26 +106,11 @@ class DwarfLinkerForBinary {
   /// Keeps track of relocations.
   template <typename AddressesMapBase>
   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.
     /// {
     std::vector<ValidReloc> ValidDebugInfoRelocs;
     std::vector<ValidReloc> ValidDebugAddrRelocs;
@@ -94,6 +118,12 @@ class DwarfLinkerForBinary {
 
     StringRef SrcFileName;
 
+    uint8_t DebugMapObjectType;
+
+    std::shared_ptr<DwarfLinkerForBinaryRelocationMap> DwarfLinkerRelocMap;
+
+    std::optional<std::string> LibInstallName;
+
     /// Returns list of valid relocations from \p Relocs,
     /// between \p StartOffset and \p NextOffset.
     ///
@@ -115,9 +145,29 @@ class DwarfLinkerForBinary {
 
   public:
     AddressManager(DwarfLinkerForBinary &Linker, const object::ObjectFile &Obj,
-                   const DebugMapObject &DMO)
-        : Linker(Linker), SrcFileName(DMO.getObjectFilename()) {
-      findValidRelocsInDebugSections(Obj, DMO);
+                   const DebugMapObject &DMO,
+                   std::shared_ptr<DwarfLinkerForBinaryRelocationMap> DLBRM)
+        : Linker(Linker), SrcFileName(DMO.getObjectFilename()),
+          DebugMapObjectType(MachO::N_OSO), DwarfLinkerRelocMap(DLBRM) {
+      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<uint64_t> ObjAddress;
+          ObjAddress.emplace(DebugMapEntry->getValue().ObjectAddress.value());
+          ValidDebugInfoRelocs.emplace_back(
+              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,9 +208,20 @@ class DwarfLinkerForBinary {
     std::optional<int64_t>
     getSubprogramRelocAdjustment(const DWARFDie &DIE) override;
 
+    std::optional<StringRef> getLibraryInstallName() override;
+
     bool applyValidRelocs(MutableArrayRef<char> Data, uint64_t BaseOffset,
                           bool IsLittleEndian) override;
 
+    bool needToSaveValidRelocs() override { return true; }
+
+    void updateAndSaveValidRelocs(bool IsDWARF5, uint64_t OriginalUnitOffset,
+                                  int64_t LinkedOffset, uint64_t StartOffset,
+                                  uint64_t EndOffset) override;
+
+    void updateRelocationsWithUnitOffset(uint64_t OriginalUnitOffset,
+                                         uint64_t OutputUnitOffset) override;
+
     void clear() override {
       ValidDebugInfoRelocs.clear();
       ValidDebugAddrRelocs.clear();
@@ -180,11 +241,11 @@ class DwarfLinkerForBinary {
   /// Attempt to load a debug object from disk.
   ErrorOr<const object::ObjectFile &> loadObject(const DebugMapObject &Obj,
                                                  const Triple &triple);
-
   template <typename OutDWARFFile, typename AddressesMap>
-  ErrorOr<std::unique_ptr<OutDWARFFile>> loadObject(const DebugMapObject &Obj,
-                                                    const DebugMap &DebugMap,
-                                                    remarks::RemarkLinker &RL);
+  ErrorOr<std::unique_ptr<OutDWARFFile>>
+  loadObject(const DebugMapObject &Obj, const DebugMap &DebugMap,
+             remarks::RemarkLinker &RL,
+             std::shared_ptr<DwarfLinkerForBinaryRelocationMap> DLBRM);
 
   void collectRelocationsToApplyToSwiftReflectionSections(
       const object::SectionRef &Section, StringRef &Contents,
@@ -207,6 +268,11 @@ class DwarfLinkerForBinary {
   bool linkImpl(const DebugMap &Map,
                 typename Linker::OutputFileType ObjectType);
 
+  template <typename OutDwarfFile, typename AddressMap>
+  Error emitRelocations(
+      const DebugMap &DM,
+      std::vector<ObjectWithRelocMap<OutDwarfFile>> &ObjectsForLinking);
+
   raw_fd_ostream &OutFile;
   BinaryHolder &BinHolder;
   LinkOptions Options;

diff  --git a/llvm/tools/dsymutil/LinkUtils.h b/llvm/tools/dsymutil/LinkUtils.h
index 88c17d503689951..0bf6d9aac1a3f3b 100644
--- a/llvm/tools/dsymutil/LinkUtils.h
+++ b/llvm/tools/dsymutil/LinkUtils.h
@@ -93,6 +93,12 @@ struct LinkOptions {
   llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS =
       vfs::getRealFileSystem();
 
+  /// -build-variant-suffix.
+  std::string BuildVariantSuffix;
+
+  /// Paths where to search for the .dSYM files of merged libraries.
+  std::vector<std::string> DSYMSearchPaths;
+
   /// Fields used for linking and placing remarks into the .dSYM bundle.
   /// @{
 

diff  --git a/llvm/tools/dsymutil/MachODebugMapParser.cpp b/llvm/tools/dsymutil/MachODebugMapParser.cpp
index d9bf2301e21e55f..9623b71714582d5 100644
--- a/llvm/tools/dsymutil/MachODebugMapParser.cpp
+++ b/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,9 +29,13 @@ class MachODebugMapParser {
 public:
   MachODebugMapParser(llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS,
                       StringRef BinaryPath, ArrayRef<std::string> Archs,
-                      StringRef PathPrefix = "", bool Verbose = false)
+                      ArrayRef<std::string> DSYMSearchPaths,
+                      StringRef PathPrefix = "", StringRef VariantSuffix = "",
+                      bool Verbose = false)
       : BinaryPath(std::string(BinaryPath)), Archs(Archs.begin(), Archs.end()),
-        PathPrefix(std::string(PathPrefix)), BinHolder(VFS, Verbose),
+        DSYMSearchPaths(DSYMSearchPaths.begin(), DSYMSearchPaths.end()),
+        PathPrefix(std::string(PathPrefix)),
+        VariantSuffix(std::string(VariantSuffix)), BinHolder(VFS, Verbose),
         CurrentDebugMapObject(nullptr), SkipDebugMapObject(false) {}
 
   /// Parses and returns the DebugMaps of the input binary. The binary contains
@@ -47,7 +52,9 @@ class MachODebugMapParser {
 private:
   std::string BinaryPath;
   SmallVector<StringRef, 1> Archs;
+  SmallVector<StringRef, 1> DSYMSearchPaths;
   std::string PathPrefix;
+  std::string VariantSuffix;
 
   /// Owns the MemoryBuffer for the main binary.
   BinaryHolder BinHolder;
@@ -87,6 +94,9 @@ class MachODebugMapParser {
   void
   switchToNewDebugMapObject(StringRef Filename,
                             sys::TimePoint<std::chrono::seconds> Timestamp);
+  void
+  switchToNewLibDebugMapObject(StringRef Filename,
+                               sys::TimePoint<std::chrono::seconds> Timestamp);
   void resetParserState();
   uint64_t getMainBinarySymbolAddress(StringRef Name);
   std::vector<StringRef> getMainBinarySymbolNames(uint64_t Value);
@@ -176,8 +186,6 @@ void MachODebugMapParser::addCommonSymbols() {
 /// everything up to add symbols to the new one.
 void MachODebugMapParser::switchToNewDebugMapObject(
     StringRef Filename, sys::TimePoint<std::chrono::seconds> Timestamp) {
-  addCommonSymbols();
-  resetParserState();
 
   SmallString<80> Path(PathPrefix);
   sys::path::append(Path, Filename);
@@ -198,11 +206,138 @@ void MachODebugMapParser::switchToNewDebugMapObject(
     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<std::chrono::seconds> 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<MachOObjectFile>(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());
@@ -275,23 +410,39 @@ struct DarwinStabName {
   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++) {
@@ -477,13 +628,25 @@ void MachODebugMapParser::handleStabSymbolTableEntry(
 
   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)
@@ -694,18 +857,23 @@ namespace dsymutil {
 llvm::ErrorOr<std::vector<std::unique_ptr<DebugMap>>>
 parseDebugMap(llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS,
               StringRef InputFile, ArrayRef<std::string> Archs,
-              StringRef PrependPath, bool Verbose, bool InputIsYAML) {
+              ArrayRef<std::string> DSYMSearchPaths, StringRef PrependPath,
+              StringRef VariantSuffix, bool Verbose, bool InputIsYAML) {
   if (InputIsYAML)
     return DebugMap::parseYAMLDebugMap(InputFile, PrependPath, Verbose);
 
-  MachODebugMapParser Parser(VFS, InputFile, Archs, PrependPath, Verbose);
+  MachODebugMapParser Parser(VFS, InputFile, Archs, DSYMSearchPaths,
+                             PrependPath, VariantSuffix, Verbose);
+
   return Parser.parse();
 }
 
 bool dumpStab(llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS,
               StringRef InputFile, ArrayRef<std::string> Archs,
-              StringRef PrependPath) {
-  MachODebugMapParser Parser(VFS, InputFile, Archs, PrependPath, false);
+              ArrayRef<std::string> DSYMSearchPaths, StringRef PrependPath,
+              StringRef VariantSuffix) {
+  MachODebugMapParser Parser(VFS, InputFile, Archs, DSYMSearchPaths,
+                             PrependPath, VariantSuffix, false);
   return Parser.dumpStab();
 }
 } // namespace dsymutil

diff  --git a/llvm/tools/dsymutil/Options.td b/llvm/tools/dsymutil/Options.td
index 79f04fdfb036055..da071341cc01f21 100644
--- a/llvm/tools/dsymutil/Options.td
+++ b/llvm/tools/dsymutil/Options.td
@@ -201,3 +201,14 @@ def linker: Separate<["--", "-"], "linker">,
   HelpText<"Specify the desired type of DWARF linker. Defaults to 'apple'">,
   Group<grp_general>;
 def: Joined<["--", "-"], "linker=">, Alias<linker>;
+
+def build_variant_suffix: Separate<["--", "-"], "build-variant-suffix">,
+  MetaVarName<"<suffix=buildvariant>">,
+  HelpText<"Specify the build variant suffix used to build the executabe file.">,
+  Group<grp_general>;
+def: Joined<["--", "-"], "build-variant-suffix=">, Alias<build_variant_suffix>;
+
+def dsym_search_path: Separate<["-", "--"], "D">,
+  MetaVarName<"<path>">,
+  HelpText<"Specify a directory that contain dSYM files to search for.">,
+  Group<grp_general>;

diff  --git a/llvm/tools/dsymutil/RelocationMap.cpp b/llvm/tools/dsymutil/RelocationMap.cpp
new file mode 100644
index 000000000000000..5921e7c9c249521
--- /dev/null
+++ b/llvm/tools/dsymutil/RelocationMap.cpp
@@ -0,0 +1,92 @@
+//===- tools/dsymutil/RelocationMap.cpp - Relocation map representation---===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "RelocationMap.h"
+
+namespace llvm {
+
+namespace dsymutil {
+
+void RelocationMap::print(raw_ostream &OS) const {
+  yaml::Output yout(OS, /* Ctxt = */ nullptr, /* WrapColumn = */ 0);
+  yout << const_cast<RelocationMap &>(*this);
+}
+
+#ifndef NDEBUG
+void RelocationMap::dump() const { print(errs()); }
+#endif
+
+void RelocationMap::addRelocationMapEntry(const ValidReloc &Relocation) {
+  Relocations.push_back(Relocation);
+}
+
+namespace {
+
+struct YAMLContext {
+  StringRef PrependPath;
+  Triple BinaryTriple;
+};
+
+} // end anonymous namespace
+
+ErrorOr<std::unique_ptr<RelocationMap>>
+RelocationMap::parseYAMLRelocationMap(StringRef InputFile,
+                                      StringRef PrependPath) {
+  auto ErrOrFile = MemoryBuffer::getFileOrSTDIN(InputFile);
+  if (auto Err = ErrOrFile.getError())
+    return Err;
+
+  YAMLContext Ctxt;
+
+  Ctxt.PrependPath = PrependPath;
+
+  std::unique_ptr<RelocationMap> Result;
+  yaml::Input yin((*ErrOrFile)->getBuffer(), &Ctxt);
+  yin >> Result;
+
+  if (auto EC = yin.error())
+    return EC;
+  return std::move(Result);
+}
+
+} // end namespace dsymutil
+
+namespace yaml {
+
+void MappingTraits<dsymutil::ValidReloc>::mapping(IO &io,
+                                                  dsymutil::ValidReloc &VR) {
+  io.mapRequired("offset", VR.Offset);
+  io.mapRequired("size", VR.Size);
+  io.mapRequired("addend", VR.Addend);
+  io.mapRequired("symName", VR.SymbolName);
+  io.mapOptional("symObjAddr", VR.SymbolMapping.ObjectAddress);
+  io.mapRequired("symBinAddr", VR.SymbolMapping.BinaryAddress);
+  io.mapRequired("symSize", VR.SymbolMapping.Size);
+}
+
+void MappingTraits<dsymutil::RelocationMap>::mapping(
+    IO &io, dsymutil::RelocationMap &RM) {
+  io.mapRequired("triple", RM.BinaryTriple);
+  io.mapRequired("binary-path", RM.BinaryPath);
+  if (void *Ctxt = io.getContext())
+    reinterpret_cast<YAMLContext *>(Ctxt)->BinaryTriple = RM.BinaryTriple;
+  io.mapRequired("relocations", RM.Relocations);
+}
+
+void MappingTraits<std::unique_ptr<dsymutil::RelocationMap>>::mapping(
+    IO &io, std::unique_ptr<dsymutil::RelocationMap> &RM) {
+  if (!RM)
+    RM.reset(new RelocationMap());
+  io.mapRequired("triple", RM->BinaryTriple);
+  io.mapRequired("binary-path", RM->BinaryPath);
+  if (void *Ctxt = io.getContext())
+    reinterpret_cast<YAMLContext *>(Ctxt)->BinaryTriple = RM->BinaryTriple;
+  io.mapRequired("relocations", RM->Relocations);
+}
+} // end namespace yaml
+} // end namespace llvm

diff  --git a/llvm/tools/dsymutil/RelocationMap.h b/llvm/tools/dsymutil/RelocationMap.h
new file mode 100644
index 000000000000000..a7d8ce3b122a68f
--- /dev/null
+++ b/llvm/tools/dsymutil/RelocationMap.h
@@ -0,0 +1,160 @@
+//===- tools/dsymutil/RelocationMap.h -------------------------- *- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+/// \file
+///
+/// This file contains the class declaration of the RelocationMap
+/// entity. RelocationMap lists all the relocations of all the
+/// atoms used in the object files linked together to
+/// produce an executable.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_DSYMUTIL_RELOCATIONMAP_H
+#define LLVM_TOOLS_DSYMUTIL_RELOCATIONMAP_H
+
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/iterator_range.h"
+#include "llvm/Support/YAMLTraits.h"
+#include "llvm/TargetParser/Triple.h"
+
+#include <optional>
+#include <string>
+#include <vector>
+
+namespace llvm {
+
+class raw_ostream;
+
+namespace dsymutil {
+
+struct SymbolMapping {
+  std::optional<yaml::Hex64> ObjectAddress;
+  yaml::Hex64 BinaryAddress;
+  yaml::Hex32 Size;
+
+  SymbolMapping(std::optional<uint64_t> ObjectAddr, uint64_t BinaryAddress,
+                uint32_t Size)
+      : BinaryAddress(BinaryAddress), Size(Size) {
+    if (ObjectAddr)
+      ObjectAddress = *ObjectAddr;
+  }
+
+  /// For YAML IO support
+  SymbolMapping() = default;
+};
+
+/// ValidReloc represents one relocation entry described by the RelocationMap.
+/// It contains a list of DWARF relocations to apply to a linked binary.
+class ValidReloc {
+public:
+  yaml::Hex64 Offset;
+  yaml::Hex32 Size;
+  yaml::Hex64 Addend;
+  std::string SymbolName;
+  SymbolMapping SymbolMapping;
+
+  struct SymbolMapping getSymbolMapping() const { return SymbolMapping; }
+
+  ValidReloc(uint64_t Offset, uint32_t Size, uint64_t Addend,
+             StringRef SymbolName, struct SymbolMapping SymbolMapping)
+      : Offset(Offset), Size(Size), Addend(Addend), SymbolName(SymbolName),
+        SymbolMapping(SymbolMapping) {}
+
+  bool operator<(const ValidReloc &RHS) const { return Offset < RHS.Offset; }
+
+  /// For YAMLIO support.
+  ValidReloc() = default;
+};
+
+/// The RelocationMap object stores the list of relocation entries for a binary
+class RelocationMap {
+  Triple BinaryTriple;
+  std::string BinaryPath;
+  using RelocContainer = std::vector<ValidReloc>;
+
+  RelocContainer Relocations;
+
+  /// For YAML IO support.
+  ///@{
+  friend yaml::MappingTraits<std::unique_ptr<RelocationMap>>;
+  friend yaml::MappingTraits<RelocationMap>;
+
+  RelocationMap() = default;
+  ///@}
+
+public:
+  RelocationMap(const Triple &BinaryTriple, StringRef BinaryPath)
+      : BinaryTriple(BinaryTriple), BinaryPath(std::string(BinaryPath)) {}
+
+  using const_iterator = RelocContainer::const_iterator;
+
+  iterator_range<const_iterator> relocations() const {
+    return make_range(begin(), end());
+  }
+
+  const_iterator begin() const { return Relocations.begin(); }
+
+  const_iterator end() const { return Relocations.end(); }
+
+  size_t getNumberOfEntries() const { return Relocations.size(); }
+
+  /// This function adds a ValidReloc to the list owned by this
+  /// relocation map.
+  void addRelocationMapEntry(const ValidReloc &Relocation);
+
+  const Triple &getTriple() const { return BinaryTriple; }
+
+  StringRef getBinaryPath() const { return BinaryPath; }
+
+  void print(raw_ostream &OS) const;
+
+#ifndef NDEBUG
+  void dump() const;
+#endif
+
+  /// Read a relocation map from \a InputFile.
+  static ErrorOr<std::unique_ptr<RelocationMap>>
+  parseYAMLRelocationMap(StringRef InputFile, StringRef PrependPath);
+};
+
+} // end namespace dsymutil
+} // end namespace llvm
+
+LLVM_YAML_IS_SEQUENCE_VECTOR(dsymutil::ValidReloc)
+
+namespace llvm {
+namespace yaml {
+
+using namespace llvm::dsymutil;
+
+template <> struct MappingTraits<dsymutil::ValidReloc> {
+  static void mapping(IO &io, dsymutil::ValidReloc &VR);
+  static const bool flow = true;
+};
+
+template <> struct MappingTraits<dsymutil::RelocationMap> {
+  struct YamlRM;
+  static void mapping(IO &io, dsymutil::RelocationMap &RM);
+};
+
+template <> struct MappingTraits<std::unique_ptr<dsymutil::RelocationMap>> {
+  struct YamlRM;
+  static void mapping(IO &io, std::unique_ptr<dsymutil::RelocationMap> &RM);
+};
+
+template <> struct ScalarTraits<Triple> {
+  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; }
+};
+
+} // end namespace yaml
+} // end namespace llvm
+
+#endif // LLVM_TOOLS_DSYMUTIL_RELOCATIONMAP_H

diff  --git a/llvm/tools/dsymutil/dsymutil.cpp b/llvm/tools/dsymutil/dsymutil.cpp
index 104895b1a90bdaa..2dd123318e00bd0 100644
--- a/llvm/tools/dsymutil/dsymutil.cpp
+++ b/llvm/tools/dsymutil/dsymutil.cpp
@@ -398,6 +398,12 @@ static Expected<DsymutilOptions> getOptions(opt::InputArgList &Args) {
   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;
@@ -670,15 +676,18 @@ int dsymutil_main(int argc, char **argv, const llvm::ToolContext &) {
     // 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.LinkOpts.Verbose,
-                      Options.InputIsYAMLDebugMap);
+    auto DebugMapPtrsOrErr = parseDebugMap(
+        Options.LinkOpts.VFS, InputFile, Options.Archs,
+        Options.LinkOpts.DSYMSearchPaths, Options.LinkOpts.PrependPath,
+        Options.LinkOpts.BuildVariantSuffix, Options.LinkOpts.Verbose,
+        Options.InputIsYAMLDebugMap);
 
     if (auto EC = DebugMapPtrsOrErr.getError()) {
       WithColor::error() << "cannot parse the debug map for '" << InputFile

diff  --git a/llvm/tools/dsymutil/dsymutil.h b/llvm/tools/dsymutil/dsymutil.h
index ddecd8a76c7fc96..5504dd57c7e558a 100644
--- a/llvm/tools/dsymutil/dsymutil.h
+++ b/llvm/tools/dsymutil/dsymutil.h
@@ -35,12 +35,14 @@ namespace dsymutil {
 ErrorOr<std::vector<std::unique_ptr<DebugMap>>>
 parseDebugMap(llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS,
               StringRef InputFile, ArrayRef<std::string> Archs,
-              StringRef PrependPath, bool Verbose, bool InputIsYAML);
+              ArrayRef<std::string> DSYMSearchPaths, StringRef PrependPath,
+              StringRef VariantSuffix, bool Verbose, bool InputIsYAML);
 
 /// Dump the symbol table.
 bool dumpStab(llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS,
               StringRef InputFile, ArrayRef<std::string> Archs,
-              StringRef PrependPath = "");
+              ArrayRef<std::string> DSYMSearchPaths, StringRef PrependPath = "",
+              StringRef VariantSuffix = "");
 
 } // end namespace dsymutil
 } // end namespace llvm

diff  --git a/llvm/tools/llvm-dwarfutil/DebugInfoLinker.cpp b/llvm/tools/llvm-dwarfutil/DebugInfoLinker.cpp
index d97dd7392b0dfd8..02a94596ec7644d 100644
--- a/llvm/tools/llvm-dwarfutil/DebugInfoLinker.cpp
+++ b/llvm/tools/llvm-dwarfutil/DebugInfoLinker.cpp
@@ -132,11 +132,23 @@ class ObjFileAddressMap : public AddressMapBase {
     return std::nullopt;
   }
 
+  std::optional<StringRef> getLibraryInstallName() override {
+    return std::nullopt;
+  }
+
   bool applyValidRelocs(MutableArrayRef<char>, uint64_t, bool) override {
     // no need to apply relocations to the linked binary.
     return false;
   }
 
+  bool needToSaveValidRelocs() override { return false; }
+
+  void updateAndSaveValidRelocs(bool, uint64_t, int64_t, uint64_t,
+                                uint64_t) override {}
+
+  void updateRelocationsWithUnitOffset(uint64_t OriginalUnitOffset,
+                                       uint64_t OutputUnitOffset) override {}
+
   void clear() override {}
 
 protected:

diff  --git a/llvm/tools/llvm-nm/llvm-nm.cpp b/llvm/tools/llvm-nm/llvm-nm.cpp
index e32aa8ab8f5bf50..fede89e9c1167df 100644
--- a/llvm/tools/llvm-nm/llvm-nm.cpp
+++ b/llvm/tools/llvm-nm/llvm-nm.cpp
@@ -561,37 +561,22 @@ struct DarwinStabName {
   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) {


        


More information about the llvm-commits mailing list