|  | /* 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  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 definitons | 
|  | *     The DumpExternalObject function has been introduced to generate .DEF file | 
|  | * | 
|  | * Author:   Valery Fine 16/09/96  (E-mail: fine@vxcern.cern.ch) | 
|  | *---------------------------------------------------------------------- | 
|  | */ | 
|  | #include "third_party/def_parser/def_parser.h" | 
|  |  | 
|  | #include <algorithm> | 
|  | #include <iostream> | 
|  | #include <fstream> | 
|  | #include <memory>    // unique_ptr | 
|  | #include <sstream> | 
|  | #include <windows.h> | 
|  |  | 
|  | #ifndef IMAGE_FILE_MACHINE_ARMNT | 
|  | #define IMAGE_FILE_MACHINE_ARMNT 0x01c4 | 
|  | #endif | 
|  |  | 
|  | using std::string; | 
|  | using std::wstring; | 
|  | using std::stringstream; | 
|  | using std::unique_ptr; | 
|  |  | 
|  | 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<string>& symbols, | 
|  | std::set<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; | 
|  | 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 = ""; | 
|  | 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] == '_') { | 
|  | string::size_type posAt = symbol.find('@'); | 
|  | if (posAt != 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@") == 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('.') == 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<string>& Symbols; | 
|  | std::set<string>& DataSymbols; | 
|  | DWORD_PTR SymbolCount; | 
|  | PIMAGE_SECTION_HEADER SectionHeaders; | 
|  | ObjectHeaderType* ObjectImageHeader; | 
|  | SymbolTableType* SymbolTable; | 
|  | bool IsI386; | 
|  | }; | 
|  |  | 
|  | void PrintLastError() { | 
|  | DWORD last_error = GetLastError(); | 
|  | if (last_error == 0) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | char* message_buffer; | 
|  | size_t size = FormatMessageA( | 
|  | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | | 
|  | FORMAT_MESSAGE_IGNORE_INSERTS, | 
|  | NULL, last_error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), | 
|  | (LPSTR)&message_buffer, 0, NULL); | 
|  |  | 
|  | std::cerr << "(error: " << last_error << "): " << message_buffer; | 
|  | LocalFree(message_buffer); | 
|  | } | 
|  |  | 
|  | wstring StringToWString(const string& s) { | 
|  | SetLastError(ERROR_SUCCESS); | 
|  | DWORD len = MultiByteToWideChar(CP_ACP, 0, s.c_str(), -1, NULL, 0); | 
|  | if (len == 0 && GetLastError() != ERROR_INSUFFICIENT_BUFFER) { | 
|  | PrintLastError(); | 
|  | return L""; | 
|  | } | 
|  | unique_ptr<WCHAR[]> wstr(new WCHAR[len]); | 
|  | MultiByteToWideChar(CP_ACP, 0, s.c_str(), -1, wstr.get(), len); | 
|  | return wstring(wstr.get()); | 
|  | } | 
|  |  | 
|  | wstring AsAbsoluteWindowsPath(const string& path) { | 
|  | wstring wpath = StringToWString(path); | 
|  | // Get the buffer length we need for the full path. | 
|  | SetLastError(ERROR_SUCCESS); | 
|  | DWORD len = GetFullPathNameW(wpath.c_str(), 0, NULL, NULL); | 
|  | if (len == 0 && GetLastError() != ERROR_INSUFFICIENT_BUFFER) { | 
|  | PrintLastError(); | 
|  | return L""; | 
|  | } | 
|  | SetLastError(ERROR_SUCCESS); | 
|  | unique_ptr<WCHAR[]> buffer(new WCHAR[len]); | 
|  | GetFullPathNameW(wpath.c_str(), len, buffer.get(), NULL); | 
|  | if (GetLastError() != ERROR_SUCCESS) { | 
|  | PrintLastError(); | 
|  | return L""; | 
|  | } | 
|  | return wstring(L"\\\\?\\") + wstring(buffer.get()); | 
|  | } | 
|  |  | 
|  | bool DumpFile(const char* filename, std::set<string>& symbols, | 
|  | std::set<string>& dataSymbols) { | 
|  | HANDLE hFile; | 
|  | HANDLE hFileMapping; | 
|  | LPVOID lpFileBase; | 
|  | PIMAGE_DOS_HEADER dosHeader; | 
|  |  | 
|  | wstring filenameW = AsAbsoluteWindowsPath(filename); | 
|  | hFile = CreateFileW(filenameW.c_str(), GENERIC_READ, | 
|  | FILE_SHARE_READ, NULL, OPEN_EXISTING, | 
|  | FILE_ATTRIBUTE_NORMAL, 0); | 
|  |  | 
|  | if (hFile == INVALID_HANDLE_VALUE) { | 
|  | PrintLastError(); | 
|  | 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) { | 
|  | PrintLastError(); | 
|  | fprintf(stderr, "Couldn't open file mapping with CreateFileMapping()\n"); | 
|  | CloseHandle(hFile); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | lpFileBase = MapViewOfFile(hFileMapping, FILE_MAP_READ, 0, 0, 0); | 
|  | if (lpFileBase == 0) { | 
|  | PrintLastError(); | 
|  | fprintf(stderr, "Couldn't map view of file with MapViewOfFile()\n"); | 
|  | CloseHandle(hFileMapping); | 
|  | CloseHandle(hFile); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | 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; | 
|  | } | 
|  | /* Does it look like a COFF OBJ file??? */ | 
|  | else if (((dosHeader->e_magic == IMAGE_FILE_MACHINE_I386) || | 
|  | (dosHeader->e_magic == IMAGE_FILE_MACHINE_AMD64) || | 
|  | (dosHeader->e_magic == IMAGE_FILE_MACHINE_ARM64) || | 
|  | (dosHeader->e_magic == IMAGE_FILE_MACHINE_ARMNT)) && | 
|  | (dosHeader->e_sp == 0)) { | 
|  | /* | 
|  | * The two tests above aren't what they look like.  They're | 
|  | * really checking for IMAGE_FILE_HEADER.Machine == i386 (0x14C) | 
|  | * and IMAGE_FILE_HEADER.SizeOfOptionalHeader == 0; | 
|  | */ | 
|  | DumpSymbols<IMAGE_FILE_HEADER, IMAGE_SYMBOL> symbolDumper( | 
|  | (PIMAGE_FILE_HEADER)lpFileBase, symbols, dataSymbols, | 
|  | (dosHeader->e_magic == 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; | 
|  | } | 
|  |  | 
|  |  | 
|  | void DefParser::SetDLLName(const string& dllname) { | 
|  | this->DLLName = dllname; | 
|  | } | 
|  |  | 
|  | bool DefParser::AddObjectFile(const char* filename) { | 
|  | return DumpFile(filename, this->Symbols, this->DataSymbols); | 
|  | } | 
|  |  | 
|  | bool DefParser::AddDefinitionFile(const char* filename) { | 
|  | std::ifstream infile(filename); | 
|  | if (!infile) { | 
|  | PrintLastError(); | 
|  | fprintf(stderr, "Couldn't open definition file '%s'\n", filename); | 
|  | return false; | 
|  | } | 
|  | string str; | 
|  | while (std::getline(infile, str)) { | 
|  | // skip the LIBRAY 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 != string::npos) { | 
|  | str.erase(found, string::npos); | 
|  | this->DataSymbols.insert(str); | 
|  | } else { | 
|  | this->Symbols.insert(str); | 
|  | } | 
|  | } | 
|  | infile.close(); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool DefParser::IsDefFile(const string& file) { | 
|  | // Get file extension and convert it to lower case. | 
|  | string ext = file.substr(file.find_last_of(".") + 1); | 
|  | std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower); | 
|  | return ext == "def"; | 
|  | } | 
|  |  | 
|  | bool DefParser::AddFile(const string& file) { | 
|  | if (IsDefFile(file)) { | 
|  | if (!this->AddDefinitionFile(file.c_str())) { | 
|  | return false; | 
|  | } | 
|  | } else { | 
|  | if (!this->AddObjectFile(file.c_str())) { | 
|  | return false; | 
|  | } | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | void DefParser::WriteFile(FILE* file) { | 
|  | if (!this->DLLName.empty()) { | 
|  | fprintf(file, "LIBRARY %s\n", this->DLLName.c_str()); | 
|  | } | 
|  |  | 
|  | fprintf(file, "EXPORTS \n"); | 
|  | for (std::set<string>::const_iterator i = this->DataSymbols.begin(); | 
|  | i != this->DataSymbols.end(); ++i) { | 
|  | fprintf(file, "\t%s \t DATA\n", i->c_str()); | 
|  | } | 
|  | for (std::set<string>::const_iterator i = this->Symbols.begin(); | 
|  | i != this->Symbols.end(); ++i) { | 
|  | fprintf(file, "\t%s\n", i->c_str()); | 
|  | } | 
|  | } |