/*============================================================================ CMake - Cross Platform Makefile Generator Copyright 2000-2015 Kitware, Inc. Distributed under the OSI-approved BSD License (the "License"); see accompanying file Copyright.txt for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the License for more information. ============================================================================*/ /*------------------------------------------------------------------------- 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 <cmsys/Encoding.hxx> #include <windows.h> #include <stdio.h> #include <string> #include <fstream> #include <iostream> 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, FILE* fout, bool is64) { this->ObjectImageHeader = ih; this->SymbolTable = (SymbolTableType*) ((DWORD_PTR)this->ObjectImageHeader + this->ObjectImageHeader->PointerToSymbolTable); this->FileOut = fout; this->SectionHeaders = GetSectionHeaderOffset(this->ObjectImageHeader); this->ImportFlag = true; this->SymbolCount = this->ObjectImageHeader->NumberOfSymbols; this->Is64Bit = is64; } /* *---------------------------------------------------------------------- * HaveExportedObjects -- * * Returns true if export directives (declspec(dllexport)) exist. * *---------------------------------------------------------------------- */ bool HaveExportedObjects() { WORD i = 0; size_t size = 0; const char * rawdata = 0; PIMAGE_SECTION_HEADER pDirectivesSectionHeader = 0; PIMAGE_SECTION_HEADER pSectionHeaders = this->SectionHeaders; for(i = 0; (i < this->ObjectImageHeader->NumberOfSections && !pDirectivesSectionHeader); i++) if (!strncmp((const char*)&pSectionHeaders[i].Name[0], ".drectve",8)) pDirectivesSectionHeader = &pSectionHeaders[i]; if (!pDirectivesSectionHeader) return 0; rawdata=(const char*) this->ObjectImageHeader+pDirectivesSectionHeader->PointerToRawData; if (!pDirectivesSectionHeader->PointerToRawData || !rawdata) return 0; size = pDirectivesSectionHeader->SizeOfRawData; const char* posImportFlag = rawdata; while ((posImportFlag = StrNStr(posImportFlag, " /EXPORT:", size))) { const char* lookingForDict = posImportFlag + 9; if (!strncmp(lookingForDict, "_G__cpp_",8) || !strncmp(lookingForDict, "_G__set_cpp_",12)) { posImportFlag = lookingForDict; continue; } const char* lookingForDATA = posImportFlag + 9; while (*(++lookingForDATA) && *lookingForDATA != ' '); lookingForDATA -= 5; // ignore DATA exports if (strncmp(lookingForDATA, ",DATA", 5)) break; posImportFlag = lookingForDATA + 5; } if(posImportFlag) { return true; } return false; } /* *---------------------------------------------------------------------- * DumpObjFile -- * * Dump an object file's exported symbols. *---------------------------------------------------------------------- */ void DumpObjFile() { if(!HaveExportedObjects()) { 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 = ""; 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 64 bit builds we don't need to remove _ if(!this->Is64Bit) { if (symbol[0] == '_') { symbol.erase(0,1); } } if (this->ImportFlag) { this->ImportFlag = false; fprintf(this->FileOut,"EXPORTS \n"); } /* Check whether it is "Scalar deleting destructor" and "Vector deleting destructor" */ const char *scalarPrefix = "??_G"; const char *vectorPrefix = "??_E"; // original code had a check for // symbol.find("real@") == std::string::npos) // but if this disallows memmber functions with the name real // if scalarPrefix and vectorPrefix are not found then print // the symbol if (symbol.compare(0, 4, scalarPrefix) && symbol.compare(0, 4, vectorPrefix) ) { SectChar = this-> SectionHeaders[pSymbolTable->SectionNumber-1].Characteristics; if (!pSymbolTable->Type && (SectChar & IMAGE_SCN_MEM_WRITE)) { // Read only (i.e. constants) must be excluded fprintf(this->FileOut, "\t%s \t DATA\n", symbol.c_str()); } else { if ( pSymbolTable->Type || !(SectChar & IMAGE_SCN_MEM_READ)) { fprintf(this->FileOut, "\t%s\n", symbol.c_str()); } else { // printf(" strange symbol: %s \n",symbol.c_str()); } } } } } else if (pSymbolTable->SectionNumber == IMAGE_SYM_UNDEFINED && !pSymbolTable->Type && 0) { /* * The IMPORT global variable entry points */ if (pSymbolTable->StorageClass == IMAGE_SYM_CLASS_EXTERNAL) { symbol = stringTable + pSymbolTable->N.Name.Long; while (isspace(symbol[0])) symbol.erase(0,1); if (symbol[0] == '_') symbol.erase(0,1); if (!this->ImportFlag) { this->ImportFlag = true; fprintf(this->FileOut,"IMPORTS \n"); } fprintf(this->FileOut, "\t%s DATA \n", symbol.c_str()+1); } } /* * Take into account any aux symbols */ i += pSymbolTable->NumberOfAuxSymbols; pSymbolTable += pSymbolTable->NumberOfAuxSymbols; pSymbolTable++; } } private: bool ImportFlag; FILE* FileOut; DWORD_PTR SymbolCount; PIMAGE_SECTION_HEADER SectionHeaders; ObjectHeaderType* ObjectImageHeader; SymbolTableType* SymbolTable; bool Is64Bit; }; bool DumpFile(const char* filename, FILE *fout) { HANDLE hFile; HANDLE hFileMapping; LPVOID lpFileBase; PIMAGE_DOS_HEADER dosHeader; 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; } 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 i386 COFF OBJ file??? */ else if ( ((dosHeader->e_magic == IMAGE_FILE_MACHINE_I386) || (dosHeader->e_magic == IMAGE_FILE_MACHINE_AMD64)) && (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, fout, (dosHeader->e_magic == IMAGE_FILE_MACHINE_AMD64)); 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, fout, (h->Machine == IMAGE_FILE_MACHINE_AMD64)); symbolDumper.DumpObjFile(); } else { printf("unrecognized file format in '%s'\n", filename); return false; } } UnmapViewOfFile(lpFileBase); CloseHandle(hFileMapping); CloseHandle(hFile); return true; }