bindexplib.cxx 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403
  1. /* 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 <[email protected]> to distribute
  7. this derived work under the CMake license.
  8. -------------------------------------------------------------------------*/
  9. /*
  10. *----------------------------------------------------------------------
  11. * Program: dumpexts.exe
  12. * Author: Gordon Chaffee
  13. *
  14. * History: The real functionality of this file was written by
  15. * Matt Pietrek in 1993 in his pedump utility. I've
  16. * modified it to dump the externals in a bunch of object
  17. * files to create a .def file.
  18. *
  19. * Notes: Visual C++ puts an underscore before each exported symbol.
  20. * This file removes them. I don't know if this is a problem
  21. * this other compilers. If _MSC_VER is defined,
  22. * the underscore is removed. If not, it isn't. To get a
  23. * full dump of an object file, use the -f option. This can
  24. * help determine the something that may be different with a
  25. * compiler other than Visual C++.
  26. * ======================================
  27. * Corrections (Axel 2006-04-04):
  28. * Conversion to C++. Mostly.
  29. *
  30. * Extension (Axel 2006-03-15)
  31. * As soon as an object file contains an /EXPORT directive (which
  32. * is generated by the compiler when a symbol is declared as
  33. * declspec(dllexport)) no to-be-exported symbols are printed,
  34. * as the linker will see these directives, and if those directives
  35. * are present we only export selectively (i.e. we trust the
  36. * programmer).
  37. *
  38. * ======================================
  39. * ======================================
  40. * Corrections (Valery Fine 23/02/98):
  41. *
  42. * The "(vector) deleting destructor" MUST not be exported
  43. * To recognize it the following test are introduced:
  44. * "@@UAEPAXI@Z" scalar deleting dtor
  45. * "@@QAEPAXI@Z" vector deleting dtor
  46. * "AEPAXI@Z" vector deleting dtor with thunk adjustor
  47. * ======================================
  48. * Corrections (Valery Fine 12/02/97):
  49. *
  50. * It created a wrong EXPORTS for the global pointers and constants.
  51. * The Section Header has been involved to discover the missing information
  52. * Now the pointers are correctly supplied supplied with "DATA" descriptor
  53. * the constants with no extra descriptor.
  54. *
  55. * Corrections (Valery Fine 16/09/96):
  56. *
  57. * It didn't work for C++ code with global variables and class definitons
  58. * The DumpExternalObject function has been introduced to generate .DEF file
  59. *
  60. * Author: Valery Fine 16/09/96 (E-mail: [email protected])
  61. *----------------------------------------------------------------------
  62. */
  63. #include "bindexplib.h"
  64. #include "cmsys/Encoding.hxx"
  65. #include "cmsys/FStream.hxx"
  66. #include <iostream>
  67. #include <windows.h>
  68. #ifndef IMAGE_FILE_MACHINE_ARMNT
  69. #define IMAGE_FILE_MACHINE_ARMNT 0x01c4
  70. #endif
  71. typedef struct cmANON_OBJECT_HEADER_BIGOBJ
  72. {
  73. /* same as ANON_OBJECT_HEADER_V2 */
  74. WORD Sig1; // Must be IMAGE_FILE_MACHINE_UNKNOWN
  75. WORD Sig2; // Must be 0xffff
  76. WORD Version; // >= 2 (implies the Flags field is present)
  77. WORD Machine; // Actual machine - IMAGE_FILE_MACHINE_xxx
  78. DWORD TimeDateStamp;
  79. CLSID ClassID; // {D1BAA1C7-BAEE-4ba9-AF20-FAF66AA4DCB8}
  80. DWORD SizeOfData; // Size of data that follows the header
  81. DWORD Flags; // 0x1 -> contains metadata
  82. DWORD MetaDataSize; // Size of CLR metadata
  83. DWORD MetaDataOffset; // Offset of CLR metadata
  84. /* bigobj specifics */
  85. DWORD NumberOfSections; // extended from WORD
  86. DWORD PointerToSymbolTable;
  87. DWORD NumberOfSymbols;
  88. } cmANON_OBJECT_HEADER_BIGOBJ;
  89. typedef struct _cmIMAGE_SYMBOL_EX
  90. {
  91. union
  92. {
  93. BYTE ShortName[8];
  94. struct
  95. {
  96. DWORD Short; // if 0, use LongName
  97. DWORD Long; // offset into string table
  98. } Name;
  99. DWORD LongName[2]; // PBYTE [2]
  100. } N;
  101. DWORD Value;
  102. LONG SectionNumber;
  103. WORD Type;
  104. BYTE StorageClass;
  105. BYTE NumberOfAuxSymbols;
  106. } cmIMAGE_SYMBOL_EX;
  107. typedef cmIMAGE_SYMBOL_EX UNALIGNED* cmPIMAGE_SYMBOL_EX;
  108. PIMAGE_SECTION_HEADER GetSectionHeaderOffset(
  109. PIMAGE_FILE_HEADER pImageFileHeader)
  110. {
  111. return (PIMAGE_SECTION_HEADER)((DWORD_PTR)pImageFileHeader +
  112. IMAGE_SIZEOF_FILE_HEADER +
  113. pImageFileHeader->SizeOfOptionalHeader);
  114. }
  115. PIMAGE_SECTION_HEADER GetSectionHeaderOffset(
  116. cmANON_OBJECT_HEADER_BIGOBJ* pImageFileHeader)
  117. {
  118. return (PIMAGE_SECTION_HEADER)((DWORD_PTR)pImageFileHeader +
  119. sizeof(cmANON_OBJECT_HEADER_BIGOBJ));
  120. }
  121. /*
  122. + * Utility func, strstr with size
  123. + */
  124. const char* StrNStr(const char* start, const char* find, size_t& size)
  125. {
  126. size_t len;
  127. const char* hint;
  128. if (!start || !find || !size) {
  129. size = 0;
  130. return 0;
  131. }
  132. len = strlen(find);
  133. while ((hint = (const char*)memchr(start, find[0], size - len + 1))) {
  134. size -= (hint - start);
  135. if (!strncmp(hint, find, len))
  136. return hint;
  137. start = hint + 1;
  138. }
  139. size = 0;
  140. return 0;
  141. }
  142. template <
  143. // cmANON_OBJECT_HEADER_BIGOBJ or IMAGE_FILE_HEADER
  144. class ObjectHeaderType,
  145. // cmPIMAGE_SYMBOL_EX or PIMAGE_SYMBOL
  146. class SymbolTableType>
  147. class DumpSymbols
  148. {
  149. public:
  150. /*
  151. *----------------------------------------------------------------------
  152. * Constructor --
  153. *
  154. * Initialize variables from pointer to object header.
  155. *
  156. *----------------------------------------------------------------------
  157. */
  158. DumpSymbols(ObjectHeaderType* ih, std::set<std::string>& symbols,
  159. std::set<std::string>& dataSymbols, bool isI386)
  160. : Symbols(symbols)
  161. , DataSymbols(dataSymbols)
  162. {
  163. this->ObjectImageHeader = ih;
  164. this->SymbolTable =
  165. (SymbolTableType*)((DWORD_PTR) this->ObjectImageHeader +
  166. this->ObjectImageHeader->PointerToSymbolTable);
  167. this->SectionHeaders = GetSectionHeaderOffset(this->ObjectImageHeader);
  168. this->SymbolCount = this->ObjectImageHeader->NumberOfSymbols;
  169. this->IsI386 = isI386;
  170. }
  171. /*
  172. *----------------------------------------------------------------------
  173. * DumpObjFile --
  174. *
  175. * Dump an object file's exported symbols.
  176. *----------------------------------------------------------------------
  177. */
  178. void DumpObjFile() { this->DumpExternalsObjects(); }
  179. /*
  180. *----------------------------------------------------------------------
  181. * DumpExternalsObjects --
  182. *
  183. * Dumps a COFF symbol table from an OBJ.
  184. *----------------------------------------------------------------------
  185. */
  186. void DumpExternalsObjects()
  187. {
  188. unsigned i;
  189. PSTR stringTable;
  190. std::string symbol;
  191. DWORD SectChar;
  192. /*
  193. * The string table apparently starts right after the symbol table
  194. */
  195. stringTable = (PSTR) & this->SymbolTable[this->SymbolCount];
  196. SymbolTableType* pSymbolTable = this->SymbolTable;
  197. for (i = 0; i < this->SymbolCount; i++) {
  198. if (pSymbolTable->SectionNumber > 0 &&
  199. (pSymbolTable->Type == 0x20 || pSymbolTable->Type == 0x0)) {
  200. if (pSymbolTable->StorageClass == IMAGE_SYM_CLASS_EXTERNAL) {
  201. /*
  202. * The name of the Function entry points
  203. */
  204. if (pSymbolTable->N.Name.Short != 0) {
  205. symbol = "";
  206. symbol.insert(0, (const char*)pSymbolTable->N.ShortName, 8);
  207. } else {
  208. symbol = stringTable + pSymbolTable->N.Name.Long;
  209. }
  210. // clear out any leading spaces
  211. while (isspace(symbol[0]))
  212. symbol.erase(0, 1);
  213. // if it starts with _ and has an @ then it is a __cdecl
  214. // so remove the @ stuff for the export
  215. if (symbol[0] == '_') {
  216. std::string::size_type posAt = symbol.find('@');
  217. if (posAt != std::string::npos) {
  218. symbol.erase(posAt);
  219. }
  220. }
  221. // For i386 builds we need to remove _
  222. if (this->IsI386 && symbol[0] == '_') {
  223. symbol.erase(0, 1);
  224. }
  225. // Check whether it is "Scalar deleting destructor" and "Vector
  226. // deleting destructor"
  227. // if scalarPrefix and vectorPrefix are not found then print
  228. // the symbol
  229. const char* scalarPrefix = "??_G";
  230. const char* vectorPrefix = "??_E";
  231. // The original code had a check for
  232. // symbol.find("real@") == std::string::npos)
  233. // but this disallows member functions with the name "real".
  234. if (symbol.compare(0, 4, scalarPrefix) &&
  235. symbol.compare(0, 4, vectorPrefix)) {
  236. SectChar = this->SectionHeaders[pSymbolTable->SectionNumber - 1]
  237. .Characteristics;
  238. // skip symbols containing a dot
  239. if (symbol.find('.') == std::string::npos) {
  240. if (!pSymbolTable->Type && (SectChar & IMAGE_SCN_MEM_WRITE)) {
  241. // Read only (i.e. constants) must be excluded
  242. this->DataSymbols.insert(symbol);
  243. } else {
  244. if (pSymbolTable->Type || !(SectChar & IMAGE_SCN_MEM_READ) ||
  245. (SectChar & IMAGE_SCN_MEM_EXECUTE)) {
  246. this->Symbols.insert(symbol);
  247. }
  248. }
  249. }
  250. }
  251. }
  252. }
  253. /*
  254. * Take into account any aux symbols
  255. */
  256. i += pSymbolTable->NumberOfAuxSymbols;
  257. pSymbolTable += pSymbolTable->NumberOfAuxSymbols;
  258. pSymbolTable++;
  259. }
  260. }
  261. private:
  262. std::set<std::string>& Symbols;
  263. std::set<std::string>& DataSymbols;
  264. DWORD_PTR SymbolCount;
  265. PIMAGE_SECTION_HEADER SectionHeaders;
  266. ObjectHeaderType* ObjectImageHeader;
  267. SymbolTableType* SymbolTable;
  268. bool IsI386;
  269. };
  270. bool DumpFile(const char* filename, std::set<std::string>& symbols,
  271. std::set<std::string>& dataSymbols)
  272. {
  273. HANDLE hFile;
  274. HANDLE hFileMapping;
  275. LPVOID lpFileBase;
  276. PIMAGE_DOS_HEADER dosHeader;
  277. hFile = CreateFileW(cmsys::Encoding::ToWide(filename).c_str(), GENERIC_READ,
  278. FILE_SHARE_READ, NULL, OPEN_EXISTING,
  279. FILE_ATTRIBUTE_NORMAL, 0);
  280. if (hFile == INVALID_HANDLE_VALUE) {
  281. fprintf(stderr, "Couldn't open file '%s' with CreateFile()\n", filename);
  282. return false;
  283. }
  284. hFileMapping = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
  285. if (hFileMapping == 0) {
  286. CloseHandle(hFile);
  287. fprintf(stderr, "Couldn't open file mapping with CreateFileMapping()\n");
  288. return false;
  289. }
  290. lpFileBase = MapViewOfFile(hFileMapping, FILE_MAP_READ, 0, 0, 0);
  291. if (lpFileBase == 0) {
  292. CloseHandle(hFileMapping);
  293. CloseHandle(hFile);
  294. fprintf(stderr, "Couldn't map view of file with MapViewOfFile()\n");
  295. return false;
  296. }
  297. dosHeader = (PIMAGE_DOS_HEADER)lpFileBase;
  298. if (dosHeader->e_magic == IMAGE_DOS_SIGNATURE) {
  299. fprintf(stderr, "File is an executable. I don't dump those.\n");
  300. return false;
  301. }
  302. /* Does it look like a COFF OBJ file??? */
  303. else if (((dosHeader->e_magic == IMAGE_FILE_MACHINE_I386) ||
  304. (dosHeader->e_magic == IMAGE_FILE_MACHINE_AMD64) ||
  305. (dosHeader->e_magic == IMAGE_FILE_MACHINE_ARMNT)) &&
  306. (dosHeader->e_sp == 0)) {
  307. /*
  308. * The two tests above aren't what they look like. They're
  309. * really checking for IMAGE_FILE_HEADER.Machine == i386 (0x14C)
  310. * and IMAGE_FILE_HEADER.SizeOfOptionalHeader == 0;
  311. */
  312. DumpSymbols<IMAGE_FILE_HEADER, IMAGE_SYMBOL> symbolDumper(
  313. (PIMAGE_FILE_HEADER)lpFileBase, symbols, dataSymbols,
  314. (dosHeader->e_magic == IMAGE_FILE_MACHINE_I386));
  315. symbolDumper.DumpObjFile();
  316. } else {
  317. // check for /bigobj format
  318. cmANON_OBJECT_HEADER_BIGOBJ* h = (cmANON_OBJECT_HEADER_BIGOBJ*)lpFileBase;
  319. if (h->Sig1 == 0x0 && h->Sig2 == 0xffff) {
  320. DumpSymbols<cmANON_OBJECT_HEADER_BIGOBJ, cmIMAGE_SYMBOL_EX> symbolDumper(
  321. (cmANON_OBJECT_HEADER_BIGOBJ*)lpFileBase, symbols, dataSymbols,
  322. (h->Machine == IMAGE_FILE_MACHINE_I386));
  323. symbolDumper.DumpObjFile();
  324. } else {
  325. printf("unrecognized file format in '%s'\n", filename);
  326. return false;
  327. }
  328. }
  329. UnmapViewOfFile(lpFileBase);
  330. CloseHandle(hFileMapping);
  331. CloseHandle(hFile);
  332. return true;
  333. }
  334. bool bindexplib::AddObjectFile(const char* filename)
  335. {
  336. return DumpFile(filename, this->Symbols, this->DataSymbols);
  337. }
  338. bool bindexplib::AddDefinitionFile(const char* filename)
  339. {
  340. cmsys::ifstream infile(filename);
  341. if (!infile) {
  342. fprintf(stderr, "Couldn't open definition file '%s'\n", filename);
  343. return false;
  344. }
  345. std::string str;
  346. while (std::getline(infile, str)) {
  347. // skip the LIBRAY and EXPORTS lines (if any)
  348. if ((str.compare(0, 7, "LIBRARY") == 0) ||
  349. (str.compare(0, 7, "EXPORTS") == 0)) {
  350. continue;
  351. }
  352. // remove leading tabs & spaces
  353. str.erase(0, str.find_first_not_of(" \t"));
  354. std::size_t found = str.find(" \t DATA");
  355. if (found != std::string::npos) {
  356. str.erase(found, std::string::npos);
  357. this->DataSymbols.insert(str);
  358. } else {
  359. this->Symbols.insert(str);
  360. }
  361. }
  362. infile.close();
  363. return true;
  364. }
  365. void bindexplib::WriteFile(FILE* file)
  366. {
  367. fprintf(file, "EXPORTS \n");
  368. for (std::string const& ds : this->DataSymbols) {
  369. fprintf(file, "\t%s \t DATA\n", ds.c_str());
  370. }
  371. for (std::string const& s : this->Symbols) {
  372. fprintf(file, "\t%s\n", s.c_str());
  373. }
  374. }