Index: llvm/test/tools/dsymutil/ARM/obfuscated.test =================================================================== --- /dev/null +++ llvm/test/tools/dsymutil/ARM/obfuscated.test @@ -0,0 +1,166 @@ +REQUIRES: system-darwin + +RUN: dsymutil --symbol-map %p/../Inputs/obfuscated.map %p/../Inputs/obfuscated.arm64 -f -o - \ +RUN: | llvm-dwarfdump -v - \ +RUN: | FileCheck %s + +RUN: dsymutil --symbol-map %p/../Inputs/obfuscated.map %p/../Inputs/obfuscated.arm64 -f -o - \ +RUN: | llvm-dwarfdump -v - \ +RUN: | FileCheck --check-prefix=NOHIDDEN %s + +RUN: dsymutil --symbol-map %p/../Inputs/obfuscated.2.map %p/../Inputs/obfuscated.2.arm64 -f -o - \ +RUN: | llvm-dwarfdump -v - \ +RUN: | FileCheck --check-prefix=NOHIDDEN %s + +// Run with plist and make sure dsymutil finds it. +RUN: mkdir -p %t.dSYM/Contents/Resources/DWARF/ +RUN: mkdir -p %t.mapdir +RUN: cp %p/../Inputs/obfuscated.arm64 %t.dSYM/Contents/Resources/DWARF/ +RUN: cp %p/../Inputs/E828A486-8433-3A5E-B6DB-A6294D28133D.plist %t.dSYM/Contents/Resources/ +RUN: cp %p/../Inputs/obfuscated.map %t.mapdir/506AA50A-6B26-3B37-86D2-DC6EBD57B720.bcsymbolmap +RUN: dsymutil --symbol-map %t.mapdir %t.dSYM 2>&1 | FileCheck --check-prefix=OBFUSCATING %s + +// Run without plist and make sure dsymutil doesn't crash. +RUN: rm %t.dSYM/Contents/Resources/E828A486-8433-3A5E-B6DB-A6294D28133D.plist +RUN: dsymutil --symbol-map %t.mapdir %t.dSYM 2>&1 | FileCheck --check-prefix=NOTOBFUSCATING %s + +OBFUSCATING-NOT: not unobfuscating + +NOTOBFUSCATING: not unobfuscating + +NOHIDDEN-NOT: __hidden# + +CHECK: .debug_info contents: + +CHECK: DW_TAG_compile_unit [1] * +CHECK: DW_AT_producer [DW_FORM_strp] ( {{.*}} "Apple LLVM version 7.0.0 (clang-700.2.38.2)") +CHECK: DW_AT_name [DW_FORM_strp] ( {{.*}} "main.c") +CHECK: DW_AT_comp_dir [DW_FORM_strp] ( {{.*}} "/Users/steven/dev/alpena/tests/src") +CHECK: DW_TAG_subprogram [2] +CHECK: DW_AT_name [DW_FORM_strp] ( {{.*}} "main") + +CHECK: DW_TAG_compile_unit [1] * +CHECK: DW_AT_producer [DW_FORM_strp] ( {{.*}} "Apple LLVM version 7.0.0 (clang-700.2.38.2)") +CHECK: DW_AT_name [DW_FORM_strp] ( {{.*}} "one.c") +CHECK: DW_AT_comp_dir [DW_FORM_strp] ( {{.*}} "/Users/steven/dev/alpena/tests/src") +CHECK: DW_TAG_subprogram [2] +CHECK: DW_AT_name [DW_FORM_strp] ( {{.*}} "one") + +CHECK: DW_TAG_compile_unit [1] * +CHECK: DW_AT_producer [DW_FORM_strp] ( {{.*}} "Apple LLVM version 7.0.0 (clang-700.2.38.2)") +CHECK: DW_AT_name [DW_FORM_strp] ( {{.*}} "two.c") +CHECK: DW_AT_comp_dir [DW_FORM_strp] ( {{.*}} "/Users/steven/dev/alpena/tests/src") +CHECK: DW_TAG_subprogram [2] +CHECK: DW_AT_name [DW_FORM_strp] ( {{.*}} "two") + +CHECK: DW_TAG_compile_unit [1] * +CHECK: DW_AT_producer [DW_FORM_strp] ( {{.*}} "Apple LLVM version 7.0.0 (clang-700.2.38.2)") +CHECK: DW_AT_name [DW_FORM_strp] ( {{.*}} "three.c") +CHECK: DW_AT_comp_dir [DW_FORM_strp] ( {{.*}} "/Users/steven/dev/alpena/tests/src") +CHECK: DW_TAG_subprogram [2] +CHECK: DW_AT_name [DW_FORM_strp] ( {{.*}} "three") + +CHECK: DW_TAG_compile_unit [1] * +CHECK: DW_AT_producer [DW_FORM_strp] ( {{.*}} "Apple LLVM version 7.0.0 (clang-700.2.38.2)") +CHECK: DW_AT_name [DW_FORM_strp] ( {{.*}} "four.c") +CHECK: DW_AT_stmt_list [DW_FORM_data4] (0x0000011e) +CHECK: DW_AT_comp_dir [DW_FORM_strp] ( {{.*}} "/Users/steven/dev/alpena/tests/src") +CHECK: DW_TAG_subprogram [2] +CHECK: DW_AT_name [DW_FORM_strp] ( {{.*}} "four") + +CHECK: DW_TAG_compile_unit [1] * +CHECK: DW_AT_producer [DW_FORM_strp] ( {{.*}} "Apple LLVM version 7.0.0 (clang-700.2.38.2)") +CHECK: DW_AT_name [DW_FORM_strp] ( {{.*}} "five.c") +CHECK: DW_AT_comp_dir [DW_FORM_strp] ( {{.*}} "/Users/steven/dev/alpena/tests/src") +CHECK: DW_TAG_subprogram [2] +CHECK: DW_AT_name [DW_FORM_strp] ( {{.*}} "five") + +CHECK: DW_TAG_compile_unit [1] * +CHECK: DW_AT_producer [DW_FORM_strp] ( {{.*}} "Apple LLVM version 7.0.0 (clang-700.2.38.2)") +CHECK: DW_AT_name [DW_FORM_strp] ( {{.*}} "six.c") +CHECK: DW_AT_comp_dir [DW_FORM_strp] ( {{.*}} "/Users/steven/dev/alpena/tests/src") +CHECK: DW_TAG_subprogram [2] +CHECK: DW_AT_name [DW_FORM_strp] ( {{.*}} "six") + +CHECK: .debug_line contents: +CHECK: file_names[ 1]: +CHECK: name: "main.c" +CHECK: dir_index: 0 +CHECK: mod_time: 0x00000000 +CHECK: file_names[ 1]: +CHECK: name: "one.c" +CHECK: dir_index: 0 +CHECK: mod_time: 0x00000000 +CHECK: length: 0x00000000 +CHECK: file_names[ 1]: +CHECK: name: "two.c" +CHECK: dir_index: 0 +CHECK: mod_time: 0x00000000 +CHECK: length: 0x00000000 +CHECK: file_names[ 1]: +CHECK: name: "three.c" +CHECK: dir_index: 0 +CHECK: mod_time: 0x00000000 +CHECK: length: 0x00000000 +CHECK: file_names[ 1]: +CHECK: name: "four.c" +CHECK: dir_index: 0 +CHECK: mod_time: 0x00000000 +CHECK: length: 0x00000000 +CHECK: file_names[ 1]: +CHECK: name: "five.c" +CHECK: dir_index: 0 +CHECK: mod_time: 0x00000000 +CHECK: length: 0x00000000 +CHECK: file_names[ 1]: +CHECK: name: "six.c" +CHECK: dir_index: 0 +CHECK: mod_time: 0x00000000 +CHECK: length: 0x00000000 + +CHECK: .debug_pubnames contents: +CHECK: length = 0x00000017 version = 0x0002 unit_offset = 0x00000000 unit_size = 0x00000044 +CHECK: 0x0000002e "main" +CHECK: length = 0x00000016 version = 0x0002 unit_offset = 0x00000044 unit_size = 0x00000044 +CHECK: 0x0000002e "one" +CHECK: length = 0x00000016 version = 0x0002 unit_offset = 0x00000088 unit_size = 0x00000044 +CHECK: 0x0000002e "two" +CHECK: length = 0x00000018 version = 0x0002 unit_offset = 0x000000cc unit_size = 0x00000044 +CHECK: 0x0000002e "three" +CHECK: length = 0x00000017 version = 0x0002 unit_offset = 0x00000110 unit_size = 0x00000044 +CHECK: 0x0000002e "four" +CHECK: length = 0x00000017 version = 0x0002 unit_offset = 0x00000154 unit_size = 0x00000044 +CHECK: 0x0000002e "five" +CHECK: length = 0x00000016 version = 0x0002 unit_offset = 0x00000198 unit_size = 0x00000044 +CHECK: 0x0000002e "six" + +CHECK: .apple_names contents: + +CHECK: String: 0x00000091 "five" +CHECK-NEXT: Data 0 [ +CHECK-NEXT: Atom[0]: 0x00000182 +CHECK-NEXT: ] +CHECK: String: 0x0000009c "six" +CHECK-NEXT: Data 0 [ +CHECK-NEXT: Atom[0]: 0x000001c6 +CHECK-NEXT: ] +CHECK: String: 0x00000078 "three" +CHECK-NEXT: Data 0 [ +CHECK-NEXT: Atom[0]: 0x000000fa +CHECK-NEXT: ] +CHECK: String: 0x0000006c "two" +CHECK-NEXT: Data 0 [ +CHECK-NEXT: Atom[0]: 0x000000b6 +CHECK-NEXT: ] +CHECK: String: 0x00000057 "main" +CHECK-NEXT: Data 0 [ +CHECK-NEXT: Atom[0]: 0x0000002e +CHECK-NEXT: ] +CHECK: String: 0x00000085 "four" +CHECK-NEXT: Data 0 [ +CHECK-NEXT: Atom[0]: 0x0000013e +CHECK-NEXT: ] +CHECK: String: 0x00000062 "one" +CHECK-NEXT: Data 0 [ +CHECK-NEXT: Atom[0]: 0x00000072 +CHECK-NEXT: ] Index: llvm/test/tools/dsymutil/Inputs/E828A486-8433-3A5E-B6DB-A6294D28133D.plist =================================================================== --- /dev/null +++ llvm/test/tools/dsymutil/Inputs/E828A486-8433-3A5E-B6DB-A6294D28133D.plist @@ -0,0 +1,7 @@ + + + + DBGOriginalUUID + 506AA50A-6B26-3B37-86D2-DC6EBD57B720 + + \ No newline at end of file Index: llvm/test/tools/dsymutil/Inputs/obfuscated.2.map =================================================================== --- /dev/null +++ llvm/test/tools/dsymutil/Inputs/obfuscated.2.map @@ -0,0 +1,22 @@ +BCSymbolMap Version: 2.0 +_two +_three +_four +_five +_six +LLVM version 3.9.0 (ssh://git@stash.sd.apple.com/devtools/clang.git c74ae34bd917b77f9c848bd599dfde2813fb509f) +main +main.c +/Volumes/Data/dev/BitcodeBuildTests/unit +one +one.c +two +two.c +three +three.c +four +four.c +five +five.c +six +six.c Index: llvm/test/tools/dsymutil/Inputs/obfuscated.map =================================================================== --- /dev/null +++ llvm/test/tools/dsymutil/Inputs/obfuscated.map @@ -0,0 +1,17 @@ +one +two +three +four +five +six +.str +Apple LLVM version 7.0.0 (clang-700.2.38.2) +main +main.c +/Users/steven/dev/alpena/tests/src +one.c +two.c +three.c +four.c +five.c +six.c Index: llvm/test/tools/dsymutil/cmdline.test =================================================================== --- llvm/test/tools/dsymutil/cmdline.test +++ llvm/test/tools/dsymutil/cmdline.test @@ -17,6 +17,7 @@ HELP: -o= HELP: -oso-prepend-path= HELP: -papertrail +HELP: -symbol-map HELP: -symtab HELP: -toolchain HELP: -update Index: llvm/tools/dsymutil/CMakeLists.txt =================================================================== --- llvm/tools/dsymutil/CMakeLists.txt +++ llvm/tools/dsymutil/CMakeLists.txt @@ -20,6 +20,7 @@ MachODebugMapParser.cpp MachOUtils.cpp NonRelocatableStringpool.cpp + SymbolMap.cpp DEPENDS intrinsics_gen Index: llvm/tools/dsymutil/DebugMap.h =================================================================== --- llvm/tools/dsymutil/DebugMap.h +++ llvm/tools/dsymutil/DebugMap.h @@ -75,7 +75,7 @@ class DebugMap { Triple BinaryTriple; std::string BinaryPath; - + std::vector BinaryUUID; using ObjectContainer = std::vector>; ObjectContainer Objects; @@ -89,8 +89,10 @@ ///@} public: - DebugMap(const Triple &BinaryTriple, StringRef BinaryPath) - : BinaryTriple(BinaryTriple), BinaryPath(BinaryPath) {} + DebugMap(const Triple &BinaryTriple, StringRef BinaryPath, + ArrayRef BinaryUUID = ArrayRef()) + : BinaryTriple(BinaryTriple), BinaryPath(BinaryPath), + BinaryUUID(BinaryUUID.begin(), BinaryUUID.end()) {} using const_iterator = ObjectContainer::const_iterator; @@ -113,6 +115,10 @@ const Triple &getTriple() const { return BinaryTriple; } + const ArrayRef getUUID() const { + return ArrayRef(BinaryUUID); + } + StringRef getBinaryPath() const { return BinaryPath; } void print(raw_ostream &OS) const; Index: llvm/tools/dsymutil/DwarfLinker.cpp =================================================================== --- llvm/tools/dsymutil/DwarfLinker.cpp +++ llvm/tools/dsymutil/DwarfLinker.cpp @@ -1701,6 +1701,8 @@ DWARFDataExtractor LineExtractor( OrigDwarf.getDWARFObj(), OrigDwarf.getDWARFObj().getLineSection(), OrigDwarf.isLittleEndian(), Unit.getOrigUnit().getAddressByteSize()); + if (Options.Translator) + return Streamer->translateLineTable(LineExtractor, StmtOffset, Options); Error Err = LineTable.parse(LineExtractor, &StmtOffset, OrigDwarf, &Unit.getOrigUnit(), DWARFContext::dumpWarning); @@ -2245,17 +2247,16 @@ if (Linker.Options.NoOutput) continue; - 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. + // 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. + if (LLVM_LIKELY(!Linker.Options.Update) || Linker.Options.Translator) Linker.patchLineTableForUnit(*CurrentUnit, DwarfContext, Ranges, DMO); - Linker.emitAcceleratorEntriesForUnit(*CurrentUnit); - Linker.patchRangesForUnit(*CurrentUnit, DwarfContext, DMO); - Linker.Streamer->emitLocationsForUnit(*CurrentUnit, DwarfContext); - } else { - Linker.emitAcceleratorEntriesForUnit(*CurrentUnit); - } + Linker.emitAcceleratorEntriesForUnit(*CurrentUnit); + if (Linker.Options.Update) + continue; + Linker.patchRangesForUnit(*CurrentUnit, DwarfContext, DMO); + Linker.Streamer->emitLocationsForUnit(*CurrentUnit, DwarfContext); } if (Linker.Options.NoOutput) @@ -2380,7 +2381,7 @@ // This Dwarf string pool which is used for emission. It must be used // serially as the order of calling getStringOffset matters for // reproducibility. - OffsetsStringPool OffsetsStringPool; + OffsetsStringPool OffsetsStringPool(Options.Translator); // ODR Contexts for the link. DeclContextTree ODRContexts; @@ -2649,7 +2650,7 @@ pool.wait(); } - return Options.NoOutput ? true : Streamer->finish(Map); + return Options.NoOutput ? true : Streamer->finish(Map, Options.Translator); } // namespace dsymutil bool linkDwarf(raw_fd_ostream &OutFile, BinaryHolder &BinHolder, Index: llvm/tools/dsymutil/DwarfStreamer.h =================================================================== --- llvm/tools/dsymutil/DwarfStreamer.h +++ llvm/tools/dsymutil/DwarfStreamer.h @@ -50,7 +50,7 @@ bool init(Triple TheTriple); /// Dump the file to the disk. - bool finish(const DebugMap &); + bool finish(const DebugMap &, SymbolMapTranslator &T); AsmPrinter &getAsmPrinter() const { return *Asm; } @@ -104,6 +104,11 @@ std::vector &Rows, unsigned AdddressSize); + /// Copy the debug_line over to the updated binary while unobfuscating the + /// file names and directories. + void translateLineTable(DataExtractor LineData, uint32_t Offset, + LinkOptions &Options); + /// Copy over the debug sections that are not modified when updating. void copyInvariantDebugSection(const object::ObjectFile &Obj); Index: llvm/tools/dsymutil/DwarfStreamer.cpp =================================================================== --- llvm/tools/dsymutil/DwarfStreamer.cpp +++ llvm/tools/dsymutil/DwarfStreamer.cpp @@ -124,11 +124,11 @@ return true; } -bool DwarfStreamer::finish(const DebugMap &DM) { +bool DwarfStreamer::finish(const DebugMap &DM, SymbolMapTranslator &T) { bool Result = true; if (DM.getTriple().isOSDarwin() && !DM.getBinaryPath().empty() && Options.FileType == OutputFileType::Object) - Result = MachOUtils::generateDsymCompanion(DM, *MS, OutFile); + Result = MachOUtils::generateDsymCompanion(DM, T, *MS, OutFile); else MS->Finish(); return Result; @@ -577,6 +577,89 @@ MS->EmitLabel(LineEndSym); } +/// Copy the debug_line over to the updated binary while unobfuscating the file +/// names and directories. +void DwarfStreamer::translateLineTable(DataExtractor Data, uint32_t Offset, + LinkOptions &Options) { + MS->SwitchSection(MC->getObjectFileInfo()->getDwarfLineSection()); + StringRef Contents = Data.getData(); + + // We have to deconstruct the line table header, because it contains to + // length fields that will need to be updated when we change the length of + // the files and directories in there. + unsigned UnitLength = Data.getU32(&Offset); + unsigned UnitEnd = Offset + UnitLength; + MCSymbol *BeginLabel = MC->createTempSymbol(); + MCSymbol *EndLabel = MC->createTempSymbol(); + unsigned Version = Data.getU16(&Offset); + + if (Version > 5) { + warn("Unsupported line table version: dropping contents and not " + "unobfsucating line table."); + return; + } + + Asm->EmitLabelDifference(EndLabel, BeginLabel, 4); + Asm->OutStreamer->EmitLabel(BeginLabel); + Asm->emitInt16(Version); + LineSectionSize += 6; + + MCSymbol *HeaderBeginLabel = MC->createTempSymbol(); + MCSymbol *HeaderEndLabel = MC->createTempSymbol(); + Asm->EmitLabelDifference(HeaderEndLabel, HeaderBeginLabel, 4); + Asm->OutStreamer->EmitLabel(HeaderBeginLabel); + Offset += 4; + LineSectionSize += 4; + + uint32_t AfterHeaderLengthOffset = Offset; + // Skip to the directories. + Offset += (Version >= 4) ? 5 : 4; + unsigned OpcodeBase = Data.getU8(&Offset); + Offset += OpcodeBase - 1; + Asm->OutStreamer->EmitBytes(Contents.slice(AfterHeaderLengthOffset, Offset)); + LineSectionSize += Offset - AfterHeaderLengthOffset; + + // Offset points to the first directory. + while (const char *Dir = Data.getCStr(&Offset)) { + if (Dir[0] == 0) + break; + + StringRef Translated = Options.Translator(Dir); + Asm->OutStreamer->EmitBytes(Translated); + Asm->emitInt8(0); + LineSectionSize += Translated.size() + 1; + } + Asm->emitInt8(0); + LineSectionSize += 1; + + while (const char *File = Data.getCStr(&Offset)) { + if (File[0] == 0) + break; + + StringRef Translated = Options.Translator(File); + Asm->OutStreamer->EmitBytes(Translated); + Asm->emitInt8(0); + LineSectionSize += Translated.size() + 1; + + uint32_t OffsetBeforeLEBs = Offset; + Asm->EmitULEB128(Data.getULEB128(&Offset)); + Asm->EmitULEB128(Data.getULEB128(&Offset)); + Asm->EmitULEB128(Data.getULEB128(&Offset)); + LineSectionSize += Offset - OffsetBeforeLEBs; + } + Asm->emitInt8(0); + LineSectionSize += 1; + + Asm->OutStreamer->EmitLabel(HeaderEndLabel); + + // Copy the actual line table program over. + Asm->OutStreamer->EmitBytes(Contents.slice(Offset, UnitEnd)); + LineSectionSize += UnitEnd - Offset; + + Asm->OutStreamer->EmitLabel(EndLabel); + Offset = UnitEnd; +} + static void emitSectionContents(const object::ObjectFile &Obj, StringRef SecName, MCStreamer *MS) { StringRef Contents; @@ -586,8 +669,10 @@ } void DwarfStreamer::copyInvariantDebugSection(const object::ObjectFile &Obj) { - MS->SwitchSection(MC->getObjectFileInfo()->getDwarfLineSection()); - emitSectionContents(Obj, "debug_line", MS); + if (!Options.Translator) { + MS->SwitchSection(MC->getObjectFileInfo()->getDwarfLineSection()); + emitSectionContents(Obj, "debug_line", MS); + } MS->SwitchSection(MC->getObjectFileInfo()->getDwarfLocSection()); emitSectionContents(Obj, "debug_loc", MS); Index: llvm/tools/dsymutil/LinkUtils.h =================================================================== --- llvm/tools/dsymutil/LinkUtils.h +++ llvm/tools/dsymutil/LinkUtils.h @@ -10,8 +10,11 @@ #ifndef LLVM_TOOLS_DSYMUTIL_LINKOPTIONS_H #define LLVM_TOOLS_DSYMUTIL_LINKOPTIONS_H +#include "SymbolMap.h" + #include "llvm/ADT/Twine.h" #include "llvm/Support/WithColor.h" + #include namespace llvm { @@ -60,6 +63,9 @@ /// -oso-prepend-path std::string PrependPath; + /// Symbol map translator. + SymbolMapTranslator Translator; + LinkOptions() = default; }; Index: llvm/tools/dsymutil/MachODebugMapParser.cpp =================================================================== --- llvm/tools/dsymutil/MachODebugMapParser.cpp +++ llvm/tools/dsymutil/MachODebugMapParser.cpp @@ -163,7 +163,8 @@ MachODebugMapParser::parseOneBinary(const MachOObjectFile &MainBinary, StringRef BinaryPath) { loadMainBinarySymbols(MainBinary); - Result = make_unique(MainBinary.getArchTriple(), BinaryPath); + ArrayRef UUID = MainBinary.getUuid(); + Result = make_unique(MainBinary.getArchTriple(), BinaryPath, UUID); MainBinaryStrings = MainBinary.getStringTableData(); for (const SymbolRef &Symbol : MainBinary.symbols()) { const DataRefImpl &DRI = Symbol.getRawDataRefImpl(); Index: llvm/tools/dsymutil/MachOUtils.h =================================================================== --- llvm/tools/dsymutil/MachOUtils.h +++ llvm/tools/dsymutil/MachOUtils.h @@ -9,8 +9,11 @@ #ifndef LLVM_TOOLS_DSYMUTIL_MACHOUTILS_H #define LLVM_TOOLS_DSYMUTIL_MACHOUTILS_H +#include "SymbolMap.h" + #include "llvm/ADT/StringRef.h" #include "llvm/Support/FileSystem.h" + #include namespace llvm { @@ -38,8 +41,8 @@ StringRef OutputFileName, const LinkOptions &, StringRef SDKPath); -bool generateDsymCompanion(const DebugMap &DM, MCStreamer &MS, - raw_fd_ostream &OutFile); +bool generateDsymCompanion(const DebugMap &DM, SymbolMapTranslator &Translator, + MCStreamer &MS, raw_fd_ostream &OutFile); std::string getArchName(StringRef Arch); } // namespace MachOUtils Index: llvm/tools/dsymutil/MachOUtils.cpp =================================================================== --- llvm/tools/dsymutil/MachOUtils.cpp +++ llvm/tools/dsymutil/MachOUtils.cpp @@ -333,8 +333,8 @@ // Stream a dSYM companion binary file corresponding to the binary referenced // by \a DM to \a OutFile. The passed \a MS MCStreamer is setup to write to // \a OutFile and it must be using a MachObjectWriter object to do so. -bool generateDsymCompanion(const DebugMap &DM, MCStreamer &MS, - raw_fd_ostream &OutFile) { +bool generateDsymCompanion(const DebugMap &DM, SymbolMapTranslator &Translator, + MCStreamer &MS, raw_fd_ostream &OutFile) { auto &ObjectStreamer = static_cast(MS); MCAssembler &MCAsm = ObjectStreamer.getAssembler(); auto &Writer = static_cast(MCAsm.getWriter()); @@ -443,7 +443,7 @@ } SmallString<0> NewSymtab; - NonRelocatableStringpool NewStrings; + NonRelocatableStringpool NewStrings(Translator); unsigned NListSize = Is64Bit ? sizeof(MachO::nlist_64) : sizeof(MachO::nlist); unsigned NumSyms = 0; uint64_t NewStringsSize = 0; Index: llvm/tools/dsymutil/NonRelocatableStringpool.h =================================================================== --- llvm/tools/dsymutil/NonRelocatableStringpool.h +++ llvm/tools/dsymutil/NonRelocatableStringpool.h @@ -10,6 +10,8 @@ #ifndef LLVM_TOOLS_DSYMUTIL_NONRELOCATABLESTRINGPOOL_H #define LLVM_TOOLS_DSYMUTIL_NONRELOCATABLESTRINGPOOL_H +#include "SymbolMap.h" + #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" #include "llvm/CodeGen/DwarfStringPoolEntry.h" @@ -32,7 +34,9 @@ /// order. using MapTy = StringMap; - NonRelocatableStringpool() { + NonRelocatableStringpool( + SymbolMapTranslator Translator = SymbolMapTranslator()) + : Translator(Translator) { // Legacy dsymutil puts an empty string at the start of the line table. EmptyString = getEntry(""); } @@ -62,6 +66,7 @@ uint32_t CurrentEndOffset = 0; unsigned NumEntries = 0; DwarfStringPoolEntryRef EmptyString; + SymbolMapTranslator Translator; }; /// Helper for making strong types. Index: llvm/tools/dsymutil/NonRelocatableStringpool.cpp =================================================================== --- llvm/tools/dsymutil/NonRelocatableStringpool.cpp +++ llvm/tools/dsymutil/NonRelocatableStringpool.cpp @@ -16,6 +16,8 @@ if (S.empty() && !Strings.empty()) return EmptyString; + if (Translator) + S = Translator(S); auto I = Strings.insert({S, DwarfStringPoolEntry()}); auto &Entry = I.first->second; if (I.second || !Entry.isIndexed()) { @@ -29,6 +31,10 @@ StringRef NonRelocatableStringpool::internString(StringRef S) { DwarfStringPoolEntry Entry{nullptr, 0, DwarfStringPoolEntry::NotIndexed}; + + if (Translator) + S = Translator(S); + auto InsertResult = Strings.insert({S, Entry}); return InsertResult.first->getKey(); } Index: llvm/tools/dsymutil/SymbolMap.h =================================================================== --- /dev/null +++ llvm/tools/dsymutil/SymbolMap.h @@ -0,0 +1,50 @@ +//=- tools/dsymutil/SymbolMap.h -----------------------------------*- C++ -*-=// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_DSYMUTIL_SYMBOLMAP_H +#define LLVM_TOOLS_DSYMUTIL_SYMBOLMAP_H + +#include "llvm/ADT/StringRef.h" + +#include +#include + +namespace llvm { +namespace dsymutil { +class DebugMap; + +/// Callable class to unobfuscate strings based on a BCSymbolMap. +class SymbolMapTranslator { +public: + SymbolMapTranslator() : MangleNames(false) {} + + SymbolMapTranslator(std::vector UnobfuscatedStrings, + bool MangleNames) + : UnobfuscatedStrings(std::move(UnobfuscatedStrings)), + MangleNames(MangleNames) {} + + llvm::StringRef operator()(llvm::StringRef Input); + + operator bool() const { return !UnobfuscatedStrings.empty(); } + +private: + std::vector UnobfuscatedStrings; + bool MangleNames; +}; + +/// Class to initialize SymbolMapTranslators from a BCSymbolMap. +class SymbolMapLoader { +public: + SymbolMapTranslator Load(llvm::StringRef SymbolMapFile, + llvm::StringRef InputFile, const DebugMap &Map); +}; +} // namespace dsymutil +} // namespace llvm + +#endif // LLVM_TOOLS_DSYMUTIL_SYMBOLMAP_H Index: llvm/tools/dsymutil/SymbolMap.cpp =================================================================== --- /dev/null +++ llvm/tools/dsymutil/SymbolMap.cpp @@ -0,0 +1,160 @@ +//===- tools/dsymutil/SymbolMap.cpp ---------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "SymbolMap.h" +#include "DebugMap.h" +#include "MachOUtils.h" + +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/WithColor.h" + +#ifdef __APPLE__ +#include +#include +#endif + +namespace llvm { +namespace dsymutil { + +StringRef SymbolMapTranslator::operator()(StringRef Input) { + if (!Input.startswith("__hidden#") && !Input.startswith("___hidden#")) + return Input; + + bool MightNeedUnderscore = false; + StringRef Line = Input.drop_front(sizeof("__hidden#") - 1); + if (Line[0] == '#') { + Line = Line.drop_front(); + MightNeedUnderscore = true; + } + + std::size_t LineNumber = std::numeric_limits::max(); + Line.split('_').first.getAsInteger(10, LineNumber); + if (LineNumber >= UnobfuscatedStrings.size()) { + WithColor::warning() << "reference to a unexisting unobfuscated string " + << Input << ": symbol map mismatch?\n" + << Line << '\n'; + return Input; + } + + const std::string &Translation = UnobfuscatedStrings[LineNumber]; + if (!MightNeedUnderscore || !MangleNames) + return Translation; + + // Objective-C symbols for the MachO symbol table start with a \1. Please see + // `CGObjCCommonMac::GetNameForMethod` in clang. + if (Translation[0] == 1) + return StringRef(Translation).drop_front(); + + // We need permanent storage for the string we are about to create. Just + // append it to the vector containing translations. This should only happen + // during MachO symbol table translation, thus there should be no risk on + // exponential growth. + UnobfuscatedStrings.emplace_back("_" + Translation); + return UnobfuscatedStrings.back(); +} + +SymbolMapTranslator SymbolMapLoader::Load(StringRef SymbolMapFile, + StringRef InputFile, + const DebugMap &Map) { + std::string SymbolMapPath = SymbolMapFile; + +#if __APPLE__ + // Look through the UUID Map. + if (sys::fs::is_directory(SymbolMapPath) && !Map.getUUID().empty()) { + uuid_string_t UUIDString; + uuid_unparse_upper((const uint8_t *)Map.getUUID().data(), UUIDString); + + SmallString<256> PlistPath( + sys::path::parent_path(sys::path::parent_path(InputFile))); + sys::path::append(PlistPath, StringRef(UUIDString).str() + ".plist"); + + CFStringRef plistFile = CFStringCreateWithCString( + kCFAllocatorDefault, PlistPath.c_str(), kCFStringEncodingUTF8); + CFURLRef fileURL = CFURLCreateWithFileSystemPath( + kCFAllocatorDefault, plistFile, kCFURLPOSIXPathStyle, false); + CFReadStreamRef resourceData = + CFReadStreamCreateWithFile(kCFAllocatorDefault, fileURL); + if (resourceData) { + CFReadStreamOpen(resourceData); + CFDictionaryRef plist = (CFDictionaryRef)CFPropertyListCreateWithStream( + kCFAllocatorDefault, resourceData, 0, kCFPropertyListImmutable, + nullptr, nullptr); + + if (plist) { + if (CFDictionaryContainsKey(plist, CFSTR("DBGOriginalUUID"))) { + CFStringRef OldUUID = (CFStringRef)CFDictionaryGetValue( + plist, CFSTR("DBGOriginalUUID")); + + StringRef UUID(CFStringGetCStringPtr(OldUUID, kCFStringEncodingUTF8)); + SmallString<256> BCSymbolMapPath(SymbolMapPath); + sys::path::append(BCSymbolMapPath, UUID.str() + ".bcsymbolmap"); + SymbolMapPath = BCSymbolMapPath.str(); + } + CFRelease(plist); + } + CFReadStreamClose(resourceData); + CFRelease(resourceData); + } + CFRelease(fileURL); + CFRelease(plistFile); + } +#endif + + if (sys::fs::is_directory(SymbolMapPath)) { + SymbolMapPath += (Twine("/") + sys::path::filename(InputFile) + "-" + + MachOUtils::getArchName(Map.getTriple().getArchName()) + + ".bcsymbolmap") + .str(); + } + + auto ErrOrMemBuffer = MemoryBuffer::getFile(SymbolMapPath); + if (auto EC = ErrOrMemBuffer.getError()) { + WithColor::warning() << SymbolMapPath << ": " << EC.message() + << ": not unobfuscating.\n"; + return {}; + } + + std::vector UnobfuscatedStrings; + auto &MemBuf = **ErrOrMemBuffer; + StringRef Data(MemBuf.getBufferStart(), + MemBuf.getBufferEnd() - MemBuf.getBufferStart()); + StringRef LHS; + std::tie(LHS, Data) = Data.split('\n'); + bool MangleNames = false; + + // Check version string first. + if (!LHS.startswith("BCSymbolMap Version:")) { + // Version string not present, warns but try to parse it. + WithColor::warning() << SymbolMapPath + << " is missing version string: assuming 1.0.\n"; + UnobfuscatedStrings.emplace_back(LHS); + } else if (LHS.equals("BCSymbolMap Version: 1.0")) { + MangleNames = true; + } else if (LHS.equals("BCSymbolMap Version: 2.0")) { + MangleNames = false; + } else { + StringRef VersionNum; + std::tie(LHS, VersionNum) = LHS.split(':'); + WithColor::warning() << SymbolMapPath + << " has unsupported symbol map version" << VersionNum + << ": not unobfuscating.\n"; + return {}; + } + + while (!Data.empty()) { + std::tie(LHS, Data) = Data.split('\n'); + UnobfuscatedStrings.emplace_back(LHS); + } + + return SymbolMapTranslator(std::move(UnobfuscatedStrings), MangleNames); +} + +} // namespace dsymutil +} // namespace llvm Index: llvm/tools/dsymutil/dsymutil.cpp =================================================================== --- llvm/tools/dsymutil/dsymutil.cpp +++ llvm/tools/dsymutil/dsymutil.cpp @@ -59,6 +59,8 @@ OutputFileOpt("o", desc("Specify the output file. default: .dwarf"), value_desc("filename"), cat(DsymCategory)); +static alias OutputFileOptA("out", desc("Alias for -o"), + aliasopt(OutputFileOpt)); static opt OsoPrependPath( "oso-prepend-path", @@ -100,6 +102,11 @@ init(false), cat(DsymCategory)); static alias UpdateA("u", desc("Alias for --update"), aliasopt(Update)); +static opt SymbolMap( + "symbol-map", + desc("Updates the existing dSYMs inplace using symbol map specified."), + value_desc("bcsymbolmap"), cat(DsymCategory)); + static cl::opt AcceleratorTable( "accelerator", cl::desc("Output accelerator tables."), cl::values(clEnumValN(AccelTableKind::Default, "Default", @@ -273,8 +280,11 @@ } static Expected getOutputFileName(llvm::StringRef InputFile) { + if (OutputFileOpt == "-") + return OutputFileOpt; + // When updating, do in place replacement. - if (OutputFileOpt.empty() && Update) + if (OutputFileOpt.empty() && (Update || !SymbolMap.empty())) return InputFile; // If a flat dSYM has been requested, things are pretty simple. @@ -325,6 +335,9 @@ Options.PrependPath = OsoPrependPath; Options.TheAccelTableKind = AcceleratorTable; + if (!SymbolMap.empty()) + Options.Update = true; + if (Assembly) Options.FileType = OutputFileType::Assembly; @@ -443,6 +456,13 @@ return 1; } + if (InputFiles.size() > 1 && !SymbolMap.empty() && + !llvm::sys::fs::is_directory(SymbolMap)) { + WithColor::error() << "when unobfuscating multiple files, --symbol-map " + << "needs to point to a directory.\n"; + return 1; + } + if (getenv("RC_DEBUG_OPTIONS")) PaperTrailWarnings = true; @@ -492,6 +512,9 @@ // Shared a single binary holder for all the link steps. BinaryHolder BinHolder; + // Symbol map loader. + SymbolMapLoader SymMapLoader; + NumThreads = std::min(OptionsOrErr->Threads, DebugMapPtrsOrErr->size()); llvm::ThreadPool Threads(NumThreads); @@ -511,6 +534,10 @@ if (DumpDebugMap) continue; + if (!SymbolMap.empty()) + OptionsOrErr->Translator = + SymMapLoader.Load(SymbolMap, InputFile, *Map); + if (Map->begin() == Map->end()) WithColor::warning() << "no debug symbols in executable (-arch "