blob: 2ed2db6cd1c5ddf8c414d75848db2126248d0d44 [file] [log] [blame]
Yun Peng66437a02017-08-25 09:59:14 +02001/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
2 file Copyright.txt or https://cmake.org/licensing for details. */
3/*-------------------------------------------------------------------------
4 Portions of this source have been derived from the 'bindexplib' tool
5 provided by the CERN ROOT Data Analysis Framework project (root.cern.ch).
6 Permission has been granted by Pere Mato <pere.mato@cern.ch> to distribute
7 this derived work under the CMake license.
8-------------------------------------------------------------------------*/
9
10/*
11*----------------------------------------------------------------------
12* Program: dumpexts.exe
13* Author: Gordon Chaffee
14*
15* History: The real functionality of this file was written by
16* Matt Pietrek in 1993 in his pedump utility. I've
17* modified it to dump the externals in a bunch of object
18* files to create a .def file.
19*
20* Notes: Visual C++ puts an underscore before each exported symbol.
21* This file removes them. I don't know if this is a problem
22* this other compilers. If _MSC_VER is defined,
23* the underscore is removed. If not, it isn't. To get a
24* full dump of an object file, use the -f option. This can
25* help determine the something that may be different with a
26* compiler other than Visual C++.
27* ======================================
28* Corrections (Axel 2006-04-04):
29* Conversion to C++. Mostly.
30*
31 * Extension (Axel 2006-03-15)
32 * As soon as an object file contains an /EXPORT directive (which
33 * is generated by the compiler when a symbol is declared as
34 * declspec(dllexport)) no to-be-exported symbols are printed,
35 * as the linker will see these directives, and if those directives
36 * are present we only export selectively (i.e. we trust the
37 * programmer).
38 *
39 * ======================================
40* ======================================
41* Corrections (Valery Fine 23/02/98):
42*
43* The "(vector) deleting destructor" MUST not be exported
44* To recognize it the following test are introduced:
45* "@@UAEPAXI@Z" scalar deleting dtor
46* "@@QAEPAXI@Z" vector deleting dtor
47* "AEPAXI@Z" vector deleting dtor with thunk adjustor
48* ======================================
49* Corrections (Valery Fine 12/02/97):
50*
51* It created a wrong EXPORTS for the global pointers and constants.
52* The Section Header has been involved to discover the missing information
53* Now the pointers are correctly supplied supplied with "DATA" descriptor
54* the constants with no extra descriptor.
55*
56* Corrections (Valery Fine 16/09/96):
57*
58* It didn't work for C++ code with global variables and class definitons
59* The DumpExternalObject function has been introduced to generate .DEF file
60*
61* Author: Valery Fine 16/09/96 (E-mail: fine@vxcern.cern.ch)
62*----------------------------------------------------------------------
63*/
Yun Peng66437a02017-08-25 09:59:14 +020064#include "third_party/def_parser/def_parser.h"
65
66#include <algorithm>
67#include <iostream>
68#include <fstream>
Yun Pengf203db02017-11-15 10:50:14 +010069#include <memory> // unique_ptr
Yun Peng66437a02017-08-25 09:59:14 +020070#include <sstream>
71#include <windows.h>
72
73#ifndef IMAGE_FILE_MACHINE_ARMNT
74#define IMAGE_FILE_MACHINE_ARMNT 0x01c4
75#endif
76
77using std::string;
78using std::wstring;
79using std::stringstream;
Yun Pengf203db02017-11-15 10:50:14 +010080using std::unique_ptr;
Yun Peng66437a02017-08-25 09:59:14 +020081
82typedef struct cmANON_OBJECT_HEADER_BIGOBJ {
83 /* same as ANON_OBJECT_HEADER_V2 */
84 WORD Sig1; // Must be IMAGE_FILE_MACHINE_UNKNOWN
85 WORD Sig2; // Must be 0xffff
86 WORD Version; // >= 2 (implies the Flags field is present)
87 WORD Machine; // Actual machine - IMAGE_FILE_MACHINE_xxx
88 DWORD TimeDateStamp;
89 CLSID ClassID; // {D1BAA1C7-BAEE-4ba9-AF20-FAF66AA4DCB8}
90 DWORD SizeOfData; // Size of data that follows the header
91 DWORD Flags; // 0x1 -> contains metadata
92 DWORD MetaDataSize; // Size of CLR metadata
93 DWORD MetaDataOffset; // Offset of CLR metadata
94
95 /* bigobj specifics */
96 DWORD NumberOfSections; // extended from WORD
97 DWORD PointerToSymbolTable;
98 DWORD NumberOfSymbols;
99} cmANON_OBJECT_HEADER_BIGOBJ;
100
101typedef struct _cmIMAGE_SYMBOL_EX {
102 union {
103 BYTE ShortName[8];
104 struct {
105 DWORD Short; // if 0, use LongName
106 DWORD Long; // offset into string table
107 } Name;
108 DWORD LongName[2]; // PBYTE [2]
109 } N;
110 DWORD Value;
111 LONG SectionNumber;
112 WORD Type;
113 BYTE StorageClass;
114 BYTE NumberOfAuxSymbols;
115} cmIMAGE_SYMBOL_EX;
116typedef cmIMAGE_SYMBOL_EX UNALIGNED* cmPIMAGE_SYMBOL_EX;
117
118PIMAGE_SECTION_HEADER GetSectionHeaderOffset(
119 PIMAGE_FILE_HEADER pImageFileHeader) {
120 return (PIMAGE_SECTION_HEADER)((DWORD_PTR)pImageFileHeader +
121 IMAGE_SIZEOF_FILE_HEADER +
122 pImageFileHeader->SizeOfOptionalHeader);
123}
124
125PIMAGE_SECTION_HEADER GetSectionHeaderOffset(
126 cmANON_OBJECT_HEADER_BIGOBJ* pImageFileHeader) {
127 return (PIMAGE_SECTION_HEADER)((DWORD_PTR)pImageFileHeader +
128 sizeof(cmANON_OBJECT_HEADER_BIGOBJ));
129}
130
131/*
132+ * Utility func, strstr with size
133+ */
134const char* StrNStr(const char* start, const char* find, size_t& size) {
135 size_t len;
136 const char* hint;
137
138 if (!start || !find || !size) {
139 size = 0;
140 return 0;
141 }
142 len = strlen(find);
143
144 while ((hint = (const char*)memchr(start, find[0], size - len + 1))) {
145 size -= (hint - start);
146 if (!strncmp(hint, find, len))
147 return hint;
148 start = hint + 1;
149 }
150
151 size = 0;
152 return 0;
153}
154
155template <
156 // cmANON_OBJECT_HEADER_BIGOBJ or IMAGE_FILE_HEADER
157 class ObjectHeaderType,
158 // cmPIMAGE_SYMBOL_EX or PIMAGE_SYMBOL
159 class SymbolTableType>
160class DumpSymbols {
161 public:
162 /*
163 *----------------------------------------------------------------------
164 * Constructor --
165 *
166 * Initialize variables from pointer to object header.
167 *
168 *----------------------------------------------------------------------
169 */
170
171 DumpSymbols(ObjectHeaderType* ih, std::set<string>& symbols,
172 std::set<string>& dataSymbols, bool isI386)
173 : Symbols(symbols)
174 , DataSymbols(dataSymbols) {
175 this->ObjectImageHeader = ih;
176 this->SymbolTable =
177 (SymbolTableType*)((DWORD_PTR) this->ObjectImageHeader +
178 this->ObjectImageHeader->PointerToSymbolTable);
179 this->SectionHeaders = GetSectionHeaderOffset(this->ObjectImageHeader);
180 this->SymbolCount = this->ObjectImageHeader->NumberOfSymbols;
181 this->IsI386 = isI386;
182 }
183
184 /*
185 *----------------------------------------------------------------------
186 * DumpObjFile --
187 *
188 * Dump an object file's exported symbols.
189 *----------------------------------------------------------------------
190 */
191 void DumpObjFile() {
192 this->DumpExternalsObjects();
193 }
194
195 /*
196 *----------------------------------------------------------------------
197 * DumpExternalsObjects --
198 *
199 * Dumps a COFF symbol table from an OBJ.
200 *----------------------------------------------------------------------
201 */
202 void DumpExternalsObjects() {
203 unsigned i;
204 PSTR stringTable;
205 string symbol;
206 DWORD SectChar;
207 /*
208 * The string table apparently starts right after the symbol table
209 */
210 stringTable = (PSTR) & this->SymbolTable[this->SymbolCount];
211 SymbolTableType* pSymbolTable = this->SymbolTable;
212 for (i = 0; i < this->SymbolCount; i++) {
213 if (pSymbolTable->SectionNumber > 0 &&
214 (pSymbolTable->Type == 0x20 || pSymbolTable->Type == 0x0)) {
215 if (pSymbolTable->StorageClass == IMAGE_SYM_CLASS_EXTERNAL) {
216 /*
217 * The name of the Function entry points
218 */
219 if (pSymbolTable->N.Name.Short != 0) {
220 symbol = "";
221 symbol.insert(0, (const char*)pSymbolTable->N.ShortName, 8);
222 } else {
223 symbol = stringTable + pSymbolTable->N.Name.Long;
224 }
225
226 // clear out any leading spaces
227 while (isspace(symbol[0]))
228 symbol.erase(0, 1);
229 // if it starts with _ and has an @ then it is a __cdecl
230 // so remove the @ stuff for the export
231 if (symbol[0] == '_') {
232 string::size_type posAt = symbol.find('@');
233 if (posAt != string::npos) {
234 symbol.erase(posAt);
235 }
236 }
237 // For i386 builds we need to remove _
238 if (this->IsI386 && symbol[0] == '_') {
239 symbol.erase(0, 1);
240 }
241
242 // Check whether it is "Scalar deleting destructor" and "Vector
243 // deleting destructor"
244 // if scalarPrefix and vectorPrefix are not found then print
245 // the symbol
246 const char* scalarPrefix = "??_G";
247 const char* vectorPrefix = "??_E";
248 // The original code had a check for
249 // symbol.find("real@") == string::npos)
250 // but this disallows member functions with the name "real".
251 if (symbol.compare(0, 4, scalarPrefix) &&
252 symbol.compare(0, 4, vectorPrefix)) {
253 SectChar = this->SectionHeaders[pSymbolTable->SectionNumber - 1]
254 .Characteristics;
255 // skip symbols containing a dot
256 if (symbol.find('.') == string::npos) {
257 if (!pSymbolTable->Type && (SectChar & IMAGE_SCN_MEM_WRITE)) {
258 // Read only (i.e. constants) must be excluded
259 this->DataSymbols.insert(symbol);
260 } else {
261 if (pSymbolTable->Type || !(SectChar & IMAGE_SCN_MEM_READ) ||
262 (SectChar & IMAGE_SCN_MEM_EXECUTE)) {
263 this->Symbols.insert(symbol);
264 }
265 }
266 }
267 }
268 }
269 }
270
271 /*
272 * Take into account any aux symbols
273 */
274 i += pSymbolTable->NumberOfAuxSymbols;
275 pSymbolTable += pSymbolTable->NumberOfAuxSymbols;
276 pSymbolTable++;
277 }
278 }
279
280 private:
281 std::set<string>& Symbols;
282 std::set<string>& DataSymbols;
283 DWORD_PTR SymbolCount;
284 PIMAGE_SECTION_HEADER SectionHeaders;
285 ObjectHeaderType* ObjectImageHeader;
286 SymbolTableType* SymbolTable;
287 bool IsI386;
288};
289
290void PrintLastError() {
291 DWORD last_error = GetLastError();
292 if (last_error == 0) {
293 return;
294 }
295
296 char* message_buffer;
297 size_t size = FormatMessageA(
298 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
299 FORMAT_MESSAGE_IGNORE_INSERTS,
300 NULL, last_error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
301 (LPSTR)&message_buffer, 0, NULL);
302
303 std::cerr << "(error: " << last_error << "): " << message_buffer;
304 LocalFree(message_buffer);
305}
306
Yun Pengf203db02017-11-15 10:50:14 +0100307wstring StringToWString(const string& s) {
308 SetLastError(ERROR_SUCCESS);
309 DWORD len = MultiByteToWideChar(CP_ACP, 0, s.c_str(), -1, NULL, 0);
310 if (len == 0 && GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
311 PrintLastError();
312 return L"";
313 }
314 unique_ptr<WCHAR[]> wstr(new WCHAR[len]);
315 MultiByteToWideChar(CP_ACP, 0, s.c_str(), -1, wstr.get(), len);
316 return wstring(wstr.get());
317}
318
319wstring AsAbsoluteWindowsPath(const string& path) {
320 wstring wpath = StringToWString(path);
321 // Get the buffer length we need for the full path.
322 SetLastError(ERROR_SUCCESS);
323 DWORD len = GetFullPathNameW(wpath.c_str(), 0, NULL, NULL);
324 if (len == 0 && GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
325 PrintLastError();
326 return L"";
327 }
328 SetLastError(ERROR_SUCCESS);
329 unique_ptr<WCHAR[]> buffer(new WCHAR[len]);
330 GetFullPathNameW(wpath.c_str(), len, buffer.get(), NULL);
331 if (GetLastError() != ERROR_SUCCESS) {
332 PrintLastError();
333 return L"";
334 }
335 return wstring(L"\\\\?\\") + wstring(buffer.get());
336}
337
Yun Peng66437a02017-08-25 09:59:14 +0200338bool DumpFile(const char* filename, std::set<string>& symbols,
339 std::set<string>& dataSymbols) {
340 HANDLE hFile;
341 HANDLE hFileMapping;
342 LPVOID lpFileBase;
343 PIMAGE_DOS_HEADER dosHeader;
344
Yun Pengf203db02017-11-15 10:50:14 +0100345 wstring filenameW = AsAbsoluteWindowsPath(filename);
Yun Peng66437a02017-08-25 09:59:14 +0200346 hFile = CreateFileW(filenameW.c_str(), GENERIC_READ,
347 FILE_SHARE_READ, NULL, OPEN_EXISTING,
348 FILE_ATTRIBUTE_NORMAL, 0);
349
350 if (hFile == INVALID_HANDLE_VALUE) {
351 PrintLastError();
352 fprintf(stderr, "Couldn't open file '%s' with CreateFile()\n", filename);
353 return false;
354 }
355
356 hFileMapping = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
357 if (hFileMapping == 0) {
358 PrintLastError();
359 fprintf(stderr, "Couldn't open file mapping with CreateFileMapping()\n");
360 CloseHandle(hFile);
361 return false;
362 }
363
364 lpFileBase = MapViewOfFile(hFileMapping, FILE_MAP_READ, 0, 0, 0);
365 if (lpFileBase == 0) {
366 PrintLastError();
367 fprintf(stderr, "Couldn't map view of file with MapViewOfFile()\n");
368 CloseHandle(hFileMapping);
369 CloseHandle(hFile);
370 return false;
371 }
372
373 dosHeader = (PIMAGE_DOS_HEADER)lpFileBase;
374 if (dosHeader->e_magic == IMAGE_DOS_SIGNATURE) {
375 fprintf(stderr, "File is an executable. I don't dump those.\n");
376 return false;
377 }
378 /* Does it look like a COFF OBJ file??? */
379 else if (((dosHeader->e_magic == IMAGE_FILE_MACHINE_I386) ||
380 (dosHeader->e_magic == IMAGE_FILE_MACHINE_AMD64) ||
381 (dosHeader->e_magic == IMAGE_FILE_MACHINE_ARMNT)) &&
382 (dosHeader->e_sp == 0)) {
383 /*
384 * The two tests above aren't what they look like. They're
385 * really checking for IMAGE_FILE_HEADER.Machine == i386 (0x14C)
386 * and IMAGE_FILE_HEADER.SizeOfOptionalHeader == 0;
387 */
388 DumpSymbols<IMAGE_FILE_HEADER, IMAGE_SYMBOL> symbolDumper(
389 (PIMAGE_FILE_HEADER)lpFileBase, symbols, dataSymbols,
390 (dosHeader->e_magic == IMAGE_FILE_MACHINE_I386));
391 symbolDumper.DumpObjFile();
392 } else {
393 // check for /bigobj format
394 cmANON_OBJECT_HEADER_BIGOBJ* h = (cmANON_OBJECT_HEADER_BIGOBJ*)lpFileBase;
395 if (h->Sig1 == 0x0 && h->Sig2 == 0xffff) {
396 DumpSymbols<cmANON_OBJECT_HEADER_BIGOBJ, cmIMAGE_SYMBOL_EX> symbolDumper(
397 (cmANON_OBJECT_HEADER_BIGOBJ*)lpFileBase, symbols, dataSymbols,
398 (h->Machine == IMAGE_FILE_MACHINE_I386));
399 symbolDumper.DumpObjFile();
400 } else {
401 printf("Unrecognized file format in '%s'\n", filename);
402 return false;
403 }
404 }
405 UnmapViewOfFile(lpFileBase);
406 CloseHandle(hFileMapping);
407 CloseHandle(hFile);
408 return true;
409}
410
411
412void DefParser::SetDLLName(const string& dllname) {
413 this->DLLName = dllname;
414}
415
416bool DefParser::AddObjectFile(const char* filename) {
417 return DumpFile(filename, this->Symbols, this->DataSymbols);
418}
419
420bool DefParser::AddDefinitionFile(const char* filename) {
421 std::ifstream infile(filename);
422 if (!infile) {
423 PrintLastError();
424 fprintf(stderr, "Couldn't open definition file '%s'\n", filename);
425 return false;
426 }
427 string str;
428 while (std::getline(infile, str)) {
429 // skip the LIBRAY and EXPORTS lines (if any)
430 if ((str.compare(0, 7, "LIBRARY") == 0) ||
431 (str.compare(0, 7, "EXPORTS") == 0)) {
432 continue;
433 }
434 // remove leading tabs & spaces
435 str.erase(0, str.find_first_not_of(" \t"));
436 std::size_t found = str.find(" \t DATA");
437 if (found != string::npos) {
438 str.erase(found, string::npos);
439 this->DataSymbols.insert(str);
440 } else {
441 this->Symbols.insert(str);
442 }
443 }
444 infile.close();
445 return true;
446}
447
448bool DefParser::IsDefFile(const string& file) {
449 // Get file extension and convert it to lower case.
450 string ext = file.substr(file.find_last_of(".") + 1);
451 std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower);
452 return ext == "def";
453}
454
455bool DefParser::AddFile(const string& file) {
456 if (IsDefFile(file)) {
457 if (!this->AddDefinitionFile(file.c_str())) {
458 return false;
459 }
460 } else {
461 if (!this->AddObjectFile(file.c_str())) {
462 return false;
463 }
464 }
465 return true;
466}
467
468void DefParser::WriteFile(FILE* file) {
469 if (!this->DLLName.empty()) {
470 fprintf(file, "LIBRARY %s\n", this->DLLName.c_str());
471 }
472
473 fprintf(file, "EXPORTS \n");
474 for (std::set<string>::const_iterator i = this->DataSymbols.begin();
475 i != this->DataSymbols.end(); ++i) {
476 fprintf(file, "\t%s \t DATA\n", i->c_str());
477 }
478 for (std::set<string>::const_iterator i = this->Symbols.begin();
479 i != this->Symbols.end(); ++i) {
480 fprintf(file, "\t%s\n", i->c_str());
481 }
482}