sourcemod/sourcepawn/compiler/sc6.cpp
David Anderson c4056aea5d Rewrite the assembly pipeline.
This patch uses SmxBuilder from spcomp2 to replace the old assemble()
pipeline. Instead of generating into an old AMX structure, and then
decoding that into SMX, we now directly generate into SMX. This greatly
simplifies code generation and smx building.
2014-08-23 13:25:58 -07:00

1000 lines
32 KiB
C++

// vim: set sts=2 ts=8 sw=2 tw=99 et:
/* Pawn compiler - Binary code generation (the "assembler")
*
* Copyright (c) ITB CompuPhase, 1997-2006
*
* This software is provided "as-is", without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from
* the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software in
* a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*
* Version: $Id$
*/
#include <assert.h>
#include <stdio.h>
#include <stdlib.h> /* for macro max() */
#include <stddef.h> /* for macro offsetof() */
#include <string.h>
#include <ctype.h>
#if defined FORTIFY
#include <alloc/fortify.h>
#endif
#include "lstring.h"
#include "sc.h"
#include "amxdbg.h"
#if defined LINUX || defined __FreeBSD__ || defined __OpenBSD__
#include <sclinux.h>
#endif
#include <am-utility.h>
#include <smx/smx-v1.h>
#include <zlib/zlib.h>
#include "smx-builder.h"
#include "memory-buffer.h"
using namespace sp;
using namespace ke;
static void append_dbginfo(void *fout);
typedef cell (*OPCODE_PROC)(Vector<cell> *buffer, char *params, cell opcode);
typedef struct {
cell opcode;
const char *name;
int segment; /* sIN_CSEG=parse in cseg, sIN_DSEG=parse in dseg */
OPCODE_PROC func;
} OPCODEC;
static cell codeindex; /* similar to "code_idx" */
static cell *LabelTable; /* label table */
static int writeerror;
static int bytes_in, bytes_out;
static jmp_buf compact_err;
/* apparently, strtol() does not work correctly on very large (unsigned)
* hexadecimal values */
static ucell hex2long(const char *s,char **n)
{
ucell result=0L;
int negate=FALSE;
int digit;
/* ignore leading whitespace */
while (*s==' ' || *s=='\t')
s++;
/* allow a negation sign to create the two's complement of numbers */
if (*s=='-') {
negate=TRUE;
s++;
} /* if */
assert((*s>='0' && *s<='9') || (*s>='a' && *s<='f') || (*s>='a' && *s<='f'));
for ( ;; ) {
if (*s>='0' && *s<='9')
digit=*s-'0';
else if (*s>='a' && *s<='f')
digit=*s-'a' + 10;
else if (*s>='A' && *s<='F')
digit=*s-'A' + 10;
else
break; /* probably whitespace */
result=(result<<4) | digit;
s++;
} /* for */
if (n!=NULL)
*n=(char*)s;
if (negate)
result=(~result)+1; /* take two's complement of the result */
return (ucell)result;
}
static ucell getparam(const char *s,char **n)
{
ucell result=0;
for ( ;; ) {
result+=hex2long(s,(char**)&s);
if (*s!='+')
break;
s++;
} /* for */
if (n!=NULL)
*n=(char*)s;
return result;
}
static char *skipwhitespace(char *str)
{
while (isspace(*str))
str++;
return str;
}
static char *stripcomment(char *str)
{
char *ptr=strchr(str,';');
if (ptr!=NULL) {
*ptr++='\n'; /* terminate the line, but leave the '\n' */
*ptr='\0';
} /* if */
return str;
}
static cell noop(Vector<cell> *buffer, char *params, cell opcode)
{
return 0;
}
static cell set_currentfile(Vector<cell> *buffer, char *params, cell opcode)
{
fcurrent=(short)getparam(params,NULL);
return 0;
}
static cell parm0(Vector<cell> *buffer, char *params, cell opcode)
{
if (buffer)
buffer->append(opcode);
return opcodes(1);
}
static cell parm1(Vector<cell> *buffer, char *params, cell opcode)
{
ucell p = getparam(params, nullptr);
if (buffer) {
buffer->append(opcode);
buffer->append(p);
}
return opcodes(1) + opargs(1);
}
static cell parm2(Vector<cell> *buffer, char *params, cell opcode)
{
ucell p1 = getparam(params, &params);
ucell p2 = getparam(params, nullptr);
if (buffer) {
buffer->append(opcode);
buffer->append(p1);
buffer->append(p2);
}
return opcodes(1) + opargs(2);
}
static cell parm3(Vector<cell> *buffer, char *params, cell opcode)
{
ucell p1 = getparam(params, &params);
ucell p2 = getparam(params, &params);
ucell p3 = getparam(params, nullptr);
if (buffer) {
buffer->append(opcode);
buffer->append(p1);
buffer->append(p2);
buffer->append(p3);
}
return opcodes(1) + opargs(3);
}
static cell parm4(Vector<cell> *buffer, char *params, cell opcode)
{
ucell p1 = getparam(params, &params);
ucell p2 = getparam(params, &params);
ucell p3 = getparam(params, &params);
ucell p4 = getparam(params, nullptr);
if (buffer) {
buffer->append(opcode);
buffer->append(p1);
buffer->append(p2);
buffer->append(p3);
buffer->append(p4);
}
return opcodes(1) + opargs(4);
}
static cell parm5(Vector<cell> *buffer, char *params, cell opcode)
{
ucell p1 = getparam(params, &params);
ucell p2 = getparam(params, &params);
ucell p3 = getparam(params, &params);
ucell p4 = getparam(params, &params);
ucell p5 = getparam(params, nullptr);
if (buffer) {
buffer->append(opcode);
buffer->append(p1);
buffer->append(p2);
buffer->append(p3);
buffer->append(p4);
buffer->append(p5);
}
return opcodes(1) + opargs(5);
}
static cell do_dump(Vector<cell> *buffer, char *params, cell opcode)
{
int num = 0;
while (*params != '\0') {
ucell p = getparam(params, &params);
if (buffer)
buffer->append(p);
num++;
while (isspace(*params))
params++;
}
return num * sizeof(cell);
}
static cell do_call(Vector<cell> *buffer, char *params, cell opcode)
{
char name[sNAMEMAX+1];
int i;
for (i=0; !isspace(*params); i++,params++) {
assert(*params != '\0');
assert(i < sNAMEMAX);
name[i] = *params;
}
name[i]='\0';
cell p;
if (name[0] == 'l' && name[1] == '.') {
// Lookup the label address.
int val = (int)hex2long(name + 2, nullptr);
assert(val >= 0 && val < sc_labnum);
p = LabelTable[val];
} else {
// Look up the function address; note that the correct file number must
// already have been set (in order for static globals to be found).
symbol *sym = findglb(name, sGLOBAL);
assert(sym->ident == iFUNCTN || sym->ident == iREFFUNC);
assert(sym->vclass == sGLOBAL);
p = sym->addr;
}
if (buffer) {
buffer->append(opcode);
buffer->append(p);
}
return opcodes(1) + opargs(1);
}
static cell do_jump(Vector<cell> *buffer, char *params, cell opcode)
{
int i = (int)hex2long(params, nullptr);
assert(i >= 0 && i < sc_labnum);
if (buffer) {
buffer->append(opcode);
buffer->append(LabelTable[i]);
}
return opcodes(1) + opargs(1);
}
static cell do_switch(Vector<cell> *buffer, char *params, cell opcode)
{
int i = (int)hex2long(params, nullptr);
assert(i >= 0 && i < sc_labnum);
if (buffer) {
buffer->append(opcode);
buffer->append(LabelTable[i]);
}
return opcodes(1) + opargs(1);
}
static cell do_case(Vector<cell> *buffer, char *params, cell opcode)
{
cell v = hex2long(params ,&params);
int i = (int)hex2long(params, nullptr);
assert(i >= 0 && i < sc_labnum);
if (buffer) {
buffer->append(v);
buffer->append(LabelTable[i]);
}
return opcodes(0) + opargs(2);
}
static OPCODEC opcodelist[] = {
/* node for "invalid instruction" */
{ 0, NULL, 0, noop },
/* opcodes in sorted order */
{ 78, "add", sIN_CSEG, parm0 },
{ 87, "add.c", sIN_CSEG, parm1 },
{ 14, "addr.alt", sIN_CSEG, parm1 },
{ 13, "addr.pri", sIN_CSEG, parm1 },
{ 30, "align.alt", sIN_CSEG, parm1 },
{ 29, "align.pri", sIN_CSEG, parm1 },
{ 81, "and", sIN_CSEG, parm0 },
{121, "bounds", sIN_CSEG, parm1 },
{137, "break", sIN_CSEG, parm0 }, /* version 8 */
{ 49, "call", sIN_CSEG, do_call },
{ 50, "call.pri", sIN_CSEG, parm0 },
{ 0, "case", sIN_CSEG, do_case },
{130, "casetbl", sIN_CSEG, parm0 }, /* version 1 */
{118, "cmps", sIN_CSEG, parm1 },
{ 0, "code", sIN_CSEG, set_currentfile },
{156, "const", sIN_CSEG, parm2 }, /* version 9 */
{ 12, "const.alt", sIN_CSEG, parm1 },
{ 11, "const.pri", sIN_CSEG, parm1 },
{157, "const.s", sIN_CSEG, parm2 }, /* version 9 */
{ 0, "data", sIN_DSEG, set_currentfile },
{114, "dec", sIN_CSEG, parm1 },
{113, "dec.alt", sIN_CSEG, parm0 },
{116, "dec.i", sIN_CSEG, parm0 },
{112, "dec.pri", sIN_CSEG, parm0 },
{115, "dec.s", sIN_CSEG, parm1 },
{ 0, "dump", sIN_DSEG, do_dump },
{ 95, "eq", sIN_CSEG, parm0 },
{106, "eq.c.alt", sIN_CSEG, parm1 },
{105, "eq.c.pri", sIN_CSEG, parm1 },
/*{124, "file", sIN_CSEG, do_file }, */
{119, "fill", sIN_CSEG, parm1 },
{162, "genarray", sIN_CSEG, parm1 },
{163, "genarray.z", sIN_CSEG, parm1 },
{100, "geq", sIN_CSEG, parm0 },
{ 99, "grtr", sIN_CSEG, parm0 },
{120, "halt", sIN_CSEG, parm1 },
{ 45, "heap", sIN_CSEG, parm1 },
{ 27, "idxaddr", sIN_CSEG, parm0 },
{ 28, "idxaddr.b", sIN_CSEG, parm1 },
{109, "inc", sIN_CSEG, parm1 },
{108, "inc.alt", sIN_CSEG, parm0 },
{111, "inc.i", sIN_CSEG, parm0 },
{107, "inc.pri", sIN_CSEG, parm0 },
{110, "inc.s", sIN_CSEG, parm1 },
{ 86, "invert", sIN_CSEG, parm0 },
{ 55, "jeq", sIN_CSEG, do_jump },
{ 60, "jgeq", sIN_CSEG, do_jump },
{ 59, "jgrtr", sIN_CSEG, do_jump },
{ 58, "jleq", sIN_CSEG, do_jump },
{ 57, "jless", sIN_CSEG, do_jump },
{ 56, "jneq", sIN_CSEG, do_jump },
{ 54, "jnz", sIN_CSEG, do_jump },
{ 52, "jrel", sIN_CSEG, parm1 }, /* always a number */
{ 64, "jsgeq", sIN_CSEG, do_jump },
{ 63, "jsgrtr", sIN_CSEG, do_jump },
{ 62, "jsleq", sIN_CSEG, do_jump },
{ 61, "jsless", sIN_CSEG, do_jump },
{ 51, "jump", sIN_CSEG, do_jump },
{128, "jump.pri", sIN_CSEG, parm0 }, /* version 1 */
{ 53, "jzer", sIN_CSEG, do_jump },
{ 31, "lctrl", sIN_CSEG, parm1 },
{ 98, "leq", sIN_CSEG, parm0 },
{ 97, "less", sIN_CSEG, parm0 },
{ 25, "lidx", sIN_CSEG, parm0 },
{ 26, "lidx.b", sIN_CSEG, parm1 },
/*{125, "line", sIN_CSEG, parm2 }, */
{ 2, "load.alt", sIN_CSEG, parm1 },
{154, "load.both", sIN_CSEG, parm2 }, /* version 9 */
{ 9, "load.i", sIN_CSEG, parm0 },
{ 1, "load.pri", sIN_CSEG, parm1 },
{ 4, "load.s.alt", sIN_CSEG, parm1 },
{155, "load.s.both",sIN_CSEG, parm2 }, /* version 9 */
{ 3, "load.s.pri", sIN_CSEG, parm1 },
{ 10, "lodb.i", sIN_CSEG, parm1 },
{ 6, "lref.alt", sIN_CSEG, parm1 },
{ 5, "lref.pri", sIN_CSEG, parm1 },
{ 8, "lref.s.alt", sIN_CSEG, parm1 },
{ 7, "lref.s.pri", sIN_CSEG, parm1 },
{ 34, "move.alt", sIN_CSEG, parm0 },
{ 33, "move.pri", sIN_CSEG, parm0 },
{117, "movs", sIN_CSEG, parm1 },
{ 85, "neg", sIN_CSEG, parm0 },
{ 96, "neq", sIN_CSEG, parm0 },
{134, "nop", sIN_CSEG, parm0 }, /* version 6 */
{ 84, "not", sIN_CSEG, parm0 },
{ 82, "or", sIN_CSEG, parm0 },
{ 43, "pop.alt", sIN_CSEG, parm0 },
{ 42, "pop.pri", sIN_CSEG, parm0 },
{ 46, "proc", sIN_CSEG, parm0 },
{ 40, "push", sIN_CSEG, parm1 },
{133, "push.adr", sIN_CSEG, parm1 }, /* version 4 */
{ 37, "push.alt", sIN_CSEG, parm0 },
{ 39, "push.c", sIN_CSEG, parm1 },
{ 36, "push.pri", sIN_CSEG, parm0 },
{ 38, "push.r", sIN_CSEG, parm1 }, /* obsolete (never generated) */
{ 41, "push.s", sIN_CSEG, parm1 },
{139, "push2", sIN_CSEG, parm2 }, /* version 9 */
{141, "push2.adr", sIN_CSEG, parm2 }, /* version 9 */
{138, "push2.c", sIN_CSEG, parm2 }, /* version 9 */
{140, "push2.s", sIN_CSEG, parm2 }, /* version 9 */
{143, "push3", sIN_CSEG, parm3 }, /* version 9 */
{145, "push3.adr", sIN_CSEG, parm3 }, /* version 9 */
{142, "push3.c", sIN_CSEG, parm3 }, /* version 9 */
{144, "push3.s", sIN_CSEG, parm3 }, /* version 9 */
{147, "push4", sIN_CSEG, parm4 }, /* version 9 */
{149, "push4.adr", sIN_CSEG, parm4 }, /* version 9 */
{146, "push4.c", sIN_CSEG, parm4 }, /* version 9 */
{148, "push4.s", sIN_CSEG, parm4 }, /* version 9 */
{151, "push5", sIN_CSEG, parm5 }, /* version 9 */
{153, "push5.adr", sIN_CSEG, parm5 }, /* version 9 */
{150, "push5.c", sIN_CSEG, parm5 }, /* version 9 */
{152, "push5.s", sIN_CSEG, parm5 }, /* version 9 */
{ 47, "ret", sIN_CSEG, parm0 },
{ 48, "retn", sIN_CSEG, parm0 },
{ 32, "sctrl", sIN_CSEG, parm1 },
{ 73, "sdiv", sIN_CSEG, parm0 },
{ 74, "sdiv.alt", sIN_CSEG, parm0 },
{104, "sgeq", sIN_CSEG, parm0 },
{103, "sgrtr", sIN_CSEG, parm0 },
{ 65, "shl", sIN_CSEG, parm0 },
{ 69, "shl.c.alt", sIN_CSEG, parm1 },
{ 68, "shl.c.pri", sIN_CSEG, parm1 },
{ 66, "shr", sIN_CSEG, parm0 },
{ 71, "shr.c.alt", sIN_CSEG, parm1 },
{ 70, "shr.c.pri", sIN_CSEG, parm1 },
{ 94, "sign.alt", sIN_CSEG, parm0 },
{ 93, "sign.pri", sIN_CSEG, parm0 },
{102, "sleq", sIN_CSEG, parm0 },
{101, "sless", sIN_CSEG, parm0 },
{ 72, "smul", sIN_CSEG, parm0 },
{ 88, "smul.c", sIN_CSEG, parm1 },
/*{127, "srange", sIN_CSEG, parm2 }, -- version 1 */
{ 20, "sref.alt", sIN_CSEG, parm1 },
{ 19, "sref.pri", sIN_CSEG, parm1 },
{ 22, "sref.s.alt", sIN_CSEG, parm1 },
{ 21, "sref.s.pri", sIN_CSEG, parm1 },
{ 67, "sshr", sIN_CSEG, parm0 },
{ 44, "stack", sIN_CSEG, parm1 },
{165, "stackadjust",sIN_CSEG, parm1 },
{ 0, "stksize", 0, noop },
{ 16, "stor.alt", sIN_CSEG, parm1 },
{ 23, "stor.i", sIN_CSEG, parm0 },
{ 15, "stor.pri", sIN_CSEG, parm1 },
{ 18, "stor.s.alt", sIN_CSEG, parm1 },
{ 17, "stor.s.pri", sIN_CSEG, parm1 },
{164, "stradjust.pri", sIN_CSEG, parm0 },
{ 24, "strb.i", sIN_CSEG, parm1 },
{ 79, "sub", sIN_CSEG, parm0 },
{ 80, "sub.alt", sIN_CSEG, parm0 },
{132, "swap.alt", sIN_CSEG, parm0 }, /* version 4 */
{131, "swap.pri", sIN_CSEG, parm0 }, /* version 4 */
{129, "switch", sIN_CSEG, do_switch }, /* version 1 */
/*{126, "symbol", sIN_CSEG, do_symbol }, */
/*{136, "symtag", sIN_CSEG, parm1 }, -- version 7 */
{123, "sysreq.c", sIN_CSEG, parm1 },
{135, "sysreq.n", sIN_CSEG, parm2 }, /* version 9 (replaces SYSREQ.d from earlier version) */
{122, "sysreq.pri", sIN_CSEG, parm0 },
{161, "tracker.pop.setheap", sIN_CSEG, parm0 },
{160, "tracker.push.c", sIN_CSEG, parm1 },
{ 76, "udiv", sIN_CSEG, parm0 },
{ 77, "udiv.alt", sIN_CSEG, parm0 },
{ 75, "umul", sIN_CSEG, parm0 },
{ 35, "xchg", sIN_CSEG, parm0 },
{ 83, "xor", sIN_CSEG, parm0 },
{ 91, "zero", sIN_CSEG, parm1 },
{ 90, "zero.alt", sIN_CSEG, parm0 },
{ 89, "zero.pri", sIN_CSEG, parm0 },
{ 92, "zero.s", sIN_CSEG, parm1 },
};
#define MAX_INSTR_LEN 30
static int findopcode(char *instr,int maxlen)
{
int low,high,mid,cmp;
char str[MAX_INSTR_LEN];
if (maxlen>=MAX_INSTR_LEN)
return 0;
strlcpy(str,instr,maxlen+1);
/* look up the instruction with a binary search
* the assembler is case insensitive to instructions (but case sensitive
* to symbols)
*/
low=1; /* entry 0 is reserved (for "not found") */
high=(sizeof opcodelist / sizeof opcodelist[0])-1;
while (low<high) {
mid=(low+high)/2;
assert(opcodelist[mid].name!=NULL);
cmp=stricmp(str,opcodelist[mid].name);
if (cmp>0)
low=mid+1;
else
high=mid;
} /* while */
assert(low==high);
if (stricmp(str,opcodelist[low].name)==0)
return low; /* found */
return 0; /* not found, return special index */
}
// This pass is necessary because the code addresses of labels is only known
// after the peephole optimization flag. Labels can occur inside expressions
// (e.g. the conditional operator), which are optimized.
static void relocate_labels(void *fin)
{
if (sc_labnum <= 0)
return;
assert(!LabelTable);
LabelTable = (cell *)calloc(sc_labnum, sizeof(cell));
char line[256];
cell codeindex = 0;
pc_resetasm(fin);
while (pc_readasm(fin, line, sizeof(line))) {
stripcomment(line);
char *instr = skipwhitespace(line);
if (*instr == '\0') // Ignore empty lines.
continue;
if (tolower(*instr) == 'l' && *(instr + 1) == '.') {
int lindex = (int)hex2long(instr + 2, nullptr);
assert(lindex >= 0 && lindex < sc_labnum);
LabelTable[lindex] = codeindex;
} else {
// Get to the end of the instruction (make use of the '\n' that fgets()
// added at the end of the line; this way we *always* drop on a whitespace
// character.
char *params;
for (params = instr; *params != '\0' && !isspace(*params); params++) {
// Nothing.
}
assert(params > instr);
int op_index = findopcode(instr, (int)(params - instr));
OPCODEC &op = opcodelist[op_index];
if (!op.name) {
*params = '\0';
error(104, instr);
}
if (op.segment == sIN_CSEG)
codeindex += op.func(nullptr, skipwhitespace(params), op.opcode);
}
}
}
// Generate code or data into a buffer.
static void generate_segment(Vector<cell> *buffer, void *fin, int pass)
{
pc_resetasm(fin);
char line[255];
while (pc_readasm(fin, line, sizeof(line))) {
stripcomment(line);
char *instr = skipwhitespace(line);
// Ignore empty lines and labels.
if (*instr=='\0' || (tolower(*instr) == 'l' && *(instr + 1)=='.'))
continue;
// Get to the end of the instruction (make use of the '\n' that fgets()
// added at the end of the line; this way we will *always* drop on a
// whitespace character) */
char *params;
for (params=instr; *params != '\0' && !isspace(*params); params++) {
// Do nothing.
}
assert(params > instr);
int op_index = findopcode(instr, (int)(params-instr));
OPCODEC &op = opcodelist[op_index];
assert(op.name != nullptr);
if (op.segment != pass)
continue;
op.func(buffer, skipwhitespace(params), op.opcode);
}
}
#if !defined NDEBUG
// The opcode list should be sorted by name.
class VerifyOpcodeSorting
{
public:
VerifyOpcodeSorting() {
assert(opcodelist[1].name!=NULL);
for (int i = 2; i<(sizeof opcodelist / sizeof opcodelist[0]); i++) {
assert(opcodelist[i].name!=NULL);
assert(stricmp(opcodelist[i].name,opcodelist[i-1].name)>0);
} /* for */
}
} sVerifyOpcodeSorting;
#endif
static int sort_by_addr(const void *a1, const void *a2)
{
symbol *s1 = *(symbol **)a1;
symbol *s2 = *(symbol **)a2;
return s1->addr - s2->addr;
}
typedef SmxListSection<sp_file_natives_t> SmxNativeSection;
typedef SmxListSection<sp_file_publics_t> SmxPublicSection;
typedef SmxListSection<sp_file_pubvars_t> SmxPubvarSection;
typedef SmxListSection<sp_file_tag_t> SmxTagSection;
typedef SmxBlobSection<sp_file_data_t> SmxDataSection;
typedef SmxBlobSection<sp_file_code_t> SmxCodeSection;
static void assemble_to_buffer(MemoryBuffer *buffer, void *fin)
{
StringPool pool;
SmxBuilder builder;
Ref<SmxNativeSection> natives = new SmxNativeSection(".natives");
Ref<SmxPublicSection> publics = new SmxPublicSection(".publics");
Ref<SmxPubvarSection> pubvars = new SmxPubvarSection(".pubvars");
Ref<SmxTagSection> tags = new SmxTagSection(".tags");
Ref<SmxDataSection> data = new SmxDataSection(".data");
Ref<SmxCodeSection> code = new SmxCodeSection(".code");
Ref<SmxNameTable> names = new SmxNameTable(".names");
Vector<symbol *> nativeList;
// Build the easy symbol tables.
for (symbol *sym=glbtab.next; sym; sym=sym->next) {
if (sym->ident==iFUNCTN) {
if ((sym->usage & uNATIVE)!=0 && (sym->usage & uREAD)!=0 && sym->addr >= 0) {
// Natives require special handling, so we save them for later.
nativeList.append(sym);
} else if ((sym->usage & uPUBLIC)!=0 && (sym->usage & uDEFINE)!=0) {
sp_file_publics_t &pubfunc = publics->add();
pubfunc.address = sym->addr;
pubfunc.name = names->add(pool, sym->name);
}
} else if (sym->ident==iVARIABLE || sym->ident == iARRAY || sym->ident == iREFARRAY) {
if ((sym->usage & uPUBLIC)!=0 && (sym->usage & (uREAD | uWRITTEN))!=0) {
sp_file_pubvars_t &pubvar = pubvars->add();
pubvar.address = sym->addr;
pubvar.name = names->add(pool, sym->name);
}
}
}
// Build the tags table.
for (constvalue *constptr = tagname_tab.next; constptr; constptr = constptr->next) {
assert(strlen(constptr->name)>0);
sp_file_tag_t &tag = tags->add();
tag.tag_id = constptr->value;
tag.name = names->add(pool, constptr->name);
}
// Shuffle natives to be in address order.
qsort(nativeList.buffer(), nativeList.length(), sizeof(symbol *), sort_by_addr);
for (size_t i = 0; i < nativeList.length(); i++) {
symbol *sym = nativeList[i];
assert(size_t(sym->addr) == i);
sp_file_natives_t &entry = natives->add();
char testalias[sNAMEMAX + 1];
if (lookup_alias(testalias, sym->name))
entry.name = names->add(pool, "@");
else
entry.name = names->add(pool, sym->name);
}
// Relocate all labels in the assembly buffer.
relocate_labels(fin);
// Generate buffers.
Vector<cell> code_buffer, data_buffer;
generate_segment(&code_buffer, fin, sIN_CSEG);
generate_segment(&data_buffer, fin, sIN_DSEG);
// Set up the code section.
code->header().codesize = code_buffer.length() * sizeof(cell);
code->header().cellsize = sizeof(cell);
code->header().codeversion = SmxConsts::CODE_VERSION_JIT2;
code->header().flags = 0; // :TODO: CODEFLAG_DEBUG;
code->header().main = 0;
code->header().code = sizeof(sp_file_code_t);
code->setBlob((uint8_t *)code_buffer.buffer(), code_buffer.length() * sizeof(cell));
// Set up the data section. Note pre-SourceMod 1.7, the |memsize| was
// computed as AMX::stp, which included the entire memory size needed to
// store the file. Here (in 1.7+), we allocate what is actually needed
// by the plugin.
data->header().datasize = data_buffer.length() * sizeof(cell);
data->header().memsize =
data->header().datasize +
glb_declared * sizeof(cell) +
pc_stksize * sizeof(cell);
data->header().data = sizeof(sp_file_data_t);
data->setBlob((uint8_t *)data_buffer.buffer(), data_buffer.length() * sizeof(cell));
free(LabelTable);
LabelTable = nullptr;
// Add tables in the same order SourceMod 1.6 added them.
builder.add(code);
builder.add(data);
builder.add(publics);
builder.add(pubvars);
builder.add(natives);
builder.add(names);
builder.add(tags);
builder.write(buffer);
}
static void splat_to_binary(const char *binfname, void *bytes, size_t size)
{
// Note: error 161 will setjmp(), which skips destructors :(
FILE *fp = fopen(binfname, "wb");
if (!fp) {
error(161, binfname);
return;
}
if (fwrite(bytes, 1, size, fp) != size) {
fclose(fp);
error(161, binfname);
return;
}
fclose(fp);
}
void assemble(const char *binfname, void *fin)
{
MemoryBuffer buffer;
assemble_to_buffer(&buffer, fin);
// Buffer compression logic.
sp_file_hdr_t *header = (sp_file_hdr_t *)buffer.bytes();
size_t region_size = header->imagesize - header->dataoffs;
size_t zbuf_max = compressBound(region_size);
Bytef *zbuf = (Bytef *)malloc(zbuf_max);
uLong new_disksize = zbuf_max;
int err = compress2(
zbuf,
&new_disksize,
(Bytef *)(buffer.bytes() + header->dataoffs),
region_size,
Z_BEST_COMPRESSION
);
if (err != Z_OK) {
free(zbuf);
pc_printf("Unable to compress, error %d\n", err);
pc_printf("Falling back to no compression.\n");
splat_to_binary(binfname, buffer.bytes(), buffer.size());
return;
}
header->disksize = new_disksize + header->dataoffs;
header->compression = SmxConsts::FILE_COMPRESSION_GZ;
buffer.rewind(header->dataoffs);
buffer.write(zbuf, new_disksize);
free(zbuf);
splat_to_binary(binfname, buffer.bytes(), buffer.size());
}
int pc_writebin(void *handle,void *buffer,int size) {
assert(false);
return 1;
}
static void append_dbginfo(void *fout)
{
AMX_DBG_HDR dbghdr;
AMX_DBG_LINE dbgline;
AMX_DBG_SYMBOL dbgsym;
AMX_DBG_SYMDIM dbgidxtag[sDIMEN_MAX];
int dim,dbgsymdim;
char *str,*prevstr,*name,*prevname;
ucell codeidx,previdx;
constvalue *constptr;
char symname[2*sNAMEMAX+16];
int16_t id1,id2;
ucell address;
stringlist *dbgstrs = get_dbgstrings();
stringlist *iter;
/* header with general information */
memset(&dbghdr, 0, sizeof dbghdr);
dbghdr.size=sizeof dbghdr;
dbghdr.magic=AMX_DBG_MAGIC;
dbghdr.file_version=CUR_FILE_VERSION;
dbghdr.amx_version=MIN_AMX_VERSION;
dbgstrs=dbgstrs->next;
/* first pass: collect the number of items in various tables */
/* file table */
previdx=0;
prevstr=NULL;
prevname=NULL;
for (iter=dbgstrs; iter!=NULL; iter=iter->next) {
str = iter->line;
assert(str!=NULL);
assert(str[0]!='\0' && str[1]==':');
if (str[0]=='F') {
codeidx=hex2long(str+2,&name);
if (codeidx!=previdx) {
if (prevstr!=NULL) {
assert(prevname!=NULL);
dbghdr.files++;
dbghdr.size+=sizeof(cell)+strlen(prevname)+1;
} /* if */
previdx=codeidx;
} /* if */
prevstr=str;
prevname=skipwhitespace(name);
} /* if */
} /* for */
if (prevstr!=NULL) {
assert(prevname!=NULL);
dbghdr.files++;
dbghdr.size+=sizeof(cell)+strlen(prevname)+1;
} /* if */
/* line number table */
for (iter=dbgstrs; iter!=NULL; iter=iter->next) {
str = iter->line;
assert(str!=NULL);
assert(str[0]!='\0' && str[1]==':');
if (str[0]=='L') {
dbghdr.lines++;
dbghdr.size+=sizeof(AMX_DBG_LINE);
} /* if */
} /* for */
/* symbol table */
for (iter=dbgstrs; iter!=NULL; iter=iter->next) {
str = iter->line;
assert(str!=NULL);
assert(str[0]!='\0' && str[1]==':');
if (str[0]=='S') {
dbghdr.symbols++;
name=strchr(str+2,':');
assert(name!=NULL);
dbghdr.size+=sizeof(AMX_DBG_SYMBOL)+strlen(skipwhitespace(name+1));
if ((prevstr=strchr(name,'['))!=NULL)
while ((prevstr=strchr(prevstr+1,':'))!=NULL)
dbghdr.size+=sizeof(AMX_DBG_SYMDIM);
} /* if */
} /* for */
/* tag table */
for (constptr=tagname_tab.next; constptr!=NULL; constptr=constptr->next) {
assert(strlen(constptr->name)>0);
dbghdr.tags++;
dbghdr.size+=sizeof(AMX_DBG_TAG)+strlen(constptr->name);
} /* for */
/* automaton table */
for (constptr=sc_automaton_tab.next; constptr!=NULL; constptr=constptr->next) {
assert((constptr->index==0 && strlen(constptr->name)==0) || strlen(constptr->name)>0);
dbghdr.automatons++;
dbghdr.size+=sizeof(AMX_DBG_MACHINE)+strlen(constptr->name);
} /* for */
/* state table */
for (constptr=sc_state_tab.next; constptr!=NULL; constptr=constptr->next) {
assert(strlen(constptr->name)>0);
dbghdr.states++;
dbghdr.size+=sizeof(AMX_DBG_STATE)+strlen(constptr->name);
} /* for */
/* pass 2: generate the tables */
writeerror |= !pc_writebin(fout,&dbghdr,sizeof dbghdr);
/* file table */
previdx=0;
prevstr=NULL;
prevname=NULL;
for (iter=dbgstrs; iter!=NULL; iter=iter->next) {
str = iter->line;
assert(str[0]!='\0' && str[1]==':');
if (str[0]=='F') {
codeidx=hex2long(str+2,&name);
if (codeidx!=previdx) {
if (prevstr!=NULL) {
assert(prevname!=NULL);
writeerror |= !pc_writebin(fout,&previdx,sizeof previdx);
writeerror |= !pc_writebin(fout,prevname,strlen(prevname)+1);
} /* if */
previdx=codeidx;
} /* if */
prevstr=str;
prevname=skipwhitespace(name);
} /* if */
} /* for */
if (prevstr!=NULL) {
assert(prevname!=NULL);
writeerror |= !pc_writebin(fout,&previdx,sizeof previdx);
writeerror |= !pc_writebin(fout,prevname,strlen(prevname)+1);
} /* if */
/* line number table */
for (iter=dbgstrs; iter!=NULL; iter=iter->next) {
str = iter->line;
assert(str!=NULL);
assert(str[0]!='\0' && str[1]==':');
if (str[0]=='L') {
dbgline.address=hex2long(str+2,&str);
dbgline.line=(int32_t)hex2long(str,NULL);
writeerror |= !pc_writebin(fout,&dbgline,sizeof dbgline);
} /* if */
} /* for */
/* symbol table */
for (iter=dbgstrs; iter!=NULL; iter=iter->next) {
str = iter->line;
assert(str!=NULL);
assert(str[0]!='\0' && str[1]==':');
if (str[0]=='S') {
dbgsym.address=hex2long(str+2,&str);
dbgsym.tag=(int16_t)hex2long(str,&str);
str=skipwhitespace(str);
assert(*str==':');
name=skipwhitespace(str+1);
str=strchr(name,' ');
assert(str!=NULL);
assert((int)(str-name)<sizeof symname);
strlcpy(symname,name,(int)(str-name)+1);
dbgsym.codestart=hex2long(str,&str);
dbgsym.codeend=hex2long(str,&str);
dbgsym.ident=(char)hex2long(str,&str);
dbgsym.vclass=(char)hex2long(str,&str);
dbgsym.dim=0;
str=skipwhitespace(str);
if (*str=='[') {
while (*(str=skipwhitespace(str+1))!=']') {
dbgidxtag[dbgsym.dim].tag=(int16_t)hex2long(str,&str);
str=skipwhitespace(str);
assert(*str==':');
dbgidxtag[dbgsym.dim].size=hex2long(str+1,&str);
dbgsym.dim++;
} /* while */
} /* if */
dbgsymdim = dbgsym.dim;
writeerror |= !pc_writebin(fout,&dbgsym,offsetof(AMX_DBG_SYMBOL, name));
writeerror |= !pc_writebin(fout,symname,strlen(symname)+1);
for (dim=0; dim<dbgsymdim; dim++) {
writeerror |= !pc_writebin(fout,&dbgidxtag[dim],sizeof dbgidxtag[dim]);
} /* for */
} /* if */
} /* for */
/* tag table */
for (constptr=tagname_tab.next; constptr!=NULL; constptr=constptr->next) {
assert(strlen(constptr->name)>0);
id1=(int16_t)(constptr->value & TAGMASK);
writeerror |= !pc_writebin(fout,&id1,sizeof id1);
writeerror |= !pc_writebin(fout,constptr->name,strlen(constptr->name)+1);
} /* for */
/* automaton table */
for (constptr=sc_automaton_tab.next; constptr!=NULL; constptr=constptr->next) {
assert((constptr->index==0 && strlen(constptr->name)==0) || strlen(constptr->name)>0);
id1=(int16_t)constptr->index;
address=(ucell)constptr->value;
writeerror |= !pc_writebin(fout,&id1,sizeof id1);
writeerror |= !pc_writebin(fout,&address,sizeof address);
writeerror |= !pc_writebin(fout,constptr->name,strlen(constptr->name)+1);
} /* for */
/* state table */
for (constptr=sc_state_tab.next; constptr!=NULL; constptr=constptr->next) {
assert(strlen(constptr->name)>0);
id1=(int16_t)constptr->value;
id2=(int16_t)constptr->index;
address=(ucell)constptr->value;
writeerror |= !pc_writebin(fout,&id1,sizeof id1);
writeerror |= !pc_writebin(fout,&id2,sizeof id2);
writeerror |= !pc_writebin(fout,constptr->name,strlen(constptr->name)+1);
} /* for */
delete_dbgstringtable();
}