blob: 27715a5d9b4015087214ddf0bf997c43b9cac200 [file] [edit]
/* 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 <map>
#include <sstream>
#include <string>
#include <utility>
#include <vector>
#ifdef _WIN32
# include <windows.h>
# include "cmsys/Encoding.hxx"
#endif
#include "cmsys/FStream.hxx"
#include "cmGeneratedFileStream.h"
#include "cmOutputConverter.h"
#include "cmStringAlgorithms.h"
#include "cmSystemTools.h"
namespace {
enum class Arch
{
Generic,
I386,
ARM64EC,
};
/*
* The two backends below, the COFF symbol table walker used on Windows
* hosts and the nm reader used everywhere else, must select the same
* symbols, so every rule that depends only on the symbol name is shared.
*/
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;
}
/*
* The "(vector) deleting destructor" must not be exported:
* "??_G" scalar deleting dtor
* "??_E" vector deleting dtor
*/
bool SymbolIsDeletingDestructor(std::string const& symbol)
{
return cmHasLiteralPrefix(symbol, "??_G") ||
cmHasLiteralPrefix(symbol, "??_E");
}
bool SymbolIsVftable(std::string const& symbol)
{
return cmHasLiteralPrefix(symbol, "??_7");
}
bool SymbolIsArm64ECThunk(std::string const& symbol)
{
return 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;
}
/*
* Strip the decorations that must not appear in the generated .def file.
*/
void NormalizeSymbolName(std::string& symbol, Arch arch)
{
// clear out any leading spaces
symbol.erase(0, symbol.find_first_not_of(" \t\n\v\f\r"));
// if it starts with _ and has an @ then it is a __cdecl
// so remove the @ stuff for the export
if (!symbol.empty() && 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 (arch == Arch::I386 && !symbol.empty() && symbol[0] == '_') {
symbol.erase(0, 1);
}
}
/*
* Symbols that are never exported, whatever section they are defined in.
*/
bool SymbolIsExcluded(std::string const& symbol, Arch arch)
{
// The original code had a check for
// symbol.find("real@") == std::string::npos)
// but this disallows member functions with the name "real".
return symbol.empty() || SymbolIsDeletingDestructor(symbol) ||
// Skip symbols containing a dot, that are from managed code,
// or that are C++ operators incorrectly declared extern "C".
symbol.find('.') != std::string::npos || SymbolIsFromManagedCode(symbol) ||
SymbolIsOperatorExternC(symbol) ||
// Skip arm64ec thunk symbols.
(arch == Arch::ARM64EC && SymbolIsArm64ECThunk(symbol));
}
/*
* Read the machine type out of a COFF object file header so that the nm
* backend can apply the same architecture-specific rules as the COFF
* symbol table walker. Inputs that are not COFF object files, such as
* LLVM bitcode, are reported as generic.
*/
Arch GetObjectArch(std::string const& filename)
{
cmsys::ifstream fin(filename.c_str(), std::ios::in | std::ios::binary);
if (!fin) {
return Arch::Generic;
}
unsigned char header[8] = { 0 };
fin.read(reinterpret_cast<char*>(header), sizeof(header));
if (!fin) {
return Arch::Generic;
}
auto readWord = [&header](size_t i) -> unsigned int {
return static_cast<unsigned int>(header[i]) |
(static_cast<unsigned int>(header[i + 1]) << 8);
};
unsigned int machine = readWord(0);
if (machine == 0x0000 && readWord(2) == 0xffff) {
// In a /bigobj file the machine type follows Sig1, Sig2 and Version.
machine = readWord(6);
}
switch (machine) {
case 0x014c: // IMAGE_FILE_MACHINE_I386
return Arch::I386;
case 0xa641: // IMAGE_FILE_MACHINE_ARM64EC
return Arch::ARM64EC;
default:
return Arch::Generic;
}
}
} // namespace
#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;
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;
}
NormalizeSymbolName(symbol, this->SymbolArch);
if (!SymbolIsExcluded(symbol, this->SymbolArch)) {
SectChar = this->SectionHeaders[pSymbolTable->SectionNumber - 1]
.Characteristics;
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
SymbolIsVftable(symbol))) {
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
SymbolIsVftable(symbol))) {
this->Symbols.insert(symbol);
}
}
}
}
/*
* Take into account any aux symbols
*/
i += pSymbolTable->NumberOfAuxSymbols;
pSymbolTable += pSymbolTable->NumberOfAuxSymbols;
pSymbolTable++;
}
}
private:
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;
}
// The COFF symbol table walker applies rules that depend on the object
// file's architecture, but nm does not report it. Read it from the
// object files named in the output, remembering it because one nm run
// may cover many object files.
std::map<std::string, Arch> archOfObject;
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 objectFile = line.substr(0, filename_end);
std::string symbol = line.substr(sym_start, sym_end - sym_start);
auto archEntry = archOfObject.find(objectFile);
if (archEntry == archOfObject.end()) {
archEntry =
archOfObject.emplace(objectFile, GetObjectArch(objectFile)).first;
}
Arch const arch = archEntry->second;
NormalizeSymbolName(symbol, arch);
if (SymbolIsExcluded(symbol, arch)) {
continue;
}
switch (sym_type) {
case 'B':
case 'D':
dataSymbols.insert(symbol);
continue;
case 'T':
symbols.insert(symbol);
continue;
case 'R':
// Read only (i.e. constants) must be excluded, but vftables live
// there too. They fail if marked as DATA, except on ARM64EC where
// they must be marked as DATA.
if (SymbolIsVftable(symbol)) {
if (arch == Arch::ARM64EC) {
dataSymbols.insert(symbol);
} else {
symbols.insert(symbol);
}
}
continue;
case 'A':
case 'C':
// Common symbols have no section, and the COFF symbol table walker
// rejects symbols whose section number is not positive.
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 '" << objectFile << "'.\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;
}