Lua mode, binaries, build scripts, docs

This commit is contained in:
Cosmin Apreutesei
2015-10-29 21:06:15 +02:00
parent 93805cb5ba
commit 1c754e1167
25 changed files with 1580 additions and 5413 deletions
+6
View File
@@ -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).
+1
View File
@@ -0,0 +1 @@
P=linux32 L="-s -static-libgcc" D=libdasm_x86.so A=libdasm_x86.a ./build.sh
+1
View File
@@ -0,0 +1 @@
P=linux64 C=-fPIC L="-s -static-libgcc" D=libdasm_x86.so A=libdasm_x86.a ./build.sh
+1
View File
@@ -0,0 +1 @@
P=mingw32 L="-s -static-libgcc" D=dasm_x86.dll A=dasm_x86.a ./build.sh
+1
View File
@@ -0,0 +1 @@
P=mingw64 L="-s -static-libgcc" D=dasm_x86.dll A=dasm_x86.a ./build.sh
+2
View File
@@ -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
+2
View File
@@ -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
+4
View File
@@ -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
-456
View File
@@ -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
+5
View File
@@ -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)
-416
View File
@@ -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
-419
View File
@@ -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
+3
View File
@@ -0,0 +1,3 @@
#include "dasm_extern.h"
#include "dasm_proto.h"
#include "dasm_x86.h"
+274
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
-953
View File
@@ -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
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+11 -2
View File
@@ -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
View File
@@ -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
-27
View File
@@ -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
View File
@@ -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{...}
------------------------------------------------------------------------------
+317
View File
@@ -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
View File
@@ -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
+245
View File
@@ -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}