koffi 2.5.19 → 2.5.21-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -1
- package/build/koffi/darwin_arm64/koffi.node +0 -0
- package/build/koffi/darwin_x64/koffi.node +0 -0
- package/build/koffi/freebsd_arm64/koffi.node +0 -0
- package/build/koffi/freebsd_ia32/koffi.node +0 -0
- package/build/koffi/freebsd_x64/koffi.node +0 -0
- package/build/koffi/linux_arm32hf/koffi.node +0 -0
- package/build/koffi/linux_arm64/koffi.node +0 -0
- package/build/koffi/linux_ia32/koffi.node +0 -0
- package/build/koffi/linux_riscv64hf64/koffi.node +0 -0
- package/build/koffi/linux_x64/koffi.node +0 -0
- package/build/koffi/openbsd_ia32/koffi.node +0 -0
- package/build/koffi/openbsd_x64/koffi.node +0 -0
- package/build/koffi/win32_arm64/koffi.node +0 -0
- package/build/koffi/win32_ia32/koffi.node +0 -0
- package/build/koffi/win32_x64/koffi.node +0 -0
- package/doc/callbacks.md +1 -1
- package/doc/contribute.md +38 -6
- package/doc/input.md +15 -15
- package/doc/misc.md +3 -3
- package/package.json +2 -2
- package/src/index.js +3 -2
- package/src/koffi/CMakeLists.txt +1 -0
- package/src/koffi/src/call.cc +2 -10
- package/src/koffi/src/ffi.cc +47 -4
- package/src/koffi/src/ffi.hh +2 -0
- package/src/koffi/src/win32.cc +155 -0
- package/src/koffi/src/win32.hh +44 -0
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,11 @@
|
|
|
4
4
|
|
|
5
5
|
### Koffi 2.5
|
|
6
6
|
|
|
7
|
+
#### Koffi 2.5.20 (2023-08-31)
|
|
8
|
+
|
|
9
|
+
- Fix possible crash with async registered callbacks introduced in Koffi 2.5.19
|
|
10
|
+
- Various documentation fixes and improvements
|
|
11
|
+
|
|
7
12
|
#### Koffi 2.5.19 (2023-08-29)
|
|
8
13
|
|
|
9
14
|
- Create thread-safe function broker lazily
|
|
@@ -332,7 +337,7 @@ Pre-built binaries don't work correctly in Koffi 2.5.13 to 2.5.15, skip those ve
|
|
|
332
337
|
**Main changes:**
|
|
333
338
|
|
|
334
339
|
- Add [koffi.as()](polymorphism.md#input-polymorphism) to support polymorphic APIs based on `void *` parameters
|
|
335
|
-
- Add [endian-sensitive integer types](input.md#endian-sensitive-
|
|
340
|
+
- Add [endian-sensitive integer types](input.md#endian-sensitive-integers): `intX_le_t`, `intX_be_t`, `uintX_le_t`, `uintX_be_t`
|
|
336
341
|
- Accept typed arrays for `void *` parameters
|
|
337
342
|
- Introduce `koffi.opaque()` to replace `koffi.handle()` (which remains supported until Koffi 3.0)
|
|
338
343
|
- Support JS Array and TypedArray to fill struct and array pointer members
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/doc/callbacks.md
CHANGED
|
@@ -88,7 +88,7 @@ console.log(ret);
|
|
|
88
88
|
|
|
89
89
|
Use registered callbacks when the function needs to be called at a later time (e.g. log handler, event handler, `fopencookie/funopen`). Call `koffi.register(func, type)` to register a callback function, with two arguments: the JS function, and the callback type.
|
|
90
90
|
|
|
91
|
-
When you are done, call `koffi.unregister()` (with the value returned by `koffi.register()`) to release the slot. A maximum of
|
|
91
|
+
When you are done, call `koffi.unregister()` (with the value returned by `koffi.register()`) to release the slot. A maximum of 8192 callbacks can exist at the same time. Failure to do so will leak the slot, and subsequent registrations may fail (with an exception) once all slots are used.
|
|
92
92
|
|
|
93
93
|
The example below shows how to register and unregister delayed callbacks.
|
|
94
94
|
|
package/doc/contribute.md
CHANGED
|
@@ -14,7 +14,7 @@ Start by cloning the repository with [Git](https://git-scm.com/):
|
|
|
14
14
|
|
|
15
15
|
```sh
|
|
16
16
|
git clone https://github.com/Koromix/rygel
|
|
17
|
-
cd rygel
|
|
17
|
+
cd rygel
|
|
18
18
|
```
|
|
19
19
|
|
|
20
20
|
As said before, this is a monorepository containg multiple projects, hence the name.
|
|
@@ -30,8 +30,8 @@ First, make sure the following dependencies are met:
|
|
|
30
30
|
Once this is done, run this command _from the test or the benchmark directory_ (depending on what you want to build):
|
|
31
31
|
|
|
32
32
|
```sh
|
|
33
|
-
cd
|
|
34
|
-
node
|
|
33
|
+
cd src/koffi
|
|
34
|
+
node ../cnoke/cnoke.js
|
|
35
35
|
```
|
|
36
36
|
|
|
37
37
|
### Other platforms
|
|
@@ -46,11 +46,31 @@ Make sure the following dependencies are met:
|
|
|
46
46
|
Once this is done, run this command _from the test or the benchmark directory_ (depending on what you want to build):
|
|
47
47
|
|
|
48
48
|
```sh
|
|
49
|
-
cd
|
|
49
|
+
cd src/koffi
|
|
50
|
+
node ../cnoke/cnoke.js
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Run tests
|
|
54
|
+
|
|
55
|
+
### On your machine
|
|
56
|
+
|
|
57
|
+
Once Koffi is built, you can build the tests and run them with the following commands:
|
|
58
|
+
|
|
59
|
+
```sh
|
|
60
|
+
cd src/koffi/test
|
|
50
61
|
node ../../cnoke/cnoke.js
|
|
62
|
+
|
|
63
|
+
node sync.js # Run synchronous unit tests
|
|
64
|
+
node async.js # Run asynchronous unit tests
|
|
65
|
+
node callbacks.js # Run callback unit tests
|
|
66
|
+
node union.js # Run union unit tests
|
|
67
|
+
node win32.js # Run Windows-specific unit tests (only on Windows)
|
|
68
|
+
|
|
69
|
+
node sqlite.js # Run SQLite integration tests
|
|
70
|
+
node raylib.js # Run Raylib integration tests
|
|
51
71
|
```
|
|
52
72
|
|
|
53
|
-
|
|
73
|
+
### On virtual machines
|
|
54
74
|
|
|
55
75
|
Koffi is tested on multiple architectures using emulated (accelerated when possible) QEMU machines. First, you need to install qemu packages, such as `qemu-system` (or even `qemu-system-gui`) on Ubuntu.
|
|
56
76
|
|
|
@@ -59,7 +79,7 @@ These machines are not included directly in this repository (for license and siz
|
|
|
59
79
|
For example, if you want to run the tests on Debian ARM64, run the following commands:
|
|
60
80
|
|
|
61
81
|
```sh
|
|
62
|
-
cd
|
|
82
|
+
cd src/koffi/tools/
|
|
63
83
|
wget -q -O- https://koromix.dev/files/machines/qemu_debian_arm64.tar.zst | zstd -d | tar xv
|
|
64
84
|
sha256sum -c --ignore-missing registry/sha256sum.txt
|
|
65
85
|
```
|
|
@@ -107,6 +127,18 @@ Each machine is configured to run a VNC server available locally, which you can
|
|
|
107
127
|
node qemu.js info debian_x64
|
|
108
128
|
```
|
|
109
129
|
|
|
130
|
+
## Making a release
|
|
131
|
+
|
|
132
|
+
First, change the version numbers in `package.json`. Then publish a new release with the following commands:
|
|
133
|
+
|
|
134
|
+
```sh
|
|
135
|
+
node qemu.js test
|
|
136
|
+
node qemu.js dist
|
|
137
|
+
|
|
138
|
+
cd build/dist
|
|
139
|
+
npm publish
|
|
140
|
+
```
|
|
141
|
+
|
|
110
142
|
## Code style
|
|
111
143
|
|
|
112
144
|
Koffi is programmed in a mix of C++ and assembly code (architecture-specific code). It uses [node-addon-api](https://github.com/nodejs/node-addon-api) (C++ N-API wrapper) to interact with Node.js.
|
package/doc/input.md
CHANGED
|
@@ -56,26 +56,26 @@ let struct1 = koffi.struct({ dummy: 'long' });
|
|
|
56
56
|
let struct2 = koffi.struct({ dummy: koffi.types.long });
|
|
57
57
|
```
|
|
58
58
|
|
|
59
|
-
### Endian-sensitive
|
|
59
|
+
### Endian-sensitive integers
|
|
60
60
|
|
|
61
61
|
*New in Koffi 2.1*
|
|
62
62
|
|
|
63
63
|
Koffi defines a bunch of endian-sensitive types, which can be used when dealing with binary data (network payloads, binary file formats, etc.).
|
|
64
64
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
65
|
+
C type | Bytes | Signedness | Endianness
|
|
66
|
+
---------------------- | ----- | ---------- | -------------
|
|
67
|
+
int16_le, int16_le_t | 2 | Signed | Little Endian
|
|
68
|
+
int16_be, int16_be_t | 2 | Signed | Big Endian
|
|
69
|
+
uint16_le, uint16_le_t | 2 | Unsigned | Little Endian
|
|
70
|
+
uint16_be, uint16_be_t | 2 | Unsigned | Big Endian
|
|
71
|
+
int32_le, int32_le_t | 4 | Signed | Little Endian
|
|
72
|
+
int32_be, int32_be_t | 4 | Signed | Big Endian
|
|
73
|
+
uint32_le, uint32_le_t | 4 | Unsigned | Little Endian
|
|
74
|
+
uint32_be, uint32_be_t | 4 | Unsigned | Big Endian
|
|
75
|
+
int64_le, int64_le_t | 8 | Signed | Little Endian
|
|
76
|
+
int64_be, int64_be_t | 8 | Signed | Big Endian
|
|
77
|
+
uint64_le, uint64_le_t | 8 | Unsigned | Little Endian
|
|
78
|
+
uint64_be, uint64_be_t | 8 | Unsigned | Big Endian
|
|
79
79
|
|
|
80
80
|
## Struct types
|
|
81
81
|
|
package/doc/misc.md
CHANGED
|
@@ -106,9 +106,9 @@ You can use `koffi.stats()` to get a few statistics related to Koffi.
|
|
|
106
106
|
|
|
107
107
|
*New in Koffi 2.3.14*
|
|
108
108
|
|
|
109
|
-
You can use `koffi.errno()` to the current errno value, and `koffi.errno(value)` to change it.
|
|
109
|
+
You can use `koffi.errno()` to get the current errno value, and `koffi.errno(value)` to change it.
|
|
110
110
|
|
|
111
|
-
The standard POSIX error codes are available in `koffi.os.errno`, as
|
|
111
|
+
The standard POSIX error codes are available in `koffi.os.errno`, as shown below:
|
|
112
112
|
|
|
113
113
|
```js
|
|
114
114
|
const assert = require('assert');
|
|
@@ -138,5 +138,5 @@ You can use `koffi.reset()` to clear some Koffi internal state such as:
|
|
|
138
138
|
This function is mainly intended for test code, when you execute the same code over and over and you need to reuse type names.
|
|
139
139
|
|
|
140
140
|
```{warning}
|
|
141
|
-
Trying to use a function or a type defined before the reset is undefined behavior and will likely lead to a crash!
|
|
141
|
+
Trying to use a function or a type that was initially defined before the reset is undefined behavior and will likely lead to a crash!
|
|
142
142
|
```
|
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -378,8 +378,8 @@ var require_package = __commonJS({
|
|
|
378
378
|
"build/dist/src/koffi/package.json"(exports2, module2) {
|
|
379
379
|
module2.exports = {
|
|
380
380
|
name: "koffi",
|
|
381
|
-
version: "2.5.
|
|
382
|
-
stable: "2.5.
|
|
381
|
+
version: "2.5.21-beta.1",
|
|
382
|
+
stable: "2.5.20",
|
|
383
383
|
description: "Fast and simple C FFI (foreign function interface) for Node.js",
|
|
384
384
|
keywords: [
|
|
385
385
|
"foreign",
|
|
@@ -413,6 +413,7 @@ var require_package = __commonJS({
|
|
|
413
413
|
chalk: "^4.1.2",
|
|
414
414
|
esbuild: "^0.19.2",
|
|
415
415
|
"ffi-napi": "^4.0.3",
|
|
416
|
+
"ffi-rs": "^1.0.12",
|
|
416
417
|
minimatch: "^5.0.1",
|
|
417
418
|
"node-ssh": "^12.0.3",
|
|
418
419
|
raylib: "^0.9.2",
|
package/src/koffi/CMakeLists.txt
CHANGED
package/src/koffi/src/call.cc
CHANGED
|
@@ -1236,16 +1236,8 @@ void CallData::PopOutArguments()
|
|
|
1236
1236
|
|
|
1237
1237
|
void *CallData::ReserveTrampoline(const FunctionInfo *proto, Napi::Function func)
|
|
1238
1238
|
{
|
|
1239
|
-
if (!instance
|
|
1240
|
-
|
|
1241
|
-
Napi::String::New(env, "Koffi Async Callback Broker"),
|
|
1242
|
-
0, 1, nullptr, nullptr, nullptr,
|
|
1243
|
-
CallData::RelayAsync, &instance->broker) != napi_ok) {
|
|
1244
|
-
LogError("Failed to create async callback broker");
|
|
1245
|
-
return nullptr;
|
|
1246
|
-
}
|
|
1247
|
-
napi_unref_threadsafe_function(env, instance->broker);
|
|
1248
|
-
}
|
|
1239
|
+
if (!InitAsyncBroker(env, instance)) [[unlikely]]
|
|
1240
|
+
return nullptr;
|
|
1249
1241
|
|
|
1250
1242
|
int16_t idx;
|
|
1251
1243
|
{
|
package/src/koffi/src/ffi.cc
CHANGED
|
@@ -1568,6 +1568,32 @@ static Napi::Value UnloadLibrary(const Napi::CallbackInfo &info)
|
|
|
1568
1568
|
return env.Undefined();
|
|
1569
1569
|
}
|
|
1570
1570
|
|
|
1571
|
+
#ifdef _WIN32
|
|
1572
|
+
static HANDLE LoadWindowsLibrary(Napi::Env env, const char16_t *filename)
|
|
1573
|
+
{
|
|
1574
|
+
HANDLE module = LoadLibraryW((LPCWSTR)filename);
|
|
1575
|
+
|
|
1576
|
+
if (!module) {
|
|
1577
|
+
if (GetLastError() == ERROR_BAD_EXE_FORMAT) {
|
|
1578
|
+
int process = GetSelfMachine();
|
|
1579
|
+
int dll = GetDllMachine(filename);
|
|
1580
|
+
|
|
1581
|
+
if (process >= 0 && dll >= 0 && dll != process) {
|
|
1582
|
+
ThrowError<Napi::Error>(env, "Cannot load '%1' DLL in '%2' process",
|
|
1583
|
+
WindowsMachineNames.FindValue(dll, "Unknown"),
|
|
1584
|
+
WindowsMachineNames.FindValue(process, "Unknown"));
|
|
1585
|
+
return nullptr;
|
|
1586
|
+
}
|
|
1587
|
+
}
|
|
1588
|
+
|
|
1589
|
+
ThrowError<Napi::Error>(env, "Failed to load shared library: %1", GetWin32ErrorString());
|
|
1590
|
+
return nullptr;
|
|
1591
|
+
}
|
|
1592
|
+
|
|
1593
|
+
return module;
|
|
1594
|
+
}
|
|
1595
|
+
#endif
|
|
1596
|
+
|
|
1571
1597
|
static Napi::Value LoadSharedLibrary(const Napi::CallbackInfo &info)
|
|
1572
1598
|
{
|
|
1573
1599
|
Napi::Env env = info.Env();
|
|
@@ -1592,12 +1618,10 @@ static Napi::Value LoadSharedLibrary(const Napi::CallbackInfo &info)
|
|
|
1592
1618
|
#ifdef _WIN32
|
|
1593
1619
|
if (info[0].IsString()) {
|
|
1594
1620
|
std::u16string filename = info[0].As<Napi::String>();
|
|
1595
|
-
module =
|
|
1621
|
+
module = LoadWindowsLibrary(env, filename.c_str());
|
|
1596
1622
|
|
|
1597
|
-
if (!module)
|
|
1598
|
-
ThrowError<Napi::Error>(env, "Failed to load shared library: %1", GetWin32ErrorString());
|
|
1623
|
+
if (!module)
|
|
1599
1624
|
return env.Null();
|
|
1600
|
-
}
|
|
1601
1625
|
} else {
|
|
1602
1626
|
module = GetModuleHandle(nullptr);
|
|
1603
1627
|
RG_ASSERT(module);
|
|
@@ -1656,6 +1680,9 @@ static Napi::Value RegisterCallback(const Napi::CallbackInfo &info)
|
|
|
1656
1680
|
Napi::Env env = info.Env();
|
|
1657
1681
|
InstanceData *instance = env.GetInstanceData<InstanceData>();
|
|
1658
1682
|
|
|
1683
|
+
if (!InitAsyncBroker(env, instance)) [[unlikely]]
|
|
1684
|
+
return env.Null();
|
|
1685
|
+
|
|
1659
1686
|
bool has_recv = (info.Length() >= 3 && info[1].IsFunction());
|
|
1660
1687
|
|
|
1661
1688
|
if (info.Length() < 2u + has_recv) {
|
|
@@ -1937,6 +1964,22 @@ InstanceMemory::~InstanceMemory()
|
|
|
1937
1964
|
#endif
|
|
1938
1965
|
}
|
|
1939
1966
|
|
|
1967
|
+
bool InitAsyncBroker(Napi::Env env, InstanceData *instance)
|
|
1968
|
+
{
|
|
1969
|
+
if (!instance->broker) {
|
|
1970
|
+
if (napi_create_threadsafe_function(env, nullptr, nullptr,
|
|
1971
|
+
Napi::String::New(env, "Koffi Async Callback Broker"),
|
|
1972
|
+
0, 1, nullptr, nullptr, nullptr,
|
|
1973
|
+
CallData::RelayAsync, &instance->broker) != napi_ok) {
|
|
1974
|
+
LogError("Failed to create async callback broker");
|
|
1975
|
+
return false;
|
|
1976
|
+
}
|
|
1977
|
+
napi_unref_threadsafe_function(env, instance->broker);
|
|
1978
|
+
}
|
|
1979
|
+
|
|
1980
|
+
return true;
|
|
1981
|
+
}
|
|
1982
|
+
|
|
1940
1983
|
static void RegisterPrimitiveType(Napi::Env env, Napi::Object map, std::initializer_list<const char *> names,
|
|
1941
1984
|
PrimitiveKind primitive, int32_t size, int16_t align, const char *ref = nullptr)
|
|
1942
1985
|
{
|
package/src/koffi/src/ffi.hh
CHANGED
|
@@ -341,4 +341,6 @@ Napi::Value TranslateNormalCall(const Napi::CallbackInfo &info);
|
|
|
341
341
|
Napi::Value TranslateVariadicCall(const Napi::CallbackInfo &info);
|
|
342
342
|
Napi::Value TranslateAsyncCall(const Napi::CallbackInfo &info);
|
|
343
343
|
|
|
344
|
+
bool InitAsyncBroker(Napi::Env env, InstanceData *instance);
|
|
345
|
+
|
|
344
346
|
}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
// Copyright 2023 Niels Martignène <niels.martignene@protonmail.com>
|
|
2
|
+
//
|
|
3
|
+
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
4
|
+
// this software and associated documentation files (the “Software”), to deal in
|
|
5
|
+
// the Software without restriction, including without limitation the rights to use,
|
|
6
|
+
// copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
|
7
|
+
// Software, and to permit persons to whom the Software is furnished to do so,
|
|
8
|
+
// subject to the following conditions:
|
|
9
|
+
//
|
|
10
|
+
// The above copyright notice and this permission notice shall be included in all
|
|
11
|
+
// copies or substantial portions of the Software.
|
|
12
|
+
//
|
|
13
|
+
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND,
|
|
14
|
+
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|
15
|
+
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
16
|
+
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
17
|
+
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
18
|
+
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
19
|
+
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
20
|
+
// OTHER DEALINGS IN THE SOFTWARE.
|
|
21
|
+
|
|
22
|
+
#ifdef _WIN32
|
|
23
|
+
|
|
24
|
+
#include "win32.hh"
|
|
25
|
+
|
|
26
|
+
#ifndef NOMINMAX
|
|
27
|
+
#define NOMINMAX
|
|
28
|
+
#endif
|
|
29
|
+
#ifndef WIN32_LEAN_AND_MEAN
|
|
30
|
+
#define WIN32_LEAN_AND_MEAN
|
|
31
|
+
#endif
|
|
32
|
+
#include <windows.h>
|
|
33
|
+
#include <ntsecapi.h>
|
|
34
|
+
#include <processthreadsapi.h>
|
|
35
|
+
|
|
36
|
+
namespace RG {
|
|
37
|
+
|
|
38
|
+
const HashMap<int, const char *> WindowsMachineNames = {
|
|
39
|
+
{ 0x184, "Alpha AXP, 32-bit" },
|
|
40
|
+
{ 0x284, "Alpha 64" },
|
|
41
|
+
{ 0x1d3, "Matsushita AM33" },
|
|
42
|
+
{ 0x8664, "AMD x64" },
|
|
43
|
+
{ 0x1c0, "ARM little endian" },
|
|
44
|
+
{ 0xaa64, "ARM64 little endian" },
|
|
45
|
+
{ 0x1c4, "ARM Thumb-2 little endian" },
|
|
46
|
+
{ 0x284, "AXP 64" },
|
|
47
|
+
{ 0xebc, "EFI byte code" },
|
|
48
|
+
{ 0x14c, "Intel 386+" },
|
|
49
|
+
{ 0x200, "Intel Itanium" },
|
|
50
|
+
{ 0x6232, "LoongArch 32-bit" },
|
|
51
|
+
{ 0x6264, "LoongArch 64-bit" },
|
|
52
|
+
{ 0x9041, "Mitsubishi M32R little endian" },
|
|
53
|
+
{ 0x266, "MIPS16" },
|
|
54
|
+
{ 0x366, "MIPS with FPU" },
|
|
55
|
+
{ 0x466, "MIPS16 with FPU" },
|
|
56
|
+
{ 0x1f0, "Power PC little endian" },
|
|
57
|
+
{ 0x1f1, "Power PC with FP support" },
|
|
58
|
+
{ 0x166, "MIPS little endian" },
|
|
59
|
+
{ 0x5032, "RISC-V 32-bit" },
|
|
60
|
+
{ 0x5064, "RISC-V 64-bit" },
|
|
61
|
+
{ 0x5128, "RISC-V 128-bit" },
|
|
62
|
+
{ 0x1a2, "Hitachi SH3" },
|
|
63
|
+
{ 0x1a3, "Hitachi SH3 DSP" },
|
|
64
|
+
{ 0x1a6, "Hitachi SH4" },
|
|
65
|
+
{ 0x1a8, "Hitachi SH5" },
|
|
66
|
+
{ 0x1c2, "Thumb" },
|
|
67
|
+
{ 0x169, "MIPS little-endian WCE v2" }
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
// Fails silently on purpose
|
|
71
|
+
static bool ReadAt(HANDLE h, int32_t offset, void *buf, int len)
|
|
72
|
+
{
|
|
73
|
+
OVERLAPPED ov = {};
|
|
74
|
+
DWORD read;
|
|
75
|
+
|
|
76
|
+
ov.Offset = offset & 0x7FFFFFFFu;
|
|
77
|
+
|
|
78
|
+
if (offset < 0)
|
|
79
|
+
return false;
|
|
80
|
+
if (!ReadFile(h, buf, (DWORD)len, &read, &ov))
|
|
81
|
+
return false;
|
|
82
|
+
if (read != len)
|
|
83
|
+
return false;
|
|
84
|
+
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
static int GetFileMachine(HANDLE h, bool check_dll)
|
|
89
|
+
{
|
|
90
|
+
PE_DOS_HEADER dos = {};
|
|
91
|
+
PE_NT_HEADERS nt = {};
|
|
92
|
+
|
|
93
|
+
if (!ReadAt(h, 0, &dos, RG_SIZE(dos)))
|
|
94
|
+
goto generic;
|
|
95
|
+
if (!ReadAt(h, dos.e_lfanew, &nt, RG_SIZE(nt)))
|
|
96
|
+
goto generic;
|
|
97
|
+
|
|
98
|
+
if (dos.e_magic != 0x5A4D) // MZ
|
|
99
|
+
goto generic;
|
|
100
|
+
if (nt.Signature != 0x00004550) // PE\0\0
|
|
101
|
+
goto generic;
|
|
102
|
+
if (check_dll && !(nt.FileHeader.Characteristics & IMAGE_FILE_DLL))
|
|
103
|
+
goto generic;
|
|
104
|
+
|
|
105
|
+
return (int)nt.FileHeader.Machine;
|
|
106
|
+
|
|
107
|
+
generic:
|
|
108
|
+
LogError("Invalid or forbidden %1 file: %2", check_dll ? "DLL" : "executable", GetWin32ErrorString());
|
|
109
|
+
return -1;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
int GetSelfMachine()
|
|
113
|
+
{
|
|
114
|
+
const char *filename = GetApplicationExecutable();
|
|
115
|
+
|
|
116
|
+
HANDLE h;
|
|
117
|
+
if (IsWin32Utf8()) {
|
|
118
|
+
h = CreateFileA(filename, GENERIC_READ,
|
|
119
|
+
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
|
120
|
+
nullptr, OPEN_EXISTING, 0, nullptr);
|
|
121
|
+
} else {
|
|
122
|
+
wchar_t filename_w[4096];
|
|
123
|
+
if (ConvertUtf8ToWin32Wide(filename, filename_w) < 0)
|
|
124
|
+
return -1;
|
|
125
|
+
|
|
126
|
+
h = CreateFileW(filename_w, GENERIC_READ,
|
|
127
|
+
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
|
128
|
+
nullptr, OPEN_EXISTING, 0, nullptr);
|
|
129
|
+
}
|
|
130
|
+
if (h == INVALID_HANDLE_VALUE) {
|
|
131
|
+
LogError("Cannot open '%1': %2", filename, GetWin32ErrorString());
|
|
132
|
+
return -1;
|
|
133
|
+
}
|
|
134
|
+
RG_DEFER { CloseHandle(h); };
|
|
135
|
+
|
|
136
|
+
return GetFileMachine(h, false);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
int GetDllMachine(const char16_t *filename)
|
|
140
|
+
{
|
|
141
|
+
HANDLE h = CreateFileW((LPCWSTR)filename, GENERIC_READ,
|
|
142
|
+
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
|
143
|
+
nullptr, OPEN_EXISTING, 0, nullptr);
|
|
144
|
+
if (h == INVALID_HANDLE_VALUE) {
|
|
145
|
+
LogError("Cannot open '%1': %2", filename, GetWin32ErrorString());
|
|
146
|
+
return -1;
|
|
147
|
+
}
|
|
148
|
+
RG_DEFER { CloseHandle(h); };
|
|
149
|
+
|
|
150
|
+
return GetFileMachine(h, true);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
#endif
|
package/src/koffi/src/win32.hh
CHANGED
|
@@ -27,6 +27,45 @@
|
|
|
27
27
|
|
|
28
28
|
namespace RG {
|
|
29
29
|
|
|
30
|
+
struct PE_DOS_HEADER {
|
|
31
|
+
uint16_t e_magic;
|
|
32
|
+
uint16_t e_cblp;
|
|
33
|
+
uint16_t e_cp;
|
|
34
|
+
uint16_t e_crlc;
|
|
35
|
+
uint16_t e_cparhdr;
|
|
36
|
+
uint16_t e_minalloc;
|
|
37
|
+
uint16_t e_maxalloc;
|
|
38
|
+
uint16_t e_ss;
|
|
39
|
+
uint16_t e_sp;
|
|
40
|
+
uint16_t e_csum;
|
|
41
|
+
uint16_t e_ip;
|
|
42
|
+
uint16_t e_cs;
|
|
43
|
+
uint16_t e_lfarlc;
|
|
44
|
+
uint16_t e_ovno;
|
|
45
|
+
uint16_t e_res[4];
|
|
46
|
+
uint16_t e_oemid;
|
|
47
|
+
uint16_t e_oeminfo;
|
|
48
|
+
uint16_t e_res2[10];
|
|
49
|
+
uint32_t e_lfanew;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
struct PE_FILE_HEADER {
|
|
53
|
+
uint16_t Machine;
|
|
54
|
+
uint16_t NumberOfSections;
|
|
55
|
+
uint32_t TimeDateStamp;
|
|
56
|
+
uint32_t PointerToSymbolTable;
|
|
57
|
+
uint32_t NumberOfSymbols;
|
|
58
|
+
uint16_t SizeOfOptionalHeader;
|
|
59
|
+
uint16_t Characteristics;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
struct PE_NT_HEADERS {
|
|
63
|
+
uint32_t Signature;
|
|
64
|
+
PE_FILE_HEADER FileHeader;
|
|
65
|
+
|
|
66
|
+
// ... OptionalHeader;
|
|
67
|
+
};
|
|
68
|
+
|
|
30
69
|
#if _WIN64
|
|
31
70
|
|
|
32
71
|
struct TEB {
|
|
@@ -70,4 +109,9 @@ static inline TEB *GetTEB()
|
|
|
70
109
|
return teb;
|
|
71
110
|
}
|
|
72
111
|
|
|
112
|
+
extern const HashMap<int, const char *> WindowsMachineNames;
|
|
113
|
+
|
|
114
|
+
int GetSelfMachine();
|
|
115
|
+
int GetDllMachine(const char16_t *filename);
|
|
116
|
+
|
|
73
117
|
}
|