Commit 4a425b4

Browse files
PickBasaduh95
authored andcommitted
build,tools: fix shared library cross-compile
Fixes: #52664 Signed-off-by: PickBas <sayed.kirill@gmail.com> PR-URL: #63963 Reviewed-By: Stefan Stojanovic <stefan.stojanovic@janeasystems.com>
1 parent d562716 commit 4a425b4

2 files changed

Lines changed: 109 additions & 49 deletions

File tree

β€Žnode.gypβ€Ž

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1157,6 +1157,11 @@
11571157
'sources': [
11581158
'src/res/node.rc',
11591159
],
1160+
'libraries': [
1161+
'Dbghelp.lib',
1162+
'winmm.lib',
1163+
'Ws2_32.lib',
1164+
],
11601165
}],
11611166
],
11621167
}, # node_lib_target_name
@@ -1604,6 +1609,10 @@
16041609

16051610
'defines': [ 'NODE_WANT_INTERNALS=1' ],
16061611

1612+
# node_mksnapshot statically links node_base; it must not use the
1613+
# dllimport path meant for executables that load the libnode DLL.
1614+
'defines!': [ 'BUILDING_NODE_EXTENSION' ],
1615+
16071616
'sources': [
16081617
'src/node_snapshot_stub.cc',
16091618
'tools/snapshot/node_mksnapshot.cc',
@@ -1681,13 +1690,27 @@
16811690
'sources': [
16821691
'tools/gen_node_def.cc'
16831692
],
1693+
'conditions': [
1694+
# When cross-compiling, build this tool for the host so it can
1695+
# run during the build. The MSVS generator expects it to be
1696+
# named gen_node_def_host.exe in that case.
1697+
['want_separate_host_toolset', {
1698+
'toolsets': ['host'],
1699+
}],
1700+
],
16841701
},
16851702
{
16861703
'target_name': 'generate_node_def',
16871704
'dependencies': [
1688-
'gen_node_def',
16891705
'<(node_lib_target_name)',
16901706
],
1707+
'conditions': [
1708+
['want_separate_host_toolset', {
1709+
'dependencies': ['gen_node_def#host'],
1710+
}, {
1711+
'dependencies': ['gen_node_def'],
1712+
}],
1713+
],
16911714
'type': 'none',
16921715
'actions': [
16931716
{

β€Žtools/gen_node_def.ccβ€Ž

Lines changed: 85 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
#include<Windows.h>
2-
#include<algorithm>
32
#include<cstdint>
43
#include<fstream>
54
#include<iostream>
@@ -13,9 +12,9 @@
1312
// when building Node.js as a shared library. This is conceptually
1413
// similar to the create_expfile.sh script used on AIX.
1514
//
16-
// Generating this .def file requires parsing data out of the
15+
// Generating this .def file requires parsing data out of the
1716
// PE32/PE32+ file format. Helper structs are defined in <Windows.h>
18-
// hence why this is an executable and not a script. See [2] for
17+
// hence why this is an executable and not a script. See [2] for
1918
// details on the PE format.
2019
//
2120
// [1]: https://docs.microsoft.com/en-us/cpp/build/reference/module-definition-dot-def-files
@@ -28,11 +27,16 @@ struct RelativeAddress {
2827
uintptr_t root;
2928
uintptr_t offset = 0;
3029

31-
RelativeAddress(HMODULE handle) noexcept
32-
: root(reinterpret_cast<uintptr_t>(handle)) {}
30+
explicitRelativeAddress(HMODULE handle) noexcept
31+
: RelativeAddress(handle, 0) {}
3332

33+
// LoadLibraryEx with LOAD_LIBRARY_AS_IMAGE_RESOURCE tags the returned
34+
// handle by setting one of its two lowest bits. Mask them off to recover
35+
// the actual base address of the mapping.
3436
RelativeAddress(HMODULE handle, uintptr_t offset) noexcept
35-
: root(reinterpret_cast<uintptr_t>(handle)), offset(offset) {}
37+
: root(reinterpret_cast<uintptr_t>(handle) &
38+
~static_cast<uintptr_t>(3)),
39+
offset(offset) {}
3640

3741
RelativeAddress(uintptr_t root, uintptr_t offset) noexcept
3842
: root(root), offset(offset) {}
@@ -60,15 +64,28 @@ struct RelativeAddress {
6064
}
6165
};
6266

63-
// A wrapper around a dynamically loaded Windows DLL. This steps through the
64-
// PE file structure to find the export directory and pulls out a list of
65-
// all the exported symbol names.
67+
structSymbol {
68+
std::string name;
69+
uint32_t rva;
70+
};
71+
72+
// A wrapper around a memory-mapped Windows DLL image. The DLL is mapped as
73+
// an image resource (laid out as if loaded, but never executed), so its
74+
// architecture does not need to match ours; this allows generating the
75+
// .def file for a cross-compiled DLL. This steps through the PE file
76+
// structure to find the export directory and pulls out a list of all the
77+
// exported symbols.
6678
structLibrary {
6779
HMODULE library;
6880
std::string libraryName;
69-
std::vector<std::string> exportedSymbols;
81+
std::vector<IMAGE_SECTION_HEADER> sections;
82+
std::vector<Symbol> exportedSymbols;
83+
84+
// Location of the export directory itself, used to detect forwarders.
85+
uint32_t exportDirStart;
86+
uint32_t exportDirSize;
7087

71-
Library(HMODULE library) : library(library) {
88+
explicitLibrary(HMODULE library) : library(library) {
7289
auto libnode = RelativeAddress(library);
7390

7491
// At relative offset 0x3C is a 32 bit offset to the COFF signature, 4 bytes
@@ -83,11 +100,21 @@ struct Library {
83100
auto optionalHeaderPtr = coffHeaderPtr.AtOffset(sizeof(IMAGE_FILE_HEADER));
84101
auto optionalHeader = optionalHeaderPtr.AsPtrTo<IMAGE_OPTIONAL_HEADER>();
85102

103+
// The section table starts right after the optional header.
104+
auto sectionTablePtr =
105+
optionalHeaderPtr.AtOffset(coffHeader->SizeOfOptionalHeader);
106+
constIMAGE_SECTION_HEADER* firstSection =
107+
sectionTablePtr.AsPtrTo<IMAGE_SECTION_HEADER>();
108+
sections.assign(firstSection, firstSection + coffHeader->NumberOfSections);
109+
86110
auto exportDirectory =
87-
(optionalHeader->Magic == 0x20b) ? optionalHeaderPtr.AsPtrTo<IMAGE_OPTIONAL_HEADER64>()
88-
->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]
89-
: optionalHeaderPtr.AsPtrTo<IMAGE_OPTIONAL_HEADER32>()
90-
->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
111+
(optionalHeader->Magic == 0x20b)
112+
? optionalHeaderPtr.AsPtrTo<IMAGE_OPTIONAL_HEADER64>()
113+
->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]
114+
: optionalHeaderPtr.AsPtrTo<IMAGE_OPTIONAL_HEADER32>()
115+
->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
116+
exportDirStart = exportDirectory.VirtualAddress;
117+
exportDirSize = exportDirectory.Size;
91118

92119
auto exportTable = libnode.AtOffset(exportDirectory.VirtualAddress)
93120
.AsPtrTo<IMAGE_EXPORT_DIRECTORY>();
@@ -99,6 +126,11 @@ struct Library {
99126

100127
constuint32_t* functionNameTable =
101128
libnode.AtOffset(exportTable->AddressOfNames).AsPtrTo<uint32_t>();
129+
constuint32_t* functionLocations =
130+
libnode.AtOffset(exportTable->AddressOfFunctions).AsPtrTo<uint32_t>();
131+
constuint16_t* functionOrdinals =
132+
libnode.AtOffset(exportTable->AddressOfNameOrdinals)
133+
.AsPtrTo<uint16_t>();
102134

103135
// Given an RVA, parse it as a std::string. The resulting string is empty
104136
// if the symbol does not have a name (i.e. it is ordinal only).
@@ -107,32 +139,33 @@ struct Library {
107139
if (namePtr == nullptr) return {};
108140
return {namePtr};
109141
};
110-
std::transform(functionNameTable,
111-
functionNameTable + exportTable->NumberOfNames,
112-
std::back_inserter(exportedSymbols),
113-
nameRvaToName);
142+
for (uint32_t i = 0; i < exportTable->NumberOfNames; ++i) {
143+
exportedSymbols.push_back({nameRvaToName(functionNameTable[i]),
144+
functionLocations[functionOrdinals[i]]});
145+
}
114146
}
115147

116148
~Library() { FreeLibrary(library); }
117-
};
118149

119-
boolIsPageExecutable(void* address) {
120-
MEMORY_BASIC_INFORMATION memoryInformation;
121-
size_t rc = VirtualQuery(
122-
address, &memoryInformation, sizeof(MEMORY_BASIC_INFORMATION));
150+
boolIsRvaExecutable(uint32_t rva) const {
151+
for (constauto& s : sections) {
152+
if (rva >= s.VirtualAddress &&
153+
rva < s.VirtualAddress + s.Misc.VirtualSize) {
154+
return (s.Characteristics & IMAGE_SCN_MEM_EXECUTE) != 0;
155+
}
156+
}
157+
returntrue;
158+
}
123159

124-
if (rc != 0 && memoryInformation.Protect != 0) {
125-
return memoryInformation.Protect == PAGE_EXECUTE ||
126-
memoryInformation.Protect == PAGE_EXECUTE_READ ||
127-
memoryInformation.Protect == PAGE_EXECUTE_READWRITE ||
128-
memoryInformation.Protect == PAGE_EXECUTE_WRITECOPY;
160+
boolIsForwarderRva(uint32_t rva) const {
161+
return rva >= exportDirStart && rva < exportDirStart + exportDirSize;
129162
}
130-
returnfalse;
131-
}
163+
};
132164

133165
Library LoadLibraryOrExit(constchar* dllPath) {
134-
auto library = LoadLibrary(dllPath);
135-
if (library != nullptr) return library;
166+
auto library =
167+
LoadLibraryEx(dllPath, nullptr, LOAD_LIBRARY_AS_IMAGE_RESOURCE);
168+
if (library != nullptr) returnLibrary(library);
136169

137170
auto error = GetLastError();
138171
std::cerr << "ERROR: Failed to load " << dllPath << std::endl;
@@ -163,31 +196,35 @@ int main(int argc, char** argv) {
163196
auto defFile = std::ofstream(argv[2]);
164197
defFile << "EXPORTS" << std::endl;
165198

166-
for (conststd::string& functionName : libnode.exportedSymbols) {
199+
for (constSymbol& symbol : libnode.exportedSymbols) {
167200
// If a symbol doesn't have a name then it has been exported as an
168201
// ordinal only. We assume that only named symbols are exported.
169-
if (functionName.empty()) continue;
170-
171-
// Every name in the exported symbols table should be resolvable
172-
// to an address because we have actually loaded the library into
173-
// our address space.
174-
auto address = GetProcAddress(libnode.library, functionName.c_str());
175-
if (address == nullptr) {
176-
std::cerr << "WARNING: " << functionName
202+
if (symbol.name.empty()) continue;
203+
204+
if (symbol.rva == 0) {
205+
std::cerr << "WARNING: " << symbol.name
177206
<< " appears in export table but is not a valid symbol"
178207
<< std::endl;
179208
continue;
180209
}
181210

182-
defFile << "" << functionName << " = " << libnode.libraryName << "."
183-
<< functionName;
184-
211+
defFile << "" << symbol.name << " = " << libnode.libraryName << "."
212+
<< symbol.name;
213+
185214
// Nothing distinguishes exported global data from exported functions
186215
// with C linkage. If we do not specify the DATA keyword for such symbols
187216
// then consumers of the .def file will get a linker error. This manifests
188-
// as nodedbg_ symbols not being found. We assert that if the symbol is in
189-
// an executable page in this process then it is a function, not data.
190-
if (!IsPageExecutable(address)) {
217+
// as nodedbg_ symbols not being found. We assert that if the symbol's
218+
// RVA falls in a section with the IMAGE_SCN_MEM_EXECUTE characteristic
219+
// then it is a function, not data.
220+
//
221+
// A forwarder export is the exception: its RVA points back inside the
222+
// export directory, at a redirect string like "NTDLL.RtlAllocateHeap",
223+
// rather than at code or data. The export directory lives in a
224+
// non-executable section, but forwarders resolve to functions, so they
225+
// must not be marked DATA.
226+
if (!libnode.IsForwarderRva(symbol.rva) &&
227+
!libnode.IsRvaExecutable(symbol.rva)) {
191228
defFile << " DATA";
192229
}
193230
defFile << std::endl;

0 commit comments

Comments
Β (0)
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Commit 4a425b4

Browse files
PickBasaduh95
authored andcommitted
build,tools: fix shared library cross-compile
Fixes: #52664 Signed-off-by: PickBas <sayed.kirill@gmail.com> PR-URL: #63963 Reviewed-By: Stefan Stojanovic <stefan.stojanovic@janeasystems.com>
1 parent d562716 commit 4a425b4

2 files changed

Lines changed: 109 additions & 49 deletions

File tree

β€Žnode.gypβ€Ž

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1157,6 +1157,11 @@
11571157
'sources': [
11581158
'src/res/node.rc',
11591159
],
1160+
'libraries': [
1161+
'Dbghelp.lib',
1162+
'winmm.lib',
1163+
'Ws2_32.lib',
1164+
],
11601165
}],
11611166
],
11621167
}, # node_lib_target_name
@@ -1604,6 +1609,10 @@
16041609

16051610
'defines': [ 'NODE_WANT_INTERNALS=1' ],
16061611

1612+
# node_mksnapshot statically links node_base; it must not use the
1613+
# dllimport path meant for executables that load the libnode DLL.
1614+
'defines!': [ 'BUILDING_NODE_EXTENSION' ],
1615+
16071616
'sources': [
16081617
'src/node_snapshot_stub.cc',
16091618
'tools/snapshot/node_mksnapshot.cc',
@@ -1681,13 +1690,27 @@
16811690
'sources': [
16821691
'tools/gen_node_def.cc'
16831692
],
1693+
'conditions': [
1694+
# When cross-compiling, build this tool for the host so it can
1695+
# run during the build. The MSVS generator expects it to be
1696+
# named gen_node_def_host.exe in that case.
1697+
['want_separate_host_toolset', {
1698+
'toolsets': ['host'],
1699+
}],
1700+
],
16841701
},
16851702
{
16861703
'target_name': 'generate_node_def',
16871704
'dependencies': [
1688-
'gen_node_def',
16891705
'<(node_lib_target_name)',
16901706
],
1707+
'conditions': [
1708+
['want_separate_host_toolset', {
1709+
'dependencies': ['gen_node_def#host'],
1710+
}, {
1711+
'dependencies': ['gen_node_def'],
1712+
}],
1713+
],
16911714
'type': 'none',
16921715
'actions': [
16931716
{

β€Žtools/gen_node_def.ccβ€Ž

Lines changed: 85 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
#include<Windows.h>
2-
#include<algorithm>
32
#include<cstdint>
43
#include<fstream>
54
#include<iostream>
@@ -13,9 +12,9 @@
1312
// when building Node.js as a shared library. This is conceptually
1413
// similar to the create_expfile.sh script used on AIX.
1514
//
16-
// Generating this .def file requires parsing data out of the
15+
// Generating this .def file requires parsing data out of the
1716
// PE32/PE32+ file format. Helper structs are defined in <Windows.h>
18-
// hence why this is an executable and not a script. See [2] for
17+
// hence why this is an executable and not a script. See [2] for
1918
// details on the PE format.
2019
//
2120
// [1]: https://docs.microsoft.com/en-us/cpp/build/reference/module-definition-dot-def-files
@@ -28,11 +27,16 @@ struct RelativeAddress {
2827
uintptr_t root;
2928
uintptr_t offset = 0;
3029

31-
RelativeAddress(HMODULE handle) noexcept
32-
: root(reinterpret_cast<uintptr_t>(handle)) {}
30+
explicitRelativeAddress(HMODULE handle) noexcept
31+
: RelativeAddress(handle, 0) {}
3332

33+
// LoadLibraryEx with LOAD_LIBRARY_AS_IMAGE_RESOURCE tags the returned
34+
// handle by setting one of its two lowest bits. Mask them off to recover
35+
// the actual base address of the mapping.
3436
RelativeAddress(HMODULE handle, uintptr_t offset) noexcept
35-
: root(reinterpret_cast<uintptr_t>(handle)), offset(offset) {}
37+
: root(reinterpret_cast<uintptr_t>(handle) &
38+
~static_cast<uintptr_t>(3)),
39+
offset(offset) {}
3640

3741
RelativeAddress(uintptr_t root, uintptr_t offset) noexcept
3842
: root(root), offset(offset) {}
@@ -60,15 +64,28 @@ struct RelativeAddress {
6064
}
6165
};
6266

63-
// A wrapper around a dynamically loaded Windows DLL. This steps through the
64-
// PE file structure to find the export directory and pulls out a list of
65-
// all the exported symbol names.
67+
structSymbol {
68+
std::string name;
69+
uint32_t rva;
70+
};
71+
72+
// A wrapper around a memory-mapped Windows DLL image. The DLL is mapped as
73+
// an image resource (laid out as if loaded, but never executed), so its
74+
// architecture does not need to match ours; this allows generating the
75+
// .def file for a cross-compiled DLL. This steps through the PE file
76+
// structure to find the export directory and pulls out a list of all the
77+
// exported symbols.
6678
structLibrary {
6779
HMODULE library;
6880
std::string libraryName;
69-
std::vector<std::string> exportedSymbols;
81+
std::vector<IMAGE_SECTION_HEADER> sections;
82+
std::vector<Symbol> exportedSymbols;
83+
84+
// Location of the export directory itself, used to detect forwarders.
85+
uint32_t exportDirStart;
86+
uint32_t exportDirSize;
7087

71-
Library(HMODULE library) : library(library) {
88+
explicitLibrary(HMODULE library) : library(library) {
7289
auto libnode = RelativeAddress(library);
7390

7491
// At relative offset 0x3C is a 32 bit offset to the COFF signature, 4 bytes
@@ -83,11 +100,21 @@ struct Library {
83100
auto optionalHeaderPtr = coffHeaderPtr.AtOffset(sizeof(IMAGE_FILE_HEADER));
84101
auto optionalHeader = optionalHeaderPtr.AsPtrTo<IMAGE_OPTIONAL_HEADER>();
85102

103+
// The section table starts right after the optional header.
104+
auto sectionTablePtr =
105+
optionalHeaderPtr.AtOffset(coffHeader->SizeOfOptionalHeader);
106+
constIMAGE_SECTION_HEADER* firstSection =
107+
sectionTablePtr.AsPtrTo<IMAGE_SECTION_HEADER>();
108+
sections.assign(firstSection, firstSection + coffHeader->NumberOfSections);
109+
86110
auto exportDirectory =
87-
(optionalHeader->Magic == 0x20b) ? optionalHeaderPtr.AsPtrTo<IMAGE_OPTIONAL_HEADER64>()
88-
->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]
89-
: optionalHeaderPtr.AsPtrTo<IMAGE_OPTIONAL_HEADER32>()
90-
->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
111+
(optionalHeader->Magic == 0x20b)
112+
? optionalHeaderPtr.AsPtrTo<IMAGE_OPTIONAL_HEADER64>()
113+
->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]
114+
: optionalHeaderPtr.AsPtrTo<IMAGE_OPTIONAL_HEADER32>()
115+
->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
116+
exportDirStart = exportDirectory.VirtualAddress;
117+
exportDirSize = exportDirectory.Size;
91118

92119
auto exportTable = libnode.AtOffset(exportDirectory.VirtualAddress)
93120
.AsPtrTo<IMAGE_EXPORT_DIRECTORY>();
@@ -99,6 +126,11 @@ struct Library {
99126

100127
constuint32_t* functionNameTable =
101128
libnode.AtOffset(exportTable->AddressOfNames).AsPtrTo<uint32_t>();
129+
constuint32_t* functionLocations =
130+
libnode.AtOffset(exportTable->AddressOfFunctions).AsPtrTo<uint32_t>();
131+
constuint16_t* functionOrdinals =
132+
libnode.AtOffset(exportTable->AddressOfNameOrdinals)
133+
.AsPtrTo<uint16_t>();
102134

103135
// Given an RVA, parse it as a std::string. The resulting string is empty
104136
// if the symbol does not have a name (i.e. it is ordinal only).
@@ -107,32 +139,33 @@ struct Library {
107139
if (namePtr == nullptr) return {};
108140
return {namePtr};
109141
};
110-
std::transform(functionNameTable,
111-
functionNameTable + exportTable->NumberOfNames,
112-
std::back_inserter(exportedSymbols),
113-
nameRvaToName);
142+
for (uint32_t i = 0; i < exportTable->NumberOfNames; ++i) {
143+
exportedSymbols.push_back({nameRvaToName(functionNameTable[i]),
144+
functionLocations[functionOrdinals[i]]});
145+
}
114146
}
115147

116148
~Library() { FreeLibrary(library); }
117-
};
118149

119-
boolIsPageExecutable(void* address) {
120-
MEMORY_BASIC_INFORMATION memoryInformation;
121-
size_t rc = VirtualQuery(
122-
address, &memoryInformation, sizeof(MEMORY_BASIC_INFORMATION));
150+
boolIsRvaExecutable(uint32_t rva) const {
151+
for (constauto& s : sections) {
152+
if (rva >= s.VirtualAddress &&
153+
rva < s.VirtualAddress + s.Misc.VirtualSize) {
154+
return (s.Characteristics & IMAGE_SCN_MEM_EXECUTE) != 0;
155+
}
156+
}
157+
returntrue;
158+
}
123159

124-
if (rc != 0 && memoryInformation.Protect != 0) {
125-
return memoryInformation.Protect == PAGE_EXECUTE ||
126-
memoryInformation.Protect == PAGE_EXECUTE_READ ||
127-
memoryInformation.Protect == PAGE_EXECUTE_READWRITE ||
128-
memoryInformation.Protect == PAGE_EXECUTE_WRITECOPY;
160+
boolIsForwarderRva(uint32_t rva) const {
161+
return rva >= exportDirStart && rva < exportDirStart + exportDirSize;
129162
}
130-
returnfalse;
131-
}
163+
};
132164

133165
Library LoadLibraryOrExit(constchar* dllPath) {
134-
auto library = LoadLibrary(dllPath);
135-
if (library != nullptr) return library;
166+
auto library =
167+
LoadLibraryEx(dllPath, nullptr, LOAD_LIBRARY_AS_IMAGE_RESOURCE);
168+
if (library != nullptr) returnLibrary(library);
136169

137170
auto error = GetLastError();
138171
std::cerr << "ERROR: Failed to load " << dllPath << std::endl;
@@ -163,31 +196,35 @@ int main(int argc, char** argv) {
163196
auto defFile = std::ofstream(argv[2]);
164197
defFile << "EXPORTS" << std::endl;
165198

166-
for (conststd::string& functionName : libnode.exportedSymbols) {
199+
for (constSymbol& symbol : libnode.exportedSymbols) {
167200
// If a symbol doesn't have a name then it has been exported as an
168201
// ordinal only. We assume that only named symbols are exported.
169-
if (functionName.empty()) continue;
170-
171-
// Every name in the exported symbols table should be resolvable
172-
// to an address because we have actually loaded the library into
173-
// our address space.
174-
auto address = GetProcAddress(libnode.library, functionName.c_str());
175-
if (address == nullptr) {
176-
std::cerr << "WARNING: " << functionName
202+
if (symbol.name.empty()) continue;
203+
204+
if (symbol.rva == 0) {
205+
std::cerr << "WARNING: " << symbol.name
177206
<< " appears in export table but is not a valid symbol"
178207
<< std::endl;
179208
continue;
180209
}
181210

182-
defFile << "" << functionName << " = " << libnode.libraryName << "."
183-
<< functionName;
184-
211+
defFile << "" << symbol.name << " = " << libnode.libraryName << "."
212+
<< symbol.name;
213+
185214
// Nothing distinguishes exported global data from exported functions
186215
// with C linkage. If we do not specify the DATA keyword for such symbols
187216
// then consumers of the .def file will get a linker error. This manifests
188-
// as nodedbg_ symbols not being found. We assert that if the symbol is in
189-
// an executable page in this process then it is a function, not data.
190-
if (!IsPageExecutable(address)) {
217+
// as nodedbg_ symbols not being found. We assert that if the symbol's
218+
// RVA falls in a section with the IMAGE_SCN_MEM_EXECUTE characteristic
219+
// then it is a function, not data.
220+
//
221+
// A forwarder export is the exception: its RVA points back inside the
222+
// export directory, at a redirect string like "NTDLL.RtlAllocateHeap",
223+
// rather than at code or data. The export directory lives in a
224+
// non-executable section, but forwarders resolve to functions, so they
225+
// must not be marked DATA.
226+
if (!libnode.IsForwarderRva(symbol.rva) &&
227+
!libnode.IsRvaExecutable(symbol.rva)) {
191228
defFile << " DATA";
192229
}
193230
defFile << std::endl;

0 commit comments

Comments
Β (0)
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Commit 4a425b4

Browse files
PickBasaduh95
authored andcommitted
build,tools: fix shared library cross-compile
Fixes: #52664 Signed-off-by: PickBas <sayed.kirill@gmail.com> PR-URL: #63963 Reviewed-By: Stefan Stojanovic <stefan.stojanovic@janeasystems.com>
1 parent d562716 commit 4a425b4

2 files changed

Lines changed: 109 additions & 49 deletions

File tree

β€Žnode.gypβ€Ž

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1157,6 +1157,11 @@
11571157
'sources': [
11581158
'src/res/node.rc',
11591159
],
1160+
'libraries': [
1161+
'Dbghelp.lib',
1162+
'winmm.lib',
1163+
'Ws2_32.lib',
1164+
],
11601165
}],
11611166
],
11621167
}, # node_lib_target_name
@@ -1604,6 +1609,10 @@
16041609

16051610
'defines': [ 'NODE_WANT_INTERNALS=1' ],
16061611

1612+
# node_mksnapshot statically links node_base; it must not use the
1613+
# dllimport path meant for executables that load the libnode DLL.
1614+
'defines!': [ 'BUILDING_NODE_EXTENSION' ],
1615+
16071616
'sources': [
16081617
'src/node_snapshot_stub.cc',
16091618
'tools/snapshot/node_mksnapshot.cc',
@@ -1681,13 +1690,27 @@
16811690
'sources': [
16821691
'tools/gen_node_def.cc'
16831692
],
1693+
'conditions': [
1694+
# When cross-compiling, build this tool for the host so it can
1695+
# run during the build. The MSVS generator expects it to be
1696+
# named gen_node_def_host.exe in that case.
1697+
['want_separate_host_toolset', {
1698+
'toolsets': ['host'],
1699+
}],
1700+
],
16841701
},
16851702
{
16861703
'target_name': 'generate_node_def',
16871704
'dependencies': [
1688-
'gen_node_def',
16891705
'<(node_lib_target_name)',
16901706
],
1707+
'conditions': [
1708+
['want_separate_host_toolset', {
1709+
'dependencies': ['gen_node_def#host'],
1710+
}, {
1711+
'dependencies': ['gen_node_def'],
1712+
}],
1713+
],
16911714
'type': 'none',
16921715
'actions': [
16931716
{

β€Žtools/gen_node_def.ccβ€Ž

Lines changed: 85 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
#include<Windows.h>
2-
#include<algorithm>
32
#include<cstdint>
43
#include<fstream>
54
#include<iostream>
@@ -13,9 +12,9 @@
1312
// when building Node.js as a shared library. This is conceptually
1413
// similar to the create_expfile.sh script used on AIX.
1514
//
16-
// Generating this .def file requires parsing data out of the
15+
// Generating this .def file requires parsing data out of the
1716
// PE32/PE32+ file format. Helper structs are defined in <Windows.h>
18-
// hence why this is an executable and not a script. See [2] for
17+
// hence why this is an executable and not a script. See [2] for
1918
// details on the PE format.
2019
//
2120
// [1]: https://docs.microsoft.com/en-us/cpp/build/reference/module-definition-dot-def-files
@@ -28,11 +27,16 @@ struct RelativeAddress {
2827
uintptr_t root;
2928
uintptr_t offset = 0;
3029

31-
RelativeAddress(HMODULE handle) noexcept
32-
: root(reinterpret_cast<uintptr_t>(handle)) {}
30+
explicitRelativeAddress(HMODULE handle) noexcept
31+
: RelativeAddress(handle, 0) {}
3332

33+
// LoadLibraryEx with LOAD_LIBRARY_AS_IMAGE_RESOURCE tags the returned
34+
// handle by setting one of its two lowest bits. Mask them off to recover
35+
// the actual base address of the mapping.
3436
RelativeAddress(HMODULE handle, uintptr_t offset) noexcept
35-
: root(reinterpret_cast<uintptr_t>(handle)), offset(offset) {}
37+
: root(reinterpret_cast<uintptr_t>(handle) &
38+
~static_cast<uintptr_t>(3)),
39+
offset(offset) {}
3640

3741
RelativeAddress(uintptr_t root, uintptr_t offset) noexcept
3842
: root(root), offset(offset) {}
@@ -60,15 +64,28 @@ struct RelativeAddress {
6064
}
6165
};
6266

63-
// A wrapper around a dynamically loaded Windows DLL. This steps through the
64-
// PE file structure to find the export directory and pulls out a list of
65-
// all the exported symbol names.
67+
structSymbol {
68+
std::string name;
69+
uint32_t rva;
70+
};
71+
72+
// A wrapper around a memory-mapped Windows DLL image. The DLL is mapped as
73+
// an image resource (laid out as if loaded, but never executed), so its
74+
// architecture does not need to match ours; this allows generating the
75+
// .def file for a cross-compiled DLL. This steps through the PE file
76+
// structure to find the export directory and pulls out a list of all the
77+
// exported symbols.
6678
structLibrary {
6779
HMODULE library;
6880
std::string libraryName;
69-
std::vector<std::string> exportedSymbols;
81+
std::vector<IMAGE_SECTION_HEADER> sections;
82+
std::vector<Symbol> exportedSymbols;
83+
84+
// Location of the export directory itself, used to detect forwarders.
85+
uint32_t exportDirStart;
86+
uint32_t exportDirSize;
7087

71-
Library(HMODULE library) : library(library) {
88+
explicitLibrary(HMODULE library) : library(library) {
7289
auto libnode = RelativeAddress(library);
7390

7491
// At relative offset 0x3C is a 32 bit offset to the COFF signature, 4 bytes
@@ -83,11 +100,21 @@ struct Library {
83100
auto optionalHeaderPtr = coffHeaderPtr.AtOffset(sizeof(IMAGE_FILE_HEADER));
84101
auto optionalHeader = optionalHeaderPtr.AsPtrTo<IMAGE_OPTIONAL_HEADER>();
85102

103+
// The section table starts right after the optional header.
104+
auto sectionTablePtr =
105+
optionalHeaderPtr.AtOffset(coffHeader->SizeOfOptionalHeader);
106+
constIMAGE_SECTION_HEADER* firstSection =
107+
sectionTablePtr.AsPtrTo<IMAGE_SECTION_HEADER>();
108+
sections.assign(firstSection, firstSection + coffHeader->NumberOfSections);
109+
86110
auto exportDirectory =
87-
(optionalHeader->Magic == 0x20b) ? optionalHeaderPtr.AsPtrTo<IMAGE_OPTIONAL_HEADER64>()
88-
->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]
89-
: optionalHeaderPtr.AsPtrTo<IMAGE_OPTIONAL_HEADER32>()
90-
->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
111+
(optionalHeader->Magic == 0x20b)
112+
? optionalHeaderPtr.AsPtrTo<IMAGE_OPTIONAL_HEADER64>()
113+
->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]
114+
: optionalHeaderPtr.AsPtrTo<IMAGE_OPTIONAL_HEADER32>()
115+
->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
116+
exportDirStart = exportDirectory.VirtualAddress;
117+
exportDirSize = exportDirectory.Size;
91118

92119
auto exportTable = libnode.AtOffset(exportDirectory.VirtualAddress)
93120
.AsPtrTo<IMAGE_EXPORT_DIRECTORY>();
@@ -99,6 +126,11 @@ struct Library {
99126

100127
constuint32_t* functionNameTable =
101128
libnode.AtOffset(exportTable->AddressOfNames).AsPtrTo<uint32_t>();
129+
constuint32_t* functionLocations =
130+
libnode.AtOffset(exportTable->AddressOfFunctions).AsPtrTo<uint32_t>();
131+
constuint16_t* functionOrdinals =
132+
libnode.AtOffset(exportTable->AddressOfNameOrdinals)
133+
.AsPtrTo<uint16_t>();
102134

103135
// Given an RVA, parse it as a std::string. The resulting string is empty
104136
// if the symbol does not have a name (i.e. it is ordinal only).
@@ -107,32 +139,33 @@ struct Library {
107139
if (namePtr == nullptr) return {};
108140
return {namePtr};
109141
};
110-
std::transform(functionNameTable,
111-
functionNameTable + exportTable->NumberOfNames,
112-
std::back_inserter(exportedSymbols),
113-
nameRvaToName);
142+
for (uint32_t i = 0; i < exportTable->NumberOfNames; ++i) {
143+
exportedSymbols.push_back({nameRvaToName(functionNameTable[i]),
144+
functionLocations[functionOrdinals[i]]});
145+
}
114146
}
115147

116148
~Library() { FreeLibrary(library); }
117-
};
118149

119-
boolIsPageExecutable(void* address) {
120-
MEMORY_BASIC_INFORMATION memoryInformation;
121-
size_t rc = VirtualQuery(
122-
address, &memoryInformation, sizeof(MEMORY_BASIC_INFORMATION));
150+
boolIsRvaExecutable(uint32_t rva) const {
151+
for (constauto& s : sections) {
152+
if (rva >= s.VirtualAddress &&
153+
rva < s.VirtualAddress + s.Misc.VirtualSize) {
154+
return (s.Characteristics & IMAGE_SCN_MEM_EXECUTE) != 0;
155+
}
156+
}
157+
returntrue;
158+
}
123159

124-
if (rc != 0 && memoryInformation.Protect != 0) {
125-
return memoryInformation.Protect == PAGE_EXECUTE ||
126-
memoryInformation.Protect == PAGE_EXECUTE_READ ||
127-
memoryInformation.Protect == PAGE_EXECUTE_READWRITE ||
128-
memoryInformation.Protect == PAGE_EXECUTE_WRITECOPY;
160+
boolIsForwarderRva(uint32_t rva) const {
161+
return rva >= exportDirStart && rva < exportDirStart + exportDirSize;
129162
}
130-
returnfalse;
131-
}
163+
};
132164

133165
Library LoadLibraryOrExit(constchar* dllPath) {
134-
auto library = LoadLibrary(dllPath);
135-
if (library != nullptr) return library;
166+
auto library =
167+
LoadLibraryEx(dllPath, nullptr, LOAD_LIBRARY_AS_IMAGE_RESOURCE);
168+
if (library != nullptr) returnLibrary(library);
136169

137170
auto error = GetLastError();
138171
std::cerr << "ERROR: Failed to load " << dllPath << std::endl;
@@ -163,31 +196,35 @@ int main(int argc, char** argv) {
163196
auto defFile = std::ofstream(argv[2]);
164197
defFile << "EXPORTS" << std::endl;
165198

166-
for (conststd::string& functionName : libnode.exportedSymbols) {
199+
for (constSymbol& symbol : libnode.exportedSymbols) {
167200
// If a symbol doesn't have a name then it has been exported as an
168201
// ordinal only. We assume that only named symbols are exported.
169-
if (functionName.empty()) continue;
170-
171-
// Every name in the exported symbols table should be resolvable
172-
// to an address because we have actually loaded the library into
173-
// our address space.
174-
auto address = GetProcAddress(libnode.library, functionName.c_str());
175-
if (address == nullptr) {
176-
std::cerr << "WARNING: " << functionName
202+
if (symbol.name.empty()) continue;
203+
204+
if (symbol.rva == 0) {
205+
std::cerr << "WARNING: " << symbol.name
177206
<< " appears in export table but is not a valid symbol"
178207
<< std::endl;
179208
continue;
180209
}
181210

182-
defFile << "" << functionName << " = " << libnode.libraryName << "."
183-
<< functionName;
184-
211+
defFile << "" << symbol.name << " = " << libnode.libraryName << "."
212+
<< symbol.name;
213+
185214
// Nothing distinguishes exported global data from exported functions
186215
// with C linkage. If we do not specify the DATA keyword for such symbols
187216
// then consumers of the .def file will get a linker error. This manifests
188-
// as nodedbg_ symbols not being found. We assert that if the symbol is in
189-
// an executable page in this process then it is a function, not data.
190-
if (!IsPageExecutable(address)) {
217+
// as nodedbg_ symbols not being found. We assert that if the symbol's
218+
// RVA falls in a section with the IMAGE_SCN_MEM_EXECUTE characteristic
219+
// then it is a function, not data.
220+
//
221+
// A forwarder export is the exception: its RVA points back inside the
222+
// export directory, at a redirect string like "NTDLL.RtlAllocateHeap",
223+
// rather than at code or data. The export directory lives in a
224+
// non-executable section, but forwarders resolve to functions, so they
225+
// must not be marked DATA.
226+
if (!libnode.IsForwarderRva(symbol.rva) &&
227+
!libnode.IsRvaExecutable(symbol.rva)) {
191228
defFile << " DATA";
192229
}
193230
defFile << std::endl;

0 commit comments

Comments
Β (0)
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Commit 4a425b4

Browse files
PickBasaduh95
authored andcommitted
build,tools: fix shared library cross-compile
Fixes: #52664 Signed-off-by: PickBas <sayed.kirill@gmail.com> PR-URL: #63963 Reviewed-By: Stefan Stojanovic <stefan.stojanovic@janeasystems.com>
1 parent d562716 commit 4a425b4

2 files changed

Lines changed: 109 additions & 49 deletions

File tree

β€Žnode.gypβ€Ž

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1157,6 +1157,11 @@
11571157
'sources': [
11581158
'src/res/node.rc',
11591159
],
1160+
'libraries': [
1161+
'Dbghelp.lib',
1162+
'winmm.lib',
1163+
'Ws2_32.lib',
1164+
],
11601165
}],
11611166
],
11621167
}, # node_lib_target_name
@@ -1604,6 +1609,10 @@
16041609

16051610
'defines': [ 'NODE_WANT_INTERNALS=1' ],
16061611

1612+
# node_mksnapshot statically links node_base; it must not use the
1613+
# dllimport path meant for executables that load the libnode DLL.
1614+
'defines!': [ 'BUILDING_NODE_EXTENSION' ],
1615+
16071616
'sources': [
16081617
'src/node_snapshot_stub.cc',
16091618
'tools/snapshot/node_mksnapshot.cc',
@@ -1681,13 +1690,27 @@
16811690
'sources': [
16821691
'tools/gen_node_def.cc'
16831692
],
1693+
'conditions': [
1694+
# When cross-compiling, build this tool for the host so it can
1695+
# run during the build. The MSVS generator expects it to be
1696+
# named gen_node_def_host.exe in that case.
1697+
['want_separate_host_toolset', {
1698+
'toolsets': ['host'],
1699+
}],
1700+
],
16841701
},
16851702
{
16861703
'target_name': 'generate_node_def',
16871704
'dependencies': [
1688-
'gen_node_def',
16891705
'<(node_lib_target_name)',
16901706
],
1707+
'conditions': [
1708+
['want_separate_host_toolset', {
1709+
'dependencies': ['gen_node_def#host'],
1710+
}, {
1711+
'dependencies': ['gen_node_def'],
1712+
}],
1713+
],
16911714
'type': 'none',
16921715
'actions': [
16931716
{

β€Žtools/gen_node_def.ccβ€Ž

Lines changed: 85 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
#include<Windows.h>
2-
#include<algorithm>
32
#include<cstdint>
43
#include<fstream>
54
#include<iostream>
@@ -13,9 +12,9 @@
1312
// when building Node.js as a shared library. This is conceptually
1413
// similar to the create_expfile.sh script used on AIX.
1514
//
16-
// Generating this .def file requires parsing data out of the
15+
// Generating this .def file requires parsing data out of the
1716
// PE32/PE32+ file format. Helper structs are defined in <Windows.h>
18-
// hence why this is an executable and not a script. See [2] for
17+
// hence why this is an executable and not a script. See [2] for
1918
// details on the PE format.
2019
//
2120
// [1]: https://docs.microsoft.com/en-us/cpp/build/reference/module-definition-dot-def-files
@@ -28,11 +27,16 @@ struct RelativeAddress {
2827
uintptr_t root;
2928
uintptr_t offset = 0;
3029

31-
RelativeAddress(HMODULE handle) noexcept
32-
: root(reinterpret_cast<uintptr_t>(handle)) {}
30+
explicitRelativeAddress(HMODULE handle) noexcept
31+
: RelativeAddress(handle, 0) {}
3332

33+
// LoadLibraryEx with LOAD_LIBRARY_AS_IMAGE_RESOURCE tags the returned
34+
// handle by setting one of its two lowest bits. Mask them off to recover
35+
// the actual base address of the mapping.
3436
RelativeAddress(HMODULE handle, uintptr_t offset) noexcept
35-
: root(reinterpret_cast<uintptr_t>(handle)), offset(offset) {}
37+
: root(reinterpret_cast<uintptr_t>(handle) &
38+
~static_cast<uintptr_t>(3)),
39+
offset(offset) {}
3640

3741
RelativeAddress(uintptr_t root, uintptr_t offset) noexcept
3842
: root(root), offset(offset) {}
@@ -60,15 +64,28 @@ struct RelativeAddress {
6064
}
6165
};
6266

63-
// A wrapper around a dynamically loaded Windows DLL. This steps through the
64-
// PE file structure to find the export directory and pulls out a list of
65-
// all the exported symbol names.
67+
structSymbol {
68+
std::string name;
69+
uint32_t rva;
70+
};
71+
72+
// A wrapper around a memory-mapped Windows DLL image. The DLL is mapped as
73+
// an image resource (laid out as if loaded, but never executed), so its
74+
// architecture does not need to match ours; this allows generating the
75+
// .def file for a cross-compiled DLL. This steps through the PE file
76+
// structure to find the export directory and pulls out a list of all the
77+
// exported symbols.
6678
structLibrary {
6779
HMODULE library;
6880
std::string libraryName;
69-
std::vector<std::string> exportedSymbols;
81+
std::vector<IMAGE_SECTION_HEADER> sections;
82+
std::vector<Symbol> exportedSymbols;
83+
84+
// Location of the export directory itself, used to detect forwarders.
85+
uint32_t exportDirStart;
86+
uint32_t exportDirSize;
7087

71-
Library(HMODULE library) : library(library) {
88+
explicitLibrary(HMODULE library) : library(library) {
7289
auto libnode = RelativeAddress(library);
7390

7491
// At relative offset 0x3C is a 32 bit offset to the COFF signature, 4 bytes
@@ -83,11 +100,21 @@ struct Library {
83100
auto optionalHeaderPtr = coffHeaderPtr.AtOffset(sizeof(IMAGE_FILE_HEADER));
84101
auto optionalHeader = optionalHeaderPtr.AsPtrTo<IMAGE_OPTIONAL_HEADER>();
85102

103+
// The section table starts right after the optional header.
104+
auto sectionTablePtr =
105+
optionalHeaderPtr.AtOffset(coffHeader->SizeOfOptionalHeader);
106+
constIMAGE_SECTION_HEADER* firstSection =
107+
sectionTablePtr.AsPtrTo<IMAGE_SECTION_HEADER>();
108+
sections.assign(firstSection, firstSection + coffHeader->NumberOfSections);
109+
86110
auto exportDirectory =
87-
(optionalHeader->Magic == 0x20b) ? optionalHeaderPtr.AsPtrTo<IMAGE_OPTIONAL_HEADER64>()
88-
->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]
89-
: optionalHeaderPtr.AsPtrTo<IMAGE_OPTIONAL_HEADER32>()
90-
->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
111+
(optionalHeader->Magic == 0x20b)
112+
? optionalHeaderPtr.AsPtrTo<IMAGE_OPTIONAL_HEADER64>()
113+
->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]
114+
: optionalHeaderPtr.AsPtrTo<IMAGE_OPTIONAL_HEADER32>()
115+
->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
116+
exportDirStart = exportDirectory.VirtualAddress;
117+
exportDirSize = exportDirectory.Size;
91118

92119
auto exportTable = libnode.AtOffset(exportDirectory.VirtualAddress)
93120
.AsPtrTo<IMAGE_EXPORT_DIRECTORY>();
@@ -99,6 +126,11 @@ struct Library {
99126

100127
constuint32_t* functionNameTable =
101128
libnode.AtOffset(exportTable->AddressOfNames).AsPtrTo<uint32_t>();
129+
constuint32_t* functionLocations =
130+
libnode.AtOffset(exportTable->AddressOfFunctions).AsPtrTo<uint32_t>();
131+
constuint16_t* functionOrdinals =
132+
libnode.AtOffset(exportTable->AddressOfNameOrdinals)
133+
.AsPtrTo<uint16_t>();
102134

103135
// Given an RVA, parse it as a std::string. The resulting string is empty
104136
// if the symbol does not have a name (i.e. it is ordinal only).
@@ -107,32 +139,33 @@ struct Library {
107139
if (namePtr == nullptr) return {};
108140
return {namePtr};
109141
};
110-
std::transform(functionNameTable,
111-
functionNameTable + exportTable->NumberOfNames,
112-
std::back_inserter(exportedSymbols),
113-
nameRvaToName);
142+
for (uint32_t i = 0; i < exportTable->NumberOfNames; ++i) {
143+
exportedSymbols.push_back({nameRvaToName(functionNameTable[i]),
144+
functionLocations[functionOrdinals[i]]});
145+
}
114146
}
115147

116148
~Library() { FreeLibrary(library); }
117-
};
118149

119-
boolIsPageExecutable(void* address) {
120-
MEMORY_BASIC_INFORMATION memoryInformation;
121-
size_t rc = VirtualQuery(
122-
address, &memoryInformation, sizeof(MEMORY_BASIC_INFORMATION));
150+
boolIsRvaExecutable(uint32_t rva) const {
151+
for (constauto& s : sections) {
152+
if (rva >= s.VirtualAddress &&
153+
rva < s.VirtualAddress + s.Misc.VirtualSize) {
154+
return (s.Characteristics & IMAGE_SCN_MEM_EXECUTE) != 0;
155+
}
156+
}
157+
returntrue;
158+
}
123159

124-
if (rc != 0 && memoryInformation.Protect != 0) {
125-
return memoryInformation.Protect == PAGE_EXECUTE ||
126-
memoryInformation.Protect == PAGE_EXECUTE_READ ||
127-
memoryInformation.Protect == PAGE_EXECUTE_READWRITE ||
128-
memoryInformation.Protect == PAGE_EXECUTE_WRITECOPY;
160+
boolIsForwarderRva(uint32_t rva) const {
161+
return rva >= exportDirStart && rva < exportDirStart + exportDirSize;
129162
}
130-
returnfalse;
131-
}
163+
};
132164

133165
Library LoadLibraryOrExit(constchar* dllPath) {
134-
auto library = LoadLibrary(dllPath);
135-
if (library != nullptr) return library;
166+
auto library =
167+
LoadLibraryEx(dllPath, nullptr, LOAD_LIBRARY_AS_IMAGE_RESOURCE);
168+
if (library != nullptr) returnLibrary(library);
136169

137170
auto error = GetLastError();
138171
std::cerr << "ERROR: Failed to load " << dllPath << std::endl;
@@ -163,31 +196,35 @@ int main(int argc, char** argv) {
163196
auto defFile = std::ofstream(argv[2]);
164197
defFile << "EXPORTS" << std::endl;
165198

166-
for (conststd::string& functionName : libnode.exportedSymbols) {
199+
for (constSymbol& symbol : libnode.exportedSymbols) {
167200
// If a symbol doesn't have a name then it has been exported as an
168201
// ordinal only. We assume that only named symbols are exported.
169-
if (functionName.empty()) continue;
170-
171-
// Every name in the exported symbols table should be resolvable
172-
// to an address because we have actually loaded the library into
173-
// our address space.
174-
auto address = GetProcAddress(libnode.library, functionName.c_str());
175-
if (address == nullptr) {
176-
std::cerr << "WARNING: " << functionName
202+
if (symbol.name.empty()) continue;
203+
204+
if (symbol.rva == 0) {
205+
std::cerr << "WARNING: " << symbol.name
177206
<< " appears in export table but is not a valid symbol"
178207
<< std::endl;
179208
continue;
180209
}
181210

182-
defFile << "" << functionName << " = " << libnode.libraryName << "."
183-
<< functionName;
184-
211+
defFile << "" << symbol.name << " = " << libnode.libraryName << "."
212+
<< symbol.name;
213+
185214
// Nothing distinguishes exported global data from exported functions
186215
// with C linkage. If we do not specify the DATA keyword for such symbols
187216
// then consumers of the .def file will get a linker error. This manifests
188-
// as nodedbg_ symbols not being found. We assert that if the symbol is in
189-
// an executable page in this process then it is a function, not data.
190-
if (!IsPageExecutable(address)) {
217+
// as nodedbg_ symbols not being found. We assert that if the symbol's
218+
// RVA falls in a section with the IMAGE_SCN_MEM_EXECUTE characteristic
219+
// then it is a function, not data.
220+
//
221+
// A forwarder export is the exception: its RVA points back inside the
222+
// export directory, at a redirect string like "NTDLL.RtlAllocateHeap",
223+
// rather than at code or data. The export directory lives in a
224+
// non-executable section, but forwarders resolve to functions, so they
225+
// must not be marked DATA.
226+
if (!libnode.IsForwarderRva(symbol.rva) &&
227+
!libnode.IsRvaExecutable(symbol.rva)) {
191228
defFile << " DATA";
192229
}
193230
defFile << std::endl;

0 commit comments

Comments
Β (0)
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Commit 4a425b4

Browse files
PickBasaduh95
authored andcommitted
build,tools: fix shared library cross-compile
Fixes: #52664 Signed-off-by: PickBas <sayed.kirill@gmail.com> PR-URL: #63963 Reviewed-By: Stefan Stojanovic <stefan.stojanovic@janeasystems.com>
1 parent d562716 commit 4a425b4

2 files changed

Lines changed: 109 additions & 49 deletions

File tree

β€Žnode.gypβ€Ž

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1157,6 +1157,11 @@
11571157
'sources': [
11581158
'src/res/node.rc',
11591159
],
1160+
'libraries': [
1161+
'Dbghelp.lib',
1162+
'winmm.lib',
1163+
'Ws2_32.lib',
1164+
],
11601165
}],
11611166
],
11621167
}, # node_lib_target_name
@@ -1604,6 +1609,10 @@
16041609

16051610
'defines': [ 'NODE_WANT_INTERNALS=1' ],
16061611

1612+
# node_mksnapshot statically links node_base; it must not use the
1613+
# dllimport path meant for executables that load the libnode DLL.
1614+
'defines!': [ 'BUILDING_NODE_EXTENSION' ],
1615+
16071616
'sources': [
16081617
'src/node_snapshot_stub.cc',
16091618
'tools/snapshot/node_mksnapshot.cc',
@@ -1681,13 +1690,27 @@
16811690
'sources': [
16821691
'tools/gen_node_def.cc'
16831692
],
1693+
'conditions': [
1694+
# When cross-compiling, build this tool for the host so it can
1695+
# run during the build. The MSVS generator expects it to be
1696+
# named gen_node_def_host.exe in that case.
1697+
['want_separate_host_toolset', {
1698+
'toolsets': ['host'],
1699+
}],
1700+
],
16841701
},
16851702
{
16861703
'target_name': 'generate_node_def',
16871704
'dependencies': [
1688-
'gen_node_def',
16891705
'<(node_lib_target_name)',
16901706
],
1707+
'conditions': [
1708+
['want_separate_host_toolset', {
1709+
'dependencies': ['gen_node_def#host'],
1710+
}, {
1711+
'dependencies': ['gen_node_def'],
1712+
}],
1713+
],
16911714
'type': 'none',
16921715
'actions': [
16931716
{

β€Žtools/gen_node_def.ccβ€Ž

Lines changed: 85 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
#include<Windows.h>
2-
#include<algorithm>
32
#include<cstdint>
43
#include<fstream>
54
#include<iostream>
@@ -13,9 +12,9 @@
1312
// when building Node.js as a shared library. This is conceptually
1413
// similar to the create_expfile.sh script used on AIX.
1514
//
16-
// Generating this .def file requires parsing data out of the
15+
// Generating this .def file requires parsing data out of the
1716
// PE32/PE32+ file format. Helper structs are defined in <Windows.h>
18-
// hence why this is an executable and not a script. See [2] for
17+
// hence why this is an executable and not a script. See [2] for
1918
// details on the PE format.
2019
//
2120
// [1]: https://docs.microsoft.com/en-us/cpp/build/reference/module-definition-dot-def-files
@@ -28,11 +27,16 @@ struct RelativeAddress {
2827
uintptr_t root;
2928
uintptr_t offset = 0;
3029

31-
RelativeAddress(HMODULE handle) noexcept
32-
: root(reinterpret_cast<uintptr_t>(handle)) {}
30+
explicitRelativeAddress(HMODULE handle) noexcept
31+
: RelativeAddress(handle, 0) {}
3332

33+
// LoadLibraryEx with LOAD_LIBRARY_AS_IMAGE_RESOURCE tags the returned
34+
// handle by setting one of its two lowest bits. Mask them off to recover
35+
// the actual base address of the mapping.
3436
RelativeAddress(HMODULE handle, uintptr_t offset) noexcept
35-
: root(reinterpret_cast<uintptr_t>(handle)), offset(offset) {}
37+
: root(reinterpret_cast<uintptr_t>(handle) &
38+
~static_cast<uintptr_t>(3)),
39+
offset(offset) {}
3640

3741
RelativeAddress(uintptr_t root, uintptr_t offset) noexcept
3842
: root(root), offset(offset) {}
@@ -60,15 +64,28 @@ struct RelativeAddress {
6064
}
6165
};
6266

63-
// A wrapper around a dynamically loaded Windows DLL. This steps through the
64-
// PE file structure to find the export directory and pulls out a list of
65-
// all the exported symbol names.
67+
structSymbol {
68+
std::string name;
69+
uint32_t rva;
70+
};
71+
72+
// A wrapper around a memory-mapped Windows DLL image. The DLL is mapped as
73+
// an image resource (laid out as if loaded, but never executed), so its
74+
// architecture does not need to match ours; this allows generating the
75+
// .def file for a cross-compiled DLL. This steps through the PE file
76+
// structure to find the export directory and pulls out a list of all the
77+
// exported symbols.
6678
structLibrary {
6779
HMODULE library;
6880
std::string libraryName;
69-
std::vector<std::string> exportedSymbols;
81+
std::vector<IMAGE_SECTION_HEADER> sections;
82+
std::vector<Symbol> exportedSymbols;
83+
84+
// Location of the export directory itself, used to detect forwarders.
85+
uint32_t exportDirStart;
86+
uint32_t exportDirSize;
7087

71-
Library(HMODULE library) : library(library) {
88+
explicitLibrary(HMODULE library) : library(library) {
7289
auto libnode = RelativeAddress(library);
7390

7491
// At relative offset 0x3C is a 32 bit offset to the COFF signature, 4 bytes
@@ -83,11 +100,21 @@ struct Library {
83100
auto optionalHeaderPtr = coffHeaderPtr.AtOffset(sizeof(IMAGE_FILE_HEADER));
84101
auto optionalHeader = optionalHeaderPtr.AsPtrTo<IMAGE_OPTIONAL_HEADER>();
85102

103+
// The section table starts right after the optional header.
104+
auto sectionTablePtr =
105+
optionalHeaderPtr.AtOffset(coffHeader->SizeOfOptionalHeader);
106+
constIMAGE_SECTION_HEADER* firstSection =
107+
sectionTablePtr.AsPtrTo<IMAGE_SECTION_HEADER>();
108+
sections.assign(firstSection, firstSection + coffHeader->NumberOfSections);
109+
86110
auto exportDirectory =
87-
(optionalHeader->Magic == 0x20b) ? optionalHeaderPtr.AsPtrTo<IMAGE_OPTIONAL_HEADER64>()
88-
->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]
89-
: optionalHeaderPtr.AsPtrTo<IMAGE_OPTIONAL_HEADER32>()
90-
->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
111+
(optionalHeader->Magic == 0x20b)
112+
? optionalHeaderPtr.AsPtrTo<IMAGE_OPTIONAL_HEADER64>()
113+
->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]
114+
: optionalHeaderPtr.AsPtrTo<IMAGE_OPTIONAL_HEADER32>()
115+
->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
116+
exportDirStart = exportDirectory.VirtualAddress;
117+
exportDirSize = exportDirectory.Size;
91118

92119
auto exportTable = libnode.AtOffset(exportDirectory.VirtualAddress)
93120
.AsPtrTo<IMAGE_EXPORT_DIRECTORY>();
@@ -99,6 +126,11 @@ struct Library {
99126

100127
constuint32_t* functionNameTable =
101128
libnode.AtOffset(exportTable->AddressOfNames).AsPtrTo<uint32_t>();
129+
constuint32_t* functionLocations =
130+
libnode.AtOffset(exportTable->AddressOfFunctions).AsPtrTo<uint32_t>();
131+
constuint16_t* functionOrdinals =
132+
libnode.AtOffset(exportTable->AddressOfNameOrdinals)
133+
.AsPtrTo<uint16_t>();
102134

103135
// Given an RVA, parse it as a std::string. The resulting string is empty
104136
// if the symbol does not have a name (i.e. it is ordinal only).
@@ -107,32 +139,33 @@ struct Library {
107139
if (namePtr == nullptr) return {};
108140
return {namePtr};
109141
};
110-
std::transform(functionNameTable,
111-
functionNameTable + exportTable->NumberOfNames,
112-
std::back_inserter(exportedSymbols),
113-
nameRvaToName);
142+
for (uint32_t i = 0; i < exportTable->NumberOfNames; ++i) {
143+
exportedSymbols.push_back({nameRvaToName(functionNameTable[i]),
144+
functionLocations[functionOrdinals[i]]});
145+
}
114146
}
115147

116148
~Library() { FreeLibrary(library); }
117-
};
118149

119-
boolIsPageExecutable(void* address) {
120-
MEMORY_BASIC_INFORMATION memoryInformation;
121-
size_t rc = VirtualQuery(
122-
address, &memoryInformation, sizeof(MEMORY_BASIC_INFORMATION));
150+
boolIsRvaExecutable(uint32_t rva) const {
151+
for (constauto& s : sections) {
152+
if (rva >= s.VirtualAddress &&
153+
rva < s.VirtualAddress + s.Misc.VirtualSize) {
154+
return (s.Characteristics & IMAGE_SCN_MEM_EXECUTE) != 0;
155+
}
156+
}
157+
returntrue;
158+
}
123159

124-
if (rc != 0 && memoryInformation.Protect != 0) {
125-
return memoryInformation.Protect == PAGE_EXECUTE ||
126-
memoryInformation.Protect == PAGE_EXECUTE_READ ||
127-
memoryInformation.Protect == PAGE_EXECUTE_READWRITE ||
128-
memoryInformation.Protect == PAGE_EXECUTE_WRITECOPY;
160+
boolIsForwarderRva(uint32_t rva) const {
161+
return rva >= exportDirStart && rva < exportDirStart + exportDirSize;
129162
}
130-
returnfalse;
131-
}
163+
};
132164

133165
Library LoadLibraryOrExit(constchar* dllPath) {
134-
auto library = LoadLibrary(dllPath);
135-
if (library != nullptr) return library;
166+
auto library =
167+
LoadLibraryEx(dllPath, nullptr, LOAD_LIBRARY_AS_IMAGE_RESOURCE);
168+
if (library != nullptr) returnLibrary(library);
136169

137170
auto error = GetLastError();
138171
std::cerr << "ERROR: Failed to load " << dllPath << std::endl;
@@ -163,31 +196,35 @@ int main(int argc, char** argv) {
163196
auto defFile = std::ofstream(argv[2]);
164197
defFile << "EXPORTS" << std::endl;
165198

166-
for (conststd::string& functionName : libnode.exportedSymbols) {
199+
for (constSymbol& symbol : libnode.exportedSymbols) {
167200
// If a symbol doesn't have a name then it has been exported as an
168201
// ordinal only. We assume that only named symbols are exported.
169-
if (functionName.empty()) continue;
170-
171-
// Every name in the exported symbols table should be resolvable
172-
// to an address because we have actually loaded the library into
173-
// our address space.
174-
auto address = GetProcAddress(libnode.library, functionName.c_str());
175-
if (address == nullptr) {
176-
std::cerr << "WARNING: " << functionName
202+
if (symbol.name.empty()) continue;
203+
204+
if (symbol.rva == 0) {
205+
std::cerr << "WARNING: " << symbol.name
177206
<< " appears in export table but is not a valid symbol"
178207
<< std::endl;
179208
continue;
180209
}
181210

182-
defFile << "" << functionName << " = " << libnode.libraryName << "."
183-
<< functionName;
184-
211+
defFile << "" << symbol.name << " = " << libnode.libraryName << "."
212+
<< symbol.name;
213+
185214
// Nothing distinguishes exported global data from exported functions
186215
// with C linkage. If we do not specify the DATA keyword for such symbols
187216
// then consumers of the .def file will get a linker error. This manifests
188-
// as nodedbg_ symbols not being found. We assert that if the symbol is in
189-
// an executable page in this process then it is a function, not data.
190-
if (!IsPageExecutable(address)) {
217+
// as nodedbg_ symbols not being found. We assert that if the symbol's
218+
// RVA falls in a section with the IMAGE_SCN_MEM_EXECUTE characteristic
219+
// then it is a function, not data.
220+
//
221+
// A forwarder export is the exception: its RVA points back inside the
222+
// export directory, at a redirect string like "NTDLL.RtlAllocateHeap",
223+
// rather than at code or data. The export directory lives in a
224+
// non-executable section, but forwarders resolve to functions, so they
225+
// must not be marked DATA.
226+
if (!libnode.IsForwarderRva(symbol.rva) &&
227+
!libnode.IsRvaExecutable(symbol.rva)) {
191228
defFile << " DATA";
192229
}
193230
defFile << std::endl;

0 commit comments

Comments
Β (0)
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Commit 4a425b4

Browse files
PickBasaduh95
authored andcommitted
build,tools: fix shared library cross-compile
Fixes: #52664 Signed-off-by: PickBas <sayed.kirill@gmail.com> PR-URL: #63963 Reviewed-By: Stefan Stojanovic <stefan.stojanovic@janeasystems.com>
1 parent d562716 commit 4a425b4

2 files changed

Lines changed: 109 additions & 49 deletions

File tree

β€Žnode.gypβ€Ž

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1157,6 +1157,11 @@
11571157
'sources': [
11581158
'src/res/node.rc',
11591159
],
1160+
'libraries': [
1161+
'Dbghelp.lib',
1162+
'winmm.lib',
1163+
'Ws2_32.lib',
1164+
],
11601165
}],
11611166
],
11621167
}, # node_lib_target_name
@@ -1604,6 +1609,10 @@
16041609

16051610
'defines': [ 'NODE_WANT_INTERNALS=1' ],
16061611

1612+
# node_mksnapshot statically links node_base; it must not use the
1613+
# dllimport path meant for executables that load the libnode DLL.
1614+
'defines!': [ 'BUILDING_NODE_EXTENSION' ],
1615+
16071616
'sources': [
16081617
'src/node_snapshot_stub.cc',
16091618
'tools/snapshot/node_mksnapshot.cc',
@@ -1681,13 +1690,27 @@
16811690
'sources': [
16821691
'tools/gen_node_def.cc'
16831692
],
1693+
'conditions': [
1694+
# When cross-compiling, build this tool for the host so it can
1695+
# run during the build. The MSVS generator expects it to be
1696+
# named gen_node_def_host.exe in that case.
1697+
['want_separate_host_toolset', {
1698+
'toolsets': ['host'],
1699+
}],
1700+
],
16841701
},
16851702
{
16861703
'target_name': 'generate_node_def',
16871704
'dependencies': [
1688-
'gen_node_def',
16891705
'<(node_lib_target_name)',
16901706
],
1707+
'conditions': [
1708+
['want_separate_host_toolset', {
1709+
'dependencies': ['gen_node_def#host'],
1710+
}, {
1711+
'dependencies': ['gen_node_def'],
1712+
}],
1713+
],
16911714
'type': 'none',
16921715
'actions': [
16931716
{

β€Žtools/gen_node_def.ccβ€Ž

Lines changed: 85 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
#include<Windows.h>
2-
#include<algorithm>
32
#include<cstdint>
43
#include<fstream>
54
#include<iostream>
@@ -13,9 +12,9 @@
1312
// when building Node.js as a shared library. This is conceptually
1413
// similar to the create_expfile.sh script used on AIX.
1514
//
16-
// Generating this .def file requires parsing data out of the
15+
// Generating this .def file requires parsing data out of the
1716
// PE32/PE32+ file format. Helper structs are defined in <Windows.h>
18-
// hence why this is an executable and not a script. See [2] for
17+
// hence why this is an executable and not a script. See [2] for
1918
// details on the PE format.
2019
//
2120
// [1]: https://docs.microsoft.com/en-us/cpp/build/reference/module-definition-dot-def-files
@@ -28,11 +27,16 @@ struct RelativeAddress {
2827
uintptr_t root;
2928
uintptr_t offset = 0;
3029

31-
RelativeAddress(HMODULE handle) noexcept
32-
: root(reinterpret_cast<uintptr_t>(handle)) {}
30+
explicitRelativeAddress(HMODULE handle) noexcept
31+
: RelativeAddress(handle, 0) {}
3332

33+
// LoadLibraryEx with LOAD_LIBRARY_AS_IMAGE_RESOURCE tags the returned
34+
// handle by setting one of its two lowest bits. Mask them off to recover
35+
// the actual base address of the mapping.
3436
RelativeAddress(HMODULE handle, uintptr_t offset) noexcept
35-
: root(reinterpret_cast<uintptr_t>(handle)), offset(offset) {}
37+
: root(reinterpret_cast<uintptr_t>(handle) &
38+
~static_cast<uintptr_t>(3)),
39+
offset(offset) {}
3640

3741
RelativeAddress(uintptr_t root, uintptr_t offset) noexcept
3842
: root(root), offset(offset) {}
@@ -60,15 +64,28 @@ struct RelativeAddress {
6064
}
6165
};
6266

63-
// A wrapper around a dynamically loaded Windows DLL. This steps through the
64-
// PE file structure to find the export directory and pulls out a list of
65-
// all the exported symbol names.
67+
structSymbol {
68+
std::string name;
69+
uint32_t rva;
70+
};
71+
72+
// A wrapper around a memory-mapped Windows DLL image. The DLL is mapped as
73+
// an image resource (laid out as if loaded, but never executed), so its
74+
// architecture does not need to match ours; this allows generating the
75+
// .def file for a cross-compiled DLL. This steps through the PE file
76+
// structure to find the export directory and pulls out a list of all the
77+
// exported symbols.
6678
structLibrary {
6779
HMODULE library;
6880
std::string libraryName;
69-
std::vector<std::string> exportedSymbols;
81+
std::vector<IMAGE_SECTION_HEADER> sections;
82+
std::vector<Symbol> exportedSymbols;
83+
84+
// Location of the export directory itself, used to detect forwarders.
85+
uint32_t exportDirStart;
86+
uint32_t exportDirSize;
7087

71-
Library(HMODULE library) : library(library) {
88+
explicitLibrary(HMODULE library) : library(library) {
7289
auto libnode = RelativeAddress(library);
7390

7491
// At relative offset 0x3C is a 32 bit offset to the COFF signature, 4 bytes
@@ -83,11 +100,21 @@ struct Library {
83100
auto optionalHeaderPtr = coffHeaderPtr.AtOffset(sizeof(IMAGE_FILE_HEADER));
84101
auto optionalHeader = optionalHeaderPtr.AsPtrTo<IMAGE_OPTIONAL_HEADER>();
85102

103+
// The section table starts right after the optional header.
104+
auto sectionTablePtr =
105+
optionalHeaderPtr.AtOffset(coffHeader->SizeOfOptionalHeader);
106+
constIMAGE_SECTION_HEADER* firstSection =
107+
sectionTablePtr.AsPtrTo<IMAGE_SECTION_HEADER>();
108+
sections.assign(firstSection, firstSection + coffHeader->NumberOfSections);
109+
86110
auto exportDirectory =
87-
(optionalHeader->Magic == 0x20b) ? optionalHeaderPtr.AsPtrTo<IMAGE_OPTIONAL_HEADER64>()
88-
->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]
89-
: optionalHeaderPtr.AsPtrTo<IMAGE_OPTIONAL_HEADER32>()
90-
->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
111+
(optionalHeader->Magic == 0x20b)
112+
? optionalHeaderPtr.AsPtrTo<IMAGE_OPTIONAL_HEADER64>()
113+
->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]
114+
: optionalHeaderPtr.AsPtrTo<IMAGE_OPTIONAL_HEADER32>()
115+
->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
116+
exportDirStart = exportDirectory.VirtualAddress;
117+
exportDirSize = exportDirectory.Size;
91118

92119
auto exportTable = libnode.AtOffset(exportDirectory.VirtualAddress)
93120
.AsPtrTo<IMAGE_EXPORT_DIRECTORY>();
@@ -99,6 +126,11 @@ struct Library {
99126

100127
constuint32_t* functionNameTable =
101128
libnode.AtOffset(exportTable->AddressOfNames).AsPtrTo<uint32_t>();
129+
constuint32_t* functionLocations =
130+
libnode.AtOffset(exportTable->AddressOfFunctions).AsPtrTo<uint32_t>();
131+
constuint16_t* functionOrdinals =
132+
libnode.AtOffset(exportTable->AddressOfNameOrdinals)
133+
.AsPtrTo<uint16_t>();
102134

103135
// Given an RVA, parse it as a std::string. The resulting string is empty
104136
// if the symbol does not have a name (i.e. it is ordinal only).
@@ -107,32 +139,33 @@ struct Library {
107139
if (namePtr == nullptr) return {};
108140
return {namePtr};
109141
};
110-
std::transform(functionNameTable,
111-
functionNameTable + exportTable->NumberOfNames,
112-
std::back_inserter(exportedSymbols),
113-
nameRvaToName);
142+
for (uint32_t i = 0; i < exportTable->NumberOfNames; ++i) {
143+
exportedSymbols.push_back({nameRvaToName(functionNameTable[i]),
144+
functionLocations[functionOrdinals[i]]});
145+
}
114146
}
115147

116148
~Library() { FreeLibrary(library); }
117-
};
118149

119-
boolIsPageExecutable(void* address) {
120-
MEMORY_BASIC_INFORMATION memoryInformation;
121-
size_t rc = VirtualQuery(
122-
address, &memoryInformation, sizeof(MEMORY_BASIC_INFORMATION));
150+
boolIsRvaExecutable(uint32_t rva) const {
151+
for (constauto& s : sections) {
152+
if (rva >= s.VirtualAddress &&
153+
rva < s.VirtualAddress + s.Misc.VirtualSize) {
154+
return (s.Characteristics & IMAGE_SCN_MEM_EXECUTE) != 0;
155+
}
156+
}
157+
returntrue;
158+
}
123159

124-
if (rc != 0 && memoryInformation.Protect != 0) {
125-
return memoryInformation.Protect == PAGE_EXECUTE ||
126-
memoryInformation.Protect == PAGE_EXECUTE_READ ||
127-
memoryInformation.Protect == PAGE_EXECUTE_READWRITE ||
128-
memoryInformation.Protect == PAGE_EXECUTE_WRITECOPY;
160+
boolIsForwarderRva(uint32_t rva) const {
161+
return rva >= exportDirStart && rva < exportDirStart + exportDirSize;
129162
}
130-
returnfalse;
131-
}
163+
};
132164

133165
Library LoadLibraryOrExit(constchar* dllPath) {
134-
auto library = LoadLibrary(dllPath);
135-
if (library != nullptr) return library;
166+
auto library =
167+
LoadLibraryEx(dllPath, nullptr, LOAD_LIBRARY_AS_IMAGE_RESOURCE);
168+
if (library != nullptr) returnLibrary(library);
136169

137170
auto error = GetLastError();
138171
std::cerr << "ERROR: Failed to load " << dllPath << std::endl;
@@ -163,31 +196,35 @@ int main(int argc, char** argv) {
163196
auto defFile = std::ofstream(argv[2]);
164197
defFile << "EXPORTS" << std::endl;
165198

166-
for (conststd::string& functionName : libnode.exportedSymbols) {
199+
for (constSymbol& symbol : libnode.exportedSymbols) {
167200
// If a symbol doesn't have a name then it has been exported as an
168201
// ordinal only. We assume that only named symbols are exported.
169-
if (functionName.empty()) continue;
170-
171-
// Every name in the exported symbols table should be resolvable
172-
// to an address because we have actually loaded the library into
173-
// our address space.
174-
auto address = GetProcAddress(libnode.library, functionName.c_str());
175-
if (address == nullptr) {
176-
std::cerr << "WARNING: " << functionName
202+
if (symbol.name.empty()) continue;
203+
204+
if (symbol.rva == 0) {
205+
std::cerr << "WARNING: " << symbol.name
177206
<< " appears in export table but is not a valid symbol"
178207
<< std::endl;
179208
continue;
180209
}
181210

182-
defFile << "" << functionName << " = " << libnode.libraryName << "."
183-
<< functionName;
184-
211+
defFile << "" << symbol.name << " = " << libnode.libraryName << "."
212+
<< symbol.name;
213+
185214
// Nothing distinguishes exported global data from exported functions
186215
// with C linkage. If we do not specify the DATA keyword for such symbols
187216
// then consumers of the .def file will get a linker error. This manifests
188-
// as nodedbg_ symbols not being found. We assert that if the symbol is in
189-
// an executable page in this process then it is a function, not data.
190-
if (!IsPageExecutable(address)) {
217+
// as nodedbg_ symbols not being found. We assert that if the symbol's
218+
// RVA falls in a section with the IMAGE_SCN_MEM_EXECUTE characteristic
219+
// then it is a function, not data.
220+
//
221+
// A forwarder export is the exception: its RVA points back inside the
222+
// export directory, at a redirect string like "NTDLL.RtlAllocateHeap",
223+
// rather than at code or data. The export directory lives in a
224+
// non-executable section, but forwarders resolve to functions, so they
225+
// must not be marked DATA.
226+
if (!libnode.IsForwarderRva(symbol.rva) &&
227+
!libnode.IsRvaExecutable(symbol.rva)) {
191228
defFile << " DATA";
192229
}
193230
defFile << std::endl;

0 commit comments

Comments
Β (0)
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Commit 4a425b4

Browse files
PickBasaduh95
authored andcommitted
build,tools: fix shared library cross-compile
Fixes: #52664 Signed-off-by: PickBas <sayed.kirill@gmail.com> PR-URL: #63963 Reviewed-By: Stefan Stojanovic <stefan.stojanovic@janeasystems.com>
1 parent d562716 commit 4a425b4

2 files changed

Lines changed: 109 additions & 49 deletions

File tree

β€Žnode.gypβ€Ž

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1157,6 +1157,11 @@
11571157
'sources': [
11581158
'src/res/node.rc',
11591159
],
1160+
'libraries': [
1161+
'Dbghelp.lib',
1162+
'winmm.lib',
1163+
'Ws2_32.lib',
1164+
],
11601165
}],
11611166
],
11621167
}, # node_lib_target_name
@@ -1604,6 +1609,10 @@
16041609

16051610
'defines': [ 'NODE_WANT_INTERNALS=1' ],
16061611

1612+
# node_mksnapshot statically links node_base; it must not use the
1613+
# dllimport path meant for executables that load the libnode DLL.
1614+
'defines!': [ 'BUILDING_NODE_EXTENSION' ],
1615+
16071616
'sources': [
16081617
'src/node_snapshot_stub.cc',
16091618
'tools/snapshot/node_mksnapshot.cc',
@@ -1681,13 +1690,27 @@
16811690
'sources': [
16821691
'tools/gen_node_def.cc'
16831692
],
1693+
'conditions': [
1694+
# When cross-compiling, build this tool for the host so it can
1695+
# run during the build. The MSVS generator expects it to be
1696+
# named gen_node_def_host.exe in that case.
1697+
['want_separate_host_toolset', {
1698+
'toolsets': ['host'],
1699+
}],
1700+
],
16841701
},
16851702
{
16861703
'target_name': 'generate_node_def',
16871704
'dependencies': [
1688-
'gen_node_def',
16891705
'<(node_lib_target_name)',
16901706
],
1707+
'conditions': [
1708+
['want_separate_host_toolset', {
1709+
'dependencies': ['gen_node_def#host'],
1710+
}, {
1711+
'dependencies': ['gen_node_def'],
1712+
}],
1713+
],
16911714
'type': 'none',
16921715
'actions': [
16931716
{

β€Žtools/gen_node_def.ccβ€Ž

Lines changed: 85 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
#include<Windows.h>
2-
#include<algorithm>
32
#include<cstdint>
43
#include<fstream>
54
#include<iostream>
@@ -13,9 +12,9 @@
1312
// when building Node.js as a shared library. This is conceptually
1413
// similar to the create_expfile.sh script used on AIX.
1514
//
16-
// Generating this .def file requires parsing data out of the
15+
// Generating this .def file requires parsing data out of the
1716
// PE32/PE32+ file format. Helper structs are defined in <Windows.h>
18-
// hence why this is an executable and not a script. See [2] for
17+
// hence why this is an executable and not a script. See [2] for
1918
// details on the PE format.
2019
//
2120
// [1]: https://docs.microsoft.com/en-us/cpp/build/reference/module-definition-dot-def-files
@@ -28,11 +27,16 @@ struct RelativeAddress {
2827
uintptr_t root;
2928
uintptr_t offset = 0;
3029

31-
RelativeAddress(HMODULE handle) noexcept
32-
: root(reinterpret_cast<uintptr_t>(handle)) {}
30+
explicitRelativeAddress(HMODULE handle) noexcept
31+
: RelativeAddress(handle, 0) {}
3332

33+
// LoadLibraryEx with LOAD_LIBRARY_AS_IMAGE_RESOURCE tags the returned
34+
// handle by setting one of its two lowest bits. Mask them off to recover
35+
// the actual base address of the mapping.
3436
RelativeAddress(HMODULE handle, uintptr_t offset) noexcept
35-
: root(reinterpret_cast<uintptr_t>(handle)), offset(offset) {}
37+
: root(reinterpret_cast<uintptr_t>(handle) &
38+
~static_cast<uintptr_t>(3)),
39+
offset(offset) {}
3640

3741
RelativeAddress(uintptr_t root, uintptr_t offset) noexcept
3842
: root(root), offset(offset) {}
@@ -60,15 +64,28 @@ struct RelativeAddress {
6064
}
6165
};
6266

63-
// A wrapper around a dynamically loaded Windows DLL. This steps through the
64-
// PE file structure to find the export directory and pulls out a list of
65-
// all the exported symbol names.
67+
structSymbol {
68+
std::string name;
69+
uint32_t rva;
70+
};
71+
72+
// A wrapper around a memory-mapped Windows DLL image. The DLL is mapped as
73+
// an image resource (laid out as if loaded, but never executed), so its
74+
// architecture does not need to match ours; this allows generating the
75+
// .def file for a cross-compiled DLL. This steps through the PE file
76+
// structure to find the export directory and pulls out a list of all the
77+
// exported symbols.
6678
structLibrary {
6779
HMODULE library;
6880
std::string libraryName;
69-
std::vector<std::string> exportedSymbols;
81+
std::vector<IMAGE_SECTION_HEADER> sections;
82+
std::vector<Symbol> exportedSymbols;
83+
84+
// Location of the export directory itself, used to detect forwarders.
85+
uint32_t exportDirStart;
86+
uint32_t exportDirSize;
7087

71-
Library(HMODULE library) : library(library) {
88+
explicitLibrary(HMODULE library) : library(library) {
7289
auto libnode = RelativeAddress(library);
7390

7491
// At relative offset 0x3C is a 32 bit offset to the COFF signature, 4 bytes
@@ -83,11 +100,21 @@ struct Library {
83100
auto optionalHeaderPtr = coffHeaderPtr.AtOffset(sizeof(IMAGE_FILE_HEADER));
84101
auto optionalHeader = optionalHeaderPtr.AsPtrTo<IMAGE_OPTIONAL_HEADER>();
85102

103+
// The section table starts right after the optional header.
104+
auto sectionTablePtr =
105+
optionalHeaderPtr.AtOffset(coffHeader->SizeOfOptionalHeader);
106+
constIMAGE_SECTION_HEADER* firstSection =
107+
sectionTablePtr.AsPtrTo<IMAGE_SECTION_HEADER>();
108+
sections.assign(firstSection, firstSection + coffHeader->NumberOfSections);
109+
86110
auto exportDirectory =
87-
(optionalHeader->Magic == 0x20b) ? optionalHeaderPtr.AsPtrTo<IMAGE_OPTIONAL_HEADER64>()
88-
->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]
89-
: optionalHeaderPtr.AsPtrTo<IMAGE_OPTIONAL_HEADER32>()
90-
->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
111+
(optionalHeader->Magic == 0x20b)
112+
? optionalHeaderPtr.AsPtrTo<IMAGE_OPTIONAL_HEADER64>()
113+
->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]
114+
: optionalHeaderPtr.AsPtrTo<IMAGE_OPTIONAL_HEADER32>()
115+
->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
116+
exportDirStart = exportDirectory.VirtualAddress;
117+
exportDirSize = exportDirectory.Size;
91118

92119
auto exportTable = libnode.AtOffset(exportDirectory.VirtualAddress)
93120
.AsPtrTo<IMAGE_EXPORT_DIRECTORY>();
@@ -99,6 +126,11 @@ struct Library {
99126

100127
constuint32_t* functionNameTable =
101128
libnode.AtOffset(exportTable->AddressOfNames).AsPtrTo<uint32_t>();
129+
constuint32_t* functionLocations =
130+
libnode.AtOffset(exportTable->AddressOfFunctions).AsPtrTo<uint32_t>();
131+
constuint16_t* functionOrdinals =
132+
libnode.AtOffset(exportTable->AddressOfNameOrdinals)
133+
.AsPtrTo<uint16_t>();
102134

103135
// Given an RVA, parse it as a std::string. The resulting string is empty
104136
// if the symbol does not have a name (i.e. it is ordinal only).
@@ -107,32 +139,33 @@ struct Library {
107139
if (namePtr == nullptr) return {};
108140
return {namePtr};
109141
};
110-
std::transform(functionNameTable,
111-
functionNameTable + exportTable->NumberOfNames,
112-
std::back_inserter(exportedSymbols),
113-
nameRvaToName);
142+
for (uint32_t i = 0; i < exportTable->NumberOfNames; ++i) {
143+
exportedSymbols.push_back({nameRvaToName(functionNameTable[i]),
144+
functionLocations[functionOrdinals[i]]});
145+
}
114146
}
115147

116148
~Library() { FreeLibrary(library); }
117-
};
118149

119-
boolIsPageExecutable(void* address) {
120-
MEMORY_BASIC_INFORMATION memoryInformation;
121-
size_t rc = VirtualQuery(
122-
address, &memoryInformation, sizeof(MEMORY_BASIC_INFORMATION));
150+
boolIsRvaExecutable(uint32_t rva) const {
151+
for (constauto& s : sections) {
152+
if (rva >= s.VirtualAddress &&
153+
rva < s.VirtualAddress + s.Misc.VirtualSize) {
154+
return (s.Characteristics & IMAGE_SCN_MEM_EXECUTE) != 0;
155+
}
156+
}
157+
returntrue;
158+
}
123159

124-
if (rc != 0 && memoryInformation.Protect != 0) {
125-
return memoryInformation.Protect == PAGE_EXECUTE ||
126-
memoryInformation.Protect == PAGE_EXECUTE_READ ||
127-
memoryInformation.Protect == PAGE_EXECUTE_READWRITE ||
128-
memoryInformation.Protect == PAGE_EXECUTE_WRITECOPY;
160+
boolIsForwarderRva(uint32_t rva) const {
161+
return rva >= exportDirStart && rva < exportDirStart + exportDirSize;
129162
}
130-
returnfalse;
131-
}
163+
};
132164

133165
Library LoadLibraryOrExit(constchar* dllPath) {
134-
auto library = LoadLibrary(dllPath);
135-
if (library != nullptr) return library;
166+
auto library =
167+
LoadLibraryEx(dllPath, nullptr, LOAD_LIBRARY_AS_IMAGE_RESOURCE);
168+
if (library != nullptr) returnLibrary(library);
136169

137170
auto error = GetLastError();
138171
std::cerr << "ERROR: Failed to load " << dllPath << std::endl;
@@ -163,31 +196,35 @@ int main(int argc, char** argv) {
163196
auto defFile = std::ofstream(argv[2]);
164197
defFile << "EXPORTS" << std::endl;
165198

166-
for (conststd::string& functionName : libnode.exportedSymbols) {
199+
for (constSymbol& symbol : libnode.exportedSymbols) {
167200
// If a symbol doesn't have a name then it has been exported as an
168201
// ordinal only. We assume that only named symbols are exported.
169-
if (functionName.empty()) continue;
170-
171-
// Every name in the exported symbols table should be resolvable
172-
// to an address because we have actually loaded the library into
173-
// our address space.
174-
auto address = GetProcAddress(libnode.library, functionName.c_str());
175-
if (address == nullptr) {
176-
std::cerr << "WARNING: " << functionName
202+
if (symbol.name.empty()) continue;
203+
204+
if (symbol.rva == 0) {
205+
std::cerr << "WARNING: " << symbol.name
177206
<< " appears in export table but is not a valid symbol"
178207
<< std::endl;
179208
continue;
180209
}
181210

182-
defFile << "" << functionName << " = " << libnode.libraryName << "."
183-
<< functionName;
184-
211+
defFile << "" << symbol.name << " = " << libnode.libraryName << "."
212+
<< symbol.name;
213+
185214
// Nothing distinguishes exported global data from exported functions
186215
// with C linkage. If we do not specify the DATA keyword for such symbols
187216
// then consumers of the .def file will get a linker error. This manifests
188-
// as nodedbg_ symbols not being found. We assert that if the symbol is in
189-
// an executable page in this process then it is a function, not data.
190-
if (!IsPageExecutable(address)) {
217+
// as nodedbg_ symbols not being found. We assert that if the symbol's
218+
// RVA falls in a section with the IMAGE_SCN_MEM_EXECUTE characteristic
219+
// then it is a function, not data.
220+
//
221+
// A forwarder export is the exception: its RVA points back inside the
222+
// export directory, at a redirect string like "NTDLL.RtlAllocateHeap",
223+
// rather than at code or data. The export directory lives in a
224+
// non-executable section, but forwarders resolve to functions, so they
225+
// must not be marked DATA.
226+
if (!libnode.IsForwarderRva(symbol.rva) &&
227+
!libnode.IsRvaExecutable(symbol.rva)) {
191228
defFile << " DATA";
192229
}
193230
defFile << std::endl;

0 commit comments

Comments
Β (0)
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Commit 4a425b4

Browse files
PickBasaduh95
authored andcommitted
build,tools: fix shared library cross-compile
Fixes: #52664 Signed-off-by: PickBas <sayed.kirill@gmail.com> PR-URL: #63963 Reviewed-By: Stefan Stojanovic <stefan.stojanovic@janeasystems.com>
1 parent d562716 commit 4a425b4

2 files changed

Lines changed: 109 additions & 49 deletions

File tree

β€Žnode.gypβ€Ž

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1157,6 +1157,11 @@
11571157
'sources': [
11581158
'src/res/node.rc',
11591159
],
1160+
'libraries': [
1161+
'Dbghelp.lib',
1162+
'winmm.lib',
1163+
'Ws2_32.lib',
1164+
],
11601165
}],
11611166
],
11621167
}, # node_lib_target_name
@@ -1604,6 +1609,10 @@
16041609

16051610
'defines': [ 'NODE_WANT_INTERNALS=1' ],
16061611

1612+
# node_mksnapshot statically links node_base; it must not use the
1613+
# dllimport path meant for executables that load the libnode DLL.
1614+
'defines!': [ 'BUILDING_NODE_EXTENSION' ],
1615+
16071616
'sources': [
16081617
'src/node_snapshot_stub.cc',
16091618
'tools/snapshot/node_mksnapshot.cc',
@@ -1681,13 +1690,27 @@
16811690
'sources': [
16821691
'tools/gen_node_def.cc'
16831692
],
1693+
'conditions': [
1694+
# When cross-compiling, build this tool for the host so it can
1695+
# run during the build. The MSVS generator expects it to be
1696+
# named gen_node_def_host.exe in that case.
1697+
['want_separate_host_toolset', {
1698+
'toolsets': ['host'],
1699+
}],
1700+
],
16841701
},
16851702
{
16861703
'target_name': 'generate_node_def',
16871704
'dependencies': [
1688-
'gen_node_def',
16891705
'<(node_lib_target_name)',
16901706
],
1707+
'conditions': [
1708+
['want_separate_host_toolset', {
1709+
'dependencies': ['gen_node_def#host'],
1710+
}, {
1711+
'dependencies': ['gen_node_def'],
1712+
}],
1713+
],
16911714
'type': 'none',
16921715
'actions': [
16931716
{

β€Žtools/gen_node_def.ccβ€Ž

Lines changed: 85 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
#include<Windows.h>
2-
#include<algorithm>
32
#include<cstdint>
43
#include<fstream>
54
#include<iostream>
@@ -13,9 +12,9 @@
1312
// when building Node.js as a shared library. This is conceptually
1413
// similar to the create_expfile.sh script used on AIX.
1514
//
16-
// Generating this .def file requires parsing data out of the
15+
// Generating this .def file requires parsing data out of the
1716
// PE32/PE32+ file format. Helper structs are defined in <Windows.h>
18-
// hence why this is an executable and not a script. See [2] for
17+
// hence why this is an executable and not a script. See [2] for
1918
// details on the PE format.
2019
//
2120
// [1]: https://docs.microsoft.com/en-us/cpp/build/reference/module-definition-dot-def-files
@@ -28,11 +27,16 @@ struct RelativeAddress {
2827
uintptr_t root;
2928
uintptr_t offset = 0;
3029

31-
RelativeAddress(HMODULE handle) noexcept
32-
: root(reinterpret_cast<uintptr_t>(handle)) {}
30+
explicitRelativeAddress(HMODULE handle) noexcept
31+
: RelativeAddress(handle, 0) {}
3332

33+
// LoadLibraryEx with LOAD_LIBRARY_AS_IMAGE_RESOURCE tags the returned
34+
// handle by setting one of its two lowest bits. Mask them off to recover
35+
// the actual base address of the mapping.
3436
RelativeAddress(HMODULE handle, uintptr_t offset) noexcept
35-
: root(reinterpret_cast<uintptr_t>(handle)), offset(offset) {}
37+
: root(reinterpret_cast<uintptr_t>(handle) &
38+
~static_cast<uintptr_t>(3)),
39+
offset(offset) {}
3640

3741
RelativeAddress(uintptr_t root, uintptr_t offset) noexcept
3842
: root(root), offset(offset) {}
@@ -60,15 +64,28 @@ struct RelativeAddress {
6064
}
6165
};
6266

63-
// A wrapper around a dynamically loaded Windows DLL. This steps through the
64-
// PE file structure to find the export directory and pulls out a list of
65-
// all the exported symbol names.
67+
structSymbol {
68+
std::string name;
69+
uint32_t rva;
70+
};
71+
72+
// A wrapper around a memory-mapped Windows DLL image. The DLL is mapped as
73+
// an image resource (laid out as if loaded, but never executed), so its
74+
// architecture does not need to match ours; this allows generating the
75+
// .def file for a cross-compiled DLL. This steps through the PE file
76+
// structure to find the export directory and pulls out a list of all the
77+
// exported symbols.
6678
structLibrary {
6779
HMODULE library;
6880
std::string libraryName;
69-
std::vector<std::string> exportedSymbols;
81+
std::vector<IMAGE_SECTION_HEADER> sections;
82+
std::vector<Symbol> exportedSymbols;
83+
84+
// Location of the export directory itself, used to detect forwarders.
85+
uint32_t exportDirStart;
86+
uint32_t exportDirSize;
7087

71-
Library(HMODULE library) : library(library) {
88+
explicitLibrary(HMODULE library) : library(library) {
7289
auto libnode = RelativeAddress(library);
7390

7491
// At relative offset 0x3C is a 32 bit offset to the COFF signature, 4 bytes
@@ -83,11 +100,21 @@ struct Library {
83100
auto optionalHeaderPtr = coffHeaderPtr.AtOffset(sizeof(IMAGE_FILE_HEADER));
84101
auto optionalHeader = optionalHeaderPtr.AsPtrTo<IMAGE_OPTIONAL_HEADER>();
85102

103+
// The section table starts right after the optional header.
104+
auto sectionTablePtr =
105+
optionalHeaderPtr.AtOffset(coffHeader->SizeOfOptionalHeader);
106+
constIMAGE_SECTION_HEADER* firstSection =
107+
sectionTablePtr.AsPtrTo<IMAGE_SECTION_HEADER>();
108+
sections.assign(firstSection, firstSection + coffHeader->NumberOfSections);
109+
86110
auto exportDirectory =
87-
(optionalHeader->Magic == 0x20b) ? optionalHeaderPtr.AsPtrTo<IMAGE_OPTIONAL_HEADER64>()
88-
->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]
89-
: optionalHeaderPtr.AsPtrTo<IMAGE_OPTIONAL_HEADER32>()
90-
->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
111+
(optionalHeader->Magic == 0x20b)
112+
? optionalHeaderPtr.AsPtrTo<IMAGE_OPTIONAL_HEADER64>()
113+
->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]
114+
: optionalHeaderPtr.AsPtrTo<IMAGE_OPTIONAL_HEADER32>()
115+
->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
116+
exportDirStart = exportDirectory.VirtualAddress;
117+
exportDirSize = exportDirectory.Size;
91118

92119
auto exportTable = libnode.AtOffset(exportDirectory.VirtualAddress)
93120
.AsPtrTo<IMAGE_EXPORT_DIRECTORY>();
@@ -99,6 +126,11 @@ struct Library {
99126

100127
constuint32_t* functionNameTable =
101128
libnode.AtOffset(exportTable->AddressOfNames).AsPtrTo<uint32_t>();
129+
constuint32_t* functionLocations =
130+
libnode.AtOffset(exportTable->AddressOfFunctions).AsPtrTo<uint32_t>();
131+
constuint16_t* functionOrdinals =
132+
libnode.AtOffset(exportTable->AddressOfNameOrdinals)
133+
.AsPtrTo<uint16_t>();
102134

103135
// Given an RVA, parse it as a std::string. The resulting string is empty
104136
// if the symbol does not have a name (i.e. it is ordinal only).
@@ -107,32 +139,33 @@ struct Library {
107139
if (namePtr == nullptr) return {};
108140
return {namePtr};
109141
};
110-
std::transform(functionNameTable,
111-
functionNameTable + exportTable->NumberOfNames,
112-
std::back_inserter(exportedSymbols),
113-
nameRvaToName);
142+
for (uint32_t i = 0; i < exportTable->NumberOfNames; ++i) {
143+
exportedSymbols.push_back({nameRvaToName(functionNameTable[i]),
144+
functionLocations[functionOrdinals[i]]});
145+
}
114146
}
115147

116148
~Library() { FreeLibrary(library); }
117-
};
118149

119-
boolIsPageExecutable(void* address) {
120-
MEMORY_BASIC_INFORMATION memoryInformation;
121-
size_t rc = VirtualQuery(
122-
address, &memoryInformation, sizeof(MEMORY_BASIC_INFORMATION));
150+
boolIsRvaExecutable(uint32_t rva) const {
151+
for (constauto& s : sections) {
152+
if (rva >= s.VirtualAddress &&
153+
rva < s.VirtualAddress + s.Misc.VirtualSize) {
154+
return (s.Characteristics & IMAGE_SCN_MEM_EXECUTE) != 0;
155+
}
156+
}
157+
returntrue;
158+
}
123159

124-
if (rc != 0 && memoryInformation.Protect != 0) {
125-
return memoryInformation.Protect == PAGE_EXECUTE ||
126-
memoryInformation.Protect == PAGE_EXECUTE_READ ||
127-
memoryInformation.Protect == PAGE_EXECUTE_READWRITE ||
128-
memoryInformation.Protect == PAGE_EXECUTE_WRITECOPY;
160+
boolIsForwarderRva(uint32_t rva) const {
161+
return rva >= exportDirStart && rva < exportDirStart + exportDirSize;
129162
}
130-
returnfalse;
131-
}
163+
};
132164

133165
Library LoadLibraryOrExit(constchar* dllPath) {
134-
auto library = LoadLibrary(dllPath);
135-
if (library != nullptr) return library;
166+
auto library =
167+
LoadLibraryEx(dllPath, nullptr, LOAD_LIBRARY_AS_IMAGE_RESOURCE);
168+
if (library != nullptr) returnLibrary(library);
136169

137170
auto error = GetLastError();
138171
std::cerr << "ERROR: Failed to load " << dllPath << std::endl;
@@ -163,31 +196,35 @@ int main(int argc, char** argv) {
163196
auto defFile = std::ofstream(argv[2]);
164197
defFile << "EXPORTS" << std::endl;
165198

166-
for (conststd::string& functionName : libnode.exportedSymbols) {
199+
for (constSymbol& symbol : libnode.exportedSymbols) {
167200
// If a symbol doesn't have a name then it has been exported as an
168201
// ordinal only. We assume that only named symbols are exported.
169-
if (functionName.empty()) continue;
170-
171-
// Every name in the exported symbols table should be resolvable
172-
// to an address because we have actually loaded the library into
173-
// our address space.
174-
auto address = GetProcAddress(libnode.library, functionName.c_str());
175-
if (address == nullptr) {
176-
std::cerr << "WARNING: " << functionName
202+
if (symbol.name.empty()) continue;
203+
204+
if (symbol.rva == 0) {
205+
std::cerr << "WARNING: " << symbol.name
177206
<< " appears in export table but is not a valid symbol"
178207
<< std::endl;
179208
continue;
180209
}
181210

182-
defFile << "" << functionName << " = " << libnode.libraryName << "."
183-
<< functionName;
184-
211+
defFile << "" << symbol.name << " = " << libnode.libraryName << "."
212+
<< symbol.name;
213+
185214
// Nothing distinguishes exported global data from exported functions
186215
// with C linkage. If we do not specify the DATA keyword for such symbols
187216
// then consumers of the .def file will get a linker error. This manifests
188-
// as nodedbg_ symbols not being found. We assert that if the symbol is in
189-
// an executable page in this process then it is a function, not data.
190-
if (!IsPageExecutable(address)) {
217+
// as nodedbg_ symbols not being found. We assert that if the symbol's
218+
// RVA falls in a section with the IMAGE_SCN_MEM_EXECUTE characteristic
219+
// then it is a function, not data.
220+
//
221+
// A forwarder export is the exception: its RVA points back inside the
222+
// export directory, at a redirect string like "NTDLL.RtlAllocateHeap",
223+
// rather than at code or data. The export directory lives in a
224+
// non-executable section, but forwarders resolve to functions, so they
225+
// must not be marked DATA.
226+
if (!libnode.IsForwarderRva(symbol.rva) &&
227+
!libnode.IsRvaExecutable(symbol.rva)) {
191228
defFile << " DATA";
192229
}
193230
defFile << std::endl;

0 commit comments

Comments
Β (0)