aboutsummaryrefslogtreecommitdiffhomepage
path: root/third_party/def_parser/def_parser.cc
diff options
context:
space:
mode:
authorGravatar Yun Peng <pcloudy@google.com>2017-08-25 09:59:14 +0200
committerGravatar Yun Peng <pcloudy@google.com>2017-08-28 09:37:47 +0200
commit66437a0173488ca4e77aab8c41d5454871c8c6a2 (patch)
tree3527d0aff2c40c5be751aa972d880b27e6eb6aa2 /third_party/def_parser/def_parser.cc
parent9e3a3e2f6a16288febc74a4a3c9086c995f6aa5a (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.cc451
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());
+ }
+}