Skip to content

Instantly share code, notes, and snippets.

@bnoordhuis
Created October 31, 2012 04:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bnoordhuis/5e2422c8dea2c8579379 to your computer and use it in GitHub Desktop.
Save bnoordhuis/5e2422c8dea2c8579379 to your computer and use it in GitHub Desktop.
node.js ffi
# Copyright (c) 2012, Ben Noordhuis <info@bnoordhuis.nl>
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
{
'targets': [
{
'target_name': 'ffi',
'type': 'shared_library',
'sources': ['src/ffi.cc'],
'conditions': [
['OS=="linux"', { 'libraries': ['-ldl'] }]
]
}
]
}
/*
* Copyright (c) 2012, Ben Noordhuis <info@bnoordhuis.nl>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "v8.h"
#include "node.h" // NODE_MODULE
namespace
{
using namespace v8;
void Initialize(Handle<Object> obj)
{
}
} // anonymous namespace
NODE_MODULE(ffi, Initialize)
/*
* Copyright (c) 2012, Ben Noordhuis <info@bnoordhuis.nl>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
if (process.arch === 'ia32')
module.exports = require('./ia32/ffi');
else
throw new Error('Architecture not supported: ' + process.arch);
/*
* Copyright (c) 2012, Ben Noordhuis <info@bnoordhuis.nl>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
var assert = require('assert');
var masm = require('./masm');
function ccall(fun, proto, options)
{
return ffi('ccall', fun, proto, options);
}
function ffi(type, fun, proto, options)
{
options = options || {};
// sanity checks
assert(type === 'ccall');
assert(-1 !== 'cdfilv'.indexOf(proto[0]));
for (var i = 1, k = proto.length; i < k; ++i) {
assert(-1 !== 'cdfil'.indexOf(proto[i]));
}
// the macro assembler uses AT&T syntax, i.e. opcode(src, dst)
if (type === 'ccall') {
with (new masm.MASM) {
push(ebp); // 55
mov(esp, ebp); // 89 e5
mov(ebp(8), edx); // 8b 55 08
mov(edx, ebp(8)); // 89 55 08
mov(edx, ebp(8)); // 89 55 08
pop(eax); //
pop(ecx); //
pop(ebp); // 5d
ret(); // c3
return $.slice();
}
}
}
exports.ccall = ccall;
/*
* Copyright (c) 2012, Ben Noordhuis <info@bnoordhuis.nl>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
var assert = require('assert');
var masm = require('../lib/ia32/masm');
var tests = [
function push_eax(m)
{
with (m) push(eax);
return [0x50];
},
function push_eax_ecx(m)
{
with (m) push(eax);
with (m) push(ecx);
return [0x50, 0x51];
},
function pop_eax(m)
{
with (m) pop(eax);
return [0x58];
},
function pop_eax_ecx(m)
{
with (m) pop(eax);
with (m) pop(ecx);
return [0x58, 0x59];
},
function pop_ind_eax(m)
{
with (m) pop(eax()); // pop (%eax)
return [0x8f, 0x00];
},
function pop_0x100000_eax(m)
{
with (m) pop(eax(0x100000)); // pop 0x100000(%eax)
return [0x8f, 0x80, 0x00, 0x00, 0x10, 0x00];
},
function ret(m)
{
with (m) ret();
return [0xc3];
},
];
var failed = [];
tests.forEach(function(test) {
var name = test.name;
var m = new masm.MASM;
try {
var expected = test(m);
} catch (e) {
console.error('%s: %s', name, e.stack);
return failed.push(test.name);
}
var actual = m.$.slice();
try {
assert.deepEqual(actual, expected);
} catch (e) {
console.error('%s: %s', name, e.stack);
return failed.push(test.name);
}
});
if (failed.length !== 0) {
console.log('FAIL: %s', failed.join(', '));
process.exit(1);
}
/*
* Copyright (c) 2012, Ben Noordhuis <info@bnoordhuis.nl>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
var assert = require('assert');
function Register(id, index)
{
this.index = index;
this.id = id;
}
Register.prototype = new Function;
Register.make = function(id, index)
{
// Apply magic, make the Register both an object and a function.
// Consequences:
//
// reg instanceof Function
// reg instanceof Register
// reg.name === '' // function name (immutable, empty because anon)
// reg() // calls funcall()
// reg() !== reg // if funcall() returns this
// reg() === self
//
var self = new Register(id, index);
var reg = self.funcall.bind(self);
reg.__proto__ = self;
reg.index = index;
reg.id = id;
return reg;
};
Register.prototype.funcall = function(reg, disp) {
if (typeof reg === 'number')
disp = reg, reg = this;
else if (typeof reg === 'undefined')
disp = 0, reg = this;
return new Indirect(reg, disp);
};
function Indirect(reg, disp)
{
this.reg = reg;
this.disp = disp || 0;
}
function MASM()
{
this.$ = [];
this.$.imm8 = this.imm8.bind(this);
this.$.imm16 = this.imm16.bind(this);
this.$.imm32 = this.imm32.bind(this);
this.$.modrm = this.modrm.bind(this);
}
MASM.prototype.cs = Register.make('cs');
MASM.prototype.ss = Register.make('ss');
MASM.prototype.ds = Register.make('ds');
MASM.prototype.es = Register.make('es');
MASM.prototype.fs = Register.make('fs');
MASM.prototype.gs = Register.make('gs');
MASM.prototype.eax = Register.make('eax', 0);
MASM.prototype.ecx = Register.make('ecx', 1);
MASM.prototype.edx = Register.make('edx', 2);
MASM.prototype.ebx = Register.make('ebx', 3);
MASM.prototype.esp = Register.make('esp', 4);
MASM.prototype.ebp = Register.make('ebp', 5);
MASM.prototype.esi = Register.make('esi', 6);
MASM.prototype.edi = Register.make('edi', 7);
MASM.prototype.imm8 = function(val)
{
assert(typeof val === 'number');
if (val < 0) val += 0x100;
assert(val === (val & 0xff));
this.$.push(val);
};
MASM.prototype.imm16 = function(val)
{
assert(typeof val === 'number');
if (val < 0) val += 0x10000;
assert(val === (val & 0xffff));
this.$.push((val >>> 0) & 0xff,
(val >>> 8) & 0xff);
};
MASM.prototype.imm32 = function(val)
{
assert(typeof val === 'number');
if (val < 0) val += 0x100000000;
assert(val === (val & 0xffffffff));
this.$.push((val >>> 0) & 0xff,
(val >>> 8) & 0xff,
(val >>> 16) & 0xff,
(val >>> 24) & 0xff);
};
MASM.prototype.modrm = function(src, dst)
{
var $ = this.$;
if ((src instanceof Register) && (dst instanceof Register))
$.push(0xc0 | (src.index << 3) | dst.index);
else if ((src instanceof Register) && (dst instanceof Indirect)) {
// equiv. to this.modrm(dst, src)
if (dst.disp <= 255) {
$.push(0x40 | (src.index << 3) | dst.reg.index); // disp8
$.imm8(dst.disp);
}
else {
$.push(0x80 | (src.index << 3) | dst.reg.index); // disp32
$.imm32(dst.disp);
}
}
else if ((src instanceof Indirect) && (dst instanceof Register)) {
if (src.disp <= 255) {
$.push(0x40 | (dst.index << 3) | src.reg.index); // disp8
$.imm8(src.disp);
}
else {
$.push(0x80 | (dst.index << 3) | src.reg.index); // disp32
$.imm32(src.disp);
}
}
else if ((src instanceof Indirect) && typeof dst === 'undefined') {
if (src.disp === 0)
$.push(src.reg.index);
else if (src.disp <= 255) {
$.push(0x40 | src.reg.index);
$.imm8(src.disp);
}
else {
$.push(0x80 | src.reg.index);
$.imm32(src.disp);
}
}
else
assert();
};
MASM.prototype.mov = function(src, dst)
{
var $ = this.$;
assert(typeof src !== 'undefined');
assert(typeof dst !== 'undefined');
assert(isNaN(dst)); // not a literal
if (src instanceof Register) {
assert((dst instanceof Register) || (dst instanceof Indirect));
$.push(0x89);
$.modrm(src, dst);
}
else if (src instanceof Indirect) {
assert(dst instanceof Register);
$.push(0x8b);
$.modrm(src, dst);
}
else if (isNaN(src) === false) { // literal
assert(dst instanceof Register);
$.push(0xb8 + dst.index);
$.imm32(src);
}
};
MASM.prototype.push = function(arg)
{
var $ = this.$;
assert(typeof arg !== 'undefined');
if (arg === this.cs)
$.push(0x0e);
else if (arg === this.ds)
$.push(0x1e);
else if (arg === this.es)
$.push(0x06);
else if (arg === this.fs)
$.push(0x0f, 0xa0);
else if (arg === this.gs)
$.push(0x0f, 0xa8);
else if (arg === this.ss)
$.push(0x16);
else if (arg instanceof Register)
$.push(0x50 + arg.index);
else if (isNaN(arg) === false) { // literal
$.push(0x68);
$.imm32(arg);
}
else
assert();
};
MASM.prototype.pop = function(arg)
{
var $ = this.$;
assert(typeof arg !== 'undefined');
if (arg instanceof Register)
$.push(0x58 + arg.index);
else if (arg instanceof Indirect) {
$.push(0x8f);
$.modrm(arg);
}
else
assert();
};
MASM.prototype.ret = function(arg)
{
this.$.push(0xc3);
};
exports.MASM = MASM;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment