mirror of
https://github.com/intel/llvm.git
synced 2026-01-12 01:30:26 +08:00
[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 <midhuensh.p@ibm.com>
This commit is contained in:
@@ -535,16 +535,20 @@ MACH-O SPECIFIC OPTIONS
|
||||
.. option:: --default-arch <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 <path/to/file.dSYM>
|
||||
|
||||
If the debug info for a binary isn't present in the default location, look for
|
||||
|
||||
@@ -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 <cstdint>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
@@ -196,11 +198,18 @@ private:
|
||||
Expected<ObjectPair> 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<ObjectFile *> 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<ObjectFile *> 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<ObjectFile *> 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<std::string, CachedBinary, std::less<>> BinaryForPath;
|
||||
|
||||
/// Store the archive path for the object file.
|
||||
DenseMap<const object::ObjectFile *, std::string> ObjectToArchivePath;
|
||||
|
||||
/// A list of cached binaries in LRU order.
|
||||
simple_ilist<CachedBinary> 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::pair<std::string, std::string>, std::unique_ptr<ObjectFile>>
|
||||
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<ContainerCacheKey, std::unique_ptr<ObjectFile>> ObjectFileCache;
|
||||
|
||||
Expected<object::Binary *>
|
||||
loadOrGetBinary(const std::string &ArchivePathKey,
|
||||
std::optional<StringRef> FullPathKey = std::nullopt);
|
||||
|
||||
Expected<ObjectFile *> findOrCacheObject(
|
||||
const ContainerCacheKey &Key,
|
||||
llvm::function_ref<Expected<std::unique_ptr<ObjectFile>>()> Loader,
|
||||
const std::string &PathForBinaryCache);
|
||||
|
||||
Options Opts;
|
||||
|
||||
|
||||
@@ -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<uint8_t> 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<object::Binary *>
|
||||
LLVMSymbolizer::loadOrGetBinary(const std::string &ArchivePathKey,
|
||||
std::optional<StringRef> 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<Binary>());
|
||||
if (!Pair.second) {
|
||||
recordAccess(Pair.first->second);
|
||||
return Pair.first->second->getBinary();
|
||||
}
|
||||
|
||||
Expected<OwningBinary<Binary>> 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<ObjectFile *> LLVMSymbolizer::findOrCacheObject(
|
||||
const ContainerCacheKey &Key,
|
||||
llvm::function_ref<Expected<std::unique_ptr<ObjectFile>>()> Loader,
|
||||
const std::string &PathForBinaryCache) {
|
||||
auto It = ObjectFileCache.find(Key);
|
||||
if (It != ObjectFileCache.end())
|
||||
return It->second.get();
|
||||
|
||||
Expected<std::unique_ptr<ObjectFile>> ObjOrErr = Loader();
|
||||
if (!ObjOrErr) {
|
||||
ObjectFileCache.emplace(Key, std::unique_ptr<ObjectFile>());
|
||||
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<ObjectFile *> LLVMSymbolizer::getOrCreateObjectFromArchive(
|
||||
StringRef ArchivePath, StringRef MemberName, StringRef ArchName,
|
||||
StringRef FullPath) {
|
||||
Expected<object::Binary *> BinOrErr =
|
||||
loadOrGetBinary(ArchivePath.str(), FullPath);
|
||||
if (!BinOrErr)
|
||||
return BinOrErr.takeError();
|
||||
object::Binary *Bin = *BinOrErr;
|
||||
|
||||
object::Archive *Archive = dyn_cast_if_present<object::Archive>(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<StringRef> 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<std::unique_ptr<object::Binary>> 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<object::Binary> Binary = std::move(*MemberOrErr);
|
||||
if (auto *Obj = dyn_cast<object::ObjectFile>(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<ObjectFile *> Res = findOrCacheObject(
|
||||
CacheKey,
|
||||
[O = std::unique_ptr<ObjectFile>(
|
||||
Obj)]() mutable -> Expected<std::unique_ptr<ObjectFile>> {
|
||||
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<ObjectFile *>
|
||||
LLVMSymbolizer::getOrCreateObject(const std::string &Path,
|
||||
const std::string &ArchName) {
|
||||
Binary *Bin;
|
||||
auto Pair = BinaryForPath.emplace(Path, OwningBinary<Binary>());
|
||||
if (!Pair.second) {
|
||||
Bin = Pair.first->second->getBinary();
|
||||
recordAccess(Pair.first->second);
|
||||
} else {
|
||||
Expected<OwningBinary<Binary>> 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<ObjectFile *>(nullptr);
|
||||
Expected<object::Binary *> BinOrErr = loadOrGetBinary(Path);
|
||||
if (!BinOrErr)
|
||||
return BinOrErr.takeError();
|
||||
object::Binary *Bin = *BinOrErr;
|
||||
|
||||
if (MachOUniversalBinary *UB = dyn_cast_or_null<MachOUniversalBinary>(Bin)) {
|
||||
auto I = ObjectForUBPathAndArch.find(std::make_pair(Path, ArchName));
|
||||
if (I != ObjectForUBPathAndArch.end())
|
||||
return I->second.get();
|
||||
|
||||
Expected<std::unique_ptr<ObjectFile>> ObjOrErr =
|
||||
UB->getMachOObjectForArch(ArchName);
|
||||
if (!ObjOrErr) {
|
||||
ObjectForUBPathAndArch.emplace(std::make_pair(Path, ArchName),
|
||||
std::unique_ptr<ObjectFile>());
|
||||
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<std::unique_ptr<ObjectFile>> {
|
||||
return UB->getMachOObjectForArch(ArchName);
|
||||
},
|
||||
Path);
|
||||
}
|
||||
if (Bin->isObject()) {
|
||||
return cast<ObjectFile>(Bin);
|
||||
|
||||
129
llvm/test/tools/llvm-symbolizer/archive-member-big-archive.test
Normal file
129
llvm/test/tools/llvm-symbolizer/archive-member-big-archive.test
Normal file
@@ -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
|
||||
126
llvm/test/tools/llvm-symbolizer/archive-member-gnu.test
Normal file
126
llvm/test/tools/llvm-symbolizer/archive-member-gnu.test
Normal file
@@ -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
|
||||
@@ -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<"<dir>">;
|
||||
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<grp_mach_o>;
|
||||
: 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<grp_gsym>;
|
||||
def filter_markup : Flag<["--"], "filter-markup">, HelpText<"Filter symbolizer markup from stdin.">;
|
||||
|
||||
Reference in New Issue
Block a user