| /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying | |
| file LICENSE.rst or https://cmake.org/licensing for details. */ | |
| /*------------------------------------------------------------------------- | |
| Portions of this source have been derived from the 'bindexplib' tool | |
| provided by the CERN ROOT Data Analysis Framework project (root.cern.ch). | |
| Permission has been granted by Pere Mato <pere.mato@cern.ch> to distribute | |
| this derived work under the CMake license. | |
| -------------------------------------------------------------------------*/ | |
| /* | |
| *---------------------------------------------------------------------- | |
| * Program: dumpexts.exe | |
| * Author: Gordon Chaffee | |
| * | |
| * History: The real functionality of this file was written by | |
| * Matt Pietrek in 1993 in his pedump utility. I've | |
| * modified it to dump the externals in a bunch of object | |
| * files to create a .def file. | |
| * | |
| * Notes: Visual C++ puts an underscore before each exported symbol. | |
| * This file removes them. I don't know if this is a problem | |
| * this other compilers. If _MSC_VER is defined, | |
| * the underscore is removed. If not, it isn't. To get a | |
| * full dump of an object file, use the -f option. This can | |
| * help determine the something that may be different with a | |
| * compiler other than Visual C++. | |
| * ====================================== | |
| * Corrections (Axel 2006-04-04): | |
| * Conversion to C++. Mostly. | |
| * | |
| * Extension (Axel 2006-03-15) | |
| * As soon as an object file contains an /EXPORT directive (which | |
| * is generated by the compiler when a symbol is declared as | |
| * __declspec(dllexport) no to-be-exported symbols are printed, | |
| * as the linker will see these directives, and if those directives | |
| * are present we only export selectively (i.e. we trust the | |
| * programmer). | |
| * | |
| * ====================================== | |
| * ====================================== | |
| * Corrections (Valery Fine 23/02/98): | |
| * | |
| * The "(vector) deleting destructor" MUST not be exported | |
| * To recognize it the following test are introduced: | |
| * "@@UAEPAXI@Z" scalar deleting dtor | |
| * "@@QAEPAXI@Z" vector deleting dtor | |
| * "AEPAXI@Z" vector deleting dtor with thunk adjustor | |
| * ====================================== | |
| * Corrections (Valery Fine 12/02/97): | |
| * | |
| * It created a wrong EXPORTS for the global pointers and constants. | |
| * The Section Header has been involved to discover the missing information | |
| * Now the pointers are correctly supplied with "DATA" descriptor | |
| * the constants with no extra descriptor. | |
| * | |
| * Corrections (Valery Fine 16/09/96): | |
| * | |
| * It didn't work for C++ code with global variables and class definitions | |
| * The DumpExternalObject function has been introduced to generate .DEF | |
| *file | |
| * | |
| * Author: Valery Fine 16/09/96 (E-mail: fine@vxcern.cern.ch) | |
| *---------------------------------------------------------------------- | |
| */ | |
| #include "bindexplib.h" | |
| #include <algorithm> | |
| #include <cstddef> // IWYU pragma: keep | |
| #include <iostream> | |
| #include <map> | |
| #include <sstream> | |
| #include <string> | |
| #include <utility> | |
| #include <vector> | |
| #ifdef _WIN32 | |
| # include <windows.h> | |
| # include "cmsys/Encoding.hxx" | |
| #endif | |
| #include "cmsys/FStream.hxx" | |
| #include "cmGeneratedFileStream.h" | |
| #include "cmOutputConverter.h" | |
| #include "cmStringAlgorithms.h" | |
| #include "cmSystemTools.h" | |
| namespace { | |
| enum class Arch | |
| { | |
| Generic, | |
| I386, | |
| ARM64EC, | |
| }; | |
| /* | |
| * The two backends below, the COFF symbol table walker used on Windows | |
| * hosts and the nm reader used everywhere else, must select the same | |
| * symbols, so every rule that depends only on the symbol name is shared. | |
| */ | |
| bool SymbolIsFromManagedCode(std::string const& symbol) | |
| { | |
| return symbol == "__t2m" || symbol == "__m2mep" || symbol == "__mep" || | |
| symbol.find("$$F") != std::string::npos || | |
| symbol.find("$$J") != std::string::npos; | |
| } | |
| bool SymbolIsOperatorExternC(std::string const& symbol) | |
| { | |
| return symbol.find_first_not_of("=<>+-*/%,?|~!^&[]()") == std::string::npos; | |
| } | |
| /* | |
| * The "(vector) deleting destructor" must not be exported: | |
| * "??_G" scalar deleting dtor | |
| * "??_E" vector deleting dtor | |
| */ | |
| bool SymbolIsDeletingDestructor(std::string const& symbol) | |
| { | |
| return cmHasLiteralPrefix(symbol, "??_G") || | |
| cmHasLiteralPrefix(symbol, "??_E"); | |
| } | |
| bool SymbolIsVftable(std::string const& symbol) | |
| { | |
| return cmHasLiteralPrefix(symbol, "??_7"); | |
| } | |
| bool SymbolIsArm64ECThunk(std::string const& symbol) | |
| { | |
| return symbol.find("$ientry_thunk") != std::string::npos || | |
| symbol.find("$entry_thunk") != std::string::npos || | |
| symbol.find("$iexit_thunk") != std::string::npos || | |
| symbol.find("$exit_thunk") != std::string::npos; | |
| } | |
| /* | |
| * Strip the decorations that must not appear in the generated .def file. | |
| */ | |
| void NormalizeSymbolName(std::string& symbol, Arch arch) | |
| { | |
| // clear out any leading spaces | |
| symbol.erase(0, symbol.find_first_not_of(" \t\n\v\f\r")); | |
| // if it starts with _ and has an @ then it is a __cdecl | |
| // so remove the @ stuff for the export | |
| if (!symbol.empty() && symbol[0] == '_') { | |
| std::string::size_type posAt = symbol.find('@'); | |
| if (posAt != std::string::npos) { | |
| symbol.erase(posAt); | |
| } | |
| } | |
| // For i386 builds we need to remove _ | |
| if (arch == Arch::I386 && !symbol.empty() && symbol[0] == '_') { | |
| symbol.erase(0, 1); | |
| } | |
| } | |
| /* | |
| * Symbols that are never exported, whatever section they are defined in. | |
| */ | |
| bool SymbolIsExcluded(std::string const& symbol, Arch arch) | |
| { | |
| // The original code had a check for | |
| // symbol.find("real@") == std::string::npos) | |
| // but this disallows member functions with the name "real". | |
| return symbol.empty() || SymbolIsDeletingDestructor(symbol) || | |
| // Skip symbols containing a dot, that are from managed code, | |
| // or that are C++ operators incorrectly declared extern "C". | |
| symbol.find('.') != std::string::npos || SymbolIsFromManagedCode(symbol) || | |
| SymbolIsOperatorExternC(symbol) || | |
| // Skip arm64ec thunk symbols. | |
| (arch == Arch::ARM64EC && SymbolIsArm64ECThunk(symbol)); | |
| } | |
| /* | |
| * Read the machine type out of a COFF object file header so that the nm | |
| * backend can apply the same architecture-specific rules as the COFF | |
| * symbol table walker. Inputs that are not COFF object files, such as | |
| * LLVM bitcode, are reported as generic. | |
| */ | |
| Arch GetObjectArch(std::string const& filename) | |
| { | |
| cmsys::ifstream fin(filename.c_str(), std::ios::in | std::ios::binary); | |
| if (!fin) { | |
| return Arch::Generic; | |
| } | |
| unsigned char header[8] = { 0 }; | |
| fin.read(reinterpret_cast<char*>(header), sizeof(header)); | |
| if (!fin) { | |
| return Arch::Generic; | |
| } | |
| auto readWord = [&header](size_t i) -> unsigned int { | |
| return static_cast<unsigned int>(header[i]) | | |
| (static_cast<unsigned int>(header[i + 1]) << 8); | |
| }; | |
| unsigned int machine = readWord(0); | |
| if (machine == 0x0000 && readWord(2) == 0xffff) { | |
| // In a /bigobj file the machine type follows Sig1, Sig2 and Version. | |
| machine = readWord(6); | |
| } | |
| switch (machine) { | |
| case 0x014c: // IMAGE_FILE_MACHINE_I386 | |
| return Arch::I386; | |
| case 0xa641: // IMAGE_FILE_MACHINE_ARM64EC | |
| return Arch::ARM64EC; | |
| default: | |
| return Arch::Generic; | |
| } | |
| } | |
| } // namespace | |
| #ifdef _WIN32 | |
| # ifndef IMAGE_FILE_MACHINE_ARM | |
| # define IMAGE_FILE_MACHINE_ARM 0x01c0 // ARM Little-Endian | |
| # endif | |
| # ifndef IMAGE_FILE_MACHINE_THUMB | |
| # define IMAGE_FILE_MACHINE_THUMB 0x01c2 // ARM Thumb/Thumb-2 Little-Endian | |
| # endif | |
| # ifndef IMAGE_FILE_MACHINE_ARMNT | |
| # define IMAGE_FILE_MACHINE_ARMNT 0x01c4 // ARM Thumb-2 Little-Endian | |
| # endif | |
| # ifndef IMAGE_FILE_MACHINE_ARM64 | |
| # define IMAGE_FILE_MACHINE_ARM64 0xaa64 // ARM64 Little-Endian | |
| # endif | |
| # ifndef IMAGE_FILE_MACHINE_ARM64EC | |
| # define IMAGE_FILE_MACHINE_ARM64EC 0xa641 // ARM64EC Little-Endian | |
| # endif | |
| typedef struct cmANON_OBJECT_HEADER_BIGOBJ | |
| { | |
| /* same as ANON_OBJECT_HEADER_V2 */ | |
| WORD Sig1; // Must be IMAGE_FILE_MACHINE_UNKNOWN | |
| WORD Sig2; // Must be 0xffff | |
| WORD Version; // >= 2 (implies the Flags field is present) | |
| WORD Machine; // Actual machine - IMAGE_FILE_MACHINE_xxx | |
| DWORD TimeDateStamp; | |
| CLSID ClassID; // {D1BAA1C7-BAEE-4ba9-AF20-FAF66AA4DCB8} | |
| DWORD SizeOfData; // Size of data that follows the header | |
| DWORD Flags; // 0x1 -> contains metadata | |
| DWORD MetaDataSize; // Size of CLR metadata | |
| DWORD MetaDataOffset; // Offset of CLR metadata | |
| /* bigobj specifics */ | |
| DWORD NumberOfSections; // extended from WORD | |
| DWORD PointerToSymbolTable; | |
| DWORD NumberOfSymbols; | |
| } cmANON_OBJECT_HEADER_BIGOBJ; | |
| typedef struct _cmIMAGE_SYMBOL_EX | |
| { | |
| union | |
| { | |
| BYTE ShortName[8]; | |
| struct | |
| { | |
| DWORD Short; // if 0, use LongName | |
| DWORD Long; // offset into string table | |
| } Name; | |
| DWORD LongName[2]; // PBYTE [2] | |
| } N; | |
| DWORD Value; | |
| LONG SectionNumber; | |
| WORD Type; | |
| BYTE StorageClass; | |
| BYTE NumberOfAuxSymbols; | |
| } cmIMAGE_SYMBOL_EX; | |
| typedef cmIMAGE_SYMBOL_EX UNALIGNED* cmPIMAGE_SYMBOL_EX; | |
| PIMAGE_SECTION_HEADER GetSectionHeaderOffset( | |
| PIMAGE_FILE_HEADER pImageFileHeader) | |
| { | |
| return (PIMAGE_SECTION_HEADER)((DWORD_PTR)pImageFileHeader + | |
| IMAGE_SIZEOF_FILE_HEADER + | |
| pImageFileHeader->SizeOfOptionalHeader); | |
| } | |
| PIMAGE_SECTION_HEADER GetSectionHeaderOffset( | |
| cmANON_OBJECT_HEADER_BIGOBJ* pImageFileHeader) | |
| { | |
| return (PIMAGE_SECTION_HEADER)((DWORD_PTR)pImageFileHeader + | |
| sizeof(cmANON_OBJECT_HEADER_BIGOBJ)); | |
| } | |
| /* | |
| + * Utility func, strstr with size | |
| + */ | |
| char const* StrNStr(char const* start, char const* find, size_t& size) | |
| { | |
| size_t len; | |
| char const* hint; | |
| if (!start || !find || !size) { | |
| size = 0; | |
| return 0; | |
| } | |
| len = strlen(find); | |
| while ((hint = (char const*)memchr(start, find[0], size - len + 1))) { | |
| size -= (hint - start); | |
| if (!strncmp(hint, find, len)) | |
| return hint; | |
| start = hint + 1; | |
| } | |
| size = 0; | |
| return 0; | |
| } | |
| template < | |
| // cmANON_OBJECT_HEADER_BIGOBJ or IMAGE_FILE_HEADER | |
| class ObjectHeaderType, | |
| // cmPIMAGE_SYMBOL_EX or PIMAGE_SYMBOL | |
| class SymbolTableType> | |
| class DumpSymbols | |
| { | |
| public: | |
| /* | |
| *---------------------------------------------------------------------- | |
| * Constructor -- | |
| * | |
| * Initialize variables from pointer to object header. | |
| * | |
| *---------------------------------------------------------------------- | |
| */ | |
| DumpSymbols(ObjectHeaderType* ih, std::set<std::string>& symbols, | |
| std::set<std::string>& dataSymbols, | |
| Arch symbolArch = Arch::Generic) | |
| : Symbols(symbols) | |
| , DataSymbols(dataSymbols) | |
| { | |
| this->ObjectImageHeader = ih; | |
| this->SymbolTable = | |
| (SymbolTableType*)((DWORD_PTR)this->ObjectImageHeader + | |
| this->ObjectImageHeader->PointerToSymbolTable); | |
| this->SectionHeaders = GetSectionHeaderOffset(this->ObjectImageHeader); | |
| this->SymbolCount = this->ObjectImageHeader->NumberOfSymbols; | |
| this->SymbolArch = symbolArch; | |
| } | |
| /* | |
| *---------------------------------------------------------------------- | |
| * DumpObjFile -- | |
| * | |
| * Dump an object file's exported symbols. | |
| *---------------------------------------------------------------------- | |
| */ | |
| void DumpObjFile() { this->DumpExternalsObjects(); } | |
| /* | |
| *---------------------------------------------------------------------- | |
| * DumpExternalsObjects -- | |
| * | |
| * Dumps a COFF symbol table from an OBJ. | |
| *---------------------------------------------------------------------- | |
| */ | |
| void DumpExternalsObjects() | |
| { | |
| unsigned i; | |
| PSTR stringTable; | |
| std::string symbol; | |
| DWORD SectChar; | |
| /* | |
| * The string table apparently starts right after the symbol table | |
| */ | |
| stringTable = (PSTR) & this->SymbolTable[this->SymbolCount]; | |
| SymbolTableType* pSymbolTable = this->SymbolTable; | |
| for (i = 0; i < this->SymbolCount; i++) { | |
| if (pSymbolTable->SectionNumber > 0 && | |
| (pSymbolTable->Type == 0x20 || pSymbolTable->Type == 0x0)) { | |
| if (pSymbolTable->StorageClass == IMAGE_SYM_CLASS_EXTERNAL) { | |
| /* | |
| * The name of the Function entry points | |
| */ | |
| if (pSymbolTable->N.Name.Short != 0) { | |
| symbol.clear(); | |
| symbol.insert(0, (char const*)pSymbolTable->N.ShortName, | |
| strnlen((char const*)pSymbolTable->N.ShortName, 8)); | |
| } else { | |
| symbol = stringTable + pSymbolTable->N.Name.Long; | |
| } | |
| NormalizeSymbolName(symbol, this->SymbolArch); | |
| if (!SymbolIsExcluded(symbol, this->SymbolArch)) { | |
| SectChar = this->SectionHeaders[pSymbolTable->SectionNumber - 1] | |
| .Characteristics; | |
| if ((!pSymbolTable->Type && | |
| // Read only (i.e. constants) must be excluded | |
| (SectChar & IMAGE_SCN_MEM_WRITE)) || | |
| (this->SymbolArch == Arch::ARM64EC && | |
| // vftable symbols are DATA on ARM64EC | |
| SymbolIsVftable(symbol))) { | |
| this->DataSymbols.insert(symbol); | |
| } else if (pSymbolTable->Type || | |
| !(SectChar & IMAGE_SCN_MEM_READ) || | |
| (SectChar & IMAGE_SCN_MEM_EXECUTE) || | |
| (this->SymbolArch != Arch::ARM64EC && | |
| // vftable symbols fail if marked as DATA | |
| SymbolIsVftable(symbol))) { | |
| this->Symbols.insert(symbol); | |
| } | |
| } | |
| } | |
| } | |
| /* | |
| * Take into account any aux symbols | |
| */ | |
| i += pSymbolTable->NumberOfAuxSymbols; | |
| pSymbolTable += pSymbolTable->NumberOfAuxSymbols; | |
| pSymbolTable++; | |
| } | |
| } | |
| private: | |
| std::set<std::string>& Symbols; | |
| std::set<std::string>& DataSymbols; | |
| DWORD_PTR SymbolCount; | |
| PIMAGE_SECTION_HEADER SectionHeaders; | |
| ObjectHeaderType* ObjectImageHeader; | |
| SymbolTableType* SymbolTable; | |
| Arch SymbolArch; | |
| }; | |
| #endif | |
| static bool CreateResponseFile(std::vector<std::string> const& arguments, | |
| std::string const& responseFile) | |
| { | |
| cmGeneratedFileStream responseStream; | |
| responseStream.Open(responseFile, true, true); | |
| if (!responseStream) { | |
| std::cerr << "Could not open response file '" << responseFile << "'.\n"; | |
| return false; | |
| } | |
| int responseFlags = cmOutputConverter::Shell_Flag_IsResponse; | |
| #if !defined(_WIN32) || defined(__CYGWIN__) | |
| responseFlags |= cmOutputConverter::Shell_Flag_IsUnix; | |
| #endif | |
| for (auto const& argument : arguments) { | |
| responseStream << cmOutputConverter::EscapeForShell(argument, | |
| responseFlags) | |
| << '\n'; | |
| } | |
| if (!responseStream.Close()) { | |
| std::cerr << "Could not write response file '" << responseFile << "'.\n"; | |
| return false; | |
| } | |
| return true; | |
| } | |
| static bool DumpFileWithNm(std::string const& nmPath, | |
| std::string const& filename, | |
| std::set<std::string>& symbols, | |
| std::set<std::string>& dataSymbols) | |
| { | |
| std::string const nmName = cmSystemTools::GetFilenameName(nmPath); | |
| std::string output; | |
| std::string error; | |
| // break up command line into a vector | |
| std::vector<std::string> command; | |
| command.push_back(nmPath); | |
| command.emplace_back("--no-weak"); | |
| command.emplace_back("--defined-only"); | |
| command.emplace_back("--format=posix"); | |
| command.emplace_back("--print-file-name"); | |
| command.push_back(filename); | |
| // run the command | |
| int exit_code = 0; | |
| bool const commandResult = cmSystemTools::RunSingleCommand( | |
| command, &output, &error, &exit_code, nullptr, cmSystemTools::OUTPUT_NONE); | |
| if (!commandResult || exit_code != 0) { | |
| std::cerr << nmName << " returned an error: " << output << error << '\n'; | |
| return false; | |
| } | |
| // The COFF symbol table walker applies rules that depend on the object | |
| // file's architecture, but nm does not report it. Read it from the | |
| // object files named in the output, remembering it because one nm run | |
| // may cover many object files. | |
| std::map<std::string, Arch> archOfObject; | |
| std::istringstream ss(output); | |
| std::string line; | |
| while (std::getline(ss, line)) { | |
| if (line.empty()) { // last line | |
| continue; | |
| } | |
| size_t const filename_end = line.find(": "); | |
| if (filename_end == std::string::npos) { | |
| std::cerr << "Couldn't parse " << nmName << " output line: " << line | |
| << '\n'; | |
| return false; | |
| } | |
| size_t const sym_start = filename_end + 2; | |
| size_t const sym_end = line.find(' ', sym_start); | |
| if (sym_end == std::string::npos) { | |
| std::cerr << "Couldn't parse " << nmName << " output line: " << line | |
| << '\n'; | |
| return false; | |
| } | |
| if (line.size() <= sym_end + 1) { | |
| std::cerr << "Couldn't parse " << nmName << " output line: " << line | |
| << '\n'; | |
| return false; | |
| } | |
| char const sym_type = line[sym_end + 1]; | |
| std::string const objectFile = line.substr(0, filename_end); | |
| std::string symbol = line.substr(sym_start, sym_end - sym_start); | |
| auto archEntry = archOfObject.find(objectFile); | |
| if (archEntry == archOfObject.end()) { | |
| archEntry = | |
| archOfObject.emplace(objectFile, GetObjectArch(objectFile)).first; | |
| } | |
| Arch const arch = archEntry->second; | |
| NormalizeSymbolName(symbol, arch); | |
| if (SymbolIsExcluded(symbol, arch)) { | |
| continue; | |
| } | |
| switch (sym_type) { | |
| case 'B': | |
| case 'D': | |
| dataSymbols.insert(symbol); | |
| continue; | |
| case 'T': | |
| symbols.insert(symbol); | |
| continue; | |
| case 'R': | |
| // Read only (i.e. constants) must be excluded, but vftables live | |
| // there too. They fail if marked as DATA, except on ARM64EC where | |
| // they must be marked as DATA. | |
| if (SymbolIsVftable(symbol)) { | |
| if (arch == Arch::ARM64EC) { | |
| dataSymbols.insert(symbol); | |
| } else { | |
| symbols.insert(symbol); | |
| } | |
| } | |
| continue; | |
| case 'A': | |
| case 'C': | |
| // Common symbols have no section, and the COFF symbol table walker | |
| // rejects symbols whose section number is not positive. | |
| case 'I': | |
| case 'N': | |
| case 'S': | |
| case 'U': | |
| case 'V': | |
| case 'W': | |
| case 'a': | |
| case 'b': | |
| case 'c': | |
| case 'd': | |
| case 'i': | |
| case 'n': | |
| case 'r': | |
| case 's': | |
| case 't': | |
| case 'v': | |
| case 'w': | |
| continue; | |
| default: | |
| std::cerr << "Ignoring symbol '" << symbol | |
| << "' with unrecognized type '" << sym_type | |
| << "' in object '" << objectFile << "'.\n"; | |
| } | |
| } | |
| return true; | |
| } | |
| static bool DumpFileWithNm(std::string const& nmPath, | |
| std::vector<std::string> const& objectFiles, | |
| std::string const& responseFile, | |
| std::set<std::string>& symbols, | |
| std::set<std::string>& dataSymbols) | |
| { | |
| if (!CreateResponseFile(objectFiles, responseFile)) { | |
| return false; | |
| } | |
| std::string const rspFile = '@' + responseFile; | |
| bool result = DumpFileWithNm(nmPath, rspFile, symbols, dataSymbols); | |
| cmSystemTools::RemoveFile(responseFile); | |
| return result; | |
| } | |
| enum class DumpFileResult | |
| { | |
| Success, | |
| Failed, | |
| LLVMBitcodeDetected, | |
| NotImplemented, | |
| }; | |
| static DumpFileResult DumpFile(std::string const& filename, | |
| std::set<std::string>& symbols, | |
| std::set<std::string>& dataSymbols) | |
| { | |
| #ifndef _WIN32 | |
| static_cast<void>(filename); | |
| static_cast<void>(symbols); | |
| static_cast<void>(dataSymbols); | |
| return DumpFileResult::NotImplemented; | |
| #else | |
| if (filename.empty()) { | |
| fprintf(stderr, "No object file was specified.\n"); | |
| return DumpFileResult::Failed; | |
| } | |
| HANDLE hFile; | |
| HANDLE hFileMapping; | |
| LPVOID lpFileBase; | |
| hFile = CreateFileW(cmsys::Encoding::ToWide(filename).c_str(), GENERIC_READ, | |
| FILE_SHARE_READ, nullptr, OPEN_EXISTING, | |
| FILE_ATTRIBUTE_NORMAL, 0); | |
| if (hFile == INVALID_HANDLE_VALUE) { | |
| fprintf(stderr, "Couldn't open file '%s' with CreateFile()\n", | |
| filename.c_str()); | |
| return DumpFileResult::Failed; | |
| } | |
| hFileMapping = | |
| CreateFileMapping(hFile, nullptr, PAGE_READONLY, 0, 0, nullptr); | |
| if (hFileMapping == 0) { | |
| CloseHandle(hFile); | |
| fprintf(stderr, "Couldn't open file mapping with CreateFileMapping()\n"); | |
| return DumpFileResult::Failed; | |
| } | |
| lpFileBase = MapViewOfFile(hFileMapping, FILE_MAP_READ, 0, 0, 0); | |
| if (lpFileBase == 0) { | |
| CloseHandle(hFileMapping); | |
| CloseHandle(hFile); | |
| fprintf(stderr, "Couldn't map view of file with MapViewOfFile()\n"); | |
| return DumpFileResult::Failed; | |
| } | |
| DumpFileResult result = DumpFileResult::Success; | |
| const PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)lpFileBase; | |
| if (dosHeader->e_magic == IMAGE_DOS_SIGNATURE) { | |
| fprintf(stderr, "File is an executable. I don't dump those.\n"); | |
| result = DumpFileResult::Failed; | |
| } else { | |
| const PIMAGE_FILE_HEADER imageHeader = (PIMAGE_FILE_HEADER)lpFileBase; | |
| /* Does it look like a COFF OBJ file??? */ | |
| if (((imageHeader->Machine == IMAGE_FILE_MACHINE_I386) || | |
| (imageHeader->Machine == IMAGE_FILE_MACHINE_AMD64) || | |
| (imageHeader->Machine == IMAGE_FILE_MACHINE_ARM) || | |
| (imageHeader->Machine == IMAGE_FILE_MACHINE_ARMNT) || | |
| (imageHeader->Machine == IMAGE_FILE_MACHINE_ARM64) || | |
| (imageHeader->Machine == IMAGE_FILE_MACHINE_ARM64EC)) && | |
| (imageHeader->Characteristics == 0)) { | |
| /* | |
| * The tests above are checking for IMAGE_FILE_HEADER.Machine | |
| * if it contains supported machine formats (currently ARM and x86) | |
| * and IMAGE_FILE_HEADER.Characteristics == 0 indicating that | |
| * this is not linked COFF OBJ file; | |
| */ | |
| DumpSymbols<IMAGE_FILE_HEADER, IMAGE_SYMBOL> symbolDumper( | |
| (PIMAGE_FILE_HEADER)lpFileBase, symbols, dataSymbols, | |
| (imageHeader->Machine == IMAGE_FILE_MACHINE_I386 | |
| ? Arch::I386 | |
| : (imageHeader->Machine == IMAGE_FILE_MACHINE_ARM64EC | |
| ? Arch::ARM64EC | |
| : Arch::Generic))); | |
| symbolDumper.DumpObjFile(); | |
| } else { | |
| // check for /bigobj and llvm LTO format | |
| cmANON_OBJECT_HEADER_BIGOBJ* h = | |
| (cmANON_OBJECT_HEADER_BIGOBJ*)lpFileBase; | |
| if (h->Sig1 == 0x0 && h->Sig2 == 0xffff && h->Version >= 2) { | |
| // bigobj | |
| DumpSymbols<cmANON_OBJECT_HEADER_BIGOBJ, cmIMAGE_SYMBOL_EX> | |
| symbolDumper( | |
| (cmANON_OBJECT_HEADER_BIGOBJ*)lpFileBase, symbols, dataSymbols, | |
| (h->Machine == IMAGE_FILE_MACHINE_I386 | |
| ? Arch::I386 | |
| : (h->Machine == IMAGE_FILE_MACHINE_ARM64EC ? Arch::ARM64EC | |
| : Arch::Generic))); | |
| symbolDumper.DumpObjFile(); | |
| } else if ( | |
| // BCexCODE - llvm bitcode | |
| (h->Sig1 == 0x4342 && h->Sig2 == 0xDEC0) || | |
| // 0x0B17C0DE - llvm bitcode BC wrapper | |
| (h->Sig1 == 0x0B17 && h->Sig2 == 0xC0DE)) { | |
| result = DumpFileResult::LLVMBitcodeDetected; | |
| } else { | |
| printf("unrecognized file format in '%s, %u'\n", filename.c_str(), | |
| imageHeader->Machine); | |
| result = DumpFileResult::Failed; | |
| } | |
| } | |
| } | |
| UnmapViewOfFile(lpFileBase); | |
| CloseHandle(hFileMapping); | |
| CloseHandle(hFile); | |
| return result; | |
| #endif | |
| } | |
| bool bindexplib::AddObjectFile(std::string const& objectFile) | |
| { | |
| auto const result = DumpFile(objectFile, this->Symbols, this->DataSymbols); | |
| if (result == DumpFileResult::Success) { | |
| return true; | |
| } | |
| if (result == DumpFileResult::Failed) { | |
| return false; | |
| } | |
| if (this->NmPath.empty()) { | |
| std::cerr << "No nm tool was specified for object file '" << objectFile | |
| << "'.\n"; | |
| return false; | |
| } | |
| return DumpFileWithNm(this->NmPath, objectFile, this->Symbols, | |
| this->DataSymbols); | |
| } | |
| bool bindexplib::AddObjectFile(std::vector<std::string> const& objectFiles, | |
| std::string const& responseFile) | |
| { | |
| std::vector<std::string> objectFilesTryNm; | |
| for (auto const& objectFile : objectFiles) { | |
| auto const result = DumpFile(objectFile, this->Symbols, this->DataSymbols); | |
| if (result == DumpFileResult::Success) { | |
| continue; | |
| } | |
| if (result == DumpFileResult::Failed) { | |
| return false; | |
| } | |
| objectFilesTryNm.push_back(objectFile); | |
| } | |
| if (objectFilesTryNm.empty()) { | |
| return true; | |
| } | |
| if (this->NmPath.empty()) { | |
| std::cerr << "No nm tool was specified for object file '" | |
| << objectFilesTryNm.front() << "'.\n"; | |
| return false; | |
| } | |
| return DumpFileWithNm(this->NmPath, objectFilesTryNm, responseFile, | |
| this->Symbols, this->DataSymbols); | |
| } | |
| bool bindexplib::AddDefinitionFile(std::string const& definitionFile) | |
| { | |
| cmsys::ifstream infile(definitionFile.c_str()); | |
| if (!infile) { | |
| std::cerr << "Couldn't open definition file '" << definitionFile << "'\n"; | |
| return false; | |
| } | |
| std::string str; | |
| while (std::getline(infile, str)) { | |
| // skip the LIBRARY and EXPORTS lines (if any) | |
| if ((str.compare(0, 7, "LIBRARY") == 0) || | |
| (str.compare(0, 7, "EXPORTS") == 0)) { | |
| continue; | |
| } | |
| // remove leading tabs & spaces | |
| str.erase(0, str.find_first_not_of(" \t")); | |
| std::size_t found = str.find(" \t DATA"); | |
| if (found != std::string::npos) { | |
| str.erase(found, std::string::npos); | |
| this->DataSymbols.insert(str); | |
| } else { | |
| this->Symbols.insert(str); | |
| } | |
| } | |
| infile.close(); | |
| return true; | |
| } | |
| bool bindexplib::AddDefinitionFile( | |
| std::vector<std::string> const& definitionFiles) | |
| { | |
| return std::all_of(definitionFiles.cbegin(), definitionFiles.cend(), | |
| [this](std::string const& definitionFile) { | |
| return this->AddDefinitionFile(definitionFile); | |
| }); | |
| } | |
| bool bindexplib::WriteFile(std::ostream& output) | |
| { | |
| output << "EXPORTS \n"; | |
| for (auto const& ds : this->DataSymbols) { | |
| output << '\t' << ds << " \t DATA\n"; | |
| } | |
| for (auto const& s : this->Symbols) { | |
| output << '\t' << s << '\n'; | |
| } | |
| output.flush(); | |
| return output.good(); | |
| } | |
| void bindexplib::SetNmPath(std::string const& nm) | |
| { | |
| this->NmPath = nm; | |
| } |