--- /dev/null
+RUN: dsymutil -accelerator=Dwarf -oso-prepend-path=%p/.. %p/../Inputs/basic.macho.x86_64 -o %t.dwarf.dSYM
+RUN: dsymutil -accelerator=Apple -oso-prepend-path=%p/.. %p/../Inputs/basic.macho.x86_64 -o %t.apple.dSYM
+
+RUN: llvm-dwarfdump -verify %t.dwarf.dSYM
+RUN: llvm-dwarfdump -verify %t.apple.dSYM
+
+RUN: llvm-dwarfdump -debug-names %t.dwarf.dSYM | FileCheck %s -check-prefix=NAMES -check-prefix=DWARF
+RUN: llvm-dwarfdump -apple-names -apple-namespaces -apple-types %t.apple.dSYM | FileCheck %s -check-prefix=NAMES -check-prefix=APPLE
+
+RUN: dsymutil -update -accelerator=Dwarf %t.apple.dSYM -o %t.dwarf.updated.dSYM
+RUN: dsymutil -update -accelerator=Apple %t.dwarf.dSYM -o %t.apple.updated.dSYM
+
+RUN: llvm-dwarfdump -verify %t.dwarf.updated.dSYM
+RUN: llvm-dwarfdump -verify %t.apple.updated.dSYM
+
+RUN: llvm-dwarfdump -debug-names %t.dwarf.updated.dSYM | FileCheck %s -check-prefix=NAMES -check-prefix=DWARF
+RUN: llvm-dwarfdump -apple-names -apple-namespaces -apple-types %t.apple.updated.dSYM | FileCheck %s -check-prefix=NAMES -check-prefix=APPLE
+
+DWARF: .debug_names contents:
+DWARF: Compilation Unit offsets [
+DWARF: CU[0]
+DWARF: CU[1]
+DWARF: CU[2]
+DWARF: ]
+
+APPLE-DAG: .apple_names contents:
+APPLE-DAG: .apple_types contents:
+APPLE-DAG: .apple_namespaces contents:
+
+NAMES-DAG: "private_int"
+NAMES-DAG: "baz"
+NAMES-DAG: "int"
+NAMES-DAG: "bar"
+NAMES-DAG: "foo"
+NAMES-DAG: "inc"
+NAMES-DAG: "val"
+NAMES-DAG: "main"
+NAMES-DAG: "char"
HELP: Color Options
HELP: -color
HELP: Specific Options:
+HELP: -accelerator
HELP: -arch=<arch>
HELP: -dump-debug-map
HELP: -flat
void DwarfLinker::endDebugObject(LinkContext &Context) {
Context.Clear();
+
for (auto I = DIEBlocks.begin(), E = DIEBlocks.end(); I != E; ++I)
(*I)->~DIEBlock();
for (auto I = DIELocs.begin(), E = DIELocs.end(); I != E; ++I)
}
void DwarfLinker::emitAcceleratorEntriesForUnit(CompileUnit &Unit) {
+ switch (Options.TheAccelTableKind) {
+ case AccelTableKind::Apple:
+ emitAppleAcceleratorEntriesForUnit(Unit);
+ break;
+ case AccelTableKind::Dwarf:
+ emitDwarfAcceleratorEntriesForUnit(Unit);
+ break;
+ case AccelTableKind::Default:
+ llvm_unreachable("The default must be updated to a concrete value.");
+ break;
+ }
+}
+
+void DwarfLinker::emitAppleAcceleratorEntriesForUnit(CompileUnit &Unit) {
// Add namespaces.
for (const auto &Namespace : Unit.getNamespaces())
AppleNamespaces.addName(Namespace.Name,
AppleObjc.addName(ObjC.Name, ObjC.Die->getOffset() + Unit.getStartOffset());
}
+void DwarfLinker::emitDwarfAcceleratorEntriesForUnit(CompileUnit &Unit) {
+ for (const auto &Namespace : Unit.getNamespaces())
+ DebugNames.addName(Namespace.Name, Namespace.Die->getOffset(),
+ Namespace.Die->getTag(), Unit.getUniqueID());
+ for (const auto &Pubname : Unit.getPubnames())
+ DebugNames.addName(Pubname.Name, Pubname.Die->getOffset(),
+ Pubname.Die->getTag(), Unit.getUniqueID());
+ for (const auto &Pubtype : Unit.getPubtypes())
+ DebugNames.addName(Pubtype.Name, Pubtype.Die->getOffset(),
+ Pubtype.Die->getTag(), Unit.getUniqueID());
+}
+
/// Read the frame info stored in the object, and emit the
/// patched frame descriptions for the linked binary.
///
// Setup access to the debug info.
auto DwarfContext = DWARFContext::create(*ErrOrObj);
RelocationManager RelocMgr(*this);
- for (const auto &CU : DwarfContext->compile_units()) {
- maybeUpdateMaxDwarfVersion(CU->getVersion());
+ for (const auto &CU : DwarfContext->compile_units()) {
+ updateDwarfVersion(CU->getVersion());
// Recursively get all modules imported by this one.
auto CUDie = CU->getUnitDIE(false);
if (!CUDie)
}
}
+void DwarfLinker::updateAccelKind(DWARFContext &Dwarf) {
+ if (Options.TheAccelTableKind != AccelTableKind::Default)
+ return;
+
+ auto &DwarfObj = Dwarf.getDWARFObj();
+
+ if (!AtLeastOneDwarfAccelTable &&
+ (!DwarfObj.getAppleNamesSection().Data.empty() ||
+ !DwarfObj.getAppleTypesSection().Data.empty() ||
+ !DwarfObj.getAppleNamespacesSection().Data.empty() ||
+ !DwarfObj.getAppleObjCSection().Data.empty())) {
+ AtLeastOneAppleAccelTable = true;
+ }
+
+ if (!AtLeastOneDwarfAccelTable &&
+ !DwarfObj.getDebugNamesSection().Data.empty()) {
+ AtLeastOneDwarfAccelTable = true;
+ }
+}
+
bool DwarfLinker::emitPaperTrailWarnings(const DebugMapObject &DMO,
const DebugMap &Map,
OffsetsStringPool &StringPool) {
unsigned NumObjects = Map.getNumberOfObjects();
std::vector<LinkContext> ObjectContexts;
ObjectContexts.reserve(NumObjects);
- for (const auto &Obj : Map.objects())
+ for (const auto &Obj : Map.objects()) {
ObjectContexts.emplace_back(Map, *this, *Obj.get());
+ LinkContext &LC = ObjectContexts.back();
+ if (LC.ObjectFile)
+ updateAccelKind(*LC.DwarfContext);
+ }
// This Dwarf string pool which is only used for uniquing. This one should
// never be used for offsets as its not thread-safe or predictable.
// ODR Contexts for the link.
DeclContextTree ODRContexts;
+ // If we haven't decided on an accelerator table kind yet, we base ourselves
+ // on the DWARF we have seen so far. At this point we haven't pulled in debug
+ // information from modules yet, so it is technically possible that they
+ // would affect the decision. However, as they're built with the same
+ // compiler and flags, it is safe to assume that they will follow the
+ // decision made here.
+ if (Options.TheAccelTableKind == AccelTableKind::Default) {
+ if (AtLeastOneDwarfAccelTable && !AtLeastOneAppleAccelTable)
+ Options.TheAccelTableKind = AccelTableKind::Dwarf;
+ else
+ Options.TheAccelTableKind = AccelTableKind::Apple;
+ }
+
for (LinkContext &LinkContext : ObjectContexts) {
if (Options.Verbose)
outs() << "DEBUG MAP OBJECT: " << LinkContext.DMO.getObjectFilename()
// In a first phase, just read in the debug info and load all clang modules.
LinkContext.CompileUnits.reserve(
LinkContext.DwarfContext->getNumCompileUnits());
+
for (const auto &CU : LinkContext.DwarfContext->compile_units()) {
+ updateDwarfVersion(CU->getVersion());
auto CUDie = CU->getUnitDIE(false);
if (Options.Verbose) {
outs() << "Input compilation unit:";
UniquingStringPool, ODRContexts, UnitID)) {
LinkContext.CompileUnits.push_back(llvm::make_unique<CompileUnit>(
*CU, UnitID++, !Options.NoODR && !Options.Update, ""));
- maybeUpdateMaxDwarfVersion(CU->getVersion());
}
}
}
- // If we haven't seen any CUs, pick an arbitrary valid Dwarf version anyway,
- // to be able to emit papertrail warnings.
+ // If we haven't seen any CUs, pick an arbitrary valid Dwarf version anyway.
if (MaxDwarfVersion == 0)
MaxDwarfVersion = 3;
if (!Options.NoOutput) {
Streamer->emitAbbrevs(Abbreviations, MaxDwarfVersion);
Streamer->emitStrings(OffsetsStringPool);
- Streamer->emitAppleNames(AppleNames);
- Streamer->emitAppleNamespaces(AppleNamespaces);
- Streamer->emitAppleTypes(AppleTypes);
- Streamer->emitAppleObjc(AppleObjc);
+ switch (Options.TheAccelTableKind) {
+ case AccelTableKind::Apple:
+ Streamer->emitAppleNames(AppleNames);
+ Streamer->emitAppleNamespaces(AppleNamespaces);
+ Streamer->emitAppleTypes(AppleTypes);
+ Streamer->emitAppleObjc(AppleObjc);
+ break;
+ case AccelTableKind::Dwarf:
+ Streamer->emitDebugNames(DebugNames);
+ break;
+ case AccelTableKind::Default:
+ llvm_unreachable("Default should have already been resolved.");
+ break;
+ }
}
};
}
return Options.NoOutput ? true : Streamer->finish(Map);
-}
+} // namespace dsymutil
bool linkDwarf(raw_fd_ostream &OutFile, BinaryHolder &BinHolder,
const DebugMap &DM, const LinkOptions &Options) {
const DWARFDie *DIE = nullptr) const;
private:
- /// Remembers the newest DWARF version we've seen in a unit.
- void maybeUpdateMaxDwarfVersion(unsigned Version) {
- if (MaxDwarfVersion < Version)
- MaxDwarfVersion = Version;
+ /// Remembers the oldest and newest DWARF version we've seen in a unit.
+ void updateDwarfVersion(unsigned Version) {
+ MaxDwarfVersion = std::max(MaxDwarfVersion, Version);
+ MinDwarfVersion = std::min(MinDwarfVersion, Version);
}
+ /// Remembers the kinds of accelerator tables we've seen in a unit.
+ void updateAccelKind(DWARFContext &Dwarf);
+
/// Emit warnings as Dwarf compile units to leave a trail after linking.
bool emitPaperTrailWarnings(const DebugMapObject &DMO, const DebugMap &Map,
OffsetsStringPool &StringPool);
DwarfContext = ObjectFile ? DWARFContext::create(*ObjectFile) : nullptr;
}
- /// Clear compile units and ranges.
+ /// Clear part of the context that's no longer needed when we're done with
+ /// the debug object.
void Clear() {
+ DwarfContext.reset(nullptr);
CompileUnits.clear();
Ranges.clear();
}
/// Emit the accelerator entries for \p Unit.
void emitAcceleratorEntriesForUnit(CompileUnit &Unit);
+ void emitDwarfAcceleratorEntriesForUnit(CompileUnit &Unit);
+ void emitAppleAcceleratorEntriesForUnit(CompileUnit &Unit);
/// Patch the frame info for an object file and emit it.
void patchFrameInfoForObject(const DebugMapObject &, RangesTy &Ranges,
LinkOptions Options;
std::unique_ptr<DwarfStreamer> Streamer;
uint64_t OutputDebugInfoSize;
+
unsigned MaxDwarfVersion = 0;
+ unsigned MinDwarfVersion = std::numeric_limits<unsigned>::max();
+
+ bool AtLeastOneAppleAccelTable = false;
+ bool AtLeastOneDwarfAccelTable = false;
/// The CIEs that have been emitted in the output section. The actual CIE
/// data serves a the key to this StringMap, this takes care of comparing the
uint32_t LastCIEOffset = 0;
/// Apple accelerator tables.
+ AccelTable<DWARF5AccelTableStaticData> DebugNames;
AccelTable<AppleAccelTableStaticOffsetData> AppleNames;
AccelTable<AppleAccelTableStaticOffsetData> AppleNamespaces;
AccelTable<AppleAccelTableStaticOffsetData> AppleObjc;
//===----------------------------------------------------------------------===//
#include "DwarfStreamer.h"
+#include "CompileUnit.h"
#include "LinkUtils.h"
#include "MachOUtils.h"
#include "llvm/ADT/Triple.h"
// start of the section.
Asm->emitInt32(0);
Asm->emitInt8(Unit.getOrigUnit().getAddressByteSize());
+
+ // Remember this CU.
+ EmittedUnits.push_back({Unit.getUniqueID(), Unit.getLabelBegin()});
}
/// Emit the \p Abbrevs array as the shared abbreviation table
}
}
+void DwarfStreamer::emitDebugNames(
+ AccelTable<DWARF5AccelTableStaticData> &Table) {
+ if (EmittedUnits.empty())
+ return;
+
+ // Build up data structures needed to emit this section.
+ std::vector<MCSymbol *> CompUnits;
+ DenseMap<unsigned, size_t> UniqueIdToCuMap;
+ unsigned Id = 0;
+ for (auto &CU : EmittedUnits) {
+ CompUnits.push_back(CU.LabelBegin);
+ // We might be omitting CUs, so we need to remap them.
+ UniqueIdToCuMap[CU.ID] = Id++;
+ }
+
+ Asm->OutStreamer->SwitchSection(MOFI->getDwarfDebugNamesSection());
+ emitDWARF5AccelTable(
+ Asm.get(), Table, CompUnits,
+ [&UniqueIdToCuMap](const DWARF5AccelTableStaticData &Entry) {
+ return UniqueIdToCuMap[Entry.getCUIndex()];
+ });
+}
+
void DwarfStreamer::emitAppleNamespaces(
AccelTable<AppleAccelTableStaticOffsetData> &Table) {
Asm->OutStreamer->SwitchSection(MOFI->getDwarfAccelNamespaceSection());
#include "llvm/MC/MCSection.h"
#include "llvm/MC/MCStreamer.h"
#include "llvm/MC/MCSubtargetInfo.h"
+#include "llvm/MC/MCSymbol.h"
#include "llvm/MC/MCTargetOptions.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/Target/TargetOptions.h"
void emitFDE(uint32_t CIEOffset, uint32_t AddreSize, uint32_t Address,
StringRef Bytes);
+ /// Emit DWARF debug names.
+ void emitDebugNames(AccelTable<DWARF5AccelTableStaticData> &Table);
+
/// Emit Apple namespaces accelerator table.
void emitAppleNamespaces(AccelTable<AppleAccelTableStaticOffsetData> &Table);
uint32_t LineSectionSize;
uint32_t FrameSectionSize;
+ /// Keep track of emitted CUs and their Unique ID.
+ struct EmittedUnit {
+ unsigned ID;
+ MCSymbol *LabelBegin;
+ };
+ std::vector<EmittedUnit> EmittedUnits;
+
/// Emit the pubnames or pubtypes section contribution for \p
/// Unit into \p Sec. The data is provided in \p Names.
void emitPubSectionForUnit(MCSection *Sec, StringRef Name,
Assembly,
};
+/// The kind of accelerator tables we should emit.
+enum class AccelTableKind {
+ Apple, ///< .apple_names, .apple_namespaces, .apple_types, .apple_objc.
+ Dwarf, ///< DWARF v5 .debug_names.
+ Default, ///< Dwarf for DWARF5 or later, Apple otherwise.
+};
+
struct LinkOptions {
/// Verbosity
bool Verbose = false;
// Output file type.
OutputFileType FileType = OutputFileType::Object;
+ /// The accelerator table kind
+ AccelTableKind TheAccelTableKind;
+
/// -oso-prepend-path
std::string PrependPath;
static opt<bool> Minimize(
"minimize",
- desc("When used when creating a dSYM file, this option will suppress\n"
- "the emission of the .debug_inlines, .debug_pubnames, and\n"
- ".debug_pubtypes sections since dsymutil currently has better\n"
- "equivalents: .apple_names and .apple_types. When used in\n"
+ desc("When used when creating a dSYM file with Apple accelerator tables,\n"
+ "this option will suppress the emission of the .debug_inlines, \n"
+ ".debug_pubnames, and .debug_pubtypes sections since dsymutil \n"
+ "has better equivalents: .apple_names and .apple_types. When used in\n"
"conjunction with --update option, this option will cause redundant\n"
"accelerator tables to be removed."),
init(false), cat(DsymCategory));
static opt<bool> Update(
"update",
desc("Updates existing dSYM files to contain the latest accelerator\n"
- "tables and other DWARF optimizations. This option will currently\n"
- "add the new .apple_names and .apple_types hashed accelerator\n"
- "tables."),
+ "tables and other DWARF optimizations."),
init(false), cat(DsymCategory));
static alias UpdateA("u", desc("Alias for --update"), aliasopt(Update));
+static cl::opt<AccelTableKind> AcceleratorTable(
+ "accelerator", cl::desc("Output accelerator tables."),
+ cl::values(clEnumValN(AccelTableKind::Default, "Default",
+ "Default for input."),
+ clEnumValN(AccelTableKind::Apple, "Apple", "Apple"),
+ clEnumValN(AccelTableKind::Dwarf, "Dwarf", "DWARF")),
+ cl::init(AccelTableKind::Default), cat(DsymCategory));
+
static opt<unsigned> NumThreads(
"num-threads",
desc("Specifies the maximum number (n) of simultaneous threads to use\n"
Options.Update = Update;
Options.NoTimestamp = NoTimestamp;
Options.PrependPath = OsoPrependPath;
+ Options.TheAccelTableKind = AcceleratorTable;
if (Assembly)
Options.FileType = OutputFileType::Assembly;