diff options
author | 2017-08-25 09:59:14 +0200 | |
---|---|---|
committer | 2017-08-28 09:37:47 +0200 | |
commit | 66437a0173488ca4e77aab8c41d5454871c8c6a2 (patch) | |
tree | 3527d0aff2c40c5be751aa972d880b27e6eb6aa2 /third_party/def_parser/def_parser.cc | |
parent | 9e3a3e2f6a16288febc74a4a3c9086c995f6aa5a (diff) |
Windows: Implementing C++ DEF parser
C++ DEF parser can generating a DEF file from a object file, which can
be used to export symbols during linking DLL on Windows.
This parser is based on an implementation in CMake
See https://github.com/Kitware/CMake/blob/master/Source/bindexplib.cxx
A few changes has been made to make it work better.
Usage: output_deffile dllname [objfile ...] [input_deffile ...] [@paramfile ...]
output_deffile: the output DEF file
dllname: the DLL name this DEF file is used for, if dllname is not empty
string (eg. ""), def_parser writes an 'LIBRARY <dllname>' entry
into DEF file.
objfile: a object file, def_parser parses this file to find symbols,
then merges them into final result.
Can apppear multiple times.
input_deffile: an existing def file, def_parser merges all symbols in this file.
Can appear multiple times.
@paramfile: a parameter file that can contain objfile and input_deffile
Can appear multiple time.
Change-Id: I0ee65fa3119ecae2ea195b707af5690e4bc6a6c2
Diffstat (limited to 'third_party/def_parser/def_parser.cc')
-rw-r--r-- | third_party/def_parser/def_parser.cc | 451 |
1 files changed, 451 insertions, 0 deletions
diff --git a/third_party/def_parser/def_parser.cc b/third_party/def_parser/def_parser.cc new file mode 100644 index 0000000000..07cb727b71 --- /dev/null +++ b/third_party/def_parser/def_parser.cc @@ -0,0 +1,451 @@ +/* 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 "src/main/cpp/util/file_platform.h" +#include "third_party/def_parser/def_parser.h" + +#include <algorithm> +#include <iostream> +#include <fstream> +#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; + +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); +} + +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; + blaze_util::AsAbsoluteWindowsPath(filename, &filenameW); + 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_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()); + } +} |