mirror of
https://github.com/Kitware/CMake.git
synced 2026-11-06 05:16:58 +00:00
bindexplib: Export the same symbols on every host
WINDOWS_EXPORT_ALL_SYMBOLS reads object files with two backends: on Windows hosts CMake walks the COFF symbol table itself, elsewhere (and for LLVM bitcode) it parses `nm` output. Only the walker applied the rules the MSVC ABI needs, so a DLL cross-compiled from Linux or macOS exported a different set of symbols than the same sources built on Windows. Move every rule that depends only on the symbol name into one place used by both backends, and teach the nm backend the rest: skip deleting destructors (`??_G`, `??_E`), export vftables (`??_7`) as code except on ARM64EC where they are DATA, ignore common symbols, and strip i386 decorations. The machine type these need is read from the COFF file header, once per object file. The expected output of the nm symbol-type test changes accordingly. Add a test running `cmake -E __create_def` over checked-in objects for x86_64, i386 and ARM64EC against expected .def files, so one expectation pins both backends. Fixes: #28103
This commit is contained in:
1 parent
d291927f5d
commit
a498d459a3
12 files changed
+363
-83
No files matched your search
@@ -51,6 +51,7 @@ coverage.xml.in tab-indent
|
||||
*.make tab-indent
|
||||
.hooks-config tab-indent
|
||||
|
||||
*.obj -text
|
||||
*.pfx -text
|
||||
*.png -text
|
||||
*.png.in -text
|
||||
|
||||
+179
-81
@@ -67,14 +67,16 @@
|
||||
#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"
|
||||
# include "cmsys/String.h"
|
||||
#endif
|
||||
|
||||
#include "cmsys/FStream.hxx"
|
||||
@@ -84,6 +86,133 @@
|
||||
#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
|
||||
@@ -145,13 +274,6 @@ typedef struct _cmIMAGE_SYMBOL_EX
|
||||
} 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)
|
||||
{
|
||||
@@ -266,63 +388,24 @@ public:
|
||||
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)) {
|
||||
NormalizeSymbolName(symbol, this->SymbolArch);
|
||||
if (!SymbolIsExcluded(symbol, this->SymbolArch)) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -338,19 +421,6 @@ public:
|
||||
}
|
||||
|
||||
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;
|
||||
@@ -415,6 +485,12 @@ static bool DumpFileWithNm(std::string const& nmPath,
|
||||
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)) {
|
||||
@@ -440,10 +516,22 @@ static bool DumpFileWithNm(std::string const& nmPath,
|
||||
return false;
|
||||
}
|
||||
char const sym_type = line[sym_end + 1];
|
||||
std::string const symbol = line.substr(sym_start, sym_end - sym_start);
|
||||
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 'C':
|
||||
case 'D':
|
||||
dataSymbols.insert(symbol);
|
||||
continue;
|
||||
@@ -451,11 +539,21 @@ static bool DumpFileWithNm(std::string const& nmPath,
|
||||
symbols.insert(symbol);
|
||||
continue;
|
||||
case 'R':
|
||||
if (cmHasLiteralPrefix(symbol, "??_7")) {
|
||||
dataSymbols.insert(symbol);
|
||||
// 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':
|
||||
@@ -477,7 +575,7 @@ static bool DumpFileWithNm(std::string const& nmPath,
|
||||
default:
|
||||
std::cerr << "Ignoring symbol '" << symbol
|
||||
<< "' with unrecognized type '" << sym_type
|
||||
<< "' in object '" << line.substr(0, filename_end) << "'.\n";
|
||||
<< "' in object '" << objectFile << "'.\n";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
# Run "cmake -E __create_def" on the checked-in object files and compare the
|
||||
# generated .def files with the expected ones. The object files are compiled
|
||||
# for Windows but are read on every host, so this checks that the COFF symbol
|
||||
# table backend used on Windows hosts and the nm backend used on other
|
||||
# hosts export the same symbols.
|
||||
#
|
||||
# The object files were generated from CreateDef/fixture.cxx with
|
||||
#
|
||||
# clang-cl --target=<arch>-pc-windows-msvc /c /GR- /EHs- /Os /Zl -nostdinc \
|
||||
# CreateDef/fixture.cxx /Fo:CreateDef/fixture-<arch>.obj
|
||||
#
|
||||
# once per architecture.
|
||||
|
||||
set(failed "")
|
||||
foreach(arch IN ITEMS x86_64 i386 arm64ec)
|
||||
set(objlist "${bin}/${arch}-objects.txt")
|
||||
set(def "${bin}/${arch}.def")
|
||||
file(WRITE "${objlist}" "${src}/CreateDef/fixture-${arch}.obj\n")
|
||||
file(REMOVE "${def}")
|
||||
execute_process(
|
||||
COMMAND ${cmake} -E __create_def "${def}" "${objlist}" ${nm}
|
||||
RESULT_VARIABLE result
|
||||
OUTPUT_VARIABLE output
|
||||
ERROR_VARIABLE output
|
||||
)
|
||||
if(NOT result EQUAL 0)
|
||||
string(APPEND failed
|
||||
"__create_def failed for ${arch}: ${result}\n${output}")
|
||||
continue()
|
||||
endif()
|
||||
file(READ "${def}" actual)
|
||||
file(READ "${src}/CreateDef/fixture-${arch}.def" expect)
|
||||
# Compare ignoring trailing whitespace, which __create_def emits.
|
||||
string(REGEX REPLACE "[ \t\r]+\n" "\n" actual "${actual}")
|
||||
string(REGEX REPLACE "[ \t\r]+\n" "\n" expect "${expect}")
|
||||
if(NOT actual STREQUAL expect)
|
||||
string(APPEND failed
|
||||
"Wrong exports for ${arch}.\n"
|
||||
"Expected:\n${expect}\n"
|
||||
"Actual:\n${actual}\n")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
if(failed)
|
||||
message(FATAL_ERROR "${failed}")
|
||||
endif()
|
||||
@@ -0,0 +1,13 @@
|
||||
EXPORTS
|
||||
??_7Virt@@6B@ DATA
|
||||
?bssData@@3HA DATA
|
||||
?writableData@@3HA DATA
|
||||
cWritableData DATA
|
||||
#CFunction
|
||||
??0Virt@@$$hQEAA@XZ
|
||||
??1Virt@@$$hUEAA@XZ
|
||||
?CodeFunction@@$$hYAHXZ
|
||||
?DeleteOne@@$$hYAXPEAVVirt@@@Z
|
||||
?Method@Virt@@$$hUEAAHXZ
|
||||
?UseConstData@@$$hYAPEBHXZ
|
||||
?UseLocalFunction@@$$hYAHXZ
|
||||
Binary file not shown.
@@ -0,0 +1,14 @@
|
||||
EXPORTS
|
||||
?bssData@@3HA DATA
|
||||
?writableData@@3HA DATA
|
||||
cWritableData DATA
|
||||
??0Virt@@QAE@XZ
|
||||
??1Virt@@UAE@XZ
|
||||
??_7Virt@@6B@
|
||||
?CodeFunction@@YAHXZ
|
||||
?DeleteOne@@YAXPAVVirt@@@Z
|
||||
?Method@Virt@@UAEHXZ
|
||||
?UseConstData@@YAPBHXZ
|
||||
?UseLocalFunction@@YAHXZ
|
||||
CFunction
|
||||
StdcallFunction
|
||||
Binary file not shown.
@@ -0,0 +1,13 @@
|
||||
EXPORTS
|
||||
?bssData@@3HA DATA
|
||||
?writableData@@3HA DATA
|
||||
cWritableData DATA
|
||||
??0Virt@@QEAA@XZ
|
||||
??1Virt@@UEAA@XZ
|
||||
??_7Virt@@6B@
|
||||
?CodeFunction@@YAHXZ
|
||||
?DeleteOne@@YAXPEAVVirt@@@Z
|
||||
?Method@Virt@@UEAAHXZ
|
||||
?UseConstData@@YAPEBHXZ
|
||||
?UseLocalFunction@@YAHXZ
|
||||
CFunction
|
||||
Binary file not shown.
@@ -0,0 +1,64 @@
|
||||
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
||||
file LICENSE.rst or https://cmake.org/licensing for details. */
|
||||
|
||||
/* Source of the checked-in object files used by the CreateDef test.
|
||||
See CreateDef.cmake for the commands used to regenerate them. */
|
||||
|
||||
class Virt
|
||||
{
|
||||
public:
|
||||
Virt();
|
||||
virtual ~Virt();
|
||||
virtual int Method();
|
||||
};
|
||||
Virt::Virt()
|
||||
{
|
||||
}
|
||||
Virt::~Virt()
|
||||
{
|
||||
}
|
||||
int Virt::Method()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
/* Makes the compiler emit the scalar deleting destructor "??_G". */
|
||||
void DeleteOne(Virt* v)
|
||||
{
|
||||
delete v;
|
||||
}
|
||||
|
||||
int writableData = 1;
|
||||
int bssData;
|
||||
extern int const constData;
|
||||
int const constData = 2;
|
||||
int const* UseConstData()
|
||||
{
|
||||
return &constData;
|
||||
}
|
||||
|
||||
int CodeFunction()
|
||||
{
|
||||
return 3;
|
||||
}
|
||||
static int LocalFunction()
|
||||
{
|
||||
return 4;
|
||||
}
|
||||
int UseLocalFunction()
|
||||
{
|
||||
return LocalFunction();
|
||||
}
|
||||
|
||||
/* On i386 these lose their leading underscore, and __stdcall loses its
|
||||
"@<n>" suffix. */
|
||||
extern "C" int CFunction(int a)
|
||||
{
|
||||
return a;
|
||||
}
|
||||
extern "C" int cWritableData = 5;
|
||||
#ifdef _M_IX86
|
||||
extern "C" int __stdcall StdcallFunction(int a)
|
||||
{
|
||||
return a;
|
||||
}
|
||||
#endif
|
||||
@@ -66,6 +66,38 @@ if (RunCMake_GENERATOR MATCHES "(Ninja|Makefiles|Visual Studio)")
|
||||
run_cmake_AutoExport(AutoExportShort ".o/0cb3d702")
|
||||
endif ()
|
||||
|
||||
# Verify that the two bindexplib backends, the COFF symbol table walker used
|
||||
# on Windows hosts and the nm reader used on other hosts, export the same
|
||||
# symbols from a fixed set of object files.
|
||||
function(run_create_def)
|
||||
set(nm_arg "")
|
||||
if(NOT CMAKE_HOST_WIN32)
|
||||
# Reading COFF object files on other hosts requires llvm-nm.
|
||||
find_program(LLVM_NM NAMES llvm-nm)
|
||||
if(NOT LLVM_NM)
|
||||
return()
|
||||
endif()
|
||||
# llvm-nm 16 is the first version that reads ARM64EC object files.
|
||||
execute_process(COMMAND "${LLVM_NM}" --version
|
||||
OUTPUT_VARIABLE nm_version ERROR_QUIET)
|
||||
if(NOT nm_version MATCHES "LLVM version ([0-9]+)" OR CMAKE_MATCH_1 LESS 16)
|
||||
return()
|
||||
endif()
|
||||
set(nm_arg "--nm=${LLVM_NM}")
|
||||
endif()
|
||||
set(RunCMake_TEST_BINARY_DIR "${RunCMake_BINARY_DIR}/CreateDef-build")
|
||||
file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}")
|
||||
file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}")
|
||||
run_cmake_command(CreateDef ${CMAKE_COMMAND}
|
||||
"-Dcmake=${CMAKE_COMMAND}"
|
||||
"-Dnm=${nm_arg}"
|
||||
"-Dsrc=${RunCMake_SOURCE_DIR}"
|
||||
"-Dbin=${RunCMake_TEST_BINARY_DIR}"
|
||||
-P "${RunCMake_SOURCE_DIR}/CreateDef.cmake"
|
||||
)
|
||||
endfunction()
|
||||
run_create_def()
|
||||
|
||||
function(run_AIXExportExplicit)
|
||||
set(RunCMake_TEST_BINARY_DIR "${RunCMake_BINARY_DIR}/AIXExportExplicit-build")
|
||||
run_cmake(AIXExportExplicit)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
EXPORTS
|
||||
??_7Example@@6B@ DATA
|
||||
bss DATA
|
||||
common DATA
|
||||
data DATA
|
||||
??_7Example@@6B@
|
||||
text
|
||||
Reference in new issue
Block a user