diff --git a/llvm/docs/CommandGuide/dsymutil.rst b/llvm/docs/CommandGuide/dsymutil.rst index e3f2f33224b01..af9d7f16b3619 100644 --- a/llvm/docs/CommandGuide/dsymutil.rst +++ b/llvm/docs/CommandGuide/dsymutil.rst @@ -140,6 +140,10 @@ OPTIONS (in bytes) to the linked dSYM. The table is sorted by the output size listing the object files with the largest contribution first. +.. option:: --symbol-map + + Update the existing dSYMs inplace using symbol map specified. + .. option:: -s, --symtab Dumps the symbol table found in *executable* or object file(s) and exits. diff --git a/llvm/include/llvm/CodeGen/NonRelocatableStringpool.h b/llvm/include/llvm/CodeGen/NonRelocatableStringpool.h index 4b8eed7bdb1b5..3dc0731f5a04e 100644 --- a/llvm/include/llvm/CodeGen/NonRelocatableStringpool.h +++ b/llvm/include/llvm/CodeGen/NonRelocatableStringpool.h @@ -27,7 +27,10 @@ class NonRelocatableStringpool { /// order. using MapTy = StringMap; - NonRelocatableStringpool(bool PutEmptyString = false) { + NonRelocatableStringpool( + std::function Translator = nullptr, + bool PutEmptyString = false) + : Translator(Translator) { if (PutEmptyString) getEntry(""); } @@ -56,6 +59,7 @@ class NonRelocatableStringpool { MapTy Strings; uint64_t CurrentEndOffset = 0; unsigned NumEntries = 0; + std::function Translator; }; /// Helper for making strong types. diff --git a/llvm/include/llvm/DWARFLinker/Classic/DWARFStreamer.h b/llvm/include/llvm/DWARFLinker/Classic/DWARFStreamer.h index e7a1a3cd838c2..bebdfd5e60257 100644 --- a/llvm/include/llvm/DWARFLinker/Classic/DWARFStreamer.h +++ b/llvm/include/llvm/DWARFLinker/Classic/DWARFStreamer.h @@ -45,13 +45,16 @@ class DwarfStreamer : public DwarfEmitter { public: DwarfStreamer(DWARFLinkerBase::OutputFileType OutFileType, raw_pwrite_stream &OutFile, + DWARFLinkerBase::TranslatorFuncTy Translator, DWARFLinkerBase::MessageHandlerTy Warning) - : OutFile(OutFile), OutFileType(OutFileType), WarningHandler(Warning) {} + : OutFile(OutFile), OutFileType(OutFileType), Translator(Translator), + WarningHandler(Warning) {} virtual ~DwarfStreamer() = default; static Expected> createStreamer( const Triple &TheTriple, DWARFLinkerBase::OutputFileType FileType, - raw_pwrite_stream &OutFile, DWARFLinkerBase::MessageHandlerTy Warning); + raw_pwrite_stream &OutFile, DWARFLinkerBase::TranslatorFuncTy Translator, + DWARFLinkerBase::MessageHandlerTy Warning); Error init(Triple TheTriple, StringRef Swift5ReflectionSegmentName); @@ -292,6 +295,7 @@ class DwarfStreamer : public DwarfEmitter { /// The output file we stream the linked Dwarf to. raw_pwrite_stream &OutFile; DWARFLinker::OutputFileType OutFileType = DWARFLinker::OutputFileType::Object; + std::function Translator; uint64_t RangesSectionSize = 0; uint64_t RngListsSectionSize = 0; diff --git a/llvm/include/llvm/DWARFLinker/DWARFLinkerBase.h b/llvm/include/llvm/DWARFLinker/DWARFLinkerBase.h index 70b25cf02df7f..5c811b668f0a3 100644 --- a/llvm/include/llvm/DWARFLinker/DWARFLinkerBase.h +++ b/llvm/include/llvm/DWARFLinker/DWARFLinkerBase.h @@ -82,6 +82,7 @@ class DWARFLinkerBase { std::function; using ObjectPrefixMapTy = std::map; using CompileUnitHandlerTy = function_ref; + using TranslatorFuncTy = std::function; using SwiftInterfacesMapTy = std::map; /// Type of output file. enum class OutputFileType : uint8_t { diff --git a/llvm/include/llvm/DWARFLinker/Parallel/DWARFLinker.h b/llvm/include/llvm/DWARFLinker/Parallel/DWARFLinker.h index 8acc046d07249..5312712a4a5b8 100644 --- a/llvm/include/llvm/DWARFLinker/Parallel/DWARFLinker.h +++ b/llvm/include/llvm/DWARFLinker/Parallel/DWARFLinker.h @@ -123,7 +123,8 @@ class DWARFLinker : public DWARFLinkerBase { /// Creates dwarf linker instance. static std::unique_ptr - createLinker(MessageHandlerTy ErrorHandler, MessageHandlerTy WarningHandler); + createLinker(MessageHandlerTy ErrorHandler, MessageHandlerTy WarningHandler, + TranslatorFuncTy StringsTranslator = nullptr); /// Set output DWARF handler. Result of linking DWARF is set of sections /// containing final debug info. DWARFLinkerBase::link() pass generated diff --git a/llvm/lib/CodeGen/NonRelocatableStringpool.cpp b/llvm/lib/CodeGen/NonRelocatableStringpool.cpp index 26857c6a40889..e8391afb8e3f8 100644 --- a/llvm/lib/CodeGen/NonRelocatableStringpool.cpp +++ b/llvm/lib/CodeGen/NonRelocatableStringpool.cpp @@ -12,6 +12,8 @@ namespace llvm { DwarfStringPoolEntryRef NonRelocatableStringpool::getEntry(StringRef S) { + if (Translator) + S = Translator(S); auto I = Strings.insert({S, DwarfStringPoolEntry()}); auto &Entry = I.first->second; if (I.second || !Entry.isIndexed()) { @@ -26,6 +28,9 @@ DwarfStringPoolEntryRef NonRelocatableStringpool::getEntry(StringRef S) { 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(); } diff --git a/llvm/lib/DWARFLinker/Classic/DWARFLinker.cpp b/llvm/lib/DWARFLinker/Classic/DWARFLinker.cpp index 60f664ece7eef..9b581a6c9ab77 100644 --- a/llvm/lib/DWARFLinker/Classic/DWARFLinker.cpp +++ b/llvm/lib/DWARFLinker/Classic/DWARFLinker.cpp @@ -2701,8 +2701,8 @@ Error DWARFLinker::link() { // This Dwarf string pool which is used for emission. It must be used // serially as the order of calling getStringOffset matters for // reproducibility. - OffsetsStringPool DebugStrPool(true); - OffsetsStringPool DebugLineStrPool(false); + OffsetsStringPool DebugStrPool(StringsTranslator, true); + OffsetsStringPool DebugLineStrPool(StringsTranslator, false); DebugDieValuePool StringOffsetPool; // ODR Contexts for the optimize. diff --git a/llvm/lib/DWARFLinker/Classic/DWARFStreamer.cpp b/llvm/lib/DWARFLinker/Classic/DWARFStreamer.cpp index 8b31b5ead29ad..6d522370e440d 100644 --- a/llvm/lib/DWARFLinker/Classic/DWARFStreamer.cpp +++ b/llvm/lib/DWARFLinker/Classic/DWARFStreamer.cpp @@ -32,9 +32,10 @@ using namespace dwarf_linker::classic; Expected> DwarfStreamer::createStreamer( const Triple &TheTriple, DWARFLinkerBase::OutputFileType FileType, - raw_pwrite_stream &OutFile, DWARFLinkerBase::MessageHandlerTy Warning) { + raw_pwrite_stream &OutFile, DWARFLinkerBase::TranslatorFuncTy Translator, + DWARFLinkerBase::MessageHandlerTy Warning) { std::unique_ptr Streamer = - std::make_unique(FileType, OutFile, Warning); + std::make_unique(FileType, OutFile, Translator, Warning); if (Error Err = Streamer->init(TheTriple, "__DWARF")) return std::move(Err); @@ -976,10 +977,11 @@ void DwarfStreamer::emitLineTableString(const DWARFDebugLine::Prologue &P, switch (String.getForm()) { case dwarf::DW_FORM_string: { - StringRef Str = *StringVal; - Asm->OutStreamer->emitBytes(Str.data()); + StringRef TranslatedString = + (Translator) ? Translator(*StringVal) : *StringVal; + Asm->OutStreamer->emitBytes(TranslatedString.data()); Asm->emitInt8(0); - LineSectionSize += Str.size() + 1; + LineSectionSize += TranslatedString.size() + 1; } break; case dwarf::DW_FORM_strp: case dwarf::DW_FORM_line_strp: { diff --git a/llvm/lib/DWARFLinker/Parallel/DWARFLinker.cpp b/llvm/lib/DWARFLinker/Parallel/DWARFLinker.cpp index 31c7e867767a0..ad8d28a643174 100644 --- a/llvm/lib/DWARFLinker/Parallel/DWARFLinker.cpp +++ b/llvm/lib/DWARFLinker/Parallel/DWARFLinker.cpp @@ -15,6 +15,8 @@ using namespace dwarf_linker::parallel; std::unique_ptr DWARFLinker::createLinker(MessageHandlerTy ErrorHandler, - MessageHandlerTy WarningHandler) { - return std::make_unique(ErrorHandler, WarningHandler); + MessageHandlerTy WarningHandler, + TranslatorFuncTy StringsTranslator) { + return std::make_unique(ErrorHandler, WarningHandler, + StringsTranslator); } diff --git a/llvm/lib/DWARFLinker/Parallel/DWARFLinkerGlobalData.h b/llvm/lib/DWARFLinker/Parallel/DWARFLinkerGlobalData.h index 7ca81eb34f005..38c261a8106fc 100644 --- a/llvm/lib/DWARFLinker/Parallel/DWARFLinkerGlobalData.h +++ b/llvm/lib/DWARFLinker/Parallel/DWARFLinkerGlobalData.h @@ -21,6 +21,7 @@ class DWARFDie; namespace dwarf_linker { namespace parallel { +using TranslatorFuncTy = std::function; using MessageHandlerTy = std::function; @@ -94,6 +95,19 @@ class LinkingGlobalData { /// Returns global string pool. StringPool &getStringPool() { return Strings; } + /// Set translation function. + void setTranslator(TranslatorFuncTy Translator) { + this->Translator = Translator; + } + + /// Translate specified string. + StringRef translateString(StringRef String) { + if (Translator) + return Translator(String); + + return String; + } + /// Returns linking options. const DWARFLinkerOptions &getOptions() const { return Options; } @@ -147,6 +161,7 @@ class LinkingGlobalData { protected: llvm::parallel::PerThreadBumpPtrAllocator Allocator; StringPool Strings; + TranslatorFuncTy Translator; DWARFLinkerOptions Options; MessageHandlerTy WarningHandler; MessageHandlerTy ErrorHandler; diff --git a/llvm/lib/DWARFLinker/Parallel/DWARFLinkerImpl.cpp b/llvm/lib/DWARFLinker/Parallel/DWARFLinkerImpl.cpp index e68bf0c227a0a..49b08997eb9c1 100644 --- a/llvm/lib/DWARFLinker/Parallel/DWARFLinkerImpl.cpp +++ b/llvm/lib/DWARFLinker/Parallel/DWARFLinkerImpl.cpp @@ -20,9 +20,11 @@ using namespace dwarf_linker; using namespace dwarf_linker::parallel; DWARFLinkerImpl::DWARFLinkerImpl(MessageHandlerTy ErrorHandler, - MessageHandlerTy WarningHandler) + MessageHandlerTy WarningHandler, + TranslatorFuncTy StringsTranslator) : UniqueUnitID(0), DebugStrStrings(GlobalData), DebugLineStrStrings(GlobalData), CommonSections(GlobalData) { + GlobalData.setTranslator(StringsTranslator); GlobalData.setErrorHandler(ErrorHandler); GlobalData.setWarningHandler(WarningHandler); } diff --git a/llvm/lib/DWARFLinker/Parallel/DWARFLinkerImpl.h b/llvm/lib/DWARFLinker/Parallel/DWARFLinkerImpl.h index 597bb1b4da59a..7c17c5b79c7c1 100644 --- a/llvm/lib/DWARFLinker/Parallel/DWARFLinkerImpl.h +++ b/llvm/lib/DWARFLinker/Parallel/DWARFLinkerImpl.h @@ -26,7 +26,8 @@ namespace parallel { class DWARFLinkerImpl : public DWARFLinker { public: DWARFLinkerImpl(MessageHandlerTy ErrorHandler, - MessageHandlerTy WarningHandler); + MessageHandlerTy WarningHandler, + TranslatorFuncTy StringsTranslator); /// Add object file to be linked. Pre-load compile unit die. Call /// \p OnCUDieLoaded for each compile unit die. If specified \p File diff --git a/llvm/lib/DWARFLinker/Parallel/OutputSections.h b/llvm/lib/DWARFLinker/Parallel/OutputSections.h index 0e1f2dae54bcc..a21e4b2b75a50 100644 --- a/llvm/lib/DWARFLinker/Parallel/OutputSections.h +++ b/llvm/lib/DWARFLinker/Parallel/OutputSections.h @@ -253,7 +253,7 @@ struct SectionDescriptor : SectionDescriptorBase { /// Emit specified inplace string value into the current section contents. void emitInplaceString(StringRef String) { - OS << String; + OS << GlobalData.translateString(String); emitIntVal(0, 1); } diff --git a/llvm/lib/DWARFLinker/Parallel/StringEntryToDwarfStringPoolEntryMap.h b/llvm/lib/DWARFLinker/Parallel/StringEntryToDwarfStringPoolEntryMap.h index f67536ef7a1a8..858f224777dba 100644 --- a/llvm/lib/DWARFLinker/Parallel/StringEntryToDwarfStringPoolEntryMap.h +++ b/llvm/lib/DWARFLinker/Parallel/StringEntryToDwarfStringPoolEntryMap.h @@ -33,7 +33,7 @@ class StringEntryToDwarfStringPoolEntryMap { DwarfStringPoolEntryWithExtString *DataPtr = GlobalData.getAllocator() .Allocate(); - DataPtr->String = String->getKey(); + DataPtr->String = GlobalData.translateString(String->getKey()); DataPtr->Index = DwarfStringPoolEntry::NotIndexed; DataPtr->Offset = 0; DataPtr->Symbol = nullptr; diff --git a/llvm/test/tools/dsymutil/ARM/obfuscated.test b/llvm/test/tools/dsymutil/ARM/obfuscated.test new file mode 100644 index 0000000000000..3443b8e634692 --- /dev/null +++ b/llvm/test/tools/dsymutil/ARM/obfuscated.test @@ -0,0 +1,200 @@ +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 --accelerator=Pub --symbol-map %p/../Inputs/obfuscated.map %p/../Inputs/obfuscated.arm64 -f -o - \ +RUN: | llvm-dwarfdump -v - \ +RUN: | FileCheck --check-prefix=PUB %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 + +// ---------------------------------------- +// Repeat the same steps for --linker parallel. +RUN: dsymutil --linker parallel --symbol-map %p/../Inputs/obfuscated.map %p/../Inputs/obfuscated.arm64 -f -o - \ +RUN: | llvm-dwarfdump -v - \ +RUN: | FileCheck %s + +RUN: dsymutil --linker parallel --accelerator=Pub --symbol-map %p/../Inputs/obfuscated.map %p/../Inputs/obfuscated.arm64 -f -o - \ +RUN: | llvm-dwarfdump -v - \ +RUN: | FileCheck --check-prefix=PUB %s + +RUN: dsymutil --linker parallel --symbol-map %p/../Inputs/obfuscated.map %p/../Inputs/obfuscated.arm64 -f -o - \ +RUN: | llvm-dwarfdump -v - \ +RUN: | FileCheck --check-prefix=NOHIDDEN %s + +RUN: dsymutil --linker parallel --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 --linker parallel --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 --linker parallel --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 + +PUB: .debug_pubnames contents: +PUB: length = 0x00000017, format = DWARF32, version = 0x0002, unit_offset = 0x00000000, unit_size = 0x00000044 +PUB: 0x0000002e "main" +PUB: length = 0x00000016, format = DWARF32, version = 0x0002, unit_offset = 0x00000044, unit_size = 0x00000044 +PUB: 0x0000002e "one" +PUB: length = 0x00000016, format = DWARF32, version = 0x0002, unit_offset = 0x00000088, unit_size = 0x00000044 +PUB: 0x0000002e "two" +PUB: length = 0x00000018, format = DWARF32, version = 0x0002, unit_offset = 0x000000cc, unit_size = 0x00000044 +PUB: 0x0000002e "three" +PUB: length = 0x00000017, format = DWARF32, version = 0x0002, unit_offset = 0x00000110, unit_size = 0x00000044 +PUB: 0x0000002e "four" +PUB: length = 0x00000017, format = DWARF32, version = 0x0002, unit_offset = 0x00000154, unit_size = 0x00000044 +PUB: 0x0000002e "five" +PUB: length = 0x00000016, format = DWARF32, version = 0x0002, unit_offset = 0x00000198, unit_size = 0x00000044 +PUB: 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: ] diff --git a/llvm/test/tools/dsymutil/Inputs/obfuscated.2.arm64 b/llvm/test/tools/dsymutil/Inputs/obfuscated.2.arm64 new file mode 100644 index 0000000000000..b40e023eb4916 Binary files /dev/null and b/llvm/test/tools/dsymutil/Inputs/obfuscated.2.arm64 differ diff --git a/llvm/test/tools/dsymutil/Inputs/obfuscated.2.map b/llvm/test/tools/dsymutil/Inputs/obfuscated.2.map new file mode 100644 index 0000000000000..6efca5960311d --- /dev/null +++ b/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 diff --git a/llvm/test/tools/dsymutil/Inputs/obfuscated.arm64 b/llvm/test/tools/dsymutil/Inputs/obfuscated.arm64 new file mode 100644 index 0000000000000..8395798be17ff Binary files /dev/null and b/llvm/test/tools/dsymutil/Inputs/obfuscated.arm64 differ diff --git a/llvm/test/tools/dsymutil/Inputs/obfuscated.map b/llvm/test/tools/dsymutil/Inputs/obfuscated.map new file mode 100644 index 0000000000000..30fed8bf9b5a5 --- /dev/null +++ b/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 diff --git a/llvm/test/tools/dsymutil/cmdline.test b/llvm/test/tools/dsymutil/cmdline.test index 814252b6e2306..36cf3f542695c 100644 --- a/llvm/test/tools/dsymutil/cmdline.test +++ b/llvm/test/tools/dsymutil/cmdline.test @@ -28,6 +28,7 @@ CHECK: -remarks-output-format CHECK: -remarks-prepend-path CHECK: -reproducer CHECK: -statistics +CHECK: -symbol-map CHECK: -symtab CHECK: {{-S}} CHECK: -toolchain diff --git a/llvm/tools/dsymutil/CMakeLists.txt b/llvm/tools/dsymutil/CMakeLists.txt index efe28bda68ebf..53f882e90b4e9 100644 --- a/llvm/tools/dsymutil/CMakeLists.txt +++ b/llvm/tools/dsymutil/CMakeLists.txt @@ -32,6 +32,7 @@ add_llvm_tool(dsymutil MachOUtils.cpp Reproducer.cpp RelocationMap.cpp + SymbolMap.cpp DEPENDS intrinsics_gen diff --git a/llvm/tools/dsymutil/DwarfLinkerForBinary.cpp b/llvm/tools/dsymutil/DwarfLinkerForBinary.cpp index 677dfc44c54a4..5ae5ecd556adb 100644 --- a/llvm/tools/dsymutil/DwarfLinkerForBinary.cpp +++ b/llvm/tools/dsymutil/DwarfLinkerForBinary.cpp @@ -634,19 +634,25 @@ bool DwarfLinkerForBinary::linkImpl( DebugMap DebugMap(Map.getTriple(), Map.getBinaryPath()); + std::function TranslationLambda = [&](StringRef Input) { + assert(Options.Translator); + return Options.Translator(Input); + }; + std::unique_ptr GeneralLinker = Linker::createLinker( [&](const Twine &Error, StringRef Context, const DWARFDie *DIE) { reportError(Error, Context, DIE); }, [&](const Twine &Warning, StringRef Context, const DWARFDie *DIE) { reportWarning(Warning, Context, DIE); - }); + }, + Options.Translator ? TranslationLambda : nullptr); std::unique_ptr Streamer; if (!Options.NoOutput) { if (Expected> StreamerOrErr = classic::DwarfStreamer::createStreamer( - Map.getTriple(), ObjectType, OutFile, + Map.getTriple(), ObjectType, OutFile, Options.Translator, [&](const Twine &Warning, StringRef Context, const DWARFDie *DIE) { reportWarning(Warning, Context, DIE); @@ -860,8 +866,8 @@ bool DwarfLinkerForBinary::linkImpl( if (Map.getTriple().isOSDarwin() && !Map.getBinaryPath().empty() && ObjectType == Linker::OutputFileType::Object) return MachOUtils::generateDsymCompanion( - Options.VFS, Map, *Streamer->getAsmPrinter().OutStreamer, OutFile, - RelocationsToApply); + Options.VFS, Map, Options.Translator, + *Streamer->getAsmPrinter().OutStreamer, OutFile, RelocationsToApply); Streamer->finish(); return true; diff --git a/llvm/tools/dsymutil/LinkUtils.h b/llvm/tools/dsymutil/LinkUtils.h index 6aa0b847eebd6..fd9d985097d6e 100644 --- a/llvm/tools/dsymutil/LinkUtils.h +++ b/llvm/tools/dsymutil/LinkUtils.h @@ -9,6 +9,8 @@ #ifndef LLVM_TOOLS_DSYMUTIL_LINKOPTIONS_H #define LLVM_TOOLS_DSYMUTIL_LINKOPTIONS_H +#include "SymbolMap.h" + #include "llvm/ADT/Twine.h" #include "llvm/Remarks/RemarkFormat.h" #include "llvm/Support/VirtualFileSystem.h" @@ -85,6 +87,9 @@ struct LinkOptions { /// The Resources directory in the .dSYM bundle. std::optional ResourceDir; + /// Symbol map translator. + SymbolMapTranslator Translator; + /// Virtual File System. llvm::IntrusiveRefCntPtr VFS = vfs::getRealFileSystem(); diff --git a/llvm/tools/dsymutil/MachOUtils.cpp b/llvm/tools/dsymutil/MachOUtils.cpp index 8e144d640ed01..3efc1aff82374 100644 --- a/llvm/tools/dsymutil/MachOUtils.cpp +++ b/llvm/tools/dsymutil/MachOUtils.cpp @@ -373,7 +373,7 @@ static unsigned segmentLoadCommandSize(bool Is64Bit, unsigned NumSections) { // \a OutFile and it must be using a MachObjectWriter object to do so. bool generateDsymCompanion( llvm::IntrusiveRefCntPtr VFS, const DebugMap &DM, - MCStreamer &MS, raw_fd_ostream &OutFile, + SymbolMapTranslator &Translator, MCStreamer &MS, raw_fd_ostream &OutFile, const std::vector &RelocationsToApply) { auto &ObjectStreamer = static_cast(MS); @@ -509,9 +509,12 @@ bool generateDsymCompanion( } SmallString<0> NewSymtab; + std::function TranslationLambda = + Translator ? [&](StringRef Input) { return Translator(Input); } + : static_cast>(nullptr); // Legacy dsymutil puts an empty string at the start of the line table. // thus we set NonRelocatableStringpool(,PutEmptyString=true) - NonRelocatableStringpool NewStrings(true); + NonRelocatableStringpool NewStrings(TranslationLambda, true); unsigned NListSize = Is64Bit ? sizeof(MachO::nlist_64) : sizeof(MachO::nlist); unsigned NumSyms = 0; uint64_t NewStringsSize = 0; diff --git a/llvm/tools/dsymutil/MachOUtils.h b/llvm/tools/dsymutil/MachOUtils.h index 0229647b00f85..059d9fdd788a6 100644 --- a/llvm/tools/dsymutil/MachOUtils.h +++ b/llvm/tools/dsymutil/MachOUtils.h @@ -8,6 +8,8 @@ #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 "llvm/Support/VirtualFileSystem.h" @@ -57,7 +59,7 @@ bool generateUniversalBinary(SmallVectorImpl &ArchFiles, StringRef SDKPath, bool Fat64 = false); bool generateDsymCompanion( llvm::IntrusiveRefCntPtr VFS, const DebugMap &DM, - MCStreamer &MS, raw_fd_ostream &OutFile, + SymbolMapTranslator &Translator, MCStreamer &MS, raw_fd_ostream &OutFile, const std::vector &RelocationsToApply); diff --git a/llvm/tools/dsymutil/Options.td b/llvm/tools/dsymutil/Options.td index d8cec0cb2c410..a4e4c6c4cdb9c 100644 --- a/llvm/tools/dsymutil/Options.td +++ b/llvm/tools/dsymutil/Options.td @@ -128,6 +128,12 @@ def object_prefix_map: Separate<["--", "-"], "object-prefix-map">, Group; def: Joined<["--", "-"], "object-prefix-map=">, Alias; +def symbolmap: Separate<["--", "-"], "symbol-map">, + MetaVarName<"">, + HelpText<"Updates the existing dSYMs inplace using symbol map specified.">, + Group; +def: Joined<["--", "-"], "symbol-map=">, Alias; + def arch: Separate<["--", "-"], "arch">, MetaVarName<"">, HelpText<"Link DWARF debug information only for specified CPU architecture" diff --git a/llvm/tools/dsymutil/SymbolMap.cpp b/llvm/tools/dsymutil/SymbolMap.cpp new file mode 100644 index 0000000000000..c55362e5f7756 --- /dev/null +++ b/llvm/tools/dsymutil/SymbolMap.cpp @@ -0,0 +1,157 @@ +//===- tools/dsymutil/SymbolMap.cpp ---------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#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.starts_with("__hidden#") && !Input.starts_with("___hidden#")) + return Input; + + StringRef Line = Input.drop_front(sizeof("__hidden#") - 1); + bool MightNeedUnderscore = Line.consume_front("#"); + + 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 + // `MangleContext::mangleObjCMethodName` 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 InputFile, + const DebugMap &Map) const { + if (SymbolMap.empty()) + return {}; + + std::string SymbolMapPath = SymbolMap; + +#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 = std::string(BCSymbolMapPath); + } + 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.starts_with("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 diff --git a/llvm/tools/dsymutil/SymbolMap.h b/llvm/tools/dsymutil/SymbolMap.h new file mode 100644 index 0000000000000..977de31a5a17a --- /dev/null +++ b/llvm/tools/dsymutil/SymbolMap.h @@ -0,0 +1,53 @@ +//=- tools/dsymutil/SymbolMap.h -----------------------------------*- C++ -*-=// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#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) {} + + StringRef operator()(StringRef Input); + + operator bool() const { return !UnobfuscatedStrings.empty(); } + +private: + std::vector UnobfuscatedStrings; + bool MangleNames; +}; + +/// Class to initialize SymbolMapTranslators from a BCSymbolMap. +class SymbolMapLoader { +public: + SymbolMapLoader(std::string SymbolMap) : SymbolMap(std::move(SymbolMap)) {} + + SymbolMapTranslator Load(StringRef InputFile, const DebugMap &Map) const; + +private: + const std::string SymbolMap; +}; +} // namespace dsymutil +} // namespace llvm + +#endif // LLVM_TOOLS_DSYMUTIL_SYMBOLMAP_H diff --git a/llvm/tools/dsymutil/dsymutil.cpp b/llvm/tools/dsymutil/dsymutil.cpp index bc968b6387b65..25e281c415e75 100644 --- a/llvm/tools/dsymutil/dsymutil.cpp +++ b/llvm/tools/dsymutil/dsymutil.cpp @@ -108,6 +108,7 @@ struct DsymutilOptions { bool Flat = false; bool InputIsYAMLDebugMap = false; bool ForceKeepFunctionForStatic = false; + std::string SymbolMap; std::string OutputFile; std::string Toolchain; std::string ReproducerPath; @@ -340,6 +341,12 @@ static Expected getOptions(opt::InputArgList &Args) { return DWARFLinkerType.takeError(); } + if (opt::Arg *SymbolMap = Args.getLastArg(OPT_symbolmap)) + Options.SymbolMap = SymbolMap->getValue(); + + if (Args.hasArg(OPT_symbolmap)) + Options.LinkOpts.Update = true; + if (Expected> InputFiles = getInputs(Args, Options.LinkOpts.Update)) { Options.InputFiles = std::move(*InputFiles); @@ -553,7 +560,8 @@ getOutputFileName(StringRef InputFile, const DsymutilOptions &Options) { return OutputLocation(Options.OutputFile); // When updating, do in place replacement. - if (Options.OutputFile.empty() && Options.LinkOpts.Update) + if (Options.OutputFile.empty() && + (Options.LinkOpts.Update || !Options.SymbolMap.empty())) return OutputLocation(std::string(InputFile)); // When dumping the debug map, just return an empty output location. This @@ -660,6 +668,8 @@ int dsymutil_main(int argc, char **argv, const llvm::ToolContext &) { return EXIT_FAILURE; } + SymbolMapLoader SymMapLoader(Options.SymbolMap); + for (auto &InputFile : Options.InputFiles) { // Dump the symbol table for each input file and requested arch if (Options.DumpStab) { @@ -750,6 +760,9 @@ int dsymutil_main(int argc, char **argv, const llvm::ToolContext &) { if (Options.DumpDebugMap) continue; + if (!Options.SymbolMap.empty()) + Options.LinkOpts.Translator = SymMapLoader.Load(InputFile, *Map); + if (Map->begin() == Map->end()) { std::lock_guard Guard(ErrorHandlerMutex); WithColor::warning() diff --git a/llvm/tools/llvm-dwarfutil/DebugInfoLinker.cpp b/llvm/tools/llvm-dwarfutil/DebugInfoLinker.cpp index 285dcf75ecd9d..bd17f3c4a6595 100644 --- a/llvm/tools/llvm-dwarfutil/DebugInfoLinker.cpp +++ b/llvm/tools/llvm-dwarfutil/DebugInfoLinker.cpp @@ -338,9 +338,9 @@ Error linkDebugInfoImpl(object::ObjectFile &File, const Options &Options, Triple TargetTriple = File.makeTriple(); std::unique_ptr Streamer; if (Expected> StreamerOrErr = - classic::DwarfStreamer::createStreamer(TargetTriple, - Linker::OutputFileType::Object, - OutStream, ReportWarn)) + classic::DwarfStreamer::createStreamer( + TargetTriple, Linker::OutputFileType::Object, OutStream, nullptr, + ReportWarn)) Streamer = std::move(*StreamerOrErr); else return StreamerOrErr.takeError();