mirror of
https://gitlab.kitware.com/cmake/cmake.git
synced 2026-09-25 04:09:36 +03:00
- Separate definition files from object files while reading the generated input list. - Add batch bindexplib interfaces. - Preserve native Windows COFF parsing for ordinary objects. - Collect objects requiring nm and pass them to one nm process through a response file. On non-Windows hosts, pass every object to nm. - Request --print-file-name so single- and multi-object output use the same format. - Recognize text, data, and MSVC vftable symbols; ignore known non-exported types and warn about unknown types. - Capture nm stderr separately so diagnostics cannot corrupt stdout symbol records. Closes: #27995
718 lines
23 KiB
C++
718 lines
23 KiB
C++
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
|
file LICENSE.rst 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 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 definitions
|
|
* The DumpExternalObject function has been introduced to generate .DEF
|
|
*file
|
|
*
|
|
* Author: Valery Fine 16/09/96 (E-mail: fine@vxcern.cern.ch)
|
|
*----------------------------------------------------------------------
|
|
*/
|
|
#include "bindexplib.h"
|
|
|
|
#include <algorithm>
|
|
#include <cstddef> // IWYU pragma: keep
|
|
#include <iostream>
|
|
#include <sstream>
|
|
#include <vector>
|
|
|
|
#ifdef _WIN32
|
|
# include <windows.h>
|
|
|
|
# include "cmsys/Encoding.hxx"
|
|
# include "cmsys/String.h"
|
|
#endif
|
|
|
|
#include "cmsys/FStream.hxx"
|
|
|
|
#include "cmGeneratedFileStream.h"
|
|
#include "cmOutputConverter.h"
|
|
#include "cmStringAlgorithms.h"
|
|
#include "cmSystemTools.h"
|
|
|
|
#ifdef _WIN32
|
|
# ifndef IMAGE_FILE_MACHINE_ARM
|
|
# define IMAGE_FILE_MACHINE_ARM 0x01c0 // ARM Little-Endian
|
|
# endif
|
|
|
|
# ifndef IMAGE_FILE_MACHINE_THUMB
|
|
# define IMAGE_FILE_MACHINE_THUMB 0x01c2 // ARM Thumb/Thumb-2 Little-Endian
|
|
# endif
|
|
|
|
# ifndef IMAGE_FILE_MACHINE_ARMNT
|
|
# define IMAGE_FILE_MACHINE_ARMNT 0x01c4 // ARM Thumb-2 Little-Endian
|
|
# endif
|
|
|
|
# ifndef IMAGE_FILE_MACHINE_ARM64
|
|
# define IMAGE_FILE_MACHINE_ARM64 0xaa64 // ARM64 Little-Endian
|
|
# endif
|
|
|
|
# ifndef IMAGE_FILE_MACHINE_ARM64EC
|
|
# define IMAGE_FILE_MACHINE_ARM64EC 0xa641 // ARM64EC Little-Endian
|
|
# endif
|
|
|
|
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;
|
|
|
|
enum class Arch
|
|
{
|
|
Generic,
|
|
I386,
|
|
ARM64EC,
|
|
};
|
|
|
|
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
|
|
+ */
|
|
char const* StrNStr(char const* start, char const* find, size_t& size)
|
|
{
|
|
size_t len;
|
|
char const* hint;
|
|
|
|
if (!start || !find || !size) {
|
|
size = 0;
|
|
return 0;
|
|
}
|
|
len = strlen(find);
|
|
|
|
while ((hint = (char const*)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<std::string>& symbols,
|
|
std::set<std::string>& dataSymbols,
|
|
Arch symbolArch = Arch::Generic)
|
|
: 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->SymbolArch = symbolArch;
|
|
}
|
|
|
|
/*
|
|
*----------------------------------------------------------------------
|
|
* 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;
|
|
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.clear();
|
|
symbol.insert(0, (char const*)pSymbolTable->N.ShortName,
|
|
strnlen((char const*)pSymbolTable->N.ShortName, 8));
|
|
} else {
|
|
symbol = stringTable + pSymbolTable->N.Name.Long;
|
|
}
|
|
|
|
// clear out any leading spaces
|
|
while (cmsysString_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 i386 builds we need to remove _
|
|
if (this->SymbolArch == Arch::I386 && 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
|
|
char const* scalarPrefix = "??_G";
|
|
char const* vectorPrefix = "??_E";
|
|
char const* vftablePrefix = "??_7";
|
|
// The original code had a check for
|
|
// symbol.find("real@") == std::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, are from managed code,
|
|
// or are C++ operators incorrectly declared extern "C".
|
|
if (symbol.find('.') == std::string::npos &&
|
|
!SymbolIsFromManagedCode(symbol) &&
|
|
!SymbolIsOperatorExternC(symbol)) {
|
|
// skip arm64ec thunk symbols
|
|
if (this->SymbolArch != Arch::ARM64EC ||
|
|
(symbol.find("$ientry_thunk") == std::string::npos &&
|
|
symbol.find("$entry_thunk") == std::string::npos &&
|
|
symbol.find("$iexit_thunk") == std::string::npos &&
|
|
symbol.find("$exit_thunk") == std::string::npos)) {
|
|
if ((!pSymbolTable->Type &&
|
|
// Read only (i.e. constants) must be excluded
|
|
(SectChar & IMAGE_SCN_MEM_WRITE)) ||
|
|
(this->SymbolArch == Arch::ARM64EC &&
|
|
// vftable symbols are DATA on ARM64EC
|
|
symbol.compare(0, 4, vftablePrefix) == 0)) {
|
|
this->DataSymbols.insert(symbol);
|
|
} else if (pSymbolTable->Type ||
|
|
!(SectChar & IMAGE_SCN_MEM_READ) ||
|
|
(SectChar & IMAGE_SCN_MEM_EXECUTE) ||
|
|
(this->SymbolArch != Arch::ARM64EC &&
|
|
// vftable symbols fail if marked as DATA
|
|
symbol.compare(0, 4, vftablePrefix) == 0)) {
|
|
this->Symbols.insert(symbol);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Take into account any aux symbols
|
|
*/
|
|
i += pSymbolTable->NumberOfAuxSymbols;
|
|
pSymbolTable += pSymbolTable->NumberOfAuxSymbols;
|
|
pSymbolTable++;
|
|
}
|
|
}
|
|
|
|
private:
|
|
bool SymbolIsFromManagedCode(std::string const& symbol)
|
|
{
|
|
return symbol == "__t2m" || symbol == "__m2mep" || symbol == "__mep" ||
|
|
symbol.find("$$F") != std::string::npos ||
|
|
symbol.find("$$J") != std::string::npos;
|
|
}
|
|
|
|
bool SymbolIsOperatorExternC(std::string const& symbol)
|
|
{
|
|
return symbol.find_first_not_of("=<>+-*/%,?|~!^&[]()") ==
|
|
std::string::npos;
|
|
}
|
|
|
|
std::set<std::string>& Symbols;
|
|
std::set<std::string>& DataSymbols;
|
|
DWORD_PTR SymbolCount;
|
|
PIMAGE_SECTION_HEADER SectionHeaders;
|
|
ObjectHeaderType* ObjectImageHeader;
|
|
SymbolTableType* SymbolTable;
|
|
Arch SymbolArch;
|
|
};
|
|
#endif
|
|
|
|
static bool CreateResponseFile(std::vector<std::string> const& arguments,
|
|
std::string const& responseFile)
|
|
{
|
|
cmGeneratedFileStream responseStream;
|
|
responseStream.Open(responseFile, true, true);
|
|
if (!responseStream) {
|
|
std::cerr << "Could not open response file '" << responseFile << "'.\n";
|
|
return false;
|
|
}
|
|
|
|
int responseFlags = cmOutputConverter::Shell_Flag_IsResponse;
|
|
#if !defined(_WIN32) || defined(__CYGWIN__)
|
|
responseFlags |= cmOutputConverter::Shell_Flag_IsUnix;
|
|
#endif
|
|
for (auto const& argument : arguments) {
|
|
responseStream << cmOutputConverter::EscapeForShell(argument,
|
|
responseFlags)
|
|
<< '\n';
|
|
}
|
|
if (!responseStream.Close()) {
|
|
std::cerr << "Could not write response file '" << responseFile << "'.\n";
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool DumpFileWithNm(std::string const& nmPath,
|
|
std::string const& filename,
|
|
std::set<std::string>& symbols,
|
|
std::set<std::string>& dataSymbols)
|
|
{
|
|
std::string const nmName = cmSystemTools::GetFilenameName(nmPath);
|
|
std::string output;
|
|
std::string error;
|
|
// break up command line into a vector
|
|
std::vector<std::string> command;
|
|
command.push_back(nmPath);
|
|
command.emplace_back("--no-weak");
|
|
command.emplace_back("--defined-only");
|
|
command.emplace_back("--format=posix");
|
|
command.emplace_back("--print-file-name");
|
|
command.push_back(filename);
|
|
|
|
// run the command
|
|
int exit_code = 0;
|
|
bool const commandResult = cmSystemTools::RunSingleCommand(
|
|
command, &output, &error, &exit_code, nullptr, cmSystemTools::OUTPUT_NONE);
|
|
|
|
if (!commandResult || exit_code != 0) {
|
|
std::cerr << nmName << " returned an error: " << output << error << '\n';
|
|
return false;
|
|
}
|
|
|
|
std::istringstream ss(output);
|
|
std::string line;
|
|
while (std::getline(ss, line)) {
|
|
if (line.empty()) { // last line
|
|
continue;
|
|
}
|
|
size_t const filename_end = line.find(": ");
|
|
if (filename_end == std::string::npos) {
|
|
std::cerr << "Couldn't parse " << nmName << " output line: " << line
|
|
<< '\n';
|
|
return false;
|
|
}
|
|
size_t const sym_start = filename_end + 2;
|
|
size_t const sym_end = line.find(' ', sym_start);
|
|
if (sym_end == std::string::npos) {
|
|
std::cerr << "Couldn't parse " << nmName << " output line: " << line
|
|
<< '\n';
|
|
return false;
|
|
}
|
|
if (line.size() <= sym_end + 1) {
|
|
std::cerr << "Couldn't parse " << nmName << " output line: " << line
|
|
<< '\n';
|
|
return false;
|
|
}
|
|
char const sym_type = line[sym_end + 1];
|
|
std::string const symbol = line.substr(sym_start, sym_end - sym_start);
|
|
switch (sym_type) {
|
|
case 'B':
|
|
case 'C':
|
|
case 'D':
|
|
dataSymbols.insert(symbol);
|
|
continue;
|
|
case 'T':
|
|
symbols.insert(symbol);
|
|
continue;
|
|
case 'R':
|
|
if (cmHasLiteralPrefix(symbol, "??_7")) {
|
|
dataSymbols.insert(symbol);
|
|
}
|
|
continue;
|
|
case 'A':
|
|
case 'I':
|
|
case 'N':
|
|
case 'S':
|
|
case 'U':
|
|
case 'V':
|
|
case 'W':
|
|
case 'a':
|
|
case 'b':
|
|
case 'c':
|
|
case 'd':
|
|
case 'i':
|
|
case 'n':
|
|
case 'r':
|
|
case 's':
|
|
case 't':
|
|
case 'v':
|
|
case 'w':
|
|
continue;
|
|
default:
|
|
std::cerr << "Ignoring symbol '" << symbol
|
|
<< "' with unrecognized type '" << sym_type
|
|
<< "' in object '" << line.substr(0, filename_end) << "'.\n";
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool DumpFileWithNm(std::string const& nmPath,
|
|
std::vector<std::string> const& objectFiles,
|
|
std::string const& responseFile,
|
|
std::set<std::string>& symbols,
|
|
std::set<std::string>& dataSymbols)
|
|
{
|
|
if (!CreateResponseFile(objectFiles, responseFile)) {
|
|
return false;
|
|
}
|
|
std::string const rspFile = '@' + responseFile;
|
|
bool result = DumpFileWithNm(nmPath, rspFile, symbols, dataSymbols);
|
|
cmSystemTools::RemoveFile(responseFile);
|
|
return result;
|
|
}
|
|
|
|
enum class DumpFileResult
|
|
{
|
|
Success,
|
|
Failed,
|
|
LLVMBitcodeDetected,
|
|
NotImplemented,
|
|
};
|
|
|
|
static DumpFileResult DumpFile(std::string const& filename,
|
|
std::set<std::string>& symbols,
|
|
std::set<std::string>& dataSymbols)
|
|
{
|
|
#ifndef _WIN32
|
|
static_cast<void>(filename);
|
|
static_cast<void>(symbols);
|
|
static_cast<void>(dataSymbols);
|
|
return DumpFileResult::NotImplemented;
|
|
#else
|
|
if (filename.empty()) {
|
|
fprintf(stderr, "No object file was specified.\n");
|
|
return DumpFileResult::Failed;
|
|
}
|
|
|
|
HANDLE hFile;
|
|
HANDLE hFileMapping;
|
|
LPVOID lpFileBase;
|
|
|
|
hFile = CreateFileW(cmsys::Encoding::ToWide(filename).c_str(), GENERIC_READ,
|
|
FILE_SHARE_READ, nullptr, OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL, 0);
|
|
|
|
if (hFile == INVALID_HANDLE_VALUE) {
|
|
fprintf(stderr, "Couldn't open file '%s' with CreateFile()\n",
|
|
filename.c_str());
|
|
return DumpFileResult::Failed;
|
|
}
|
|
|
|
hFileMapping =
|
|
CreateFileMapping(hFile, nullptr, PAGE_READONLY, 0, 0, nullptr);
|
|
if (hFileMapping == 0) {
|
|
CloseHandle(hFile);
|
|
fprintf(stderr, "Couldn't open file mapping with CreateFileMapping()\n");
|
|
return DumpFileResult::Failed;
|
|
}
|
|
|
|
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 DumpFileResult::Failed;
|
|
}
|
|
|
|
DumpFileResult result = DumpFileResult::Success;
|
|
const PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)lpFileBase;
|
|
if (dosHeader->e_magic == IMAGE_DOS_SIGNATURE) {
|
|
fprintf(stderr, "File is an executable. I don't dump those.\n");
|
|
result = DumpFileResult::Failed;
|
|
} else {
|
|
const PIMAGE_FILE_HEADER imageHeader = (PIMAGE_FILE_HEADER)lpFileBase;
|
|
/* Does it look like a COFF OBJ file??? */
|
|
if (((imageHeader->Machine == IMAGE_FILE_MACHINE_I386) ||
|
|
(imageHeader->Machine == IMAGE_FILE_MACHINE_AMD64) ||
|
|
(imageHeader->Machine == IMAGE_FILE_MACHINE_ARM) ||
|
|
(imageHeader->Machine == IMAGE_FILE_MACHINE_ARMNT) ||
|
|
(imageHeader->Machine == IMAGE_FILE_MACHINE_ARM64) ||
|
|
(imageHeader->Machine == IMAGE_FILE_MACHINE_ARM64EC)) &&
|
|
(imageHeader->Characteristics == 0)) {
|
|
/*
|
|
* The tests above are checking for IMAGE_FILE_HEADER.Machine
|
|
* if it contains supported machine formats (currently ARM and x86)
|
|
* and IMAGE_FILE_HEADER.Characteristics == 0 indicating that
|
|
* this is not linked COFF OBJ file;
|
|
*/
|
|
DumpSymbols<IMAGE_FILE_HEADER, IMAGE_SYMBOL> symbolDumper(
|
|
(PIMAGE_FILE_HEADER)lpFileBase, symbols, dataSymbols,
|
|
(imageHeader->Machine == IMAGE_FILE_MACHINE_I386
|
|
? Arch::I386
|
|
: (imageHeader->Machine == IMAGE_FILE_MACHINE_ARM64EC
|
|
? Arch::ARM64EC
|
|
: Arch::Generic)));
|
|
symbolDumper.DumpObjFile();
|
|
} else {
|
|
// check for /bigobj and llvm LTO format
|
|
cmANON_OBJECT_HEADER_BIGOBJ* h =
|
|
(cmANON_OBJECT_HEADER_BIGOBJ*)lpFileBase;
|
|
if (h->Sig1 == 0x0 && h->Sig2 == 0xffff && h->Version >= 2) {
|
|
// bigobj
|
|
DumpSymbols<cmANON_OBJECT_HEADER_BIGOBJ, cmIMAGE_SYMBOL_EX>
|
|
symbolDumper(
|
|
(cmANON_OBJECT_HEADER_BIGOBJ*)lpFileBase, symbols, dataSymbols,
|
|
(h->Machine == IMAGE_FILE_MACHINE_I386
|
|
? Arch::I386
|
|
: (h->Machine == IMAGE_FILE_MACHINE_ARM64EC ? Arch::ARM64EC
|
|
: Arch::Generic)));
|
|
symbolDumper.DumpObjFile();
|
|
} else if (
|
|
// BCexCODE - llvm bitcode
|
|
(h->Sig1 == 0x4342 && h->Sig2 == 0xDEC0) ||
|
|
// 0x0B17C0DE - llvm bitcode BC wrapper
|
|
(h->Sig1 == 0x0B17 && h->Sig2 == 0xC0DE)) {
|
|
result = DumpFileResult::LLVMBitcodeDetected;
|
|
} else {
|
|
printf("unrecognized file format in '%s, %u'\n", filename.c_str(),
|
|
imageHeader->Machine);
|
|
result = DumpFileResult::Failed;
|
|
}
|
|
}
|
|
}
|
|
UnmapViewOfFile(lpFileBase);
|
|
CloseHandle(hFileMapping);
|
|
CloseHandle(hFile);
|
|
return result;
|
|
#endif
|
|
}
|
|
|
|
bool bindexplib::AddObjectFile(std::string const& objectFile)
|
|
{
|
|
auto const result = DumpFile(objectFile, this->Symbols, this->DataSymbols);
|
|
if (result == DumpFileResult::Success) {
|
|
return true;
|
|
}
|
|
if (result == DumpFileResult::Failed) {
|
|
return false;
|
|
}
|
|
if (this->NmPath.empty()) {
|
|
std::cerr << "No nm tool was specified for object file '" << objectFile
|
|
<< "'.\n";
|
|
return false;
|
|
}
|
|
return DumpFileWithNm(this->NmPath, objectFile, this->Symbols,
|
|
this->DataSymbols);
|
|
}
|
|
|
|
bool bindexplib::AddObjectFile(std::vector<std::string> const& objectFiles,
|
|
std::string const& responseFile)
|
|
{
|
|
std::vector<std::string> objectFilesTryNm;
|
|
for (auto const& objectFile : objectFiles) {
|
|
auto const result = DumpFile(objectFile, this->Symbols, this->DataSymbols);
|
|
if (result == DumpFileResult::Success) {
|
|
continue;
|
|
}
|
|
if (result == DumpFileResult::Failed) {
|
|
return false;
|
|
}
|
|
objectFilesTryNm.push_back(objectFile);
|
|
}
|
|
|
|
if (objectFilesTryNm.empty()) {
|
|
return true;
|
|
}
|
|
if (this->NmPath.empty()) {
|
|
std::cerr << "No nm tool was specified for object file '"
|
|
<< objectFilesTryNm.front() << "'.\n";
|
|
return false;
|
|
}
|
|
|
|
return DumpFileWithNm(this->NmPath, objectFilesTryNm, responseFile,
|
|
this->Symbols, this->DataSymbols);
|
|
}
|
|
|
|
bool bindexplib::AddDefinitionFile(std::string const& definitionFile)
|
|
{
|
|
cmsys::ifstream infile(definitionFile.c_str());
|
|
if (!infile) {
|
|
std::cerr << "Couldn't open definition file '" << definitionFile << "'\n";
|
|
return false;
|
|
}
|
|
std::string str;
|
|
while (std::getline(infile, str)) {
|
|
// skip the LIBRARY 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 != std::string::npos) {
|
|
str.erase(found, std::string::npos);
|
|
this->DataSymbols.insert(str);
|
|
} else {
|
|
this->Symbols.insert(str);
|
|
}
|
|
}
|
|
infile.close();
|
|
return true;
|
|
}
|
|
|
|
bool bindexplib::AddDefinitionFile(
|
|
std::vector<std::string> const& definitionFiles)
|
|
{
|
|
return std::all_of(definitionFiles.cbegin(), definitionFiles.cend(),
|
|
[this](std::string const& definitionFile) {
|
|
return this->AddDefinitionFile(definitionFile);
|
|
});
|
|
}
|
|
|
|
bool bindexplib::WriteFile(std::ostream& output)
|
|
{
|
|
output << "EXPORTS \n";
|
|
for (auto const& ds : this->DataSymbols) {
|
|
output << '\t' << ds << " \t DATA\n";
|
|
}
|
|
for (auto const& s : this->Symbols) {
|
|
output << '\t' << s << '\n';
|
|
}
|
|
output.flush();
|
|
return output.good();
|
|
}
|
|
|
|
void bindexplib::SetNmPath(std::string const& nm)
|
|
{
|
|
this->NmPath = nm;
|
|
}
|