From ee5b9cd59d161990be3436aaee94776ec5d455aa Mon Sep 17 00:00:00 2001 From: Midhunesh Date: Mon, 15 Dec 2025 18:55:36 +0530 Subject: [PATCH] [llvm-symbolizer] Recognize and symbolize archive members (#150401) This PR adds support for selecting specific archive members in llvm-symbolizer using the `archive.a(member.o)` syntax, with architecture-aware member selection. **Key features:** 1. **Archive member selection syntax**: Specify archive members using `archive.a(member.o)` format 2. **Architecture selection via `--default-arch` flag**: Select the appropriate member when multiple members have the same name but different architectures 3. **Architecture selection via `:arch` suffix**: Alternative syntax `archive.a(member.o):arch` for specifying architecture This functionality is primarily designed for AIX big archives, which can contain multiple members with the same name but different architectures (32-bit and 64-bit). However, the implementation works with all archive formats (GNU, BSD, Darwin, big archive) and handles same-named members created with llvm-ar q. --------- Co-authored-by: Midhunesh --- llvm/docs/CommandGuide/llvm-symbolizer.rst | 18 +- .../llvm/DebugInfo/Symbolize/Symbolize.h | 51 ++++- llvm/lib/DebugInfo/Symbolize/Symbolize.cpp | 188 ++++++++++++++---- .../archive-member-big-archive.test | 129 ++++++++++++ .../llvm-symbolizer/archive-member-gnu.test | 126 ++++++++++++ llvm/tools/llvm-symbolizer/Opts.td | 3 +- 6 files changed, 460 insertions(+), 55 deletions(-) create mode 100644 llvm/test/tools/llvm-symbolizer/archive-member-big-archive.test create mode 100644 llvm/test/tools/llvm-symbolizer/archive-member-gnu.test diff --git a/llvm/docs/CommandGuide/llvm-symbolizer.rst b/llvm/docs/CommandGuide/llvm-symbolizer.rst index fb86a694f5d3..cb5127e9f4e1 100644 --- a/llvm/docs/CommandGuide/llvm-symbolizer.rst +++ b/llvm/docs/CommandGuide/llvm-symbolizer.rst @@ -535,16 +535,20 @@ MACH-O SPECIFIC OPTIONS .. option:: --default-arch If a binary contains object files for multiple architectures (e.g. it is a - Mach-O universal binary), symbolize the object file for a given architecture. - You can also specify the architecture by writing ``binary_name:arch_name`` in - the input (see example below). If the architecture is not specified in either - way, the address will not be symbolized. Defaults to empty string. + Mach-O universal binary or an archive with architecture variants), + symbolize the object file for a given architecture. You can also specify + the architecture by writing ``binary_name:arch_name`` in the input (see + example below). For archives, the format ``archive.a(member.o):arch`` + is also supported. If the architecture is not specified, + the address will not be symbolized. Defaults to empty string. .. code-block:: console $ cat addr.txt /tmp/mach_universal_binary:i386 0x1f84 /tmp/mach_universal_binary:x86_64 0x100000f24 + /tmp/archive.a(member.o):ppc 0x1000 + /tmp/archive.a(member.o):ppc64 0x2000 $ llvm-symbolizer < addr.txt _main @@ -553,6 +557,12 @@ MACH-O SPECIFIC OPTIONS _main /tmp/source_x86_64.cc:8 + _foo + /tmp/source_ppc.cc:12 + + _foo + /tmp/source_ppc64.cc:12 + .. option:: --dsym-hint If the debug info for a binary isn't present in the default location, look for diff --git a/llvm/include/llvm/DebugInfo/Symbolize/Symbolize.h b/llvm/include/llvm/DebugInfo/Symbolize/Symbolize.h index fb8f3d8af6b1..4fd7462d52ce 100644 --- a/llvm/include/llvm/DebugInfo/Symbolize/Symbolize.h +++ b/llvm/include/llvm/DebugInfo/Symbolize/Symbolize.h @@ -13,6 +13,7 @@ #ifndef LLVM_DEBUGINFO_SYMBOLIZE_SYMBOLIZE_H #define LLVM_DEBUGINFO_SYMBOLIZE_SYMBOLIZE_H +#include "llvm/ADT/DenseMap.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/ilist_node.h" #include "llvm/ADT/simple_ilist.h" @@ -25,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -196,11 +198,18 @@ private: Expected getOrCreateObjectPair(const std::string &Path, const std::string &ArchName); - /// Return a pointer to object file at specified path, for a specified - /// architecture (e.g. if path refers to a Mach-O universal binary, only one - /// object file from it will be returned). - Expected getOrCreateObject(const std::string &Path, - const std::string &ArchName); + /// Return a pointer to the object file with the specified name, for a + /// specified architecture (e.g. if path refers to a Mach-O universal + /// binary, only one object file from it will be returned). + Expected getOrCreateObject(const std::string &InputPath, + const std::string &DefaultArchName); + + /// Return a pointer to the object file with the specified name, for a + /// specified architecture that is present inside an archive file. + Expected getOrCreateObjectFromArchive(StringRef ArchivePath, + StringRef MemberName, + StringRef ArchName, + StringRef FullPath); /// Update the LRU cache order when a binary is accessed. void recordAccess(CachedBinary &Bin); @@ -216,15 +225,39 @@ private: /// Contains parsed binary for each path, or parsing error. std::map> BinaryForPath; + /// Store the archive path for the object file. + DenseMap ObjectToArchivePath; + /// A list of cached binaries in LRU order. simple_ilist LRUBinaries; /// Sum of the sizes of the cached binaries. size_t CacheSize = 0; - /// Parsed object file for path/architecture pair, where "path" refers - /// to Mach-O universal binary. - std::map, std::unique_ptr> - ObjectForUBPathAndArch; + struct ContainerCacheKey { + std::string Path; + std::string MemberName; + std::string ArchName; + + // Required for map comparison. + bool operator<(const ContainerCacheKey &Other) const { + return std::tie(Path, MemberName, ArchName) < + std::tie(Other.Path, Other.MemberName, Other.ArchName); + } + }; + + /// Parsed object file for each path/member/architecture triple. + /// Used to cache objects extracted from containers (e.g., Mach-O + /// universal binaries, archives). + std::map> ObjectFileCache; + + Expected + loadOrGetBinary(const std::string &ArchivePathKey, + std::optional FullPathKey = std::nullopt); + + Expected findOrCacheObject( + const ContainerCacheKey &Key, + llvm::function_ref>()> Loader, + const std::string &PathForBinaryCache); Options Opts; diff --git a/llvm/lib/DebugInfo/Symbolize/Symbolize.cpp b/llvm/lib/DebugInfo/Symbolize/Symbolize.cpp index 56527719da51..3821f53d26b9 100644 --- a/llvm/lib/DebugInfo/Symbolize/Symbolize.cpp +++ b/llvm/lib/DebugInfo/Symbolize/Symbolize.cpp @@ -21,6 +21,7 @@ #include "llvm/DebugInfo/PDB/PDBContext.h" #include "llvm/DebugInfo/Symbolize/SymbolizableObjectFile.h" #include "llvm/Demangle/Demangle.h" +#include "llvm/Object/Archive.h" #include "llvm/Object/BuildID.h" #include "llvm/Object/COFF.h" #include "llvm/Object/ELFObjectFile.h" @@ -285,7 +286,7 @@ LLVMSymbolizer::findSymbol(ArrayRef BuildID, StringRef Symbol, } void LLVMSymbolizer::flush() { - ObjectForUBPathAndArch.clear(); + ObjectFileCache.clear(); LRUBinaries.clear(); CacheSize = 0; BinaryForPath.clear(); @@ -557,57 +558,164 @@ LLVMSymbolizer::getOrCreateObjectPair(const std::string &Path, if (!DbgObj) DbgObj = Obj; ObjectPair Res = std::make_pair(Obj, DbgObj); - std::string DbgObjPath = DbgObj->getFileName().str(); auto Pair = ObjectPairForPathArch.emplace(std::make_pair(Path, ArchName), Res); - BinaryForPath.find(DbgObjPath)->second.pushEvictor([this, I = Pair.first]() { - ObjectPairForPathArch.erase(I); - }); + std::string FullDbgObjKey; + auto It = ObjectToArchivePath.find(DbgObj); + if (It != ObjectToArchivePath.end()) { + StringRef ArchivePath = It->second; + StringRef MemberName = sys::path::filename(DbgObj->getFileName()); + FullDbgObjKey = (ArchivePath + "(" + MemberName + ")").str(); + } else { + FullDbgObjKey = DbgObj->getFileName().str(); + } + BinaryForPath.find(FullDbgObjKey) + ->second.pushEvictor( + [this, I = Pair.first]() { ObjectPairForPathArch.erase(I); }); return Res; } +Expected +LLVMSymbolizer::loadOrGetBinary(const std::string &ArchivePathKey, + std::optional FullPathKey) { + // If no separate cache key is provided, use the archive path itself. + std::string FullPathKeyStr = + FullPathKey ? FullPathKey->str() : ArchivePathKey; + auto Pair = BinaryForPath.emplace(FullPathKeyStr, OwningBinary()); + if (!Pair.second) { + recordAccess(Pair.first->second); + return Pair.first->second->getBinary(); + } + + Expected> BinOrErr = createBinary(ArchivePathKey); + if (!BinOrErr) + return BinOrErr.takeError(); + + CachedBinary &CachedBin = Pair.first->second; + CachedBin = std::move(*BinOrErr); + CachedBin.pushEvictor([this, I = Pair.first]() { BinaryForPath.erase(I); }); + LRUBinaries.push_back(CachedBin); + CacheSize += CachedBin.size(); + return CachedBin->getBinary(); +} + +Expected LLVMSymbolizer::findOrCacheObject( + const ContainerCacheKey &Key, + llvm::function_ref>()> Loader, + const std::string &PathForBinaryCache) { + auto It = ObjectFileCache.find(Key); + if (It != ObjectFileCache.end()) + return It->second.get(); + + Expected> ObjOrErr = Loader(); + if (!ObjOrErr) { + ObjectFileCache.emplace(Key, std::unique_ptr()); + return ObjOrErr.takeError(); + } + + ObjectFile *Res = ObjOrErr->get(); + auto NewEntry = ObjectFileCache.emplace(Key, std::move(*ObjOrErr)); + auto CacheIter = BinaryForPath.find(PathForBinaryCache); + if (CacheIter != BinaryForPath.end()) + CacheIter->second.pushEvictor( + [this, Iter = NewEntry.first]() { ObjectFileCache.erase(Iter); }); + return Res; +} + +Expected LLVMSymbolizer::getOrCreateObjectFromArchive( + StringRef ArchivePath, StringRef MemberName, StringRef ArchName, + StringRef FullPath) { + Expected BinOrErr = + loadOrGetBinary(ArchivePath.str(), FullPath); + if (!BinOrErr) + return BinOrErr.takeError(); + object::Binary *Bin = *BinOrErr; + + object::Archive *Archive = dyn_cast_if_present(Bin); + if (!Archive) + return createStringError(std::errc::invalid_argument, + "'%s' is not a valid archive", + ArchivePath.str().c_str()); + + Error Err = Error::success(); + for (auto &Child : Archive->children(Err, /*SkipInternal=*/true)) { + Expected NameOrErr = Child.getName(); + if (!NameOrErr) { + // TODO: Report this as a warning to the client. Consider adding a + // callback mechanism to report warning-level issues. + consumeError(NameOrErr.takeError()); + continue; + } + if (*NameOrErr == MemberName) { + Expected> MemberOrErr = + Child.getAsBinary(); + if (!MemberOrErr) { + // TODO: Report this as a warning to the client. Consider adding a + // callback mechanism to report warning-level issues. + consumeError(MemberOrErr.takeError()); + continue; + } + + std::unique_ptr Binary = std::move(*MemberOrErr); + if (auto *Obj = dyn_cast(Binary.get())) { + ObjectToArchivePath[Obj] = ArchivePath.str(); + Triple::ArchType ObjArch = Obj->makeTriple().getArch(); + Triple RequestedTriple; + RequestedTriple.setArch(Triple::getArchTypeForLLVMName(ArchName)); + if (ObjArch != RequestedTriple.getArch()) + continue; + + ContainerCacheKey CacheKey{ArchivePath.str(), MemberName.str(), + ArchName.str()}; + Expected Res = findOrCacheObject( + CacheKey, + [O = std::unique_ptr( + Obj)]() mutable -> Expected> { + return std::move(O); + }, + ArchivePath.str()); + Binary.release(); + return Res; + } + } + } + if (Err) + return std::move(Err); + return createStringError(std::errc::invalid_argument, + "no matching member '%s' with arch '%s' in '%s'", + MemberName.str().c_str(), ArchName.str().c_str(), + ArchivePath.str().c_str()); +} + Expected LLVMSymbolizer::getOrCreateObject(const std::string &Path, const std::string &ArchName) { - Binary *Bin; - auto Pair = BinaryForPath.emplace(Path, OwningBinary()); - if (!Pair.second) { - Bin = Pair.first->second->getBinary(); - recordAccess(Pair.first->second); - } else { - Expected> BinOrErr = createBinary(Path); - if (!BinOrErr) - return BinOrErr.takeError(); - - CachedBinary &CachedBin = Pair.first->second; - CachedBin = std::move(BinOrErr.get()); - CachedBin.pushEvictor([this, I = Pair.first]() { BinaryForPath.erase(I); }); - LRUBinaries.push_back(CachedBin); - CacheSize += CachedBin.size(); - Bin = CachedBin->getBinary(); + // First check for archive(member) format - more efficient to check closing + // paren first. + if (!Path.empty() && Path.back() == ')') { + size_t OpenParen = Path.rfind('(', Path.size() - 1); + if (OpenParen != std::string::npos) { + StringRef ArchivePath = StringRef(Path).substr(0, OpenParen); + StringRef MemberName = + StringRef(Path).substr(OpenParen + 1, Path.size() - OpenParen - 2); + return getOrCreateObjectFromArchive(ArchivePath, MemberName, ArchName, + Path); + } } - if (!Bin) - return static_cast(nullptr); + Expected BinOrErr = loadOrGetBinary(Path); + if (!BinOrErr) + return BinOrErr.takeError(); + object::Binary *Bin = *BinOrErr; if (MachOUniversalBinary *UB = dyn_cast_or_null(Bin)) { - auto I = ObjectForUBPathAndArch.find(std::make_pair(Path, ArchName)); - if (I != ObjectForUBPathAndArch.end()) - return I->second.get(); - - Expected> ObjOrErr = - UB->getMachOObjectForArch(ArchName); - if (!ObjOrErr) { - ObjectForUBPathAndArch.emplace(std::make_pair(Path, ArchName), - std::unique_ptr()); - return ObjOrErr.takeError(); - } - ObjectFile *Res = ObjOrErr->get(); - auto Pair = ObjectForUBPathAndArch.emplace(std::make_pair(Path, ArchName), - std::move(ObjOrErr.get())); - BinaryForPath.find(Path)->second.pushEvictor( - [this, Iter = Pair.first]() { ObjectForUBPathAndArch.erase(Iter); }); - return Res; + ContainerCacheKey CacheKey{Path, "", ArchName}; + return findOrCacheObject( + CacheKey, + [UB, ArchName]() -> Expected> { + return UB->getMachOObjectForArch(ArchName); + }, + Path); } if (Bin->isObject()) { return cast(Bin); diff --git a/llvm/test/tools/llvm-symbolizer/archive-member-big-archive.test b/llvm/test/tools/llvm-symbolizer/archive-member-big-archive.test new file mode 100644 index 000000000000..e35d7272b641 --- /dev/null +++ b/llvm/test/tools/llvm-symbolizer/archive-member-big-archive.test @@ -0,0 +1,129 @@ +## Test big archive recognition with same-named members (XCOFF format). +## Big archives can contain multiple members with the same name but different +## architectures. This test verifies llvm-symbolizer can disambiguate using +## architecture specification. + +# RUN: rm -rf %t.dir && mkdir %t.dir && cd %t.dir + +# RUN: yaml2obj -DSYMBOL=foo32 --docnum=1 %s -o 32.o +# RUN: yaml2obj -DSYMBOL=foo64 --docnum=2 %s -o 64.o +# RUN: yaml2obj -DSYMBOL=foo1 --docnum=2 %s -o 1.o +# RUN: yaml2obj -DSYMBOL=foo2 --docnum=2 %s -o 2.o + +## Create big archive with unique member names. +# RUN: llvm-ar --format=bigarchive %if system-aix %{-X64%} crv archive.a 1.o 2.o + +## Create big archive with duplicate member names (64-bit first, 32-bit second). +# RUN: cp 64.o test.o +# RUN: llvm-ar --format=bigarchive %if system-aix %{-X64%} crv archive.a test.o +# RUN: cp 32.o test.o +# RUN: llvm-ar --format=bigarchive %if system-aix %{-X32 rv%} %else %{qv%} archive.a test.o + +## Test symbolization with unique member names. +# RUN: llvm-symbolizer --default-arch=ppc64 --obj="archive.a(1.o)" 0x0 | FileCheck %s --check-prefix=CHECK-1 +# RUN: llvm-symbolizer --obj="archive.a(1.o):ppc64" 0x0 | FileCheck %s --check-prefix=CHECK-1 +# RUN: llvm-symbolizer --default-arch=ppc64 --obj="archive.a(2.o)" 0x0 | FileCheck %s --check-prefix=CHECK-2 +# RUN: llvm-symbolizer --obj="archive.a(2.o):ppc64" 0x0 | FileCheck %s --check-prefix=CHECK-2 + +# CHECK-1: foo1 +# CHECK-2: foo2 + +## Test 64-bit member (first in archive). +# RUN: llvm-symbolizer --default-arch=ppc64 --obj="archive.a(test.o)" 0x0 | FileCheck %s --check-prefix=CHECK-64 +# RUN: llvm-symbolizer --obj="archive.a(test.o):ppc64" 0x0 | FileCheck %s --check-prefix=CHECK-64 + +# CHECK-64: foo64 + +## Test 32-bit member (second in archive). +# RUN: llvm-symbolizer --default-arch=ppc --obj="archive.a(test.o)" 0x0 | FileCheck %s --check-prefix=CHECK-32 +# RUN: llvm-symbolizer --obj="archive.a(test.o):ppc" 0x0 | FileCheck %s --check-prefix=CHECK-32 + +# CHECK-32: foo32 + +## Test error: no matching architecture. +# RUN: not llvm-symbolizer --default-arch=x86_64 --obj="archive.a(test.o)" 0x1000 2>&1 | FileCheck %s --check-prefix=CHECK-NOARCH +# CHECK-NOARCH: error: 'archive.a(test.o)': no matching member 'test.o' with arch 'x86_64' in 'archive.a' + +## Test error: no architecture specified. +# RUN: not llvm-symbolizer --obj="archive.a(test.o)" 0x1000 2>&1 | FileCheck %s --check-prefix=CHECK-NOARCHSPEC +# CHECK-NOARCHSPEC: error: 'archive.a(test.o)': no matching member 'test.o' with arch '' in 'archive.a' + +## Test error: nonexistent member. +# RUN: not llvm-symbolizer --default-arch=ppc --obj="archive.a(nonexistent.o)" 0x1000 2>&1 | FileCheck %s --check-prefix=CHECK-ERROR +# CHECK-ERROR: error: 'archive.a(nonexistent.o)': no matching member 'nonexistent.o' with arch 'ppc' in 'archive.a' + +## Test error: not an archive. +# RUN: not llvm-symbolizer --obj="test.o(test.o)" 0x1000 2>&1 | FileCheck %s --check-prefix=CHECK-NOTARCHIVE +# CHECK-NOTARCHIVE: error: 'test.o(test.o)': 'test.o' is not a valid archive + +## Test error: empty member name. +# RUN: not llvm-symbolizer --obj="archive.a():ppc64" 0x1000 2>&1 | FileCheck %s --check-prefix=CHECK-NONAME +# CHECK-NONAME: error: 'archive.a():ppc64': no matching member '' with arch 'ppc64' in 'archive.a' + +## 32-bit XCOFF object. +--- !XCOFF +FileHeader: + MagicNumber: 0x1DF +Sections: + - Name: .text + Flags: [ STYP_TEXT ] + SectionData: 4E800020 +Symbols: + - Name: .file + Section: N_DEBUG + StorageClass: C_FILE + NumberOfAuxEntries: 1 + AuxEntries: + - Type: AUX_FILE + FileNameOrString: foo.c + FileStringType: XFT_FN + - Name: .[[SYMBOL]] + Section: .text + StorageClass: C_EXT + NumberOfAuxEntries: 1 + AuxEntries: + - Type: AUX_CSECT + SymbolType: XTY_LD + StorageMappingClass: XMC_PR + - Name: [[SYMBOL]] + Section: .text + StorageClass: C_EXT + NumberOfAuxEntries: 1 + AuxEntries: + - Type: AUX_CSECT + SymbolType: XTY_SD + StorageMappingClass: XMC_PR + +## 64-bit XCOFF object. +--- !XCOFF +FileHeader: + MagicNumber: 0x1F7 +Sections: + - Name: .text + Flags: [ STYP_TEXT ] + SectionData: 4E800020 +Symbols: + - Name: .file + Section: N_DEBUG + StorageClass: C_FILE + NumberOfAuxEntries: 1 + AuxEntries: + - Type: AUX_FILE + FileNameOrString: foo.c + FileStringType: XFT_FN + - Name: .[[SYMBOL]] + Section: .text + StorageClass: C_EXT + NumberOfAuxEntries: 1 + AuxEntries: + - Type: AUX_CSECT + SymbolType: XTY_LD + StorageMappingClass: XMC_PR + - Name: [[SYMBOL]] + Section: .text + StorageClass: C_EXT + NumberOfAuxEntries: 1 + AuxEntries: + - Type: AUX_CSECT + SymbolType: XTY_SD + StorageMappingClass: XMC_PR diff --git a/llvm/test/tools/llvm-symbolizer/archive-member-gnu.test b/llvm/test/tools/llvm-symbolizer/archive-member-gnu.test new file mode 100644 index 000000000000..75cafde02110 --- /dev/null +++ b/llvm/test/tools/llvm-symbolizer/archive-member-gnu.test @@ -0,0 +1,126 @@ +## Test archive member recognition (GNU archive format). + +# RUN: rm -rf %t.dir && mkdir %t.dir && cd %t.dir + +# RUN: yaml2obj -DSYMBOL=foo32 --docnum=1 %s -o 32.o +# RUN: yaml2obj -DSYMBOL=foo64 --docnum=2 %s -o 64.o +# RUN: yaml2obj -DSYMBOL=foo1 --docnum=2 %s -o 1.o +# RUN: yaml2obj -DSYMBOL=foo2 --docnum=2 %s -o 2.o + +## Create archive with unique member names. +# RUN: llvm-ar --format=gnu crv archive.a 1.o 2.o + +## Create archive with duplicate member names (64-bit first, 32-bit second). +# RUN: cp 64.o test.o +# RUN: llvm-ar --format=gnu crv archive.a test.o +# RUN: cp 32.o test.o +# RUN: llvm-ar --format=gnu qv archive.a test.o + +## Test symbolization with unique member names. +# RUN: llvm-symbolizer --default-arch=ppc64 --obj="archive.a(1.o)" 0x0 | FileCheck %s --check-prefix=CHECK-1 +# RUN: llvm-symbolizer --obj="archive.a(1.o):ppc64" 0x0 | FileCheck %s --check-prefix=CHECK-1 +# RUN: llvm-symbolizer --default-arch=ppc64 --obj="archive.a(2.o)" 0x0 | FileCheck %s --check-prefix=CHECK-2 +# RUN: llvm-symbolizer --obj="archive.a(2.o):ppc64" 0x0 | FileCheck %s --check-prefix=CHECK-2 + +# CHECK-1: foo1 +# CHECK-2: foo2 + +## Test 64-bit member (first in archive). +# RUN: llvm-symbolizer --default-arch=ppc64 --obj="archive.a(test.o)" 0x0 | FileCheck %s --check-prefix=CHECK-64 +# RUN: llvm-symbolizer --obj="archive.a(test.o):ppc64" 0x0 | FileCheck %s --check-prefix=CHECK-64 + +# CHECK-64: foo64 + +## Test 32-bit member (second in archive). +# RUN: llvm-symbolizer --default-arch=ppc --obj="archive.a(test.o)" 0x0 | FileCheck %s --check-prefix=CHECK-32 +# RUN: llvm-symbolizer --obj="archive.a(test.o):ppc" 0x0 | FileCheck %s --check-prefix=CHECK-32 + +# CHECK-32: foo32 + +## Test error: no matching architecture. +# RUN: not llvm-symbolizer --default-arch=x86_64 --obj="archive.a(test.o)" 0x1000 2>&1 | FileCheck %s --check-prefix=CHECK-NOARCH +# CHECK-NOARCH: error: 'archive.a(test.o)': no matching member 'test.o' with arch 'x86_64' in 'archive.a' + +## Test error: no architecture specified. +# RUN: not llvm-symbolizer --obj="archive.a(test.o)" 0x1000 2>&1 | FileCheck %s --check-prefix=CHECK-NOARCHSPEC +# CHECK-NOARCHSPEC: error: 'archive.a(test.o)': no matching member 'test.o' with arch '' in 'archive.a' + +## Test error: nonexistent member. +# RUN: not llvm-symbolizer --default-arch=ppc64 --obj="archive.a(nonexistent.o)" 0x1000 2>&1 | FileCheck %s --check-prefix=CHECK-ERROR +# CHECK-ERROR: error: 'archive.a(nonexistent.o)': no matching member 'nonexistent.o' with arch 'ppc64' in 'archive.a' + +## Test error: not an archive. +# RUN: not llvm-symbolizer --obj="1.o(1.o)" 0x1000 2>&1 | FileCheck %s --check-prefix=CHECK-NOTARCHIVE +# CHECK-NOTARCHIVE: error: '1.o(1.o)': '1.o' is not a valid archive + +## Test error: empty member name. +# RUN: not llvm-symbolizer --obj="archive.a():ppc64" 0x1000 2>&1 | FileCheck %s --check-prefix=CHECK-NONAME +# CHECK-NONAME: error: 'archive.a():ppc64': no matching member '' with arch 'ppc64' in 'archive.a' + +## 32-bit XCOFF object. +--- !XCOFF +FileHeader: + MagicNumber: 0x1DF +Sections: + - Name: .text + Flags: [ STYP_TEXT ] + SectionData: 4E800020 +Symbols: + - Name: .file + Section: N_DEBUG + StorageClass: C_FILE + NumberOfAuxEntries: 1 + AuxEntries: + - Type: AUX_FILE + FileNameOrString: foo.c + FileStringType: XFT_FN + - Name: .[[SYMBOL]] + Section: .text + StorageClass: C_EXT + NumberOfAuxEntries: 1 + AuxEntries: + - Type: AUX_CSECT + SymbolType: XTY_LD + StorageMappingClass: XMC_PR + - Name: [[SYMBOL]] + Section: .text + StorageClass: C_EXT + NumberOfAuxEntries: 1 + AuxEntries: + - Type: AUX_CSECT + SymbolType: XTY_SD + StorageMappingClass: XMC_PR + +## 64-bit XCOFF object. +--- !XCOFF +FileHeader: + MagicNumber: 0x1F7 +Sections: + - Name: .text + Flags: [ STYP_TEXT ] + SectionData: 4E800020 +Symbols: + - Name: .file + Section: N_DEBUG + StorageClass: C_FILE + NumberOfAuxEntries: 1 + AuxEntries: + - Type: AUX_FILE + FileNameOrString: foo.c + FileStringType: XFT_FN + - Name: .[[SYMBOL]] + Section: .text + StorageClass: C_EXT + NumberOfAuxEntries: 1 + AuxEntries: + - Type: AUX_CSECT + SymbolType: XTY_LD + StorageMappingClass: XMC_PR + - Name: [[SYMBOL]] + Section: .text + StorageClass: C_EXT + NumberOfAuxEntries: 1 + AuxEntries: + - Type: AUX_CSECT + SymbolType: XTY_SD + StorageMappingClass: XMC_PR diff --git a/llvm/tools/llvm-symbolizer/Opts.td b/llvm/tools/llvm-symbolizer/Opts.td index 10f1e6dbbddf..ba22cfdde623 100644 --- a/llvm/tools/llvm-symbolizer/Opts.td +++ b/llvm/tools/llvm-symbolizer/Opts.td @@ -31,8 +31,7 @@ def color_EQ : Joined<["--"], "color=">, HelpText<"Whether to use color when sym defm debug_file_directory : Eq<"debug-file-directory", "Path to directory where to look for debug files">, MetaVarName<"">; defm debuginfod : B<"debuginfod", "Use debuginfod to find debug binaries", "Don't use debuginfod to find debug binaries">; defm default_arch - : Eq<"default-arch", "Default architecture (for multi-arch objects)">, - Group; + : Eq<"default-arch", "Default architecture for multi-arch containers (e.g. Mach-O objects or AIX archives)">; defm demangle : B<"demangle", "Demangle function names", "Don't demangle function names">; def disable_gsym : F<"disable-gsym", "Don't consider using GSYM files for symbolication">, Group; def filter_markup : Flag<["--"], "filter-markup">, HelpText<"Filter symbolizer markup from stdin.">;