├── atomics ├── atomics.c ├── atomics.h └── x86.c ├── libsys.js ├── shim.js ├── .github └── FUNDING.yml ├── syscall ├── syscall.c ├── syscall.h ├── linux.c └── darwin.c ├── CONTRIBUTING.md ├── .gitignore ├── Dockerfile ├── .npmignore ├── renovate.json ├── demo ├── write.js ├── error.js ├── setTimeout.js ├── call.js ├── signal.js ├── jumper.js └── sigaction.js ├── .travis.yml ├── binding.gyp ├── tsconfig.json ├── test ├── dlsym.test.js ├── main.test.js ├── address.test.js ├── async.test.js ├── cmpxchg.test.js ├── call.test.js └── syscall.test.js ├── package.json ├── call └── call.h ├── LICENSE ├── index.d.ts ├── README.md ├── async └── async.c └── libsys.cc /atomics/atomics.c: -------------------------------------------------------------------------------- 1 | #include "x86.c" 2 | -------------------------------------------------------------------------------- /libsys.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./build/Release/sys.node'); 2 | -------------------------------------------------------------------------------- /shim.js: -------------------------------------------------------------------------------- 1 | global.libsys = require('./build/Release/sys.node'); 2 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: streamich 4 | -------------------------------------------------------------------------------- /syscall/syscall.c: -------------------------------------------------------------------------------- 1 | #ifdef __APPLE__ 2 | #include "darwin.c" 3 | #else 4 | #include "linux.c" 5 | #endif 6 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Linux 4 | 5 | ```bash 6 | docker build -t libsys . 7 | docker run -it --rm -v $(pwd):/libsys libsys /bin/bash 8 | ``` 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | node_modules 3 | /.idea 4 | .nyc_output 5 | coverage 6 | package-lock.json 7 | yarn.lock 8 | test.js 9 | old.cc 10 | build 11 | .vscode/ 12 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:cosmic-20190719 2 | 3 | RUN apt-get update -y 4 | RUN apt-get install -y gcc build-essential 5 | RUN apt-get install -y nodejs 6 | RUN apt-get install -y npm 7 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | test.js 2 | old.cc 3 | *.ts 4 | /typings 5 | example.js 6 | tsd.json 7 | simple.js 8 | node_modules 9 | /test 10 | .idea 11 | .idea/ 12 | .nyc_output 13 | coverage 14 | package-lock.json 15 | yarn.lock 16 | build 17 | -------------------------------------------------------------------------------- /atomics/atomics.h: -------------------------------------------------------------------------------- 1 | static inline int8_t cmpxchg8(int8_t* ptr, int8_t oldval, int8_t newval); 2 | static inline int16_t cmpxchg16(int16_t* ptr, int16_t oldval, int16_t newval); 3 | static inline int32_t cmpxchg32(int32_t* ptr, int32_t oldval, int32_t newval); 4 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "config:base" 4 | ], 5 | "automerge": true, 6 | "pinVersions": false, 7 | "major": { 8 | "automerge": false 9 | }, 10 | "devDependencies": { 11 | "automerge": true, 12 | "pinVersions": true 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /demo/write.js: -------------------------------------------------------------------------------- 1 | const libsys = require('..'); 2 | 3 | const STDOUT = 1; 4 | const isMac = process.platform === 'darwin'; 5 | const SYS_write = isMac ? 0x2000004 : 1; 6 | 7 | const buf = Buffer.from('Hello world\n'); 8 | 9 | libsys.syscall(SYS_write, STDOUT, buf, buf.length); 10 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | os: 2 | - linux 3 | - osx 4 | language: node_js 5 | cache: 6 | directories: 7 | - ~/.npm 8 | notifications: 9 | email: false 10 | node_js: 11 | - '10' 12 | - '8' 13 | after_success: 14 | - npx semantic-release 15 | branches: 16 | except: 17 | - /^v\d+\.\d+\.\d+$/ 18 | -------------------------------------------------------------------------------- /demo/error.js: -------------------------------------------------------------------------------- 1 | const libsys = require('..'); 2 | 3 | const STDOUT = 1; 4 | const isMac = process.platform === 'darwin'; 5 | const SYS_write = isMac ? 0x2000004 : 1; 6 | 7 | const buf = Buffer.from('Hello world....\n'); 8 | 9 | const res = libsys.syscall(SYS_write, 123, buf, buf.length); 10 | console.log('res', res); 11 | -------------------------------------------------------------------------------- /binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "targets": [ 3 | { 4 | "target_name": "libsys", 5 | "include_dirs" : [ 6 | " 2 | 3 | static inline int64_t syscall_6(int64_t num, int64_t arg1, int64_t arg2, int64_t arg3, int64_t arg4, int64_t arg5, int64_t arg6); 4 | static inline int64_t syscall_5(int64_t num, int64_t arg1, int64_t arg2, int64_t arg3, int64_t arg4, int64_t arg5); 5 | static inline int64_t syscall_4(int64_t num, int64_t arg1, int64_t arg2, int64_t arg3, int64_t arg4); 6 | static inline int64_t syscall_3(int64_t num, int64_t arg1, int64_t arg2, int64_t arg3); 7 | static inline int64_t syscall_2(int64_t num, int64_t arg1, int64_t arg2); 8 | static inline int64_t syscall_1(int64_t num, int64_t arg1); 9 | static inline int64_t syscall_0(int64_t num); 10 | -------------------------------------------------------------------------------- /demo/setTimeout.js: -------------------------------------------------------------------------------- 1 | const libsys = require('..'); 2 | const {X64} = require('ass-js'); 3 | 4 | const asm = X64(); 5 | asm._('dd', process.termination_handler[1]); 6 | asm._('dd', process.termination_handler[0]); 7 | asm._('dq', 0); 8 | 9 | const buf = asm.compile(); 10 | 11 | const isMac = process.platform === 'darwin'; 12 | const SYS_sigaction = isMac ? (0x2000000 + 46) : 13; 13 | const SIGINT = 2; 14 | 15 | const ab = libsys.frame(process.new_action, 16) 16 | const b = Buffer.from(ab); 17 | 18 | console.log(b); 19 | 20 | // libsys.syscall(SYS_sigaction, SIGINT, buf, 0); 21 | 22 | setTimeout(() => { 23 | console.log(process.new_action); 24 | console.log('TIMEOUT'); 25 | }, 2000); 26 | -------------------------------------------------------------------------------- /test/dlsym.test.js: -------------------------------------------------------------------------------- 1 | const libsys = require('..'); 2 | 3 | describe('dlsym', function() { 4 | it('method exists', () => { 5 | expect(typeof libsys.dlsym).toBe('function'); 6 | }); 7 | 8 | it('returns an address', () => { 9 | const addr = libsys.dlsym('fcntl'); 10 | expect(addr.length).toBe(2); 11 | expect(typeof addr[0]).toBe('number'); 12 | expect(typeof addr[1]).toBe('number'); 13 | }); 14 | 15 | it('returns correct dlsym function address', () => { 16 | const addr1 = libsys.dlsym('dlsym'); 17 | const addr2 = libsys.__testDlsymAddr() 18 | expect(addr1).toEqual(addr2); 19 | }); 20 | 21 | it('call getpid()', () => { 22 | const addr1 = libsys.dlsym('getpid'); 23 | const pid1 = process.pid; 24 | const [pid2] = libsys.call64(addr1, 0, []); 25 | expect(pid2).toBe(pid1); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "libsys", 3 | "description": "syscall() function for Node.js", 4 | "version": "3.0.0", 5 | "keywords": [ 6 | "syscall", 7 | "system-call", 8 | "kernel", 9 | "exokernel", 10 | "exo-kernel", 11 | "read", 12 | "write", 13 | "open", 14 | "sys", 15 | "system" 16 | ], 17 | "repository": { 18 | "type": "git", 19 | "url": "https://github.com/streamich/libsys" 20 | }, 21 | "main": "./build/Release/sys.node", 22 | "engines": { 23 | "node": ">= 4.4.3" 24 | }, 25 | "gypfile": true, 26 | "devDependencies": { 27 | "@types/node": "22.18.5", 28 | "ass-js": "2.1.1", 29 | "jest": "30.1.3", 30 | "node-addon-api": "8.5.0", 31 | "node-gyp": "11.4.2" 32 | }, 33 | "scripts": { 34 | "test": "jest", 35 | "build": "node-gyp configure && node-gyp rebuild" 36 | }, 37 | "dependencies": { 38 | "nan": "^2.14.0" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /demo/call.js: -------------------------------------------------------------------------------- 1 | const libsys = require('..'); 2 | const {rax, rdi, rsi} = require('ass-js/lib/x86/operand'); 3 | const Code = require('ass-js/lib/x86/x64/code').Code; 4 | 5 | const isMac = process.platform === 'darwin'; 6 | const SYS_mmap = isMac ? (0x2000000 + 197) : 9; 7 | 8 | // Allocate executable memory, returns `ArrayBuffer`. 9 | function alloc(size) { 10 | const flags = 2 /* MAP_PRIVATE */ | (isMac ? 4096 : 32 /* MAP_ANONYMOUS */); 11 | const protnum = 1 | 2 | 4; // Read, write and execute; 12 | const addr = libsys.syscall64(SYS_mmap, 0, size, protnum, flags, -1, 0); 13 | return libsys.frame(addr, size); 14 | } 15 | 16 | var _ = new Code; 17 | 18 | _._('mov', [rax, -9]); 19 | _._('ret'); 20 | 21 | const code = _.compile(); 22 | const ab = alloc(code.length); 23 | const uint8 = new Uint8Array(ab); 24 | 25 | for (let i = 0; i < code.length; i++) uint8[i] = code[i]; 26 | 27 | const result = libsys.call(ab, 0, []); 28 | 29 | console.log('result', result); 30 | -------------------------------------------------------------------------------- /call/call.h: -------------------------------------------------------------------------------- 1 | typedef int64_t (*callback)(); 2 | typedef int64_t (*callback1)(int64_t arg1); 3 | typedef int64_t (*callback2)(int64_t arg1, int64_t arg2); 4 | typedef int64_t (*callback3)(int64_t arg1, int64_t arg2, int64_t arg3); 5 | typedef int64_t (*callback4)(int64_t arg1, int64_t arg2, int64_t arg3, int64_t arg4); 6 | typedef int64_t (*callback5)(int64_t arg1, int64_t arg2, int64_t arg3, int64_t arg4, int64_t arg5); 7 | typedef int64_t (*callback6)(int64_t arg1, int64_t arg2, int64_t arg3, int64_t arg4, int64_t arg5, int64_t arg6); 8 | typedef int64_t (*callback7)(int64_t arg1, int64_t arg2, int64_t arg3, int64_t arg4, int64_t arg5, int64_t arg6, int64_t arg7); 9 | typedef int64_t (*callback8)(int64_t arg1, int64_t arg2, int64_t arg3, int64_t arg4, int64_t arg5, int64_t arg6, int64_t arg7, int64_t arg8); 10 | typedef int64_t (*callback9)(int64_t arg1, int64_t arg2, int64_t arg3, int64_t arg4, int64_t arg5, int64_t arg6, int64_t arg7, int64_t arg8, int64_t arg9); 11 | typedef int64_t (*callback10)(int64_t arg1, int64_t arg2, int64_t arg3, int64_t arg4, int64_t arg5, int64_t arg6, int64_t arg7, int64_t arg8, int64_t arg9, int64_t arg10); 12 | -------------------------------------------------------------------------------- /atomics/x86.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | static inline int8_t atomic_increment(int8_t* ptr, int8_t increment) { 4 | __asm__ volatile("lock; xadd %0, %1" 5 | : "+r" (increment), "+m" (*ptr) // input + output 6 | : // No input-only 7 | : "memory" 8 | ); 9 | return increment; 10 | } 11 | 12 | static inline int8_t cmpxchg8(int8_t* ptr, int8_t oldval, int8_t newval) { 13 | int8_t out; 14 | __asm__ __volatile__ ("lock; cmpxchg %2, %1;" 15 | : "=a" (out), "+m" (*(volatile int8_t*) ptr) 16 | : "r" (newval), "0" (oldval) 17 | : "memory"); 18 | return out; 19 | } 20 | 21 | static inline int16_t cmpxchg16(int16_t* ptr, int16_t oldval, int16_t newval) { 22 | int16_t out; 23 | __asm__ __volatile__ ("lock; cmpxchg %2, %1;" 24 | : "=a" (out), "+m" (*(volatile int16_t*) ptr) 25 | : "r" (newval), "0" (oldval) 26 | : "memory"); 27 | return out; 28 | } 29 | 30 | static inline int32_t cmpxchg32(int32_t* ptr, int32_t oldval, int32_t newval) { 31 | int32_t out; 32 | __asm__ __volatile__ ("lock; cmpxchg %2, %1;" 33 | : "=a" (out), "+m" (*(volatile int32_t*) ptr) 34 | : "r" (newval), "0" (oldval) 35 | : "memory"); 36 | return out; 37 | } 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /demo/signal.js: -------------------------------------------------------------------------------- 1 | const libsys = require('..'); 2 | const {X64} = require('ass-js'); 3 | 4 | const isMac = process.platform === 'darwin'; 5 | const SYS_mmap = isMac ? (0x2000000 + 197) : 9; 6 | const SYS_write = isMac ? 0x2000004 : 1; 7 | const STDOUT = 1; 8 | 9 | // Allocate executable memory, returns `ArrayBuffer`. 10 | function alloc(size) { 11 | const flags = 2 /* MAP_PRIVATE */ | (isMac ? 4096 : 32 /* MAP_ANONYMOUS */); 12 | const protnum = 1 | 2 | 4; // Read, write and execute; 13 | const addr = libsys.syscall64(SYS_mmap, 0, size, protnum, flags, -1, 0); 14 | return libsys.frame(addr, size); 15 | } 16 | 17 | const asm = X64(); 18 | 19 | const tpl = _ => { 20 | const str = _.lbl('string'); 21 | 22 | // .text 23 | _('push', 'rdi'); 24 | _('push', 'rsi'); 25 | _('push', 'rdx'); 26 | 27 | _('mov', ['rax', SYS_write], 64); 28 | _('mov', ['rdi', STDOUT], 64); 29 | _('lea', ['rsi', _('rip').disp(str)], 64); 30 | _('mov', ['rdx', 14], 64); 31 | _('syscall'); 32 | 33 | _('pop', 'rdx'); 34 | _('pop', 'rsi'); 35 | _('pop', 'rdi'); 36 | _('ret'); 37 | 38 | // .data 39 | _.insert(str); 40 | _('db', 'Hello World!\n'); 41 | }; 42 | 43 | asm.code(tpl); 44 | // console.log(String(asm)); 45 | const code = asm.compile([]); 46 | console.log(String(asm)); 47 | 48 | const ab = alloc(code.length); 49 | const uint8 = new Uint8Array(ab); 50 | 51 | for (let i = 0; i < code.length; i++) uint8[i] = code[i]; 52 | 53 | console.log('MY PID IS: ', process.pid); 54 | 55 | setTimeout(() => { 56 | let result = libsys.call(ab, 0, []); 57 | console.log('DONE', result); 58 | }, 3000); 59 | -------------------------------------------------------------------------------- /demo/jumper.js: -------------------------------------------------------------------------------- 1 | const libsys = require('..'); 2 | const {X64} = require('ass-js'); 3 | 4 | const isMac = process.platform === 'darwin'; 5 | const SYS_mmap = isMac ? (0x2000000 + 197) : 9; 6 | 7 | // Allocate executable memory, returns `ArrayBuffer`. 8 | function alloc(size) { 9 | const flags = 2 /* MAP_PRIVATE */ | (isMac ? 4096 : 32 /* MAP_ANONYMOUS */); 10 | const protnum = 1 | 2 | 4; // Read, write and execute; 11 | const addr = libsys.syscall64(SYS_mmap, 0, size, protnum, flags, -1, 0); 12 | return libsys.frame(addr, size); 13 | } 14 | 15 | process.jumpers[22] = (a, b) => { console.log('lol, 22 called', a, b); }; 16 | 17 | const asm = X64(); 18 | 19 | const tpl = _ => { 20 | const address = _('label', 'jump_address'); 21 | _('dq', [process.jumperAddress]); 22 | 23 | _('push', 'rdi'); 24 | _('push', 'rsi'); 25 | _('push', 'rdx'); 26 | 27 | _('mov', ['rdi', 22], 64); 28 | _('mov', ['rsi', 12], 64); 29 | _('mov', ['rdx', 8], 64); 30 | 31 | _('call', [_('rip').disp(address)], 64); 32 | 33 | _('pop', 'rdx'); 34 | _('pop', 'rsi'); 35 | _('pop', 'rdi'); 36 | _('ret'); 37 | }; 38 | 39 | asm.code(tpl); 40 | console.log(String(asm)); 41 | const code = asm.compile([]); 42 | console.log(String(asm)); 43 | 44 | const ab = alloc(code.length); 45 | const uint8 = new Uint8Array(ab); 46 | 47 | for (let i = 0; i < code.length; i++) uint8[i] = code[i]; 48 | 49 | console.log('MY PID IS: ', process.pid); 50 | 51 | setTimeout(() => { 52 | let result = libsys.call(ab, 8, []); 53 | console.log('result', result); 54 | 55 | setTimeout(() => { 56 | result = libsys.call(ab, 8, []); 57 | console.log('result', result); 58 | console.log('END'); 59 | }, 3000); 60 | }, 3000); 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /test/main.test.js: -------------------------------------------------------------------------------- 1 | const libsys = require('..'); 2 | 3 | describe('libsys', function() { 4 | it('returns expected API', () => { 5 | expect(typeof libsys.syscall).toBe('function'); 6 | expect(typeof libsys.syscall64).toBe('function'); 7 | expect(typeof libsys.syscall_0).toBe('function'); 8 | expect(typeof libsys.syscall_1).toBe('function'); 9 | expect(typeof libsys.syscall_2).toBe('function'); 10 | expect(typeof libsys.syscall_3).toBe('function'); 11 | expect(typeof libsys.syscall_4).toBe('function'); 12 | expect(typeof libsys.syscall_5).toBe('function'); 13 | expect(typeof libsys.syscall_6).toBe('function'); 14 | expect(typeof libsys.syscall64_0).toBe('function'); 15 | expect(typeof libsys.syscall64_1).toBe('function'); 16 | expect(typeof libsys.syscall64_2).toBe('function'); 17 | expect(typeof libsys.syscall64_3).toBe('function'); 18 | expect(typeof libsys.syscall64_4).toBe('function'); 19 | expect(typeof libsys.syscall64_5).toBe('function'); 20 | expect(typeof libsys.syscall64_6).toBe('function'); 21 | 22 | expect(typeof libsys.frame).toBe('function'); 23 | 24 | expect(typeof libsys.call).toBe('function'); 25 | expect(typeof libsys.call64).toBe('function'); 26 | expect(typeof libsys.call_0).toBe('function'); 27 | expect(typeof libsys.call_1).toBe('function'); 28 | expect(typeof libsys.call64_0).toBe('function'); 29 | expect(typeof libsys.call64_1).toBe('function'); 30 | 31 | expect(typeof libsys.getAddress).toBe('function'); 32 | expect(typeof libsys.getAddressArrayBuffer).toBe('function'); 33 | expect(typeof libsys.getAddressTypedArray).toBe('function'); 34 | expect(typeof libsys.getAddressBuffer).toBe('function'); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | declare const Buffer 2 | export type TSyscallArg = number|string|Buffer; 3 | export function syscall(cmd: number, arg1?: TSyscallArg, arg2?: TSyscallArg, arg3?: TSyscallArg, arg4?: TSyscallArg, arg5?: TSyscallArg, arg6?: TSyscallArg): number; 4 | export function syscall64(cmd: number, arg1?: TSyscallArg, arg2?: TSyscallArg, arg3?: TSyscallArg, arg4?: TSyscallArg, arg5?: TSyscallArg, arg6?: TSyscallArg): [number, number]; 5 | 6 | export function getAddress (buf: Buffer | ArrayBuffer): [number, number]; 7 | 8 | export function malloc(addr: number, size: number): ArrayBuffer; 9 | export function malloc64(addr_lo: number, addr_hi: number, size: number): ArrayBuffer; 10 | export function call(addr: number, offset: number, args: number[]); 11 | export function call64(addr: [number, number], offset: number, args: number[]); 12 | export function errno(): number; 13 | 14 | export function dlsym(symbol: string): [number, number]; 15 | 16 | export function cmpxchg8 (ptr: [number, number], oldValue: number, newValue: number); 17 | export function cmpxchg8 (ptr: [number, number, number], oldValue: number, newValue: number); 18 | export function cmpxchg8 (buf: ArrayBuffer, oldValue: number, newValue: number); 19 | export function cmpxchg8 (buf: Buffer, oldValue: number, newValue: number); 20 | 21 | export function cmpxchg16 (ptr: [number, number], oldValue: number, newValue: number); 22 | export function cmpxchg16 (ptr: [number, number, number], oldValue: number, newValue: number); 23 | export function cmpxchg16 (buf: ArrayBuffer, oldValue: number, newValue: number); 24 | export function cmpxchg16 (buf: Buffer, oldValue: number, newValue: number); 25 | 26 | export function cmpxchg32 (ptr: [number, number], oldValue: number, newValue: number); 27 | export function cmpxchg32 (ptr: [number, number, number], oldValue: number, newValue: number); 28 | export function cmpxchg32 (buf: ArrayBuffer, oldValue: number, newValue: number); 29 | export function cmpxchg32 (buf: Buffer, oldValue: number, newValue: number); 30 | 31 | export function async (); 32 | -------------------------------------------------------------------------------- /demo/sigaction.js: -------------------------------------------------------------------------------- 1 | const libsys = require('..'); 2 | const {X64} = require('ass-js'); 3 | 4 | const isMac = process.platform === 'darwin'; 5 | const SYS_mmap = isMac ? (0x2000000 + 197) : 9; 6 | const SYS_sigaction = isMac ? (0x2000000 + 46) : 13; 7 | const SYS_write = isMac ? 0x2000004 : 1; 8 | const SYS_exit = isMac ? 0x2000001 : 60; 9 | const SIGINT = 2; 10 | const STDOUT = 1; 11 | 12 | // Allocate executable memory, returns `ArrayBuffer`. 13 | function alloc(size) { 14 | const flags = 2 /* MAP_PRIVATE */ | (isMac ? 4096 : 32 /* MAP_ANONYMOUS */); 15 | const protnum = 1 | 2 | 4; // Read, write and execute; 16 | const addr = libsys.syscall64(SYS_mmap, 0, size, protnum, flags, -1, 0); 17 | return libsys.frame(addr, size); 18 | } 19 | 20 | libsys.jumpers[33] = (a, b) => { console.log('JUMPER 33 CALLED', a, b); }; 21 | 22 | const code = (template) => { 23 | const asm = X64(); 24 | asm.code(template); 25 | const code = asm.compile([]); 26 | console.log(String(asm)); 27 | 28 | const ab = alloc(code.length); 29 | const uint8 = new Uint8Array(ab); 30 | 31 | for (let i = 0; i < code.length; i++) uint8[i] = code[i]; 32 | 33 | return ab; 34 | }; 35 | 36 | const abHello = code(_ => { 37 | const str = _.lbl('string'); 38 | 39 | // .text 40 | _('push', 'rdi'); 41 | _('push', 'rsi'); 42 | _('push', 'rdx'); 43 | 44 | _('mov', ['rax', SYS_write], 64); 45 | _('mov', ['rdi', STDOUT], 64); 46 | _('lea', ['rsi', _('rip').disp(str)], 64); 47 | _('mov', ['rdx', 14], 64); 48 | _('syscall'); 49 | 50 | // _('mov', ['rdi', 'rax']); 51 | // _('mov', ['rax', SYS_exit]); 52 | // _('syscall'); 53 | 54 | _('pop', 'rdx'); 55 | _('pop', 'rsi'); 56 | _('pop', 'rdi'); 57 | _('ret'); 58 | 59 | // .data 60 | _.insert(str); 61 | _('db', 'Hello World!\n'); 62 | }); 63 | 64 | const trampoline = code(_ => { 65 | _('mov', ['rdi', 33], 64); 66 | _('mov', ['rsi', 0], 64); 67 | _('mov', ['rdx', 0], 64); 68 | _('call', [_('rip').disp(address)], 64); 69 | 70 | _('dq', [process.jumperAddress]); 71 | }); 72 | 73 | // 00 80 4e 02 01 00 00 00 74 | // 00 00 00 00 00 00 00 00 75 | // 89 00 a5 00 01 00 00 00 76 | // ff fe fe ff 00 00 00 00 77 | console.log('ADDR', libsys.getAddress(abHello)); 78 | 79 | const nas = code(_ => { 80 | _('dq', [libsys.getAddress(abHello)]); 81 | _('dq', [0]); 82 | }); 83 | 84 | libsys.sigaction(SIGINT, nas, 0); 85 | 86 | setTimeout(() => { 87 | console.log('END'); 88 | 89 | }, 5000); 90 | -------------------------------------------------------------------------------- /test/address.test.js: -------------------------------------------------------------------------------- 1 | const libsys = require('..'); 2 | 3 | describe('address', function() { 4 | describe('.getAddressArrayBuffer()', function() { 5 | it('Returns 2-tuple representing a pointer', function() { 6 | const ab = new ArrayBuffer(10); 7 | const tuple = libsys.getAddressArrayBuffer(ab); 8 | 9 | expect(Array.isArray(tuple)).toBe(true); 10 | expect(tuple.length).toBe(2); 11 | expect(typeof tuple[0]).toBe('number'); 12 | expect(typeof tuple[1]).toBe('number'); 13 | }); 14 | }); 15 | 16 | describe('.getAddressTypedArray()', function() { 17 | it('Returns 2-tuple representing a pointer', function() { 18 | const ab = new ArrayBuffer(10); 19 | const uint8 = new Uint8Array(ab); 20 | const tuple = libsys.getAddressTypedArray(uint8); 21 | 22 | expect(Array.isArray(tuple)).toBe(true); 23 | expect(tuple.length).toBe(2); 24 | expect(typeof tuple[0]).toBe('number'); 25 | expect(typeof tuple[1]).toBe('number'); 26 | }); 27 | 28 | it('is same address as ArrayBuffer', function() { 29 | const ab = new ArrayBuffer(10); 30 | const uint8 = new Uint8Array(ab); 31 | 32 | const tuple1 = libsys.getAddressArrayBuffer(ab); 33 | const tuple2 = libsys.getAddressTypedArray(uint8); 34 | 35 | expect(tuple1).toEqual(tuple2); 36 | }); 37 | }); 38 | 39 | describe('.getAddressTypedBuffer()', function() { 40 | it('Returns 2-tuple representing a pointer', function() { 41 | const buf = Buffer.from('foobar'); 42 | const tuple = libsys.getAddressBuffer(buf); 43 | 44 | expect(Array.isArray(tuple)).toBe(true); 45 | expect(tuple.length).toBe(2); 46 | expect(typeof tuple[0]).toBe('number'); 47 | expect(typeof tuple[1]).toBe('number'); 48 | }); 49 | 50 | it('is same address as TypedArray', function() { 51 | const buf = Buffer.from('foobar'); 52 | 53 | const tuple1 = libsys.getAddressTypedArray(buf); 54 | const tuple2 = libsys.getAddressBuffer(buf); 55 | 56 | expect(tuple1).toEqual(tuple2); 57 | }); 58 | }); 59 | 60 | describe('.getAddress()', () => { 61 | it('returns same address as corresponding methods', () => { 62 | const ab = new ArrayBuffer(1); 63 | expect(libsys.getAddress(ab)).toEqual(libsys.getAddressArrayBuffer(ab)); 64 | 65 | const uint8 = new Uint8Array(ab); 66 | expect(libsys.getAddress(uint8)).toEqual(libsys.getAddressTypedArray(uint8)); 67 | 68 | const buf = Buffer.from('foo'); 69 | expect(libsys.getAddress(buf)).toEqual(libsys.getAddressBuffer(buf)); 70 | }); 71 | }); 72 | }); 73 | -------------------------------------------------------------------------------- /syscall/linux.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // Note: RCX and R11 are clobbered by Intel, see SYSCALL mnemonic manual. 4 | 5 | static inline int64_t syscall_6(int64_t num, int64_t arg1, int64_t arg2, int64_t arg3, int64_t arg4, int64_t arg5, int64_t arg6) { 6 | int64_t result; 7 | 8 | __asm__ __volatile__ ( 9 | "movq %5, %%r10;\n" 10 | "movq %6, %%r8;\n" 11 | "movq %7, %%r9;\n" 12 | "syscall;\n" 13 | : "=a" (result) 14 | : "a" (num), "D" (arg1), "S" (arg2), "d" (arg3), "r" (arg4), "r" (arg5), "r" (arg6) 15 | : "%r10", "%r8", "%r9", "%rcx", "%r11" 16 | ); 17 | 18 | return result; 19 | } 20 | 21 | static inline int64_t syscall_5(int64_t num, int64_t arg1, int64_t arg2, int64_t arg3, int64_t arg4, int64_t arg5) { 22 | int64_t result; 23 | 24 | __asm__ __volatile__ ( 25 | "movq %5, %%r10;\n" 26 | "movq %6, %%r8;\n" 27 | "syscall;\n" 28 | : "=a" (result) 29 | : "a" (num), "D" (arg1), "S" (arg2), "d" (arg3), "r" (arg4), "r" (arg5) 30 | : "%r10", "%r8", "%rcx", "%r11" 31 | ); 32 | 33 | return result; 34 | } 35 | 36 | static inline int64_t syscall_4(int64_t num, int64_t arg1, int64_t arg2, int64_t arg3, int64_t arg4) { 37 | int64_t result; 38 | 39 | __asm__ __volatile__ ( 40 | "movq %5, %%r10;\n" 41 | "syscall;\n" 42 | : "=a" (result) 43 | : "a" (num), "D" (arg1), "S" (arg2), "d" (arg3), "r" (arg4) 44 | : "%r10", "%rcx", "%r11" 45 | ); 46 | 47 | return result; 48 | } 49 | 50 | static inline int64_t syscall_3(int64_t num, int64_t arg1, int64_t arg2, int64_t arg3) { 51 | int64_t result; 52 | 53 | __asm__ __volatile__ ( 54 | "syscall;\n" 55 | : "=a" (result) 56 | : "a" (num), "D" (arg1), "S" (arg2), "d" (arg3) 57 | : "%rcx", "%r11" 58 | ); 59 | 60 | return result; 61 | } 62 | 63 | static inline int64_t syscall_2(int64_t num, int64_t arg1, int64_t arg2) { 64 | int64_t result; 65 | 66 | __asm__ __volatile__ ( 67 | "syscall;\n" 68 | : "=a" (result) 69 | : "a" (num), "D" (arg1), "S" (arg2) 70 | : "%rcx", "%r11" 71 | ); 72 | 73 | return result; 74 | } 75 | 76 | static inline int64_t syscall_1(int64_t num, int64_t arg1) { 77 | int64_t result; 78 | 79 | __asm__ __volatile__ ( 80 | "syscall;\n" 81 | : "=a" (result) 82 | : "a" (num), "D" (arg1) 83 | : "%rcx", "%r11" 84 | ); 85 | 86 | return result; 87 | } 88 | 89 | static inline int64_t syscall_0(int64_t num) { 90 | int64_t result; 91 | 92 | __asm__ __volatile__ ( 93 | "syscall;\n" 94 | : "=a" (result) 95 | : "a" (num) 96 | : "%rcx", "%r11" 97 | ); 98 | 99 | return result; 100 | } 101 | -------------------------------------------------------------------------------- /test/async.test.js: -------------------------------------------------------------------------------- 1 | const {async, getAddress, dlsym} = require('..'); 2 | 3 | const {pid} = process; 4 | const SYS_getpid = (0x2000000) + 20; 5 | 6 | xdescribe('async', function() { 7 | it('method exists', () => { 8 | expect(typeof async).toBe('function'); 9 | }); 10 | 11 | for (let i = 1; i < 7; i++) { 12 | it(`can execute getpid with ${i} threads`, async () => { 13 | // console.log('PID', pid); 14 | const numThreads = i; // Number of threads to spin up. 15 | const emptyRecord = Buffer.alloc(2 * 8); // Create empty call record, where threads are pointed initially. 16 | emptyRecord.writeInt8(-1, 0); // Tell threads that this record is already in use. 17 | 18 | const result = async(emptyRecord, numThreads); 19 | expect(typeof result).toBe('number'); 20 | // console.log('result', result); 21 | 22 | 23 | const recordGetpid = Buffer.alloc(3 * 8, 0); 24 | recordGetpid.writeInt32LE(SYS_getpid, 2 * 8); 25 | 26 | // console.log(emptyRecord); 27 | // console.log(recordGetpid); 28 | 29 | const addr = getAddress(recordGetpid); 30 | // console.log(addr); 31 | 32 | emptyRecord.writeInt32LE(addr[0], 8); 33 | emptyRecord.writeInt32LE(addr[1], 8 + 4); 34 | 35 | // console.log(emptyRecord); 36 | // console.log(recordGetpid); 37 | 38 | emptyRecord.writeInt8(1, 2); 39 | 40 | // console.log(emptyRecord); 41 | // console.log(recordGetpid); 42 | 43 | await new Promise(r => setTimeout(r, 10)); 44 | 45 | // console.log(emptyRecord); 46 | // console.log(recordGetpid); 47 | 48 | const res = recordGetpid.readInt32LE(2 * 8); 49 | // console.log('result', res); 50 | expect(res).toBe(pid); 51 | 52 | const exitRecord = Buffer.alloc(2 * 8); 53 | exitRecord.writeInt8(2, 1); 54 | // console.log(exitRecord); 55 | const exitAddr = getAddress(exitRecord); 56 | recordGetpid.writeInt32LE(exitAddr[0], 8); 57 | recordGetpid.writeInt32LE(exitAddr[1], 8 + 4); 58 | recordGetpid.writeInt8(1, 2); 59 | 60 | await new Promise(r => setTimeout(r, 5)); 61 | 62 | const numExited = exitRecord.readInt8(3); 63 | // console.log('numExited', emptyRecord.readInt8(3), recordGetpid.readInt8(3), numExited); 64 | }); 65 | } 66 | 67 | test('can execute a call', async () => { 68 | const addr = dlsym('getpid'); 69 | const numThreads = 2; // Number of threads to spin up. 70 | const emptyRecord = Buffer.alloc(2 * 8); // Create empty call record, where threads are pointed initially. 71 | emptyRecord.writeInt8(-1, 0); // Tell threads that this record is already in use. 72 | 73 | async(emptyRecord, numThreads); 74 | 75 | 76 | const recordGetpid = Buffer.alloc(4 * 8, 0); 77 | recordGetpid.writeInt8(1, 1); // set TYPE_CALL 78 | recordGetpid.writeInt32LE(addr[0], 2 * 8); 79 | recordGetpid.writeInt32LE(addr[1], 2 * 8 + 4); 80 | 81 | const nextAddr = getAddress(recordGetpid); 82 | emptyRecord.writeInt32LE(nextAddr[0], 8); 83 | emptyRecord.writeInt32LE(nextAddr[1], 8 + 4); 84 | emptyRecord.writeInt8(1, 2); 85 | 86 | await new Promise(r => setTimeout(r, 10)); 87 | const res = recordGetpid.readInt32LE(2 * 8); 88 | expect(res).toBe(pid); 89 | }); 90 | }); 91 | -------------------------------------------------------------------------------- /syscall/darwin.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // Note: RCX and R11 are clobbered by Intel, see SYSCALL mnemonic manual. 4 | 5 | #define CARRY_FLAG_BIT 1 6 | #define RETURN_SYSCALL_RESULT(result, flags) return (flags & CARRY_FLAG_BIT) ? -result : result; 7 | 8 | static inline int64_t syscall_6(int64_t num, int64_t arg1, int64_t arg2, int64_t arg3, int64_t arg4, int64_t arg5, int64_t arg6) { 9 | int64_t result; 10 | int64_t flags; 11 | 12 | __asm__ __volatile__ ( 13 | "movq %6, %%r10;\n" 14 | "movq %7, %%r8;\n" 15 | "movq %8, %%r9;\n" 16 | "syscall;\n" 17 | "movq %%r11, %1;\n" 18 | : "=a" (result), "=r" (flags) 19 | : "a" (num), "D" (arg1), "S" (arg2), "d" (arg3), "r" (arg4), "r" (arg5), "r" (arg6) 20 | : "%r10", "%r8", "%r9", "%rcx", "%r11" 21 | ); 22 | 23 | RETURN_SYSCALL_RESULT(result, flags); 24 | } 25 | 26 | static inline int64_t syscall_5(int64_t num, int64_t arg1, int64_t arg2, int64_t arg3, int64_t arg4, int64_t arg5) { 27 | int64_t result; 28 | int64_t flags; 29 | 30 | __asm__ __volatile__ ( 31 | "movq %6, %%r10;\n" 32 | "movq %7, %%r8;\n" 33 | "syscall;\n" 34 | "movq %%r11, %1;\n" 35 | : "=a" (result), "=r" (flags) 36 | : "a" (num), "D" (arg1), "S" (arg2), "d" (arg3), "r" (arg4), "r" (arg5) 37 | : "%r10", "%r8", "%rcx", "%r11" 38 | ); 39 | 40 | RETURN_SYSCALL_RESULT(result, flags); 41 | } 42 | 43 | static inline int64_t syscall_4(int64_t num, int64_t arg1, int64_t arg2, int64_t arg3, int64_t arg4) { 44 | int64_t result; 45 | int64_t flags; 46 | 47 | __asm__ __volatile__ ( 48 | "movq %6, %%r10;\n" 49 | "syscall;\n" 50 | "movq %%r11, %1;\n" 51 | : "=a" (result), "=r" (flags) 52 | : "a" (num), "D" (arg1), "S" (arg2), "d" (arg3), "r" (arg4) 53 | : "%r10", "%rcx", "%r11" 54 | ); 55 | 56 | RETURN_SYSCALL_RESULT(result, flags); 57 | } 58 | 59 | static inline int64_t syscall_3(int64_t num, int64_t arg1, int64_t arg2, int64_t arg3) { 60 | int64_t result; 61 | int64_t flags; 62 | 63 | __asm__ __volatile__ ( 64 | "syscall;\n" 65 | "movq %%r11, %1;\n" 66 | : "=a" (result), "=r" (flags) 67 | : "a" (num), "D" (arg1), "S" (arg2), "d" (arg3) 68 | : "%rcx", "%r11" 69 | ); 70 | 71 | RETURN_SYSCALL_RESULT(result, flags); 72 | } 73 | 74 | static inline int64_t syscall_2(int64_t num, int64_t arg1, int64_t arg2) { 75 | int64_t result; 76 | int64_t flags; 77 | 78 | __asm__ __volatile__ ( 79 | "syscall;\n" 80 | "movq %%r11, %1;\n" 81 | : "=a" (result), "=r" (flags) 82 | : "a" (num), "D" (arg1), "S" (arg2) 83 | : "%rcx", "%r11" 84 | ); 85 | 86 | RETURN_SYSCALL_RESULT(result, flags); 87 | } 88 | 89 | static inline int64_t syscall_1(int64_t num, int64_t arg1) { 90 | int64_t result; 91 | int64_t flags; 92 | 93 | __asm__ __volatile__ ( 94 | "syscall;\n" 95 | "movq %%r11, %1;\n" 96 | : "=a" (result), "=r" (flags) 97 | : "a" (num), "D" (arg1) 98 | : "%rcx", "%r11" 99 | ); 100 | 101 | RETURN_SYSCALL_RESULT(result, flags); 102 | } 103 | 104 | static inline int64_t syscall_0(int64_t num) { 105 | int64_t result; 106 | int64_t flags; 107 | 108 | __asm__ __volatile__ ( 109 | "syscall;\n" 110 | "movq %%r11, %1;\n" 111 | : "=a" (result), "=r" (flags) 112 | : "a" (num) 113 | : "%rcx", "%r11" 114 | ); 115 | 116 | RETURN_SYSCALL_RESULT(result, flags); 117 | } 118 | -------------------------------------------------------------------------------- /test/cmpxchg.test.js: -------------------------------------------------------------------------------- 1 | const {cmpxchg8, cmpxchg16, cmpxchg32, getAddress} = require('..'); 2 | 3 | describe('cmpxchg32', function() { 4 | it('method exists', () => { 5 | expect(typeof cmpxchg32).toBe('function'); 6 | }); 7 | 8 | it('can exchange value', () => { 9 | const buf = Buffer.alloc(4, 0); 10 | expect(buf.readInt32LE()).toBe(0); 11 | cmpxchg32(buf, 0, 3); 12 | expect(buf.readInt32LE()).toBe(3); 13 | }); 14 | 15 | it('can exchange value multiple times', () => { 16 | const buf = Buffer.alloc(4, 0); 17 | expect(buf.readInt32LE()).toBe(0); 18 | cmpxchg32(buf, 0, 3); 19 | expect(buf.readInt32LE()).toBe(3); 20 | cmpxchg32(buf, 3, 2); 21 | expect(buf.readInt32LE()).toBe(2); 22 | cmpxchg32(buf, 2, 7); 23 | expect(buf.readInt32LE()).toBe(7); 24 | }); 25 | 26 | it('does not exchange value if test value does not match', () => { 27 | const buf = Buffer.alloc(4, 0); 28 | expect(buf.readInt32LE()).toBe(0); 29 | cmpxchg32(buf, 1, 3); 30 | expect(buf.readInt32LE()).toBe(0); 31 | }); 32 | 33 | it('does not exchange value if test value does not match - 2', () => { 34 | const buf = Buffer.alloc(4, 0); 35 | expect(buf.readInt32LE()).toBe(0); 36 | cmpxchg32(buf, 0, 1); 37 | cmpxchg32(buf, 1, 2); 38 | cmpxchg32(buf, 2, 3); 39 | cmpxchg32(buf, 2, 5); 40 | expect(buf.readInt32LE()).toBe(3); 41 | }); 42 | 43 | it('can exchange value at address', () => { 44 | const buf = Buffer.alloc(4, 0); 45 | const addr = getAddress(buf); 46 | expect(buf.readInt32LE()).toBe(0); 47 | cmpxchg32(addr, 0, 3); 48 | expect(buf.readInt32LE(0)).toBe(3); 49 | }); 50 | 51 | it('can exchange value at address and offset', () => { 52 | const buf = Buffer.alloc(8, 0); 53 | const addr = [...getAddress(buf), 4]; 54 | 55 | expect(buf.readInt32LE(0)).toBe(0); 56 | expect(buf.readInt32LE(4)).toBe(0); 57 | 58 | cmpxchg32(addr, 0, 10); 59 | 60 | expect(buf.readInt32LE(0)).toBe(0); 61 | expect(buf.readInt32LE(4)).toBe(10); 62 | }); 63 | 64 | it('returns value in memory on success', () => { 65 | const buf = Buffer.alloc(4, 0); 66 | buf.writeInt32LE(1); 67 | const res = cmpxchg32(buf, 1, 10); 68 | expect(res).toBe(1); 69 | }); 70 | 71 | it('returns value in memory on failure', () => { 72 | const buf = Buffer.alloc(4, 0); 73 | buf.writeInt32LE(1); 74 | const res = cmpxchg32(buf, 2, 10); 75 | expect(res).toBe(1); 76 | }); 77 | }); 78 | 79 | describe('cmpxchg16', function() { 80 | it('method exists', () => { 81 | expect(typeof cmpxchg16).toBe('function'); 82 | }); 83 | 84 | it('can exchange value', () => { 85 | const buf = Buffer.alloc(2, 0); 86 | expect(buf.readInt16LE()).toBe(0); 87 | cmpxchg16(buf, 0, 1); 88 | expect(buf.readInt16LE()).toBe(1); 89 | cmpxchg16(buf, 1, -1); 90 | cmpxchg16(buf, 1, 6); 91 | expect(buf.readInt16LE()).toBe(-1); 92 | }); 93 | 94 | it('does not affect adjacent memory blocks', () => { 95 | const buf = Buffer.alloc(6, 0); 96 | 97 | expect(buf.readInt16LE(0)).toBe(0); 98 | expect(buf.readInt16LE(2)).toBe(0); 99 | expect(buf.readInt16LE(4)).toBe(0); 100 | 101 | const addr = [...getAddress(buf), 2]; 102 | cmpxchg16(addr, 0, -1); 103 | 104 | expect(buf.readInt16LE(0)).toBe(0); 105 | expect(buf.readInt16LE(2)).toBe(-1); 106 | expect(buf.readInt16LE(4)).toBe(0); 107 | }); 108 | }); 109 | 110 | describe('cmpxchg8', function() { 111 | it('method exists', () => { 112 | expect(typeof cmpxchg8).toBe('function'); 113 | }); 114 | 115 | it('can exchange value', () => { 116 | const buf = Buffer.alloc(1, 0); 117 | expect(buf.readInt8()).toBe(0); 118 | cmpxchg8(buf, 0, 1); 119 | expect(buf.readInt8()).toBe(1); 120 | cmpxchg8(buf, 1, -2); 121 | cmpxchg8(buf, 1, 6); 122 | expect(buf.readInt8()).toBe(-2); 123 | }); 124 | 125 | it('does not affect adjacent memory blocks', () => { 126 | const buf = Buffer.alloc(3, 0); 127 | 128 | expect(buf.readInt8(0)).toBe(0); 129 | expect(buf.readInt8(1)).toBe(0); 130 | expect(buf.readInt8(2)).toBe(0); 131 | 132 | const addr = [...getAddress(buf), 1]; 133 | cmpxchg8(addr, 0, -1); 134 | 135 | expect(buf.readInt8(0)).toBe(0); 136 | expect(buf.readInt8(1)).toBe(-1); 137 | expect(buf.readInt8(2)).toBe(0); 138 | }); 139 | }); 140 | 141 | -------------------------------------------------------------------------------- /test/call.test.js: -------------------------------------------------------------------------------- 1 | const libsys = require('..'); 2 | const {X64} = require('ass-js'); 3 | 4 | const isMac = process.platform === 'darwin'; 5 | const SYS_mmap = isMac ? (0x2000000 + 197) : 9; 6 | 7 | // Allocate executable memory, returns `ArrayBuffer`. 8 | function alloc(size) { 9 | const flags = 2 /* MAP_PRIVATE */ | (isMac ? 4096 : 32 /* MAP_ANONYMOUS */); 10 | const protnum = 1 | 2 | 4; // Read, write and execute; 11 | const addr = libsys.syscall64(SYS_mmap, 0, size, protnum, flags, -1, 0); 12 | return libsys.frame(addr, size); 13 | } 14 | 15 | describe('libsys', function() { 16 | describe('.call()', function () { 17 | it('calls executable code', () => { 18 | var _ = X64(); 19 | 20 | _._('mov', ['rax', 0xBABE]); 21 | _._('ret'); 22 | 23 | const code = _.compile(); 24 | const ab = alloc(code.length); 25 | const uint8 = new Uint8Array(ab); 26 | 27 | for (let i = 0; i < code.length; i++) uint8[i] = code[i]; 28 | 29 | const result = libsys.call(ab, 0, []); 30 | 31 | expect(result).toBe(0xBABE); 32 | }); 33 | 34 | it('add(a, b) = a + b', () => { 35 | var _ = X64(); 36 | 37 | _._('add', ['rdi', 'rsi']); 38 | _._('mov', ['rax', 'rdi']); 39 | _._('ret'); 40 | 41 | const code = _.compile(); 42 | const ab = alloc(code.length); 43 | const uint8 = new Uint8Array(ab); 44 | 45 | for (let i = 0; i < code.length; i++) uint8[i] = code[i]; 46 | 47 | const add = (a, b) => libsys.call(ab, 0, [a, b]); 48 | 49 | expect(add(1, 1)).toBe(2); 50 | expect(add(2, 2)).toBe(4); 51 | expect(add(1000, 999)).toBe(1999); 52 | expect(add(-5, -3)).toBe(-8); 53 | expect(add(-5, 10)).toBe(5); 54 | }); 55 | }); 56 | 57 | describe('.call64()', function () { 58 | it('calls executable code', () => { 59 | var _ = X64(); 60 | 61 | _._('mov', ['rax', 0xBABE]); 62 | _._('ret'); 63 | 64 | const code = _.compile(); 65 | const ab = alloc(code.length); 66 | const uint8 = new Uint8Array(ab); 67 | 68 | for (let i = 0; i < code.length; i++) uint8[i] = code[i]; 69 | 70 | const [result, zero] = libsys.call64(ab, 0, []); 71 | 72 | expect(result).toBe(0xBABE); 73 | expect(zero).toBe(0); 74 | }); 75 | 76 | it('add(a, b) = a + b', () => { 77 | var _ = X64(); 78 | 79 | _._('add', ['rdi', 'rsi']); 80 | _._('mov', ['rax', 'rdi']); 81 | _._('ret'); 82 | 83 | const code = _.compile(); 84 | const ab = alloc(code.length); 85 | const uint8 = new Uint8Array(ab); 86 | 87 | for (let i = 0; i < code.length; i++) uint8[i] = code[i]; 88 | 89 | const add = (a, b) => libsys.call64(ab, 0, [a, b])[0]; 90 | 91 | expect(add(1, 1)).toBe(2); 92 | expect(add(2, 2)).toBe(4); 93 | expect(add(1000, 999)).toBe(1999); 94 | expect(add(-5, -3)).toBe(-8); 95 | expect(add(-5, 10)).toBe(5); 96 | }); 97 | }); 98 | 99 | describe('.call_0()', function () { 100 | it('calls executable code', () => { 101 | var _ = X64(); 102 | 103 | _._('mov', ['rax', 0xBABE]); 104 | _._('ret'); 105 | 106 | const code = _.compile(); 107 | const ab = alloc(code.length); 108 | const uint8 = new Uint8Array(ab); 109 | 110 | for (let i = 0; i < code.length; i++) uint8[i] = code[i]; 111 | 112 | const result = libsys.call_0(ab); 113 | 114 | expect(result).toBe(0xBABE); 115 | }); 116 | }); 117 | 118 | describe('.call_1()', function () { 119 | it('calls executable code', () => { 120 | var _ = X64(); 121 | 122 | _._('mov', ['rax', 'rdi']); 123 | _._('ret'); 124 | 125 | const code = _.compile(); 126 | const ab = alloc(code.length); 127 | const uint8 = new Uint8Array(ab); 128 | 129 | for (let i = 0; i < code.length; i++) uint8[i] = code[i]; 130 | 131 | const result = libsys.call_1(ab, 0xBABE); 132 | 133 | expect(result).toBe(0xBABE); 134 | }); 135 | }); 136 | 137 | describe('.call64_0()', function () { 138 | it('calls executable code', () => { 139 | var _ = X64(); 140 | 141 | _._('mov', ['rax', 0xBABE]); 142 | _._('ret'); 143 | 144 | const code = _.compile(); 145 | const ab = alloc(code.length); 146 | const uint8 = new Uint8Array(ab); 147 | 148 | for (let i = 0; i < code.length; i++) uint8[i] = code[i]; 149 | 150 | const [result, zero] = libsys.call64_0(ab); 151 | 152 | expect(result).toBe(0xBABE); 153 | expect(zero).toBe(0); 154 | }); 155 | }); 156 | 157 | describe('.call64_1()', function () { 158 | it('calls executable code', () => { 159 | var _ = X64(); 160 | 161 | _._('mov', ['rax', 'rdi']); 162 | _._('ret'); 163 | 164 | const code = _.compile(); 165 | const ab = alloc(code.length); 166 | const uint8 = new Uint8Array(ab); 167 | 168 | for (let i = 0; i < code.length; i++) uint8[i] = code[i]; 169 | 170 | const [result, zero] = libsys.call64_1(ab, 0xBABE); 171 | 172 | expect(result).toBe(0xBABE); 173 | expect(zero).toBe(0); 174 | }); 175 | }); 176 | }); 177 | -------------------------------------------------------------------------------- /test/syscall.test.js: -------------------------------------------------------------------------------- 1 | const libsys = require('..'); 2 | 3 | const STDOUT = 1; 4 | const isMac = process.platform === 'darwin'; 5 | const SYS_write = isMac ? 0x2000004 : 1; 6 | const SYS_getpid = isMac ? (0x2000000 + 20) : 39; 7 | const SYS_mmap = isMac ? (0x2000000 + 197) : 9; 8 | 9 | // Allocate executable memory, returns `ArrayBuffer`. 10 | function alloc(size) { 11 | const flags = 2 /* MAP_PRIVATE */ | (isMac ? 4096 : 32 /* MAP_ANONYMOUS */); 12 | const protnum = 1 | 2 | 4; // Read, write and execute; 13 | const addr = libsys.syscall64(SYS_mmap, 0, size, protnum, flags, -1, 0); 14 | return libsys.malloc(addr, size); 15 | } 16 | 17 | describe('syscall', function() { 18 | describe('.syscall()', function() { 19 | it('Writes buffer to STDOUT', function() { 20 | const str = 'Hello world, hell yeah!'; 21 | const buf = new Buffer(str + '\n'); 22 | const res = libsys.syscall(SYS_write, STDOUT, buf, buf.length); 23 | 24 | expect(res).toBe(buf.length); 25 | }); 26 | 27 | it('gets correct process ID', () => { 28 | const res = libsys.syscall(SYS_getpid); 29 | 30 | expect(res).toBe(process.pid); 31 | }); 32 | 33 | it('returns negative error number', () => { 34 | const res = libsys.syscall(SYS_mmap, 1, 1, 1, 1, 1, 1); 35 | console.log('res', res); 36 | }); 37 | }); 38 | 39 | describe('.syscall64()', function() { 40 | it('Writes buffer to STDOUT', function() { 41 | const str = 'Hello world, hell yeah!'; 42 | const buf = new Buffer(str + '\n'); 43 | const [length, zero] = libsys.syscall64(SYS_write, STDOUT, buf, buf.length); 44 | 45 | expect(length).toBe(buf.length); 46 | expect(zero).toBe(0); 47 | }); 48 | 49 | it('gets correct process ID', () => { 50 | const [pid, zero] = libsys.syscall64(SYS_getpid); 51 | 52 | expect(pid).toBe(process.pid); 53 | expect(zero).toBe(0); 54 | }); 55 | }); 56 | 57 | describe('.syscall_0()', () => { 58 | it('gets correct process ID', () => { 59 | const res = libsys.syscall_0(SYS_getpid); 60 | 61 | expect(res).toBe(process.pid); 62 | }); 63 | }); 64 | 65 | describe('.syscall_1()', () => { 66 | it('gets correct process ID', () => { 67 | const res = libsys.syscall_1(SYS_getpid, 0); 68 | 69 | expect(res).toBe(process.pid); 70 | }); 71 | }); 72 | 73 | describe('.syscall_2()', () => { 74 | it('gets correct process ID', () => { 75 | const res = libsys.syscall_2(SYS_getpid, 0, 0); 76 | 77 | expect(res).toBe(process.pid); 78 | }); 79 | }); 80 | 81 | describe('.syscall_3()', () => { 82 | it('gets correct process ID', () => { 83 | const res = libsys.syscall_3(SYS_getpid, 0, 0, 0); 84 | 85 | expect(res).toBe(process.pid); 86 | }); 87 | }); 88 | 89 | describe('.syscall_4()', () => { 90 | it('gets correct process ID', () => { 91 | const res = libsys.syscall_4(SYS_getpid, 0, 0, 0, 0); 92 | 93 | expect(res).toBe(process.pid); 94 | }); 95 | }); 96 | 97 | describe('.syscall_5()', () => { 98 | it('gets correct process ID', () => { 99 | const res = libsys.syscall_5(SYS_getpid, 0, 0, 0, 0, 0); 100 | 101 | expect(res).toBe(process.pid); 102 | }); 103 | }); 104 | 105 | describe('.syscall_6()', () => { 106 | it('gets correct process ID', () => { 107 | const res = libsys.syscall_5(SYS_getpid, 0, 0, 0, 0, 0, 0); 108 | 109 | expect(res).toBe(process.pid); 110 | }); 111 | }); 112 | 113 | describe('.syscall64_0()', () => { 114 | it('gets correct process ID', () => { 115 | const [pid, zero] = libsys.syscall64_0(SYS_getpid); 116 | 117 | expect(pid).toBe(process.pid); 118 | expect(zero).toBe(0); 119 | }); 120 | }); 121 | 122 | describe('.syscall64_1()', () => { 123 | it('gets correct process ID', () => { 124 | const [pid, zero] = libsys.syscall64_1(SYS_getpid); 125 | 126 | expect(pid).toBe(process.pid); 127 | expect(zero).toBe(0); 128 | }); 129 | }); 130 | 131 | describe('.syscall64_2()', () => { 132 | it('gets correct process ID', () => { 133 | const [pid, zero] = libsys.syscall64_2(SYS_getpid); 134 | 135 | expect(pid).toBe(process.pid); 136 | expect(zero).toBe(0); 137 | }); 138 | }); 139 | 140 | describe('.syscall64_3()', () => { 141 | it('gets correct process ID', () => { 142 | const [pid, zero] = libsys.syscall64_3(SYS_getpid); 143 | 144 | expect(pid).toBe(process.pid); 145 | expect(zero).toBe(0); 146 | }); 147 | }); 148 | 149 | describe('.syscall64_4()', () => { 150 | it('gets correct process ID', () => { 151 | const [pid, zero] = libsys.syscall64_4(SYS_getpid); 152 | 153 | expect(pid).toBe(process.pid); 154 | expect(zero).toBe(0); 155 | }); 156 | }); 157 | 158 | describe('.syscall64_5()', () => { 159 | it('gets correct process ID', () => { 160 | const [pid, zero] = libsys.syscall64_5(SYS_getpid); 161 | 162 | expect(pid).toBe(process.pid); 163 | expect(zero).toBe(0); 164 | }); 165 | }); 166 | 167 | describe('.syscall64_6()', () => { 168 | it('gets correct process ID', () => { 169 | const [pid, zero] = libsys.syscall64_6(SYS_getpid); 170 | 171 | expect(pid).toBe(process.pid); 172 | expect(zero).toBe(0); 173 | }); 174 | }); 175 | }); 176 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # libsys 2 | 3 | [![][npm-badge]][npm-url] [![][travis-badge]][travis-url] 4 | 5 | - Execute [Linux](https://filippo.io/linux-syscall-table/)/[Mac](https://opensource.apple.com/source/xnu/xnu-1504.3.12/bsd/kern/syscalls.master) `syscall`s from Node.js 6 | - Get memory address of `Buffer`, `ArrayBuffer`, `TypedArray` 7 | - Call arbitrary machine code from Node.js 8 | - Create machine code trampolines that will call Node.js functions 9 | 10 | 11 | ## Installation 12 | 13 | ```shell 14 | npm install libsys 15 | ``` 16 | 17 | Compiles on Linux, Mac and Windows ([in WSL process](https://docs.microsoft.com/en-us/windows/wsl/install-win10)). 18 | 19 | 20 | ## Usage 21 | 22 | Print `Hello world` to console 23 | 24 | ```js 25 | const libsys = require('libsys'); 26 | 27 | const STDOUT = 1; 28 | const isMac = process.platform === 'darwin'; 29 | const SYS_write = isMac ? 0x2000004 : 1; 30 | const buf = Buffer.from('Hello world\n'); 31 | 32 | libsys.syscall(SYS_write, STDOUT, buf, buf.length); 33 | ``` 34 | 35 | 36 | ## More goodies 37 | 38 | - [libjs](http://www.github.com/streamich/libjs) — POSIX command implementation (`libc` in JavaScript). 39 | - [bamboo](http://www.github.com/streamich/bamboo) — Node.js clone in pure JavaScript. 40 | - [jskernel](http://www.github.com/streamich/jskernel) — proposal of Node in kernel space. 41 | - [Assembler.js](http://www.github.com/streamich/ass-js) — X86_64 assembler in JavaScript. 42 | 43 | 44 | ## Reference 45 | 46 | - [`syscall`](#syscall) — Executes system call with varied type arguments, returns 32-bit result 47 | - [`syscall64`](#syscall64) — Executes system call with varied type arguments, return 64-bit result 48 | - [`syscall_N`](#syscall_0) — Executes system call with N `number` arguments, returns 32-bit result 49 | - [`syscall64_N`](#syscall64_0) — Executes system call with N `number` arguments, returns 64-bit result 50 | - [`getAddressArrayBuffer`](#getaddressarraybuffer) — Returns 64-bit address of `ArrayBuffer` 51 | - [`getAddressTypedArray`](#getaddresstypedarray) — Returns 64-bit address of `TypedArray` (including `Uint8Array`, etc..) 52 | - [`getAddressBuffer`](#getaddressbuffer) — Returns 64-bit address of Node's `Buffer` 53 | - [`getAddress`](#getaddress) — Returns 64-bit address of any buffer type 54 | - [`frame`](#frame) — Creates `ArrayBuffer` in the specified memory location 55 | - [`call`](#call) — Calls machine code at specified address with up to 10 arguments, returns 32-bit result 56 | - [`call64`](#call64) — Calls machine code at specified address with up to 10 arguments, returns 64-bit result 57 | - [`call_0`](#call_0) — Call machine code with no arguments 58 | - `call_1` 59 | - `call64_0` 60 | - `call64_1` 61 | - `jumper` 62 | - `sigaction` - Executes `sigaction` system call 63 | - `cmpxchg8` - Compare and exchange value at memory location 64 | - `cmpxchg16` - Compare and exchange value at memory location 65 | - `cmpxchg32` - Compare and exchange value at memory location 66 | 67 | ### Arguments 68 | 69 | Different JavaScript types can be used as `Targ` argument in some functions. Here is how they are converted to 64-bit integers: 70 | 71 | ```ts 72 | type Targ = number | [number, number] | [number, number, number] | string | ArrayBuffer | TypedArray | Buffer; 73 | ``` 74 | 75 | - `number` is treated as 32-bit integer and gets extended to 64-bit integer; 76 | - `[number, number]` treated as a `[lo, hi]` tuple of two 32-bit integers, which are combined into 64-bit integer; 77 | - `[number, number, number]` treated as a `[lo, hi, offset]` tuple, same as above with the difference that `offset` is added to the resulting 64-bit integer; 78 | - `string` gets converted to C null-terminated string and 64-bit pointer created to the beginning of that string; 79 | - `ArrayBuffer`, `TypedArray`, `Buffer` 64-bit pointer to the beginning of data contents of those objects is created; 80 | 81 | 82 | ### `syscall` 83 | 84 | ```ts 85 | syscall(command: number, ...args: Targ[]): number; 86 | ``` 87 | 88 | `syscall` accepts up to 6 command arguments `args`. See discussion on *Arguments* 89 | above to see how JavaScript objects are converted to 64-bit integers. 90 | 91 | `syscall` returns a `number` which is the result returned by the kernel, 92 | negative numbers usually represent an error. 93 | 94 | 95 | ### `syscall64` 96 | 97 | ```ts 98 | syscall64(command: number, ...args: TArg[]): [number, number]; 99 | ``` 100 | 101 | Same as `syscall`, but returns 64-bit result. 102 | 103 | 104 | ### `syscall_0` 105 | 106 | ```ts 107 | syscall_0(command: number): number; 108 | syscall_1(command: number, arg1: number): number; 109 | syscall_2(command: number, arg1: number, arg2: number): number; 110 | syscall_3(command: number, arg1: number, arg2: number, arg3: number): number; 111 | syscall_4(command: number, arg1: number, arg2: number, arg3: number, arg4: number): number; 112 | syscall_5(command: number, arg1: number, arg2: number, arg3: number, arg4: number, arg5: number): number; 113 | syscall_6(command: number, arg1: number, arg2: number, arg3: number, arg4: number, arg5: number, arg6: number): number; 114 | ``` 115 | 116 | Executes system calls and returns 32-bit result. Expects all arguments to be of type `number`. 117 | 118 | 119 | ### `syscall64_0` 120 | 121 | ```ts 122 | syscall64_0(command: number): [number, number]; 123 | syscall64_1(command: number, arg1: number): [number, number]; 124 | syscall64_2(command: number, arg1: number, arg2: number): [number, number]; 125 | syscall64_3(command: number, arg1: number, arg2: number, arg3: number): [number, number]; 126 | syscall64_4(command: number, arg1: number, arg2: number, arg3: number, arg4: number): [number, number]; 127 | syscall64_5(command: number, arg1: number, arg2: number, arg3: number, arg4: number, arg5: number): [number, number]; 128 | syscall64_6(command: number, arg1: number, arg2: number, arg3: number, arg4: number, arg5: number, arg6: number): [number, number]; 129 | ``` 130 | 131 | Executes system calls and returns 64-bit result. Expects all arguments to be of type `number`. 132 | 133 | 134 | ### `getAddressArrayBuffer` 135 | 136 | ```ts 137 | getAddressArrayBuffer(ab: ArrayBuffer): [number, number]; 138 | ``` 139 | 140 | ### `getAddressTypedArray` 141 | 142 | ```ts 143 | getAddressTypedArray(ta: TypedArray | Uint8Array): [number, number]; 144 | ``` 145 | 146 | ### `getAddressBuffer` 147 | 148 | ```ts 149 | getAddressBuffer(buf: Buffer): [number, number]; 150 | ``` 151 | 152 | Return memory address of `Buffer`'s data contents. 153 | 154 | 155 | ### `getAddress` 156 | 157 | ```ts 158 | getAddress(buffer: Buffer | ArrayBuffer | TypedArray): [number, number]; 159 | ``` 160 | 161 | ### `frame` 162 | 163 | ```ts 164 | frame(addresss: Targ, size: number): ArrayBuffer; 165 | ``` 166 | 167 | `frame` returns an `ArrayBuffer` object of size `size` that is mapped to memory location 168 | specified in `addr` argument. 169 | 170 | 171 | ### `call` 172 | 173 | ```ts 174 | call(address: Targ, offset?: number, arguments?: Targ[]); 175 | ``` 176 | 177 | Execute machine code at specified memory address `address`. The memory address is converted 178 | to function pointer and called using your architecture calling conventions. `offest` is added 179 | to the address, but defaults to 0. 180 | 181 | Up to 10 `arguments` can be supplied. 182 | 183 | 184 | ### `call64` 185 | 186 | Same as `call` but ruturns a 64-bit `[number, number]`. 187 | 188 | 189 | ### `call_0` 190 | 191 | ```ts 192 | call_0(address: Targ): number; 193 | ``` 194 | 195 | Call machine code at `address` without any arguments using architecture specific calling conventions. Returns a 32-bit result. 196 | 197 | 198 | ## License 199 | 200 | [Unlicense](./LICENSE) - public domain. 201 | 202 | 203 | 204 | [npm-url]: https://www.npmjs.com/package/libsys 205 | [npm-badge]: https://img.shields.io/npm/v/libsys.svg 206 | [travis-url]: https://travis-ci.org/streamich/libsys 207 | [travis-badge]: https://travis-ci.org/streamich/libsys.svg?branch=master 208 | 209 | -------------------------------------------------------------------------------- /async/async.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "../atomics/atomics.h" 7 | #include "../syscall/syscall.h" 8 | #include "../call/call.h" 9 | 10 | #define DEBUG 0 11 | #define debug_print(fmt, ...) \ 12 | do { if (DEBUG) fprintf(stderr, fmt, __VA_ARGS__); } while (0) 13 | 14 | /* fix yield on OS X */ 15 | #ifdef __APPLE__ 16 | #define pthread_yield() pthread_yield_np() 17 | #endif 18 | 19 | typedef struct async_headers async_headers; 20 | typedef struct async_syscall async_syscall; 21 | typedef struct async_call async_call; 22 | typedef struct worker_start_record worker_start_record; 23 | 24 | struct async_headers { 25 | int8_t lock; // Used for CMPXCHG, which is supposed to be fastest on 32 bits. Workers exchange this value for their ID. 26 | int8_t type; // Type of the async operation. 27 | int8_t ready; // Whether it is safe to follow `next` pointer to the next async record. 28 | int8_t left; // Whether `next` address is ready, this is needed because JavaScript can write into `next` field only in 32-bit chunks 29 | uint32_t id; // ID of this record, used to identify this record by parent thread. 30 | uint64_t next; // Address of the next record header. 31 | }; 32 | 33 | struct async_syscall { 34 | struct async_headers headers; 35 | uint32_t sys; // Syscall number. 36 | uint32_t len; // Number of arguments `args` for this syscall. 37 | int64_t args[]; 38 | }; 39 | 40 | struct async_call { 41 | struct async_headers headers; 42 | uint64_t addr; // Address of the function to be called. 43 | uint64_t len; // Number of arguments `args` for this function. 44 | int64_t args[]; 45 | }; 46 | 47 | struct async_result { 48 | struct async_headers headers; 49 | int64_t result; 50 | }; 51 | 52 | struct worker_start_record { 53 | int32_t worker; // Worker ID, starts from 1. 54 | int32_t pipe_fd; // One end of `pipe()` file descriptor, used to notify parent thread about processed records. 55 | struct async_headers* headers; 56 | }; 57 | 58 | const int8_t LOCK_BLOCKED = -1; // When lock is -1, record is not meant for processing. 59 | const int8_t LOCK_FREE = 0; // When lock is 0, it is free to be picked up by one of the threads. 60 | const int8_t TYPE_SYSCALL = 0; // Record specifies a syscall to be executed. 61 | const int8_t TYPE_CALL = 1; // Record specifies a regular function call to be executed. 62 | const int8_t TYPE_EXIT = 2; // Tells thread to quit. 63 | 64 | inline int64_t worker_exec_syscall (int32_t worker, async_syscall* record) { 65 | // debug_print("~> worker_exec_syscall [worker = %x, syscall = %u, len = %x] \n", worker, record->sys, record->len); 66 | switch (record->len) { 67 | case 0: return syscall_0(record->sys); 68 | case 1: return syscall_1(record->sys, record->args[0]); 69 | case 2: return syscall_2(record->sys, record->args[0], record->args[1]); 70 | case 3: return syscall_3(record->sys, record->args[0], record->args[1], record->args[2]); 71 | case 4: return syscall_4(record->sys, record->args[0], record->args[1], record->args[2], record->args[3]); 72 | case 5: return syscall_5(record->sys, record->args[0], record->args[1], record->args[2], record->args[3], record->args[4]); 73 | case 6: return syscall_6(record->sys, record->args[0], record->args[1], record->args[2], record->args[3], record->args[4], record->args[5]); 74 | default: return -1; 75 | } 76 | } 77 | 78 | int64_t worker_exec_call (int32_t worker, async_call* record) { 79 | // debug_print("~> worker_exec_call [worker = %x, address = %llu, len = %llx] \n", worker, record->addr, record->len); 80 | switch (record->len) { 81 | case 0: return ((callback) record->addr)(); 82 | case 1: return ((callback1) record->addr)(record->args[0]); 83 | case 2: return ((callback2) record->addr)(record->args[0], record->args[1]); 84 | case 3: return ((callback3) record->addr)(record->args[0], record->args[1], record->args[2]); 85 | case 4: return ((callback4) record->addr)(record->args[0], record->args[1], record->args[2], record->args[3]); 86 | case 5: return ((callback5) record->addr)(record->args[0], record->args[1], record->args[2], record->args[3], record->args[4]); 87 | case 6: return ((callback6) record->addr)(record->args[0], record->args[1], record->args[2], record->args[3], record->args[4], record->args[5]); 88 | case 7: return ((callback7) record->addr)(record->args[0], record->args[1], record->args[2], record->args[3], record->args[4], record->args[5], record->args[6]); 89 | case 8: return ((callback8) record->addr)(record->args[0], record->args[1], record->args[2], record->args[3], record->args[4], record->args[5], record->args[6], record->args[7]); 90 | case 9: return ((callback9) record->addr)(record->args[0], record->args[1], record->args[2], record->args[3], record->args[4], record->args[5], record->args[6], record->args[7], record->args[8]); 91 | case 10: return ((callback10) record->addr)(record->args[0], record->args[1], record->args[2], record->args[3], record->args[4], record->args[5], record->args[6], record->args[7], record->args[8], record->args[9]); 92 | default: return -1; 93 | } 94 | } 95 | 96 | inline void* worker_process_new_block (int32_t worker, int32_t pipe_fd, async_headers* headers) { 97 | start: 98 | // debug_print("~> start [worker = %x, lock = %x]\n", worker, headers->lock); 99 | if (headers->lock == LOCK_FREE) { 100 | cmpxchg8(&(headers->lock), LOCK_FREE, worker); 101 | if (headers->lock == worker) goto process_block; 102 | else goto look_for_next_block; 103 | } 104 | 105 | look_for_next_block: 106 | if (headers->ready == 0) goto sleep; 107 | atomic_increment(&(headers->left), 1); 108 | headers = (async_headers*) headers->next; 109 | goto start; 110 | 111 | process_block: 112 | // debug_print("~> process_block [worker = %x]\n", worker); 113 | switch (headers->type) { 114 | case TYPE_SYSCALL: 115 | ((async_result*) headers)->result = worker_exec_syscall(worker, (async_syscall*) headers); 116 | ((void (*)(int32_t, unsigned int*, int32_t)) write)(pipe_fd, &(headers->id), sizeof(headers->id)); 117 | // debug_print(" syscall result: [worker = %x, result = %lld] \n", worker, ((async_result*) headers)->result); 118 | break; 119 | case TYPE_CALL: 120 | ((async_result*) headers)->result = worker_exec_call(worker, (async_call*) headers); 121 | ((void (*)(int32_t, unsigned int*, int32_t)) write)(pipe_fd, &(headers->id), sizeof(headers->id)); 122 | // debug_print(" call result: [worker = %x, result = %lld] \n", worker, ((async_result*) headers)->result); 123 | break; 124 | case TYPE_EXIT: 125 | goto exit; 126 | break; 127 | } 128 | goto look_for_next_block; 129 | 130 | sleep: 131 | // debug_print("Waiting for next block [worker = %i]\n", worker); 132 | if (headers->type == TYPE_EXIT) goto exit; 133 | pthread_yield(); 134 | goto look_for_next_block; 135 | 136 | exit: 137 | atomic_increment(&(headers->left), 1); 138 | // debug_print("Exiting [worker = %x] \n", worker); 139 | pthread_exit(NULL); 140 | } 141 | 142 | void* worker_start (void* arg) { 143 | worker_start_record* start_record = (worker_start_record*) arg; 144 | assert(start_record->worker != 0); // Worker ID cannot be 0, because it is LOCK_FREE. 145 | worker_process_new_block(start_record->worker, start_record->pipe_fd, start_record->headers); 146 | return NULL; 147 | } 148 | 149 | int32_t create_async_pool (void* headers, uint32_t nthreads) { 150 | int32_t fds[2]; 151 | ((void (*)(int32_t[2])) pipe)(fds); 152 | for (; nthreads > 0; nthreads--) { 153 | pthread_t* thread = (pthread_t*) malloc(sizeof(pthread_t)); 154 | pthread_attr_t* attr = (pthread_attr_t*) malloc(sizeof(pthread_attr_t)); 155 | pthread_attr_init(attr); 156 | worker_start_record* record = (worker_start_record*) malloc(sizeof(worker_start_record)); 157 | record->worker = nthreads; 158 | record->pipe_fd = fds[1]; 159 | record->headers = (async_headers*) headers; 160 | pthread_create(thread, attr, worker_start, record); 161 | // debug_print("created thread [worker = %x]\n", record->worker); 162 | } 163 | return fds[0]; 164 | } 165 | -------------------------------------------------------------------------------- /libsys.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "syscall/syscall.c" 13 | #include "atomics/atomics.c" 14 | #include "async/async.c" 15 | #include "call/call.h" 16 | #include 17 | #include 18 | 19 | #define V8_RETURN_NUM(X) args.GetReturnValue().Set(Integer::New(args.GetIsolate(), X)); 20 | #define V8_RETURN_NUM64(X) args.GetReturnValue().Set(Int64ToArray(args.GetIsolate(), X)); 21 | #define SET_KEY(ISO, OBJ, K, V) OBJ->Set(String::NewFromUtf8(ISO, K), V); 22 | 23 | namespace libsys { 24 | 25 | using v8::FunctionCallbackInfo; 26 | using v8::Isolate; 27 | using v8::Local; 28 | using v8::Object; 29 | using v8::String; 30 | using v8::Value; 31 | using v8::Array; 32 | using v8::Integer; 33 | using v8::Exception; 34 | using v8::ArrayBuffer; 35 | using v8::Uint8Array; 36 | using v8::TypedArray; 37 | using v8::Function; 38 | using v8::Primitive; 39 | 40 | Local _exports; 41 | 42 | v8::Local Int64ToArray(Isolate* isolate, int64_t number) { 43 | int32_t lo = number & 0xffffffff; 44 | int32_t hi = number >> 32; 45 | v8::Local array = Array::New(isolate, 2); 46 | array->Set(Nan::GetCurrentContext(), 0, Integer::New(isolate, lo)); 47 | array->Set(Nan::GetCurrentContext(), 1, Integer::New(isolate, hi)); 48 | return array; 49 | } 50 | 51 | inline uint64_t GetAddrArrayBuffer(Local obj) { 52 | Local ab = obj.As(); 53 | ArrayBuffer::Contents ab_c = ab->GetContents(); 54 | return (uint64_t)(ab_c.Data()); 55 | } 56 | 57 | inline uint64_t GetAddrTypedArray(Local obj) { 58 | Local ta = obj.As(); 59 | ArrayBuffer::Contents ab_c = ta->Buffer()->GetContents(); 60 | return (uint64_t)(ab_c.Data()) + ta->ByteOffset(); 61 | } 62 | 63 | inline uint64_t GetAddrUint8Array(Local obj) { 64 | return GetAddrTypedArray(obj); 65 | // Local ui = obj.As(); 66 | // ArrayBuffer::Contents ab_c = ui->Buffer()->GetContents(); 67 | // return (uint64_t)(ab_c.Data()) + ui->ByteOffset(); 68 | } 69 | 70 | inline uint64_t GetAddrBuffer(Local obj) { 71 | return (uint64_t) node::Buffer::Data(obj); 72 | } 73 | 74 | void MethodGetAddressArrayBuffer(const FunctionCallbackInfo& args) { 75 | uint64_t addr = GetAddrArrayBuffer(args[0]->ToObject(Nan::GetCurrentContext()).ToLocalChecked()); 76 | V8_RETURN_NUM64(addr); 77 | } 78 | 79 | void MethodGetAddressTypedArray(const FunctionCallbackInfo& args) { 80 | uint64_t addr = GetAddrTypedArray(args[0]->ToObject(Nan::GetCurrentContext()).ToLocalChecked()); 81 | V8_RETURN_NUM64(addr); 82 | } 83 | 84 | void MethodGetAddressBuffer(const FunctionCallbackInfo& args) { 85 | uint64_t addr = GetAddrBuffer(args[0]->ToObject(Nan::GetCurrentContext()).ToLocalChecked()); 86 | V8_RETURN_NUM64(addr); 87 | } 88 | 89 | void MethodGetAddress(const FunctionCallbackInfo& args) { 90 | Local obj = args[0]->ToObject(Nan::GetCurrentContext()).ToLocalChecked(); 91 | uint64_t addr; 92 | 93 | // Here we use the fact that Uint8Array is TypedArray, and Node's Buffer is Uint8Array. 94 | if(obj->IsArrayBuffer()) addr = GetAddrArrayBuffer(obj); 95 | else addr = GetAddrTypedArray(obj); 96 | 97 | V8_RETURN_NUM64(addr); 98 | } 99 | 100 | // Transfrom different JavaScript objects to 64-bit integer. 101 | int64_t ArgToInt(Local arg) { 102 | if(arg->IsNumber()) { 103 | return Nan::To(arg).FromJust(); 104 | } else { 105 | if(arg->IsString()) { 106 | Nan::Utf8String v8str(arg); 107 | // String::AsciiValue v8str(arg->ToString()); 108 | std::string cppstr = std::string(*v8str); 109 | const char *cstr = cppstr.c_str(); 110 | return (uint64_t) cstr; 111 | } else if(arg->IsArrayBuffer()) { 112 | return GetAddrArrayBuffer(arg->ToObject(Nan::GetCurrentContext()).ToLocalChecked()); 113 | // } else if(arg->IsUint8Array()) { 114 | } else if(arg->IsTypedArray()) { 115 | // return GetAddrUint8Array(arg->ToObject()); 116 | return GetAddrTypedArray(arg->ToObject(Nan::GetCurrentContext()).ToLocalChecked()); 117 | } else if (arg->IsArray()) { // [lo, hi, offset] 118 | Local arr = arg.As(); 119 | uint32_t arrlen = arr->Length(); 120 | 121 | int32_t lo = (int32_t) Nan::To(arr->Get(Nan::GetCurrentContext(), 0).ToLocalChecked()).FromJust(); 122 | int32_t hi = (int32_t) Nan::To(arr->Get(Nan::GetCurrentContext(), 1).ToLocalChecked()).FromJust(); 123 | 124 | uint64_t addr = (uint64_t) ((((int64_t) hi) << 32) | ((int64_t) lo & 0xffffffff)); 125 | 126 | if(arrlen == 3) { 127 | int32_t offset = Nan::To(arr->Get(Nan::GetCurrentContext(), 2).ToLocalChecked()).FromJust(); 128 | addr += offset; 129 | } 130 | 131 | return addr; 132 | } else { 133 | // Assume it is `Buffer`. 134 | return GetAddrBuffer(arg->ToObject(Nan::GetCurrentContext()).ToLocalChecked()); 135 | } 136 | } 137 | } 138 | 139 | int64_t ExecSyscall(const FunctionCallbackInfo& args) { 140 | char len = (char) args.Length(); 141 | 142 | int64_t cmd = Nan::To(args[0]).FromJust(); 143 | if(len == 1) { 144 | return syscall_0(cmd); 145 | } 146 | 147 | int64_t arg1 = ArgToInt(args[1]); 148 | if(len == 2) { 149 | return syscall_1(cmd, arg1); 150 | } 151 | 152 | int64_t arg2 = ArgToInt(args[2]); 153 | if(len == 3) { 154 | return syscall_2(cmd, arg1, arg2); 155 | } 156 | 157 | int64_t arg3 = ArgToInt(args[3]); 158 | if(len == 4) { 159 | return syscall_3(cmd, arg1, arg2, arg3); 160 | } 161 | 162 | int64_t arg4 = ArgToInt(args[4]); 163 | if(len == 5) { 164 | return syscall_4(cmd, arg1, arg2, arg3, arg4); 165 | } 166 | 167 | int64_t arg5 = ArgToInt(args[5]); 168 | if(len == 6) { 169 | return syscall_5(cmd, arg1, arg2, arg3, arg4, arg5); 170 | } 171 | 172 | int64_t arg6 = ArgToInt(args[6]); 173 | if(len == 7) { 174 | return syscall_6(cmd, arg1, arg2, arg3, arg4, arg5, arg6); 175 | } 176 | 177 | return -1; 178 | } 179 | 180 | void MethodSyscall(const FunctionCallbackInfo& args) { 181 | Isolate* isolate = args.GetIsolate(); 182 | char len = (char) args.Length(); 183 | if(len > 7) Nan::ThrowRangeError("Syscall with over 6 arguments."); 184 | else args.GetReturnValue().Set(Integer::New(isolate, ExecSyscall(args))); 185 | } 186 | 187 | void MethodSyscall64(const FunctionCallbackInfo& args) { 188 | Isolate* isolate = args.GetIsolate(); 189 | char len = (char) args.Length(); 190 | if(len > 7) Nan::ThrowRangeError("Syscall with over 6 arguments."); 191 | else { 192 | int64_t result = ExecSyscall(args); 193 | args.GetReturnValue().Set(Int64ToArray(isolate, result)); 194 | } 195 | } 196 | 197 | void MethodSyscall_0(const FunctionCallbackInfo& args) { 198 | int64_t cmd = Nan::To(args[0]).FromJust(); 199 | int64_t result = syscall_0(cmd); 200 | V8_RETURN_NUM(result); 201 | } 202 | 203 | void MethodSyscall64_0(const FunctionCallbackInfo& args) { 204 | int64_t cmd = Nan::To(args[0]).FromJust(); 205 | int64_t result = syscall_0(cmd); 206 | V8_RETURN_NUM64(result); 207 | } 208 | 209 | void MethodSyscall_1(const FunctionCallbackInfo& args) { 210 | int64_t cmd = Nan::To(args[0]).FromJust(); 211 | int64_t arg1 = Nan::To(args[1]).FromJust(); 212 | int64_t result = syscall_1(cmd, arg1); 213 | V8_RETURN_NUM(result); 214 | } 215 | 216 | void MethodSyscall64_1(const FunctionCallbackInfo& args) { 217 | int64_t cmd = Nan::To(args[0]).FromJust(); 218 | int64_t arg1 = Nan::To(args[1]).FromJust(); 219 | int64_t result = syscall_1(cmd, arg1); 220 | V8_RETURN_NUM64(result); 221 | } 222 | 223 | void MethodSyscall_2(const FunctionCallbackInfo& args) { 224 | int64_t cmd = Nan::To(args[0]).FromJust(); 225 | int64_t arg1 = Nan::To(args[1]).FromJust(); 226 | int64_t arg2 = Nan::To(args[2]).FromJust(); 227 | int64_t result = syscall_2(cmd, arg1, arg2); 228 | V8_RETURN_NUM(result); 229 | } 230 | 231 | void MethodSyscall64_2(const FunctionCallbackInfo& args) { 232 | int64_t cmd = Nan::To(args[0]).FromJust(); 233 | int64_t arg1 = Nan::To(args[1]).FromJust(); 234 | int64_t arg2 = Nan::To(args[2]).FromJust(); 235 | int64_t result = syscall_2(cmd, arg1, arg2); 236 | V8_RETURN_NUM64(result); 237 | } 238 | 239 | void MethodSyscall_3(const FunctionCallbackInfo& args) { 240 | int64_t cmd = Nan::To(args[0]).FromJust(); 241 | int64_t arg1 = Nan::To(args[1]).FromJust(); 242 | int64_t arg2 = Nan::To(args[2]).FromJust(); 243 | int64_t arg3 = Nan::To(args[3]).FromJust(); 244 | int64_t result = syscall_3(cmd, arg1, arg2, arg3); 245 | V8_RETURN_NUM(result); 246 | } 247 | 248 | void MethodSyscall64_3(const FunctionCallbackInfo& args) { 249 | int64_t cmd = Nan::To(args[0]).FromJust(); 250 | int64_t arg1 = Nan::To(args[1]).FromJust(); 251 | int64_t arg2 = Nan::To(args[2]).FromJust(); 252 | int64_t arg3 = Nan::To(args[3]).FromJust(); 253 | int64_t result = syscall_3(cmd, arg1, arg2, arg3); 254 | V8_RETURN_NUM64(result); 255 | } 256 | 257 | void MethodSyscall_4(const FunctionCallbackInfo& args) { 258 | int64_t cmd = Nan::To(args[0]).FromJust(); 259 | int64_t arg1 = Nan::To(args[1]).FromJust(); 260 | int64_t arg2 = Nan::To(args[2]).FromJust(); 261 | int64_t arg3 = Nan::To(args[3]).FromJust(); 262 | int64_t arg4 = Nan::To(args[4]).FromJust(); 263 | int64_t result = syscall_4(cmd, arg1, arg2, arg3, arg4); 264 | V8_RETURN_NUM(result); 265 | } 266 | 267 | void MethodSyscall64_4(const FunctionCallbackInfo& args) { 268 | int64_t cmd = Nan::To(args[0]).FromJust(); 269 | int64_t arg1 = Nan::To(args[1]).FromJust(); 270 | int64_t arg2 = Nan::To(args[2]).FromJust(); 271 | int64_t arg3 = Nan::To(args[3]).FromJust(); 272 | int64_t arg4 = Nan::To(args[4]).FromJust(); 273 | int64_t result = syscall_4(cmd, arg1, arg2, arg3, arg4); 274 | V8_RETURN_NUM64(result); 275 | } 276 | 277 | void MethodSyscall_5(const FunctionCallbackInfo& args) { 278 | int64_t cmd = Nan::To(args[0]).FromJust(); 279 | int64_t arg1 = Nan::To(args[1]).FromJust(); 280 | int64_t arg2 = Nan::To(args[2]).FromJust(); 281 | int64_t arg3 = Nan::To(args[3]).FromJust(); 282 | int64_t arg4 = Nan::To(args[4]).FromJust(); 283 | int64_t arg5 = Nan::To(args[5]).FromJust(); 284 | int64_t result = syscall_5(cmd, arg1, arg2, arg3, arg4, arg5); 285 | V8_RETURN_NUM(result); 286 | } 287 | 288 | void MethodSyscall64_5(const FunctionCallbackInfo& args) { 289 | int64_t cmd = Nan::To(args[0]).FromJust(); 290 | int64_t arg1 = Nan::To(args[1]).FromJust(); 291 | int64_t arg2 = Nan::To(args[2]).FromJust(); 292 | int64_t arg3 = Nan::To(args[3]).FromJust(); 293 | int64_t arg4 = Nan::To(args[4]).FromJust(); 294 | int64_t arg5 = Nan::To(args[5]).FromJust(); 295 | int64_t result = syscall_5(cmd, arg1, arg2, arg3, arg4, arg5); 296 | V8_RETURN_NUM64(result); 297 | } 298 | 299 | void MethodSyscall_6(const FunctionCallbackInfo& args) { 300 | int64_t cmd = Nan::To(args[0]).FromJust(); 301 | int64_t arg1 = Nan::To(args[1]).FromJust(); 302 | int64_t arg2 = Nan::To(args[2]).FromJust(); 303 | int64_t arg3 = Nan::To(args[3]).FromJust(); 304 | int64_t arg4 = Nan::To(args[4]).FromJust(); 305 | int64_t arg5 = Nan::To(args[5]).FromJust(); 306 | int64_t arg6 = Nan::To(args[6]).FromJust(); 307 | int64_t result = syscall_6(cmd, arg1, arg2, arg3, arg4, arg5, arg6); 308 | V8_RETURN_NUM(result); 309 | } 310 | 311 | // const res = libsys.syscall64_6(num, 1, 2, 3, 4, 5, 6); 312 | void MethodSyscall64_6(const FunctionCallbackInfo& args) { 313 | int64_t cmd = Nan::To(args[0]).FromJust(); 314 | int64_t arg1 = Nan::To(args[1]).FromJust(); 315 | int64_t arg2 = Nan::To(args[2]).FromJust(); 316 | int64_t arg3 = Nan::To(args[3]).FromJust(); 317 | int64_t arg4 = Nan::To(args[4]).FromJust(); 318 | int64_t arg5 = Nan::To(args[5]).FromJust(); 319 | int64_t arg6 = Nan::To(args[6]).FromJust(); 320 | int64_t result = syscall_6(cmd, arg1, arg2, arg3, arg4, arg5, arg6); 321 | V8_RETURN_NUM64(result); 322 | } 323 | 324 | // const ab = libsys.frame(address, size); 325 | void MethodFrame(const FunctionCallbackInfo& args) { 326 | Isolate* isolate = args.GetIsolate(); 327 | 328 | void* addr = (void*) ArgToInt(args[0]); 329 | size_t size = (size_t) Nan::To(args[1]).FromJust(); 330 | 331 | Local buf = ArrayBuffer::New(isolate, addr, size); 332 | args.GetReturnValue().Set(buf); 333 | } 334 | 335 | int64_t call_method(const FunctionCallbackInfo& args) { 336 | Isolate* isolate = args.GetIsolate(); 337 | 338 | uint64_t addr = ArgToInt(args[0]); 339 | char len = (char) args.Length(); 340 | 341 | int32_t offset; 342 | if(len > 1) { 343 | offset = Nan::To(args[1]).FromJust(); 344 | addr += offset; 345 | } 346 | 347 | if(len <= 2) { 348 | return ((callback) addr)(); 349 | } 350 | 351 | Local arr = args[2].As(); 352 | uint32_t arrlen = arr->Length(); 353 | 354 | switch(arrlen) { 355 | case 0: 356 | return ((callback) addr)(); 357 | case 1: 358 | return ((callback1) addr)( 359 | Nan::To(arr->Get(Nan::GetCurrentContext(), 0).ToLocalChecked()).FromJust() 360 | ); 361 | case 2: 362 | return ((callback2) addr)( 363 | Nan::To(arr->Get(Nan::GetCurrentContext(), 0).ToLocalChecked()).FromJust(), 364 | Nan::To(arr->Get(Nan::GetCurrentContext(), 1).ToLocalChecked()).FromJust() 365 | ); 366 | case 3: 367 | return ((callback3) addr)( 368 | Nan::To(arr->Get(Nan::GetCurrentContext(), 0).ToLocalChecked()).FromJust(), 369 | Nan::To(arr->Get(Nan::GetCurrentContext(), 1).ToLocalChecked()).FromJust(), 370 | Nan::To(arr->Get(Nan::GetCurrentContext(), 2).ToLocalChecked()).FromJust() 371 | ); 372 | case 4: 373 | return ((callback4) addr)( 374 | Nan::To(arr->Get(Nan::GetCurrentContext(), 0).ToLocalChecked()).FromJust(), 375 | Nan::To(arr->Get(Nan::GetCurrentContext(), 1).ToLocalChecked()).FromJust(), 376 | Nan::To(arr->Get(Nan::GetCurrentContext(), 2).ToLocalChecked()).FromJust(), 377 | Nan::To(arr->Get(Nan::GetCurrentContext(), 3).ToLocalChecked()).FromJust() 378 | ); 379 | case 5: 380 | return ((callback5) addr)( 381 | Nan::To(arr->Get(Nan::GetCurrentContext(), 0).ToLocalChecked()).FromJust(), 382 | Nan::To(arr->Get(Nan::GetCurrentContext(), 1).ToLocalChecked()).FromJust(), 383 | Nan::To(arr->Get(Nan::GetCurrentContext(), 2).ToLocalChecked()).FromJust(), 384 | Nan::To(arr->Get(Nan::GetCurrentContext(), 3).ToLocalChecked()).FromJust(), 385 | Nan::To(arr->Get(Nan::GetCurrentContext(), 4).ToLocalChecked()).FromJust() 386 | ); 387 | case 6: 388 | return ((callback6) addr)( 389 | Nan::To(arr->Get(Nan::GetCurrentContext(), 0).ToLocalChecked()).FromJust(), 390 | Nan::To(arr->Get(Nan::GetCurrentContext(), 1).ToLocalChecked()).FromJust(), 391 | Nan::To(arr->Get(Nan::GetCurrentContext(), 2).ToLocalChecked()).FromJust(), 392 | Nan::To(arr->Get(Nan::GetCurrentContext(), 3).ToLocalChecked()).FromJust(), 393 | Nan::To(arr->Get(Nan::GetCurrentContext(), 4).ToLocalChecked()).FromJust(), 394 | Nan::To(arr->Get(Nan::GetCurrentContext(), 5).ToLocalChecked()).FromJust() 395 | ); 396 | case 7: 397 | return ((callback7) addr)( 398 | Nan::To(arr->Get(Nan::GetCurrentContext(), 0).ToLocalChecked()).FromJust(), 399 | Nan::To(arr->Get(Nan::GetCurrentContext(), 1).ToLocalChecked()).FromJust(), 400 | Nan::To(arr->Get(Nan::GetCurrentContext(), 2).ToLocalChecked()).FromJust(), 401 | Nan::To(arr->Get(Nan::GetCurrentContext(), 3).ToLocalChecked()).FromJust(), 402 | Nan::To(arr->Get(Nan::GetCurrentContext(), 4).ToLocalChecked()).FromJust(), 403 | Nan::To(arr->Get(Nan::GetCurrentContext(), 5).ToLocalChecked()).FromJust(), 404 | Nan::To(arr->Get(Nan::GetCurrentContext(), 6).ToLocalChecked()).FromJust() 405 | ); 406 | case 8: 407 | return ((callback8) addr)( 408 | Nan::To(arr->Get(Nan::GetCurrentContext(), 0).ToLocalChecked()).FromJust(), 409 | Nan::To(arr->Get(Nan::GetCurrentContext(), 1).ToLocalChecked()).FromJust(), 410 | Nan::To(arr->Get(Nan::GetCurrentContext(), 2).ToLocalChecked()).FromJust(), 411 | Nan::To(arr->Get(Nan::GetCurrentContext(), 3).ToLocalChecked()).FromJust(), 412 | Nan::To(arr->Get(Nan::GetCurrentContext(), 4).ToLocalChecked()).FromJust(), 413 | Nan::To(arr->Get(Nan::GetCurrentContext(), 5).ToLocalChecked()).FromJust(), 414 | Nan::To(arr->Get(Nan::GetCurrentContext(), 6).ToLocalChecked()).FromJust(), 415 | Nan::To(arr->Get(Nan::GetCurrentContext(), 7).ToLocalChecked()).FromJust() 416 | ); 417 | case 9: 418 | return ((callback9) addr)( 419 | Nan::To(arr->Get(Nan::GetCurrentContext(), 0).ToLocalChecked()).FromJust(), 420 | Nan::To(arr->Get(Nan::GetCurrentContext(), 1).ToLocalChecked()).FromJust(), 421 | Nan::To(arr->Get(Nan::GetCurrentContext(), 2).ToLocalChecked()).FromJust(), 422 | Nan::To(arr->Get(Nan::GetCurrentContext(), 3).ToLocalChecked()).FromJust(), 423 | Nan::To(arr->Get(Nan::GetCurrentContext(), 4).ToLocalChecked()).FromJust(), 424 | Nan::To(arr->Get(Nan::GetCurrentContext(), 5).ToLocalChecked()).FromJust(), 425 | Nan::To(arr->Get(Nan::GetCurrentContext(), 6).ToLocalChecked()).FromJust(), 426 | Nan::To(arr->Get(Nan::GetCurrentContext(), 7).ToLocalChecked()).FromJust(), 427 | Nan::To(arr->Get(Nan::GetCurrentContext(), 8).ToLocalChecked()).FromJust() 428 | ); 429 | case 10: 430 | return ((callback10) addr)( 431 | Nan::To(arr->Get(Nan::GetCurrentContext(), 0).ToLocalChecked()).FromJust(), 432 | Nan::To(arr->Get(Nan::GetCurrentContext(), 1).ToLocalChecked()).FromJust(), 433 | Nan::To(arr->Get(Nan::GetCurrentContext(), 2).ToLocalChecked()).FromJust(), 434 | Nan::To(arr->Get(Nan::GetCurrentContext(), 3).ToLocalChecked()).FromJust(), 435 | Nan::To(arr->Get(Nan::GetCurrentContext(), 4).ToLocalChecked()).FromJust(), 436 | Nan::To(arr->Get(Nan::GetCurrentContext(), 5).ToLocalChecked()).FromJust(), 437 | Nan::To(arr->Get(Nan::GetCurrentContext(), 6).ToLocalChecked()).FromJust(), 438 | Nan::To(arr->Get(Nan::GetCurrentContext(), 7).ToLocalChecked()).FromJust(), 439 | Nan::To(arr->Get(Nan::GetCurrentContext(), 8).ToLocalChecked()).FromJust(), 440 | Nan::To(arr->Get(Nan::GetCurrentContext(), 9).ToLocalChecked()).FromJust() 441 | ); 442 | default: 443 | Nan::ThrowError("Too many arguments."); 444 | return -1; 445 | } 446 | } 447 | 448 | void MethodCall(const FunctionCallbackInfo& args) { 449 | int64_t result = call_method(args); 450 | V8_RETURN_NUM(result); 451 | } 452 | 453 | void MethodCall64(const FunctionCallbackInfo& args) { 454 | int64_t result = call_method(args); 455 | V8_RETURN_NUM64(result); 456 | } 457 | 458 | void MethodCall_0(const FunctionCallbackInfo& args) { 459 | uint64_t addr = ArgToInt(args[0]); 460 | int64_t result = ((callback) addr)(); 461 | V8_RETURN_NUM(result); 462 | } 463 | 464 | void MethodCall64_0(const FunctionCallbackInfo& args) { 465 | uint64_t addr = ArgToInt(args[0]); 466 | int64_t result = ((callback) addr)(); 467 | V8_RETURN_NUM64(result); 468 | } 469 | 470 | void MethodCall_1(const FunctionCallbackInfo& args) { 471 | uint64_t addr = ArgToInt(args[0]); 472 | int64_t arg1 = ArgToInt(args[1]); 473 | int64_t result = ((callback1) addr)(arg1); 474 | V8_RETURN_NUM(result); 475 | } 476 | 477 | void MethodCall64_1(const FunctionCallbackInfo& args) { 478 | uint64_t addr = ArgToInt(args[0]); 479 | int64_t arg1 = ArgToInt(args[1]); 480 | int64_t result = ((callback1) addr)(arg1); 481 | V8_RETURN_NUM64(result); 482 | } 483 | 484 | void MethodSigaction(const FunctionCallbackInfo& args) { 485 | int signum = Nan::To(args[0]).FromJust(); 486 | struct sigaction* nas = (struct sigaction*) ArgToInt(args[1]); 487 | struct sigaction* oas = (struct sigaction*) ArgToInt(args[2]); 488 | int result = sigaction(signum, nas, oas); 489 | V8_RETURN_NUM(result); 490 | } 491 | 492 | // void jumper(uint64_t id, uint64_t data, uint64_t size) { 493 | // Isolate* isolate = Isolate::GetCurrent(); 494 | // v8::MaybeLocal jumpers = _exports->Get(String::NewFromUtf8(isolate, "jumpers")).As(); 495 | 496 | // Local function = jumpers.ToLocalChecked()->Get(Integer::New(isolate, id)).As(); 497 | // Nan::Callback callback(function); 498 | 499 | // const unsigned argc = 2; 500 | // Local argv[] = {Int64ToArray(isolate, data), Integer::New(isolate, size)}; 501 | 502 | // Nan::Call(callback, argc, argv); 503 | // } 504 | 505 | // void MethodJumper(const FunctionCallbackInfo& args) { 506 | // Local function = Local::Cast(args[0]); 507 | // Nan::Callback callback(function); 508 | 509 | // const unsigned argc = 0; 510 | // Local argv[] = {}; 511 | 512 | // Nan::Call(callback, argc, argv); 513 | // } 514 | 515 | void DLSym(const FunctionCallbackInfo& args) { 516 | Nan::Utf8String v8str(args[0]); 517 | std::string cppstr = std::string(*v8str); 518 | const char* cstr = cppstr.c_str(); 519 | uint64_t result = (uint64_t) dlsym(RTLD_DEFAULT, cstr); 520 | V8_RETURN_NUM64(result); 521 | } 522 | 523 | void TestDlsymAddr(const FunctionCallbackInfo& args) { 524 | uint64_t result = (uint64_t) dlsym; 525 | V8_RETURN_NUM64(result); 526 | } 527 | 528 | void CmpXchg8(const FunctionCallbackInfo& args) { 529 | int8_t* ptr = (int8_t*) ArgToInt(args[0]); 530 | int8_t newval = (int8_t) Nan::To(args[2]).FromJust(); 531 | int8_t oldval = (int8_t) Nan::To(args[1]).FromJust(); 532 | int8_t result = cmpxchg8(ptr, oldval, newval); 533 | V8_RETURN_NUM(result); 534 | } 535 | 536 | void CmpXchg16(const FunctionCallbackInfo& args) { 537 | int16_t* ptr = (int16_t*) ArgToInt(args[0]); 538 | int16_t newval = (int8_t) Nan::To(args[2]).FromJust(); 539 | int16_t oldval = (int8_t) Nan::To(args[1]).FromJust(); 540 | int16_t result = cmpxchg16(ptr, oldval, newval); 541 | V8_RETURN_NUM(result); 542 | } 543 | 544 | void CmpXchg32(const FunctionCallbackInfo& args) { 545 | int32_t* ptr = (int32_t*) ArgToInt(args[0]); 546 | int32_t oldval = Nan::To(args[1]).FromJust(); 547 | int32_t newval = Nan::To(args[2]).FromJust(); 548 | int32_t result = cmpxchg32(ptr, oldval, newval); 549 | V8_RETURN_NUM(result); 550 | } 551 | 552 | void Async(const FunctionCallbackInfo& args) { 553 | void* init_record_addr = (void*) ArgToInt(args[0]); 554 | uint32_t nthreads = (uint32_t) ArgToInt(args[1]); 555 | int res = create_async_pool(init_record_addr, nthreads); 556 | V8_RETURN_NUM(res); 557 | } 558 | 559 | void init(Local exports) { 560 | _exports = exports; 561 | 562 | NODE_SET_METHOD(exports, "syscall", MethodSyscall); 563 | NODE_SET_METHOD(exports, "syscall64", MethodSyscall64); 564 | NODE_SET_METHOD(exports, "syscall_0", MethodSyscall_0); 565 | NODE_SET_METHOD(exports, "syscall_1", MethodSyscall_1); 566 | NODE_SET_METHOD(exports, "syscall_2", MethodSyscall_2); 567 | NODE_SET_METHOD(exports, "syscall_3", MethodSyscall_3); 568 | NODE_SET_METHOD(exports, "syscall_4", MethodSyscall_4); 569 | NODE_SET_METHOD(exports, "syscall_5", MethodSyscall_5); 570 | NODE_SET_METHOD(exports, "syscall_6", MethodSyscall_6); 571 | NODE_SET_METHOD(exports, "syscall64_0", MethodSyscall64_0); 572 | NODE_SET_METHOD(exports, "syscall64_1", MethodSyscall64_1); 573 | NODE_SET_METHOD(exports, "syscall64_2", MethodSyscall64_2); 574 | NODE_SET_METHOD(exports, "syscall64_3", MethodSyscall64_3); 575 | NODE_SET_METHOD(exports, "syscall64_4", MethodSyscall64_4); 576 | NODE_SET_METHOD(exports, "syscall64_5", MethodSyscall64_5); 577 | NODE_SET_METHOD(exports, "syscall64_6", MethodSyscall64_6); 578 | NODE_SET_METHOD(exports, "getAddressArrayBuffer", MethodGetAddressArrayBuffer); 579 | NODE_SET_METHOD(exports, "getAddressTypedArray", MethodGetAddressTypedArray); 580 | NODE_SET_METHOD(exports, "getAddressBuffer", MethodGetAddressBuffer); 581 | NODE_SET_METHOD(exports, "getAddress", MethodGetAddress); 582 | NODE_SET_METHOD(exports, "frame", MethodFrame); 583 | NODE_SET_METHOD(exports, "call", MethodCall); 584 | NODE_SET_METHOD(exports, "call64", MethodCall64); 585 | NODE_SET_METHOD(exports, "call_0", MethodCall_0); 586 | NODE_SET_METHOD(exports, "call_1", MethodCall_1); 587 | NODE_SET_METHOD(exports, "call64_0", MethodCall64_0); 588 | NODE_SET_METHOD(exports, "call64_1", MethodCall64_1); 589 | // NODE_SET_METHOD(exports, "jumper", MethodJumper); 590 | NODE_SET_METHOD(exports, "sigaction", MethodSigaction); 591 | NODE_SET_METHOD(exports, "dlsym", DLSym); 592 | NODE_SET_METHOD(exports, "__testDlsymAddr", TestDlsymAddr); 593 | NODE_SET_METHOD(exports, "cmpxchg8", CmpXchg8); 594 | NODE_SET_METHOD(exports, "cmpxchg16", CmpXchg16); 595 | NODE_SET_METHOD(exports, "cmpxchg32", CmpXchg32); 596 | NODE_SET_METHOD(exports, "async", Async); 597 | } 598 | 599 | NODE_MODULE(addon, init) 600 | } 601 | --------------------------------------------------------------------------------