[dsymutil] Upstream update feature.

Now that dsymutil can generate accelerator tables, we can upstream the
update logic that, as the name implies, updates the accelerator tables
in an existing dSYM bundle. In combination with `-minimize` this can be
used to remove redundant .debug_(inlines|pubtypes|pubnames).

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

llvm-svn: 324480
This commit is contained in:
Jonas Devlieghere 2018-02-07 13:51:29 +00:00
parent c90d79f80a
commit a4b9417b52
8 changed files with 341 additions and 45 deletions

View File

@ -40,7 +40,9 @@ OPTIONS
When used when creating a dSYM file, this option will suppress the emission of
the .debug_inlines, .debug_pubnames, and .debug_pubtypes sections since
dsymutil currently has better equivalents: .apple_names and .apple_types.
dsymutil currently has better equivalents: .apple_names and .apple_types. When
used in conjunction with --update option, this option will cause redundant
accelerator tables to be removed.
.. option:: --no-odr
@ -72,6 +74,12 @@ OPTIONS
Dumps the symbol table found in *executable* or object file(s) and exits.
.. option:: -u, --update
Update an existing dSYM file to contain the latest accelerator tables and
other DWARF optimizations. This option will rebuild the '.apple_names' and
'.apple_types' hashed accelerator tables.
.. option:: -v, --verbose
Display verbose information when linking.

View File

@ -8,6 +8,21 @@ RUN: llvm-dsymutil -f -o - -oso-prepend-path=%p/.. %p/../Inputs/basic-archive.ma
RUN: llvm-dsymutil -dump-debug-map -oso-prepend-path=%p/.. %p/../Inputs/basic.macho.x86_64 | llvm-dsymutil -f -y -o - - | llvm-dwarfdump -a - | FileCheck %s --check-prefix=CHECK --check-prefix=BASIC
RUN: llvm-dsymutil -dump-debug-map -oso-prepend-path=%p/.. %p/../Inputs/basic-archive.macho.x86_64 | llvm-dsymutil -f -o - -y - | llvm-dwarfdump -a - | FileCheck %s --check-prefix=CHECK --check-prefix=ARCHIVE
# Update tests
RUN: rm -rf %t.dir
RUN: mkdir -p %t.dir
RUN: cat %p/../Inputs/basic.macho.x86_64 > %t.dir/basic
RUN: llvm-dsymutil -oso-prepend-path=%p/.. %t.dir/basic
RUN: llvm-dwarfdump -a %t.dir/basic.dSYM/Contents/Resources/DWARF/basic | FileCheck %s
RUN: llvm-dsymutil --update %t.dir/basic.dSYM
RUN: llvm-dwarfdump -a %t.dir/basic.dSYM/Contents/Resources/DWARF/basic | FileCheck %s
RUN: llvm-dsymutil -u %t.dir/basic.dSYM
RUN: llvm-dwarfdump -a %t.dir/basic.dSYM/Contents/Resources/DWARF/basic | FileCheck %s
RUN: llvm-dsymutil --update %t.dir/basic.dSYM -o %t.dir/updated.dSYM
RUN: llvm-dwarfdump -a %t.dir/updated.dSYM/Contents/Resources/DWARF/basic | FileCheck %s
RUN: llvm-dsymutil -f -u %t2 -o %t3
RUN: llvm-dwarfdump -a %t3 | FileCheck %s
CHECK: file format Mach-O 64-bit x86-64
CHECK: debug_info contents

View File

@ -0,0 +1,35 @@
RUN: llvm-dsymutil -oso-prepend-path=%p/.. %p/../Inputs/objc.macho.x86_64 -o %t.dSYM
RUN: llvm-dsymutil -update %t.dSYM
RUN: llvm-dwarfdump -apple-types -apple-objc %t.dSYM/Contents/Resources/DWARF/objc.macho.x86_64 | FileCheck %s
CHECK: .apple_types contents:
CHECK: Hash 0x2b5e6 [
CHECK-NEXT: Name@0x145 {
CHECK-NEXT: String: 0x00000066 "A"
CHECK-NEXT: Data 0 [
CHECK-NEXT: Atom[0]: 0x0000012d
CHECK-NEXT: Atom[1]: 0x0013
CHECK-NEXT: Atom[2]: 0x02
CHECK-NEXT: Atom[3]: 0x0b87b15a
CHECK-NEXT: ]
CHECK-NEXT: }
CHECK-NEXT: ]
CHECK: .apple_objc contents:
CHECK: Hash 0x2b5e6
CHECK-NEXT: Name@0x38 {
CHECK-NEXT: String: 0x00000066 "A"
CHECK-NEXT: Data 0 [
CHECK-NEXT: Atom[0]: 0x00000027
CHECK-NEXT: ]
CHECK-NEXT: Data 1 [
CHECK-NEXT: Atom[0]: 0x0000007a
CHECK-NEXT: ]
CHECK-NEXT: }
CHECK: Hash 0x3fa0f4b5
CHECK-NEXT: Name@0x4c {
CHECK-NEXT: String: 0x0000009d "A(Category)"
CHECK-NEXT: Data 0 [
CHECK-NEXT: Atom[0]: 0x0000007a
CHECK-NEXT: ]
CHECK-NEXT: }

View File

@ -14,6 +14,7 @@ HELP: -num-threads=<n>
HELP: -o=<filename>
HELP: -oso-prepend-path=<path>
HELP: -symtab
HELP: -update
HELP: -verbose
HELP: -verify
HELP: -y

View File

@ -28,6 +28,7 @@
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/Triple.h"
#include "llvm/ADT/iterator_range.h"
#include "llvm/Object/MachO.h"
#include "llvm/Support/Chrono.h"
#include "llvm/Support/ErrorOr.h"
#include "llvm/Support/YAMLTraits.h"
@ -107,7 +108,7 @@ public:
DebugMapObject &
addDebugMapObject(StringRef ObjectFilePath,
sys::TimePoint<std::chrono::seconds> Timestamp,
uint8_t Type);
uint8_t Type = llvm::MachO::N_OSO);
const Triple &getTriple() const { return BinaryTriple; }

View File

@ -100,6 +100,24 @@ namespace dsymutil {
namespace {
/// Retrieve the section named \a SecName in \a Obj.
///
/// To accommodate for platform discrepancies, the name passed should be
/// (for example) 'debug_info' to match either '__debug_info' or '.debug_info'.
/// This function will strip the initial platform-specific characters.
static Optional<object::SectionRef>
getSectionByName(const object::ObjectFile &Obj, StringRef SecName) {
for (const object::SectionRef &Section : Obj.sections()) {
StringRef SectionName;
Section.getName(SectionName);
SectionName = SectionName.substr(SectionName.find_first_not_of("._"));
if (SectionName != SecName)
continue;
return Section;
}
return None;
}
template <typename KeyT, typename ValT>
using HalfOpenIntervalMap =
IntervalMap<KeyT, ValT, IntervalMapImpl::NodeSizer<KeyT, ValT>::LeafSize,
@ -491,12 +509,48 @@ private:
std::string ClangModuleName;
};
/// Check if the DIE at \p Idx is in the scope of a function.
static bool inFunctionScope(CompileUnit &U, unsigned Idx) {
while (Idx) {
if (U.getOrigUnit().getDIEAtIndex(Idx).getTag() == dwarf::DW_TAG_subprogram)
return true;
Idx = U.getInfo(Idx).ParentIdx;
}
return false;
}
} // end anonymous namespace
void CompileUnit::markEverythingAsKept() {
for (auto &I : Info)
// Mark everything that wasn't explicity marked for pruning.
unsigned Idx = 0;
setHasInterestingContent();
for (auto &I : Info) {
// Mark everything that wasn't explicit marked for pruning.
I.Keep = !I.Prune;
auto DIE = OrigUnit.getDIEAtIndex(Idx++);
// Try to guess which DIEs must go to the accelerator tables. We do that
// just for variables, because functions will be handled depending on
// whether they carry a DW_AT_low_pc attribute or not.
if (DIE.getTag() != dwarf::DW_TAG_variable &&
DIE.getTag() != dwarf::DW_TAG_constant)
continue;
Optional<DWARFFormValue> Value;
if (!(Value = DIE.find(dwarf::DW_AT_location))) {
if ((Value = DIE.find(dwarf::DW_AT_const_value)) &&
!inFunctionScope(*this, I.ParentIdx))
I.InDebugMap = true;
continue;
}
if (auto Block = Value->getAsBlock()) {
if (Block->size() > OrigUnit.getAddressByteSize() &&
(*Block)[0] == dwarf::DW_OP_addr)
I.InDebugMap = true;
}
}
}
uint64_t CompileUnit::computeNextUnitOffset() {
@ -668,6 +722,9 @@ public:
std::vector<DWARFDebugLine::Row> &Rows,
unsigned AdddressSize);
/// Copy over the debug sections that are not modified when updating.
void copyInvariantDebugSection(const object::ObjectFile &Obj, LinkOptions &);
uint32_t getLineSectionSize() const { return LineSectionSize; }
/// Emit the .debug_pubnames contribution for \p Unit.
@ -1200,6 +1257,32 @@ void DwarfStreamer::emitLineTableForUnit(MCDwarfLineTableParams Params,
MS->EmitLabel(LineEndSym);
}
static void emitSectionContents(const object::ObjectFile &Obj,
StringRef SecName, MCStreamer *MS) {
StringRef Contents;
if (auto Sec = getSectionByName(Obj, SecName))
if (!Sec->getContents(Contents))
MS->EmitBytes(Contents);
}
void DwarfStreamer::copyInvariantDebugSection(const object::ObjectFile &Obj,
LinkOptions &Options) {
MS->SwitchSection(MC->getObjectFileInfo()->getDwarfLineSection());
emitSectionContents(Obj, "debug_line", MS);
MS->SwitchSection(MC->getObjectFileInfo()->getDwarfLocSection());
emitSectionContents(Obj, "debug_loc", MS);
MS->SwitchSection(MC->getObjectFileInfo()->getDwarfRangesSection());
emitSectionContents(Obj, "debug_ranges", MS);
MS->SwitchSection(MC->getObjectFileInfo()->getDwarfFrameSection());
emitSectionContents(Obj, "debug_frame", MS);
MS->SwitchSection(MC->getObjectFileInfo()->getDwarfARangesSection());
emitSectionContents(Obj, "debug_aranges", MS);
}
/// Emit the pubnames or pubtypes section contribution for \p
/// Unit into \p Sec. The data is provided in \p Names.
void DwarfStreamer::emitPubSectionForUnit(
@ -1529,8 +1612,8 @@ private:
/// it to \p Die.
/// \returns the size of the new attribute.
unsigned cloneStringAttribute(DIE &Die, AttributeSpec AttrSpec,
const DWARFFormValue &Val,
const DWARFUnit &U);
const DWARFFormValue &Val, const DWARFUnit &U,
AttributesInfo &Info);
/// Clone an attribute referencing another DIE and add
/// it to \p Die.
@ -2621,12 +2704,18 @@ void DwarfLinker::AssignAbbrev(DIEAbbrev &Abbrev) {
unsigned DwarfLinker::DIECloner::cloneStringAttribute(DIE &Die,
AttributeSpec AttrSpec,
const DWARFFormValue &Val,
const DWARFUnit &U) {
const DWARFUnit &U,
AttributesInfo &Info) {
// Switch everything to out of line strings.
const char *String = *Val.getAsCString();
unsigned Offset = Linker.StringPool.getStringOffset(String);
auto StringEntry = Linker.StringPool.getEntry(String);
if (AttrSpec.Attr == dwarf::DW_AT_name)
Info.Name = StringEntry;
else if (AttrSpec.Attr == dwarf::DW_AT_MIPS_linkage_name ||
AttrSpec.Attr == dwarf::DW_AT_linkage_name)
Info.MangledName = StringEntry;
Die.addValue(DIEAlloc, dwarf::Attribute(AttrSpec.Attr), dwarf::DW_FORM_strp,
DIEInteger(Offset));
DIEInteger(StringEntry.getOffset()));
return 4;
}
@ -2749,6 +2838,14 @@ unsigned DwarfLinker::DIECloner::cloneAddressAttribute(
const CompileUnit &Unit, AttributesInfo &Info) {
uint64_t Addr = *Val.getAsAddress();
if (LLVM_UNLIKELY(Linker.Options.Update)) {
if (AttrSpec.Attr == dwarf::DW_AT_low_pc)
Info.HasLowPc = true;
Die.addValue(DIEAlloc, dwarf::Attribute(AttrSpec.Attr),
dwarf::Form(AttrSpec.Form), DIEInteger(Addr));
return Unit.getOrigUnit().getAddressByteSize();
}
if (AttrSpec.Attr == dwarf::DW_AT_low_pc) {
if (Die.getTag() == dwarf::DW_TAG_inlined_subroutine ||
Die.getTag() == dwarf::DW_TAG_lexical_block)
@ -2789,6 +2886,26 @@ unsigned DwarfLinker::DIECloner::cloneScalarAttribute(
AttributeSpec AttrSpec, const DWARFFormValue &Val, unsigned AttrSize,
AttributesInfo &Info) {
uint64_t Value;
if (LLVM_UNLIKELY(Linker.Options.Update)) {
if (auto OptionalValue = Val.getAsUnsignedConstant())
Value = *OptionalValue;
else if (auto OptionalValue = Val.getAsSignedConstant())
Value = *OptionalValue;
else if (auto OptionalValue = Val.getAsSectionOffset())
Value = *OptionalValue;
else {
Linker.reportWarning(
"Unsupported scalar attribute form. Dropping attribute.", &InputDIE);
return 0;
}
if (AttrSpec.Attr == dwarf::DW_AT_declaration && Value)
Info.IsDeclaration = true;
Die.addValue(DIEAlloc, dwarf::Attribute(AttrSpec.Attr),
dwarf::Form(AttrSpec.Form), DIEInteger(Value));
return AttrSize;
}
if (AttrSpec.Attr == dwarf::DW_AT_high_pc &&
Die.getTag() == dwarf::DW_TAG_compile_unit) {
if (Unit.getLowPc() == -1ULL)
@ -2839,7 +2956,7 @@ unsigned DwarfLinker::DIECloner::cloneAttribute(
switch (AttrSpec.Form) {
case dwarf::DW_FORM_strp:
case dwarf::DW_FORM_string:
return cloneStringAttribute(Die, AttrSpec, Val, U);
return cloneStringAttribute(Die, AttrSpec, Val, U, Info);
case dwarf::DW_FORM_ref_addr:
case dwarf::DW_FORM_ref1:
case dwarf::DW_FORM_ref2:
@ -3108,13 +3225,14 @@ DIE *DwarfLinker::DIECloner::cloneDIE(
if (Abbrev->getTag() == dwarf::DW_TAG_subprogram) {
Flags |= TF_InFunctionScope;
if (!Info.InDebugMap)
if (!Info.InDebugMap && LLVM_LIKELY(!Options.Update))
Flags |= TF_SkipPC;
}
bool Copied = false;
for (const auto &AttrSpec : Abbrev->attributes()) {
if (shouldSkipAttribute(AttrSpec, Die->getTag(), Info.InDebugMap,
if (LLVM_LIKELY(!Options.Update) &&
shouldSkipAttribute(AttrSpec, Die->getTag(), Info.InDebugMap,
Flags & TF_SkipPC, Flags & TF_InFunctionScope)) {
DWARFFormValue::skipValue(AttrSpec.Form, Data, &Offset,
U.getFormParams());
@ -3827,13 +3945,18 @@ void DwarfLinker::DIECloner::cloneAllCompileUnits(DWARFContext &DwarfContext) {
Linker.OutputDebugInfoSize = CurrentUnit->computeNextUnitOffset();
if (Linker.Options.NoOutput)
continue;
// FIXME: for compatibility with the classic dsymutil, we emit
// an empty line table for the unit, even if the unit doesn't
// actually exist in the DIE tree.
Linker.patchLineTableForUnit(*CurrentUnit, DwarfContext);
Linker.emitAcceleratorEntriesForUnit(*CurrentUnit);
Linker.patchRangesForUnit(*CurrentUnit, DwarfContext);
Linker.Streamer->emitLocationsForUnit(*CurrentUnit, DwarfContext);
if (LLVM_LIKELY(!Linker.Options.Update)) {
// FIXME: for compatibility with the classic dsymutil, we emit an empty
// line table for the unit, even if the unit doesn't actually exist in
// the DIE tree.
Linker.patchLineTableForUnit(*CurrentUnit, DwarfContext);
Linker.emitAcceleratorEntriesForUnit(*CurrentUnit);
Linker.patchRangesForUnit(*CurrentUnit, DwarfContext);
Linker.Streamer->emitLocationsForUnit(*CurrentUnit, DwarfContext);
} else {
Linker.emitAcceleratorEntriesForUnit(*CurrentUnit);
}
}
if (Linker.Options.NoOutput)
@ -3841,7 +3964,8 @@ void DwarfLinker::DIECloner::cloneAllCompileUnits(DWARFContext &DwarfContext) {
// Emit all the compile unit's debug information.
for (auto &CurrentUnit : CompileUnits) {
Linker.generateUnitRanges(*CurrentUnit);
if (LLVM_LIKELY(!Linker.Options.Update))
Linker.generateUnitRanges(*CurrentUnit);
CurrentUnit->fixupForwardReferences();
Linker.Streamer->emitCompileUnitHeader(*CurrentUnit);
if (!CurrentUnit->getOutputUnitDIE())
@ -3900,7 +4024,8 @@ bool DwarfLinker::link(const DebugMap &Map) {
// Look for relocations that correspond to debug map entries.
RelocationManager RelocMgr(*this);
if (!RelocMgr.findValidRelocsInDebugInfo(*ErrOrObj, *Obj)) {
if (LLVM_LIKELY(!Options.Update) &&
!RelocMgr.findValidRelocsInDebugInfo(*ErrOrObj, *Obj)) {
if (Options.Verbose)
outs() << "No valid relocations found. Skipping.\n";
continue;
@ -3921,9 +4046,10 @@ bool DwarfLinker::link(const DebugMap &Map) {
CUDie.dump(outs(), 0, DumpOpts);
}
if (!registerModuleReference(CUDie, *CU, ModuleMap)) {
Units.push_back(llvm::make_unique<CompileUnit>(*CU, UnitID++,
!Options.NoODR, ""));
if (!CUDie || LLVM_UNLIKELY(Options.Update) ||
!registerModuleReference(CUDie, *CU, ModuleMap)) {
Units.push_back(llvm::make_unique<CompileUnit>(
*CU, UnitID++, !Options.NoODR && !Options.Update, ""));
maybeUpdateMaxDwarfVersion(CU->getVersion());
}
}
@ -3935,21 +4061,27 @@ bool DwarfLinker::link(const DebugMap &Map) {
// Then mark all the DIEs that need to be present in the linked
// output and collect some information about them. Note that this
// loop can not be merged with the previous one becaue cross-cu
// loop can not be merged with the previous one because cross-CU
// references require the ParentIdx to be setup for every CU in
// the object file before calling this.
for (auto &CurrentUnit : Units)
lookForDIEsToKeep(RelocMgr, CurrentUnit->getOrigUnit().getUnitDIE(), *Obj,
*CurrentUnit, 0);
if (LLVM_UNLIKELY(Options.Update)) {
for (auto &CurrentUnit : Units)
CurrentUnit->markEverythingAsKept();
Streamer->copyInvariantDebugSection(*ErrOrObj, Options);
} else {
for (auto &CurrentUnit : Units)
lookForDIEsToKeep(RelocMgr, CurrentUnit->getOrigUnit().getUnitDIE(),
*Obj, *CurrentUnit, 0);
}
// The calls to applyValidRelocs inside cloneDIE will walk the
// reloc array again (in the same way findValidRelocsInDebugInfo()
// did). We need to reset the NextValidReloc index to the beginning.
RelocMgr.resetValidRelocs();
if (RelocMgr.hasValidRelocs())
if (RelocMgr.hasValidRelocs() || LLVM_UNLIKELY(Options.Update))
DIECloner(*this, RelocMgr, DIEAlloc, Units, Options)
.cloneAllCompileUnits(*DwarfContext);
if (!Options.NoOutput && !Units.empty())
if (!Options.NoOutput && !Units.empty() && LLVM_LIKELY(!Options.Update))
patchFrameInfoForObject(*Obj, *DwarfContext,
Units[0]->getOrigUnit().getAddressByteSize());

View File

@ -80,10 +80,21 @@ static opt<bool> 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."),
"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 alias MinimizeA("z", desc("Alias for --minimize"), aliasopt(Minimize));
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."),
init(false), cat(DsymCategory));
static alias UpdateA("u", desc("Alias for --update"), aliasopt(Update));
static opt<unsigned> NumThreads(
"num-threads",
desc("Specifies the maximum number (n) of simultaneous threads to use\n"
@ -230,8 +241,12 @@ static bool verify(llvm::StringRef OutputFile, llvm::StringRef Arch) {
}
static std::string getOutputFileName(llvm::StringRef InputFile) {
// When updating, do in place replacement.
if (OutputFileOpt.empty() && Update)
return InputFile;
// If a flat dSYM has been requested, things are pretty simple.
if (FlatOut) {
// If a flat dSYM has been requested, things are pretty simple.
if (OutputFileOpt.empty()) {
if (InputFile == "-")
return "a.out.dwarf";
@ -269,6 +284,76 @@ static Expected<sys::fs::TempFile> createTempFile() {
return sys::fs::TempFile::create(TmpModel);
}
/// Parses the command line options into the LinkOptions struct and performs
/// some sanity checking. Returns an error in case the latter fails.
static Expected<LinkOptions> getOptions() {
LinkOptions Options;
Options.Verbose = Verbose;
Options.NoOutput = NoOutput;
Options.NoODR = NoODR;
Options.Minimize = Minimize;
Options.Update = Update;
Options.NoTimestamp = NoTimestamp;
Options.PrependPath = OsoPrependPath;
if (Options.Update && std::find(InputFiles.begin(), InputFiles.end(), "-") !=
InputFiles.end()) {
// FIXME: We cannot use stdin for an update because stdin will be
// consumed by the BinaryHolder during the debugmap parsing, and
// then we will want to consume it again in DwarfLinker. If we
// used a unique BinaryHolder object that could cache multiple
// binaries this restriction would go away.
return make_error<StringError>(
"error: standard input cannot be used as input for a dSYM update.",
inconvertibleErrorCode());
}
return Options;
}
/// Return a list of input files. This function has logic for dealing with the
/// special case where we might have dSYM bundles as input. The function
/// returns an error when the directory structure doesn't match that of a dSYM
/// bundle.
static Expected<std::vector<std::string>> getInputs(bool DsymAsInput) {
if (!DsymAsInput)
return InputFiles;
// If we are updating, we might get dSYM bundles as input.
std::vector<std::string> Inputs;
for (const auto &Input : InputFiles) {
if (!llvm::sys::fs::is_directory(Input)) {
Inputs.push_back(Input);
continue;
}
// Make sure that we're dealing with a dSYM bundle.
std::string dSYMDir = Input + "/Contents/Resources/DWARF";
if (!llvm::sys::fs::is_directory(dSYMDir))
return make_error<StringError>(
Input + " is a directory, but doesn't look like a dSYM bundle.",
inconvertibleErrorCode());
// Create a directory iterator to iterate over all the entries in the
// bundle.
std::error_code EC;
llvm::sys::fs::directory_iterator DirIt(dSYMDir, EC);
llvm::sys::fs::directory_iterator DirEnd;
if (EC)
return errorCodeToError(EC);
// Add each entry to the list of inputs.
while (DirIt != DirEnd) {
Inputs.push_back(DirIt->path());
DirIt.increment(EC);
if (EC)
return errorCodeToError(EC);
}
}
return Inputs;
}
namespace {
struct TempFileVector {
std::vector<sys::fs::TempFile> Files;
@ -285,7 +370,6 @@ int main(int argc, char **argv) {
llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);
llvm::PrettyStackTraceProgram StackPrinter(argc, argv);
llvm::llvm_shutdown_obj Shutdown;
LinkOptions Options;
void *P = (void *)(intptr_t)getOutputFileName;
std::string SDKPath = llvm::sys::fs::getMainExecutable(argv[0], P);
SDKPath = llvm::sys::path::parent_path(SDKPath);
@ -308,24 +392,29 @@ int main(int argc, char **argv) {
return 0;
}
Options.Verbose = Verbose;
Options.NoOutput = NoOutput;
Options.NoODR = NoODR;
Options.Minimize = Minimize;
Options.NoTimestamp = NoTimestamp;
Options.PrependPath = OsoPrependPath;
auto OptionsOrErr = getOptions();
if (!OptionsOrErr) {
errs() << "error: " << toString(OptionsOrErr.takeError());
return 1;
}
llvm::InitializeAllTargetInfos();
llvm::InitializeAllTargetMCs();
llvm::InitializeAllTargets();
llvm::InitializeAllAsmPrinters();
auto InputsOrErr = getInputs(OptionsOrErr->Update);
if (!InputsOrErr) {
errs() << "error: " << toString(InputsOrErr.takeError()) << '\n';
return 1;
}
if (!FlatOut && OutputFileOpt == "-") {
llvm::errs() << "error: cannot emit to standard output without --flat\n";
return 1;
}
if (InputFiles.size() > 1 && FlatOut && !OutputFileOpt.empty()) {
if (InputsOrErr->size() > 1 && FlatOut && !OutputFileOpt.empty()) {
llvm::errs() << "error: cannot use -o with multiple inputs in flat mode\n";
return 1;
}
@ -337,7 +426,7 @@ int main(int argc, char **argv) {
return 1;
}
for (auto &InputFile : InputFiles) {
for (auto &InputFile : *InputsOrErr) {
// Dump the symbol table for each input file and requested arch
if (DumpStab) {
if (!dumpStab(InputFile, ArchFlags, OsoPrependPath))
@ -354,6 +443,15 @@ int main(int argc, char **argv) {
return 1;
}
if (OptionsOrErr->Update) {
// The debug map should be empty. Add one object file corresponding to
// the input file.
for (auto &Map : *DebugMapPtrsOrErr)
Map->addDebugMapObject(InputFile,
llvm::sys::TimePoint<std::chrono::seconds>());
}
// Ensure that the debug map is not empty (anymore).
if (DebugMapPtrsOrErr->empty()) {
llvm::errs() << "error: no architecture to link\n";
return 1;
@ -369,7 +467,10 @@ int main(int argc, char **argv) {
// If there is more than one link to execute, we need to generate
// temporary files.
bool NeedsTempFiles = !DumpDebugMap && (*DebugMapPtrsOrErr).size() != 1;
bool NeedsTempFiles =
!DumpDebugMap && (OutputFileOpt != "-") &&
(DebugMapPtrsOrErr->size() != 1 || OptionsOrErr->Update);
llvm::SmallVector<MachOUtils::ArchAndFilename, 4> TempFiles;
TempFileVector TempFileStore;
std::atomic_char AllOK(1);
@ -412,7 +513,7 @@ int main(int argc, char **argv) {
auto LinkLambda = [&,
OutputFile](std::shared_ptr<raw_fd_ostream> Stream) {
AllOK.fetch_and(linkDwarf(*Stream, *Map, Options));
AllOK.fetch_and(linkDwarf(*Stream, *Map, *OptionsOrErr));
Stream->flush();
if (Verify && !NoOutput)
AllOK.fetch_and(verify(OutputFile, Map->getTriple().getArchName()));
@ -434,7 +535,7 @@ int main(int argc, char **argv) {
if (NeedsTempFiles &&
!MachOUtils::generateUniversalBinary(
TempFiles, getOutputFileName(InputFile), Options, SDKPath))
TempFiles, getOutputFileName(InputFile), *OptionsOrErr, SDKPath))
return 1;
}

View File

@ -37,7 +37,10 @@ struct LinkOptions {
bool NoOutput = false;
/// Do not unique types according to ODR
bool NoODR;
bool NoODR = false;
/// Update
bool Update = false;
/// Minimize
bool Minimize = false;