mirror of
https://gitlab.kitware.com/cmake/cmake.git
synced 2026-09-25 04:09:36 +03:00
WINDOWS_EXPORT_ALL_SYMBOLS: Improve performance with Clang toolsets
- 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
This commit is contained in:
+201
-46
@@ -64,7 +64,9 @@
|
||||
*/
|
||||
#include "bindexplib.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef> // IWYU pragma: keep
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
@@ -77,6 +79,9 @@
|
||||
|
||||
#include "cmsys/FStream.hxx"
|
||||
|
||||
#include "cmGeneratedFileStream.h"
|
||||
#include "cmOutputConverter.h"
|
||||
#include "cmStringAlgorithms.h"
|
||||
#include "cmSystemTools.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
@@ -356,27 +361,57 @@ private:
|
||||
};
|
||||
#endif
|
||||
|
||||
static bool DumpFileWithLlvmNm(std::string const& nmPath, char const* filename,
|
||||
std::set<std::string>& symbols,
|
||||
std::set<std::string>& dataSymbols)
|
||||
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(filename);
|
||||
command.emplace_back("--print-file-name");
|
||||
command.push_back(filename);
|
||||
|
||||
// run the command
|
||||
int exit_code = 0;
|
||||
bool const commandResult =
|
||||
cmSystemTools::RunSingleCommand(command, &output, &output, &exit_code,
|
||||
nullptr, cmSystemTools::OUTPUT_NONE);
|
||||
bool const commandResult = cmSystemTools::RunSingleCommand(
|
||||
command, &output, &error, &exit_code, nullptr, cmSystemTools::OUTPUT_NONE);
|
||||
|
||||
if (!commandResult || exit_code != 0) {
|
||||
fprintf(stderr, "llvm-nm returned an error: %s\n", output.c_str());
|
||||
std::cerr << nmName << " returned an error: " << output << error << '\n';
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -386,39 +421,107 @@ static bool DumpFileWithLlvmNm(std::string const& nmPath, char const* filename,
|
||||
if (line.empty()) { // last line
|
||||
continue;
|
||||
}
|
||||
size_t sym_end = line.find(' ');
|
||||
if (sym_end == std::string::npos) {
|
||||
fprintf(stderr, "Couldn't parse llvm-nm output line: %s\n",
|
||||
line.c_str());
|
||||
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;
|
||||
}
|
||||
if (line.size() < sym_end) {
|
||||
fprintf(stderr, "Couldn't parse llvm-nm output line: %s\n",
|
||||
line.c_str());
|
||||
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];
|
||||
line.resize(sym_end);
|
||||
std::string const symbol = line.substr(sym_start, sym_end - sym_start);
|
||||
switch (sym_type) {
|
||||
case 'B':
|
||||
case 'C':
|
||||
case 'D':
|
||||
dataSymbols.insert(line);
|
||||
break;
|
||||
dataSymbols.insert(symbol);
|
||||
continue;
|
||||
case 'T':
|
||||
symbols.insert(line);
|
||||
break;
|
||||
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 DumpFile(std::string const& nmPath, char const* filename,
|
||||
std::set<std::string>& symbols,
|
||||
std::set<std::string>& dataSymbols)
|
||||
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
|
||||
return DumpFileWithLlvmNm(nmPath, filename, symbols, dataSymbols);
|
||||
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;
|
||||
@@ -428,8 +531,9 @@ static bool DumpFile(std::string const& nmPath, char const* filename,
|
||||
FILE_ATTRIBUTE_NORMAL, 0);
|
||||
|
||||
if (hFile == INVALID_HANDLE_VALUE) {
|
||||
fprintf(stderr, "Couldn't open file '%s' with CreateFile()\n", filename);
|
||||
return false;
|
||||
fprintf(stderr, "Couldn't open file '%s' with CreateFile()\n",
|
||||
filename.c_str());
|
||||
return DumpFileResult::Failed;
|
||||
}
|
||||
|
||||
hFileMapping =
|
||||
@@ -437,7 +541,7 @@ static bool DumpFile(std::string const& nmPath, char const* filename,
|
||||
if (hFileMapping == 0) {
|
||||
CloseHandle(hFile);
|
||||
fprintf(stderr, "Couldn't open file mapping with CreateFileMapping()\n");
|
||||
return false;
|
||||
return DumpFileResult::Failed;
|
||||
}
|
||||
|
||||
lpFileBase = MapViewOfFile(hFileMapping, FILE_MAP_READ, 0, 0, 0);
|
||||
@@ -445,13 +549,14 @@ static bool DumpFile(std::string const& nmPath, char const* filename,
|
||||
CloseHandle(hFileMapping);
|
||||
CloseHandle(hFile);
|
||||
fprintf(stderr, "Couldn't map view of file with MapViewOfFile()\n");
|
||||
return false;
|
||||
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");
|
||||
return false;
|
||||
result = DumpFileResult::Failed;
|
||||
} else {
|
||||
const PIMAGE_FILE_HEADER imageHeader = (PIMAGE_FILE_HEADER)lpFileBase;
|
||||
/* Does it look like a COFF OBJ file??? */
|
||||
@@ -495,33 +600,72 @@ static bool DumpFile(std::string const& nmPath, char const* filename,
|
||||
(h->Sig1 == 0x4342 && h->Sig2 == 0xDEC0) ||
|
||||
// 0x0B17C0DE - llvm bitcode BC wrapper
|
||||
(h->Sig1 == 0x0B17 && h->Sig2 == 0xC0DE)) {
|
||||
|
||||
return DumpFileWithLlvmNm(nmPath, filename, symbols, dataSymbols);
|
||||
|
||||
result = DumpFileResult::LLVMBitcodeDetected;
|
||||
} else {
|
||||
printf("unrecognized file format in '%s, %u'\n", filename,
|
||||
printf("unrecognized file format in '%s, %u'\n", filename.c_str(),
|
||||
imageHeader->Machine);
|
||||
return false;
|
||||
result = DumpFileResult::Failed;
|
||||
}
|
||||
}
|
||||
}
|
||||
UnmapViewOfFile(lpFileBase);
|
||||
CloseHandle(hFileMapping);
|
||||
CloseHandle(hFile);
|
||||
return true;
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool bindexplib::AddObjectFile(char const* filename)
|
||||
bool bindexplib::AddObjectFile(std::string const& objectFile)
|
||||
{
|
||||
return DumpFile(this->NmPath, filename, this->Symbols, this->DataSymbols);
|
||||
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::AddDefinitionFile(char const* filename)
|
||||
bool bindexplib::AddObjectFile(std::vector<std::string> const& objectFiles,
|
||||
std::string const& responseFile)
|
||||
{
|
||||
cmsys::ifstream infile(filename);
|
||||
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) {
|
||||
fprintf(stderr, "Couldn't open definition file '%s'\n", filename);
|
||||
std::cerr << "Couldn't open definition file '" << definitionFile << "'\n";
|
||||
return false;
|
||||
}
|
||||
std::string str;
|
||||
@@ -545,15 +689,26 @@ bool bindexplib::AddDefinitionFile(char const* filename)
|
||||
return true;
|
||||
}
|
||||
|
||||
void bindexplib::WriteFile(FILE* file)
|
||||
bool bindexplib::AddDefinitionFile(
|
||||
std::vector<std::string> const& definitionFiles)
|
||||
{
|
||||
fprintf(file, "EXPORTS \n");
|
||||
for (std::string const& ds : this->DataSymbols) {
|
||||
fprintf(file, "\t%s \t DATA\n", ds.c_str());
|
||||
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 (std::string const& s : this->Symbols) {
|
||||
fprintf(file, "\t%s\n", s.c_str());
|
||||
for (auto const& s : this->Symbols) {
|
||||
output << '\t' << s << '\n';
|
||||
}
|
||||
output.flush();
|
||||
return output.good();
|
||||
}
|
||||
|
||||
void bindexplib::SetNmPath(std::string const& nm)
|
||||
|
||||
+8
-5
@@ -4,17 +4,20 @@
|
||||
|
||||
#include "cmConfigure.h" // IWYU pragma: keep
|
||||
|
||||
#include <cstdio>
|
||||
#include <iosfwd>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class bindexplib
|
||||
{
|
||||
public:
|
||||
bindexplib() { NmPath = "nm"; }
|
||||
bool AddDefinitionFile(char const* filename);
|
||||
bool AddObjectFile(char const* filename);
|
||||
void WriteFile(FILE* file);
|
||||
bool AddDefinitionFile(std::string const& definitionFile);
|
||||
bool AddDefinitionFile(std::vector<std::string> const& definitionFiles);
|
||||
bool AddObjectFile(std::string const& objectFile);
|
||||
bool AddObjectFile(std::vector<std::string> const& objectFiles,
|
||||
std::string const& responseFile = "exports.def.objs.rsp");
|
||||
bool WriteFile(std::ostream& output);
|
||||
|
||||
void SetNmPath(std::string const& nm);
|
||||
|
||||
|
||||
+18
-16
@@ -1201,13 +1201,20 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string> const& args,
|
||||
std::cerr << "could not open object list file: " << args[3] << '\n';
|
||||
return 1;
|
||||
}
|
||||
std::vector<std::string> files;
|
||||
std::vector<std::string> defFiles;
|
||||
std::vector<std::string> objFiles;
|
||||
{
|
||||
std::string file;
|
||||
cmFileTime outTime;
|
||||
bool outValid = outTime.Load(args[2]);
|
||||
while (cmSystemTools::GetLineFromStream(fin, file)) {
|
||||
files.push_back(file);
|
||||
std::string const& ext =
|
||||
cmSystemTools::GetFilenameLastExtension(file);
|
||||
if (cmSystemTools::LowerCase(ext) == ".def") {
|
||||
defFiles.push_back(file);
|
||||
} else {
|
||||
objFiles.push_back(file);
|
||||
}
|
||||
if (outValid) {
|
||||
cmFileTime inTime;
|
||||
outValid = inTime.Load(file) && inTime.Older(outTime);
|
||||
@@ -1219,7 +1226,8 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string> const& args,
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
FILE* fout = cmsys::SystemTools::Fopen(args[2], "w+");
|
||||
fin.close();
|
||||
cmsys::ofstream fout(args[2].c_str());
|
||||
if (!fout) {
|
||||
std::cerr << "could not open output .def file: " << args[2] << '\n';
|
||||
return 1;
|
||||
@@ -1233,20 +1241,14 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string> const& args,
|
||||
std::cerr << "unknown argument: " << a << '\n';
|
||||
}
|
||||
}
|
||||
for (std::string const& file : files) {
|
||||
std::string const& ext = cmSystemTools::GetFilenameLastExtension(file);
|
||||
if (cmSystemTools::LowerCase(ext) == ".def") {
|
||||
if (!deffile.AddDefinitionFile(file.c_str())) {
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
if (!deffile.AddObjectFile(file.c_str())) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
if (!deffile.AddDefinitionFile(defFiles) ||
|
||||
!deffile.AddObjectFile(objFiles, cmStrCat(args[3], ".rsp"))) {
|
||||
return 1;
|
||||
}
|
||||
if (!deffile.WriteFile(fout)) {
|
||||
std::cerr << "could not write output .def file: " << args[2] << '\n';
|
||||
return 1;
|
||||
}
|
||||
deffile.WriteFile(fout);
|
||||
fclose(fout);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user