diff --git a/llvm/docs/CommandGuide/llvm-symbolizer.rst b/llvm/docs/CommandGuide/llvm-symbolizer.rst index fb86a694f5d3c..b665a28d266ec 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 AIX 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 AIX 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 fb8f3d8af6b1b..d9764d20cd638 100644 --- a/llvm/include/llvm/DebugInfo/Symbolize/Symbolize.h +++ b/llvm/include/llvm/DebugInfo/Symbolize/Symbolize.h @@ -196,11 +196,17 @@ class LLVMSymbolizer { 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); /// Update the LRU cache order when a binary is accessed. void recordAccess(CachedBinary &Bin); @@ -221,10 +227,30 @@ class LLVMSymbolizer { /// 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 ArchiveCacheKey { + std::string ArchivePath; + std::string MemberName; + std::string ArchName; + + // Required for map comparison. + bool operator<(const ArchiveCacheKey &Other) const { + return std::tie(ArchivePath, MemberName, ArchName) < + std::tie(Other.ArchivePath, Other.MemberName, Other.ArchName); + } + }; + + /// Parsed object file for path/object/architecture pair, where + /// "path" refers to Mach-O universal binary. + std::map> ObjectFileCache; + + /// Helper function to load binary. + Expected loadOrGetBinary(const std::string &Path); + + /// Helper function to find and get object. + Expected findOrCacheObject( + const ArchiveCacheKey &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 56527719da51f..983f82f95fcd3 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,146 @@ 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 DbgObjPath = DbgObj->getFileName().str(); + auto BinIter = BinaryForPath.find(DbgObjPath); + if (BinIter != BinaryForPath.end()) { + BinIter->second.pushEvictor( + [this, I = Pair.first]() { ObjectPairForPathArch.erase(I); }); + } return Res; } -Expected -LLVMSymbolizer::getOrCreateObject(const std::string &Path, - const std::string &ArchName) { - Binary *Bin; +Expected +LLVMSymbolizer::loadOrGetBinary(const std::string &Path) { 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(); + return Pair.first->second->getBinary(); + } - 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(); + Expected> BinOrErr = createBinary(Path); + if (!BinOrErr) { + return BinOrErr.takeError(); } - if (!Bin) - return static_cast(nullptr); + 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(); +} - 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(); +Expected LLVMSymbolizer::findOrCacheObject( + const ArchiveCacheKey &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) { + Expected BinOrErr = loadOrGetBinary(ArchivePath.str()); + 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(); + // On AIX, archives can contain multiple members with the same name but + // different types. We need to check all matches and find one that matches + // both name and architecture. + for (auto &Child : Archive->children(Err, /*SkipInternal=*/true)) { + Expected NameOrErr = Child.getName(); + if (!NameOrErr) + continue; + if (*NameOrErr == MemberName) { + Expected> MemberOrErr = + Child.getAsBinary(); + if (!MemberOrErr) + continue; + + std::unique_ptr Binary = std::move(*MemberOrErr); + if (auto *Obj = dyn_cast(Binary.get())) { + Triple::ArchType ObjArch = Obj->makeTriple().getArch(); + Triple RequestedTriple; + RequestedTriple.setArch(Triple::getArchTypeForLLVMName(ArchName)); + if (ObjArch != RequestedTriple.getArch()) + continue; + + ArchiveCacheKey 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; + } } - 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; + } + 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) { + // 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); + } + } + + Expected BinOrErr = loadOrGetBinary(Path); + if (!BinOrErr) + return BinOrErr.takeError(); + object::Binary *Bin = *BinOrErr; + + if (MachOUniversalBinary *UB = dyn_cast_or_null(Bin)) { + ArchiveCacheKey CacheKey{Path, "", ArchName}; + return findOrCacheObject( + CacheKey, + [UB, ArchName]() -> Expected> { + return UB->getMachOObjectForArch(ArchName); + }, + Path); } if (Bin->isObject()) { return cast(Bin); @@ -648,7 +738,9 @@ LLVMSymbolizer::getOrCreateModuleInfo(StringRef ModuleName) { auto I = Modules.find(ModuleName); if (I != Modules.end()) { - recordAccess(BinaryForPath.find(BinaryName)->second); + auto BinIter = BinaryForPath.find(BinaryName); + if (BinIter != BinaryForPath.end()) + recordAccess(BinIter->second); return I->second.get(); } @@ -716,9 +808,9 @@ LLVMSymbolizer::getOrCreateModuleInfo(StringRef ModuleName) { createModuleInfo(Objects.first, std::move(Context), ModuleName); if (ModuleOrErr) { auto I = Modules.find(ModuleName); - BinaryForPath.find(BinaryName)->second.pushEvictor([this, I]() { - Modules.erase(I); - }); + auto BinIter = BinaryForPath.find(BinaryName); + if (BinIter != BinaryForPath.end()) + BinIter->second.pushEvictor([this, I]() { Modules.erase(I); }); } return ModuleOrErr; } diff --git a/llvm/test/DebugInfo/Inputs/big-archive-32.yaml b/llvm/test/DebugInfo/Inputs/big-archive-32.yaml new file mode 100644 index 0000000000000..2080607a1a88c --- /dev/null +++ b/llvm/test/DebugInfo/Inputs/big-archive-32.yaml @@ -0,0 +1,119 @@ +--- !XCOFF +FileHeader: + MagicNumber: 0x1DF + NumberOfSections: 2 + CreationTime: 0 + OffsetToSymbolTable: 0xA0 + EntriesInSymbolTable: 11 + AuxiliaryHeaderSize: 0 + Flags: 0x0 +Sections: + - Name: .text + Address: 0x0 + Size: 0x1C + FileOffsetToData: 0x64 + FileOffsetToRelocations: 0x0 + FileOffsetToLineNumbers: 0x0 + NumberOfRelocations: 0x0 + NumberOfLineNumbers: 0x0 + Flags: [ STYP_TEXT ] + SectionData: 4E800020000000000009204000000001000000040003666F6F000000 + - Name: .data + Address: 0x1C + Size: 0xC + FileOffsetToData: 0x80 + FileOffsetToRelocations: 0x8C + FileOffsetToLineNumbers: 0x0 + NumberOfRelocations: 0x2 + NumberOfLineNumbers: 0x0 + Flags: [ STYP_DATA ] + SectionData: '000000000000002800000000' + Relocations: + - Address: 0x1C + Symbol: 0x5 + Info: 0x1F + Type: 0x0 + - Address: 0x20 + Symbol: 0x9 + Info: 0x1F + Type: 0x0 +Symbols: + - Name: .file + Value: 0x0 + Section: N_DEBUG + Type: 0x18 + StorageClass: C_FILE + NumberOfAuxEntries: 2 + AuxEntries: + - Type: AUX_FILE + FileNameOrString: foo.c + FileStringType: XFT_FN + - Type: AUX_FILE + FileNameOrString: 'IBM Open XL C/C++ for AIX 17.1.3 (5725-C72, 5765-J18), version 17.1.3.0, LLVM version 21.0.0git (145c02cece3630765e6412e6820bc446ddb4e138)' + FileStringType: XFT_CV + - Name: '' + Value: 0x0 + Section: .text + Type: 0x0 + StorageClass: C_HIDEXT + NumberOfAuxEntries: 1 + AuxEntries: + - Type: AUX_CSECT + ParameterHashIndex: 0 + TypeChkSectNum: 0 + SymbolType: XTY_SD + SymbolAlignment: 5 + StorageMappingClass: XMC_PR + SectionOrLength: 25 + StabInfoIndex: 0 + StabSectNum: 0 + - Name: .foo + Value: 0x0 + Section: .text + Type: 0x0 + StorageClass: C_EXT + NumberOfAuxEntries: 1 + AuxEntries: + - Type: AUX_CSECT + ParameterHashIndex: 0 + TypeChkSectNum: 0 + SymbolType: XTY_LD + SymbolAlignment: 0 + StorageMappingClass: XMC_PR + SectionOrLength: 3 + StabInfoIndex: 0 + StabSectNum: 0 + - Name: foo + Value: 0x1C + Section: .data + Type: 0x0 + StorageClass: C_EXT + NumberOfAuxEntries: 1 + AuxEntries: + - Type: AUX_CSECT + ParameterHashIndex: 0 + TypeChkSectNum: 0 + SymbolType: XTY_SD + SymbolAlignment: 2 + StorageMappingClass: XMC_DS + SectionOrLength: 12 + StabInfoIndex: 0 + StabSectNum: 0 + - Name: TOC + Value: 0x28 + Section: .data + Type: 0x0 + StorageClass: C_HIDEXT + NumberOfAuxEntries: 1 + AuxEntries: + - Type: AUX_CSECT + ParameterHashIndex: 0 + TypeChkSectNum: 0 + SymbolType: XTY_SD + SymbolAlignment: 2 + StorageMappingClass: XMC_TC0 + SectionOrLength: 0 + StabInfoIndex: 0 + StabSectNum: 0 +StringTable: {} +... diff --git a/llvm/test/DebugInfo/Inputs/big-archive-64.yaml b/llvm/test/DebugInfo/Inputs/big-archive-64.yaml new file mode 100644 index 0000000000000..9bbb1107555e0 --- /dev/null +++ b/llvm/test/DebugInfo/Inputs/big-archive-64.yaml @@ -0,0 +1,115 @@ +--- !XCOFF +FileHeader: + MagicNumber: 0x1F7 + NumberOfSections: 2 + CreationTime: 0 + OffsetToSymbolTable: 0xF8 + EntriesInSymbolTable: 11 + AuxiliaryHeaderSize: 0 + Flags: 0x0 +Sections: + - Name: .text + Address: 0x0 + Size: 0x1C + FileOffsetToData: 0xA8 + FileOffsetToRelocations: 0x0 + FileOffsetToLineNumbers: 0x0 + NumberOfRelocations: 0x0 + NumberOfLineNumbers: 0x0 + Flags: [ STYP_TEXT ] + SectionData: 4E800020000000000009204000000001000000040003666F6F000000 + - Name: .data + Address: 0x20 + Size: 0x18 + FileOffsetToData: 0xC4 + FileOffsetToRelocations: 0xDC + FileOffsetToLineNumbers: 0x0 + NumberOfRelocations: 0x2 + NumberOfLineNumbers: 0x0 + Flags: [ STYP_DATA ] + SectionData: '000000000000000000000000000000380000000000000000' + Relocations: + - Address: 0x20 + Symbol: 0x5 + Info: 0x3F + Type: 0x0 + - Address: 0x28 + Symbol: 0x9 + Info: 0x3F + Type: 0x0 +Symbols: + - Name: .file + Value: 0x0 + Section: N_DEBUG + Type: 0x18 + StorageClass: C_FILE + NumberOfAuxEntries: 2 + AuxEntries: + - Type: AUX_FILE + FileNameOrString: foo.c + FileStringType: XFT_FN + - Type: AUX_FILE + FileNameOrString: 'IBM Open XL C/C++ for AIX 17.1.3 (5725-C72, 5765-J18), version 17.1.3.0, LLVM version 21.0.0git (5ca72bc8d2e87445649eab1825dffd2a047440ba)' + FileStringType: XFT_CV + - Name: '' + Value: 0x0 + Section: .text + Type: 0x0 + StorageClass: C_HIDEXT + NumberOfAuxEntries: 1 + AuxEntries: + - Type: AUX_CSECT + ParameterHashIndex: 0 + TypeChkSectNum: 0 + SymbolType: XTY_SD + SymbolAlignment: 5 + StorageMappingClass: XMC_PR + SectionOrLengthLo: 25 + SectionOrLengthHi: 0 + - Name: .foo + Value: 0x0 + Section: .text + Type: 0x0 + StorageClass: C_EXT + NumberOfAuxEntries: 1 + AuxEntries: + - Type: AUX_CSECT + ParameterHashIndex: 0 + TypeChkSectNum: 0 + SymbolType: XTY_LD + SymbolAlignment: 0 + StorageMappingClass: XMC_PR + SectionOrLengthLo: 3 + SectionOrLengthHi: 0 + - Name: foo + Value: 0x20 + Section: .data + Type: 0x0 + StorageClass: C_EXT + NumberOfAuxEntries: 1 + AuxEntries: + - Type: AUX_CSECT + ParameterHashIndex: 0 + TypeChkSectNum: 0 + SymbolType: XTY_SD + SymbolAlignment: 3 + StorageMappingClass: XMC_DS + SectionOrLengthLo: 24 + SectionOrLengthHi: 0 + - Name: TOC + Value: 0x38 + Section: .data + Type: 0x0 + StorageClass: C_HIDEXT + NumberOfAuxEntries: 1 + AuxEntries: + - Type: AUX_CSECT + ParameterHashIndex: 0 + TypeChkSectNum: 0 + SymbolType: XTY_SD + SymbolAlignment: 2 + StorageMappingClass: XMC_TC0 + SectionOrLengthLo: 0 + SectionOrLengthHi: 0 +StringTable: {} +... diff --git a/llvm/test/DebugInfo/Inputs/big-archive-elf-1.yaml b/llvm/test/DebugInfo/Inputs/big-archive-elf-1.yaml new file mode 100644 index 0000000000000..8e5c929e82878 --- /dev/null +++ b/llvm/test/DebugInfo/Inputs/big-archive-elf-1.yaml @@ -0,0 +1,68 @@ +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_PPC64 + Flags: [ ] + SectionHeaderStringTable: .strtab +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x10 + Content: '2000804E000000000000000000000000' + - Name: .comment + Type: SHT_PROGBITS + Flags: [ SHF_MERGE, SHF_STRINGS ] + AddressAlign: 0x1 + EntSize: 0x1 + Content: 0049424D204F70656E20584C20432F432B2B20666F72204C696E7578206F6E20506F7765722031372E312E322028353732352D4337322C20353736352D4A3230292C2076657273696F6E2031372E312E322E302C20636C616E672076657273696F6E2032312E302E306769742028676974406769746875622E69626D2E636F6D3A636F6D70696C65722F6C6C766D2D70726F6A6563742E67697420653165653233663838333532623937333563363735386661396335653035313366626234393361322900 + - Name: .note.GNU-stack + Type: SHT_PROGBITS + AddressAlign: 0x1 + - Name: .eh_frame + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + AddressAlign: 0x8 + Content: 1000000000000000017A5200047841011B0C01001000000018000000000000001000000000000000 + - Name: .rela.eh_frame + Type: SHT_RELA + Flags: [ SHF_INFO_LINK ] + Link: .symtab + AddressAlign: 0x8 + Info: .eh_frame + Relocations: + - Offset: 0x1C + Symbol: .text + Type: R_PPC64_REL32 + - Name: .llvm_addrsig + Type: SHT_LLVM_ADDRSIG + Flags: [ SHF_EXCLUDE ] + Link: .symtab + AddressAlign: 0x1 + Offset: 0x1B8 + Symbols: [ ] + - Type: SectionHeaderTable + Sections: + - Name: .strtab + - Name: .text + - Name: .comment + - Name: .note.GNU-stack + - Name: .eh_frame + - Name: .rela.eh_frame + - Name: .llvm_addrsig + - Name: .symtab +Symbols: + - Name: foo1.c + Type: STT_FILE + Index: SHN_ABS + - Name: .text + Type: STT_SECTION + Section: .text + - Name: foo1 + Type: STT_FUNC + Section: .text + Binding: STB_GLOBAL + Size: 0x10 +... diff --git a/llvm/test/DebugInfo/Inputs/big-archive-elf-2.yaml b/llvm/test/DebugInfo/Inputs/big-archive-elf-2.yaml new file mode 100644 index 0000000000000..0052db732500f --- /dev/null +++ b/llvm/test/DebugInfo/Inputs/big-archive-elf-2.yaml @@ -0,0 +1,68 @@ +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_PPC64 + Flags: [ ] + SectionHeaderStringTable: .strtab +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x10 + Content: '2000804E000000000000000000000000' + - Name: .comment + Type: SHT_PROGBITS + Flags: [ SHF_MERGE, SHF_STRINGS ] + AddressAlign: 0x1 + EntSize: 0x1 + Content: 0049424D204F70656E20584C20432F432B2B20666F72204C696E7578206F6E20506F7765722031372E312E322028353732352D4337322C20353736352D4A3230292C2076657273696F6E2031372E312E322E302C20636C616E672076657273696F6E2032312E302E306769742028676974406769746875622E69626D2E636F6D3A636F6D70696C65722F6C6C766D2D70726F6A6563742E67697420653165653233663838333532623937333563363735386661396335653035313366626234393361322900 + - Name: .note.GNU-stack + Type: SHT_PROGBITS + AddressAlign: 0x1 + - Name: .eh_frame + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + AddressAlign: 0x8 + Content: 1000000000000000017A5200047841011B0C01001000000018000000000000001000000000000000 + - Name: .rela.eh_frame + Type: SHT_RELA + Flags: [ SHF_INFO_LINK ] + Link: .symtab + AddressAlign: 0x8 + Info: .eh_frame + Relocations: + - Offset: 0x1C + Symbol: .text + Type: R_PPC64_REL32 + - Name: .llvm_addrsig + Type: SHT_LLVM_ADDRSIG + Flags: [ SHF_EXCLUDE ] + Link: .symtab + AddressAlign: 0x1 + Offset: 0x1B8 + Symbols: [ ] + - Type: SectionHeaderTable + Sections: + - Name: .strtab + - Name: .text + - Name: .comment + - Name: .note.GNU-stack + - Name: .eh_frame + - Name: .rela.eh_frame + - Name: .llvm_addrsig + - Name: .symtab +Symbols: + - Name: foo2.c + Type: STT_FILE + Index: SHN_ABS + - Name: .text + Type: STT_SECTION + Section: .text + - Name: foo2 + Type: STT_FUNC + Section: .text + Binding: STB_GLOBAL + Size: 0x10 +... diff --git a/llvm/test/DebugInfo/symbolize-big-archive-elf.test b/llvm/test/DebugInfo/symbolize-big-archive-elf.test new file mode 100644 index 0000000000000..20d007a8bdd82 --- /dev/null +++ b/llvm/test/DebugInfo/symbolize-big-archive-elf.test @@ -0,0 +1,25 @@ +// Test archive member recognition by name (ELF format). + +// Generate object files from YAML. +// RUN: yaml2obj -o %t-1.o %S/Inputs/big-archive-elf-1.yaml +// RUN: yaml2obj -o %t-2.o %S/Inputs/big-archive-elf-2.yaml + +// Create archive with differently named members. +// RUN: rm -f %t.a +// RUN: llvm-ar crv %t.a %t-1.o %t-2.o + +// Test symbolization by member name (using just base names). +// RUN: llvm-symbolizer --default-arch=ppc64le --obj="%t.a(%{t:stem}.tmp-1.o)" 0x0 | FileCheck %s --check-prefix=CHECK-1 +// RUN: llvm-symbolizer --obj="%t.a(%{t:stem}.tmp-1.o):ppc64le" 0x0 | FileCheck %s --check-prefix=CHECK-1 +// RUN: llvm-symbolizer --default-arch=ppc64le --obj="%t.a(%{t:stem}.tmp-2.o)" 0x0 | FileCheck %s --check-prefix=CHECK-2 +// RUN: llvm-symbolizer --obj="%t.a(%{t:stem}.tmp-2.o):ppc64le" 0x0 | FileCheck %s --check-prefix=CHECK-2 +// CHECK-1: foo1 +// CHECK-2: foo2 + +// Test error cases. +// RUN: not llvm-symbolizer --obj="%t.a(nonexistent.o)" 0x1000 2>&1 | FileCheck %s --check-prefix=CHECK-ERROR +// CHECK-ERROR: error: {{.*}}no matching member{{.*}} +// RUN: not llvm-symbolizer --obj="%t-1.o(%{t:stem}.tmp-1.o)" 0x1000 2>&1 | FileCheck %s --check-prefix=CHECK-NOTARCHIVE +// CHECK-NOTARCHIVE: error: '{{.*}}' is not a valid archive +// RUN: not llvm-symbolizer --obj="%t.a()" 0x1000 2>&1 | FileCheck %s --check-prefix=CHECK-EMPTY +// CHECK-EMPTY: {{.*}}no matching member{{.*}} diff --git a/llvm/test/DebugInfo/symbolize-big-archive-xcoff.test b/llvm/test/DebugInfo/symbolize-big-archive-xcoff.test new file mode 100644 index 0000000000000..2811038eca65f --- /dev/null +++ b/llvm/test/DebugInfo/symbolize-big-archive-xcoff.test @@ -0,0 +1,33 @@ +// Test big archive recognition and error handling in llvm-symbolizer. + +// Generate object files. +// RUN: yaml2obj -o %t-32.o %S/Inputs/big-archive-32.yaml +// RUN: yaml2obj -o %t-64.o %S/Inputs/big-archive-64.yaml + +// Create archive with same-named members using different modes. +// RUN: rm -f %t.a +// RUN: cp %t-32.o %t.o +// RUN: llvm-ar %if system-aix %{-X32%} crv %t.a %t.o +// RUN: cp %t-64.o %t.o +// RUN: llvm-ar %if system-aix %{-X64 rv%} %else %{qv%} %t.a %t.o + +// Verify archive contains two members with same name. +// RUN: llvm-ar tv %if system-aix %{-X32_64%} %t.a | FileCheck %s --check-prefix=CHECK-ARCHIVE +// CHECK-ARCHIVE: {{.*}}symbolize-big-archive-xcoff.test.tmp.o{{$}} +// CHECK-ARCHIVE: {{.*}}symbolize-big-archive-xcoff.test.tmp.o{{$}} + +// Test successful symbolization. +// RUN: llvm-symbolizer --default-arch=ppc --obj="%t.a(%{t:stem}.tmp.o)" 0x0 | FileCheck %s --check-prefix=CHECK-32 +// RUN: llvm-symbolizer --default-arch=ppc64 --obj="%t.a(%{t:stem}.tmp.o)" 0x0 | FileCheck %s --check-prefix=CHECK-64 +// RUN: llvm-symbolizer --obj="%t.a(%{t:stem}.tmp.o):ppc" 0x0 | FileCheck %s --check-prefix=CHECK-32 +// RUN: llvm-symbolizer --obj="%t.a(%{t:stem}.tmp.o):ppc64" 0x0 | FileCheck %s --check-prefix=CHECK-64 +// CHECK-32: foo +// CHECK-64: foo + +// Test error cases. +// RUN: not llvm-symbolizer --obj="%t.a(nonexistent.o)" 0x1000 2>&1 | FileCheck %s --check-prefix=CHECK-ERROR +// CHECK-ERROR: error: {{.*}}no matching member{{.*}} +// RUN: not llvm-symbolizer --obj="%t.o(%{t:stem}.tmp.o)" 0x1000 2>&1 | FileCheck %s --check-prefix=CHECK-NOTARCHIVE +// CHECK-NOTARCHIVE: error: '{{.*}}' is not a valid archive +// RUN: not llvm-symbolizer --obj="%t.a()" 0x1000 2>&1 | FileCheck %s --check-prefix=CHECK-EMPTY +// CHECK-EMPTY: {{.*}}no matching member{{.*}} diff --git a/llvm/tools/llvm-symbolizer/Opts.td b/llvm/tools/llvm-symbolizer/Opts.td index 10f1e6dbbddf7..f616109346c55 100644 --- a/llvm/tools/llvm-symbolizer/Opts.td +++ b/llvm/tools/llvm-symbolizer/Opts.td @@ -15,7 +15,7 @@ class F: Flag<["--"], name>, HelpText; def grp_mach_o : OptionGroup<"kind">, HelpText<"llvm-symbolizer Mach-O Specific Options">; - +def grp_symbolizer : OptionGroup<"Symbolizer Options">; def grp_gsym : OptionGroup<"kind">, HelpText<"llvm-symbolizer GSYM Related Options">; @@ -31,8 +31,8 @@ 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 (Mach-O objects or AIX archives)">, + Group; 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.">;