diff --git a/llvm/test/tools/dsymutil/ARM/static-archive-collision.test b/llvm/test/tools/dsymutil/ARM/static-archive-collision.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/dsymutil/ARM/static-archive-collision.test @@ -0,0 +1,26 @@ +$ cat f.c +int f() { + volatile int i; + return i; +} +$ cat g.c +int g() { + volatile int i; + return i; +} +$ cat main.c +int f(); +int g(); + +int main() { + return f() + g(); +} +$ clang -g f.c -c -o f/foo.o +$ clang -g g.c -c -o g/foo.o +$ libtool -static f/foo.o g/foo.o -o foo.a +$ clang main.o foo.a -o main.out + +RUN: dsymutil -oso-prepend-path %p/../Inputs %p/../Inputs/private/tmp/collision/main.out --dump-debug-map 2>&1 | FileCheck %s +CHECK: skipping debug map object with duplicate name and timestamp: 1969-12-31 16:00:00.000000000 /private/tmp/collision/foo.a(foo.o) +CHECK-NOT: could not find object file symbol for symbol _g +CHECK-NOT: could not find object file symbol for symbol _f diff --git a/llvm/test/tools/dsymutil/Inputs/private/tmp/collision/foo.a b/llvm/test/tools/dsymutil/Inputs/private/tmp/collision/foo.a new file mode 100644 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 GIT binary patch literal 0 Hc$@; + private: std::string BinaryPath; SmallVector Archs; @@ -70,12 +74,18 @@ /// Element of the debug map corresponding to the current object file. DebugMapObject *CurrentDebugMapObject; + /// Whether we need to skip the current debug map object. + bool SkipDebugMapObject; + /// Holds function info while function scope processing. const char *CurrentFunctionName; uint64_t CurrentFunctionAddress; std::unique_ptr parseOneBinary(const MachOObjectFile &MainBinary, StringRef BinaryPath); + void handleStabDebugMap( + const MachOObjectFile &MainBinary, + std::function F); void switchToNewDebugMapObject(StringRef Filename, @@ -85,13 +95,21 @@ std::vector getMainBinarySymbolNames(uint64_t Value); void loadMainBinarySymbols(const MachOObjectFile &MainBinary); void loadCurrentObjectFileSymbols(const object::MachOObjectFile &Obj); + + void handleStabOSOEntry(uint32_t StringIndex, uint8_t Type, + uint8_t SectionIndex, uint16_t Flags, uint64_t Value, + llvm::DenseSet &OSOs, + llvm::SmallSet &Duplicates); void handleStabSymbolTableEntry(uint32_t StringIndex, uint8_t Type, uint8_t SectionIndex, uint16_t Flags, - uint64_t Value); + uint64_t Value, + const llvm::SmallSet &Duplicates); - template void handleStabDebugMapEntry(const STEType &STE) { - handleStabSymbolTableEntry(STE.n_strx, STE.n_type, STE.n_sect, STE.n_desc, - STE.n_value); + template + void handleStabDebugMapEntry( + const STEType &STE, + std::function F) { + F(STE.n_strx, STE.n_type, STE.n_sect, STE.n_desc, STE.n_value); } void addCommonSymbols(); @@ -140,6 +158,7 @@ CurrentObjectAliasMap.clear(); SeenAliasValues.clear(); CurrentDebugMapObject = nullptr; + SkipDebugMapObject = false; } /// Commons symbols won't show up in the symbol map but might need to be @@ -199,6 +218,18 @@ return std::string(T.getArchName()); } +void MachODebugMapParser::handleStabDebugMap( + const MachOObjectFile &MainBinary, + std::function F) { + for (const SymbolRef &Symbol : MainBinary.symbols()) { + const DataRefImpl &DRI = Symbol.getRawDataRefImpl(); + if (MainBinary.is64Bit()) + handleStabDebugMapEntry(MainBinary.getSymbol64TableEntry(DRI), F); + else + handleStabDebugMapEntry(MainBinary.getSymbolTableEntry(DRI), F); + } +} + std::unique_ptr MachODebugMapParser::parseOneBinary(const MachOObjectFile &MainBinary, StringRef BinaryPath) { @@ -206,14 +237,41 @@ MainBinary.getUuid()); loadMainBinarySymbols(MainBinary); MainBinaryStrings = MainBinary.getStringTableData(); - for (const SymbolRef &Symbol : MainBinary.symbols()) { - const DataRefImpl &DRI = Symbol.getRawDataRefImpl(); - if (MainBinary.is64Bit()) - handleStabDebugMapEntry(MainBinary.getSymbol64TableEntry(DRI)); - else - handleStabDebugMapEntry(MainBinary.getSymbolTableEntry(DRI)); + + // Static archives can contain multiple object files with identical names, in + // which case the timestamp is used to disambiguate. However, if both are + // identical, there's no way to tell them apart. Detect this and skip + // duplicate debug map objects. + llvm::DenseSet OSOs; + llvm::SmallSet Duplicates; + + // Iterate over all the STABS to find duplicate OSO entries. + handleStabDebugMap(MainBinary, + [&](uint32_t StringIndex, uint8_t Type, + uint8_t SectionIndex, uint16_t Flags, uint64_t Value) { + handleStabOSOEntry(StringIndex, Type, SectionIndex, + Flags, Value, OSOs, Duplicates); + }); + + // Print an informative warning with the duplicate object file name and time + // stamp. + for (const auto &OSO : Duplicates) { + std::string Buffer; + llvm::raw_string_ostream OS(Buffer); + OS << sys::TimePoint(sys::toTimePoint(OSO.second)); + Warning("skipping debug map object with duplicate name and timestamp: " + + OS.str() + Twine(" ") + Twine(OSO.first)); } + // Build the debug map by iterating over the STABS again but ignore the + // duplicate debug objects. + handleStabDebugMap(MainBinary, [&](uint32_t StringIndex, uint8_t Type, + uint8_t SectionIndex, uint16_t Flags, + uint64_t Value) { + handleStabSymbolTableEntry(StringIndex, Type, SectionIndex, Flags, Value, + Duplicates); + }); + resetParserState(); return std::move(Result); } @@ -408,20 +466,38 @@ return std::move(Results); } +void MachODebugMapParser::handleStabOSOEntry( + uint32_t StringIndex, uint8_t Type, uint8_t SectionIndex, uint16_t Flags, + uint64_t Value, llvm::DenseSet &OSOs, + llvm::SmallSet &Duplicates) { + if (Type != MachO::N_OSO) + return; + + OSO O(&MainBinaryStrings.data()[StringIndex], Value); + if (!OSOs.insert(O).second) + Duplicates.insert(O); +} + /// Interpret the STAB entries to fill the DebugMap. -void MachODebugMapParser::handleStabSymbolTableEntry(uint32_t StringIndex, - uint8_t Type, - uint8_t SectionIndex, - uint16_t Flags, - uint64_t Value) { +void MachODebugMapParser::handleStabSymbolTableEntry( + uint32_t StringIndex, uint8_t Type, uint8_t SectionIndex, uint16_t Flags, + uint64_t Value, const llvm::SmallSet &Duplicates) { if (!(Type & MachO::N_STAB)) return; const char *Name = &MainBinaryStrings.data()[StringIndex]; // An N_OSO entry represents the start of a new object file description. - if (Type == MachO::N_OSO) + if (Type == MachO::N_OSO) { + if (Duplicates.count(OSO(Name, Value))) { + SkipDebugMapObject = true; + return; + } return switchToNewDebugMapObject(Name, sys::toTimePoint(Value)); + } + + if (SkipDebugMapObject) + return; if (Type == MachO::N_AST) { SmallString<80> Path(PathPrefix);