mirror of
https://github.com/vxcontrol/lualibs-dynasm.git
synced 2026-07-01 13:19:14 -04:00
Lua mode, binaries, build scripts, docs
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
DynASM 1.4.0 from https://github.com/LuaJIT/LuaJIT (MIT License)
|
||||
|
||||
|
||||
|
||||
The encoder part written in C was not modified.
|
||||
The Lua part was modified to support Lua mode (see docs for details).
|
||||
Executable
+1
@@ -0,0 +1 @@
|
||||
P=linux32 L="-s -static-libgcc" D=libdasm_x86.so A=libdasm_x86.a ./build.sh
|
||||
Executable
+1
@@ -0,0 +1 @@
|
||||
P=linux64 C=-fPIC L="-s -static-libgcc" D=libdasm_x86.so A=libdasm_x86.a ./build.sh
|
||||
Executable
+1
@@ -0,0 +1 @@
|
||||
P=mingw32 L="-s -static-libgcc" D=dasm_x86.dll A=dasm_x86.a ./build.sh
|
||||
Executable
+1
@@ -0,0 +1 @@
|
||||
P=mingw64 L="-s -static-libgcc" D=dasm_x86.dll A=dasm_x86.a ./build.sh
|
||||
Executable
+2
@@ -0,0 +1,2 @@
|
||||
P=osx32 C="-arch i386" L="-arch i386 -install_name @rpath/libdasm_x86.dylib" \
|
||||
D=libdasm_x86.dylib A=libdasm_x86.a ./build.sh
|
||||
Executable
+2
@@ -0,0 +1,2 @@
|
||||
P=osx64 C="-arch x86_64" L="-arch x86_64 -install_name @rpath/libdasm_x86.dylib" \
|
||||
D=libdasm_x86.dylib A=libdasm_x86.a ./build.sh
|
||||
Executable
+4
@@ -0,0 +1,4 @@
|
||||
gcc -c -O2 $C dasm_x86.c -DDASM_CHECKS
|
||||
gcc *.o -shared $L -o ../../bin/$P/$D
|
||||
ar rcs ../../bin/$P/$A *.o
|
||||
rm *.o
|
||||
@@ -1,456 +0,0 @@
|
||||
/*
|
||||
** DynASM ARM encoding engine.
|
||||
** Copyright (C) 2005-2015 Mike Pall. All rights reserved.
|
||||
** Released under the MIT license. See dynasm.lua for full copyright notice.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define DASM_ARCH "arm"
|
||||
|
||||
#ifndef DASM_EXTERN
|
||||
#define DASM_EXTERN(a,b,c,d) 0
|
||||
#endif
|
||||
|
||||
/* Action definitions. */
|
||||
enum {
|
||||
DASM_STOP, DASM_SECTION, DASM_ESC, DASM_REL_EXT,
|
||||
/* The following actions need a buffer position. */
|
||||
DASM_ALIGN, DASM_REL_LG, DASM_LABEL_LG,
|
||||
/* The following actions also have an argument. */
|
||||
DASM_REL_PC, DASM_LABEL_PC,
|
||||
DASM_IMM, DASM_IMM12, DASM_IMM16, DASM_IMML8, DASM_IMML12, DASM_IMMV8,
|
||||
DASM__MAX
|
||||
};
|
||||
|
||||
/* Maximum number of section buffer positions for a single dasm_put() call. */
|
||||
#define DASM_MAXSECPOS 25
|
||||
|
||||
/* DynASM encoder status codes. Action list offset or number are or'ed in. */
|
||||
#define DASM_S_OK 0x00000000
|
||||
#define DASM_S_NOMEM 0x01000000
|
||||
#define DASM_S_PHASE 0x02000000
|
||||
#define DASM_S_MATCH_SEC 0x03000000
|
||||
#define DASM_S_RANGE_I 0x11000000
|
||||
#define DASM_S_RANGE_SEC 0x12000000
|
||||
#define DASM_S_RANGE_LG 0x13000000
|
||||
#define DASM_S_RANGE_PC 0x14000000
|
||||
#define DASM_S_RANGE_REL 0x15000000
|
||||
#define DASM_S_UNDEF_LG 0x21000000
|
||||
#define DASM_S_UNDEF_PC 0x22000000
|
||||
|
||||
/* Macros to convert positions (8 bit section + 24 bit index). */
|
||||
#define DASM_POS2IDX(pos) ((pos)&0x00ffffff)
|
||||
#define DASM_POS2BIAS(pos) ((pos)&0xff000000)
|
||||
#define DASM_SEC2POS(sec) ((sec)<<24)
|
||||
#define DASM_POS2SEC(pos) ((pos)>>24)
|
||||
#define DASM_POS2PTR(D, pos) (D->sections[DASM_POS2SEC(pos)].rbuf + (pos))
|
||||
|
||||
/* Action list type. */
|
||||
typedef const unsigned int *dasm_ActList;
|
||||
|
||||
/* Per-section structure. */
|
||||
typedef struct dasm_Section {
|
||||
int *rbuf; /* Biased buffer pointer (negative section bias). */
|
||||
int *buf; /* True buffer pointer. */
|
||||
size_t bsize; /* Buffer size in bytes. */
|
||||
int pos; /* Biased buffer position. */
|
||||
int epos; /* End of biased buffer position - max single put. */
|
||||
int ofs; /* Byte offset into section. */
|
||||
} dasm_Section;
|
||||
|
||||
/* Core structure holding the DynASM encoding state. */
|
||||
struct dasm_State {
|
||||
size_t psize; /* Allocated size of this structure. */
|
||||
dasm_ActList actionlist; /* Current actionlist pointer. */
|
||||
int *lglabels; /* Local/global chain/pos ptrs. */
|
||||
size_t lgsize;
|
||||
int *pclabels; /* PC label chains/pos ptrs. */
|
||||
size_t pcsize;
|
||||
void **globals; /* Array of globals (bias -10). */
|
||||
dasm_Section *section; /* Pointer to active section. */
|
||||
size_t codesize; /* Total size of all code sections. */
|
||||
int maxsection; /* 0 <= sectionidx < maxsection. */
|
||||
int status; /* Status code. */
|
||||
dasm_Section sections[1]; /* All sections. Alloc-extended. */
|
||||
};
|
||||
|
||||
/* The size of the core structure depends on the max. number of sections. */
|
||||
#define DASM_PSZ(ms) (sizeof(dasm_State)+(ms-1)*sizeof(dasm_Section))
|
||||
|
||||
|
||||
/* Initialize DynASM state. */
|
||||
void dasm_init(Dst_DECL, int maxsection)
|
||||
{
|
||||
dasm_State *D;
|
||||
size_t psz = 0;
|
||||
int i;
|
||||
Dst_REF = NULL;
|
||||
DASM_M_GROW(Dst, struct dasm_State, Dst_REF, psz, DASM_PSZ(maxsection));
|
||||
D = Dst_REF;
|
||||
D->psize = psz;
|
||||
D->lglabels = NULL;
|
||||
D->lgsize = 0;
|
||||
D->pclabels = NULL;
|
||||
D->pcsize = 0;
|
||||
D->globals = NULL;
|
||||
D->maxsection = maxsection;
|
||||
for (i = 0; i < maxsection; i++) {
|
||||
D->sections[i].buf = NULL; /* Need this for pass3. */
|
||||
D->sections[i].rbuf = D->sections[i].buf - DASM_SEC2POS(i);
|
||||
D->sections[i].bsize = 0;
|
||||
D->sections[i].epos = 0; /* Wrong, but is recalculated after resize. */
|
||||
}
|
||||
}
|
||||
|
||||
/* Free DynASM state. */
|
||||
void dasm_free(Dst_DECL)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
int i;
|
||||
for (i = 0; i < D->maxsection; i++)
|
||||
if (D->sections[i].buf)
|
||||
DASM_M_FREE(Dst, D->sections[i].buf, D->sections[i].bsize);
|
||||
if (D->pclabels) DASM_M_FREE(Dst, D->pclabels, D->pcsize);
|
||||
if (D->lglabels) DASM_M_FREE(Dst, D->lglabels, D->lgsize);
|
||||
DASM_M_FREE(Dst, D, D->psize);
|
||||
}
|
||||
|
||||
/* Setup global label array. Must be called before dasm_setup(). */
|
||||
void dasm_setupglobal(Dst_DECL, void **gl, unsigned int maxgl)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
D->globals = gl - 10; /* Negative bias to compensate for locals. */
|
||||
DASM_M_GROW(Dst, int, D->lglabels, D->lgsize, (10+maxgl)*sizeof(int));
|
||||
}
|
||||
|
||||
/* Grow PC label array. Can be called after dasm_setup(), too. */
|
||||
void dasm_growpc(Dst_DECL, unsigned int maxpc)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
size_t osz = D->pcsize;
|
||||
DASM_M_GROW(Dst, int, D->pclabels, D->pcsize, maxpc*sizeof(int));
|
||||
memset((void *)(((unsigned char *)D->pclabels)+osz), 0, D->pcsize-osz);
|
||||
}
|
||||
|
||||
/* Setup encoder. */
|
||||
void dasm_setup(Dst_DECL, const void *actionlist)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
int i;
|
||||
D->actionlist = (dasm_ActList)actionlist;
|
||||
D->status = DASM_S_OK;
|
||||
D->section = &D->sections[0];
|
||||
memset((void *)D->lglabels, 0, D->lgsize);
|
||||
if (D->pclabels) memset((void *)D->pclabels, 0, D->pcsize);
|
||||
for (i = 0; i < D->maxsection; i++) {
|
||||
D->sections[i].pos = DASM_SEC2POS(i);
|
||||
D->sections[i].ofs = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef DASM_CHECKS
|
||||
#define CK(x, st) \
|
||||
do { if (!(x)) { \
|
||||
D->status = DASM_S_##st|(p-D->actionlist-1); return; } } while (0)
|
||||
#define CKPL(kind, st) \
|
||||
do { if ((size_t)((char *)pl-(char *)D->kind##labels) >= D->kind##size) { \
|
||||
D->status = DASM_S_RANGE_##st|(p-D->actionlist-1); return; } } while (0)
|
||||
#else
|
||||
#define CK(x, st) ((void)0)
|
||||
#define CKPL(kind, st) ((void)0)
|
||||
#endif
|
||||
|
||||
static int dasm_imm12(unsigned int n)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < 16; i++, n = (n << 2) | (n >> 30))
|
||||
if (n <= 255) return (int)(n + (i << 8));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Pass 1: Store actions and args, link branches/labels, estimate offsets. */
|
||||
void dasm_put(Dst_DECL, int start, ...)
|
||||
{
|
||||
va_list ap;
|
||||
dasm_State *D = Dst_REF;
|
||||
dasm_ActList p = D->actionlist + start;
|
||||
dasm_Section *sec = D->section;
|
||||
int pos = sec->pos, ofs = sec->ofs;
|
||||
int *b;
|
||||
|
||||
if (pos >= sec->epos) {
|
||||
DASM_M_GROW(Dst, int, sec->buf, sec->bsize,
|
||||
sec->bsize + 2*DASM_MAXSECPOS*sizeof(int));
|
||||
sec->rbuf = sec->buf - DASM_POS2BIAS(pos);
|
||||
sec->epos = (int)sec->bsize/sizeof(int) - DASM_MAXSECPOS+DASM_POS2BIAS(pos);
|
||||
}
|
||||
|
||||
b = sec->rbuf;
|
||||
b[pos++] = start;
|
||||
|
||||
va_start(ap, start);
|
||||
while (1) {
|
||||
unsigned int ins = *p++;
|
||||
unsigned int action = (ins >> 16);
|
||||
if (action >= DASM__MAX) {
|
||||
ofs += 4;
|
||||
} else {
|
||||
int *pl, n = action >= DASM_REL_PC ? va_arg(ap, int) : 0;
|
||||
switch (action) {
|
||||
case DASM_STOP: goto stop;
|
||||
case DASM_SECTION:
|
||||
n = (ins & 255); CK(n < D->maxsection, RANGE_SEC);
|
||||
D->section = &D->sections[n]; goto stop;
|
||||
case DASM_ESC: p++; ofs += 4; break;
|
||||
case DASM_REL_EXT: break;
|
||||
case DASM_ALIGN: ofs += (ins & 255); b[pos++] = ofs; break;
|
||||
case DASM_REL_LG:
|
||||
n = (ins & 2047) - 10; pl = D->lglabels + n;
|
||||
/* Bkwd rel or global. */
|
||||
if (n >= 0) { CK(n>=10||*pl<0, RANGE_LG); CKPL(lg, LG); goto putrel; }
|
||||
pl += 10; n = *pl;
|
||||
if (n < 0) n = 0; /* Start new chain for fwd rel if label exists. */
|
||||
goto linkrel;
|
||||
case DASM_REL_PC:
|
||||
pl = D->pclabels + n; CKPL(pc, PC);
|
||||
putrel:
|
||||
n = *pl;
|
||||
if (n < 0) { /* Label exists. Get label pos and store it. */
|
||||
b[pos] = -n;
|
||||
} else {
|
||||
linkrel:
|
||||
b[pos] = n; /* Else link to rel chain, anchored at label. */
|
||||
*pl = pos;
|
||||
}
|
||||
pos++;
|
||||
break;
|
||||
case DASM_LABEL_LG:
|
||||
pl = D->lglabels + (ins & 2047) - 10; CKPL(lg, LG); goto putlabel;
|
||||
case DASM_LABEL_PC:
|
||||
pl = D->pclabels + n; CKPL(pc, PC);
|
||||
putlabel:
|
||||
n = *pl; /* n > 0: Collapse rel chain and replace with label pos. */
|
||||
while (n > 0) { int *pb = DASM_POS2PTR(D, n); n = *pb; *pb = pos;
|
||||
}
|
||||
*pl = -pos; /* Label exists now. */
|
||||
b[pos++] = ofs; /* Store pass1 offset estimate. */
|
||||
break;
|
||||
case DASM_IMM:
|
||||
case DASM_IMM16:
|
||||
#ifdef DASM_CHECKS
|
||||
CK((n & ((1<<((ins>>10)&31))-1)) == 0, RANGE_I);
|
||||
if ((ins & 0x8000))
|
||||
CK(((n + (1<<(((ins>>5)&31)-1)))>>((ins>>5)&31)) == 0, RANGE_I);
|
||||
else
|
||||
CK((n>>((ins>>5)&31)) == 0, RANGE_I);
|
||||
#endif
|
||||
b[pos++] = n;
|
||||
break;
|
||||
case DASM_IMMV8:
|
||||
CK((n & 3) == 0, RANGE_I);
|
||||
n >>= 2;
|
||||
case DASM_IMML8:
|
||||
case DASM_IMML12:
|
||||
CK(n >= 0 ? ((n>>((ins>>5)&31)) == 0) :
|
||||
(((-n)>>((ins>>5)&31)) == 0), RANGE_I);
|
||||
b[pos++] = n;
|
||||
break;
|
||||
case DASM_IMM12:
|
||||
CK(dasm_imm12((unsigned int)n) != -1, RANGE_I);
|
||||
b[pos++] = n;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
stop:
|
||||
va_end(ap);
|
||||
sec->pos = pos;
|
||||
sec->ofs = ofs;
|
||||
}
|
||||
#undef CK
|
||||
|
||||
/* Pass 2: Link sections, shrink aligns, fix label offsets. */
|
||||
int dasm_link(Dst_DECL, size_t *szp)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
int secnum;
|
||||
int ofs = 0;
|
||||
|
||||
#ifdef DASM_CHECKS
|
||||
*szp = 0;
|
||||
if (D->status != DASM_S_OK) return D->status;
|
||||
{
|
||||
int pc;
|
||||
for (pc = 0; pc*sizeof(int) < D->pcsize; pc++)
|
||||
if (D->pclabels[pc] > 0) return DASM_S_UNDEF_PC|pc;
|
||||
}
|
||||
#endif
|
||||
|
||||
{ /* Handle globals not defined in this translation unit. */
|
||||
int idx;
|
||||
for (idx = 20; idx*sizeof(int) < D->lgsize; idx++) {
|
||||
int n = D->lglabels[idx];
|
||||
/* Undefined label: Collapse rel chain and replace with marker (< 0). */
|
||||
while (n > 0) { int *pb = DASM_POS2PTR(D, n); n = *pb; *pb = -idx; }
|
||||
}
|
||||
}
|
||||
|
||||
/* Combine all code sections. No support for data sections (yet). */
|
||||
for (secnum = 0; secnum < D->maxsection; secnum++) {
|
||||
dasm_Section *sec = D->sections + secnum;
|
||||
int *b = sec->rbuf;
|
||||
int pos = DASM_SEC2POS(secnum);
|
||||
int lastpos = sec->pos;
|
||||
|
||||
while (pos != lastpos) {
|
||||
dasm_ActList p = D->actionlist + b[pos++];
|
||||
while (1) {
|
||||
unsigned int ins = *p++;
|
||||
unsigned int action = (ins >> 16);
|
||||
switch (action) {
|
||||
case DASM_STOP: case DASM_SECTION: goto stop;
|
||||
case DASM_ESC: p++; break;
|
||||
case DASM_REL_EXT: break;
|
||||
case DASM_ALIGN: ofs -= (b[pos++] + ofs) & (ins & 255); break;
|
||||
case DASM_REL_LG: case DASM_REL_PC: pos++; break;
|
||||
case DASM_LABEL_LG: case DASM_LABEL_PC: b[pos++] += ofs; break;
|
||||
case DASM_IMM: case DASM_IMM12: case DASM_IMM16:
|
||||
case DASM_IMML8: case DASM_IMML12: case DASM_IMMV8: pos++; break;
|
||||
}
|
||||
}
|
||||
stop: (void)0;
|
||||
}
|
||||
ofs += sec->ofs; /* Next section starts right after current section. */
|
||||
}
|
||||
|
||||
D->codesize = ofs; /* Total size of all code sections */
|
||||
*szp = ofs;
|
||||
return DASM_S_OK;
|
||||
}
|
||||
|
||||
#ifdef DASM_CHECKS
|
||||
#define CK(x, st) \
|
||||
do { if (!(x)) return DASM_S_##st|(p-D->actionlist-1); } while (0)
|
||||
#else
|
||||
#define CK(x, st) ((void)0)
|
||||
#endif
|
||||
|
||||
/* Pass 3: Encode sections. */
|
||||
int dasm_encode(Dst_DECL, void *buffer)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
char *base = (char *)buffer;
|
||||
unsigned int *cp = (unsigned int *)buffer;
|
||||
int secnum;
|
||||
|
||||
/* Encode all code sections. No support for data sections (yet). */
|
||||
for (secnum = 0; secnum < D->maxsection; secnum++) {
|
||||
dasm_Section *sec = D->sections + secnum;
|
||||
int *b = sec->buf;
|
||||
int *endb = sec->rbuf + sec->pos;
|
||||
|
||||
while (b != endb) {
|
||||
dasm_ActList p = D->actionlist + *b++;
|
||||
while (1) {
|
||||
unsigned int ins = *p++;
|
||||
unsigned int action = (ins >> 16);
|
||||
int n = (action >= DASM_ALIGN && action < DASM__MAX) ? *b++ : 0;
|
||||
switch (action) {
|
||||
case DASM_STOP: case DASM_SECTION: goto stop;
|
||||
case DASM_ESC: *cp++ = *p++; break;
|
||||
case DASM_REL_EXT:
|
||||
n = DASM_EXTERN(Dst, (unsigned char *)cp, (ins&2047), !(ins&2048));
|
||||
goto patchrel;
|
||||
case DASM_ALIGN:
|
||||
ins &= 255; while ((((char *)cp - base) & ins)) *cp++ = 0xe1a00000;
|
||||
break;
|
||||
case DASM_REL_LG:
|
||||
CK(n >= 0, UNDEF_LG);
|
||||
case DASM_REL_PC:
|
||||
CK(n >= 0, UNDEF_PC);
|
||||
n = *DASM_POS2PTR(D, n) - (int)((char *)cp - base) - 4;
|
||||
patchrel:
|
||||
if ((ins & 0x800) == 0) {
|
||||
CK((n & 3) == 0 && ((n+0x02000000) >> 26) == 0, RANGE_REL);
|
||||
cp[-1] |= ((n >> 2) & 0x00ffffff);
|
||||
} else if ((ins & 0x1000)) {
|
||||
CK((n & 3) == 0 && -256 <= n && n <= 256, RANGE_REL);
|
||||
goto patchimml8;
|
||||
} else if ((ins & 0x2000) == 0) {
|
||||
CK((n & 3) == 0 && -4096 <= n && n <= 4096, RANGE_REL);
|
||||
goto patchimml;
|
||||
} else {
|
||||
CK((n & 3) == 0 && -1020 <= n && n <= 1020, RANGE_REL);
|
||||
n >>= 2;
|
||||
goto patchimml;
|
||||
}
|
||||
break;
|
||||
case DASM_LABEL_LG:
|
||||
ins &= 2047; if (ins >= 20) D->globals[ins-10] = (void *)(base + n);
|
||||
break;
|
||||
case DASM_LABEL_PC: break;
|
||||
case DASM_IMM:
|
||||
cp[-1] |= ((n>>((ins>>10)&31)) & ((1<<((ins>>5)&31))-1)) << (ins&31);
|
||||
break;
|
||||
case DASM_IMM12:
|
||||
cp[-1] |= dasm_imm12((unsigned int)n);
|
||||
break;
|
||||
case DASM_IMM16:
|
||||
cp[-1] |= ((n & 0xf000) << 4) | (n & 0x0fff);
|
||||
break;
|
||||
case DASM_IMML8: patchimml8:
|
||||
cp[-1] |= n >= 0 ? (0x00800000 | (n & 0x0f) | ((n & 0xf0) << 4)) :
|
||||
((-n & 0x0f) | ((-n & 0xf0) << 4));
|
||||
break;
|
||||
case DASM_IMML12: case DASM_IMMV8: patchimml:
|
||||
cp[-1] |= n >= 0 ? (0x00800000 | n) : (-n);
|
||||
break;
|
||||
default: *cp++ = ins; break;
|
||||
}
|
||||
}
|
||||
stop: (void)0;
|
||||
}
|
||||
}
|
||||
|
||||
if (base + D->codesize != (char *)cp) /* Check for phase errors. */
|
||||
return DASM_S_PHASE;
|
||||
return DASM_S_OK;
|
||||
}
|
||||
#undef CK
|
||||
|
||||
/* Get PC label offset. */
|
||||
int dasm_getpclabel(Dst_DECL, unsigned int pc)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
if (pc*sizeof(int) < D->pcsize) {
|
||||
int pos = D->pclabels[pc];
|
||||
if (pos < 0) return *DASM_POS2PTR(D, -pos);
|
||||
if (pos > 0) return -1; /* Undefined. */
|
||||
}
|
||||
return -2; /* Unused or out of range. */
|
||||
}
|
||||
|
||||
#ifdef DASM_CHECKS
|
||||
/* Optional sanity checker to call between isolated encoding steps. */
|
||||
int dasm_checkstep(Dst_DECL, int secmatch)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
if (D->status == DASM_S_OK) {
|
||||
int i;
|
||||
for (i = 1; i <= 9; i++) {
|
||||
if (D->lglabels[i] > 0) { D->status = DASM_S_UNDEF_LG|i; break; }
|
||||
D->lglabels[i] = 0;
|
||||
}
|
||||
}
|
||||
if (D->status == DASM_S_OK && secmatch >= 0 &&
|
||||
D->section != &D->sections[secmatch])
|
||||
D->status = DASM_S_MATCH_SEC|(D->section-D->sections);
|
||||
return D->status;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
// DASM_EXTERN_FUNC is a global function pointer to be set at runtime.
|
||||
// It will be used in place of the DASM_EXTERN() macro.
|
||||
typedef int (*DASM_EXTERN_TYPE) (void *ctx, unsigned char *addr, int idx, int type);
|
||||
DASM_EXTERN_TYPE DASM_EXTERN_FUNC;
|
||||
#define DASM_EXTERN(a,b,c,d) DASM_EXTERN_FUNC(a,b,c,d)
|
||||
@@ -1,416 +0,0 @@
|
||||
/*
|
||||
** DynASM MIPS encoding engine.
|
||||
** Copyright (C) 2005-2015 Mike Pall. All rights reserved.
|
||||
** Released under the MIT license. See dynasm.lua for full copyright notice.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define DASM_ARCH "mips"
|
||||
|
||||
#ifndef DASM_EXTERN
|
||||
#define DASM_EXTERN(a,b,c,d) 0
|
||||
#endif
|
||||
|
||||
/* Action definitions. */
|
||||
enum {
|
||||
DASM_STOP, DASM_SECTION, DASM_ESC, DASM_REL_EXT,
|
||||
/* The following actions need a buffer position. */
|
||||
DASM_ALIGN, DASM_REL_LG, DASM_LABEL_LG,
|
||||
/* The following actions also have an argument. */
|
||||
DASM_REL_PC, DASM_LABEL_PC, DASM_IMM,
|
||||
DASM__MAX
|
||||
};
|
||||
|
||||
/* Maximum number of section buffer positions for a single dasm_put() call. */
|
||||
#define DASM_MAXSECPOS 25
|
||||
|
||||
/* DynASM encoder status codes. Action list offset or number are or'ed in. */
|
||||
#define DASM_S_OK 0x00000000
|
||||
#define DASM_S_NOMEM 0x01000000
|
||||
#define DASM_S_PHASE 0x02000000
|
||||
#define DASM_S_MATCH_SEC 0x03000000
|
||||
#define DASM_S_RANGE_I 0x11000000
|
||||
#define DASM_S_RANGE_SEC 0x12000000
|
||||
#define DASM_S_RANGE_LG 0x13000000
|
||||
#define DASM_S_RANGE_PC 0x14000000
|
||||
#define DASM_S_RANGE_REL 0x15000000
|
||||
#define DASM_S_UNDEF_LG 0x21000000
|
||||
#define DASM_S_UNDEF_PC 0x22000000
|
||||
|
||||
/* Macros to convert positions (8 bit section + 24 bit index). */
|
||||
#define DASM_POS2IDX(pos) ((pos)&0x00ffffff)
|
||||
#define DASM_POS2BIAS(pos) ((pos)&0xff000000)
|
||||
#define DASM_SEC2POS(sec) ((sec)<<24)
|
||||
#define DASM_POS2SEC(pos) ((pos)>>24)
|
||||
#define DASM_POS2PTR(D, pos) (D->sections[DASM_POS2SEC(pos)].rbuf + (pos))
|
||||
|
||||
/* Action list type. */
|
||||
typedef const unsigned int *dasm_ActList;
|
||||
|
||||
/* Per-section structure. */
|
||||
typedef struct dasm_Section {
|
||||
int *rbuf; /* Biased buffer pointer (negative section bias). */
|
||||
int *buf; /* True buffer pointer. */
|
||||
size_t bsize; /* Buffer size in bytes. */
|
||||
int pos; /* Biased buffer position. */
|
||||
int epos; /* End of biased buffer position - max single put. */
|
||||
int ofs; /* Byte offset into section. */
|
||||
} dasm_Section;
|
||||
|
||||
/* Core structure holding the DynASM encoding state. */
|
||||
struct dasm_State {
|
||||
size_t psize; /* Allocated size of this structure. */
|
||||
dasm_ActList actionlist; /* Current actionlist pointer. */
|
||||
int *lglabels; /* Local/global chain/pos ptrs. */
|
||||
size_t lgsize;
|
||||
int *pclabels; /* PC label chains/pos ptrs. */
|
||||
size_t pcsize;
|
||||
void **globals; /* Array of globals (bias -10). */
|
||||
dasm_Section *section; /* Pointer to active section. */
|
||||
size_t codesize; /* Total size of all code sections. */
|
||||
int maxsection; /* 0 <= sectionidx < maxsection. */
|
||||
int status; /* Status code. */
|
||||
dasm_Section sections[1]; /* All sections. Alloc-extended. */
|
||||
};
|
||||
|
||||
/* The size of the core structure depends on the max. number of sections. */
|
||||
#define DASM_PSZ(ms) (sizeof(dasm_State)+(ms-1)*sizeof(dasm_Section))
|
||||
|
||||
|
||||
/* Initialize DynASM state. */
|
||||
void dasm_init(Dst_DECL, int maxsection)
|
||||
{
|
||||
dasm_State *D;
|
||||
size_t psz = 0;
|
||||
int i;
|
||||
Dst_REF = NULL;
|
||||
DASM_M_GROW(Dst, struct dasm_State, Dst_REF, psz, DASM_PSZ(maxsection));
|
||||
D = Dst_REF;
|
||||
D->psize = psz;
|
||||
D->lglabels = NULL;
|
||||
D->lgsize = 0;
|
||||
D->pclabels = NULL;
|
||||
D->pcsize = 0;
|
||||
D->globals = NULL;
|
||||
D->maxsection = maxsection;
|
||||
for (i = 0; i < maxsection; i++) {
|
||||
D->sections[i].buf = NULL; /* Need this for pass3. */
|
||||
D->sections[i].rbuf = D->sections[i].buf - DASM_SEC2POS(i);
|
||||
D->sections[i].bsize = 0;
|
||||
D->sections[i].epos = 0; /* Wrong, but is recalculated after resize. */
|
||||
}
|
||||
}
|
||||
|
||||
/* Free DynASM state. */
|
||||
void dasm_free(Dst_DECL)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
int i;
|
||||
for (i = 0; i < D->maxsection; i++)
|
||||
if (D->sections[i].buf)
|
||||
DASM_M_FREE(Dst, D->sections[i].buf, D->sections[i].bsize);
|
||||
if (D->pclabels) DASM_M_FREE(Dst, D->pclabels, D->pcsize);
|
||||
if (D->lglabels) DASM_M_FREE(Dst, D->lglabels, D->lgsize);
|
||||
DASM_M_FREE(Dst, D, D->psize);
|
||||
}
|
||||
|
||||
/* Setup global label array. Must be called before dasm_setup(). */
|
||||
void dasm_setupglobal(Dst_DECL, void **gl, unsigned int maxgl)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
D->globals = gl - 10; /* Negative bias to compensate for locals. */
|
||||
DASM_M_GROW(Dst, int, D->lglabels, D->lgsize, (10+maxgl)*sizeof(int));
|
||||
}
|
||||
|
||||
/* Grow PC label array. Can be called after dasm_setup(), too. */
|
||||
void dasm_growpc(Dst_DECL, unsigned int maxpc)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
size_t osz = D->pcsize;
|
||||
DASM_M_GROW(Dst, int, D->pclabels, D->pcsize, maxpc*sizeof(int));
|
||||
memset((void *)(((unsigned char *)D->pclabels)+osz), 0, D->pcsize-osz);
|
||||
}
|
||||
|
||||
/* Setup encoder. */
|
||||
void dasm_setup(Dst_DECL, const void *actionlist)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
int i;
|
||||
D->actionlist = (dasm_ActList)actionlist;
|
||||
D->status = DASM_S_OK;
|
||||
D->section = &D->sections[0];
|
||||
memset((void *)D->lglabels, 0, D->lgsize);
|
||||
if (D->pclabels) memset((void *)D->pclabels, 0, D->pcsize);
|
||||
for (i = 0; i < D->maxsection; i++) {
|
||||
D->sections[i].pos = DASM_SEC2POS(i);
|
||||
D->sections[i].ofs = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef DASM_CHECKS
|
||||
#define CK(x, st) \
|
||||
do { if (!(x)) { \
|
||||
D->status = DASM_S_##st|(p-D->actionlist-1); return; } } while (0)
|
||||
#define CKPL(kind, st) \
|
||||
do { if ((size_t)((char *)pl-(char *)D->kind##labels) >= D->kind##size) { \
|
||||
D->status = DASM_S_RANGE_##st|(p-D->actionlist-1); return; } } while (0)
|
||||
#else
|
||||
#define CK(x, st) ((void)0)
|
||||
#define CKPL(kind, st) ((void)0)
|
||||
#endif
|
||||
|
||||
/* Pass 1: Store actions and args, link branches/labels, estimate offsets. */
|
||||
void dasm_put(Dst_DECL, int start, ...)
|
||||
{
|
||||
va_list ap;
|
||||
dasm_State *D = Dst_REF;
|
||||
dasm_ActList p = D->actionlist + start;
|
||||
dasm_Section *sec = D->section;
|
||||
int pos = sec->pos, ofs = sec->ofs;
|
||||
int *b;
|
||||
|
||||
if (pos >= sec->epos) {
|
||||
DASM_M_GROW(Dst, int, sec->buf, sec->bsize,
|
||||
sec->bsize + 2*DASM_MAXSECPOS*sizeof(int));
|
||||
sec->rbuf = sec->buf - DASM_POS2BIAS(pos);
|
||||
sec->epos = (int)sec->bsize/sizeof(int) - DASM_MAXSECPOS+DASM_POS2BIAS(pos);
|
||||
}
|
||||
|
||||
b = sec->rbuf;
|
||||
b[pos++] = start;
|
||||
|
||||
va_start(ap, start);
|
||||
while (1) {
|
||||
unsigned int ins = *p++;
|
||||
unsigned int action = (ins >> 16) - 0xff00;
|
||||
if (action >= DASM__MAX) {
|
||||
ofs += 4;
|
||||
} else {
|
||||
int *pl, n = action >= DASM_REL_PC ? va_arg(ap, int) : 0;
|
||||
switch (action) {
|
||||
case DASM_STOP: goto stop;
|
||||
case DASM_SECTION:
|
||||
n = (ins & 255); CK(n < D->maxsection, RANGE_SEC);
|
||||
D->section = &D->sections[n]; goto stop;
|
||||
case DASM_ESC: p++; ofs += 4; break;
|
||||
case DASM_REL_EXT: break;
|
||||
case DASM_ALIGN: ofs += (ins & 255); b[pos++] = ofs; break;
|
||||
case DASM_REL_LG:
|
||||
n = (ins & 2047) - 10; pl = D->lglabels + n;
|
||||
/* Bkwd rel or global. */
|
||||
if (n >= 0) { CK(n>=10||*pl<0, RANGE_LG); CKPL(lg, LG); goto putrel; }
|
||||
pl += 10; n = *pl;
|
||||
if (n < 0) n = 0; /* Start new chain for fwd rel if label exists. */
|
||||
goto linkrel;
|
||||
case DASM_REL_PC:
|
||||
pl = D->pclabels + n; CKPL(pc, PC);
|
||||
putrel:
|
||||
n = *pl;
|
||||
if (n < 0) { /* Label exists. Get label pos and store it. */
|
||||
b[pos] = -n;
|
||||
} else {
|
||||
linkrel:
|
||||
b[pos] = n; /* Else link to rel chain, anchored at label. */
|
||||
*pl = pos;
|
||||
}
|
||||
pos++;
|
||||
break;
|
||||
case DASM_LABEL_LG:
|
||||
pl = D->lglabels + (ins & 2047) - 10; CKPL(lg, LG); goto putlabel;
|
||||
case DASM_LABEL_PC:
|
||||
pl = D->pclabels + n; CKPL(pc, PC);
|
||||
putlabel:
|
||||
n = *pl; /* n > 0: Collapse rel chain and replace with label pos. */
|
||||
while (n > 0) { int *pb = DASM_POS2PTR(D, n); n = *pb; *pb = pos;
|
||||
}
|
||||
*pl = -pos; /* Label exists now. */
|
||||
b[pos++] = ofs; /* Store pass1 offset estimate. */
|
||||
break;
|
||||
case DASM_IMM:
|
||||
#ifdef DASM_CHECKS
|
||||
CK((n & ((1<<((ins>>10)&31))-1)) == 0, RANGE_I);
|
||||
#endif
|
||||
n >>= ((ins>>10)&31);
|
||||
#ifdef DASM_CHECKS
|
||||
if (ins & 0x8000)
|
||||
CK(((n + (1<<(((ins>>5)&31)-1)))>>((ins>>5)&31)) == 0, RANGE_I);
|
||||
else
|
||||
CK((n>>((ins>>5)&31)) == 0, RANGE_I);
|
||||
#endif
|
||||
b[pos++] = n;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
stop:
|
||||
va_end(ap);
|
||||
sec->pos = pos;
|
||||
sec->ofs = ofs;
|
||||
}
|
||||
#undef CK
|
||||
|
||||
/* Pass 2: Link sections, shrink aligns, fix label offsets. */
|
||||
int dasm_link(Dst_DECL, size_t *szp)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
int secnum;
|
||||
int ofs = 0;
|
||||
|
||||
#ifdef DASM_CHECKS
|
||||
*szp = 0;
|
||||
if (D->status != DASM_S_OK) return D->status;
|
||||
{
|
||||
int pc;
|
||||
for (pc = 0; pc*sizeof(int) < D->pcsize; pc++)
|
||||
if (D->pclabels[pc] > 0) return DASM_S_UNDEF_PC|pc;
|
||||
}
|
||||
#endif
|
||||
|
||||
{ /* Handle globals not defined in this translation unit. */
|
||||
int idx;
|
||||
for (idx = 20; idx*sizeof(int) < D->lgsize; idx++) {
|
||||
int n = D->lglabels[idx];
|
||||
/* Undefined label: Collapse rel chain and replace with marker (< 0). */
|
||||
while (n > 0) { int *pb = DASM_POS2PTR(D, n); n = *pb; *pb = -idx; }
|
||||
}
|
||||
}
|
||||
|
||||
/* Combine all code sections. No support for data sections (yet). */
|
||||
for (secnum = 0; secnum < D->maxsection; secnum++) {
|
||||
dasm_Section *sec = D->sections + secnum;
|
||||
int *b = sec->rbuf;
|
||||
int pos = DASM_SEC2POS(secnum);
|
||||
int lastpos = sec->pos;
|
||||
|
||||
while (pos != lastpos) {
|
||||
dasm_ActList p = D->actionlist + b[pos++];
|
||||
while (1) {
|
||||
unsigned int ins = *p++;
|
||||
unsigned int action = (ins >> 16) - 0xff00;
|
||||
switch (action) {
|
||||
case DASM_STOP: case DASM_SECTION: goto stop;
|
||||
case DASM_ESC: p++; break;
|
||||
case DASM_REL_EXT: break;
|
||||
case DASM_ALIGN: ofs -= (b[pos++] + ofs) & (ins & 255); break;
|
||||
case DASM_REL_LG: case DASM_REL_PC: pos++; break;
|
||||
case DASM_LABEL_LG: case DASM_LABEL_PC: b[pos++] += ofs; break;
|
||||
case DASM_IMM: pos++; break;
|
||||
}
|
||||
}
|
||||
stop: (void)0;
|
||||
}
|
||||
ofs += sec->ofs; /* Next section starts right after current section. */
|
||||
}
|
||||
|
||||
D->codesize = ofs; /* Total size of all code sections */
|
||||
*szp = ofs;
|
||||
return DASM_S_OK;
|
||||
}
|
||||
|
||||
#ifdef DASM_CHECKS
|
||||
#define CK(x, st) \
|
||||
do { if (!(x)) return DASM_S_##st|(p-D->actionlist-1); } while (0)
|
||||
#else
|
||||
#define CK(x, st) ((void)0)
|
||||
#endif
|
||||
|
||||
/* Pass 3: Encode sections. */
|
||||
int dasm_encode(Dst_DECL, void *buffer)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
char *base = (char *)buffer;
|
||||
unsigned int *cp = (unsigned int *)buffer;
|
||||
int secnum;
|
||||
|
||||
/* Encode all code sections. No support for data sections (yet). */
|
||||
for (secnum = 0; secnum < D->maxsection; secnum++) {
|
||||
dasm_Section *sec = D->sections + secnum;
|
||||
int *b = sec->buf;
|
||||
int *endb = sec->rbuf + sec->pos;
|
||||
|
||||
while (b != endb) {
|
||||
dasm_ActList p = D->actionlist + *b++;
|
||||
while (1) {
|
||||
unsigned int ins = *p++;
|
||||
unsigned int action = (ins >> 16) - 0xff00;
|
||||
int n = (action >= DASM_ALIGN && action < DASM__MAX) ? *b++ : 0;
|
||||
switch (action) {
|
||||
case DASM_STOP: case DASM_SECTION: goto stop;
|
||||
case DASM_ESC: *cp++ = *p++; break;
|
||||
case DASM_REL_EXT:
|
||||
n = DASM_EXTERN(Dst, (unsigned char *)cp, (ins & 2047), 1);
|
||||
goto patchrel;
|
||||
case DASM_ALIGN:
|
||||
ins &= 255; while ((((char *)cp - base) & ins)) *cp++ = 0x60000000;
|
||||
break;
|
||||
case DASM_REL_LG:
|
||||
CK(n >= 0, UNDEF_LG);
|
||||
case DASM_REL_PC:
|
||||
CK(n >= 0, UNDEF_PC);
|
||||
n = *DASM_POS2PTR(D, n);
|
||||
if (ins & 2048)
|
||||
n = n - (int)((char *)cp - base);
|
||||
else
|
||||
n = (n + (int)base) & 0x0fffffff;
|
||||
patchrel:
|
||||
CK((n & 3) == 0 &&
|
||||
((n + ((ins & 2048) ? 0x00020000 : 0)) >>
|
||||
((ins & 2048) ? 18 : 28)) == 0, RANGE_REL);
|
||||
cp[-1] |= ((n>>2) & ((ins & 2048) ? 0x0000ffff: 0x03ffffff));
|
||||
break;
|
||||
case DASM_LABEL_LG:
|
||||
ins &= 2047; if (ins >= 20) D->globals[ins-10] = (void *)(base + n);
|
||||
break;
|
||||
case DASM_LABEL_PC: break;
|
||||
case DASM_IMM:
|
||||
cp[-1] |= (n & ((1<<((ins>>5)&31))-1)) << (ins&31);
|
||||
break;
|
||||
default: *cp++ = ins; break;
|
||||
}
|
||||
}
|
||||
stop: (void)0;
|
||||
}
|
||||
}
|
||||
|
||||
if (base + D->codesize != (char *)cp) /* Check for phase errors. */
|
||||
return DASM_S_PHASE;
|
||||
return DASM_S_OK;
|
||||
}
|
||||
#undef CK
|
||||
|
||||
/* Get PC label offset. */
|
||||
int dasm_getpclabel(Dst_DECL, unsigned int pc)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
if (pc*sizeof(int) < D->pcsize) {
|
||||
int pos = D->pclabels[pc];
|
||||
if (pos < 0) return *DASM_POS2PTR(D, -pos);
|
||||
if (pos > 0) return -1; /* Undefined. */
|
||||
}
|
||||
return -2; /* Unused or out of range. */
|
||||
}
|
||||
|
||||
#ifdef DASM_CHECKS
|
||||
/* Optional sanity checker to call between isolated encoding steps. */
|
||||
int dasm_checkstep(Dst_DECL, int secmatch)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
if (D->status == DASM_S_OK) {
|
||||
int i;
|
||||
for (i = 1; i <= 9; i++) {
|
||||
if (D->lglabels[i] > 0) { D->status = DASM_S_UNDEF_LG|i; break; }
|
||||
D->lglabels[i] = 0;
|
||||
}
|
||||
}
|
||||
if (D->status == DASM_S_OK && secmatch >= 0 &&
|
||||
D->section != &D->sections[secmatch])
|
||||
D->status = DASM_S_MATCH_SEC|(D->section-D->sections);
|
||||
return D->status;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1,419 +0,0 @@
|
||||
/*
|
||||
** DynASM PPC/PPC64 encoding engine.
|
||||
** Copyright (C) 2005-2015 Mike Pall. All rights reserved.
|
||||
** Released under the MIT license. See dynasm.lua for full copyright notice.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define DASM_ARCH "ppc"
|
||||
|
||||
#ifndef DASM_EXTERN
|
||||
#define DASM_EXTERN(a,b,c,d) 0
|
||||
#endif
|
||||
|
||||
/* Action definitions. */
|
||||
enum {
|
||||
DASM_STOP, DASM_SECTION, DASM_ESC, DASM_REL_EXT,
|
||||
/* The following actions need a buffer position. */
|
||||
DASM_ALIGN, DASM_REL_LG, DASM_LABEL_LG,
|
||||
/* The following actions also have an argument. */
|
||||
DASM_REL_PC, DASM_LABEL_PC, DASM_IMM, DASM_IMMSH,
|
||||
DASM__MAX
|
||||
};
|
||||
|
||||
/* Maximum number of section buffer positions for a single dasm_put() call. */
|
||||
#define DASM_MAXSECPOS 25
|
||||
|
||||
/* DynASM encoder status codes. Action list offset or number are or'ed in. */
|
||||
#define DASM_S_OK 0x00000000
|
||||
#define DASM_S_NOMEM 0x01000000
|
||||
#define DASM_S_PHASE 0x02000000
|
||||
#define DASM_S_MATCH_SEC 0x03000000
|
||||
#define DASM_S_RANGE_I 0x11000000
|
||||
#define DASM_S_RANGE_SEC 0x12000000
|
||||
#define DASM_S_RANGE_LG 0x13000000
|
||||
#define DASM_S_RANGE_PC 0x14000000
|
||||
#define DASM_S_RANGE_REL 0x15000000
|
||||
#define DASM_S_UNDEF_LG 0x21000000
|
||||
#define DASM_S_UNDEF_PC 0x22000000
|
||||
|
||||
/* Macros to convert positions (8 bit section + 24 bit index). */
|
||||
#define DASM_POS2IDX(pos) ((pos)&0x00ffffff)
|
||||
#define DASM_POS2BIAS(pos) ((pos)&0xff000000)
|
||||
#define DASM_SEC2POS(sec) ((sec)<<24)
|
||||
#define DASM_POS2SEC(pos) ((pos)>>24)
|
||||
#define DASM_POS2PTR(D, pos) (D->sections[DASM_POS2SEC(pos)].rbuf + (pos))
|
||||
|
||||
/* Action list type. */
|
||||
typedef const unsigned int *dasm_ActList;
|
||||
|
||||
/* Per-section structure. */
|
||||
typedef struct dasm_Section {
|
||||
int *rbuf; /* Biased buffer pointer (negative section bias). */
|
||||
int *buf; /* True buffer pointer. */
|
||||
size_t bsize; /* Buffer size in bytes. */
|
||||
int pos; /* Biased buffer position. */
|
||||
int epos; /* End of biased buffer position - max single put. */
|
||||
int ofs; /* Byte offset into section. */
|
||||
} dasm_Section;
|
||||
|
||||
/* Core structure holding the DynASM encoding state. */
|
||||
struct dasm_State {
|
||||
size_t psize; /* Allocated size of this structure. */
|
||||
dasm_ActList actionlist; /* Current actionlist pointer. */
|
||||
int *lglabels; /* Local/global chain/pos ptrs. */
|
||||
size_t lgsize;
|
||||
int *pclabels; /* PC label chains/pos ptrs. */
|
||||
size_t pcsize;
|
||||
void **globals; /* Array of globals (bias -10). */
|
||||
dasm_Section *section; /* Pointer to active section. */
|
||||
size_t codesize; /* Total size of all code sections. */
|
||||
int maxsection; /* 0 <= sectionidx < maxsection. */
|
||||
int status; /* Status code. */
|
||||
dasm_Section sections[1]; /* All sections. Alloc-extended. */
|
||||
};
|
||||
|
||||
/* The size of the core structure depends on the max. number of sections. */
|
||||
#define DASM_PSZ(ms) (sizeof(dasm_State)+(ms-1)*sizeof(dasm_Section))
|
||||
|
||||
|
||||
/* Initialize DynASM state. */
|
||||
void dasm_init(Dst_DECL, int maxsection)
|
||||
{
|
||||
dasm_State *D;
|
||||
size_t psz = 0;
|
||||
int i;
|
||||
Dst_REF = NULL;
|
||||
DASM_M_GROW(Dst, struct dasm_State, Dst_REF, psz, DASM_PSZ(maxsection));
|
||||
D = Dst_REF;
|
||||
D->psize = psz;
|
||||
D->lglabels = NULL;
|
||||
D->lgsize = 0;
|
||||
D->pclabels = NULL;
|
||||
D->pcsize = 0;
|
||||
D->globals = NULL;
|
||||
D->maxsection = maxsection;
|
||||
for (i = 0; i < maxsection; i++) {
|
||||
D->sections[i].buf = NULL; /* Need this for pass3. */
|
||||
D->sections[i].rbuf = D->sections[i].buf - DASM_SEC2POS(i);
|
||||
D->sections[i].bsize = 0;
|
||||
D->sections[i].epos = 0; /* Wrong, but is recalculated after resize. */
|
||||
}
|
||||
}
|
||||
|
||||
/* Free DynASM state. */
|
||||
void dasm_free(Dst_DECL)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
int i;
|
||||
for (i = 0; i < D->maxsection; i++)
|
||||
if (D->sections[i].buf)
|
||||
DASM_M_FREE(Dst, D->sections[i].buf, D->sections[i].bsize);
|
||||
if (D->pclabels) DASM_M_FREE(Dst, D->pclabels, D->pcsize);
|
||||
if (D->lglabels) DASM_M_FREE(Dst, D->lglabels, D->lgsize);
|
||||
DASM_M_FREE(Dst, D, D->psize);
|
||||
}
|
||||
|
||||
/* Setup global label array. Must be called before dasm_setup(). */
|
||||
void dasm_setupglobal(Dst_DECL, void **gl, unsigned int maxgl)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
D->globals = gl - 10; /* Negative bias to compensate for locals. */
|
||||
DASM_M_GROW(Dst, int, D->lglabels, D->lgsize, (10+maxgl)*sizeof(int));
|
||||
}
|
||||
|
||||
/* Grow PC label array. Can be called after dasm_setup(), too. */
|
||||
void dasm_growpc(Dst_DECL, unsigned int maxpc)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
size_t osz = D->pcsize;
|
||||
DASM_M_GROW(Dst, int, D->pclabels, D->pcsize, maxpc*sizeof(int));
|
||||
memset((void *)(((unsigned char *)D->pclabels)+osz), 0, D->pcsize-osz);
|
||||
}
|
||||
|
||||
/* Setup encoder. */
|
||||
void dasm_setup(Dst_DECL, const void *actionlist)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
int i;
|
||||
D->actionlist = (dasm_ActList)actionlist;
|
||||
D->status = DASM_S_OK;
|
||||
D->section = &D->sections[0];
|
||||
memset((void *)D->lglabels, 0, D->lgsize);
|
||||
if (D->pclabels) memset((void *)D->pclabels, 0, D->pcsize);
|
||||
for (i = 0; i < D->maxsection; i++) {
|
||||
D->sections[i].pos = DASM_SEC2POS(i);
|
||||
D->sections[i].ofs = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef DASM_CHECKS
|
||||
#define CK(x, st) \
|
||||
do { if (!(x)) { \
|
||||
D->status = DASM_S_##st|(p-D->actionlist-1); return; } } while (0)
|
||||
#define CKPL(kind, st) \
|
||||
do { if ((size_t)((char *)pl-(char *)D->kind##labels) >= D->kind##size) { \
|
||||
D->status = DASM_S_RANGE_##st|(p-D->actionlist-1); return; } } while (0)
|
||||
#else
|
||||
#define CK(x, st) ((void)0)
|
||||
#define CKPL(kind, st) ((void)0)
|
||||
#endif
|
||||
|
||||
/* Pass 1: Store actions and args, link branches/labels, estimate offsets. */
|
||||
void dasm_put(Dst_DECL, int start, ...)
|
||||
{
|
||||
va_list ap;
|
||||
dasm_State *D = Dst_REF;
|
||||
dasm_ActList p = D->actionlist + start;
|
||||
dasm_Section *sec = D->section;
|
||||
int pos = sec->pos, ofs = sec->ofs;
|
||||
int *b;
|
||||
|
||||
if (pos >= sec->epos) {
|
||||
DASM_M_GROW(Dst, int, sec->buf, sec->bsize,
|
||||
sec->bsize + 2*DASM_MAXSECPOS*sizeof(int));
|
||||
sec->rbuf = sec->buf - DASM_POS2BIAS(pos);
|
||||
sec->epos = (int)sec->bsize/sizeof(int) - DASM_MAXSECPOS+DASM_POS2BIAS(pos);
|
||||
}
|
||||
|
||||
b = sec->rbuf;
|
||||
b[pos++] = start;
|
||||
|
||||
va_start(ap, start);
|
||||
while (1) {
|
||||
unsigned int ins = *p++;
|
||||
unsigned int action = (ins >> 16);
|
||||
if (action >= DASM__MAX) {
|
||||
ofs += 4;
|
||||
} else {
|
||||
int *pl, n = action >= DASM_REL_PC ? va_arg(ap, int) : 0;
|
||||
switch (action) {
|
||||
case DASM_STOP: goto stop;
|
||||
case DASM_SECTION:
|
||||
n = (ins & 255); CK(n < D->maxsection, RANGE_SEC);
|
||||
D->section = &D->sections[n]; goto stop;
|
||||
case DASM_ESC: p++; ofs += 4; break;
|
||||
case DASM_REL_EXT: break;
|
||||
case DASM_ALIGN: ofs += (ins & 255); b[pos++] = ofs; break;
|
||||
case DASM_REL_LG:
|
||||
n = (ins & 2047) - 10; pl = D->lglabels + n;
|
||||
/* Bkwd rel or global. */
|
||||
if (n >= 0) { CK(n>=10||*pl<0, RANGE_LG); CKPL(lg, LG); goto putrel; }
|
||||
pl += 10; n = *pl;
|
||||
if (n < 0) n = 0; /* Start new chain for fwd rel if label exists. */
|
||||
goto linkrel;
|
||||
case DASM_REL_PC:
|
||||
pl = D->pclabels + n; CKPL(pc, PC);
|
||||
putrel:
|
||||
n = *pl;
|
||||
if (n < 0) { /* Label exists. Get label pos and store it. */
|
||||
b[pos] = -n;
|
||||
} else {
|
||||
linkrel:
|
||||
b[pos] = n; /* Else link to rel chain, anchored at label. */
|
||||
*pl = pos;
|
||||
}
|
||||
pos++;
|
||||
break;
|
||||
case DASM_LABEL_LG:
|
||||
pl = D->lglabels + (ins & 2047) - 10; CKPL(lg, LG); goto putlabel;
|
||||
case DASM_LABEL_PC:
|
||||
pl = D->pclabels + n; CKPL(pc, PC);
|
||||
putlabel:
|
||||
n = *pl; /* n > 0: Collapse rel chain and replace with label pos. */
|
||||
while (n > 0) { int *pb = DASM_POS2PTR(D, n); n = *pb; *pb = pos;
|
||||
}
|
||||
*pl = -pos; /* Label exists now. */
|
||||
b[pos++] = ofs; /* Store pass1 offset estimate. */
|
||||
break;
|
||||
case DASM_IMM:
|
||||
#ifdef DASM_CHECKS
|
||||
CK((n & ((1<<((ins>>10)&31))-1)) == 0, RANGE_I);
|
||||
#endif
|
||||
n >>= ((ins>>10)&31);
|
||||
#ifdef DASM_CHECKS
|
||||
if (ins & 0x8000)
|
||||
CK(((n + (1<<(((ins>>5)&31)-1)))>>((ins>>5)&31)) == 0, RANGE_I);
|
||||
else
|
||||
CK((n>>((ins>>5)&31)) == 0, RANGE_I);
|
||||
#endif
|
||||
b[pos++] = n;
|
||||
break;
|
||||
case DASM_IMMSH:
|
||||
CK((n >> 6) == 0, RANGE_I);
|
||||
b[pos++] = n;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
stop:
|
||||
va_end(ap);
|
||||
sec->pos = pos;
|
||||
sec->ofs = ofs;
|
||||
}
|
||||
#undef CK
|
||||
|
||||
/* Pass 2: Link sections, shrink aligns, fix label offsets. */
|
||||
int dasm_link(Dst_DECL, size_t *szp)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
int secnum;
|
||||
int ofs = 0;
|
||||
|
||||
#ifdef DASM_CHECKS
|
||||
*szp = 0;
|
||||
if (D->status != DASM_S_OK) return D->status;
|
||||
{
|
||||
int pc;
|
||||
for (pc = 0; pc*sizeof(int) < D->pcsize; pc++)
|
||||
if (D->pclabels[pc] > 0) return DASM_S_UNDEF_PC|pc;
|
||||
}
|
||||
#endif
|
||||
|
||||
{ /* Handle globals not defined in this translation unit. */
|
||||
int idx;
|
||||
for (idx = 20; idx*sizeof(int) < D->lgsize; idx++) {
|
||||
int n = D->lglabels[idx];
|
||||
/* Undefined label: Collapse rel chain and replace with marker (< 0). */
|
||||
while (n > 0) { int *pb = DASM_POS2PTR(D, n); n = *pb; *pb = -idx; }
|
||||
}
|
||||
}
|
||||
|
||||
/* Combine all code sections. No support for data sections (yet). */
|
||||
for (secnum = 0; secnum < D->maxsection; secnum++) {
|
||||
dasm_Section *sec = D->sections + secnum;
|
||||
int *b = sec->rbuf;
|
||||
int pos = DASM_SEC2POS(secnum);
|
||||
int lastpos = sec->pos;
|
||||
|
||||
while (pos != lastpos) {
|
||||
dasm_ActList p = D->actionlist + b[pos++];
|
||||
while (1) {
|
||||
unsigned int ins = *p++;
|
||||
unsigned int action = (ins >> 16);
|
||||
switch (action) {
|
||||
case DASM_STOP: case DASM_SECTION: goto stop;
|
||||
case DASM_ESC: p++; break;
|
||||
case DASM_REL_EXT: break;
|
||||
case DASM_ALIGN: ofs -= (b[pos++] + ofs) & (ins & 255); break;
|
||||
case DASM_REL_LG: case DASM_REL_PC: pos++; break;
|
||||
case DASM_LABEL_LG: case DASM_LABEL_PC: b[pos++] += ofs; break;
|
||||
case DASM_IMM: case DASM_IMMSH: pos++; break;
|
||||
}
|
||||
}
|
||||
stop: (void)0;
|
||||
}
|
||||
ofs += sec->ofs; /* Next section starts right after current section. */
|
||||
}
|
||||
|
||||
D->codesize = ofs; /* Total size of all code sections */
|
||||
*szp = ofs;
|
||||
return DASM_S_OK;
|
||||
}
|
||||
|
||||
#ifdef DASM_CHECKS
|
||||
#define CK(x, st) \
|
||||
do { if (!(x)) return DASM_S_##st|(p-D->actionlist-1); } while (0)
|
||||
#else
|
||||
#define CK(x, st) ((void)0)
|
||||
#endif
|
||||
|
||||
/* Pass 3: Encode sections. */
|
||||
int dasm_encode(Dst_DECL, void *buffer)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
char *base = (char *)buffer;
|
||||
unsigned int *cp = (unsigned int *)buffer;
|
||||
int secnum;
|
||||
|
||||
/* Encode all code sections. No support for data sections (yet). */
|
||||
for (secnum = 0; secnum < D->maxsection; secnum++) {
|
||||
dasm_Section *sec = D->sections + secnum;
|
||||
int *b = sec->buf;
|
||||
int *endb = sec->rbuf + sec->pos;
|
||||
|
||||
while (b != endb) {
|
||||
dasm_ActList p = D->actionlist + *b++;
|
||||
while (1) {
|
||||
unsigned int ins = *p++;
|
||||
unsigned int action = (ins >> 16);
|
||||
int n = (action >= DASM_ALIGN && action < DASM__MAX) ? *b++ : 0;
|
||||
switch (action) {
|
||||
case DASM_STOP: case DASM_SECTION: goto stop;
|
||||
case DASM_ESC: *cp++ = *p++; break;
|
||||
case DASM_REL_EXT:
|
||||
n = DASM_EXTERN(Dst, (unsigned char *)cp, (ins & 2047), 1) - 4;
|
||||
goto patchrel;
|
||||
case DASM_ALIGN:
|
||||
ins &= 255; while ((((char *)cp - base) & ins)) *cp++ = 0x60000000;
|
||||
break;
|
||||
case DASM_REL_LG:
|
||||
CK(n >= 0, UNDEF_LG);
|
||||
case DASM_REL_PC:
|
||||
CK(n >= 0, UNDEF_PC);
|
||||
n = *DASM_POS2PTR(D, n) - (int)((char *)cp - base);
|
||||
patchrel:
|
||||
CK((n & 3) == 0 &&
|
||||
(((n+4) + ((ins & 2048) ? 0x00008000 : 0x02000000)) >>
|
||||
((ins & 2048) ? 16 : 26)) == 0, RANGE_REL);
|
||||
cp[-1] |= ((n+4) & ((ins & 2048) ? 0x0000fffc: 0x03fffffc));
|
||||
break;
|
||||
case DASM_LABEL_LG:
|
||||
ins &= 2047; if (ins >= 20) D->globals[ins-10] = (void *)(base + n);
|
||||
break;
|
||||
case DASM_LABEL_PC: break;
|
||||
case DASM_IMM:
|
||||
cp[-1] |= (n & ((1<<((ins>>5)&31))-1)) << (ins&31);
|
||||
break;
|
||||
case DASM_IMMSH:
|
||||
cp[-1] |= (ins & 1) ? ((n&31)<<11)|((n&32)>>4) : ((n&31)<<6)|(n&32);
|
||||
break;
|
||||
default: *cp++ = ins; break;
|
||||
}
|
||||
}
|
||||
stop: (void)0;
|
||||
}
|
||||
}
|
||||
|
||||
if (base + D->codesize != (char *)cp) /* Check for phase errors. */
|
||||
return DASM_S_PHASE;
|
||||
return DASM_S_OK;
|
||||
}
|
||||
#undef CK
|
||||
|
||||
/* Get PC label offset. */
|
||||
int dasm_getpclabel(Dst_DECL, unsigned int pc)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
if (pc*sizeof(int) < D->pcsize) {
|
||||
int pos = D->pclabels[pc];
|
||||
if (pos < 0) return *DASM_POS2PTR(D, -pos);
|
||||
if (pos > 0) return -1; /* Undefined. */
|
||||
}
|
||||
return -2; /* Unused or out of range. */
|
||||
}
|
||||
|
||||
#ifdef DASM_CHECKS
|
||||
/* Optional sanity checker to call between isolated encoding steps. */
|
||||
int dasm_checkstep(Dst_DECL, int secmatch)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
if (D->status == DASM_S_OK) {
|
||||
int i;
|
||||
for (i = 1; i <= 9; i++) {
|
||||
if (D->lglabels[i] > 0) { D->status = DASM_S_UNDEF_LG|i; break; }
|
||||
D->lglabels[i] = 0;
|
||||
}
|
||||
}
|
||||
if (D->status == DASM_S_OK && secmatch >= 0 &&
|
||||
D->section != &D->sections[secmatch])
|
||||
D->status = DASM_S_MATCH_SEC|(D->section-D->sections);
|
||||
return D->status;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
#include "dasm_extern.h"
|
||||
#include "dasm_proto.h"
|
||||
#include "dasm_x86.h"
|
||||
@@ -0,0 +1,274 @@
|
||||
|
||||
--binding to the DynASM encoding engine.
|
||||
--Written by Cosmin Apreutesei. Public Domain.
|
||||
|
||||
local ffi = require'ffi'
|
||||
local bit = require'bit'
|
||||
local arch = ffi.arch
|
||||
if arch == 'x64' then arch = 'x86' end --same linker for x64
|
||||
local C = ffi.load('dasm_'..arch)
|
||||
local M = {C = C}
|
||||
|
||||
M._VERSION = 10400
|
||||
|
||||
ffi.cdef[[
|
||||
enum {
|
||||
DASM_S_OK = 0x00000000,
|
||||
DASM_S_NOMEM = 0x01000000,
|
||||
DASM_S_PHASE = 0x02000000,
|
||||
DASM_S_MATCH_SEC = 0x03000000,
|
||||
DASM_S_RANGE_I = 0x11000000,
|
||||
DASM_S_RANGE_SEC = 0x12000000,
|
||||
DASM_S_RANGE_LG = 0x13000000,
|
||||
DASM_S_RANGE_PC = 0x14000000,
|
||||
DASM_S_RANGE_VREG = 0x15000000,
|
||||
DASM_S_UNDEF_L = 0x21000000,
|
||||
DASM_S_UNDEF_PC = 0x22000000,
|
||||
};
|
||||
|
||||
/* Internal DynASM encoder state. */
|
||||
typedef struct dasm_State_Ref { struct dasm_State *p; } dasm_State_Ref;
|
||||
typedef dasm_State_Ref *Dst_DECL;
|
||||
|
||||
/* Initialize and free DynASM state. */
|
||||
void dasm_init(Dst_DECL, int maxsection);
|
||||
void dasm_free(Dst_DECL);
|
||||
|
||||
/* Setup global array. Must be called before dasm_setup(). */
|
||||
void dasm_setupglobal(Dst_DECL, void **gl, unsigned int maxgl);
|
||||
|
||||
/* Grow PC label array. Can be called after dasm_setup(), too. */
|
||||
void dasm_growpc(Dst_DECL, unsigned int maxpc);
|
||||
|
||||
/* Setup encoder. */
|
||||
void dasm_setup(Dst_DECL, const void *actionlist);
|
||||
|
||||
/* Feed encoder with actions. Calls are generated by pre-processor. */
|
||||
void dasm_put(Dst_DECL, int start, ...);
|
||||
|
||||
/* Link sections and return the resulting size. */
|
||||
int dasm_link(Dst_DECL, size_t *szp);
|
||||
|
||||
/* Encode sections into buffer. */
|
||||
int dasm_encode(Dst_DECL, void *buffer);
|
||||
|
||||
/* Get PC label offset. */
|
||||
int dasm_getpclabel(Dst_DECL, unsigned int pc);
|
||||
|
||||
/* Optional sanity checker to call between isolated encoding steps. */
|
||||
int dasm_checkstep(Dst_DECL, int secmatch);
|
||||
|
||||
typedef int (*DASM_EXTERN_TYPE) (void *ctx, unsigned char *addr, int idx, int type);
|
||||
DASM_EXTERN_TYPE DASM_EXTERN_FUNC;
|
||||
]]
|
||||
|
||||
local function err(...)
|
||||
io.stderr:setvbuf'no'
|
||||
io.stderr:write('dasm error: ', ...)
|
||||
io.stderr:write'\n'
|
||||
os.exit(1)
|
||||
end
|
||||
|
||||
--status check helper
|
||||
|
||||
local status_map = {
|
||||
[C.DASM_S_NOMEM] = 'out of memory',
|
||||
[C.DASM_S_PHASE] = 'phase error',
|
||||
[C.DASM_S_MATCH_SEC] = 'section not found',
|
||||
[C.DASM_S_RANGE_I] = 'immediate value out of range',
|
||||
[C.DASM_S_RANGE_SEC] = 'too many sections',
|
||||
[C.DASM_S_RANGE_LG] = 'too many global labels',
|
||||
[C.DASM_S_RANGE_PC] = 'too many pclabels',
|
||||
[C.DASM_S_RANGE_VREG] = 'variable register out of range',
|
||||
[C.DASM_S_UNDEF_L] = 'undefined global label',
|
||||
[C.DASM_S_UNDEF_PC] = 'undefined pclabel',
|
||||
}
|
||||
|
||||
local function checkst(st)
|
||||
if st == C.DASM_S_OK then return end
|
||||
local status, arg = status_map[bit.band(st, 0xff000000)], bit.band(st, 0x00ffffff)
|
||||
if status then
|
||||
err(status, '. :', arg)
|
||||
else
|
||||
err(string.format('0x%08X', st))
|
||||
end
|
||||
end
|
||||
|
||||
--low level API
|
||||
|
||||
M.init = C.dasm_init
|
||||
M.free = C.dasm_free
|
||||
M.setupglobal = C.dasm_setupglobal
|
||||
M.growpc = C.dasm_growpc
|
||||
M.setup = C.dasm_setup
|
||||
|
||||
local int_ct = ffi.typeof'int'
|
||||
local function convert_arg(arg) --dasm_put() accepts only int32 varargs.
|
||||
if type(arg) == "number" then --but we make it accept uint32 too by normalizing the arg.
|
||||
arg = bit.tobit(arg) --non-number args are converted to int32 according to ffi rules.
|
||||
end
|
||||
return ffi.cast(int_ct, arg)
|
||||
end
|
||||
local function convert_args(...) --not a tailcall but at least it doesn't make any garbage
|
||||
if select('#', ...) == 0 then return end
|
||||
return convert_arg(...), convert_args(select(2, ...))
|
||||
end
|
||||
function M.put(state, start, ...)
|
||||
C.dasm_put(state, start, convert_args(...))
|
||||
end
|
||||
|
||||
function M.link(state, sz)
|
||||
sz = sz or ffi.new'size_t[1]'
|
||||
checkst(C.dasm_link(state, sz))
|
||||
return tonumber(sz[0])
|
||||
end
|
||||
|
||||
function M.encode(state, buf)
|
||||
checkst(C.dasm_encode(state, buf))
|
||||
end
|
||||
jit.off(M.encode) --calls the DASM_EXTERN_FUNC callback
|
||||
|
||||
local voidptr_ct = ffi.typeof'void*'
|
||||
local byteptr_ct = ffi.typeof'int8_t*'
|
||||
function M.getpclabel(state, pc, buf)
|
||||
local offset = C.dasm_getpclabel(state, pc)
|
||||
if buf then
|
||||
return ffi.cast(voidptr_ct, ffi.cast(byteptr_ct, buf) + offset)
|
||||
end
|
||||
return offset
|
||||
end
|
||||
|
||||
function M.checkstep(state, section)
|
||||
checkst(C.dasm_checkstep(state, section or -1))
|
||||
end
|
||||
|
||||
--get the address of a standard symbol.
|
||||
--TODO: ask Mike to expose clib_getsym() in ffi so we can get the address
|
||||
--of symbols without having to declare them first.
|
||||
local function getsym(name)
|
||||
return ffi.C[name]
|
||||
end
|
||||
local getsym = function(name)
|
||||
local ok, sym = pcall(getsym, name)
|
||||
if not ok then --not found or not defined: define it and try again
|
||||
ffi.cdef(string.format('void %s();', name))
|
||||
return getsym(name)
|
||||
else
|
||||
return sym
|
||||
end
|
||||
end
|
||||
|
||||
--DASM_EXTERN callback plumbing
|
||||
|
||||
local extern_names --t[idx] -> name
|
||||
local extern_get --f(name) -> ptr
|
||||
|
||||
local byteptr_ct = ffi.typeof'uint8_t*'
|
||||
|
||||
local function DASM_EXTERN_FUNC(ctx, addr, idx, type)
|
||||
if not extern_names or not extern_get then
|
||||
err'extern callback not initialized.'
|
||||
end
|
||||
local name = extern_names[idx]
|
||||
local ptr = extern_get(name)
|
||||
if ptr == nil then
|
||||
err('extern not found: ', name, '.')
|
||||
end
|
||||
if type ~= 0 then
|
||||
return ffi.cast(byteptr_ct, ptr) - addr - 4
|
||||
else
|
||||
return ptr
|
||||
end
|
||||
end
|
||||
|
||||
function M.setupextern(_, names, getter)
|
||||
extern_names = names
|
||||
extern_get = getter or getsym
|
||||
if C.DASM_EXTERN_FUNC == nil then
|
||||
C.DASM_EXTERN_FUNC = DASM_EXTERN_FUNC
|
||||
end
|
||||
end
|
||||
|
||||
--hi-level API
|
||||
|
||||
function M.new(actionlist, externnames, sectioncount, globalcount, externget, globals)
|
||||
local state = ffi.new'dasm_State_Ref'
|
||||
M.init(state, sectioncount or 1)
|
||||
globalcount = globalcount or 256
|
||||
globals = globals or ffi.new('void*[?]', globalcount)
|
||||
ffi.gc(state, function(state)
|
||||
local _ = actionlist, externnames, globals, externget --anchor those: don't rely on the user doing so
|
||||
M.free(state)
|
||||
end)
|
||||
M.setupglobal(state, globals, globalcount)
|
||||
M.setupextern(state, externnames, externget)
|
||||
M.setup(state, actionlist)
|
||||
return state, globals
|
||||
end
|
||||
|
||||
function M.build(state)
|
||||
state:checkstep(-1)
|
||||
local sz = state:link()
|
||||
if sz == 0 then err'no code?' end
|
||||
local mm = require'dasm_mm' --runtime dependency
|
||||
local buf = mm.new(sz)
|
||||
state:encode(buf)
|
||||
mm.protect(buf, sz)
|
||||
return buf, sz
|
||||
end
|
||||
|
||||
function M.dump(addr, size, out)
|
||||
local disass = require('jit.dis_'..jit.arch).disass
|
||||
disass(ffi.string(addr, size), tonumber(ffi.cast('uintptr_t', addr)), out)
|
||||
end
|
||||
|
||||
--given the globals array from dasm.new() and the globalnames list
|
||||
--from the `.globalnames` directive, return a map {global_name -> global_addr}.
|
||||
function M.globals(globals, globalnames)
|
||||
local t = {}
|
||||
for i = 0, #globalnames do
|
||||
if globals[i] ~= nil then
|
||||
t[globalnames[i]] = globals[i]
|
||||
end
|
||||
end
|
||||
return t
|
||||
end
|
||||
|
||||
--object interface
|
||||
|
||||
ffi.metatype('dasm_State_Ref', {__index = {
|
||||
--low-level API
|
||||
init = M.init,
|
||||
free = M.free,
|
||||
setupglobal = M.setupglobal,
|
||||
setupextern = M.setupextern,
|
||||
growpc = M.growpc,
|
||||
setup = M.setup,
|
||||
put = M.put,
|
||||
link = M.link,
|
||||
encode = M.encode,
|
||||
getpclabel = M.getpclabel,
|
||||
checkstep = M.checkstep,
|
||||
--hi-level API
|
||||
build = M.build,
|
||||
}})
|
||||
|
||||
if not ... then --demo
|
||||
local dasm = M
|
||||
local actions = ffi.new('const uint8_t[19]', {254,0,102,184,5,0,254,1,102,187,3,0,254,2,102,187,3,0,255})
|
||||
local Dst, globals = dasm.new(actions, nil, 3)
|
||||
--|.code
|
||||
dasm.put(Dst, 0)
|
||||
--| mov ax, 5
|
||||
--|.sub1
|
||||
dasm.put(Dst, 2)
|
||||
--| mov bx, 3
|
||||
--|.sub2
|
||||
dasm.put(Dst, 8)
|
||||
--| mov bx, 3
|
||||
dasm.put(Dst, 14)
|
||||
local addr, size = Dst:build()
|
||||
dasm.dump(addr, size)
|
||||
end
|
||||
|
||||
return M
|
||||
-1125
File diff suppressed because it is too large
Load Diff
-953
@@ -1,953 +0,0 @@
|
||||
------------------------------------------------------------------------------
|
||||
-- DynASM MIPS module.
|
||||
--
|
||||
-- Copyright (C) 2005-2015 Mike Pall. All rights reserved.
|
||||
-- See dynasm.lua for full copyright notice.
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Module information:
|
||||
local _info = {
|
||||
arch = "mips",
|
||||
description = "DynASM MIPS module",
|
||||
version = "1.4.0",
|
||||
vernum = 10400,
|
||||
release = "2015-10-18",
|
||||
author = "Mike Pall",
|
||||
license = "MIT",
|
||||
}
|
||||
|
||||
-- Exported glue functions for the arch-specific module.
|
||||
local _M = { _info = _info }
|
||||
|
||||
-- Cache library functions.
|
||||
local type, tonumber, pairs, ipairs = type, tonumber, pairs, ipairs
|
||||
local assert, setmetatable = assert, setmetatable
|
||||
local _s = string
|
||||
local sub, format, byte, char = _s.sub, _s.format, _s.byte, _s.char
|
||||
local match, gmatch = _s.match, _s.gmatch
|
||||
local concat, sort = table.concat, table.sort
|
||||
local bit = bit or require("bit")
|
||||
local band, shl, sar, tohex = bit.band, bit.lshift, bit.arshift, bit.tohex
|
||||
|
||||
-- Inherited tables and callbacks.
|
||||
local g_opt, g_arch
|
||||
local wline, werror, wfatal, wwarn
|
||||
|
||||
-- Action name list.
|
||||
-- CHECK: Keep this in sync with the C code!
|
||||
local action_names = {
|
||||
"STOP", "SECTION", "ESC", "REL_EXT",
|
||||
"ALIGN", "REL_LG", "LABEL_LG",
|
||||
"REL_PC", "LABEL_PC", "IMM",
|
||||
}
|
||||
|
||||
-- Maximum number of section buffer positions for dasm_put().
|
||||
-- CHECK: Keep this in sync with the C code!
|
||||
local maxsecpos = 25 -- Keep this low, to avoid excessively long C lines.
|
||||
|
||||
-- Action name -> action number.
|
||||
local map_action = {}
|
||||
for n,name in ipairs(action_names) do
|
||||
map_action[name] = n-1
|
||||
end
|
||||
|
||||
-- Action list buffer.
|
||||
local actlist = {}
|
||||
|
||||
-- Argument list for next dasm_put(). Start with offset 0 into action list.
|
||||
local actargs = { 0 }
|
||||
|
||||
-- Current number of section buffer positions for dasm_put().
|
||||
local secpos = 1
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Dump action names and numbers.
|
||||
local function dumpactions(out)
|
||||
out:write("DynASM encoding engine action codes:\n")
|
||||
for n,name in ipairs(action_names) do
|
||||
local num = map_action[name]
|
||||
out:write(format(" %-10s %02X %d\n", name, num, num))
|
||||
end
|
||||
out:write("\n")
|
||||
end
|
||||
|
||||
-- Write action list buffer as a huge static C array.
|
||||
local function writeactions(out, name)
|
||||
local nn = #actlist
|
||||
if nn == 0 then nn = 1; actlist[0] = map_action.STOP end
|
||||
out:write("static const unsigned int ", name, "[", nn, "] = {\n")
|
||||
for i = 1,nn-1 do
|
||||
assert(out:write("0x", tohex(actlist[i]), ",\n"))
|
||||
end
|
||||
assert(out:write("0x", tohex(actlist[nn]), "\n};\n\n"))
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Add word to action list.
|
||||
local function wputxw(n)
|
||||
assert(n >= 0 and n <= 0xffffffff and n % 1 == 0, "word out of range")
|
||||
actlist[#actlist+1] = n
|
||||
end
|
||||
|
||||
-- Add action to list with optional arg. Advance buffer pos, too.
|
||||
local function waction(action, val, a, num)
|
||||
local w = assert(map_action[action], "bad action name `"..action.."'")
|
||||
wputxw(0xff000000 + w * 0x10000 + (val or 0))
|
||||
if a then actargs[#actargs+1] = a end
|
||||
if a or num then secpos = secpos + (num or 1) end
|
||||
end
|
||||
|
||||
-- Flush action list (intervening C code or buffer pos overflow).
|
||||
local function wflush(term)
|
||||
if #actlist == actargs[1] then return end -- Nothing to flush.
|
||||
if not term then waction("STOP") end -- Terminate action list.
|
||||
wline(format("dasm_put(Dst, %s);", concat(actargs, ", ")), true)
|
||||
actargs = { #actlist } -- Actionlist offset is 1st arg to next dasm_put().
|
||||
secpos = 1 -- The actionlist offset occupies a buffer position, too.
|
||||
end
|
||||
|
||||
-- Put escaped word.
|
||||
local function wputw(n)
|
||||
if n >= 0xff000000 then waction("ESC") end
|
||||
wputxw(n)
|
||||
end
|
||||
|
||||
-- Reserve position for word.
|
||||
local function wpos()
|
||||
local pos = #actlist+1
|
||||
actlist[pos] = ""
|
||||
return pos
|
||||
end
|
||||
|
||||
-- Store word to reserved position.
|
||||
local function wputpos(pos, n)
|
||||
assert(n >= 0 and n <= 0xffffffff and n % 1 == 0, "word out of range")
|
||||
actlist[pos] = n
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Global label name -> global label number. With auto assignment on 1st use.
|
||||
local next_global = 20
|
||||
local map_global = setmetatable({}, { __index = function(t, name)
|
||||
if not match(name, "^[%a_][%w_]*$") then werror("bad global label") end
|
||||
local n = next_global
|
||||
if n > 2047 then werror("too many global labels") end
|
||||
next_global = n + 1
|
||||
t[name] = n
|
||||
return n
|
||||
end})
|
||||
|
||||
-- Dump global labels.
|
||||
local function dumpglobals(out, lvl)
|
||||
local t = {}
|
||||
for name, n in pairs(map_global) do t[n] = name end
|
||||
out:write("Global labels:\n")
|
||||
for i=20,next_global-1 do
|
||||
out:write(format(" %s\n", t[i]))
|
||||
end
|
||||
out:write("\n")
|
||||
end
|
||||
|
||||
-- Write global label enum.
|
||||
local function writeglobals(out, prefix)
|
||||
local t = {}
|
||||
for name, n in pairs(map_global) do t[n] = name end
|
||||
out:write("enum {\n")
|
||||
for i=20,next_global-1 do
|
||||
out:write(" ", prefix, t[i], ",\n")
|
||||
end
|
||||
out:write(" ", prefix, "_MAX\n};\n")
|
||||
end
|
||||
|
||||
-- Write global label names.
|
||||
local function writeglobalnames(out, name)
|
||||
local t = {}
|
||||
for name, n in pairs(map_global) do t[n] = name end
|
||||
out:write("static const char *const ", name, "[] = {\n")
|
||||
for i=20,next_global-1 do
|
||||
out:write(" \"", t[i], "\",\n")
|
||||
end
|
||||
out:write(" (const char *)0\n};\n")
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Extern label name -> extern label number. With auto assignment on 1st use.
|
||||
local next_extern = 0
|
||||
local map_extern_ = {}
|
||||
local map_extern = setmetatable({}, { __index = function(t, name)
|
||||
-- No restrictions on the name for now.
|
||||
local n = next_extern
|
||||
if n > 2047 then werror("too many extern labels") end
|
||||
next_extern = n + 1
|
||||
t[name] = n
|
||||
map_extern_[n] = name
|
||||
return n
|
||||
end})
|
||||
|
||||
-- Dump extern labels.
|
||||
local function dumpexterns(out, lvl)
|
||||
out:write("Extern labels:\n")
|
||||
for i=0,next_extern-1 do
|
||||
out:write(format(" %s\n", map_extern_[i]))
|
||||
end
|
||||
out:write("\n")
|
||||
end
|
||||
|
||||
-- Write extern label names.
|
||||
local function writeexternnames(out, name)
|
||||
out:write("static const char *const ", name, "[] = {\n")
|
||||
for i=0,next_extern-1 do
|
||||
out:write(" \"", map_extern_[i], "\",\n")
|
||||
end
|
||||
out:write(" (const char *)0\n};\n")
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Arch-specific maps.
|
||||
local map_archdef = { sp="r29", ra="r31" } -- Ext. register name -> int. name.
|
||||
|
||||
local map_type = {} -- Type name -> { ctype, reg }
|
||||
local ctypenum = 0 -- Type number (for Dt... macros).
|
||||
|
||||
-- Reverse defines for registers.
|
||||
function _M.revdef(s)
|
||||
if s == "r29" then return "sp"
|
||||
elseif s == "r31" then return "ra" end
|
||||
return s
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Template strings for MIPS instructions.
|
||||
local map_op = {
|
||||
-- First-level opcodes.
|
||||
j_1 = "08000000J",
|
||||
jal_1 = "0c000000J",
|
||||
b_1 = "10000000B",
|
||||
beqz_2 = "10000000SB",
|
||||
beq_3 = "10000000STB",
|
||||
bnez_2 = "14000000SB",
|
||||
bne_3 = "14000000STB",
|
||||
blez_2 = "18000000SB",
|
||||
bgtz_2 = "1c000000SB",
|
||||
addi_3 = "20000000TSI",
|
||||
li_2 = "24000000TI",
|
||||
addiu_3 = "24000000TSI",
|
||||
slti_3 = "28000000TSI",
|
||||
sltiu_3 = "2c000000TSI",
|
||||
andi_3 = "30000000TSU",
|
||||
lu_2 = "34000000TU",
|
||||
ori_3 = "34000000TSU",
|
||||
xori_3 = "38000000TSU",
|
||||
lui_2 = "3c000000TU",
|
||||
beqzl_2 = "50000000SB",
|
||||
beql_3 = "50000000STB",
|
||||
bnezl_2 = "54000000SB",
|
||||
bnel_3 = "54000000STB",
|
||||
blezl_2 = "58000000SB",
|
||||
bgtzl_2 = "5c000000SB",
|
||||
lb_2 = "80000000TO",
|
||||
lh_2 = "84000000TO",
|
||||
lwl_2 = "88000000TO",
|
||||
lw_2 = "8c000000TO",
|
||||
lbu_2 = "90000000TO",
|
||||
lhu_2 = "94000000TO",
|
||||
lwr_2 = "98000000TO",
|
||||
sb_2 = "a0000000TO",
|
||||
sh_2 = "a4000000TO",
|
||||
swl_2 = "a8000000TO",
|
||||
sw_2 = "ac000000TO",
|
||||
swr_2 = "b8000000TO",
|
||||
cache_2 = "bc000000NO",
|
||||
ll_2 = "c0000000TO",
|
||||
lwc1_2 = "c4000000HO",
|
||||
pref_2 = "cc000000NO",
|
||||
ldc1_2 = "d4000000HO",
|
||||
sc_2 = "e0000000TO",
|
||||
swc1_2 = "e4000000HO",
|
||||
sdc1_2 = "f4000000HO",
|
||||
|
||||
-- Opcode SPECIAL.
|
||||
nop_0 = "00000000",
|
||||
sll_3 = "00000000DTA",
|
||||
movf_2 = "00000001DS",
|
||||
movf_3 = "00000001DSC",
|
||||
movt_2 = "00010001DS",
|
||||
movt_3 = "00010001DSC",
|
||||
srl_3 = "00000002DTA",
|
||||
rotr_3 = "00200002DTA",
|
||||
sra_3 = "00000003DTA",
|
||||
sllv_3 = "00000004DTS",
|
||||
srlv_3 = "00000006DTS",
|
||||
rotrv_3 = "00000046DTS",
|
||||
srav_3 = "00000007DTS",
|
||||
jr_1 = "00000008S",
|
||||
jalr_1 = "0000f809S",
|
||||
jalr_2 = "00000009DS",
|
||||
movz_3 = "0000000aDST",
|
||||
movn_3 = "0000000bDST",
|
||||
syscall_0 = "0000000c",
|
||||
syscall_1 = "0000000cY",
|
||||
break_0 = "0000000d",
|
||||
break_1 = "0000000dY",
|
||||
sync_0 = "0000000f",
|
||||
mfhi_1 = "00000010D",
|
||||
mthi_1 = "00000011S",
|
||||
mflo_1 = "00000012D",
|
||||
mtlo_1 = "00000013S",
|
||||
mult_2 = "00000018ST",
|
||||
multu_2 = "00000019ST",
|
||||
div_2 = "0000001aST",
|
||||
divu_2 = "0000001bST",
|
||||
add_3 = "00000020DST",
|
||||
move_2 = "00000021DS",
|
||||
addu_3 = "00000021DST",
|
||||
sub_3 = "00000022DST",
|
||||
negu_2 = "00000023DT",
|
||||
subu_3 = "00000023DST",
|
||||
and_3 = "00000024DST",
|
||||
or_3 = "00000025DST",
|
||||
xor_3 = "00000026DST",
|
||||
not_2 = "00000027DS",
|
||||
nor_3 = "00000027DST",
|
||||
slt_3 = "0000002aDST",
|
||||
sltu_3 = "0000002bDST",
|
||||
tge_2 = "00000030ST",
|
||||
tge_3 = "00000030STZ",
|
||||
tgeu_2 = "00000031ST",
|
||||
tgeu_3 = "00000031STZ",
|
||||
tlt_2 = "00000032ST",
|
||||
tlt_3 = "00000032STZ",
|
||||
tltu_2 = "00000033ST",
|
||||
tltu_3 = "00000033STZ",
|
||||
teq_2 = "00000034ST",
|
||||
teq_3 = "00000034STZ",
|
||||
tne_2 = "00000036ST",
|
||||
tne_3 = "00000036STZ",
|
||||
|
||||
-- Opcode REGIMM.
|
||||
bltz_2 = "04000000SB",
|
||||
bgez_2 = "04010000SB",
|
||||
bltzl_2 = "04020000SB",
|
||||
bgezl_2 = "04030000SB",
|
||||
tgei_2 = "04080000SI",
|
||||
tgeiu_2 = "04090000SI",
|
||||
tlti_2 = "040a0000SI",
|
||||
tltiu_2 = "040b0000SI",
|
||||
teqi_2 = "040c0000SI",
|
||||
tnei_2 = "040e0000SI",
|
||||
bltzal_2 = "04100000SB",
|
||||
bal_1 = "04110000B",
|
||||
bgezal_2 = "04110000SB",
|
||||
bltzall_2 = "04120000SB",
|
||||
bgezall_2 = "04130000SB",
|
||||
synci_1 = "041f0000O",
|
||||
|
||||
-- Opcode SPECIAL2.
|
||||
madd_2 = "70000000ST",
|
||||
maddu_2 = "70000001ST",
|
||||
mul_3 = "70000002DST",
|
||||
msub_2 = "70000004ST",
|
||||
msubu_2 = "70000005ST",
|
||||
clz_2 = "70000020DS=",
|
||||
clo_2 = "70000021DS=",
|
||||
sdbbp_0 = "7000003f",
|
||||
sdbbp_1 = "7000003fY",
|
||||
|
||||
-- Opcode SPECIAL3.
|
||||
ext_4 = "7c000000TSAM", -- Note: last arg is msbd = size-1
|
||||
ins_4 = "7c000004TSAM", -- Note: last arg is msb = pos+size-1
|
||||
wsbh_2 = "7c0000a0DT",
|
||||
seb_2 = "7c000420DT",
|
||||
seh_2 = "7c000620DT",
|
||||
rdhwr_2 = "7c00003bTD",
|
||||
|
||||
-- Opcode COP0.
|
||||
mfc0_2 = "40000000TD",
|
||||
mfc0_3 = "40000000TDW",
|
||||
mtc0_2 = "40800000TD",
|
||||
mtc0_3 = "40800000TDW",
|
||||
rdpgpr_2 = "41400000DT",
|
||||
di_0 = "41606000",
|
||||
di_1 = "41606000T",
|
||||
ei_0 = "41606020",
|
||||
ei_1 = "41606020T",
|
||||
wrpgpr_2 = "41c00000DT",
|
||||
tlbr_0 = "42000001",
|
||||
tlbwi_0 = "42000002",
|
||||
tlbwr_0 = "42000006",
|
||||
tlbp_0 = "42000008",
|
||||
eret_0 = "42000018",
|
||||
deret_0 = "4200001f",
|
||||
wait_0 = "42000020",
|
||||
|
||||
-- Opcode COP1.
|
||||
mfc1_2 = "44000000TG",
|
||||
cfc1_2 = "44400000TG",
|
||||
mfhc1_2 = "44600000TG",
|
||||
mtc1_2 = "44800000TG",
|
||||
ctc1_2 = "44c00000TG",
|
||||
mthc1_2 = "44e00000TG",
|
||||
|
||||
bc1f_1 = "45000000B",
|
||||
bc1f_2 = "45000000CB",
|
||||
bc1t_1 = "45010000B",
|
||||
bc1t_2 = "45010000CB",
|
||||
bc1fl_1 = "45020000B",
|
||||
bc1fl_2 = "45020000CB",
|
||||
bc1tl_1 = "45030000B",
|
||||
bc1tl_2 = "45030000CB",
|
||||
|
||||
["add.s_3"] = "46000000FGH",
|
||||
["sub.s_3"] = "46000001FGH",
|
||||
["mul.s_3"] = "46000002FGH",
|
||||
["div.s_3"] = "46000003FGH",
|
||||
["sqrt.s_2"] = "46000004FG",
|
||||
["abs.s_2"] = "46000005FG",
|
||||
["mov.s_2"] = "46000006FG",
|
||||
["neg.s_2"] = "46000007FG",
|
||||
["round.l.s_2"] = "46000008FG",
|
||||
["trunc.l.s_2"] = "46000009FG",
|
||||
["ceil.l.s_2"] = "4600000aFG",
|
||||
["floor.l.s_2"] = "4600000bFG",
|
||||
["round.w.s_2"] = "4600000cFG",
|
||||
["trunc.w.s_2"] = "4600000dFG",
|
||||
["ceil.w.s_2"] = "4600000eFG",
|
||||
["floor.w.s_2"] = "4600000fFG",
|
||||
["movf.s_2"] = "46000011FG",
|
||||
["movf.s_3"] = "46000011FGC",
|
||||
["movt.s_2"] = "46010011FG",
|
||||
["movt.s_3"] = "46010011FGC",
|
||||
["movz.s_3"] = "46000012FGT",
|
||||
["movn.s_3"] = "46000013FGT",
|
||||
["recip.s_2"] = "46000015FG",
|
||||
["rsqrt.s_2"] = "46000016FG",
|
||||
["cvt.d.s_2"] = "46000021FG",
|
||||
["cvt.w.s_2"] = "46000024FG",
|
||||
["cvt.l.s_2"] = "46000025FG",
|
||||
["cvt.ps.s_3"] = "46000026FGH",
|
||||
["c.f.s_2"] = "46000030GH",
|
||||
["c.f.s_3"] = "46000030VGH",
|
||||
["c.un.s_2"] = "46000031GH",
|
||||
["c.un.s_3"] = "46000031VGH",
|
||||
["c.eq.s_2"] = "46000032GH",
|
||||
["c.eq.s_3"] = "46000032VGH",
|
||||
["c.ueq.s_2"] = "46000033GH",
|
||||
["c.ueq.s_3"] = "46000033VGH",
|
||||
["c.olt.s_2"] = "46000034GH",
|
||||
["c.olt.s_3"] = "46000034VGH",
|
||||
["c.ult.s_2"] = "46000035GH",
|
||||
["c.ult.s_3"] = "46000035VGH",
|
||||
["c.ole.s_2"] = "46000036GH",
|
||||
["c.ole.s_3"] = "46000036VGH",
|
||||
["c.ule.s_2"] = "46000037GH",
|
||||
["c.ule.s_3"] = "46000037VGH",
|
||||
["c.sf.s_2"] = "46000038GH",
|
||||
["c.sf.s_3"] = "46000038VGH",
|
||||
["c.ngle.s_2"] = "46000039GH",
|
||||
["c.ngle.s_3"] = "46000039VGH",
|
||||
["c.seq.s_2"] = "4600003aGH",
|
||||
["c.seq.s_3"] = "4600003aVGH",
|
||||
["c.ngl.s_2"] = "4600003bGH",
|
||||
["c.ngl.s_3"] = "4600003bVGH",
|
||||
["c.lt.s_2"] = "4600003cGH",
|
||||
["c.lt.s_3"] = "4600003cVGH",
|
||||
["c.nge.s_2"] = "4600003dGH",
|
||||
["c.nge.s_3"] = "4600003dVGH",
|
||||
["c.le.s_2"] = "4600003eGH",
|
||||
["c.le.s_3"] = "4600003eVGH",
|
||||
["c.ngt.s_2"] = "4600003fGH",
|
||||
["c.ngt.s_3"] = "4600003fVGH",
|
||||
|
||||
["add.d_3"] = "46200000FGH",
|
||||
["sub.d_3"] = "46200001FGH",
|
||||
["mul.d_3"] = "46200002FGH",
|
||||
["div.d_3"] = "46200003FGH",
|
||||
["sqrt.d_2"] = "46200004FG",
|
||||
["abs.d_2"] = "46200005FG",
|
||||
["mov.d_2"] = "46200006FG",
|
||||
["neg.d_2"] = "46200007FG",
|
||||
["round.l.d_2"] = "46200008FG",
|
||||
["trunc.l.d_2"] = "46200009FG",
|
||||
["ceil.l.d_2"] = "4620000aFG",
|
||||
["floor.l.d_2"] = "4620000bFG",
|
||||
["round.w.d_2"] = "4620000cFG",
|
||||
["trunc.w.d_2"] = "4620000dFG",
|
||||
["ceil.w.d_2"] = "4620000eFG",
|
||||
["floor.w.d_2"] = "4620000fFG",
|
||||
["movf.d_2"] = "46200011FG",
|
||||
["movf.d_3"] = "46200011FGC",
|
||||
["movt.d_2"] = "46210011FG",
|
||||
["movt.d_3"] = "46210011FGC",
|
||||
["movz.d_3"] = "46200012FGT",
|
||||
["movn.d_3"] = "46200013FGT",
|
||||
["recip.d_2"] = "46200015FG",
|
||||
["rsqrt.d_2"] = "46200016FG",
|
||||
["cvt.s.d_2"] = "46200020FG",
|
||||
["cvt.w.d_2"] = "46200024FG",
|
||||
["cvt.l.d_2"] = "46200025FG",
|
||||
["c.f.d_2"] = "46200030GH",
|
||||
["c.f.d_3"] = "46200030VGH",
|
||||
["c.un.d_2"] = "46200031GH",
|
||||
["c.un.d_3"] = "46200031VGH",
|
||||
["c.eq.d_2"] = "46200032GH",
|
||||
["c.eq.d_3"] = "46200032VGH",
|
||||
["c.ueq.d_2"] = "46200033GH",
|
||||
["c.ueq.d_3"] = "46200033VGH",
|
||||
["c.olt.d_2"] = "46200034GH",
|
||||
["c.olt.d_3"] = "46200034VGH",
|
||||
["c.ult.d_2"] = "46200035GH",
|
||||
["c.ult.d_3"] = "46200035VGH",
|
||||
["c.ole.d_2"] = "46200036GH",
|
||||
["c.ole.d_3"] = "46200036VGH",
|
||||
["c.ule.d_2"] = "46200037GH",
|
||||
["c.ule.d_3"] = "46200037VGH",
|
||||
["c.sf.d_2"] = "46200038GH",
|
||||
["c.sf.d_3"] = "46200038VGH",
|
||||
["c.ngle.d_2"] = "46200039GH",
|
||||
["c.ngle.d_3"] = "46200039VGH",
|
||||
["c.seq.d_2"] = "4620003aGH",
|
||||
["c.seq.d_3"] = "4620003aVGH",
|
||||
["c.ngl.d_2"] = "4620003bGH",
|
||||
["c.ngl.d_3"] = "4620003bVGH",
|
||||
["c.lt.d_2"] = "4620003cGH",
|
||||
["c.lt.d_3"] = "4620003cVGH",
|
||||
["c.nge.d_2"] = "4620003dGH",
|
||||
["c.nge.d_3"] = "4620003dVGH",
|
||||
["c.le.d_2"] = "4620003eGH",
|
||||
["c.le.d_3"] = "4620003eVGH",
|
||||
["c.ngt.d_2"] = "4620003fGH",
|
||||
["c.ngt.d_3"] = "4620003fVGH",
|
||||
|
||||
["add.ps_3"] = "46c00000FGH",
|
||||
["sub.ps_3"] = "46c00001FGH",
|
||||
["mul.ps_3"] = "46c00002FGH",
|
||||
["abs.ps_2"] = "46c00005FG",
|
||||
["mov.ps_2"] = "46c00006FG",
|
||||
["neg.ps_2"] = "46c00007FG",
|
||||
["movf.ps_2"] = "46c00011FG",
|
||||
["movf.ps_3"] = "46c00011FGC",
|
||||
["movt.ps_2"] = "46c10011FG",
|
||||
["movt.ps_3"] = "46c10011FGC",
|
||||
["movz.ps_3"] = "46c00012FGT",
|
||||
["movn.ps_3"] = "46c00013FGT",
|
||||
["cvt.s.pu_2"] = "46c00020FG",
|
||||
["cvt.s.pl_2"] = "46c00028FG",
|
||||
["pll.ps_3"] = "46c0002cFGH",
|
||||
["plu.ps_3"] = "46c0002dFGH",
|
||||
["pul.ps_3"] = "46c0002eFGH",
|
||||
["puu.ps_3"] = "46c0002fFGH",
|
||||
["c.f.ps_2"] = "46c00030GH",
|
||||
["c.f.ps_3"] = "46c00030VGH",
|
||||
["c.un.ps_2"] = "46c00031GH",
|
||||
["c.un.ps_3"] = "46c00031VGH",
|
||||
["c.eq.ps_2"] = "46c00032GH",
|
||||
["c.eq.ps_3"] = "46c00032VGH",
|
||||
["c.ueq.ps_2"] = "46c00033GH",
|
||||
["c.ueq.ps_3"] = "46c00033VGH",
|
||||
["c.olt.ps_2"] = "46c00034GH",
|
||||
["c.olt.ps_3"] = "46c00034VGH",
|
||||
["c.ult.ps_2"] = "46c00035GH",
|
||||
["c.ult.ps_3"] = "46c00035VGH",
|
||||
["c.ole.ps_2"] = "46c00036GH",
|
||||
["c.ole.ps_3"] = "46c00036VGH",
|
||||
["c.ule.ps_2"] = "46c00037GH",
|
||||
["c.ule.ps_3"] = "46c00037VGH",
|
||||
["c.sf.ps_2"] = "46c00038GH",
|
||||
["c.sf.ps_3"] = "46c00038VGH",
|
||||
["c.ngle.ps_2"] = "46c00039GH",
|
||||
["c.ngle.ps_3"] = "46c00039VGH",
|
||||
["c.seq.ps_2"] = "46c0003aGH",
|
||||
["c.seq.ps_3"] = "46c0003aVGH",
|
||||
["c.ngl.ps_2"] = "46c0003bGH",
|
||||
["c.ngl.ps_3"] = "46c0003bVGH",
|
||||
["c.lt.ps_2"] = "46c0003cGH",
|
||||
["c.lt.ps_3"] = "46c0003cVGH",
|
||||
["c.nge.ps_2"] = "46c0003dGH",
|
||||
["c.nge.ps_3"] = "46c0003dVGH",
|
||||
["c.le.ps_2"] = "46c0003eGH",
|
||||
["c.le.ps_3"] = "46c0003eVGH",
|
||||
["c.ngt.ps_2"] = "46c0003fGH",
|
||||
["c.ngt.ps_3"] = "46c0003fVGH",
|
||||
|
||||
["cvt.s.w_2"] = "46800020FG",
|
||||
["cvt.d.w_2"] = "46800021FG",
|
||||
|
||||
["cvt.s.l_2"] = "46a00020FG",
|
||||
["cvt.d.l_2"] = "46a00021FG",
|
||||
|
||||
-- Opcode COP1X.
|
||||
lwxc1_2 = "4c000000FX",
|
||||
ldxc1_2 = "4c000001FX",
|
||||
luxc1_2 = "4c000005FX",
|
||||
swxc1_2 = "4c000008FX",
|
||||
sdxc1_2 = "4c000009FX",
|
||||
suxc1_2 = "4c00000dFX",
|
||||
prefx_2 = "4c00000fMX",
|
||||
["alnv.ps_4"] = "4c00001eFGHS",
|
||||
["madd.s_4"] = "4c000020FRGH",
|
||||
["madd.d_4"] = "4c000021FRGH",
|
||||
["madd.ps_4"] = "4c000026FRGH",
|
||||
["msub.s_4"] = "4c000028FRGH",
|
||||
["msub.d_4"] = "4c000029FRGH",
|
||||
["msub.ps_4"] = "4c00002eFRGH",
|
||||
["nmadd.s_4"] = "4c000030FRGH",
|
||||
["nmadd.d_4"] = "4c000031FRGH",
|
||||
["nmadd.ps_4"] = "4c000036FRGH",
|
||||
["nmsub.s_4"] = "4c000038FRGH",
|
||||
["nmsub.d_4"] = "4c000039FRGH",
|
||||
["nmsub.ps_4"] = "4c00003eFRGH",
|
||||
}
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local function parse_gpr(expr)
|
||||
local tname, ovreg = match(expr, "^([%w_]+):(r[1-3]?[0-9])$")
|
||||
local tp = map_type[tname or expr]
|
||||
if tp then
|
||||
local reg = ovreg or tp.reg
|
||||
if not reg then
|
||||
werror("type `"..(tname or expr).."' needs a register override")
|
||||
end
|
||||
expr = reg
|
||||
end
|
||||
local r = match(expr, "^r([1-3]?[0-9])$")
|
||||
if r then
|
||||
r = tonumber(r)
|
||||
if r <= 31 then return r, tp end
|
||||
end
|
||||
werror("bad register name `"..expr.."'")
|
||||
end
|
||||
|
||||
local function parse_fpr(expr)
|
||||
local r = match(expr, "^f([1-3]?[0-9])$")
|
||||
if r then
|
||||
r = tonumber(r)
|
||||
if r <= 31 then return r end
|
||||
end
|
||||
werror("bad register name `"..expr.."'")
|
||||
end
|
||||
|
||||
local function parse_imm(imm, bits, shift, scale, signed)
|
||||
local n = tonumber(imm)
|
||||
if n then
|
||||
local m = sar(n, scale)
|
||||
if shl(m, scale) == n then
|
||||
if signed then
|
||||
local s = sar(m, bits-1)
|
||||
if s == 0 then return shl(m, shift)
|
||||
elseif s == -1 then return shl(m + shl(1, bits), shift) end
|
||||
else
|
||||
if sar(m, bits) == 0 then return shl(m, shift) end
|
||||
end
|
||||
end
|
||||
werror("out of range immediate `"..imm.."'")
|
||||
elseif match(imm, "^[rf]([1-3]?[0-9])$") or
|
||||
match(imm, "^([%w_]+):([rf][1-3]?[0-9])$") then
|
||||
werror("expected immediate operand, got register")
|
||||
else
|
||||
waction("IMM", (signed and 32768 or 0)+scale*1024+bits*32+shift, imm)
|
||||
return 0
|
||||
end
|
||||
end
|
||||
|
||||
local function parse_disp(disp)
|
||||
local imm, reg = match(disp, "^(.*)%(([%w_:]+)%)$")
|
||||
if imm then
|
||||
local r = shl(parse_gpr(reg), 21)
|
||||
local extname = match(imm, "^extern%s+(%S+)$")
|
||||
if extname then
|
||||
waction("REL_EXT", map_extern[extname], nil, 1)
|
||||
return r
|
||||
else
|
||||
return r + parse_imm(imm, 16, 0, 0, true)
|
||||
end
|
||||
end
|
||||
local reg, tailr = match(disp, "^([%w_:]+)%s*(.*)$")
|
||||
if reg and tailr ~= "" then
|
||||
local r, tp = parse_gpr(reg)
|
||||
if tp then
|
||||
waction("IMM", 32768+16*32, format(tp.ctypefmt, tailr))
|
||||
return shl(r, 21)
|
||||
end
|
||||
end
|
||||
werror("bad displacement `"..disp.."'")
|
||||
end
|
||||
|
||||
local function parse_index(idx)
|
||||
local rt, rs = match(idx, "^(.*)%(([%w_:]+)%)$")
|
||||
if rt then
|
||||
rt = parse_gpr(rt)
|
||||
rs = parse_gpr(rs)
|
||||
return shl(rt, 16) + shl(rs, 21)
|
||||
end
|
||||
werror("bad index `"..idx.."'")
|
||||
end
|
||||
|
||||
local function parse_label(label, def)
|
||||
local prefix = sub(label, 1, 2)
|
||||
-- =>label (pc label reference)
|
||||
if prefix == "=>" then
|
||||
return "PC", 0, sub(label, 3)
|
||||
end
|
||||
-- ->name (global label reference)
|
||||
if prefix == "->" then
|
||||
return "LG", map_global[sub(label, 3)]
|
||||
end
|
||||
if def then
|
||||
-- [1-9] (local label definition)
|
||||
if match(label, "^[1-9]$") then
|
||||
return "LG", 10+tonumber(label)
|
||||
end
|
||||
else
|
||||
-- [<>][1-9] (local label reference)
|
||||
local dir, lnum = match(label, "^([<>])([1-9])$")
|
||||
if dir then -- Fwd: 1-9, Bkwd: 11-19.
|
||||
return "LG", lnum + (dir == ">" and 0 or 10)
|
||||
end
|
||||
-- extern label (extern label reference)
|
||||
local extname = match(label, "^extern%s+(%S+)$")
|
||||
if extname then
|
||||
return "EXT", map_extern[extname]
|
||||
end
|
||||
end
|
||||
werror("bad label `"..label.."'")
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Handle opcodes defined with template strings.
|
||||
map_op[".template__"] = function(params, template, nparams)
|
||||
if not params then return sub(template, 9) end
|
||||
local op = tonumber(sub(template, 1, 8), 16)
|
||||
local n = 1
|
||||
|
||||
-- Limit number of section buffer positions used by a single dasm_put().
|
||||
-- A single opcode needs a maximum of 2 positions (ins/ext).
|
||||
if secpos+2 > maxsecpos then wflush() end
|
||||
local pos = wpos()
|
||||
|
||||
-- Process each character.
|
||||
for p in gmatch(sub(template, 9), ".") do
|
||||
if p == "D" then
|
||||
op = op + shl(parse_gpr(params[n]), 11); n = n + 1
|
||||
elseif p == "T" then
|
||||
op = op + shl(parse_gpr(params[n]), 16); n = n + 1
|
||||
elseif p == "S" then
|
||||
op = op + shl(parse_gpr(params[n]), 21); n = n + 1
|
||||
elseif p == "F" then
|
||||
op = op + shl(parse_fpr(params[n]), 6); n = n + 1
|
||||
elseif p == "G" then
|
||||
op = op + shl(parse_fpr(params[n]), 11); n = n + 1
|
||||
elseif p == "H" then
|
||||
op = op + shl(parse_fpr(params[n]), 16); n = n + 1
|
||||
elseif p == "R" then
|
||||
op = op + shl(parse_fpr(params[n]), 21); n = n + 1
|
||||
elseif p == "I" then
|
||||
op = op + parse_imm(params[n], 16, 0, 0, true); n = n + 1
|
||||
elseif p == "U" then
|
||||
op = op + parse_imm(params[n], 16, 0, 0, false); n = n + 1
|
||||
elseif p == "O" then
|
||||
op = op + parse_disp(params[n]); n = n + 1
|
||||
elseif p == "X" then
|
||||
op = op + parse_index(params[n]); n = n + 1
|
||||
elseif p == "B" or p == "J" then
|
||||
local mode, n, s = parse_label(params[n], false)
|
||||
if p == "B" then n = n + 2048 end
|
||||
waction("REL_"..mode, n, s, 1)
|
||||
n = n + 1
|
||||
elseif p == "A" then
|
||||
op = op + parse_imm(params[n], 5, 6, 0, false); n = n + 1
|
||||
elseif p == "M" then
|
||||
op = op + parse_imm(params[n], 5, 11, 0, false); n = n + 1
|
||||
elseif p == "N" then
|
||||
op = op + parse_imm(params[n], 5, 16, 0, false); n = n + 1
|
||||
elseif p == "C" then
|
||||
op = op + parse_imm(params[n], 3, 18, 0, false); n = n + 1
|
||||
elseif p == "V" then
|
||||
op = op + parse_imm(params[n], 3, 8, 0, false); n = n + 1
|
||||
elseif p == "W" then
|
||||
op = op + parse_imm(params[n], 3, 0, 0, false); n = n + 1
|
||||
elseif p == "Y" then
|
||||
op = op + parse_imm(params[n], 20, 6, 0, false); n = n + 1
|
||||
elseif p == "Z" then
|
||||
op = op + parse_imm(params[n], 10, 6, 0, false); n = n + 1
|
||||
elseif p == "=" then
|
||||
op = op + shl(band(op, 0xf800), 5) -- Copy D to T for clz, clo.
|
||||
else
|
||||
assert(false)
|
||||
end
|
||||
end
|
||||
wputpos(pos, op)
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Pseudo-opcode to mark the position where the action list is to be emitted.
|
||||
map_op[".actionlist_1"] = function(params)
|
||||
if not params then return "cvar" end
|
||||
local name = params[1] -- No syntax check. You get to keep the pieces.
|
||||
wline(function(out) writeactions(out, name) end)
|
||||
end
|
||||
|
||||
-- Pseudo-opcode to mark the position where the global enum is to be emitted.
|
||||
map_op[".globals_1"] = function(params)
|
||||
if not params then return "prefix" end
|
||||
local prefix = params[1] -- No syntax check. You get to keep the pieces.
|
||||
wline(function(out) writeglobals(out, prefix) end)
|
||||
end
|
||||
|
||||
-- Pseudo-opcode to mark the position where the global names are to be emitted.
|
||||
map_op[".globalnames_1"] = function(params)
|
||||
if not params then return "cvar" end
|
||||
local name = params[1] -- No syntax check. You get to keep the pieces.
|
||||
wline(function(out) writeglobalnames(out, name) end)
|
||||
end
|
||||
|
||||
-- Pseudo-opcode to mark the position where the extern names are to be emitted.
|
||||
map_op[".externnames_1"] = function(params)
|
||||
if not params then return "cvar" end
|
||||
local name = params[1] -- No syntax check. You get to keep the pieces.
|
||||
wline(function(out) writeexternnames(out, name) end)
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Label pseudo-opcode (converted from trailing colon form).
|
||||
map_op[".label_1"] = function(params)
|
||||
if not params then return "[1-9] | ->global | =>pcexpr" end
|
||||
if secpos+1 > maxsecpos then wflush() end
|
||||
local mode, n, s = parse_label(params[1], true)
|
||||
if mode == "EXT" then werror("bad label definition") end
|
||||
waction("LABEL_"..mode, n, s, 1)
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Pseudo-opcodes for data storage.
|
||||
map_op[".long_*"] = function(params)
|
||||
if not params then return "imm..." end
|
||||
for _,p in ipairs(params) do
|
||||
local n = tonumber(p)
|
||||
if not n then werror("bad immediate `"..p.."'") end
|
||||
if n < 0 then n = n + 2^32 end
|
||||
wputw(n)
|
||||
if secpos+2 > maxsecpos then wflush() end
|
||||
end
|
||||
end
|
||||
|
||||
-- Alignment pseudo-opcode.
|
||||
map_op[".align_1"] = function(params)
|
||||
if not params then return "numpow2" end
|
||||
if secpos+1 > maxsecpos then wflush() end
|
||||
local align = tonumber(params[1])
|
||||
if align then
|
||||
local x = align
|
||||
-- Must be a power of 2 in the range (2 ... 256).
|
||||
for i=1,8 do
|
||||
x = x / 2
|
||||
if x == 1 then
|
||||
waction("ALIGN", align-1, nil, 1) -- Action byte is 2**n-1.
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
werror("bad alignment")
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Pseudo-opcode for (primitive) type definitions (map to C types).
|
||||
map_op[".type_3"] = function(params, nparams)
|
||||
if not params then
|
||||
return nparams == 2 and "name, ctype" or "name, ctype, reg"
|
||||
end
|
||||
local name, ctype, reg = params[1], params[2], params[3]
|
||||
if not match(name, "^[%a_][%w_]*$") then
|
||||
werror("bad type name `"..name.."'")
|
||||
end
|
||||
local tp = map_type[name]
|
||||
if tp then
|
||||
werror("duplicate type `"..name.."'")
|
||||
end
|
||||
-- Add #type to defines. A bit unclean to put it in map_archdef.
|
||||
map_archdef["#"..name] = "sizeof("..ctype..")"
|
||||
-- Add new type and emit shortcut define.
|
||||
local num = ctypenum + 1
|
||||
map_type[name] = {
|
||||
ctype = ctype,
|
||||
ctypefmt = format("Dt%X(%%s)", num),
|
||||
reg = reg,
|
||||
}
|
||||
wline(format("#define Dt%X(_V) (int)(ptrdiff_t)&(((%s *)0)_V)", num, ctype))
|
||||
ctypenum = num
|
||||
end
|
||||
map_op[".type_2"] = map_op[".type_3"]
|
||||
|
||||
-- Dump type definitions.
|
||||
local function dumptypes(out, lvl)
|
||||
local t = {}
|
||||
for name in pairs(map_type) do t[#t+1] = name end
|
||||
sort(t)
|
||||
out:write("Type definitions:\n")
|
||||
for _,name in ipairs(t) do
|
||||
local tp = map_type[name]
|
||||
local reg = tp.reg or ""
|
||||
out:write(format(" %-20s %-20s %s\n", name, tp.ctype, reg))
|
||||
end
|
||||
out:write("\n")
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Set the current section.
|
||||
function _M.section(num)
|
||||
waction("SECTION", num)
|
||||
wflush(true) -- SECTION is a terminal action.
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Dump architecture description.
|
||||
function _M.dumparch(out)
|
||||
out:write(format("DynASM %s version %s, released %s\n\n",
|
||||
_info.arch, _info.version, _info.release))
|
||||
dumpactions(out)
|
||||
end
|
||||
|
||||
-- Dump all user defined elements.
|
||||
function _M.dumpdef(out, lvl)
|
||||
dumptypes(out, lvl)
|
||||
dumpglobals(out, lvl)
|
||||
dumpexterns(out, lvl)
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Pass callbacks from/to the DynASM core.
|
||||
function _M.passcb(wl, we, wf, ww)
|
||||
wline, werror, wfatal, wwarn = wl, we, wf, ww
|
||||
return wflush
|
||||
end
|
||||
|
||||
-- Setup the arch-specific module.
|
||||
function _M.setup(arch, opt)
|
||||
g_arch, g_opt = arch, opt
|
||||
end
|
||||
|
||||
-- Merge the core maps and the arch-specific maps.
|
||||
function _M.mergemaps(map_coreop, map_def)
|
||||
setmetatable(map_op, { __index = map_coreop })
|
||||
setmetatable(map_def, { __index = map_archdef })
|
||||
return map_op, map_def
|
||||
end
|
||||
|
||||
return _M
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
+144
@@ -0,0 +1,144 @@
|
||||
|
||||
--wrappers around mmap to support dynamic code exection.
|
||||
--Written by Cosmin Apreutesei. Public Domain.
|
||||
--Tested with Windows, Linux and OSX, x86 and x86-64.
|
||||
|
||||
local ffi = require'ffi'
|
||||
local C = ffi.C
|
||||
|
||||
local function checkh(ptr) return assert(ptr ~= nil and ptr) end
|
||||
local function checkz(ret) assert(ret == 0) end
|
||||
local function checknz(ret) assert(ret ~= 0) end
|
||||
|
||||
local new, free, protect
|
||||
|
||||
--Using VirtualAlloc allows memory protection, but can only allocate memory in multiple-of-64K chunks.
|
||||
local USE_VIRTUALALLOC = false
|
||||
|
||||
if ffi.os == 'Windows' then
|
||||
|
||||
if USE_VIRTUALALLOC then
|
||||
|
||||
ffi.cdef[[
|
||||
void* VirtualAlloc(void* lpAddress, size_t dwSize, uint32_t flAllocationType, uint32_t flProtect);
|
||||
int VirtualFree(void* lpAddress, size_t dwSize, uint32_t dwFreeType);
|
||||
int VirtualProtect(void* lpAddress, size_t dwSize, uint32_t flNewProtect, uint32_t* lpflOldProtect);
|
||||
]]
|
||||
|
||||
local PAGE_READWRITE = 0x04
|
||||
local PAGE_EXECUTE_READ = 0x20
|
||||
local MEM_COMMIT = 0x1000
|
||||
local MEM_RESERVE = 0x2000
|
||||
local MEM_RELEASE = 0x8000
|
||||
|
||||
function new(size)
|
||||
return checkh(C.VirtualAlloc(nil, size, bit.bor(MEM_RESERVE, MEM_COMMIT), PAGE_READWRITE))
|
||||
end
|
||||
|
||||
function protect(addr, size)
|
||||
local oldprotect = ffi.new'uint32_t[1]' --because null not accepted
|
||||
checknz(C.VirtualProtect(addr, size, PAGE_EXECUTE_READ, oldprotect))
|
||||
end
|
||||
|
||||
function free(addr, size)
|
||||
assert(size, 'size required') --on other platforms
|
||||
checknz(C.VirtualFree(addr, 0, MEM_RELEASE))
|
||||
end
|
||||
|
||||
else
|
||||
|
||||
local HEAP_NO_SERIALIZE = 0x00000001
|
||||
local HEAP_ZERO_MEMORY = 0x00000008
|
||||
local HEAP_CREATE_ENABLE_EXECUTE = 0x00040000
|
||||
|
||||
ffi.cdef[[
|
||||
void* HeapCreate(uint32_t flOptions, size_t dwInitialSize, size_t dwMaximumSize);
|
||||
void* HeapAlloc(void* hHeap, uint32_t dwFlags, size_t dwBytes);
|
||||
int HeapFree(void* hHeap, uint32_t dwFlags, void* lpMem);
|
||||
]]
|
||||
|
||||
local heap
|
||||
|
||||
function new(size)
|
||||
heap = heap or checkh(C.HeapCreate(bit.bor(HEAP_NO_SERIALIZE, HEAP_CREATE_ENABLE_EXECUTE), 0, 0))
|
||||
return checkh(C.HeapAlloc(heap, HEAP_ZERO_MEMORY, size))
|
||||
end
|
||||
|
||||
function protect(addr, size) end
|
||||
|
||||
function free(addr, size)
|
||||
assert(size, 'size required') --on other platforms
|
||||
checknz(C.HeapFree(heap, HEAP_NO_SERIALIZE, addr))
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
elseif ffi.os == 'Linux' or ffi.os == 'OSX' then
|
||||
|
||||
if ffi.os == 'OSX' then
|
||||
ffi.cdef'typedef int64_t off_t;'
|
||||
else
|
||||
ffi.cdef'typedef long int off_t;'
|
||||
end
|
||||
|
||||
ffi.cdef[[
|
||||
void* mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
|
||||
int munmap(void *addr, size_t length);
|
||||
int mprotect(void *addr, size_t len, int prot);
|
||||
]]
|
||||
|
||||
local PROT_READ = 1
|
||||
local PROT_WRITE = 2
|
||||
local PROT_EXEC = 4
|
||||
local MAP_PRIVATE = 2
|
||||
local MAP_ANON = ffi.os == 'Linux' and 0x20 or 0x1000
|
||||
|
||||
function new(size)
|
||||
local ret = C.mmap(nil, size, bit.bor(PROT_READ, PROT_WRITE), bit.bor(MAP_PRIVATE, MAP_ANON), -1, 0)
|
||||
if ffi.cast('intptr_t', ret) == ffi.cast('intptr_t', -1) then
|
||||
error(string.format('mmap errno: %d', ffi.errno()))
|
||||
end
|
||||
return checkh(ret)
|
||||
end
|
||||
|
||||
function protect(addr, size)
|
||||
checkz(C.mprotect(addr, size, bit.bor(PROT_READ, PROT_EXEC)))
|
||||
end
|
||||
|
||||
function free(addr, size)
|
||||
checkz(C.munmap(addr, size))
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
local new = function(size) --override for hooking to gc
|
||||
local addr = new(size)
|
||||
ffi.gc(addr, function(addr)
|
||||
free(addr, size)
|
||||
ffi.gc(addr, nil)
|
||||
end)
|
||||
return addr
|
||||
end
|
||||
|
||||
if not ... then
|
||||
local function test(size)
|
||||
local addr = new(size)
|
||||
print(addr)
|
||||
addr = ffi.cast('int32_t*', addr)
|
||||
assert(addr[0] == 0)
|
||||
addr[0] = 1234 --writable
|
||||
assert(addr[0] == 1234)
|
||||
protect(addr, size)
|
||||
--addr[0] = 4321 --uncomment this to get a crash (r/o memory); TODO: test if executable
|
||||
--addr = nil; collectgarbage() --enable this to fail the assertion below
|
||||
return addr
|
||||
end
|
||||
local a1 = test(64*1024*1000) --64MB
|
||||
local a2 = test(16) --16 bytes
|
||||
assert(a1 ~= a2) --different pages
|
||||
a1 = nil
|
||||
a2 = nil
|
||||
collectgarbage() --TODO: test if finalizer was called
|
||||
end
|
||||
|
||||
return {new = new, free = free, protect = protect}
|
||||
-1919
File diff suppressed because it is too large
Load Diff
+11
-2
@@ -8,5 +8,14 @@
|
||||
-- All the interesting stuff is there.
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
x64 = true -- Using a global is an ugly, but effective solution.
|
||||
return require("dasm_x86")
|
||||
--unload dasm_x86 if it's already loaded.
|
||||
local dasm_x86 = package.loaded.dasm_x86
|
||||
package.loaded.dasm_x86 = nil
|
||||
|
||||
rawset(_G, 'x64', true) -- Using a global is an ugly, but effective solution.
|
||||
local dasm_x64 = require("dasm_x86")
|
||||
|
||||
package.loaded.dasm_x86 = dasm_x86 --put it back
|
||||
|
||||
return dasm_x64
|
||||
|
||||
|
||||
+146
-39
@@ -5,13 +5,13 @@
|
||||
-- See dynasm.lua for full copyright notice.
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local x64 = x64
|
||||
local x64 = rawget(_G, "x64") --rawget so it works with strict.lua
|
||||
|
||||
-- Module information:
|
||||
local _info = {
|
||||
arch = x64 and "x64" or "x86",
|
||||
description = "DynASM x86/x64 module",
|
||||
version = "1.4.0",
|
||||
version = "1.4.0_luamode",
|
||||
vernum = 10400,
|
||||
release = "2015-10-18",
|
||||
author = "Mike Pall",
|
||||
@@ -32,9 +32,12 @@ local bit = bit or require("bit")
|
||||
local band, bxor, shl, shr = bit.band, bit.bxor, bit.lshift, bit.rshift
|
||||
|
||||
-- Inherited tables and callbacks.
|
||||
local g_opt, g_arch
|
||||
local g_opt, g_arch, g_map_def
|
||||
local wline, werror, wfatal, wwarn
|
||||
|
||||
-- Global flag to generate Lua code instead of C code.
|
||||
local luamode
|
||||
|
||||
-- Action name list.
|
||||
-- CHECK: Keep this in sync with the C code!
|
||||
local action_names = {
|
||||
@@ -74,14 +77,20 @@ local map_action = {}
|
||||
local actfirst = 256-#action_names
|
||||
|
||||
-- Action list buffer and string (only used to remove dupes).
|
||||
local actlist = {}
|
||||
local actstr = ""
|
||||
local actlist, actstr
|
||||
|
||||
-- Argument list for next dasm_put(). Start with offset 0 into action list.
|
||||
local actargs = { 0 }
|
||||
-- Argument list for next dasm_put().
|
||||
local actargs
|
||||
|
||||
-- Current number of section buffer positions for dasm_put().
|
||||
local secpos = 1
|
||||
local secpos
|
||||
|
||||
local function init_actionlist()
|
||||
actlist = {}
|
||||
actstr = ""
|
||||
actargs = { 0 } -- Start with offset 0 into the action list.
|
||||
secpos = 1
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
@@ -107,7 +116,11 @@ local function writeactions(out, name)
|
||||
local last = actlist[nn] or 255
|
||||
actlist[nn] = nil -- Remove last byte.
|
||||
if nn == 0 then nn = 1 end
|
||||
out:write("static const unsigned char ", name, "[", nn, "] = {\n")
|
||||
if luamode then
|
||||
out:write("local ", name, " = ffi.new('const uint8_t[", nn, "]', {\n")
|
||||
else
|
||||
out:write("static const unsigned char ", name, "[", nn, "] = {\n")
|
||||
end
|
||||
local s = " "
|
||||
for n,b in ipairs(actlist) do
|
||||
s = s..b..","
|
||||
@@ -116,7 +129,11 @@ local function writeactions(out, name)
|
||||
s = " "
|
||||
end
|
||||
end
|
||||
out:write(s, last, "\n};\n\n") -- Add last byte back.
|
||||
if luamode then
|
||||
out:write(s, last, "\n})\n\n") -- Add last byte back.
|
||||
else
|
||||
out:write(s, last, "\n};\n\n") -- Add last byte back.
|
||||
end
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
@@ -136,7 +153,11 @@ end
|
||||
|
||||
-- Add call to embedded DynASM C code.
|
||||
local function wcall(func, args)
|
||||
wline(format("dasm_%s(Dst, %s);", func, concat(args, ", ")), true)
|
||||
if luamode then
|
||||
wline(format("dasm.%s(Dst, %s)", func, concat(args, ", ")), true)
|
||||
else
|
||||
wline(format("dasm_%s(Dst, %s);", func, concat(args, ", ")), true)
|
||||
end
|
||||
end
|
||||
|
||||
-- Delete duplicate action list chunks. A tad slow, but so what.
|
||||
@@ -172,15 +193,21 @@ end
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Global label name -> global label number. With auto assignment on 1st use.
|
||||
local next_global = 10
|
||||
local map_global = setmetatable({}, { __index = function(t, name)
|
||||
local next_global, map_global
|
||||
|
||||
local globals_meta = { __index = function(t, name)
|
||||
if not match(name, "^[%a_][%w_@]*$") then werror("bad global label") end
|
||||
local n = next_global
|
||||
if n > 246 then werror("too many global labels") end
|
||||
next_global = n + 1
|
||||
t[name] = n
|
||||
return n
|
||||
end})
|
||||
end}
|
||||
|
||||
local function init_map_global()
|
||||
next_global = 10
|
||||
map_global = setmetatable({}, globals_meta)
|
||||
end
|
||||
|
||||
-- Dump global labels.
|
||||
local function dumpglobals(out, lvl)
|
||||
@@ -197,36 +224,60 @@ end
|
||||
local function writeglobals(out, prefix)
|
||||
local t = {}
|
||||
for name, n in pairs(map_global) do t[n] = name end
|
||||
out:write("enum {\n")
|
||||
for i=10,next_global-1 do
|
||||
out:write(" ", prefix, gsub(t[i], "@.*", ""), ",\n")
|
||||
if luamode then
|
||||
local n = 0
|
||||
for i=10,next_global-1 do
|
||||
out:write("local ", prefix, gsub(t[i], "@.*", ""), "\t= ", n, "\n")
|
||||
n = n + 1
|
||||
end
|
||||
out:write("local ", prefix, "_MAX\t= ", n, "\n") --for compatibility with the C protocol
|
||||
out:write("local DASM_MAXGLOBAL\t= ", n, "\n")
|
||||
else
|
||||
out:write("enum {\n")
|
||||
for i=10,next_global-1 do
|
||||
out:write(" ", prefix, gsub(t[i], "@.*", ""), ",\n")
|
||||
end
|
||||
out:write(" ", prefix, "_MAX\n};\n")
|
||||
end
|
||||
out:write(" ", prefix, "_MAX\n};\n")
|
||||
end
|
||||
|
||||
-- Write global label names.
|
||||
local function writeglobalnames(out, name)
|
||||
local t = {}
|
||||
for name, n in pairs(map_global) do t[n] = name end
|
||||
out:write("static const char *const ", name, "[] = {\n")
|
||||
for i=10,next_global-1 do
|
||||
out:write(" \"", t[i], "\",\n")
|
||||
if luamode then
|
||||
out:write("local ", name, " = {\n")
|
||||
for i=10,next_global-1 do
|
||||
out:write(" ", i == 10 and "[0] = " or "", "\"", t[i], "\",\n")
|
||||
end
|
||||
out:write("}\n")
|
||||
else
|
||||
out:write("static const char *const ", name, "[] = {\n")
|
||||
for i=10,next_global-1 do
|
||||
out:write(" \"", t[i], "\",\n")
|
||||
end
|
||||
out:write(" (const char *)0\n};\n")
|
||||
end
|
||||
out:write(" (const char *)0\n};\n")
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Extern label name -> extern label number. With auto assignment on 1st use.
|
||||
local next_extern = -1
|
||||
local map_extern = setmetatable({}, { __index = function(t, name)
|
||||
local next_extern, map_extern
|
||||
|
||||
local extern_meta = { __index = function(t, name)
|
||||
-- No restrictions on the name for now.
|
||||
local n = next_extern
|
||||
if n < -256 then werror("too many extern labels") end
|
||||
next_extern = n - 1
|
||||
t[name] = n
|
||||
return n
|
||||
end})
|
||||
end}
|
||||
|
||||
local function init_map_extern()
|
||||
next_extern = -1
|
||||
map_extern = setmetatable({}, extern_meta)
|
||||
end
|
||||
|
||||
-- Dump extern labels.
|
||||
local function dumpexterns(out, lvl)
|
||||
@@ -243,11 +294,19 @@ end
|
||||
local function writeexternnames(out, name)
|
||||
local t = {}
|
||||
for name, n in pairs(map_extern) do t[-n] = name end
|
||||
out:write("static const char *const ", name, "[] = {\n")
|
||||
for i=1,-next_extern-1 do
|
||||
out:write(" \"", t[i], "\",\n")
|
||||
if luamode then
|
||||
out:write("local ", name, " = {\n")
|
||||
for i=1,-next_extern-1 do
|
||||
out:write(i==1 and "[0] = " or "", "\"", t[i], "\",\n")
|
||||
end
|
||||
out:write("}\n")
|
||||
else
|
||||
out:write("static const char *const ", name, "[] = {\n")
|
||||
for i=1,-next_extern-1 do
|
||||
out:write(" \"", t[i], "\",\n")
|
||||
end
|
||||
out:write(" (const char *)0\n};\n")
|
||||
end
|
||||
out:write(" (const char *)0\n};\n")
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
@@ -262,8 +321,13 @@ local map_reg_valid_index = {} -- Int. register name -> valid index register?
|
||||
local map_reg_needrex = {} -- Int. register name -> need rex vs. no rex.
|
||||
local reg_list = {} -- Canonical list of int. register names.
|
||||
|
||||
local map_type = {} -- Type name -> { ctype, reg }
|
||||
local ctypenum = 0 -- Type number (for _PTx macros).
|
||||
local map_type -- Type name -> { ctype, reg }
|
||||
local ctypenum -- Type number (for _PTx macros).
|
||||
|
||||
local function init_map_type()
|
||||
map_type = {}
|
||||
ctypenum = 0
|
||||
end
|
||||
|
||||
local addrsize = x64 and "q" or "d" -- Size for address operands.
|
||||
|
||||
@@ -631,7 +695,7 @@ end
|
||||
local function immexpr(expr)
|
||||
-- &expr (pointer)
|
||||
if sub(expr, 1, 1) == "&" then
|
||||
return "iPJ", format("(ptrdiff_t)(%s)", sub(expr,2))
|
||||
return "iPJ", format(luamode and "(%s)" or "(ptrdiff_t)(%s)", sub(expr,2))
|
||||
end
|
||||
|
||||
local prefix = sub(expr, 1, 2)
|
||||
@@ -837,7 +901,11 @@ local function parseoperand(param)
|
||||
-- type[idx], type[idx].field, type->field -> [reg+offset_expr]
|
||||
if not tp then werror("bad operand `"..param.."'") end
|
||||
t.mode = "xm"
|
||||
t.disp = format(tp.ctypefmt, tailr)
|
||||
if luamode then
|
||||
t.disp = tp.ctypefmt(tailr)
|
||||
else
|
||||
t.disp = format(tp.ctypefmt, tailr)
|
||||
end
|
||||
else
|
||||
t.mode, t.imm = immexpr(expr)
|
||||
if sub(t.mode, -1) == "J" then
|
||||
@@ -1201,6 +1269,8 @@ local map_op = {
|
||||
andnps_2 = "rmo:0F55rM",
|
||||
andpd_2 = "rmo:660F54rM",
|
||||
andps_2 = "rmo:0F54rM",
|
||||
fxsave_1 = "x.:0FAE0m",
|
||||
fxrstor_1 = "x.:0FAE1m",
|
||||
clflush_1 = "x.:0FAE7m",
|
||||
cmppd_3 = "rmio:660FC2rMU",
|
||||
cmpps_3 = "rmio:0FC2rMU",
|
||||
@@ -1989,8 +2059,13 @@ if x64 then
|
||||
end
|
||||
wputop(sz, opcode, rex)
|
||||
if vreg then waction("VREG", vreg); wputxb(0) end
|
||||
waction("IMM_D", format("(unsigned int)(%s)", op64))
|
||||
waction("IMM_D", format("(unsigned int)((%s)>>32)", op64))
|
||||
if luamode then
|
||||
waction("IMM_D", format("ffi.cast(\"uintptr_t\", %s) %% 2^32", op64))
|
||||
waction("IMM_D", format("ffi.cast(\"uintptr_t\", %s) / 2^32", op64))
|
||||
else
|
||||
waction("IMM_D", format("(unsigned int)(%s)", op64))
|
||||
waction("IMM_D", format("(unsigned int)((%s)>>32)", op64))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -2136,16 +2211,41 @@ map_op[".type_3"] = function(params, nparams)
|
||||
if reg and not map_reg_valid_base[reg] then
|
||||
werror("bad base register `"..(map_reg_rev[reg] or reg).."'")
|
||||
end
|
||||
-- Add #type to defines. A bit unclean to put it in map_archdef.
|
||||
map_archdef["#"..name] = "sizeof("..ctype..")"
|
||||
-- Add #type to current defines table.
|
||||
g_map_def["#"..name] = luamode and "ffi.sizeof(\""..ctype.."\")" or "sizeof("..ctype..")"
|
||||
-- Add new type and emit shortcut define.
|
||||
local num = ctypenum + 1
|
||||
local ctypefmt
|
||||
if luamode then
|
||||
ctypefmt = function(tailr)
|
||||
local index, field
|
||||
index, field = match(tailr, "^(%b[])(.*)")
|
||||
index = index and sub(index, 2, -2)
|
||||
field = field or tailr
|
||||
field = match(field, "^%->(.*)") or match(field, "^%.(.*)")
|
||||
if not (index or field) then
|
||||
werror("invalid syntax `"..tailr.."`")
|
||||
end
|
||||
local Da = index and format("Da%X(%s)", num, index)
|
||||
local Dt = field and format("Dt%X(\"%s\")", num, field)
|
||||
return Da and Dt and Da.."+"..Dt or Da or Dt
|
||||
end
|
||||
else
|
||||
ctypefmt = format("Dt%X(%%s)", num)
|
||||
end
|
||||
map_type[name] = {
|
||||
ctype = ctype,
|
||||
ctypefmt = format("Dt%X(%%s)", num),
|
||||
ctypefmt = ctypefmt,
|
||||
reg = reg,
|
||||
}
|
||||
wline(format("#define Dt%X(_V) (int)(ptrdiff_t)&(((%s *)0)_V)", num, ctype))
|
||||
if luamode then
|
||||
wline(format("local Dt%X; do local ct=ffi.typeof(\"%s\"); function Dt%X(f) return ffi.offsetof(ct,f) or error(string.format(\"'struct %s' has no member named '%%s'\", f)) end; end",
|
||||
num, ctype, num, ctype))
|
||||
wline(format("local Da%X; do local sz=ffi.sizeof(\"%s\"); function Da%X(i) return i*sz end; end",
|
||||
num, ctype, num))
|
||||
else
|
||||
wline(format("#define Dt%X(_V) (int)(ptrdiff_t)&(((%s *)0)_V)", num, ctype))
|
||||
end
|
||||
ctypenum = num
|
||||
end
|
||||
map_op[".type_2"] = map_op[".type_3"]
|
||||
@@ -2201,12 +2301,19 @@ end
|
||||
-- Setup the arch-specific module.
|
||||
function _M.setup(arch, opt)
|
||||
g_arch, g_opt = arch, opt
|
||||
luamode = g_opt.lang == "lua"
|
||||
init_actionlist()
|
||||
init_map_global()
|
||||
init_map_extern()
|
||||
init_map_type()
|
||||
end
|
||||
|
||||
-- Merge the core maps and the arch-specific maps.
|
||||
function _M.mergemaps(map_coreop, map_def)
|
||||
setmetatable(map_op, { __index = map_coreop })
|
||||
setmetatable(map_def, { __index = map_archdef })
|
||||
-- Hold a ref. to map_def to store `#type` defines in.
|
||||
g_map_def = map_def
|
||||
return map_op, map_def
|
||||
end
|
||||
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
*
|
||||
!/bin/
|
||||
!/bin/mingw32/
|
||||
!/bin/mingw64/
|
||||
!/bin/linux32/
|
||||
!/bin/linux64/
|
||||
!/bin/osx32/
|
||||
!/bin/osx64/
|
||||
!/bin/mingw32/clib/
|
||||
!/bin/mingw64/clib/
|
||||
!/bin/linux32/clib/
|
||||
!/bin/linux64/clib/
|
||||
!/bin/osx32/clib/
|
||||
!/bin/osx64/clib/
|
||||
!/csrc/
|
||||
!/media/
|
||||
|
||||
!/dynasm*
|
||||
!/dasm*
|
||||
!/bin/mingw32/dasm*
|
||||
!/bin/mingw64/dasm*
|
||||
!/bin/linux32/libdasm*
|
||||
!/bin/linux64/libdasm*
|
||||
!/bin/osx32/libdasm*
|
||||
!/bin/osx64/libdasm*
|
||||
!/csrc/dynasm/
|
||||
!/csrc/dynasm/**
|
||||
+285
-57
@@ -10,7 +10,7 @@
|
||||
local _info = {
|
||||
name = "DynASM",
|
||||
description = "A dynamic assembler for code generation engines",
|
||||
version = "1.4.0",
|
||||
version = "1.4.0_luamode",
|
||||
vernum = 10400,
|
||||
release = "2015-10-18",
|
||||
author = "Mike Pall",
|
||||
@@ -45,6 +45,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
-- Cache library functions.
|
||||
local type, pairs, ipairs = type, pairs, ipairs
|
||||
local pcall, error, assert = pcall, error, assert
|
||||
local select, tostring = select, tostring
|
||||
local _s = string
|
||||
local sub, match, gmatch, gsub = _s.sub, _s.match, _s.gmatch, _s.gsub
|
||||
local format, rep, upper = _s.format, _s.rep, _s.upper
|
||||
@@ -54,18 +55,70 @@ local exit = os.exit
|
||||
local io = io
|
||||
local stdin, stdout, stderr = io.stdin, io.stdout, io.stderr
|
||||
|
||||
-- Helper to update a table with the elements another table.
|
||||
local function update(dst, src)
|
||||
if src then
|
||||
for k,v in pairs(src) do
|
||||
dst[k] = v
|
||||
end
|
||||
end
|
||||
return dst
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Program options.
|
||||
local g_opt = {}
|
||||
local g_opt
|
||||
|
||||
-- Global state for current file.
|
||||
local g_fname, g_curline, g_indent, g_lineno, g_synclineno, g_arch
|
||||
local g_errcount = 0
|
||||
local g_errcount
|
||||
|
||||
-- Write buffer for output file.
|
||||
local g_wbuffer, g_capbuffer
|
||||
|
||||
-- Map for defines (initially empty, chains to arch-specific map).
|
||||
local map_def
|
||||
|
||||
-- Sections names.
|
||||
local map_sections
|
||||
|
||||
-- The opcode map. Begins as an empty map set to inherit from map_initop,
|
||||
-- It's later changed to inherit from the arch-specific map, which itself is set
|
||||
-- to inherit from map_coreop.
|
||||
local map_op
|
||||
|
||||
-- The initial opcode map to initialize map_op with on each global reset.
|
||||
local map_initop = {}
|
||||
|
||||
-- Dummy action flush function. Replaced with arch-specific function later.
|
||||
local function dummy_wflush(term) end
|
||||
local wflush
|
||||
|
||||
-- Init/reset the global state for processing a new file.
|
||||
local function reset()
|
||||
g_opt = {}
|
||||
g_opt.dumpdef = 0
|
||||
g_opt.include = { "" }
|
||||
g_opt.lang = nil
|
||||
g_opt.comment = "//|"
|
||||
g_opt.endcomment = ""
|
||||
g_opt.cpp = true -- Write `#line` directives
|
||||
g_fname = nil
|
||||
g_curline = nil
|
||||
g_indent = ""
|
||||
g_lineno = 0
|
||||
g_synclineno = -1
|
||||
g_errcount = 0
|
||||
g_arch = nil
|
||||
g_wbuffer = {}
|
||||
g_capbuffer = nil
|
||||
map_def = {}
|
||||
map_sections = {}
|
||||
map_op = setmetatable({}, { __index = map_initop })
|
||||
wflush = dummy_wflush
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Write an output line (or callback function) to the buffer.
|
||||
@@ -85,15 +138,13 @@ end
|
||||
-- Resync CPP line numbers.
|
||||
local function wsync()
|
||||
if g_synclineno ~= g_lineno and g_opt.cpp then
|
||||
wline("#line "..g_lineno..' "'..g_fname..'"')
|
||||
if not g_opt.lang == "lua" then
|
||||
wline("#line "..g_lineno..' "'..g_fname..'"')
|
||||
end
|
||||
g_synclineno = g_lineno
|
||||
end
|
||||
end
|
||||
|
||||
-- Dummy action flush function. Replaced with arch-specific function later.
|
||||
local function wflush(term)
|
||||
end
|
||||
|
||||
-- Dump all buffered output lines.
|
||||
local function wdumplines(out, buf)
|
||||
for _,line in ipairs(buf) do
|
||||
@@ -171,8 +222,6 @@ end
|
||||
|
||||
-- Core pseudo-opcodes.
|
||||
local map_coreop = {}
|
||||
-- Dummy opcode map. Replaced by arch-specific map.
|
||||
local map_op = {}
|
||||
|
||||
-- Forward declarations.
|
||||
local dostmt
|
||||
@@ -180,9 +229,6 @@ local readfile
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Map for defines (initially empty, chains to arch-specific map).
|
||||
local map_def = {}
|
||||
|
||||
-- Pseudo-opcode to define a substitution.
|
||||
map_coreop[".define_2"] = function(params, nparams)
|
||||
if not params then return nparams == 1 and "name" or "name, subst" end
|
||||
@@ -379,11 +425,11 @@ map_coreop[".include_1"] = function(params)
|
||||
end
|
||||
|
||||
-- Make .include and conditionals initially available, too.
|
||||
map_op[".include_1"] = map_coreop[".include_1"]
|
||||
map_op[".if_1"] = map_coreop[".if_1"]
|
||||
map_op[".elif_1"] = map_coreop[".elif_1"]
|
||||
map_op[".else_0"] = map_coreop[".else_0"]
|
||||
map_op[".endif_0"] = map_coreop[".endif_0"]
|
||||
map_initop[".include_1"] = map_coreop[".include_1"]
|
||||
map_initop[".if_1"] = map_coreop[".if_1"]
|
||||
map_initop[".elif_1"] = map_coreop[".elif_1"]
|
||||
map_initop[".else_0"] = map_coreop[".else_0"]
|
||||
map_initop[".endif_0"] = map_coreop[".endif_0"]
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
@@ -583,9 +629,6 @@ end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Sections names.
|
||||
local map_sections = {}
|
||||
|
||||
-- Pseudo-opcode to define code sections.
|
||||
-- TODO: Data sections, BSS sections. Needs extra C code and API.
|
||||
map_coreop[".section_*"] = function(params)
|
||||
@@ -599,10 +642,18 @@ map_coreop[".section_*"] = function(params)
|
||||
werror("bad section name `"..name.."'")
|
||||
end
|
||||
map_sections[#map_sections+1] = name
|
||||
wline(format("#define DASM_SECTION_%s\t%d", upper(name), sn-1))
|
||||
if g_opt.lang == "lua" then
|
||||
wline(format("local DASM_SECTION_%s\t= %d", upper(name), sn-1))
|
||||
else
|
||||
wline(format("#define DASM_SECTION_%s\t%d", upper(name), sn-1))
|
||||
end
|
||||
map_op[opname] = function(params) g_arch.section(sn-1) end
|
||||
end
|
||||
wline(format("#define DASM_MAXSECTION\t\t%d", #map_sections))
|
||||
if g_opt.lang == "lua" then
|
||||
wline(format("local DASM_MAXSECTION\t= %d", #map_sections))
|
||||
else
|
||||
wline(format("#define DASM_MAXSECTION\t\t%d", #map_sections))
|
||||
end
|
||||
end
|
||||
|
||||
-- Dump all sections.
|
||||
@@ -635,7 +686,9 @@ local function loadarch(arch)
|
||||
g_arch = m_arch
|
||||
wflush = m_arch.passcb(wline, werror, wfatal, wwarn)
|
||||
m_arch.setup(arch, g_opt)
|
||||
map_op, map_def = m_arch.mergemaps(map_coreop, map_def)
|
||||
local arch_map_op
|
||||
arch_map_op, map_def = m_arch.mergemaps(map_coreop, map_def)
|
||||
map_op = setmetatable(map_op, { __index = arch_map_op })
|
||||
end
|
||||
|
||||
-- Dump architecture description.
|
||||
@@ -691,19 +744,27 @@ end
|
||||
|
||||
-- Pseudo-opcode to set the architecture.
|
||||
-- Only initially available (map_op is replaced when called).
|
||||
map_op[".arch_1"] = function(params)
|
||||
map_initop[".arch_1"] = function(params)
|
||||
if not params then return "name" end
|
||||
local err = loadarch(params[1])
|
||||
if err then wfatal(err) end
|
||||
wline(format("#if DASM_VERSION != %d", _info.vernum))
|
||||
wline('#error "Version mismatch between DynASM and included encoding engine"')
|
||||
wline("#endif")
|
||||
if g_opt.lang == "lua" then
|
||||
wline(format("if dasm._VERSION ~= %d then", _info.vernum))
|
||||
wline(' error("Version mismatch between DynASM and included encoding engine")')
|
||||
wline("end")
|
||||
else
|
||||
wline(format("#if DASM_VERSION != %d", _info.vernum))
|
||||
wline('#error "Version mismatch between DynASM and included encoding engine"')
|
||||
wline("#endif")
|
||||
end
|
||||
end
|
||||
|
||||
-- Dummy .arch pseudo-opcode to improve the error report.
|
||||
map_coreop[".arch_1"] = function(params)
|
||||
if not params then return "name" end
|
||||
wfatal("duplicate .arch statement")
|
||||
if g_arch._info.arch ~= params[1] then
|
||||
wfatal("invalid .arch statement. arch already loaded: `", g_arch._info.arch, "`.")
|
||||
end
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
@@ -859,6 +920,9 @@ local function doline(line)
|
||||
|
||||
-- Strip assembler comments.
|
||||
aline = gsub(aline, "//.*$", "")
|
||||
if g_opt.lang == "lua" then
|
||||
aline = gsub(aline, "%-%-.*$", "")
|
||||
end
|
||||
|
||||
-- Split line into statements at semicolons.
|
||||
if match(aline, ";") then
|
||||
@@ -872,7 +936,21 @@ end
|
||||
|
||||
-- Write DynASM header.
|
||||
local function dasmhead(out)
|
||||
out:write(format([[
|
||||
if not g_opt.comment then return end
|
||||
if g_opt.lang == "lua" then
|
||||
out:write(format([[
|
||||
--
|
||||
-- This file has been pre-processed with DynASM.
|
||||
-- %s
|
||||
-- DynASM version %s, DynASM %s version %s
|
||||
-- DO NOT EDIT! The original file is in "%s".
|
||||
--
|
||||
|
||||
]], _info.url,
|
||||
_info.version, g_arch._info.arch, g_arch._info.version,
|
||||
g_fname))
|
||||
else
|
||||
out:write(format([[
|
||||
/*
|
||||
** This file has been pre-processed with DynASM.
|
||||
** %s
|
||||
@@ -883,14 +961,11 @@ local function dasmhead(out)
|
||||
]], _info.url,
|
||||
_info.version, g_arch._info.arch, g_arch._info.version,
|
||||
g_fname))
|
||||
end
|
||||
end
|
||||
|
||||
-- Read input file.
|
||||
readfile = function(fin)
|
||||
g_indent = ""
|
||||
g_lineno = 0
|
||||
g_synclineno = -1
|
||||
|
||||
-- Process all lines.
|
||||
for line in fin:lines() do
|
||||
g_lineno = g_lineno + 1
|
||||
@@ -911,8 +986,10 @@ local function writefile(outfile)
|
||||
-- Open output file.
|
||||
if outfile == nil or outfile == "-" then
|
||||
fout = stdout
|
||||
else
|
||||
elseif type(outfile) == "string" then
|
||||
fout = assert(io.open(outfile, "w"))
|
||||
else
|
||||
fout = outfile
|
||||
end
|
||||
|
||||
-- Write all buffered lines
|
||||
@@ -927,10 +1004,6 @@ end
|
||||
|
||||
-- Translate an input file to an output file.
|
||||
local function translate(infile, outfile)
|
||||
g_wbuffer = {}
|
||||
g_indent = ""
|
||||
g_lineno = 0
|
||||
g_synclineno = -1
|
||||
|
||||
-- Put header.
|
||||
wline(dasmhead)
|
||||
@@ -940,9 +1013,12 @@ local function translate(infile, outfile)
|
||||
if infile == "-" then
|
||||
g_fname = "(stdin)"
|
||||
fin = stdin
|
||||
else
|
||||
elseif type(infile) == "string" then
|
||||
g_fname = infile
|
||||
fin = assert(io.open(infile, "r"))
|
||||
else
|
||||
g_fname = "=(translate)"
|
||||
fin = infile
|
||||
end
|
||||
readfile(fin)
|
||||
|
||||
@@ -974,7 +1050,7 @@ function opt_map.help()
|
||||
stdout:write("DynASM ", _info.version, " ", _info.release, " ", _info.url, "\n")
|
||||
stdout:write[[
|
||||
|
||||
Usage: dynasm [OPTION]... INFILE.dasc|-
|
||||
Usage: dynasm [OPTION]... INFILE.dasc|INFILE.dasl|-
|
||||
|
||||
-h, --help Display this help text.
|
||||
-V, --version Display version and copyright information.
|
||||
@@ -982,6 +1058,8 @@ Usage: dynasm [OPTION]... INFILE.dasc|-
|
||||
-o, --outfile FILE Output file name (default is stdout).
|
||||
-I, --include DIR Add directory to the include search path.
|
||||
|
||||
-l, --lang C|Lua Generate C or Lua code (default C for dasc, Lua for dasl).
|
||||
|
||||
-c, --ccomment Use /* */ comments for assembler lines.
|
||||
-C, --cppcomment Use // comments for assembler lines (default).
|
||||
-N, --nocomment Suppress assembler lines in output.
|
||||
@@ -1007,6 +1085,7 @@ function opt_map.version()
|
||||
end
|
||||
|
||||
-- Misc. options.
|
||||
function opt_map.lang(args) g_opt.lang = optparam(args):lower() end
|
||||
function opt_map.outfile(args) g_opt.outfile = optparam(args) end
|
||||
function opt_map.include(args) insert(g_opt.include, 1, optparam(args)) end
|
||||
function opt_map.ccomment() g_opt.comment = "/*|"; g_opt.endcomment = " */" end
|
||||
@@ -1023,6 +1102,7 @@ function opt_map.dumpdef() g_opt.dumpdef = g_opt.dumpdef + 1 end
|
||||
local opt_alias = {
|
||||
h = "help", ["?"] = "help", V = "version",
|
||||
o = "outfile", I = "include",
|
||||
l = "lang",
|
||||
c = "ccomment", C = "cppcomment", N = "nocomment", M = "maccomment",
|
||||
L = "nolineno", F = "flushline",
|
||||
P = "dumpdef", A = "dumparch",
|
||||
@@ -1038,14 +1118,44 @@ local function parseopt(opt, args)
|
||||
f(args)
|
||||
end
|
||||
|
||||
local languages = {c = true, lua = true}
|
||||
local langext = {dasc = "c", dasl = "lua"}
|
||||
|
||||
--Set language options (Lua or C code gen) based on file extension.
|
||||
local function setlang(infile)
|
||||
|
||||
-- Infer language from file extension, if `lang` not set.
|
||||
if not g_opt.lang and type(infile) == "string" then
|
||||
g_opt.lang = langext[match(infile, "%.([^%.]+)$")] or "c"
|
||||
end
|
||||
|
||||
-- Check that the `lang` option is valid.
|
||||
if not languages[g_opt.lang] then
|
||||
opterror("invalid language `", tostring(g_opt.lang), "`.")
|
||||
end
|
||||
|
||||
-- Adjust comment options for Lua mode.
|
||||
if g_opt.lang == "lua" then
|
||||
if g_opt.comment then
|
||||
g_opt.cpp = false
|
||||
g_opt.comment = "--|"
|
||||
g_opt.endcomment = ""
|
||||
end
|
||||
end
|
||||
|
||||
-- Set initial defines only available in Lua mode.
|
||||
local ffi = require'ffi'
|
||||
map_def.ARCH = ffi.arch --for `.arch ARCH`
|
||||
map_def[upper(ffi.arch)] = 1 --for `.if X86 ...`
|
||||
map_def.OS = ffi.os --for `.if OS == 'Windows'`
|
||||
map_def[upper(ffi.os)] = 1 --for `.if WINDOWS ...`
|
||||
end
|
||||
|
||||
-- Parse arguments.
|
||||
local function parseargs(args)
|
||||
-- Default options.
|
||||
g_opt.comment = "//|"
|
||||
g_opt.endcomment = ""
|
||||
g_opt.cpp = true
|
||||
g_opt.dumpdef = 0
|
||||
g_opt.include = { "" }
|
||||
|
||||
--Reset globals.
|
||||
reset()
|
||||
|
||||
-- Process all option arguments.
|
||||
args.argn = 1
|
||||
@@ -1073,22 +1183,140 @@ local function parseargs(args)
|
||||
opt_map.help()
|
||||
end
|
||||
|
||||
local infile = args[args.argn]
|
||||
|
||||
-- Set language options.
|
||||
setlang(infile)
|
||||
|
||||
-- Translate a single input file to a single output file
|
||||
-- TODO: Handle multiple files?
|
||||
translate(args[args.argn], g_opt.outfile)
|
||||
translate(infile, g_opt.outfile)
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Add the directory dynasm.lua resides in to the Lua module search path.
|
||||
local arg = arg
|
||||
if arg and arg[0] then
|
||||
prefix = match(arg[0], "^(.*[/\\])")
|
||||
if package and prefix then package.path = prefix.."?.lua;"..package.path end
|
||||
if ... == "dynasm" then -- use as module
|
||||
|
||||
-- Make a reusable translate() function with support for setting options.
|
||||
local translate = function(infile, outfile, opt)
|
||||
reset()
|
||||
update(g_opt, opt)
|
||||
setlang(infile)
|
||||
if g_opt.subst then
|
||||
for name, subst in pairs(g_opt.subst) do
|
||||
map_def[name] = tostring(subst)
|
||||
end
|
||||
end
|
||||
translate(infile, outfile)
|
||||
end
|
||||
|
||||
-- Dummy file:close() method.
|
||||
local function dummyclose()
|
||||
return true
|
||||
end
|
||||
|
||||
-- Create a pseudo-file object that implements the file:lines() method
|
||||
-- which reads data from a string.
|
||||
local function string_infile(s)
|
||||
local lines = function()
|
||||
local term =
|
||||
match(s, "\r\n") and "\r\n" or
|
||||
match(s, "\r") and "\r" or
|
||||
match(s, "\n") and "\n" or ""
|
||||
return gmatch(s, "([^\n\r]*)"..term)
|
||||
end
|
||||
return {lines = lines, close = dummyclose}
|
||||
end
|
||||
|
||||
-- Create a pseudo-file object that implements the file:write() method
|
||||
-- which forwards each non-empty value to a function.
|
||||
local function func_outfile(func)
|
||||
local function write(_, ...)
|
||||
for i = 1, select('#', ...) do
|
||||
local v = select(i, ...)
|
||||
assert(type(v) == "string" or type(v) == "number", "invalid value")
|
||||
local s = tostring(v)
|
||||
if #s > 0 then
|
||||
func(s)
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
return {write = write, close = dummyclose}
|
||||
end
|
||||
|
||||
-- Create a pseudo-file object that accumulates writes to a table.
|
||||
local function table_outfile(t)
|
||||
return func_outfile(function(s)
|
||||
t[#t+1] = s
|
||||
end)
|
||||
end
|
||||
|
||||
-- Translate an input file to a string.
|
||||
local function translate_tostring(infile, opt)
|
||||
local t = {}
|
||||
translate(infile, table_outfile(t), opt)
|
||||
return table.concat(t)
|
||||
end
|
||||
|
||||
-- Create an iterator that translates an input file
|
||||
-- and returns the translated file in chunks.
|
||||
local function translate_toiter(infile, opt)
|
||||
return coroutine.wrap(function()
|
||||
translate(infile, func_outfile(coroutine.yield), opt)
|
||||
end)
|
||||
end
|
||||
|
||||
-- Load a dasl file and return it as a Lua chunk.
|
||||
local function dasl_loadfile(infile, opt)
|
||||
local opt = update({lang = "lua"}, opt)
|
||||
local read = translate_toiter(infile, opt)
|
||||
local filename = type(infile) == "string" and infile or "=(load)"
|
||||
return load(read, filename)
|
||||
end
|
||||
|
||||
-- Load a dasl string and return it as a Lua chunk.
|
||||
local function dasl_loadstring(s, opt)
|
||||
return dasl_loadfile(string_infile(s), opt)
|
||||
end
|
||||
|
||||
-- Register a module loader for *.dasl files.
|
||||
insert(package.loaders, function(modname)
|
||||
local daslpath = gsub(gsub(package.path, "%.lua;", ".dasl;"), "%.lua$", ".dasl")
|
||||
local path, reason = package.searchpath(modname, daslpath)
|
||||
if not path then return reason end
|
||||
return function()
|
||||
local chunk = assert(dasl_loadfile(path, {comment = false}))
|
||||
return chunk(modname)
|
||||
end
|
||||
end)
|
||||
|
||||
-- Make and return the DynASM API.
|
||||
return {
|
||||
--low-level intf.
|
||||
translate = translate,
|
||||
string_infile = string_infile,
|
||||
func_outfile = func_outfile,
|
||||
table_outfile = table_outfile,
|
||||
translate_tostring = translate_tostring,
|
||||
translate_toiter = translate_toiter,
|
||||
--hi-level intf.
|
||||
loadfile = dasl_loadfile,
|
||||
loadstring = dasl_loadstring,
|
||||
}
|
||||
|
||||
else -- use as standalone script
|
||||
|
||||
-- Add the directory dynasm.lua resides in to the Lua module search path.
|
||||
local arg = arg
|
||||
if arg and arg[0] then
|
||||
prefix = match(arg[0], "^(.*[/\\])")
|
||||
if package and prefix then package.path = prefix.."?.lua;"..package.path end
|
||||
end
|
||||
|
||||
-- Start DynASM.
|
||||
parseargs{...}
|
||||
|
||||
end
|
||||
|
||||
-- Start DynASM.
|
||||
parseargs{...}
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
|
||||
@@ -0,0 +1,317 @@
|
||||
---
|
||||
tagline: DynASM with Lua mode
|
||||
---
|
||||
|
||||
## `local dynasm = require'dynasm'`
|
||||
|
||||
This is a modified version of [DynASM](http://luajit.org/dynasm.html) that allows generating,
|
||||
compiling, and running x86 and x86-64 assembly code directly from Lua. It also exposes the DynASM assembler
|
||||
and linker to be used as Lua modules.
|
||||
|
||||
Jump To: [Examples](#examples) | [DynASM API](#dynasm-api) | [DASM API](#dasm-api) |
|
||||
[Changes to DynASM](#changes-to-dynasm) |
|
||||
[List of Instructions](http://corsix.github.io/dynasm-doc/instructions.html) |
|
||||
[List of Directives](http://corsix.github.io/dynasm-doc/reference.html#directives)
|
||||
|
||||
## Features
|
||||
|
||||
* translate, compile and run Lua/ASM code from Lua (no C glue)
|
||||
* load Lua/ASM (.dasl) files with `require()`
|
||||
* works with file, string and stream inputs and outputs
|
||||
|
||||
## Before you start
|
||||
|
||||
1. DynASM is [not an inline assembler](http://www.corsix.org/content/what-is-dynasm), it's a code generator.
|
||||
The following code:
|
||||
|
||||
~~~{.lua}
|
||||
function codegen(Dst)
|
||||
for i = 1, 3 do
|
||||
| mov ax, i
|
||||
end
|
||||
end
|
||||
~~~
|
||||
|
||||
does _not_ run the assembly instruction 3 times when codegen is called, instead, it merely adds the
|
||||
instruction sequence `mov ax, 1; mov ax, 2; mov ax, 3` to the dynasm state `Dst` when codegen is called.
|
||||
Mixing Lua and ASM code like this has the effect of generating code, not running it.
|
||||
|
||||
2. DynASM has two parts: the assembler/preprocessor, written in Lua, and the the linker/encoder, written in C.
|
||||
`dynasm.lua` is the preprocessor. It takes mixed C/ASM code as input (from a file, string or file-like object)
|
||||
and generates C code (to a file, string, or file-like object). Alternatively, it can take mixed Lua/ASM
|
||||
code (like the above example) and generate Lua code, which is what the "Lua mode" part is all about.
|
||||
`dasm.lua` is the binding to the C part of DynASM (the linker/encoder) which deals with building the code into
|
||||
executable memory that can be called into.
|
||||
|
||||
3. `.dasl` files refer to Lua/ASM files, `.dasc` files refer to C/ASM files. dasl files can be used transparently
|
||||
as Lua modules (they are translated on-the-fly).
|
||||
|
||||
## Examples
|
||||
|
||||
### 1. Self-contained module
|
||||
|
||||
This simple, self-contained module publishes the function multiply(x, y) -> x * y.
|
||||
|
||||
#### `multiply_x86.dasl:`
|
||||
|
||||
~~~{.lua}
|
||||
local ffi = require'ffi' --required
|
||||
local dasm = require'dasm' --required
|
||||
|
||||
|.arch x86 --must be the first instruction
|
||||
|.actionlist actions --make an action list called `actions`
|
||||
|
||||
local Dst = dasm.new(actions) --make a dasm state
|
||||
|
||||
-- the next chunk of asm code will be added to the action list, and a call
|
||||
-- to `dasm.put(Dst, 0)` will be generated in its place, which will be copying
|
||||
-- the code from the start of the action list into the Dst state.
|
||||
|
||||
| mov eax, [esp+4]
|
||||
| imul dword [esp+8]
|
||||
| ret
|
||||
|
||||
local code = Dst:build() --check, link and encode the code
|
||||
local fptr = ffi.cast('int32_t __cdecl (*) (int32_t x, int32_t y)', code) --take a callable pointer to it
|
||||
|
||||
return function(x, y)
|
||||
local _ = code --pin the code buffer so it doesn't get collected
|
||||
return fptr(x, y)
|
||||
end
|
||||
~~~
|
||||
|
||||
The best way to understand how the above code is supposed to work is to translate it:
|
||||
|
||||
> lua dynasm.lua multiply_x86.dasl
|
||||
|
||||
#### `main.lua`:
|
||||
|
||||
~~~{.lua}
|
||||
require'dynasm' --hook in the `require` loader for .dasl files
|
||||
local multiply = require'multiply_x86' --translate, load and run `multiply_x86.dasl`
|
||||
assert(multiply(-7, 5) == -35)
|
||||
~~~
|
||||
|
||||
### 2. Code gen / build split
|
||||
|
||||
This is an idea on how you can keep your asm code separated from the plumbing required to build it,
|
||||
and also how you can make separate functions out of different asm chunks from the same dasl file.
|
||||
|
||||
#### `funcs_x86.dasl`:
|
||||
|
||||
~~~{.lua}
|
||||
local ffi = require'ffi'
|
||||
local dasm = require'dasm'
|
||||
|
||||
|.arch x86
|
||||
|.actionlist actions
|
||||
|.globalnames globalnames
|
||||
|
||||
local gen = {}
|
||||
|
||||
function gen.mul(Dst) --function which generates code into the dynasm state called `Dst`
|
||||
|->mul: --and returns a "make" function which gets a dasm.globals() map
|
||||
| mov eax, [esp+4] --and returns a function that knows how to call into its code.
|
||||
| imul dword [esp+8]
|
||||
| ret
|
||||
return function(globals)
|
||||
return ffi.cast('int32_t __cdecl (*) (int32_t x, int32_t y)', globals.mul)
|
||||
end
|
||||
end
|
||||
|
||||
function gen.add(Dst)
|
||||
|->add:
|
||||
| mov eax, [esp+4]
|
||||
| add eax, dword [esp+8]
|
||||
| ret
|
||||
return function(globals)
|
||||
return ffi.cast('int32_t __cdecl (*) (int32_t x, int32_t y)', globals.add)
|
||||
end
|
||||
end
|
||||
|
||||
return {gen = gen, actions = actions, globalnames = globalnames}
|
||||
~~~
|
||||
|
||||
#### `funcs.lua`:
|
||||
|
||||
~~~{.lua}
|
||||
local dynasm = require'dynasm'
|
||||
local dasm = require'dasm'
|
||||
local funcs = require'funcs_x86'
|
||||
|
||||
local state, globals = dasm.new(funcs.actions) --create a dynasm state with the generated action list
|
||||
|
||||
local M = {} --generate the code, collecting the make functions
|
||||
for name, gen in pairs(funcs.gen) do
|
||||
M[name] = gen(state)
|
||||
end
|
||||
|
||||
local buf, size = state:build() --check, link and encode the code
|
||||
local globals = dasm.globals(globals, funcs.globalnames) --get the map of global_name -> global_addr
|
||||
|
||||
for name, make in pairs(M) do --make the callable functions
|
||||
M[name] = make(globals)
|
||||
end
|
||||
|
||||
M.__buf = buf --pin buf so it doesn't get collected
|
||||
|
||||
return M
|
||||
~~~
|
||||
|
||||
#### `main.lua`
|
||||
|
||||
~~~{.lua}
|
||||
local funcs = require'funcs'
|
||||
|
||||
assert(funcs.mul(-7, 5) == -35)
|
||||
assert(funcs.add(-7, 5) == -2)
|
||||
~~~
|
||||
|
||||
### 3. Load code from a string
|
||||
|
||||
~~~{.lua}
|
||||
local dynasm = require'dynasm'
|
||||
|
||||
local gencode, actions = dynasm.loadstring([[
|
||||
local ffi = require'ffi'
|
||||
local dasm = require'dasm'
|
||||
|
||||
|.arch x86
|
||||
|.actionlist actions
|
||||
|
||||
local function gencode(Dst)
|
||||
| mov ax, bx
|
||||
end
|
||||
|
||||
return gencode, actions
|
||||
]])()
|
||||
~~~
|
||||
|
||||
### 4. Translate from Lua
|
||||
|
||||
~~~{.lua}
|
||||
local dynasm = require'dynasm'
|
||||
print(dynasm.translate_tostring'multiply_x86.dasl')
|
||||
~~~
|
||||
|
||||
The above is equivalent to the command line:
|
||||
|
||||
> lua dynasm.lua multiply_x86.dasl
|
||||
|
||||
> __Tip__: You can pre-assemble `foo.dasl` into `foo.lua` -- `require()` will then choose `foo.lua`
|
||||
over `foo.dasl`, so you basically get transparent caching for free. This speeds up app loading a bit,
|
||||
and you can ship your app without the assembler (you still need to ship the linker/encoder for
|
||||
all the platforms that you support).
|
||||
|
||||
|
||||
### 5. Included demo/tutorial
|
||||
|
||||
Check out the included [dynasm_demo_x86.dasl] and [dynasm_demo.lua] modules for more in-depth knowledge
|
||||
about DynASM/Lua interaction. It works on Windows, Linux and OSX, both x86 and x64.
|
||||
|
||||
[dynasm_demo.lua]: https://github.com/luapower/dynasm/blob/master/dynasm_demo.lua
|
||||
[dynasm_demo_x86.dasl]: https://github.com/luapower/dynasm/blob/master/dynasm_demo_x86.dasl
|
||||
|
||||
|
||||
### 6. Brainfuck JIT compiler
|
||||
|
||||
The examples above don't do DynASM enough justice, because DynASM was after all made for building JIT compilers.
|
||||
The [bf project](https://github.com/luapower/bf) contains a Lua/ASM translation of the code from Josh Haberman's
|
||||
[tutorial](http://blog.reverberate.org/2012/12/hello-jit-world-joy-of-simple-jits.html) on DynASM and JITs,
|
||||
and probably the simplest JIT compiler you could write. It too works on Windows, Linux and OSX, x86 and x64.
|
||||
|
||||
## DynASM API
|
||||
|
||||
<div class=small>
|
||||
-------------------------------------------------------- --------------------------------------------------
|
||||
__hi-level__
|
||||
`dynasm.loadfile(infile[, opt]) -> chunk` load a dasl file and return it as a Lua chunk
|
||||
`dynasm.loadstring(s[, opt]) -> chunk` load a dasl string and return it as a Lua chunk
|
||||
__low-level__
|
||||
`dynasm.translate(infile, outfile[, opt])` translate a dasc or dasl file
|
||||
`dynasm.string_infile(s) -> infile` use a string as an infile to translate()
|
||||
`dynasm.func_outfile(func) -> outfile` make an outfile that calls func(s) for each piece
|
||||
`dynasm.table_outfile(t) -> outfile` make an outfile that writes pieces to a table
|
||||
`dynasm.translate_tostring(infile[, opt]) -> s` translate to a string
|
||||
`dynasm.translate_toiter(infile[, opt]) -> iter() -> s` translate to an iterator of string pieces
|
||||
-------------------------------------------------------- --------------------------------------------------
|
||||
</div>
|
||||
|
||||
## DASM API
|
||||
|
||||
<div class=small>
|
||||
----------------------------------------------------- --------------------------------------------------
|
||||
__hi-level__
|
||||
|
||||
`dasm.new(` \ make a dasm state for an action list. \
|
||||
`actionlist,` \ -> per `.actionlist` directive. \
|
||||
`[externnames],` \ -> per `.externnames` directive. \
|
||||
`[sectioncount],` \ -> DASM_MAXSECTION from `.sections` directive. \
|
||||
`[globalcount],` \ -> DASM_MAXGLOBAL from `.globals` directive. \
|
||||
`[externget],` \ -> `func(externname) -> addr`, for solving `extern`s \
|
||||
`[globals]) -> state, globals` -> `void*[DASM_MAXGLOBAL]`, to hold globals
|
||||
|
||||
`state:build() -> buf, size` check, link, alloc, encode and mprotect the code
|
||||
|
||||
`dasm.dump(buf, size)` dump the code using the included disassembler in luajit
|
||||
|
||||
`dasm.globals(globals, globalnames)->{name -> addr}` given the globals array returned by dasm.new() and
|
||||
the globalnames list per `.globalnames` directive,
|
||||
return a table that maps the names to their address.
|
||||
|
||||
__low-level__
|
||||
|
||||
`state:init(maxsection)` init a state
|
||||
|
||||
`state:free()` free the state
|
||||
|
||||
`state:setupglobal(globals, globalcount)` set up the globals buffer
|
||||
|
||||
`state:growpc(maxpc)` grow the number of available pc labels
|
||||
|
||||
`state:setup(actionlist)` set up the state with an action list
|
||||
|
||||
`state:put(state, ...)` the assembler generates these calls
|
||||
|
||||
`state:link() -> size` link the code and get its size
|
||||
|
||||
`state:encode(buf)` encode the code into a buffer
|
||||
|
||||
`state:getpclabel(pclabel[, buf])` get pc label offset, or pointer if buf is passed
|
||||
|
||||
`state:checkstep(secmatch)` check code before encoding
|
||||
|
||||
`state:setupextern(externnames, getter)` set up a new `extern` handler
|
||||
----------------------------------------------------- --------------------------------------------------
|
||||
</div>
|
||||
|
||||
## Changes to DynASM
|
||||
|
||||
The [source code changes] made to DynASM were kept to a minimum in order
|
||||
to preserve DynASM semantics, make it easy to merge back changes
|
||||
from upstream, and to make it easy to add the Lua mode to other
|
||||
architectures supported by DynASM in the future.
|
||||
As for the user-facing changes, the list is again small:
|
||||
|
||||
* added `-l, --lang C|Lua` command line option (set automatically for dasl and dasc files).
|
||||
* asm comments can start with both `--` and `//` in Lua mode.
|
||||
* the defines ARCH, OS, X86, X64, WINDOWS, LINUX, OSX are available by default in Lua mode.
|
||||
* the `.globals` directive generates DASM_MAXGLOBAL in Lua mode.
|
||||
* `.type` usage is limited in Lua mode: `FOO.field`, `FOO[expr]` and `FOO[expr].field`
|
||||
are ok, but arbitrary expressions like `FOO[5].bar[2].baz` are not.
|
||||
* `extern foo` resolves to `ffi.C.foo` by default; if foo has no cdef,
|
||||
`ffi.cdef'void foo()'` is called (i.e. a dummy cdef is made for it - caveat emptor).
|
||||
|
||||
[source code changes]: https://github.com/luapower/dynasm/compare/93805cb...master
|
||||
|
||||
## Assembler tutorials & ref docs
|
||||
|
||||
* [x64 tutorial](https://software.intel.com/en-us/articles/introduction-to-x64-assembly/)
|
||||
* [SSE tutorial](http://neilkemp.us/src/sse_tutorial/sse_tutorial.html)
|
||||
* [SSE quick ref](http://softpixel.com/~cwright/programming/simd/sse.php)
|
||||
* [FPU tutorial & ref](http://www.website.masmforum.com/tutorials/fptute/index.html)
|
||||
* [Agner Fog - Calling Conventions](http://www.agner.org/optimize/calling_conventions.pdf)
|
||||
* [Agner Fog - CPU Internals](http://www.agner.org/optimize/microarchitecture.pdf)
|
||||
* [Agner Fog - Optimization Guide](http://www.agner.org/optimize/optimizing_assembly.pdf)
|
||||
* [Agner Fog - Instruction Tables](http://www.agner.org/optimize/instruction_tables.pdf)
|
||||
|
||||
+132
@@ -0,0 +1,132 @@
|
||||
io.stdout:setvbuf'no'
|
||||
io.stderr:setvbuf'no'
|
||||
|
||||
local dynasm = require'dynasm'
|
||||
local dasm = require'dasm'
|
||||
|
||||
--set the globals returned from the dasl module
|
||||
|
||||
local demo, demos, actions, externnames, globalnames, DASM_MAXSECTION, DASM_MAXGLOBAL
|
||||
local function set_vars(t)
|
||||
demo, demos, actions, externnames, globalnames, DASM_MAXSECTION, DASM_MAXGLOBAL = unpack(t)
|
||||
end
|
||||
|
||||
--load dasl files via loadfile() and via require().
|
||||
|
||||
--load and run the dasl file from the current directory.
|
||||
local function load_via_loadfile()
|
||||
set_vars(assert(dynasm.loadfile'dynasm_demo_x86.dasl')())
|
||||
end
|
||||
|
||||
--load the same file via require() from package.path.
|
||||
local function load_via_require()
|
||||
set_vars(require'dynasm_demo_x86')
|
||||
end
|
||||
|
||||
--helpers
|
||||
|
||||
local function hr() return ('-'):rep(60) end
|
||||
local function printf(...) print(string.format(...)) end
|
||||
|
||||
--assemble a demo from the dasl file, dump it and run it
|
||||
local function run_demo(name)
|
||||
collectgarbage() --clean up from the last session
|
||||
|
||||
local gencode = demo[name]
|
||||
|
||||
--make a new dasm state
|
||||
local state, globals = dasm.new(actions, externnames, DASM_MAXSECTION, DASM_MAXGLOBAL)
|
||||
|
||||
--generate the code and get the test function for that code
|
||||
local runcode = gencode(state)
|
||||
|
||||
--build the code
|
||||
local buf, size = state:build()
|
||||
|
||||
--show code and size
|
||||
printf('%-16s %-10s %s', 'code address', '', tostring(buf))
|
||||
printf('%-16s %-10d %s', 'code size', size, 'bytes')
|
||||
|
||||
--show the labels from this code
|
||||
for i = 0, #globalnames do --from .globalnames directive
|
||||
if globals[i] ~= nil then
|
||||
printf('%-16s %-10s %s', 'global', globalnames[i], globals[i])
|
||||
end
|
||||
end
|
||||
|
||||
--dump the code
|
||||
print(hr())
|
||||
dasm.dump(buf, size)
|
||||
|
||||
--run the code
|
||||
if runcode then
|
||||
runcode(buf)
|
||||
end
|
||||
end
|
||||
|
||||
--run all demos
|
||||
local function run_all_demos()
|
||||
for i,name in ipairs(demos) do
|
||||
print()
|
||||
print(name)
|
||||
print(hr())
|
||||
run_demo(name)
|
||||
end
|
||||
end
|
||||
|
||||
local function test_loadstring()
|
||||
local chunk = dynasm.loadstring[[
|
||||
|
||||
local ffi = require'ffi'
|
||||
local dasm = require'dasm'
|
||||
|
||||
|.arch ARCH
|
||||
|.actionlist actions
|
||||
|
||||
local function gen()
|
||||
| mov ax, bx
|
||||
end
|
||||
|
||||
return actions
|
||||
]]
|
||||
local ffi = require'ffi'
|
||||
local actions = chunk()
|
||||
print()
|
||||
print('loadstring test: actionlist for `mov ax, bx`:')
|
||||
print(hr())
|
||||
print(string.byte(ffi.string(actions, ffi.sizeof(actions)), 1, ffi.sizeof(actions)))
|
||||
end
|
||||
|
||||
local function test_translate_tostring()
|
||||
print()
|
||||
print'translate_tostring test:'
|
||||
print(hr())
|
||||
local asm = [[
|
||||
|
||||
local ffi = require'ffi'
|
||||
local dasm = require'dasm'
|
||||
|
||||
|.arch ARCH
|
||||
|
||||
]]
|
||||
print(dynasm.translate_tostring(dynasm.string_infile(asm), {lang = "lua"}))
|
||||
end
|
||||
|
||||
local function run_default()
|
||||
load_via_loadfile()
|
||||
run_all_demos()
|
||||
--we're loading the same file again to test the reusability of dynasm.
|
||||
--there's a lot of global state in dynasm which needs to be reset between runs.
|
||||
load_via_require()
|
||||
run_all_demos()
|
||||
--additional tests...
|
||||
test_loadstring()
|
||||
test_translate_tostring()
|
||||
end
|
||||
|
||||
if not ... then
|
||||
run_default()
|
||||
else
|
||||
load_via_require()
|
||||
run_demo((...))
|
||||
end
|
||||
@@ -0,0 +1,245 @@
|
||||
--go@ luajit dynasm.lua *
|
||||
|
||||
local ffi = require'ffi' -- required
|
||||
local dasm = require'dasm' -- required
|
||||
|
||||
|.arch ARCH
|
||||
|
|
||||
|.if not (X86 or X64)
|
||||
| .error invalid arch ARCH
|
||||
|.endif
|
||||
|
|
||||
|.actionlist actions // gen. `actions` to pass to dasm.new()
|
||||
|.externnames externnames // gen `externnames` to pass to dasm.new() to solve extern names
|
||||
|.section sec1, sec2 // gen. `DASM_SECTION_SEC1`, ..., `DASM_MAXSECTION`
|
||||
|.globals global_ // gen. `global_<labelname>`, ..., `DASM_MAXGLOBAL`
|
||||
|.globalnames globalnames // gen. `globalnames` to get the names of global labels
|
||||
|
||||
local demo = {} --demo table: {name = codegen_func}
|
||||
local demos = {} --demo list in code order: {name1, ...}
|
||||
setmetatable(demo, {__newindex = function(t,k,v) rawset(demo, k, v); demos[#demos+1] = k end})
|
||||
|
||||
--each code generator function generates some code into the Dst argument (which is a dasm state),
|
||||
--and optionally returns a function that knows how to test the resulting code (if nothing is returned,
|
||||
--the code will be just dumped for inspection).
|
||||
|
||||
function demo.literals(Dst)
|
||||
| mov al, 0xff // number literals are translation-time and are range-checked.
|
||||
| mov eax, 0xffffffff // this is correct and will result in 0xffffffff.
|
||||
| mov eax, dword*0x22222222 // size overrides with number literals are also translation-time.
|
||||
| mov eax, [ebx + ecx * 8 + 0x7fffffff] // this mov form allows +/- 2G literal displacements.
|
||||
|.if X86
|
||||
| mov eax, [0xffffffff] // this mov form allows 0..4G literals.
|
||||
|.else
|
||||
| mov eax, [0x7fffffff] // this mov form allows only 0..2G literals and you won't get an error!
|
||||
|.endif
|
||||
end
|
||||
|
||||
function demo.immediate_subst(Dst) --immediate value substitution
|
||||
| mov al, 0+0xff // expressions are encoding-time and coerced to int32.
|
||||
| mov eax, 0+0xffffffff // but expressions are also normalized first, so we can pass a full uint32.
|
||||
| mov eax, [ebx + ecx * 8 + 0x7fffffff] // this mov form allows +/- 2G expression displacements.
|
||||
|.if X86
|
||||
| mov eax, [0+0xffffffff] // this mov form allows 0..4G expressions.
|
||||
|.else
|
||||
| mov eax, [0+0x7fffffff] // this mov form allows only 0..2G expressions and you won't get an error!
|
||||
| mov64 eax, [0+0xdeadbeefdeadbeefULL] // mov64 allows full 64bit expressions (they're never literals).
|
||||
| mov64 rax, 0+0xdeadbeefdeadbeefULL // mov64 allows full 64bit expressions (they're never literals).
|
||||
|.endif
|
||||
end
|
||||
|
||||
function demo.addr(Dst) --address substution for jump targets
|
||||
local addr = ffi.arch == 'x86' and 0xdeadbeef or 0xdeadbeefdeadbeefULL
|
||||
local ptr = ffi.cast("void*", addr)
|
||||
|.if X86
|
||||
| jmp &addr // works with numbers
|
||||
| jmp &ptr // works with pointers too
|
||||
|.else
|
||||
| mov64 rax, addr // no `&` on x64 but you can load an address with `mov64`
|
||||
| mov64 rax, ptr // works with pointers too
|
||||
|.endif
|
||||
end
|
||||
|
||||
function demo.var_reg(Dst) --variable register numbers
|
||||
| mov Rd(2), 0 // Rd(2) = edx. this is always encoding-time.
|
||||
| fld Rf(2) // Rf(2) = st2. this is always encoding-time.
|
||||
end
|
||||
|
||||
function demo.types(Dst) --type macros
|
||||
|
||||
if not rawget(_G, '__TYPEDEFS') then
|
||||
rawset(_G, '__TYPEDEFS', true)
|
||||
ffi.cdef'typedef struct Type1 {int f1; int f2;} Type1'
|
||||
ffi.cdef'typedef Type1 Type2'
|
||||
end
|
||||
|
||||
local n,m = 1,1
|
||||
|
||||
|.type TP1, Type1, eax
|
||||
|.type TP2, Type2, ebx
|
||||
|
|
||||
| mov eax, TP1:ebx // +0
|
||||
| mov ebx, TP1 // +0
|
||||
| mov eax, TP1->f1 // +0
|
||||
| mov ebx, TP1.f2 // +4
|
||||
| mov ecx, TP2[n+m]->f1 // +16
|
||||
| mov edx, TP2[n-2*m].f2 // -4
|
||||
| mov edx, TP2:ecx[n-2*m].f2 // -4
|
||||
end
|
||||
|
||||
function demo.code_sections(Dst)
|
||||
|.sec1; mov ax, 1
|
||||
|.sec2; mov ax, 2
|
||||
|.sec1; mov bx, 1 // append code to .sec1
|
||||
|.sec2; mov bx, 2 // append code to .sec2
|
||||
end
|
||||
|
||||
function demo.global_labels(Dst)
|
||||
|->l1:
|
||||
| jmp ->l2
|
||||
|->l2:
|
||||
| jmp ->l1
|
||||
end
|
||||
|
||||
function demo.local_labels(Dst)
|
||||
|2:
|
||||
|1:
|
||||
| jmp >1
|
||||
|1:
|
||||
| jmp <2
|
||||
end
|
||||
|
||||
function demo.label_subst(Dst)
|
||||
|5:
|
||||
|->label_subst:
|
||||
| mov eax, [<5] // encoded as abs. addr. on x86 and as rip-relative on x64
|
||||
| mov ebx, [->label_subst]
|
||||
end
|
||||
|
||||
function demo.align(Dst)
|
||||
local n = 0x90
|
||||
| mov eax, 0x22222222
|
||||
|.byte n
|
||||
|.align word // puts nop
|
||||
|.word bit.bor(bit.lshift(n,8), n) // expressions work too
|
||||
|.align dword // puts nop
|
||||
|.space 1+1, 0x90 // size can be variable, filler must be constant
|
||||
| mov eax, 0x22222222
|
||||
end
|
||||
|
||||
function demo.cond(Dst) --conditional compilation
|
||||
|.define def1, 1
|
||||
|.define def2, 1
|
||||
|.if def1 + def2 == 2 // any lua expression works. note: a 0 result evaluates to false!
|
||||
| nop
|
||||
|.elif def2
|
||||
| .fatal this won't be parsed
|
||||
|.else
|
||||
| .fatal this won't be parsed
|
||||
|.endif
|
||||
end
|
||||
|
||||
function demo.macros(Dst)
|
||||
|.macro saveregs
|
||||
|.if X86
|
||||
| push ebp; push edi; push esi; push ebx
|
||||
|.else
|
||||
| push rbp; push rdi; push rsi; push rbx
|
||||
|.endif
|
||||
|.endmacro
|
||||
|
|
||||
|.macro restoreregs
|
||||
|.if X86
|
||||
| pop ebx; pop esi; pop edi; pop ebp
|
||||
|.else
|
||||
| pop rbx; pop rsi; pop rdi; pop rbp
|
||||
|.endif
|
||||
|.endmacro
|
||||
|
|
||||
|.macro cat, s1, s2, s3
|
||||
| s1 .. s2 .. s3 // concatenation like `##` in C
|
||||
|.endmacro
|
||||
|
|
||||
| saveregs
|
||||
| restoreregs
|
||||
| mov eax, 0xffffffff
|
||||
| cat n, o, p
|
||||
end
|
||||
|
||||
function demo.captures(Dst)
|
||||
|.capture c1; mov eax, 0x1a; .endcapture
|
||||
|.capture c2; mov ebx, 0x2a; .endcapture
|
||||
|.capture c1; mov eax, 0x1b; .endcapture
|
||||
|.capture c2; mov ebx, 0x2b; .endcapture
|
||||
|.dumpcapture c1
|
||||
|.dumpcapture c2
|
||||
|.dumpcapture c2 // we can dump it again
|
||||
end
|
||||
|
||||
function demo.extern(Dst)
|
||||
|
||||
local s1, s2 = 'Hello from %s!\n', 'DynASM'
|
||||
local p1 = ffi.cast('const char*', s1) -- with `const` we can take addr. of a Lua string without making a copy!
|
||||
local p2 = ffi.cast('const char*', s2)
|
||||
if ffi.arch == 'x64' then
|
||||
ffi.cdef'void printf(...)' --we need this to get printf's address
|
||||
end
|
||||
|
||||
|.if X86 // ptr args in stack in reverse order
|
||||
| sub esp, 4 // align the stack for OSX: 0 - call:4 - p1:4 - p2:4 = -12
|
||||
| push &p2
|
||||
| push &p1
|
||||
| call extern printf
|
||||
| add esp, 8 // pop args
|
||||
| add esp, 4 // dealign
|
||||
| ret
|
||||
|.elif WINDOWS // ptr args in rcx, rdx, ...
|
||||
| sub rsp, 32 // allocate shadow space for printf
|
||||
| mov64 rcx, p1
|
||||
| mov64 rdx, p2
|
||||
| mov64 rax, ffi.C.printf // extern doesn't work on x64
|
||||
| call rax
|
||||
| add rsp, 32 // put it back
|
||||
| ret
|
||||
|.else // ptr args in rdi, rsi, ...
|
||||
| sub rsp, 8 // align the stack for OSX: 0 - call:8 = -8
|
||||
| mov rdi, &p1
|
||||
| mov rsi, &p2
|
||||
| mov64 rdx, ffi.C.printf // extern doesn't work on x64
|
||||
| call rdx
|
||||
| add rsp, 8
|
||||
| ret
|
||||
|.endif
|
||||
|
||||
return function(buf)
|
||||
local _ = s1, s2 --pin the strings
|
||||
local code = ffi.cast('void __cdecl (*) ()', buf)
|
||||
code()
|
||||
end
|
||||
end
|
||||
|
||||
function demo.multiply(Dst)
|
||||
|
||||
|.if X86 // int args in stack in reverse order; int ret in eax
|
||||
| mov eax, [esp+4]
|
||||
| imul dword [esp+8]
|
||||
| ret
|
||||
|.elif WINDOWS // int args in rcx, rdx, ...; int ret in rax
|
||||
| mov rax, rcx
|
||||
| imul rdx
|
||||
| ret
|
||||
|.else // int args in rdi, rsi, ...; int ret in rax
|
||||
| mov rax, rdi
|
||||
| imul rsi
|
||||
| ret
|
||||
|.endif
|
||||
|
||||
return function(buf)
|
||||
local mul = ffi.cast('int32_t __cdecl (*) (int32_t x, int32_t y)', buf)
|
||||
assert(mul(7, -5) == -35)
|
||||
assert(mul(-4, 0x08000000) == -0x20000000)
|
||||
print'ok'
|
||||
end
|
||||
end
|
||||
|
||||
return {demo, demos, actions, externnames, globalnames, DASM_MAXSECTION, DASM_MAXGLOBAL}
|
||||
Reference in New Issue
Block a user