| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1241,6 +1241,11 @@ | |||
| 1241 | 1241 | 'sources': [ | |
| 1242 | 1242 | 'src/res/node.rc', | |
| 1243 | 1243 | ], | |
| 1244 | + 'libraries': [ | ||
| 1245 | + 'Dbghelp.lib', | ||
| 1246 | + 'winmm.lib', | ||
| 1247 | + 'Ws2_32.lib', | ||
| 1248 | + ], | ||
| 1244 | 1249 | }], | |
| 1245 | 1250 | ], | |
| 1246 | 1251 | }, # node_lib_target_name | |
@@ -1753,6 +1758,10 @@ | |||
| 1753 | 1758 | ||
| 1754 | 1759 | 'defines': [ 'NODE_WANT_INTERNALS=1' ], | |
| 1755 | 1760 | ||
| 1761 | + # node_mksnapshot statically links node_base; it must not use the | ||
| 1762 | + # dllimport path meant for executables that load the libnode DLL. | ||
| 1763 | + 'defines!': [ 'BUILDING_NODE_EXTENSION' ], | ||
| 1764 | + | ||
| 1756 | 1765 | 'sources': [ | |
| 1757 | 1766 | 'src/node_snapshot_stub.cc', | |
| 1758 | 1767 | 'tools/snapshot/node_mksnapshot.cc', | |
@@ -1835,13 +1844,27 @@ | |||
| 1835 | 1844 | 'sources': [ | |
| 1836 | 1845 | 'tools/gen_node_def.cc' | |
| 1837 | 1846 | ], | |
| 1847 | + 'conditions': [ | ||
| 1848 | + # When cross-compiling, build this tool for the host so it can | ||
| 1849 | + # run during the build. The MSVS generator expects it to be | ||
| 1850 | + # named gen_node_def_host.exe in that case. | ||
| 1851 | + ['want_separate_host_toolset', { | ||
| 1852 | + 'toolsets': ['host'], | ||
| 1853 | + }], | ||
| 1854 | + ], | ||
| 1838 | 1855 | }, | |
| 1839 | 1856 | { | |
| 1840 | 1857 | 'target_name': 'generate_node_def', | |
| 1841 | 1858 | 'dependencies': [ | |
| 1842 | - 'gen_node_def', | ||
| 1843 | 1859 | '<(node_lib_target_name)', | |
| 1844 | 1860 | ], | |
| 1861 | + 'conditions': [ | ||
| 1862 | + ['want_separate_host_toolset', { | ||
| 1863 | + 'dependencies': ['gen_node_def#host'], | ||
| 1864 | + }, { | ||
| 1865 | + 'dependencies': ['gen_node_def'], | ||
| 1866 | + }], | ||
| 1867 | + ], | ||
| 1845 | 1868 | 'type': 'none', | |
| 1846 | 1869 | 'actions': [ | |
| 1847 | 1870 | { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,5 +1,4 @@ | |||
| 1 | 1 | #include <Windows.h> | |
| 2 | - #include <algorithm> | ||
| 3 | 2 | #include <cstdint> | |
| 4 | 3 | #include <fstream> | |
| 5 | 4 | #include <iostream> | |
@@ -13,9 +12,9 @@ | |||
| 13 | 12 | // when building Node.js as a shared library. This is conceptually | |
| 14 | 13 | // similar to the create_expfile.sh script used on AIX. | |
| 15 | 14 | // | |
| 16 | - // Generating this .def file requires parsing data out of the | ||
| 15 | + // Generating this .def file requires parsing data out of the | ||
| 17 | 16 | // 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 | ||
| 19 | 18 | // details on the PE format. | |
| 20 | 19 | // | |
| 21 | 20 | // [1]: https://docs.microsoft.com/en-us/cpp/build/reference/module-definition-dot-def-files | |
@@ -28,11 +27,16 @@ struct RelativeAddress { | |||
| 28 | 27 | uintptr_t root; | |
| 29 | 28 | uintptr_t offset = 0; | |
| 30 | 29 | ||
| 31 | - RelativeAddress(HMODULE handle) noexcept | ||
| 32 | - : root(reinterpret_cast<uintptr_t>(handle)) {} | ||
| 30 | + explicit RelativeAddress(HMODULE handle) noexcept | ||
| 31 | + : RelativeAddress(handle, 0) {} | ||
| 33 | 32 | ||
| 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. | ||
| 34 | 36 | 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) {} | ||
| 36 | 40 | ||
| 37 | 41 | RelativeAddress(uintptr_t root, uintptr_t offset) noexcept | |
| 38 | 42 | : root(root), offset(offset) {} | |
@@ -60,15 +64,28 @@ struct RelativeAddress { | |||
| 60 | 64 | } | |
| 61 | 65 | }; | |
| 62 | 66 | ||
| 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 | + struct Symbol { | ||
| 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. | ||
| 66 | 78 | struct Library { | |
| 67 | 79 | HMODULE library; | |
| 68 | 80 | 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; | ||
| 70 | 87 | ||
| 71 | - Library(HMODULE library) : library(library) { | ||
| 88 | + explicit Library(HMODULE library) : library(library) { | ||
| 72 | 89 | auto libnode = RelativeAddress(library); | |
| 73 | 90 | ||
| 74 | 91 | // At relative offset 0x3C is a 32 bit offset to the COFF signature, 4 bytes | |
@@ -83,11 +100,21 @@ struct Library { | |||
| 83 | 100 | auto optionalHeaderPtr = coffHeaderPtr.AtOffset(sizeof(IMAGE_FILE_HEADER)); | |
| 84 | 101 | auto optionalHeader = optionalHeaderPtr.AsPtrTo<IMAGE_OPTIONAL_HEADER>(); | |
| 85 | 102 | ||
| 103 | + // The section table starts right after the optional header. | ||
| 104 | + auto sectionTablePtr = | ||
| 105 | + optionalHeaderPtr.AtOffset(coffHeader->SizeOfOptionalHeader); | ||
| 106 | + const IMAGE_SECTION_HEADER* firstSection = | ||
| 107 | + sectionTablePtr.AsPtrTo<IMAGE_SECTION_HEADER>(); | ||
| 108 | + sections.assign(firstSection, firstSection + coffHeader->NumberOfSections); | ||
| 109 | + | ||
| 86 | 110 | 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; | ||
| 91 | 118 | ||
| 92 | 119 | auto exportTable = libnode.AtOffset(exportDirectory.VirtualAddress) | |
| 93 | 120 | .AsPtrTo<IMAGE_EXPORT_DIRECTORY>(); | |
@@ -99,6 +126,11 @@ struct Library { | |||
| 99 | 126 | ||
| 100 | 127 | const uint32_t* functionNameTable = | |
| 101 | 128 | libnode.AtOffset(exportTable->AddressOfNames).AsPtrTo<uint32_t>(); | |
| 129 | + const uint32_t* functionLocations = | ||
| 130 | + libnode.AtOffset(exportTable->AddressOfFunctions).AsPtrTo<uint32_t>(); | ||
| 131 | + const uint16_t* functionOrdinals = | ||
| 132 | + libnode.AtOffset(exportTable->AddressOfNameOrdinals) | ||
| 133 | + .AsPtrTo<uint16_t>(); | ||
| 102 | 134 | ||
| 103 | 135 | // Given an RVA, parse it as a std::string. The resulting string is empty | |
| 104 | 136 | // if the symbol does not have a name (i.e. it is ordinal only). | |
@@ -107,32 +139,33 @@ struct Library { | |||
| 107 | 139 | if (namePtr == nullptr) return {}; | |
| 108 | 140 | return {namePtr}; | |
| 109 | 141 | }; | |
| 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 | + } | ||
| 114 | 146 | } | |
| 115 | 147 | ||
| 116 | 148 | ~Library() { FreeLibrary(library); } | |
| 117 | - }; | ||
| 118 | 149 | ||
| 119 | - bool IsPageExecutable(void* address) { | ||
| 120 | - MEMORY_BASIC_INFORMATION memoryInformation; | ||
| 121 | - size_t rc = VirtualQuery( | ||
| 122 | - address, &memoryInformation, sizeof(MEMORY_BASIC_INFORMATION)); | ||
| 150 | + bool IsRvaExecutable(uint32_t rva) const { | ||
| 151 | + for (const auto& 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 | + return true; | ||
| 158 | + } | ||
| 123 | 159 | ||
| 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 | + bool IsForwarderRva(uint32_t rva) const { | ||
| 161 | + return rva >= exportDirStart && rva < exportDirStart + exportDirSize; | ||
| 129 | 162 | } | |
| 130 | - return false; | ||
| 131 | - } | ||
| 163 | + }; | ||
| 132 | 164 | ||
| 133 | 165 | Library LoadLibraryOrExit(const char* 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) return Library(library); | ||
| 136 | 169 | ||
| 137 | 170 | auto error = GetLastError(); | |
| 138 | 171 | std::cerr << "ERROR: Failed to load " << dllPath << std::endl; | |
@@ -163,31 +196,35 @@ int main(int argc, char** argv) { | |||
| 163 | 196 | auto defFile = std::ofstream(argv[2]); | |
| 164 | 197 | defFile << "EXPORTS" << std::endl; | |
| 165 | 198 | ||
| 166 | - for (const std::string& functionName : libnode.exportedSymbols) { | ||
| 199 | + for (const Symbol& symbol : libnode.exportedSymbols) { | ||
| 167 | 200 | // If a symbol doesn't have a name then it has been exported as an | |
| 168 | 201 | // 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 | ||
| 177 | 206 | << " appears in export table but is not a valid symbol" | |
| 178 | 207 | << std::endl; | |
| 179 | 208 | continue; | |
| 180 | 209 | } | |
| 181 | 210 | ||
| 182 | - defFile << " " << functionName << " = " << libnode.libraryName << "." | ||
| 183 | - << functionName; | ||
| 184 | - | ||
| 211 | + defFile << " " << symbol.name << " = " << libnode.libraryName << "." | ||
| 212 | + << symbol.name; | ||
| 213 | + | ||
| 185 | 214 | // Nothing distinguishes exported global data from exported functions | |
| 186 | 215 | // with C linkage. If we do not specify the DATA keyword for such symbols | |
| 187 | 216 | // 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)) { | ||
| 191 | 228 | defFile << " DATA"; | |
| 192 | 229 | } | |
| 193 | 230 | defFile << std::endl; | |
| Back | FazBrowse Home | New Git URL |
0 commit comments