diff --git a/llvm/include/llvm/BinaryFormat/XCOFF.h b/llvm/include/llvm/BinaryFormat/XCOFF.h --- a/llvm/include/llvm/BinaryFormat/XCOFF.h +++ b/llvm/include/llvm/BinaryFormat/XCOFF.h @@ -54,6 +54,34 @@ ///< future use and should be set to 0. }; +enum XCOFFInterpret : uint16_t { + OLD_XCOFF_INTERPRET = 1, + NEW_XCOFF_INTERPRET = 2 +}; + +enum FileFlag : uint16_t { + F_RELFLG = 0x0001, ///< relocation info stripped from file + F_EXEC = 0x0002, ///< file is executable (i.e., it + ///< has a loader section) + F_LNNO = 0x0004, ///< line numbers stripped from file + F_LSYMS = 0x0008, ///< local symbols stripped from file + F_FDPR_PROF = 0x0010, ///< file was profiled with FDPR + F_FDPR_OPTI = 0x0020, ///< file was reordered with FDPR + F_DSA = 0x0040, ///< file uses Dynamic Segment Allocation (32-bit + ///< only) + F_DEP_1 = 0x0080, ///< data Execution Protection bit 1 + F_VARPG = 0x0100, ///< executable requests using variable size pages + F_LPTEXT = 0x0400, ///< executable requires large pages for text + F_LPDATA = 0x0800, ///< executable requires large pages for data + F_DYNLOAD = 0x1000, ///< file is dynamically loadable and + ///< executable (equivalent to F_EXEC on AIX) + F_SHROBJ = 0x2000, ///< file is a shared object + F_LOADONLY = + 0x4000, ///< file can be loaded by the system loader, but it is + ///< ignored by the linker if it is a member of an archive. + F_DEP_2 = 0x8000 ///< Data Execution Protection bit 2 +}; + // x_smclas field of x_csect from system header: /usr/include/syms.h /// Storage Mapping Class definitions. enum StorageMappingClass : uint8_t { @@ -212,6 +240,8 @@ SYM_V_EXPORTED = 0x4000 }; +static constexpr uint16_t VISIBILITY_MASK = 0x7000; + // Relocation types, defined in `/usr/include/reloc.h`. enum RelocationType : uint8_t { R_POS = 0x00, ///< Positive relocation. Provides the address of the referenced diff --git a/llvm/include/llvm/Object/SymbolicFile.h b/llvm/include/llvm/Object/SymbolicFile.h --- a/llvm/include/llvm/Object/SymbolicFile.h +++ b/llvm/include/llvm/Object/SymbolicFile.h @@ -119,6 +119,7 @@ SF_Const = 1U << 10, // Symbol value is constant SF_Executable = 1U << 11, // Symbol points to an executable section // (IR only) + SF_Internal = 1U << 12 // Symbol has internal visibility }; BasicSymbolRef() = default; diff --git a/llvm/include/llvm/Object/XCOFFObjectFile.h b/llvm/include/llvm/Object/XCOFFObjectFile.h --- a/llvm/include/llvm/Object/XCOFFObjectFile.h +++ b/llvm/include/llvm/Object/XCOFFObjectFile.h @@ -64,6 +64,7 @@ return static_cast(this)->FlagAndTDataAlignment & AuxiHeaderTDataAlignmentMask; } + uint16_t getVersion() const { return static_cast(this)->Version; } }; struct XCOFFAuxiliaryHeader32 : XCOFFAuxiliaryHeader { diff --git a/llvm/lib/BinaryFormat/Magic.cpp b/llvm/lib/BinaryFormat/Magic.cpp --- a/llvm/lib/BinaryFormat/Magic.cpp +++ b/llvm/lib/BinaryFormat/Magic.cpp @@ -88,6 +88,10 @@ if (startswith(Magic, "!\n") || startswith(Magic, "!\n")) return file_magic::archive; break; + case '<': + if (startswith(Magic, "\n")) + return file_magic::archive; + break; case '\177': if (startswith(Magic, "\177ELF") && Magic.size() >= 18) { bool Data2MSB = Magic[5] == 2; diff --git a/llvm/lib/Object/XCOFFObjectFile.cpp b/llvm/lib/Object/XCOFFObjectFile.cpp --- a/llvm/lib/Object/XCOFFObjectFile.cpp +++ b/llvm/lib/Object/XCOFFObjectFile.cpp @@ -615,6 +615,22 @@ if (XCOFFSym.getSectionNumber() == XCOFF::N_UNDEF) Result |= SymbolRef::SF_Undefined; + // There is no visibility in old 32 bit XCOFF object file interpret. + if (is64Bit() || (auxiliaryHeader32() && (auxiliaryHeader32()->getVersion() == + NEW_XCOFF_INTERPRET))) { + + uint16_t SymType = XCOFFSym.getSymbolType(); + if ((SymType & VISIBILITY_MASK) == SYM_V_INTERNAL) + Result |= SymbolRef::SF_Internal; + if ((SymType & VISIBILITY_MASK) == SYM_V_HIDDEN) + Result |= SymbolRef::SF_Hidden; + + // TODO: We do not deal with SYM_V_PROTECTED here. + + if ((SymType & VISIBILITY_MASK) == SYM_V_EXPORTED) + Result |= SymbolRef::SF_Exported; + } + return Result; } diff --git a/llvm/test/tools/llvm-objdump/XCOFF/Inputs/big_ar_lib.a b/llvm/test/tools/llvm-objdump/XCOFF/Inputs/big_ar_lib.a new file mode 100644 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 GIT binary patch literal 0 Hc$@&1 | FileCheck %s + +# CHECK: llvm-nm{{(\.exe)?}}: warning: {{.*}}big_ar_lib.a(export_invalid.o): for symbol with index 26: csect symbol "invalid_var" with index 26 contains no auxiliary entry +# CHECK-NEXT: __rsrc +# CHECK-NEXT: export_var export +# CHECK-NEXT: protected_var protected +# CHECK-NEXT: tf1value +# CHECK-NEXT: tf9value +# CHECK-NEXT: weak_func diff --git a/llvm/test/tools/llvm-objdump/XCOFF/export_sym_list_obj.test b/llvm/test/tools/llvm-objdump/XCOFF/export_sym_list_obj.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-objdump/XCOFF/export_sym_list_obj.test @@ -0,0 +1,168 @@ +## Test the option "--export-symbols" of llvm-nm: exporting symbol list of xcoff object file. + +## Generate XCOFF object file. +# RUN: yaml2obj -DFLAG=0x0002 %s -o %t.o + +## Generate XCOFF shared object file. +# RUN: yaml2obj -DFLAG=0x2000 %s -o %t_shared.o + +## Test following symbols: +## Not export global symbols begin with "__sinit" , "__sterm" , "." , "(" +## Not export global symbols begin with regular expression "^__[0-9]+__" +## Not export hidden symbols and internal symbols. +## Export substring of the global symbol name begin with "__tf1" and "__tf9" +## Export format: symbol_name symbol_visibility(if there is). +# RUN: llvm-nm --export-symbols %t.o | FileCheck --check-prefixes=SYM,WEAK-SYM %s + +## Test: only export unique symbols. +# RUN: llvm-nm --export-symbols %t.o %t.o | FileCheck --check-prefixes=SYM,WEAK-SYM %s + +## Test: Not export weak symbol with option "exclude-weak". +# RUN: llvm-nm --export-symbols --no-weak %t.o | FileCheck --check-prefixes=SYM %s + +## Test: Not export __rsrc symbol name with option --no-rsrc. +# RUN: llvm-nm --export-symbols --no-rsrc %t.o | FileCheck --check-prefixes=NO-RSRC %s + +## Test: Not export any symobl for shared object file. +# RUN: llvm-nm --export-symbols %t_shared.o | FileCheck --check-prefixes=ANY --allow-empty %s + + +--- !XCOFF +FileHeader: + MagicNumber: 0x1DF + Flags: [[FLAG]] +AuxiliaryHeader: + Magic: 0x10B + Version: 0x2 + TextSectionSize: 0x280 + DataSectionSize: 0x90 +Sections: + - Name: .text + Flags: [ STYP_TEXT ] + - Name: .data + Flags: [ STYP_DATA ] +Symbols: + - Name: export_var + Section: .data + Type: 0x4000 + StorageClass: C_EXT + AuxEntries: + - Type: AUX_CSECT + SymbolAlignmentAndType: 0x09 + StorageMappingClass: XMC_RW + - Name: __rsrc + Section: .data + Type: 0x0 + StorageClass: C_EXT + AuxEntries: + - Type: AUX_CSECT + SymbolAlignmentAndType: 0x09 + StorageMappingClass: XMC_RW + SectionOrLength: 0x4 + - Name: __sinit + Section: .data + Type: 0x0 + StorageClass: C_EXT + AuxEntries: + - Type: AUX_CSECT + SymbolAlignmentAndType: 0x9 + StorageMappingClass: XMC_RW + SectionOrLength: 0xC + - Name: __sterm + Section: .data + Type: 0x0 + StorageClass: C_EXT + AuxEntries: + - Type: AUX_CSECT + SymbolAlignmentAndType: 0x09 + StorageMappingClass: XMC_RW + SectionOrLength: 0xC + - Name: .func + Section: .text + Type: 0x20 + StorageClass: C_EXT + AuxEntries: + - Type: AUX_CSECT + SymbolAlignmentAndType: 0x02 + StorageMappingClass: XMC_PR + - Name: (func) + Section: .data + Type: 0x0 + StorageClass: C_EXT + AuxEntries: + - Type: AUX_CSECT + SymbolAlignmentAndType: 0x09 + StorageMappingClass: XMC_RW + SectionOrLength: 0xC + - Name: __023__ + Section: .data + Type: 0x0 + StorageClass: C_EXT + AuxEntries: + - Type: AUX_CSECT + SymbolAlignmentAndType: 0x09 + StorageMappingClass: XMC_RW + SectionOrLength: 0x4 + - Name: __tf1_tf1value + Section: .data + Type: 0x00 + StorageClass: C_EXT + AuxEntries: + - Type: AUX_CSECT + SymbolAlignmentAndType: 0x09 + StorageMappingClass: XMC_RW + - Name: __tf9_12345678tf9value + Section: .data + Type: 0x0 + StorageClass: C_EXT + AuxEntries: + - Type: AUX_CSECT + SymbolAlignmentAndType: 0x09 + StorageMappingClass: XMC_RW + - Name: weak_func + Section: .data + Type: 0x0 + StorageClass: C_WEAKEXT + AuxEntries: + - Type: AUX_CSECT + SymbolAlignmentAndType: 0x09 + StorageMappingClass: XMC_RW + - Name: protected_var + Section: .data + Type: 0x3000 + StorageClass: C_EXT + AuxEntries: + - Type: AUX_CSECT + SymbolAlignmentAndType: 0x02 + StorageMappingClass: XMC_RW + - Name: hidden_var + Section: .data + Type: 0x2000 + StorageClass: C_HIDEXT + AuxEntries: + - Type: AUX_CSECT + SymbolAlignmentAndType: 0x09 + StorageMappingClass: XMC_RW + - Name: internal_var + Section: .data + Type: 0x1000 + StorageClass: C_HIDEXT + AuxEntries: + - Type: AUX_CSECT + SymbolAlignmentAndType: 0x09 + StorageMappingClass: XMC_RW + +# SYM: __rsrc +# SYM-NEXT: export_var export +# SYM-NEXT: protected_var protected +# SYM-NEXT: tf1value +# SYM-NEXT: tf9value +# WEAK-SYM-NEXT: weak_func + +# NO-RSRC: export_var export +# NO-RSRC-NEXT: protected_var protected +# NO-RSRC-NEXT: tf1value +# NO-RSRC-NEXT: tf9value +# NO-RSRC-NEXT: weak_func + +# ANY-NOT: .* diff --git a/llvm/tools/llvm-nm/Opts.td b/llvm/tools/llvm-nm/Opts.td --- a/llvm/tools/llvm-nm/Opts.td +++ b/llvm/tools/llvm-nm/Opts.td @@ -48,6 +48,13 @@ def s : F<"s", "Dump only symbols from this segment and section name">, Group; def x : F<"x", "Print symbol entry in hex">, Group; +// XCOFF specific options. +def grp_xcoff_o : OptionGroup<"kind">, HelpText<"llvm-nm XCOFF Specific Options">; + +def export_symbols : FF<"export-symbols", "Export symbol list for xcoff object file or archive">, Group; +def no_rsrc : FF<"no-rsrc", "Exclude the rsrc symbol from export symbol list" + "for xcoff (requires --export-unique-symbol)">, Group; + def : FF<"just-symbol-name", "Alias for --format=just-symbols">, Alias, AliasArgs<["just-symbols"]>, Flags<[HelpHidden]>; def : FF<"portability", "Alias for --format=posix">, Alias, AliasArgs<["posix"]>; diff --git a/llvm/tools/llvm-nm/llvm-nm.cpp b/llvm/tools/llvm-nm/llvm-nm.cpp --- a/llvm/tools/llvm-nm/llvm-nm.cpp +++ b/llvm/tools/llvm-nm/llvm-nm.cpp @@ -17,6 +17,7 @@ #include "llvm/ADT/StringSwitch.h" #include "llvm/BinaryFormat/COFF.h" +#include "llvm/BinaryFormat/XCOFF.h" #include "llvm/Demangle/Demangle.h" #include "llvm/IR/Function.h" #include "llvm/IR/LLVMContext.h" @@ -41,6 +42,7 @@ #include "llvm/Support/InitLLVM.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Program.h" +#include "llvm/Support/Regex.h" #include "llvm/Support/Signals.h" #include "llvm/Support/TargetSelect.h" #include "llvm/Support/WithColor.h" @@ -104,6 +106,8 @@ static bool SizeSort; static bool UndefinedOnly; static bool WithoutAliases; +static bool ExportSymbols; // The option is xcoff specific option. +static bool NoRsrc; // The option is xcoff specific option. namespace { enum Radix { d, o, x }; @@ -128,7 +132,8 @@ static StringRef ToolName; -static void warn(Error Err, Twine FileName, Twine Context = Twine()) { +static void warn(Error Err, Twine FileName, Twine Context = Twine(), + Twine Archive = Twine()) { assert(Err); // Flush the standard output so that the warning isn't interleaved with other @@ -137,8 +142,9 @@ handleAllErrors(std::move(Err), [&](const ErrorInfoBase &EI) { WithColor::warning(errs(), ToolName) - << FileName << ": " << (Context.str().empty() ? "" : Context + ": ") - << EI.message() << "\n"; + << (Archive.str().empty() ? FileName : Archive + "(" + FileName + ")") + << ": " << (Context.str().empty() ? "" : Context + ": ") << EI.message() + << "\n"; }); } @@ -222,6 +228,7 @@ uint8_t NSect; uint16_t NDesc; std::string IndirectName; + StringRef Visibility; }; } // anonymous namespace @@ -256,6 +263,17 @@ std::make_tuple(B.Name, B.Size, B.Address); } +static bool compareSymbolNameVisibility(const NMSymbol &A, const NMSymbol &B) { + return std::make_tuple(A.Name, A.Visibility) < + std::make_tuple(B.Name, B.Visibility); +} + +static bool isSymbolNameVisibilityDuplicate(const NMSymbol &A, + const NMSymbol &B) { + return std::make_tuple(A.Name, A.Visibility) == + std::make_tuple(B.Name, B.Visibility); +} + static char isSymbolList64Bit(SymbolicFile &Obj) { if (auto *IRObj = dyn_cast(&Obj)) return Triple(IRObj->getTargetTriple()).isArch64Bit(); @@ -646,6 +664,34 @@ } } +void printExportSymboList(raw_fd_ostream &OS) { + if (SymbolList.size() == 0) + return; + + llvm::sort(SymbolList, compareSymbolNameVisibility); + + std::vector::const_iterator Iter = SymbolList.begin(); + std::vector::const_iterator PreIter = Iter; + + OS << Iter->Name; + if (!Iter->Visibility.empty()) + OS << " " << Iter->Visibility; + OS << "\n"; + + PreIter = Iter; + while (++Iter < SymbolList.end()) { + // ignore duplication symbols which has the same name and visibility. + if (!(isSymbolNameVisibilityDuplicate(*PreIter, *Iter))) { + OS << Iter->Name; + if (!Iter->Visibility.empty()) + OS << " " << Iter->Visibility << "\n"; + else + OS << "\n"; + } + PreIter = Iter; + } +} + static void sortAndPrintSymbolList(SymbolicFile &Obj, bool printName, StringRef ArchiveName, StringRef ArchitectureName) { @@ -1625,6 +1671,101 @@ } } +static void exportSymbolsForXCOFF(XCOFFObjectFile *Obj, StringRef ArchiveName) { + + // Skip Shared object file. + if (Obj->getFlags() & XCOFF::F_SHROBJ) + return; + + for (const SymbolRef &Sym : Obj->symbols()) { + Expected NameOrErr = Sym.getName(); + + if (!NameOrErr) { + warn(NameOrErr.takeError(), Obj->getFileName(), + "for symbol with index " + + Twine(Obj->getSymbolIndex(Sym.getRawDataRefImpl().p)), + ArchiveName); + return; + } + + Expected SymFlagsOrErr = Sym.getFlags(); + if (!SymFlagsOrErr) { + warn(SymFlagsOrErr.takeError(), Obj->getFileName(), + "for symbol with index " + + Twine(Obj->getSymbolIndex(Sym.getRawDataRefImpl().p)), + ArchiveName); + return; + } + + uint32_t Flags = *SymFlagsOrErr; + StringRef SymName = *NameOrErr; + bool Global = Flags & SymbolRef::SF_Global; + bool Weak = Flags & SymbolRef::SF_Weak; + + // if the symbol is not ext or weak, not be exported. + if (!Global) + continue; + + // if the symbol is weak and option --no_weak is enable, not be exported. + if (Weak && NoWeakSymbols) + continue; + + if (Flags & SymbolRef::SF_Hidden || Flags & SymbolRef::SF_Internal) + continue; + + Expected SymSecOrErr = Sym.getSection(); + if (!SymSecOrErr) { + warn(SymSecOrErr.takeError(), Obj->getFileName(), + "for symbol with index " + + Twine(Obj->getSymbolIndex(Sym.getRawDataRefImpl().p)), + ArchiveName); + continue; + } + + section_iterator SecIter = *SymSecOrErr; + + // if the symbol is not text or data section, not be exported. + if (SecIter == Obj->section_end()) + continue; + + if (!(SecIter->isText() || SecIter->isData() || SecIter->isBSS())) + continue; + + Regex r("^__[0-9]+__"); + if (SymName.startswith("__sinit") || SymName.startswith("__sterm") || + SymName.front() == '.' || SymName.front() == '(' || r.match(SymName)) + continue; + + if (SymName.startswith("__tf1")) { + SymName = SymName.substr(6); + } else if (SymName.startswith("__tf9")) { + SymName = SymName.substr(14); + } + + if (SymName == "__rsrc" && NoRsrc) + continue; + + NMSymbol S = {}; + S.Name = SymName.str(); + + XCOFFObjectFile *XCOFFObj = dyn_cast(Obj); + assert(XCOFFObj && "Not a XCOFFObjectFile in exportSymbolsForXCOFF."); + // There is no visibility in old 32 bit XCOFF object file interpret. + if (XCOFFObj->is64Bit() || (XCOFFObj->auxiliaryHeader32() && + (XCOFFObj->auxiliaryHeader32()->getVersion() == + XCOFF::NEW_XCOFF_INTERPRET))) { + XCOFFSymbolRef XCOFFSym = XCOFFObj->toSymbolRef(Sym.getRawDataRefImpl()); + uint16_t SymType = XCOFFSym.getSymbolType(); + if ((SymType & XCOFF::VISIBILITY_MASK) == XCOFF::SYM_V_PROTECTED) + S.Visibility = "protected"; + + if ((SymType & XCOFF::VISIBILITY_MASK) == XCOFF::SYM_V_EXPORTED) + S.Visibility = "export"; + } + SymbolList.push_back(S); + } +} + static void dumpSymbolNamesFromObject(SymbolicFile &Obj, bool printName, StringRef ArchiveName = {}, StringRef ArchitectureName = {}) { @@ -1658,6 +1799,14 @@ return; } if (!(MachO && DyldInfoOnly)) { + + if (ExportSymbols && Obj.isXCOFF()) { + XCOFFObjectFile *XCOFFObj = dyn_cast(&Obj); + assert(XCOFFObj && "Not a XCOFFObjectFile in exportSymbolsForXCOFF."); + exportSymbolsForXCOFF(XCOFFObj, ArchiveName); + return; + } + size_t I = -1; for (BasicSymbolRef Sym : Symbols) { ++I; @@ -2170,6 +2319,10 @@ DyldInfoOnly = Args.hasArg(OPT_dyldinfo_only); NoDyldInfo = Args.hasArg(OPT_no_dyldinfo); + // XCOFF specific options. + ExportSymbols = Args.hasArg(OPT_export_symbols); + NoRsrc = Args.hasArg(OPT_no_rsrc); + // llvm-nm only reads binary files. if (error(sys::ChangeStdinToBinary())) return 1; @@ -2229,6 +2382,9 @@ llvm::for_each(InputFilenames, dumpSymbolNamesFromFile); + if (ExportSymbols) + printExportSymboList(outs()); + if (HadError) return 1; }