diff --git a/llvm/docs/CommandGuide/llvm-objdump.rst b/llvm/docs/CommandGuide/llvm-objdump.rst --- a/llvm/docs/CommandGuide/llvm-objdump.rst +++ b/llvm/docs/CommandGuide/llvm-objdump.rst @@ -394,6 +394,19 @@ Add symbol description to disassembly output. +.. option:: --export-unique-symbol + + Export symbol list for xcoff object file or archive. The option will suppress other options. + +.. option:: --exclude-weak + + Exclude the weak symbol from export symbol list for xcoff files(requires --export-unique-symbol). + +.. option:: --exclude-rsrc + + Exclude the rsrc symbol from export symbol list for xcoff files(requires --export-unique-symbol). + + BUGS ---- 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 @@ -40,6 +40,33 @@ enum ReservedSectionNum : int16_t { N_DEBUG = -2, N_ABS = -1, N_UNDEF = 0 }; enum MagicNumber : uint16_t { XCOFF32 = 0x01DF, XCOFF64 = 0x01F7 }; +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. @@ -199,6 +226,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,9 @@ SF_Const = 1U << 10, // Symbol value is constant SF_Executable = 1U << 11, // Symbol points to an executable section // (IR only) + SF_XCOFF_Protected = + 1U << 12, // Symbol has protected visibility for xcoff only + SF_Internal = 1U << 13 // 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,8 @@ return static_cast(this)->FlagAndTDataAlignment & AuxiHeaderTDataAlignmentMask; } + + uint16_t getVersion() const { return static_cast(this)->Version; } }; struct XCOFFAuxiliaryHeader32 : XCOFFAuxiliaryHeader { 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,20 @@ 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; + if ((SymType & VISIBILITY_MASK) == SYM_V_PROTECTED) + Result |= SymbolRef::SF_XCOFF_Protected; + if ((SymType & VISIBILITY_MASK) == SYM_V_EXPORTED) + Result |= SymbolRef::SF_Exported; + } return Result; } diff --git a/llvm/test/tools/llvm-objdump/XCOFF/Inputs/exp_sym.o b/llvm/test/tools/llvm-objdump/XCOFF/Inputs/exp_sym.o new file mode 100644 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 GIT binary patch literal 0 Hc$@; def : Flag<["-"], "w">, Alias; +def export_unique_symbol : Flag<["--"], "export-unique-symbol">, + HelpText<"Export symbol list for xcoff object file or archive">; + defm prefix : Eq<"prefix", "Add prefix to absolute paths">, MetaVarName<"prefix">; defm prefix_strip @@ -335,3 +338,16 @@ def : Separate<["--"], "arch">, Alias, Group; + +def grp_xcoff_o : OptionGroup<"kind">, HelpText<"llvm-objdump XCOFF Specific Options">; + +def exclude_rsrc : Flag<["--"], "exclude-rsrc">, + HelpText<"Exclude the rsrc symbol from export symbol list" + "for xcoff (requires --export-unique-symbol)">, + Group; + +def exclude_weak : Flag<["--"], "exclude-weak">, + HelpText<"Exclude the weak symbol from export symbol list" + "for xcoff files (requires --export-unique-symbol)">, + Group; + diff --git a/llvm/tools/llvm-objdump/XCOFFDump.h b/llvm/tools/llvm-objdump/XCOFFDump.h --- a/llvm/tools/llvm-objdump/XCOFFDump.h +++ b/llvm/tools/llvm-objdump/XCOFFDump.h @@ -15,7 +15,12 @@ struct SymbolInfoTy; +namespace opt { +class InputArgList; +} // namespace opt + namespace objdump { + Optional getXCOFFSymbolCsectSMC(const object::XCOFFObjectFile *Obj, const object::SymbolRef &Sym); @@ -32,6 +37,11 @@ Error getXCOFFRelocationValueString(const object::XCOFFObjectFile *Obj, const object::RelocationRef &RelRef, llvm::SmallVectorImpl &Result); + +void parseXCOFFOptions(const llvm::opt::InputArgList &InputArgs); +void exportSymbolInfoFromFile(StringRef InputFile); +void printExportSymboList(raw_fd_ostream &OS); + } // namespace objdump } // namespace llvm #endif diff --git a/llvm/tools/llvm-objdump/XCOFFDump.cpp b/llvm/tools/llvm-objdump/XCOFFDump.cpp --- a/llvm/tools/llvm-objdump/XCOFFDump.cpp +++ b/llvm/tools/llvm-objdump/XCOFFDump.cpp @@ -12,12 +12,23 @@ //===----------------------------------------------------------------------===// #include "XCOFFDump.h" - +#include "ObjdumpOptID.h" #include "llvm-objdump.h" #include "llvm/Demangle/Demangle.h" +#include "llvm/Option/ArgList.h" +#include "llvm/Support/Regex.h" using namespace llvm; using namespace llvm::object; +using namespace llvm::objdump; + +static bool ExcludeRsrc; +static bool ExcludeWeak; + +void objdump::parseXCOFFOptions(const llvm::opt::InputArgList &InputArgs) { + ExcludeRsrc = InputArgs.hasArg(OBJDUMP_exclude_rsrc); + ExcludeWeak = InputArgs.hasArg(OBJDUMP_exclude_weak); +} Error objdump::getXCOFFRelocationValueString(const XCOFFObjectFile *Obj, const RelocationRef &Rel, @@ -112,3 +123,176 @@ return Result; } + +struct SymNameVisibilityTy { + std::string SymName; + StringRef Visibility; + SymNameVisibilityTy(StringRef Name, StringRef Vis) + : SymName(Name.str()), Visibility(Vis){}; + SymNameVisibilityTy(StringRef Name) : SymName(Name.str()){}; + +private: + friend bool operator<(const SymNameVisibilityTy &SNV1, + const SymNameVisibilityTy SNV2) { + int NameRes = SNV1.SymName.compare(SNV2.SymName); + if (NameRes < 0) + return true; + if (NameRes > 0) + return false; + + return SNV1.Visibility.compare(SNV2.Visibility) == -1; + } + + friend bool operator==(const SymNameVisibilityTy &SNV1, + const SymNameVisibilityTy SNV2) { + return SNV1.SymName.compare(SNV2.SymName) == 0 && + SNV1.Visibility.compare(SNV2.Visibility) == 0; + } +}; + +std::vector ExportSymbols; + +static void exportSymbolInfoFromObjectFile(const ObjectFile *O, + StringRef ObjectName, + StringRef ArchiveName) { + + if (!O->isXCOFF()) + return; + + // Skip Shared object file. + if (dyn_cast(O)->getFlags() & XCOFF::F_SHROBJ) + return; + + for (const SymbolRef &Sym : O->symbols()) { + Expected NameOrErr = Sym.getName(); + if (!NameOrErr) { + reportError(NameOrErr.takeError(), ObjectName, ArchiveName); + continue; + } + StringRef SymName = NameOrErr.get(); + + Expected FlagsOrErr = Sym.getFlags(); + if (!FlagsOrErr) { + reportError(FlagsOrErr.takeError(), ObjectName, ArchiveName); + } + uint32_t Flags = FlagsOrErr.get(); + 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 exclude-weak is enable, not be exported. + if (Weak && ExcludeWeak) + continue; + + if (Flags & SymbolRef::SF_Hidden || Flags & SymbolRef::SF_Internal) + continue; + + Expected SymSecOrErr = Sym.getSection(); + if (!SymSecOrErr) { + reportError(SymSecOrErr.takeError(), ObjectName, ArchiveName); + } + + section_iterator SecIter = SymSecOrErr.get(); + + // if the symbol is not text or data section, not be exported. + if (SecIter == O->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" && ExcludeRsrc) + continue; + + if (Flags & SymbolRef::SF_Exported) { + ExportSymbols.push_back(SymNameVisibilityTy(SymName, "export")); + continue; + } + + if (Flags & SymbolRef::SF_XCOFF_Protected) { + ExportSymbols.push_back(SymNameVisibilityTy(SymName, "protected")); + continue; + } + + ExportSymbols.push_back(SymName); + } +} + +/// Export symbols list from each object file in \a a; +static void dumpArchive(const Archive *A) { + Error Err = Error::success(); + unsigned I = -1; + for (auto &C : A->children(Err)) { + ++I; + std::string ChildName = getFileNameForError(C, I); + + Expected> ChildOrErr = C.getAsBinary(); + if (!ChildOrErr) { + if (auto E = isNotObjectErrorInvalidFileType(ChildOrErr.takeError())) + reportError(std::move(E), ChildName, A->getFileName()); + continue; + } + if (ObjectFile *O = dyn_cast(&*ChildOrErr.get())) { + exportSymbolInfoFromObjectFile(O, ChildName, A->getFileName()); + } else + reportError(errorCodeToError(object_error::invalid_file_type), ChildName, + A->getFileName()); + } + if (Err) + reportError(std::move(Err), A->getFileName()); +} + +void objdump::exportSymbolInfoFromFile(StringRef InputFile) { + // Attempt to open the binary. + OwningBinary OBinary = + unwrapOrError(createBinary(InputFile), InputFile); + Binary &Binary = *OBinary.getBinary(); + + if (Archive *A = dyn_cast(&Binary)) { + dumpArchive(A); + } else if (ObjectFile *O = dyn_cast(&Binary)) + exportSymbolInfoFromObjectFile(O, InputFile, std::string()); + else + reportError(errorCodeToError(object_error::invalid_file_type), InputFile); +} + +void objdump::printExportSymboList(raw_fd_ostream &OS) { + if (ExportSymbols.size() == 0) + return; + + llvm::sort(ExportSymbols); + + std::vector::const_iterator Iter = ExportSymbols.begin(); + std::vector::const_iterator PreIter = Iter; + + OS << Iter->SymName; + if (!Iter->Visibility.empty()) + OS << " " << Iter->Visibility; + OS << "\n"; + + PreIter = Iter; + while (++Iter < ExportSymbols.end()) { + if (!(*PreIter == *Iter)) { + OS << Iter->SymName; + if (!Iter->Visibility.empty()) + OS << " " << Iter->Visibility << "\n"; + else + OS << "\n"; + } + PreIter = Iter; + } +} diff --git a/llvm/tools/llvm-objdump/llvm-objdump.h b/llvm/tools/llvm-objdump/llvm-objdump.h --- a/llvm/tools/llvm-objdump/llvm-objdump.h +++ b/llvm/tools/llvm-objdump/llvm-objdump.h @@ -62,6 +62,7 @@ extern bool SymbolTable; extern std::string TripleName; extern bool UnwindInfo; +extern bool ExportUniqueSymbol; extern StringSet<> FoundSectionSet; diff --git a/llvm/tools/llvm-objdump/llvm-objdump.cpp b/llvm/tools/llvm-objdump/llvm-objdump.cpp --- a/llvm/tools/llvm-objdump/llvm-objdump.cpp +++ b/llvm/tools/llvm-objdump/llvm-objdump.cpp @@ -219,6 +219,8 @@ std::string objdump::Prefix; uint32_t objdump::PrefixStrip; +bool objdump::ExportUniqueSymbol; + DebugVarsFormat objdump::DbgVariables = DVDisabled; int objdump::DbgIndent = 52; @@ -2414,7 +2416,6 @@ printLazyBindTable(O); if (WeakBind) printWeakBindTable(O); - // Other special sections: if (RawClangAST) printRawClangAST(O); @@ -2473,6 +2474,12 @@ return; } + if (ExportUniqueSymbol) { + exportSymbolInfoFromFile(file); + printExportSymboList(outs()); + return; + } + // Attempt to open the binary. OwningBinary OBinary = unwrapOrError(createBinary(file), file); Binary &Binary = *OBinary.getBinary(); @@ -2606,6 +2613,7 @@ TripleName = InputArgs.getLastArgValue(OBJDUMP_triple_EQ).str(); UnwindInfo = InputArgs.hasArg(OBJDUMP_unwind_info); Wide = InputArgs.hasArg(OBJDUMP_wide); + ExportUniqueSymbol = InputArgs.hasArg(OBJDUMP_export_unique_symbol); Prefix = InputArgs.getLastArgValue(OBJDUMP_prefix).str(); parseIntArg(InputArgs, OBJDUMP_prefix_strip, PrefixStrip); if (const opt::Arg *A = InputArgs.getLastArg(OBJDUMP_debug_vars_EQ)) { @@ -2616,6 +2624,7 @@ parseIntArg(InputArgs, OBJDUMP_debug_vars_indent_EQ, DbgIndent); parseMachOOptions(InputArgs); + parseXCOFFOptions(InputArgs); // Parse -M (--disassembler-options) and deprecated // --x86-asm-syntax={att,intel}. @@ -2743,6 +2752,7 @@ !DynamicRelocations && !FileHeaders && !PrivateHeaders && !RawClangAST && !Relocations && !SectionHeaders && !SectionContents && !SymbolTable && !DynamicSymbolTable && !UnwindInfo && !FaultMapSection && + !ExportUniqueSymbol && !(MachOOpt && (Bind || DataInCode || DylibId || DylibsUsed || ExportsTrie || FirstPrivateHeader || FunctionStarts || IndirectSymbols || InfoPlist ||