/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt 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 "cmsys/Encoding.hxx" #include "cmsys/FStream.hxx" #include <iostream> #include <windows.h> #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 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 + */ const char* StrNStr(const char* start, const char* find, size_t& size) { size_t len; const char* hint; if (!start || !find || !size) { size = 0; return 0; } len = strlen(find); while ((hint = (const char*)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, bool isI386) : 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->IsI386 = isI386; } /* *---------------------------------------------------------------------- * 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, (const char*)pSymbolTable->N.ShortName, 8); } else { symbol = stringTable + pSymbolTable->N.Name.Long; } // clear out any leading spaces while (isspace(symbol[0])) symbol.erase(0, 1); // if it starts with _ and has an @ then it is a __cdecl // so remove the @ stuff for the export if (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 (this->IsI386 && symbol[0] == '_') { symbol.erase(0, 1); } // Check whether it is "Scalar deleting destructor" and "Vector // deleting destructor" // if scalarPrefix and vectorPrefix are not found then print // the symbol const char* scalarPrefix = "??_G"; const char* vectorPrefix = "??_E"; // The original code had a check for // symbol.find("real@") == std::string::npos) // but this disallows member functions with the name "real". if (symbol.compare(0, 4, scalarPrefix) && symbol.compare(0, 4, vectorPrefix)) { SectChar = this->SectionHeaders[pSymbolTable->SectionNumber - 1] .Characteristics; // skip symbols containing a dot if (symbol.find('.') == std::string::npos) { if (!pSymbolTable->Type && (SectChar & IMAGE_SCN_MEM_WRITE)) { // Read only (i.e. constants) must be excluded this->DataSymbols.insert(symbol); } else { if (pSymbolTable->Type || !(SectChar & IMAGE_SCN_MEM_READ) || (SectChar & IMAGE_SCN_MEM_EXECUTE)) { 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; bool IsI386; }; bool DumpFile(const char* filename, std::set<std::string>& symbols, std::set<std::string>& dataSymbols) { HANDLE hFile; HANDLE hFileMapping; LPVOID lpFileBase; hFile = CreateFileW(cmsys::Encoding::ToWide(filename).c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); if (hFile == INVALID_HANDLE_VALUE) { fprintf(stderr, "Couldn't open file '%s' with CreateFile()\n", filename); return false; } hFileMapping = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL); if (hFileMapping == 0) { CloseHandle(hFile); fprintf(stderr, "Couldn't open file mapping with CreateFileMapping()\n"); return false; } 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 false; } 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"); return false; } 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->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)); symbolDumper.DumpObjFile(); } else { // check for /bigobj format cmANON_OBJECT_HEADER_BIGOBJ* h = (cmANON_OBJECT_HEADER_BIGOBJ*)lpFileBase; if (h->Sig1 == 0x0 && h->Sig2 == 0xffff) { DumpSymbols<cmANON_OBJECT_HEADER_BIGOBJ, cmIMAGE_SYMBOL_EX> symbolDumper((cmANON_OBJECT_HEADER_BIGOBJ*)lpFileBase, symbols, dataSymbols, (h->Machine == IMAGE_FILE_MACHINE_I386)); symbolDumper.DumpObjFile(); } else { printf("unrecognized file format in '%s'\n", filename); return false; } } } UnmapViewOfFile(lpFileBase); CloseHandle(hFileMapping); CloseHandle(hFile); return true; } bool bindexplib::AddObjectFile(const char* filename) { return DumpFile(filename, this->Symbols, this->DataSymbols); } bool bindexplib::AddDefinitionFile(const char* filename) { cmsys::ifstream infile(filename); if (!infile) { fprintf(stderr, "Couldn't open definition file '%s'\n", filename); 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; } void bindexplib::WriteFile(FILE* file) { fprintf(file, "EXPORTS \n"); for (std::string const& ds : this->DataSymbols) { fprintf(file, "\t%s \t DATA\n", ds.c_str()); } for (std::string const& s : this->Symbols) { fprintf(file, "\t%s\n", s.c_str()); } }