Index: llvm/docs/CommandGuide/dsymutil.rst =================================================================== --- llvm/docs/CommandGuide/dsymutil.rst +++ llvm/docs/CommandGuide/dsymutil.rst @@ -40,7 +40,9 @@ 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 @@ 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. Index: llvm/test/tools/dsymutil/X86/basic-linking-x86.test =================================================================== --- llvm/test/tools/dsymutil/X86/basic-linking-x86.test +++ llvm/test/tools/dsymutil/X86/basic-linking-x86.test @@ -8,6 +8,21 @@ 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 Index: llvm/test/tools/dsymutil/X86/update-one-CU.test =================================================================== --- /dev/null +++ llvm/test/tools/dsymutil/X86/update-one-CU.test @@ -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: } Index: llvm/test/tools/dsymutil/cmdline.test =================================================================== --- llvm/test/tools/dsymutil/cmdline.test +++ llvm/test/tools/dsymutil/cmdline.test @@ -14,6 +14,7 @@ HELP: -o= HELP: -oso-prepend-path= HELP: -symtab +HELP: -update HELP: -verbose HELP: -verify HELP: -y Index: llvm/tools/dsymutil/DebugMap.h =================================================================== --- llvm/tools/dsymutil/DebugMap.h +++ llvm/tools/dsymutil/DebugMap.h @@ -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 @@ DebugMapObject & addDebugMapObject(StringRef ObjectFilePath, sys::TimePoint Timestamp, - uint8_t Type); + uint8_t Type = llvm::MachO::N_OSO); const Triple &getTriple() const { return BinaryTriple; } Index: llvm/tools/dsymutil/DwarfLinker.cpp =================================================================== --- llvm/tools/dsymutil/DwarfLinker.cpp +++ llvm/tools/dsymutil/DwarfLinker.cpp @@ -100,6 +100,24 @@ 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 +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 using HalfOpenIntervalMap = IntervalMap::LeafSize, @@ -491,12 +509,48 @@ 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 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 @@ std::vector &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 @@ 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 @@ /// 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ // 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 @@ // 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 @@ CUDie.dump(outs(), 0, DumpOpts); } - if (!registerModuleReference(CUDie, *CU, ModuleMap)) { - Units.push_back(llvm::make_unique(*CU, UnitID++, - !Options.NoODR, "")); + if (!CUDie || LLVM_UNLIKELY(Options.Update) || + !registerModuleReference(CUDie, *CU, ModuleMap)) { + Units.push_back(llvm::make_unique( + *CU, UnitID++, !Options.NoODR && !Options.Update, "")); maybeUpdateMaxDwarfVersion(CU->getVersion()); } } @@ -3935,21 +4061,27 @@ // 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()); Index: llvm/tools/dsymutil/dsymutil.h =================================================================== --- llvm/tools/dsymutil/dsymutil.h +++ llvm/tools/dsymutil/dsymutil.h @@ -37,7 +37,10 @@ bool NoOutput = false; /// Do not unique types according to ODR - bool NoODR; + bool NoODR = false; + + /// Update + bool Update = false; /// Minimize bool Minimize = false; Index: llvm/tools/dsymutil/dsymutil.cpp =================================================================== --- llvm/tools/dsymutil/dsymutil.cpp +++ llvm/tools/dsymutil/dsymutil.cpp @@ -80,10 +80,21 @@ 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 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 NumThreads( "num-threads", desc("Specifies the maximum number (n) of simultaneous threads to use\n" @@ -230,8 +241,12 @@ } 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 @@ 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 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( + "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> getInputs(bool DsymAsInput) { + if (!DsymAsInput) + return InputFiles; + + // If we are updating, we might get dSYM bundles as input. + std::vector 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( + 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 Files; @@ -285,7 +370,6 @@ 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 @@ 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 @@ 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 @@ 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()); + } + + // 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 @@ // 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 TempFiles; TempFileVector TempFileStore; std::atomic_char AllOK(1); @@ -412,7 +513,7 @@ auto LinkLambda = [&, OutputFile](std::shared_ptr 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 @@ if (NeedsTempFiles && !MachOUtils::generateUniversalBinary( - TempFiles, getOutputFileName(InputFile), Options, SDKPath)) + TempFiles, getOutputFileName(InputFile), *OptionsOrErr, SDKPath)) return 1; }