Skip to content

Commit

Permalink
[dsymutil] Add support for mergeable libraries
Browse files Browse the repository at this point in the history
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
  • Loading branch information
Alpha-10000 authored and JDevlieghere committed Oct 24, 2023
1 parent 4c28e66 commit 122c89b
Show file tree
Hide file tree
Showing 34 changed files with 1,217 additions and 198 deletions.
15 changes: 15 additions & 0 deletions llvm/docs/CommandGuide/dsymutil.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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 differently. 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.
Expand Down
1 change: 1 addition & 0 deletions llvm/include/llvm/BinaryFormat/Dwarf.def
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions llvm/include/llvm/BinaryFormat/MachO.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
23 changes: 23 additions & 0 deletions llvm/include/llvm/DWARFLinker/DWARFLinker.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,13 +62,33 @@ 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.
///
/// \returns true whether any reloc has been applied.
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;
};
Expand Down Expand Up @@ -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;
};

Expand Down
18 changes: 18 additions & 0 deletions llvm/include/llvm/DWARFLinkerParallel/AddressesMap.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,13 +55,31 @@ 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.
///
/// \returns true whether any reloc has been applied.
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;

Expand Down
6 changes: 3 additions & 3 deletions llvm/include/llvm/TargetParser/Triple.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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().
Expand Down
61 changes: 57 additions & 4 deletions llvm/lib/DWARFLinker/DWARFLinker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -1720,24 +1735,51 @@ 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,
U.getFormParams());
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.
Expand Down Expand Up @@ -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.
Expand Down
58 changes: 30 additions & 28 deletions llvm/lib/TargetParser/Triple.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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>
Original file line number Diff line number Diff line change
@@ -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 }
...
Original file line number Diff line number Diff line change
@@ -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>
Original file line number Diff line number Diff line change
@@ -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 }
...
Original file line number Diff line number Diff line change
@@ -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>

0 comments on commit 122c89b

Please sign in to comment.