mirror of
https://github.com/radareorg/radare2.git
synced 2024-11-26 22:50:48 +00:00
Initial re-import of the LUA bin parser and disassembler from extras ##arch
This commit is contained in:
parent
2d0fb6ea81
commit
4808a2a44a
2
dist/plugins-cfg/plugins.def.cfg
vendored
2
dist/plugins-cfg/plugins.def.cfg
vendored
@ -62,6 +62,7 @@ anal.xtensa
|
||||
anal.z80
|
||||
arch.amd29k
|
||||
arch.xap
|
||||
arch.lua
|
||||
arch.i4004
|
||||
arch.jdh8
|
||||
arch.null
|
||||
@ -129,6 +130,7 @@ bin.symbols
|
||||
bin.te
|
||||
bin.tic
|
||||
bin.vsf
|
||||
bin.lua
|
||||
bin.wad
|
||||
bin.wasm
|
||||
bin.xbe
|
||||
|
276
libr/arch/p/arch_lua.c
Normal file
276
libr/arch/p/arch_lua.c
Normal file
@ -0,0 +1,276 @@
|
||||
/* radare2 - BSD - Copyright 2017-2022 - pancake */
|
||||
|
||||
#include <r_arch.h>
|
||||
#include <r_lib.h>
|
||||
|
||||
// XXX should be dynlink
|
||||
#include "lua/lua53.c"
|
||||
#include "lua/lua53_parser.c"
|
||||
|
||||
static bool encode(RArchSession *as, RAnalOp *op, RArchEncodeMask mask) {
|
||||
int parsed = 0;
|
||||
ut32 instruction;
|
||||
current_write_prt = &instruction;
|
||||
current_write_index = 0;
|
||||
doParse0 (parsed, parseNextInstruction, op->mnemonic);
|
||||
|
||||
free (op->bytes);
|
||||
op->size = 4;
|
||||
op->bytes = malloc (4);
|
||||
if (!op->bytes) {
|
||||
return false;
|
||||
}
|
||||
setInstruction (instruction, op->bytes);
|
||||
|
||||
R_LOG_DEBUG ("parsed: %d instruction: %d", parsed, instruction);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool decode(RArchSession *as, RAnalOp *op, RArchDecodeMask mask) {
|
||||
if (!op) {
|
||||
return 0;
|
||||
}
|
||||
const ut8 *data = op->bytes;
|
||||
const int len = op->size;
|
||||
|
||||
const ut32 instruction = getInstruction (data);
|
||||
ut32 extraArg = 0;
|
||||
op->size = 4;
|
||||
op->type = R_ANAL_OP_TYPE_UNK;
|
||||
op->eob = false;
|
||||
if (GET_OPCODE (instruction) > OP_EXTRAARG) {
|
||||
return op->size;
|
||||
}
|
||||
if (mask & R_ARCH_OP_MASK_DISASM) {
|
||||
(void)lua53dissasm (op, data, len);
|
||||
}
|
||||
op->mnemonic = strdup (instruction_names[GET_OPCODE (instruction)]);
|
||||
switch (GET_OPCODE (instruction)) {
|
||||
case OP_MOVE: /* A B R(A) := R(B) */
|
||||
op->type = R_ANAL_OP_TYPE_MOV;
|
||||
break;
|
||||
case OP_LOADK: /* A Bx R(A) := Kst(Bx) */
|
||||
op->type = R_ANAL_OP_TYPE_LOAD;
|
||||
break;
|
||||
case OP_LOADKX: /* A R(A) := Kst(extra arg) */
|
||||
op->type = R_ANAL_OP_TYPE_LOAD;
|
||||
extraArg = getInstruction (data + 4);
|
||||
if (GET_OPCODE (extraArg) == OP_EXTRAARG) {
|
||||
op->size = 8;
|
||||
}
|
||||
break;
|
||||
case OP_LOADBOOL:/* A B C R(A) := (Bool)B; if (C) pc++ */
|
||||
op->type = R_ANAL_OP_TYPE_CJMP;
|
||||
op->val = !!GETARG_B (instruction);
|
||||
op->jump = op->addr + 8;
|
||||
op->fail = op->addr + 4;
|
||||
break;
|
||||
case OP_LOADNIL:/* A B R(A), R(A+1), ..., R(A+B) := nil */
|
||||
break;
|
||||
case OP_GETUPVAL:/* A B R(A) := UpValue[B] */
|
||||
case OP_GETTABUP:/* A B C R(A) := UpValue[B][RK(C)] */
|
||||
op->type = R_ANAL_OP_TYPE_LOAD;
|
||||
break;
|
||||
case OP_GETTABLE:/* A B C R(A) := R(B)[RK(C)] */
|
||||
break;
|
||||
|
||||
case OP_SETTABUP:/* A B C UpValue[A][RK(B)] := RK(C) */
|
||||
case OP_SETUPVAL:/* A B UpValue[B] := R(A) */
|
||||
op->type = R_ANAL_OP_TYPE_STORE;
|
||||
break;
|
||||
case OP_SETTABLE:/* A B C R(A)[RK(B)] := RK(C) */
|
||||
break;
|
||||
case OP_NEWTABLE:/* A B C R(A) := {} (size = B,C) */
|
||||
op->type = R_ANAL_OP_TYPE_NEW;
|
||||
break;
|
||||
case OP_SELF: /* A B C R(A+1) := R(B); R(A) := R(B)[RK(C)] */
|
||||
break;
|
||||
case OP_ADD: /* A B C R(A) := RK(B) + RK(C) */
|
||||
op->type = R_ANAL_OP_TYPE_ADD;
|
||||
break;
|
||||
case OP_SUB: /* A B C R(A) := RK(B) - RK(C) */
|
||||
op->type = R_ANAL_OP_TYPE_SUB;
|
||||
break;
|
||||
case OP_MUL: /* A B C R(A) := RK(B) * RK(C) */
|
||||
op->type = R_ANAL_OP_TYPE_MUL;
|
||||
break;
|
||||
case OP_MOD: /* A B C R(A) := RK(B) % RK(C) */
|
||||
op->type = R_ANAL_OP_TYPE_MOD;
|
||||
break;
|
||||
case OP_POW: /* A B C R(A) := RK(B) ^ RK(C) */
|
||||
break;
|
||||
case OP_DIV: /* A B C R(A) := RK(B) / RK(C) */
|
||||
op->type = R_ANAL_OP_TYPE_DIV;
|
||||
break;
|
||||
case OP_IDIV: /* A B C R(A) := RK(B) // RK(C) */
|
||||
op->type = R_ANAL_OP_TYPE_DIV;
|
||||
break;
|
||||
case OP_BAND: /* A B C R(A) := RK(B) & RK(C) */
|
||||
op->type = R_ANAL_OP_TYPE_AND;
|
||||
break;
|
||||
case OP_BOR: /* A B C R(A) := RK(B) | RK(C) */
|
||||
op->type = R_ANAL_OP_TYPE_OR;
|
||||
break;
|
||||
case OP_BXOR: /* A B C R(A) := RK(B) ~ RK(C) */
|
||||
op->type = R_ANAL_OP_TYPE_XOR;
|
||||
break;
|
||||
case OP_SHL: /* A B C R(A) := RK(B) << RK(C) */
|
||||
op->type = R_ANAL_OP_TYPE_SHL;
|
||||
break;
|
||||
case OP_SHR: /* A B C R(A) := RK(B) >> RK(C) */
|
||||
op->type = R_ANAL_OP_TYPE_SHR;
|
||||
break;
|
||||
case OP_UNM: /* A B R(A) := -R(B) */
|
||||
break;
|
||||
case OP_BNOT: /* A B R(A) := ~R(B) */
|
||||
op->type = R_ANAL_OP_TYPE_CPL;
|
||||
break;
|
||||
case OP_NOT: /* A B R(A) := not R(B) */
|
||||
op->type = R_ANAL_OP_TYPE_NOT;
|
||||
break;
|
||||
case OP_LEN: /* A B R(A) := length of R(B) */
|
||||
break;
|
||||
case OP_CONCAT: /* A B C R(A) := R(B).. ... ..R(C) */
|
||||
break;
|
||||
case OP_JMP: /* A sBx pc+=sBx; if (A) close all upvalues >= R(A - 1) */
|
||||
op->type = R_ANAL_OP_TYPE_CJMP;
|
||||
op->jump = op->addr + 4 * (GETARG_sBx (instruction));
|
||||
op->fail = op->addr + 4;
|
||||
break;
|
||||
case OP_EQ: /* A B C if ((RK(B) == RK(C)) ~= A) then pc++ */
|
||||
op->type = R_ANAL_OP_TYPE_CJMP;
|
||||
op->jump = op->addr + 8;
|
||||
op->fail = op->addr + 4;
|
||||
break;
|
||||
case OP_LT: /* A B C if ((RK(B) < RK(C)) ~= A) then pc++ */
|
||||
op->type = R_ANAL_OP_TYPE_CJMP;
|
||||
op->jump = op->addr + 8;
|
||||
op->fail = op->addr + 4;
|
||||
break;
|
||||
case OP_LE: /* A B C if ((RK(B) <= RK(C)) ~= A) then pc++ */
|
||||
op->type = R_ANAL_OP_TYPE_CJMP;
|
||||
op->jump = op->addr + 8;
|
||||
op->fail = op->addr + 4;
|
||||
break;
|
||||
case OP_TEST: /* A C if not (R(A) <=> C) then pc++ */
|
||||
op->type = R_ANAL_OP_TYPE_CJMP;
|
||||
op->jump = op->addr + 8;
|
||||
op->fail = op->addr + 4;
|
||||
break;
|
||||
case OP_TESTSET:/* A B C if (R(B) <=> C) then R(A) := R(B) else pc++ */
|
||||
op->type = R_ANAL_OP_TYPE_CMOV;
|
||||
op->jump = op->addr + 8;
|
||||
op->fail = op->addr + 4;
|
||||
break;
|
||||
case OP_CALL: /* A B C R(A), ... ,R(A+C-2) := R(A)(R(A+1), ... ,R(A+B-1)) */
|
||||
op->type = R_ANAL_OP_TYPE_RCALL;
|
||||
break;
|
||||
case OP_TAILCALL:/* A B C return R(A)(R(A+1), ... ,R(A+B-1)) */
|
||||
op->type = R_ANAL_OP_TYPE_RCALL;
|
||||
op->type2 = R_ANAL_OP_TYPE_RET;
|
||||
op->eob = true;
|
||||
op->stackop = R_ANAL_STACK_INC;
|
||||
op->stackptr = -4;
|
||||
break;
|
||||
case OP_RETURN: /* A B return R(A), ... ,R(A+B-2) (see note) */
|
||||
op->type = R_ANAL_OP_TYPE_RET;
|
||||
op->eob = true;
|
||||
op->stackop = R_ANAL_STACK_INC;
|
||||
op->stackptr = -4;
|
||||
break;
|
||||
case OP_FORLOOP:/* A sBx R(A)+=R(A+2);
|
||||
if R(A) <?= R(A+1) then { pc+=sBx; R(A+3)=R(A) }*/
|
||||
op->type = R_ANAL_OP_TYPE_CJMP;
|
||||
op->jump = op->addr + 4 + 4 * (GETARG_sBx (instruction));
|
||||
op->fail = op->addr + 4;
|
||||
break;
|
||||
case OP_FORPREP:/* A sBx R(A)-=R(A+2); pc+=sBx */
|
||||
op->type = R_ANAL_OP_TYPE_JMP;
|
||||
op->jump = op->addr + 4 + 4 * (GETARG_sBx (instruction));
|
||||
op->fail = op->addr + 4;
|
||||
break;
|
||||
case OP_TFORCALL:/* A C R(A+3), ... ,R(A+2+C) := R(A)(R(A+1), R(A+2)); */
|
||||
op->type = R_ANAL_OP_TYPE_RCALL;
|
||||
break;
|
||||
case OP_TFORLOOP:/* A sBx if R(A+1) ~= nil then { R(A)=R(A+1); pc += sBx }*/
|
||||
op->type = R_ANAL_OP_TYPE_CJMP;
|
||||
op->jump = op->addr + 4 + 4 * (GETARG_sBx (instruction));
|
||||
op->fail = op->addr + 4;
|
||||
break;
|
||||
case OP_SETLIST:/* A B C R(A)[(C-1)*FPF+i] := R(A+i), 1 <= i <= B */
|
||||
op->type = R_ANAL_OP_TYPE_STORE;
|
||||
break;
|
||||
case OP_CLOSURE:/* A Bx R(A) := closure(KPROTO[Bx]) */
|
||||
case OP_VARARG: /* A B R(A), R(A+1), ..., R(A+B-2) = vararg */
|
||||
case OP_EXTRAARG:/* Ax extra (larger) argument for previous opcode */
|
||||
break;
|
||||
}
|
||||
return op->size;
|
||||
}
|
||||
|
||||
#if 0
|
||||
static int lua53_anal_fcn(RAnal *a, RAnalFunction *fcn, ut64 addr, const ut8 *data, int len, int reftype){
|
||||
Dprintf ("Analyze Function: 0x%"PFMT64x "\n", addr);
|
||||
LuaFunction *function = lua53findLuaFunctionByCodeAddr (addr);
|
||||
if (function) {
|
||||
fcn->maxstack = function->maxStackSize;
|
||||
fcn->nargs = function->numParams;
|
||||
}
|
||||
fcn->addr = addr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int finit(void *user) {
|
||||
if (lua53_data.functionList) {
|
||||
r_list_free (lua53_data.functionList);
|
||||
lua53_data.functionList = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
static int archinfo(RArchSession *cfg, ut32 q) {
|
||||
return 4;
|
||||
}
|
||||
|
||||
static char *regs(RArchSession *s) {
|
||||
static const char * const p =
|
||||
"=PC pc\n"
|
||||
"=SP sp\n"
|
||||
"=A0 a\n"
|
||||
"=A1 b\n"
|
||||
"=A2 c\n"
|
||||
"=R0 a\n"
|
||||
"=R1 b\n"
|
||||
"gpr pc .32 0 0\n"
|
||||
"gpr sp .32 4 0\n"
|
||||
"gpr a .32 8 0\n"
|
||||
"gpr b .32 12 0\n"
|
||||
"gpr c .32 16 0\n"
|
||||
;
|
||||
return strdup (p);
|
||||
}
|
||||
|
||||
RArchPlugin r_arch_plugin_lua = {
|
||||
.name = "lua",
|
||||
.desc = "LUA Bytecode arch plugin",
|
||||
.license = "MIT",
|
||||
.author = "pancake",
|
||||
.arch = "lua",
|
||||
.bits = R_SYS_BITS_PACK (32),
|
||||
.addr_bits = R_SYS_BITS_PACK (32),
|
||||
.info = archinfo,
|
||||
.encode = &encode,
|
||||
.decode = &decode,
|
||||
.regs = regs,
|
||||
.cpus = "5.3", // ,5.4"
|
||||
.endian = R_SYS_ENDIAN_LITTLE,
|
||||
};
|
||||
|
||||
#ifndef R2_PLUGIN_INCORE
|
||||
R_API RLibStruct radare_plugin = {
|
||||
.type = R_LIB_TYPE_ARCH,
|
||||
.data = &r_arch_plugin_lua,
|
||||
.version = R2_VERSION
|
||||
};
|
||||
#endif
|
10
libr/arch/p/lua.mk
Normal file
10
libr/arch/p/lua.mk
Normal file
@ -0,0 +1,10 @@
|
||||
OBJ_LUA=arch_lua.o
|
||||
|
||||
STATIC_OBJ+=$(OBJ_LUA)
|
||||
TARGET_LUA=arch_lua.${EXT_SO}
|
||||
|
||||
ALL_TARGETS+=${TARGET_LUA}
|
||||
|
||||
${TARGET_LUA}: ${OBJ_LUA}
|
||||
${CC} $(LDFLAGS) ${CFLAGS} $(call libname,arch_lua) $(CS_CFLAGS) \
|
||||
-o arch_lua.${EXT_SO} ${OBJ_LUA} $(CS_LDFLAGS)
|
15
libr/arch/p/lua/Makefile
Normal file
15
libr/arch/p/lua/Makefile
Normal file
@ -0,0 +1,15 @@
|
||||
LIBEXT=$(shell r2 -H LIBEXT)
|
||||
R2_LIBR_PLUGINS=$(shell r2 -H R2_LIBR_PLUGINS)
|
||||
|
||||
all:
|
||||
for a in asm anal bin ; do $(MAKE) -C $$a ; done
|
||||
|
||||
clean:
|
||||
for a in asm anal bin ; do $(MAKE) -C $$a clean ; done
|
||||
|
||||
install:
|
||||
mkdir -p $(R2_LIBR_PLUGINS)
|
||||
cp -f {asm,anal,bin}/*.$(LIBEXT) ${R2_LIBR_PLUGINS}/
|
||||
|
||||
uninstall:
|
||||
rm -f $(R2_LIBR_PLUGINS)/*lua53*.$(LIBEXT)
|
8
libr/arch/p/lua/asm/Makefile
Normal file
8
libr/arch/p/lua/asm/Makefile
Normal file
@ -0,0 +1,8 @@
|
||||
EXT_SO=$(shell r2 -H LIBEXT)
|
||||
WITHPIC=1
|
||||
CFLAGS+=$(shell pkg-config --cflags --libs r_asm)
|
||||
LDFLAGS+=-shared -fPIC
|
||||
|
||||
include lua53.mk
|
||||
|
||||
all: $(TARGET_LUA53)
|
11
libr/arch/p/lua/asm/lua53.mk
Normal file
11
libr/arch/p/lua/asm/lua53.mk
Normal file
@ -0,0 +1,11 @@
|
||||
OBJ_LUA53=asm_lua53.o
|
||||
|
||||
STATIC_OBJ+=${OBJ_LUA53}
|
||||
TARGET_LUA53=asm_lua53.${EXT_SO}
|
||||
|
||||
ifeq ($(WITHPIC),1)
|
||||
ALL_TARGETS+=${TARGET_LUA53}
|
||||
|
||||
${TARGET_LUA53}: ${OBJ_LUA53}
|
||||
${CC} $(call libname,asm_lua53) ${LDFLAGS} ${CFLAGS} -o ${TARGET_LUA53} ${OBJ_LUA53}
|
||||
endif
|
422
libr/arch/p/lua/lua53.c
Normal file
422
libr/arch/p/lua/lua53.c
Normal file
@ -0,0 +1,422 @@
|
||||
/* radare2 - LGPL - Copyright 2017-2022 - pancake */
|
||||
|
||||
#include <r_asm.h>
|
||||
#include <stdio.h>
|
||||
#include "lua53.h"
|
||||
|
||||
static R_TH_LOCAL ut32 *current_write_prt;
|
||||
static R_TH_LOCAL ut32 current_write_index;
|
||||
|
||||
#define isAlpha(x) (('a' <= (x) && 'z' >= (x)) || ('A' <= (x) && 'Z' >= (x)))
|
||||
#define isNumeric(x) ('0' <= (x) && '9' >= (x))
|
||||
#define isWhitespace(x) (' ' == (x) || '\t' == (x))
|
||||
#define isComment(x) (';' == (x))
|
||||
|
||||
#define doParse0(inc, func, str) { \
|
||||
int temp = func (str + inc); \
|
||||
if (temp < 0) { \
|
||||
R_LOG_DEBUG ("%i from %s in String %s", temp,#func, str + inc);\
|
||||
return -1; \
|
||||
} \
|
||||
inc += temp; \
|
||||
}
|
||||
#define doParse1(inc, func, str, ...) { \
|
||||
int temp = func (str + inc, __VA_ARGS__); \
|
||||
if (temp < 0) { \
|
||||
R_LOG_DEBUG ("%i from %s in String %s", temp,#func, str + inc);\
|
||||
return -1; \
|
||||
} \
|
||||
inc += temp; \
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
PARAMETER_A,
|
||||
PARAMETER_B,
|
||||
PARAMETER_C,
|
||||
PARAMETER_Ax,
|
||||
PARAMETER_Bx,
|
||||
PARAMETER_sBx
|
||||
} Parameter;
|
||||
|
||||
static int parseParameters(const char *str, OpCode opCode);
|
||||
static int parseParameter(const char *str, Parameter parameter);
|
||||
static int parseNextInstruction(const char *str);
|
||||
static int parseWhitespaces(const char *str);
|
||||
|
||||
const char *instruction_names[] = {
|
||||
"move", "loadk", "loadkx", "loadbool", "loadnil", "getupval", "gettabup", "gettable", "settabup", "setupval", "settable", "newtable", "self", "add", "sub", "mul", "mod",
|
||||
"pow", "div", "idiv", "band", "bor", "bxor", "shl", "shr", "unm", "bnot", "not", "len", "concat", "jmp", "eq", "lt", "le",
|
||||
"test", "testset", "call", "tailcall", "return", "forloop", "forprep", "tforcall", "tforloop", "setlist", "closure", "vararg", "extraarg", 0
|
||||
};
|
||||
|
||||
static ut32 getInstruction(const ut8 *data){
|
||||
ut32 instruction = 0;
|
||||
instruction |= data[3] << 24;
|
||||
instruction |= data[2] << 16;
|
||||
instruction |= data[1] << 8;
|
||||
instruction |= data[0] << 0;
|
||||
return instruction;
|
||||
}
|
||||
|
||||
static void setInstruction(ut32 opcode, ut8 *data) {
|
||||
data[3] = opcode >> 24;
|
||||
data[2] = opcode >> 16;
|
||||
data[1] = opcode >> 8;
|
||||
data[0] = opcode >> 0;
|
||||
}
|
||||
|
||||
static int findNextWordStart(const char *str){
|
||||
int chars_skipped = 0;
|
||||
char c;
|
||||
char comment_char;
|
||||
while (1) {
|
||||
doParse0 (chars_skipped, parseWhitespaces, str);
|
||||
c = str[chars_skipped];
|
||||
if (isAlpha (c) || isNumeric (c) || c == '-') { // if alphanumeric character return position
|
||||
return chars_skipped;
|
||||
} else if (c == ';') { // skip comment
|
||||
do {
|
||||
++chars_skipped;
|
||||
comment_char = str[chars_skipped];
|
||||
} while (comment_char != '\n'); // if no newline
|
||||
} else if (c == '\n') { // skip comment
|
||||
++chars_skipped;
|
||||
continue;
|
||||
} else if (c == '\0') {
|
||||
break;
|
||||
} else {
|
||||
R_LOG_DEBUG ("Invalic Char 0x%02x", c);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
R_LOG_DEBUG ("Parsed %i empty chars", chars_skipped);
|
||||
return chars_skipped;
|
||||
}
|
||||
|
||||
static int parseNextInstruction(const char *str) {
|
||||
int chars_skipped = 0;
|
||||
doParse0 (chars_skipped, findNextWordStart, str);
|
||||
const char *str_ptr = str + chars_skipped;
|
||||
|
||||
int i;
|
||||
for (i = 0; instruction_names[i] != 0; i++) { // iterate over instruction strings
|
||||
bool accepted = true;
|
||||
int j;
|
||||
for (j = 0; instruction_names[i][j] != '\0'; j++) { // iterate over characters
|
||||
if (!((instruction_names[i][j] == str_ptr[j]) || (instruction_names[i][j] == (str_ptr[j] - 'A' + 'a')))) { // if char or uppercase char does not match
|
||||
accepted = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (((isWhitespace (str_ptr[j]) || isComment (str_ptr[j])) && accepted)) { // if this is longest match possible
|
||||
// write operation
|
||||
chars_skipped += j;
|
||||
R_LOG_DEBUG ("Opcode %i Instruction %s", i, instruction_names[i]);
|
||||
|
||||
SET_OPCODE (current_write_prt[current_write_index], i); // sets the opcode
|
||||
|
||||
doParse1 (chars_skipped, parseParameters, str, i); // Parse parameters
|
||||
|
||||
current_write_index++; // finished parsing an instruction so increase index
|
||||
return chars_skipped;
|
||||
}
|
||||
}
|
||||
R_LOG_DEBUG ("Error");
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int parseWhitespaces(const char *str){
|
||||
int skipped_whitespace = 0;
|
||||
char c = str[skipped_whitespace];
|
||||
while (isWhitespace (c)) {
|
||||
c = str[++skipped_whitespace];
|
||||
}
|
||||
R_LOG_DEBUG ("Parsed %i Whitespaces", skipped_whitespace);
|
||||
return skipped_whitespace;
|
||||
}
|
||||
|
||||
static int parseParameters(const char *str, OpCode opCode) {
|
||||
int chars_skipped = 0;
|
||||
doParse0 (chars_skipped, parseWhitespaces, str);
|
||||
switch (opCode) {
|
||||
case OP_LOADKX: /* A R(A) := Kst(extra arg) */
|
||||
doParse1 (chars_skipped, parseParameter, str, PARAMETER_A);
|
||||
break;
|
||||
case OP_MOVE: /* A B R(A) := R(B) */
|
||||
case OP_LOADNIL: /* A B R(A), R(A+1), ..., R(A+B) := nil */
|
||||
case OP_GETUPVAL: /* A B R(A) := UpValue[B] */
|
||||
case OP_SETUPVAL: /* A B UpValue[B] := R(A) */
|
||||
case OP_UNM: /* A B R(A) := -R(B) */
|
||||
case OP_BNOT: /* A B R(A) := ~R(B) */
|
||||
case OP_NOT: /* A B R(A) := not R(B) */
|
||||
case OP_LEN: /* A B R(A) := length of R(B) */
|
||||
case OP_RETURN: /* A B return R(A), ... ,R(A+B-2) (see note) */
|
||||
case OP_VARARG: /* A B R(A), R(A+1), ..., R(A+B-2) = vararg */
|
||||
doParse1 (chars_skipped, parseParameter, str, PARAMETER_A);
|
||||
doParse1 (chars_skipped, parseParameter, str, PARAMETER_B);
|
||||
break;
|
||||
case OP_TEST: /* A C if not (R(A) <=> C) then pc++ */
|
||||
case OP_TFORCALL: /* A C R(A+3), ... ,R(A+2+C) := R(A)(R(A+1), R(A+2)); */
|
||||
doParse1 (chars_skipped, parseParameter, str, PARAMETER_A);
|
||||
doParse1 (chars_skipped, parseParameter, str, PARAMETER_C);
|
||||
break;
|
||||
case OP_LOADK: /* A Bx R(A) := Kst(Bx) */
|
||||
case OP_CLOSURE: /* A Bx R(A) := closure(KPROTO[Bx]) */
|
||||
doParse1 (chars_skipped, parseParameter, str, PARAMETER_A);
|
||||
doParse1 (chars_skipped, parseParameter, str, PARAMETER_Bx);
|
||||
break;
|
||||
case OP_LOADBOOL: /* A B C R(A) := (Bool)B; if (C) pc++ */
|
||||
case OP_GETTABUP: /* A B C R(A) := UpValue[B][RK(C)] */
|
||||
case OP_GETTABLE: /* A B C R(A) := R(B)[RK(C)] */
|
||||
case OP_SETTABUP: /* A B C UpValue[A][RK(B)] := RK(C) */
|
||||
case OP_SETTABLE: /* A B C R(A)[RK(B)] := RK(C) */
|
||||
case OP_NEWTABLE: /* A B C R(A) := {} (size = B,C) */
|
||||
case OP_SELF: /* A B C R(A+1) := R(B); R(A) := R(B)[RK(C)] */
|
||||
case OP_ADD: /* A B C R(A) := RK(B) + RK(C) */
|
||||
case OP_SUB: /* A B C R(A) := RK(B) - RK(C) */
|
||||
case OP_MUL: /* A B C R(A) := RK(B) * RK(C) */
|
||||
case OP_MOD: /* A B C R(A) := RK(B) % RK(C) */
|
||||
case OP_POW: /* A B C R(A) := RK(B) ^ RK(C) */
|
||||
case OP_DIV: /* A B C R(A) := RK(B) / RK(C) */
|
||||
case OP_IDIV: /* A B C R(A) := RK(B) // RK(C) */
|
||||
case OP_BAND: /* A B C R(A) := RK(B) & RK(C) */
|
||||
case OP_BOR: /* A B C R(A) := RK(B) | RK(C) */
|
||||
case OP_BXOR: /* A B C R(A) := RK(B) ~ RK(C) */
|
||||
case OP_SHL: /* A B C R(A) := RK(B) << RK(C) */
|
||||
case OP_SHR: /* A B C R(A) := RK(B) >> RK(C) */
|
||||
case OP_CONCAT: /* A B C R(A) := R(B).. ... ..R(C) */
|
||||
case OP_EQ: /* A B C if ((RK(B) == RK(C)) ~= A) then pc++ */
|
||||
case OP_LT: /* A B C if ((RK(B) < RK(C)) ~= A) then pc++ */
|
||||
case OP_LE: /* A B C if ((RK(B) <= RK(C)) ~= A) then pc++ */
|
||||
case OP_TESTSET: /* A B C if (R(B) <=> C) then R(A) := R(B) else pc++ */
|
||||
case OP_CALL: /* A B C R(A), ... ,R(A+C-2) := R(A)(R(A+1), ... ,R(A+B-1)) */
|
||||
case OP_TAILCALL: /* A B C return R(A)(R(A+1), ... ,R(A+B-1)) */
|
||||
case OP_SETLIST: /* A B C R(A)[(C-1)*FPF+i] := R(A+i), 1 <= i <= B */
|
||||
doParse1 (chars_skipped, parseParameter, str, PARAMETER_A);
|
||||
doParse1 (chars_skipped, parseParameter, str, PARAMETER_B);
|
||||
doParse1 (chars_skipped, parseParameter, str, PARAMETER_C);
|
||||
break;
|
||||
case OP_JMP: /* A sBx pc+=sBx; if (A) close all upvalues >= R(A - 1) */
|
||||
case OP_FORLOOP: /* A sBx R(A)+=R(A+2);
|
||||
if R(A) <?= R(A+1) then { pc+=sBx; R(A+3)=R(A) }*/
|
||||
case OP_FORPREP: /* A sBx R(A)-=R(A+2); pc+=sBx */
|
||||
case OP_TFORLOOP: /* A sBx if R(A+1) ~= nil then { R(A)=R(A+1); pc += sBx }*/
|
||||
doParse1 (chars_skipped, parseParameter, str, PARAMETER_A);
|
||||
doParse1 (chars_skipped, parseParameter, str, PARAMETER_sBx);
|
||||
break;
|
||||
case OP_EXTRAARG: /* Ax extra (larger) argument for previous opcode */
|
||||
doParse1 (chars_skipped, parseParameter, str, PARAMETER_Ax);
|
||||
break;
|
||||
}
|
||||
return chars_skipped;
|
||||
}
|
||||
|
||||
static int parseParameter(const char *str, Parameter parameter) {
|
||||
int skipped_chars = findNextWordStart (str);
|
||||
int resultingNumber = 0;
|
||||
bool negative = false;
|
||||
if (str[skipped_chars] == '-') {
|
||||
negative = true;
|
||||
++skipped_chars;
|
||||
}
|
||||
char c = str[skipped_chars];
|
||||
if (!isNumeric (c)) {
|
||||
return -1;
|
||||
}
|
||||
while (isNumeric (c)) {
|
||||
resultingNumber *= 10;
|
||||
resultingNumber += c - '0';
|
||||
c = str[++skipped_chars];
|
||||
}
|
||||
resultingNumber = negative? resultingNumber * (-1): resultingNumber;
|
||||
R_LOG_DEBUG ("Parsed Parameter %i", resultingNumber);
|
||||
if (parameter != PARAMETER_sBx && resultingNumber < 0) {
|
||||
return -1;
|
||||
}
|
||||
switch (parameter) {
|
||||
case PARAMETER_A:
|
||||
SETARG_A (current_write_prt[current_write_index], resultingNumber);
|
||||
break;
|
||||
case PARAMETER_B:
|
||||
SETARG_B (current_write_prt[current_write_index], resultingNumber);
|
||||
break;
|
||||
case PARAMETER_C:
|
||||
SETARG_C (current_write_prt[current_write_index], resultingNumber);
|
||||
break;
|
||||
case PARAMETER_Ax:
|
||||
SETARG_Ax (current_write_prt[current_write_index], resultingNumber);
|
||||
break;
|
||||
case PARAMETER_Bx:
|
||||
SETARG_Bx (current_write_prt[current_write_index], resultingNumber);
|
||||
break;
|
||||
case PARAMETER_sBx:
|
||||
SETARG_sBx (current_write_prt[current_write_index], resultingNumber);
|
||||
break;
|
||||
}
|
||||
return skipped_chars;
|
||||
}
|
||||
|
||||
#if 0
|
||||
// XXX
|
||||
int lua53asm(RAsmOp *op, const char *s) {
|
||||
int parsed = 0;
|
||||
eprintf ("%s\n", s);
|
||||
ut32 instruction;
|
||||
current_write_prt = &instruction;
|
||||
current_write_index = 0;
|
||||
doParse0 (parsed, parseNextInstruction, s);
|
||||
|
||||
setInstruction (instruction, op->buf);
|
||||
|
||||
eprintf ("%d\n", parsed);
|
||||
eprintf ("%08x\n", instruction);
|
||||
return 4;
|
||||
}
|
||||
#endif
|
||||
|
||||
int lua53dissasm(RAnalOp *op, const ut8 *buf, int len){
|
||||
if (len < 4) {
|
||||
op->mnemonic = strdup ("truncated");
|
||||
op->type = R_ANAL_OP_TYPE_ILL;
|
||||
return 0;
|
||||
}
|
||||
ut32 instruction = getInstruction (buf);
|
||||
OpCode operator = GET_OPCODE (instruction);
|
||||
|
||||
op->mnemonic = strdup ("invalid");
|
||||
op->size = 4;
|
||||
switch (operator) {
|
||||
case OP_LOADKX: /* A R(A) := Kst(extra arg) */
|
||||
free (op->mnemonic);
|
||||
op->mnemonic = r_str_newf ("%s %i", instruction_names[GET_OPCODE (instruction)], GETARG_A (instruction));
|
||||
break;
|
||||
case OP_MOVE: /* A B R(A) := R(B) */
|
||||
case OP_LOADNIL: /* A B R(A), R(A+1), ..., R(A+B) := nil */
|
||||
case OP_GETUPVAL: /* A B R(A) := UpValue[B] */
|
||||
case OP_SETUPVAL: /* A B UpValue[B] := R(A) */
|
||||
case OP_UNM: /* A B R(A) := -R(B) */
|
||||
case OP_BNOT: /* A B R(A) := ~R(B) */
|
||||
case OP_NOT: /* A B R(A) := not R(B) */
|
||||
case OP_LEN: /* A B R(A) := length of R(B) */
|
||||
case OP_RETURN: /* A B return R(A), ... ,R(A+B-2) (see note) */
|
||||
case OP_VARARG: /* A B R(A), R(A+1), ..., R(A+B-2) = vararg */
|
||||
free (op->mnemonic);
|
||||
op->mnemonic = r_str_newf ("%s %i %i", instruction_names[GET_OPCODE (instruction)], GETARG_A (instruction), GETARG_B (instruction));
|
||||
break;
|
||||
case OP_TEST: /* A C if not (R(A) <=> C) then pc++ */
|
||||
case OP_TFORCALL: /* A C R(A+3), ... ,R(A+2+C) := R(A)(R(A+1), R(A+2)); */
|
||||
free (op->mnemonic);
|
||||
op->mnemonic = r_str_newf ("%s %i %i", instruction_names[GET_OPCODE (instruction)], GETARG_A (instruction), GETARG_C (instruction));
|
||||
break;
|
||||
case OP_LOADK: /* A Bx R(A) := Kst(Bx) */
|
||||
free (op->mnemonic);
|
||||
op->mnemonic = r_str_newf ("%s %i Kst(%i)", instruction_names[GET_OPCODE (instruction)], GETARG_A (instruction), GETARG_Bx (instruction));
|
||||
break;
|
||||
case OP_CLOSURE: /* A Bx R(A) := closure(KPROTO[Bx]) */
|
||||
free (op->mnemonic);
|
||||
op->mnemonic = r_str_newf ("%s %i KPROTO(%i)", instruction_names[GET_OPCODE (instruction)], GETARG_A (instruction), GETARG_Bx (instruction));
|
||||
break;
|
||||
case OP_LOADBOOL: /* A B C R(A) := (Bool)B; if (C) pc++ */
|
||||
case OP_NEWTABLE: /* A B C R(A) := {} (size = B,C) */
|
||||
case OP_CONCAT: /* A B C R(A) := R(B).. ... ..R(C) */
|
||||
case OP_TESTSET: /* A B C if (R(B) <=> C) then R(A) := R(B) else pc++ */
|
||||
case OP_CALL: /* A B C R(A), ... ,R(A+C-2) := R(A)(R(A+1), ... ,R(A+B-1)) */
|
||||
case OP_TAILCALL: /* A B C return R(A)(R(A+1), ... ,R(A+B-1)) */
|
||||
case OP_SETLIST: /* A B C R(A)[(C-1)*FPF+i] := R(A+i), 1 <= i <= B */
|
||||
op->mnemonic = r_str_newf ("%s %i %i %i", instruction_names[GET_OPCODE (instruction)], GETARG_A (instruction), GETARG_B (instruction), GETARG_C (instruction));
|
||||
break;
|
||||
case OP_SELF: /* A B C R(A+1) := R(B); R(A) := R(B)[RK(C)] */
|
||||
case OP_GETTABUP: /* A B C R(A) := UpValue[B][RK(C)] */
|
||||
case OP_GETTABLE: /* A B C R(A) := R(B)[RK(C)] */
|
||||
if (GETARG_C (instruction) & 0x100) {
|
||||
op->mnemonic = r_str_newf ("%s %i %i K(%i)", instruction_names[GET_OPCODE (instruction)], GETARG_A (instruction), GETARG_B (instruction), GETARG_C (instruction) & 0xFF);
|
||||
} else {
|
||||
op->mnemonic = r_str_newf ("%s %i %i %i", instruction_names[GET_OPCODE (instruction)], GETARG_A (instruction), GETARG_B (instruction), GETARG_C (instruction));
|
||||
}
|
||||
break;
|
||||
case OP_SETTABUP: /* A B C UpValue[A][RK(B)] := RK(C) */
|
||||
case OP_SETTABLE: /* A B C R(A)[RK(B)] := RK(C) */
|
||||
case OP_ADD: /* A B C R(A) := RK(B) + RK(C) */
|
||||
case OP_SUB: /* A B C R(A) := RK(B) - RK(C) */
|
||||
case OP_MUL: /* A B C R(A) := RK(B) * RK(C) */
|
||||
case OP_MOD: /* A B C R(A) := RK(B) % RK(C) */
|
||||
case OP_POW: /* A B C R(A) := RK(B) ^ RK(C) */
|
||||
case OP_DIV: /* A B C R(A) := RK(B) / RK(C) */
|
||||
case OP_IDIV: /* A B C R(A) := RK(B) // RK(C) */
|
||||
case OP_BAND: /* A B C R(A) := RK(B) & RK(C) */
|
||||
case OP_BOR: /* A B C R(A) := RK(B) | RK(C) */
|
||||
case OP_BXOR: /* A B C R(A) := RK(B) ~ RK(C) */
|
||||
case OP_SHL: /* A B C R(A) := RK(B) << RK(C) */
|
||||
case OP_SHR: /* A B C R(A) := RK(B) >> RK(C) */
|
||||
case OP_EQ: /* A B C if ((RK(B) == RK(C)) ~= A) then pc++ */
|
||||
case OP_LT: /* A B C if ((RK(B) < RK(C)) ~= A) then pc++ */
|
||||
case OP_LE: /* A B C if ((RK(B) <= RK(C)) ~= A) then pc++ */
|
||||
|
||||
free (op->mnemonic);
|
||||
if (GETARG_B (instruction) & 0x100) {
|
||||
if (GETARG_C (instruction) & 0x100) {
|
||||
op->mnemonic = r_str_newf ("%s %i K(%i) K(%i)", instruction_names[GET_OPCODE (instruction)], GETARG_A (instruction), GETARG_B (instruction) & 0xFF, GETARG_C (instruction) & 0xFF);
|
||||
} else {
|
||||
op->mnemonic = r_str_newf ("%s %i K(%i) %i", instruction_names[GET_OPCODE (instruction)], GETARG_A (instruction), GETARG_B (instruction) & 0xFF, GETARG_C (instruction));
|
||||
}
|
||||
} else {
|
||||
if (GETARG_C (instruction) & 0x100) {
|
||||
op->mnemonic = r_str_newf ("%s %i %i K(%i)", instruction_names[GET_OPCODE (instruction)], GETARG_A (instruction), GETARG_B (instruction), GETARG_C (instruction) & 0xFF);
|
||||
} else {
|
||||
op->mnemonic = r_str_newf ("%s %i %i %i", instruction_names[GET_OPCODE (instruction)], GETARG_A (instruction), GETARG_B (instruction), GETARG_C (instruction));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case OP_JMP: /* A sBx pc+=sBx; if (A) close all upvalues >= R(A - 1) */
|
||||
case OP_FORLOOP: /* A sBx R(A)+=R(A+2);
|
||||
if R(A) <?= R(A+1) then { pc+=sBx; R(A+3)=R(A) }*/
|
||||
case OP_FORPREP: /* A sBx R(A)-=R(A+2); pc+=sBx */
|
||||
case OP_TFORLOOP: /* A sBx if R(A+1) ~= nil then { R(A)=R(A+1); pc += sBx }*/
|
||||
free (op->mnemonic);
|
||||
op->mnemonic = r_str_newf ("%s %i %i", instruction_names[GET_OPCODE (instruction)], GETARG_A (instruction), GETARG_sBx (instruction));
|
||||
break;
|
||||
case OP_EXTRAARG: /* Ax extra (larger) argument for previous opcode */
|
||||
free (op->mnemonic);
|
||||
op->mnemonic = r_str_newf ("%s Kst(%i)", instruction_names[GET_OPCODE (instruction)], GETARG_Ax (instruction));
|
||||
break;
|
||||
default:
|
||||
// invalid
|
||||
op->size = -1;
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
#ifdef MAIN_ASM
|
||||
int main(int argc, char **argv) {
|
||||
|
||||
char *c = "move 1 2\n forprep 13 -2";
|
||||
int p = 0;
|
||||
current_write_prt = malloc (8);
|
||||
current_write_index = 0;
|
||||
eprintf ("Parsing String: %s\n", c);
|
||||
eprintf ("-----------------------\n");
|
||||
doParse0 (p, parseNextInstruction, c, (int) strlen (c));
|
||||
eprintf ("Parsed Characters %i\n", p);
|
||||
eprintf ("%d %08x\n", current_write_index, current_write_prt[current_write_index - 1]);
|
||||
|
||||
eprintf ("------------\n");
|
||||
|
||||
doParse0 (p, parseNextInstruction, c, (int) strlen (c));
|
||||
eprintf ("Parsed Characters %i\n", p);
|
||||
eprintf ("%d %08x\n", current_write_index, current_write_prt[current_write_index - 1]);
|
||||
|
||||
eprintf ("------------\n");
|
||||
|
||||
RAsmOp *asmOp = (RAsmOp *) malloc (sizeof (RAsmOp));
|
||||
int advanced = lua53dissasm (asmOp, (const char *) current_write_prt, 4);
|
||||
|
||||
eprintf ("%s\n", asmOp->buf_asm);
|
||||
lua53dissasm (asmOp, (const char *) current_write_prt + advanced, 4);
|
||||
eprintf ("%s\n", asmOp->buf_asm);
|
||||
|
||||
free (current_write_prt);
|
||||
return 0;
|
||||
}
|
||||
#endif // MAIN_ASM
|
231
libr/arch/p/lua/lua53.h
Normal file
231
libr/arch/p/lua/lua53.h
Normal file
@ -0,0 +1,231 @@
|
||||
/*
|
||||
** $Id: lopcodes.h,v 1.149 2016/07/19 17:12:21 roberto Exp $
|
||||
** Opcodes for Lua virtual machine
|
||||
*/
|
||||
|
||||
/******************************************************************************
|
||||
* Copyright (C) 1994-2017 Lua.org, PUC-Rio.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef lopcodes_h
|
||||
#define lopcodes_h
|
||||
|
||||
|
||||
#include <limits.h>
|
||||
#include <stddef.h>
|
||||
|
||||
/*===========================================================================
|
||||
We assume that instructions are unsigned numbers.
|
||||
All instructions have an opcode in the first 6 bits.
|
||||
Instructions can have the following fields:
|
||||
'A' : 8 bits
|
||||
'B' : 9 bits
|
||||
'C' : 9 bits
|
||||
'Ax' : 26 bits ('A', 'B', and 'C' together)
|
||||
'Bx' : 18 bits ('B' and 'C' together)
|
||||
'sBx' : signed Bx
|
||||
|
||||
A signed argument is represented in excess K; that is, the number
|
||||
value is the unsigned value minus K. K is exactly the maximum value
|
||||
for that argument (so that -max is represented by 0, and +max is
|
||||
represented by 2*max), which is half the maximum for the corresponding
|
||||
unsigned argument.
|
||||
===========================================================================*/
|
||||
|
||||
|
||||
enum OpMode {
|
||||
iABC, iABx, iAsBx, iAx
|
||||
}; /* basic instruction format */
|
||||
|
||||
#define cast(x, y) ((x)(y))
|
||||
|
||||
/*
|
||||
** size and position of opcode arguments.
|
||||
*/
|
||||
#define SIZE_C 9
|
||||
#define SIZE_B 9
|
||||
#define SIZE_Bx (SIZE_C + SIZE_B)
|
||||
#define SIZE_A 8
|
||||
#define SIZE_Ax (SIZE_C + SIZE_B + SIZE_A)
|
||||
|
||||
#define SIZE_OP 6
|
||||
|
||||
#define POS_OP 0
|
||||
#define POS_A (POS_OP + SIZE_OP)
|
||||
#define POS_C (POS_A + SIZE_A)
|
||||
#define POS_B (POS_C + SIZE_C)
|
||||
#define POS_Bx POS_C
|
||||
#define POS_Ax POS_A
|
||||
|
||||
#define MAX_INT INT_MAX /* maximum value of an int */
|
||||
|
||||
#define LUAI_BITSINT 32
|
||||
|
||||
/*
|
||||
** limits for opcode arguments.
|
||||
** we use (signed) int to manipulate most arguments,
|
||||
** so they must fit in LUAI_BITSINT-1 bits (-1 for sign)
|
||||
*/
|
||||
#if SIZE_Bx < LUAI_BITSINT - 1
|
||||
#define MAXARG_Bx ((1 << SIZE_Bx) - 1)
|
||||
#define MAXARG_sBx (MAXARG_Bx >> 1) /* 'sBx' is signed */
|
||||
#else
|
||||
#define MAXARG_Bx MAX_INT
|
||||
#define MAXARG_sBx MAX_INT
|
||||
#endif
|
||||
|
||||
#if SIZE_Ax < LUAI_BITSINT - 1
|
||||
#define MAXARG_Ax ((1 << SIZE_Ax) - 1)
|
||||
#else
|
||||
#define MAXARG_Ax MAX_INT
|
||||
#endif
|
||||
|
||||
|
||||
#define MAXARG_A ((1 << SIZE_A) - 1)
|
||||
#define MAXARG_B ((1 << SIZE_B) - 1)
|
||||
#define MAXARG_C ((1 << SIZE_C) - 1)
|
||||
|
||||
/* creates a mask with 'n' 1 bits at position 'p' */
|
||||
#define MASK1(n, p) ((~((~(ut32)0) << (ut32)(n))) << (ut32)(p))
|
||||
|
||||
/* creates a mask with 'n' 0 bits at position 'p' */
|
||||
#define MASK0(n, p) (~MASK1(n, p))
|
||||
|
||||
static ut32 getInstruction(const ut8 *data);
|
||||
static void setInstruction(ut32 opcode, ut8 *data);
|
||||
|
||||
|
||||
/*
|
||||
** the following macros help to manipulate instructions
|
||||
*/
|
||||
|
||||
#define GET_OPCODE(i) (cast (OpCode, ((i) >> POS_OP) & MASK1 (SIZE_OP, 0)))
|
||||
#define SET_OPCODE(i, o) ((i) = (((i) & MASK0 (SIZE_OP, POS_OP)) |\
|
||||
((cast (ut32, o) << POS_OP) & MASK1 (SIZE_OP, POS_OP))))
|
||||
|
||||
#define getarg(i, pos, size) (cast (int, ((i) >> pos) & MASK1 (size, 0)))
|
||||
#define setarg(i, v, pos, size) ((i) = (((i) & MASK0 (size, pos)) |\
|
||||
((cast (ut32, v) << pos) & MASK1 (size, pos))))
|
||||
|
||||
#define GETARG_A(i) getarg (i, POS_A, SIZE_A)
|
||||
#define SETARG_A(i, v) setarg (i, v, POS_A, SIZE_A)
|
||||
|
||||
#define GETARG_B(i) getarg (i, POS_B, SIZE_B)
|
||||
#define SETARG_B(i, v) setarg (i, v, POS_B, SIZE_B)
|
||||
|
||||
#define GETARG_C(i) getarg (i, POS_C, SIZE_C)
|
||||
#define SETARG_C(i, v) setarg (i, v, POS_C, SIZE_C)
|
||||
|
||||
#define GETARG_Bx(i) getarg (i, POS_Bx, SIZE_Bx)
|
||||
#define SETARG_Bx(i, v) setarg (i, v, POS_Bx, SIZE_Bx)
|
||||
|
||||
#define GETARG_Ax(i) getarg (i, POS_Ax, SIZE_Ax)
|
||||
#define SETARG_Ax(i, v) setarg (i, v, POS_Ax, SIZE_Ax)
|
||||
|
||||
#define GETARG_sBx(i) (GETARG_Bx (i) - MAXARG_sBx)
|
||||
#define SETARG_sBx(i, b) SETARG_Bx ((i), cast (unsigned int, (b) + MAXARG_sBx))
|
||||
|
||||
|
||||
#define CREATE_ABC(o, a, b, c) ((cast (ut32, o) << POS_OP) \
|
||||
| (cast (ut32, a) << POS_A) \
|
||||
| (cast (ut32, b) << POS_B) \
|
||||
| (cast (ut32, c) << POS_C))
|
||||
|
||||
#define CREATE_ABx(o, a, bc) ((cast (ut32, o) << POS_OP)\
|
||||
| (cast (ut32, a) << POS_A)\
|
||||
| (cast (ut32, bc) << POS_Bx))
|
||||
|
||||
#define CREATE_Ax(o, a) ((cast (ut32) << POS_OP)\
|
||||
| (cast (ut32, a) << POS_Ax))
|
||||
|
||||
|
||||
|
||||
typedef enum {
|
||||
/*----------------------------------------------------------------------
|
||||
name args description
|
||||
------------------------------------------------------------------------*/
|
||||
OP_MOVE,/* A B R(A) := R(B) */
|
||||
OP_LOADK,/* A Bx R(A) := Kst(Bx) */
|
||||
OP_LOADKX, /* A R(A) := Kst(extra arg) */
|
||||
OP_LOADBOOL, /* A B C R(A) := (Bool)B; if (C) pc++ */
|
||||
OP_LOADNIL, /* A B R(A), R(A+1), ..., R(A+B) := nil */
|
||||
OP_GETUPVAL, /* A B R(A) := UpValue[B] */
|
||||
|
||||
OP_GETTABUP, /* A B C R(A) := UpValue[B][RK(C)] */
|
||||
OP_GETTABLE, /* A B C R(A) := R(B)[RK(C)] */
|
||||
|
||||
OP_SETTABUP, /* A B C UpValue[A][RK(B)] := RK(C) */
|
||||
OP_SETUPVAL, /* A B UpValue[B] := R(A) */
|
||||
OP_SETTABLE, /* A B C R(A)[RK(B)] := RK(C) */
|
||||
|
||||
OP_NEWTABLE, /* A B C R(A) := {} (size = B,C) */
|
||||
|
||||
OP_SELF,/* A B C R(A+1) := R(B); R(A) := R(B)[RK(C)] */
|
||||
|
||||
OP_ADD, /* A B C R(A) := RK(B) + RK(C) */
|
||||
OP_SUB, /* A B C R(A) := RK(B) - RK(C) */
|
||||
OP_MUL, /* A B C R(A) := RK(B) * RK(C) */
|
||||
OP_MOD, /* A B C R(A) := RK(B) % RK(C) */
|
||||
OP_POW, /* A B C R(A) := RK(B) ^ RK(C) */
|
||||
OP_DIV, /* A B C R(A) := RK(B) / RK(C) */
|
||||
OP_IDIV,/* A B C R(A) := RK(B) // RK(C) */
|
||||
OP_BAND,/* A B C R(A) := RK(B) & RK(C) */
|
||||
OP_BOR, /* A B C R(A) := RK(B) | RK(C) */
|
||||
OP_BXOR,/* A B C R(A) := RK(B) ~ RK(C) */
|
||||
OP_SHL, /* A B C R(A) := RK(B) << RK(C) */
|
||||
OP_SHR, /* A B C R(A) := RK(B) >> RK(C) */
|
||||
OP_UNM, /* A B R(A) := -R(B) */
|
||||
OP_BNOT,/* A B R(A) := ~R(B) */
|
||||
OP_NOT, /* A B R(A) := not R(B) */
|
||||
OP_LEN, /* A B R(A) := length of R(B) */
|
||||
|
||||
OP_CONCAT, /* A B C R(A) := R(B).. ... ..R(C) */
|
||||
|
||||
OP_JMP, /* A sBx pc+=sBx; if (A) close all upvalues >= R(A - 1) */
|
||||
OP_EQ, /* A B C if ((RK(B) == RK(C)) ~= A) then pc++ */
|
||||
OP_LT, /* A B C if ((RK(B) < RK(C)) ~= A) then pc++ */
|
||||
OP_LE, /* A B C if ((RK(B) <= RK(C)) ~= A) then pc++ */
|
||||
|
||||
OP_TEST,/* A C if not (R(A) <=> C) then pc++ */
|
||||
OP_TESTSET, /* A B C if (R(B) <=> C) then R(A) := R(B) else pc++ */
|
||||
|
||||
OP_CALL,/* A B C R(A), ... ,R(A+C-2) := R(A)(R(A+1), ... ,R(A+B-1)) */
|
||||
OP_TAILCALL, /* A B C return R(A)(R(A+1), ... ,R(A+B-1)) */
|
||||
OP_RETURN, /* A B return R(A), ... ,R(A+B-2) (see note) */
|
||||
|
||||
OP_FORLOOP, /* A sBx R(A)+=R(A+2);
|
||||
if R(A) <?= R(A+1) then { pc+=sBx; R(A+3)=R(A) }*/
|
||||
OP_FORPREP, /* A sBx R(A)-=R(A+2); pc+=sBx */
|
||||
|
||||
OP_TFORCALL, /* A C R(A+3), ... ,R(A+2+C) := R(A)(R(A+1), R(A+2)); */
|
||||
OP_TFORLOOP, /* A sBx if R(A+1) ~= nil then { R(A)=R(A+1); pc += sBx }*/
|
||||
|
||||
OP_SETLIST, /* A B C R(A)[(C-1)*FPF+i] := R(A+i), 1 <= i <= B */
|
||||
|
||||
OP_CLOSURE, /* A Bx R(A) := closure(KPROTO[Bx]) */
|
||||
|
||||
OP_VARARG, /* A B R(A), R(A+1), ..., R(A+B-2) = vararg */
|
||||
|
||||
OP_EXTRAARG /* Ax extra (larger) argument for previous opcode */
|
||||
} OpCode;
|
||||
|
||||
#endif
|
10
libr/arch/p/lua/lua53.mk
Normal file
10
libr/arch/p/lua/lua53.mk
Normal file
@ -0,0 +1,10 @@
|
||||
OBJ_LUA53=anal_lua53.o
|
||||
|
||||
STATIC_OBJ+=$(OBJ_LUA53)
|
||||
TARGET_LUA53=anal_lua53.${EXT_SO}
|
||||
|
||||
ALL_TARGETS+=${TARGET_LUA53}
|
||||
|
||||
${TARGET_LUA53}: ${OBJ_LUA53}
|
||||
${CC} $(LDFLAGS) ${CFLAGS} $(call libname,anal_lua53) $(CS_CFLAGS) \
|
||||
-o anal_lua53.${EXT_SO} ${OBJ_LUA53} $(CS_LDFLAGS)
|
441
libr/arch/p/lua/lua53_parser.c
Normal file
441
libr/arch/p/lua/lua53_parser.c
Normal file
@ -0,0 +1,441 @@
|
||||
/* radare - LGPL - Copyright 2009-2022 - pancake */
|
||||
|
||||
#include <r_types.h>
|
||||
|
||||
// XXX global
|
||||
// R_UNUSED static RList *lua53_function_list = NULL;
|
||||
|
||||
struct {
|
||||
int intSize;
|
||||
int sizeSize;
|
||||
int instructionSize;
|
||||
int luaIntSize;
|
||||
int luaNumberSize;
|
||||
RList *functionList;
|
||||
} lua53_data;
|
||||
|
||||
static ut64 parseNumber(const ut8 *data, ut64 bytesize){
|
||||
int i;
|
||||
ut64 res = 0;
|
||||
for (i = 0; i < bytesize; i++) {
|
||||
res |= ((ut64) data[i]) << (8 * i);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
#define parseInt(data) parseNumber (data, lua53_data.intSize)
|
||||
#define parseSize(data) parseNumber (data, lua53_data.sizeSize)
|
||||
#define parseInstruction(data) parseNumber (data, lua53_data.instructionSize)
|
||||
#define parseLuaInt(data) parseNumber (data, lua53_data.luaIntSize)
|
||||
#define parseLuaNumber(data) parseNumber (data, lua53_data.luaNumberSize)
|
||||
|
||||
typedef struct lua_function {
|
||||
ut64 offset;
|
||||
|
||||
char *name_ptr; // only valid in onFunction methon
|
||||
ut64 name_size;
|
||||
|
||||
ut64 lineDefined;
|
||||
ut64 lastLineDefined;
|
||||
ut8 numParams;
|
||||
ut8 isVarArg;
|
||||
ut8 maxStackSize;
|
||||
|
||||
struct lua_function *parent_func;// if != NULL, should always be valid
|
||||
|
||||
ut64 const_size;
|
||||
ut64 code_size;
|
||||
ut64 upvalue_size;
|
||||
ut64 protos_size;
|
||||
|
||||
ut64 const_offset;
|
||||
ut64 code_offset;
|
||||
ut64 upvalue_offset;
|
||||
ut64 protos_offset;
|
||||
ut64 debug_offset;
|
||||
|
||||
ut64 size;
|
||||
} LuaFunction;
|
||||
|
||||
typedef struct lua_parse_struct ParseStruct;
|
||||
typedef void (*OnFunction) (LuaFunction *function, struct lua_parse_struct *parseStruct);
|
||||
typedef void (*OnString) (const ut8 *data, ut64 offset, ut64 size, struct lua_parse_struct *parseStruct);
|
||||
typedef void (*OnConst) (const ut8 *data, ut64 offset, ut64 size, struct lua_parse_struct *parseStruct);
|
||||
|
||||
typedef struct lua_parse_struct {
|
||||
OnString onString;
|
||||
OnFunction onFunction;
|
||||
OnConst onConst;
|
||||
void *data;
|
||||
} ParseStruct;
|
||||
|
||||
LuaFunction *lua53findLuaFunctionByCodeAddr(ut64 addr){
|
||||
if (!lua53_data.functionList) {
|
||||
return NULL;
|
||||
}
|
||||
LuaFunction *function = NULL;
|
||||
RListIter *iter = NULL;
|
||||
r_list_foreach (lua53_data.functionList, iter, function) {
|
||||
if (function->code_offset + lua53_data.intSize <= addr && addr < function->const_offset) {
|
||||
return function;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int storeLuaFunction(LuaFunction *function){
|
||||
if (!lua53_data.functionList) {
|
||||
lua53_data.functionList = r_list_new ();
|
||||
if (!lua53_data.functionList) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
r_list_append (lua53_data.functionList, function);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static LuaFunction *findLuaFunction(ut64 addr){
|
||||
if (!lua53_data.functionList) {
|
||||
return NULL;
|
||||
}
|
||||
LuaFunction *function = NULL;
|
||||
RListIter *iter = NULL;
|
||||
r_list_foreach (lua53_data.functionList, iter, function) {
|
||||
R_LOG_DEBUG ("Search 0x%"PFMT64x, function->offset);
|
||||
if (function->offset == addr) {
|
||||
return function;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ut64 lua53parseHeader(const ut8 *data, ut64 offset, const ut64 size, ParseStruct *parseStruct);
|
||||
ut64 lua53parseFunction(const ut8 *data, ut64 offset, const ut64 size, LuaFunction *parent_func, ParseStruct *parseStruct);
|
||||
|
||||
static ut64 parseString(const ut8 *data, ut64 offset, const ut64 size, ParseStruct *parseStruct);
|
||||
static ut64 parseStringR(const ut8 *data, ut64 offset, const ut64 size, char **str_ptr, ut64 *str_len, ParseStruct *parseStruct);
|
||||
static ut64 parseCode(const ut8 *data, ut64 offset, const ut64 size, ParseStruct *parseStruct);
|
||||
static ut64 parseConstants(const ut8 *data, ut64 offset, const ut64 size, ParseStruct *parseStruct);
|
||||
static ut64 parseUpvalues(const ut8 *data, ut64 offset, const ut64 size, ParseStruct *parseStruct);
|
||||
static ut64 parseProtos(const ut8 *data, ut64 offset, const ut64 size, LuaFunction *func, ParseStruct *parseStruct);
|
||||
static ut64 parseDebug(const ut8 *data, ut64 offset, const ut64 size, ParseStruct *parseStruct);
|
||||
|
||||
ut64 lua53parseHeader(const ut8 *data, ut64 offset, const ut64 size, ParseStruct *parseStruct) {
|
||||
if (data && offset + 16 <= size && !memcmp (data + offset, "\x1bLua", 4)) { // check the header
|
||||
offset += 4;
|
||||
if (data[offset + 0] != '\x53') {// check version
|
||||
return 0;
|
||||
}
|
||||
// skip format byte
|
||||
offset += 2;
|
||||
if (memcmp (data + offset, "\x19\x93\r\n\x1a\n", 6)) { // for version 5.3
|
||||
return 0;
|
||||
}
|
||||
offset += 6;
|
||||
lua53_data.intSize = data[offset + 0];
|
||||
lua53_data.sizeSize = data[offset + 1];
|
||||
lua53_data.instructionSize = data[offset + 2];
|
||||
lua53_data.luaIntSize = data[offset + 3];
|
||||
lua53_data.luaNumberSize = data[offset + 4];
|
||||
|
||||
R_LOG_DEBUG ("Int Size: %i", lua53_data.intSize);
|
||||
R_LOG_DEBUG ("Size Size: %i", lua53_data.sizeSize);
|
||||
R_LOG_DEBUG ("Instruction Size: %i", lua53_data.instructionSize);
|
||||
R_LOG_DEBUG ("Lua Int Size: %i", lua53_data.luaIntSize);
|
||||
R_LOG_DEBUG ("Lua Number Size: %i", lua53_data.luaNumberSize);
|
||||
|
||||
offset += 5;
|
||||
if (offset + lua53_data.luaIntSize + lua53_data.luaNumberSize >= size) {// check again the remainingsize because an int and number is appended to the header
|
||||
return 0;
|
||||
}
|
||||
if (parseLuaInt (data + offset) != 0x5678) { // check the appended integer
|
||||
return 0;
|
||||
}
|
||||
offset += lua53_data.luaIntSize;
|
||||
ut64 num = parseLuaNumber (data + offset);
|
||||
double d = 0;
|
||||
memcpy (&d, &num, sizeof (double));
|
||||
// if (*((double *) &num) != 370.5) { // check the appended number
|
||||
if (d != 370.5) { // check the appended number
|
||||
return 0;
|
||||
}
|
||||
offset += lua53_data.luaNumberSize;
|
||||
R_LOG_DEBUG ("Is a Lua Binary");
|
||||
return offset;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
ut64 lua53parseFunction(const ut8 *data, ut64 offset, const ut64 size, LuaFunction *parent_func, ParseStruct *parseStruct){
|
||||
R_LOG_DEBUG ("Function 0x%"PFMT64x, offset);
|
||||
LuaFunction *function = findLuaFunction (offset);
|
||||
if (function) { // if a function object was cached
|
||||
R_LOG_DEBUG ("Found cached Functione: 0x%"PFMT64x, function->offset);
|
||||
|
||||
if (parseStruct != NULL && parseStruct->onString != NULL) {
|
||||
parseConstants (data, function->const_offset, size, parseStruct);
|
||||
}
|
||||
|
||||
parseProtos (data, function->protos_offset, size, function, parseStruct);
|
||||
|
||||
if (parseStruct != NULL && parseStruct->onString != NULL) {
|
||||
parseDebug (data, function->debug_offset, size, parseStruct);
|
||||
}
|
||||
|
||||
if (parseStruct != NULL && parseStruct->onFunction != NULL) {
|
||||
parseStruct->onFunction (function, parseStruct);
|
||||
}
|
||||
return offset + function->size;
|
||||
} else {
|
||||
ut64 baseoffset = offset;
|
||||
|
||||
function = R_NEW0 (LuaFunction);
|
||||
function->parent_func = parent_func;
|
||||
function->offset = offset;
|
||||
offset = parseStringR (data, offset, size, &function->name_ptr, &function->name_size, parseStruct);
|
||||
if (offset == 0) {
|
||||
free (function);
|
||||
return 0;
|
||||
}
|
||||
|
||||
function->lineDefined = parseInt (data + offset);
|
||||
R_LOG_DEBUG ("Line Defined: %"PFMT64x, function->lineDefined);
|
||||
function->lastLineDefined = parseInt (data + offset + lua53_data.intSize);
|
||||
R_LOG_DEBUG ("Last Line Defined: %"PFMT64x, function->lastLineDefined);
|
||||
offset += lua53_data.intSize * 2;
|
||||
function->numParams = data[offset + 0];
|
||||
R_LOG_DEBUG ("Param Count: %d", function->numParams);
|
||||
function->isVarArg = data[offset + 1];
|
||||
R_LOG_DEBUG ("Is VarArgs: %d", function->isVarArg);
|
||||
function->maxStackSize = data[offset + 2];
|
||||
R_LOG_DEBUG ("Max Stack Size: %d", function->maxStackSize);
|
||||
offset += 3;
|
||||
|
||||
function->code_offset = offset;
|
||||
function->code_size = parseInt (data + offset);
|
||||
offset = parseCode (data, offset, size, parseStruct);
|
||||
if (offset == 0) {
|
||||
free (function);
|
||||
return 0;
|
||||
}
|
||||
function->const_offset = offset;
|
||||
function->const_size = parseInt (data + offset);
|
||||
offset = parseConstants (data, offset, size, parseStruct);
|
||||
if (offset == 0) {
|
||||
free (function);
|
||||
return 0;
|
||||
}
|
||||
function->upvalue_offset = offset;
|
||||
function->upvalue_size = parseInt (data + offset);
|
||||
offset = parseUpvalues (data, offset, size, parseStruct);
|
||||
if (offset == 0) {
|
||||
free (function);
|
||||
return 0;
|
||||
}
|
||||
function->protos_offset = offset;
|
||||
function->protos_size = parseInt (data + offset);
|
||||
offset = parseProtos (data, offset, size, function, parseStruct);
|
||||
if (offset == 0) {
|
||||
free (function);
|
||||
return 0;
|
||||
}
|
||||
function->debug_offset = offset;
|
||||
offset = parseDebug (data, offset, size, parseStruct);
|
||||
if (offset == 0) {
|
||||
free (function);
|
||||
return 0;
|
||||
}
|
||||
|
||||
function->size = offset - baseoffset;
|
||||
if (parseStruct && parseStruct->onFunction) {
|
||||
parseStruct->onFunction (function, parseStruct);
|
||||
}
|
||||
if (!storeLuaFunction (function)) {
|
||||
free (function);
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
}
|
||||
|
||||
static ut64 parseCode(const ut8 *data, ut64 offset, const ut64 size, ParseStruct *parseStruct){
|
||||
if (offset + lua53_data.intSize >= size) {
|
||||
return 0;
|
||||
}
|
||||
ut64 length = parseInt (data + offset);
|
||||
offset += lua53_data.intSize;
|
||||
|
||||
if (offset + length * lua53_data.instructionSize >= size) {
|
||||
return 0;
|
||||
}
|
||||
R_LOG_DEBUG ("Function has %"PFMT64x " Instructions", length);
|
||||
|
||||
return offset + length * lua53_data.instructionSize;
|
||||
}
|
||||
|
||||
static ut64 parseConstants(const ut8 *data, ut64 offset, const ut64 size, ParseStruct *parseStruct){
|
||||
if (offset + lua53_data.intSize >= size) {
|
||||
return 0;
|
||||
}
|
||||
ut64 length = parseInt (data + offset);
|
||||
offset += lua53_data.intSize;
|
||||
R_LOG_DEBUG ("Function has %"PFMT64x " Constants", length);
|
||||
|
||||
int i;
|
||||
for (i = 0; i < length; i++) {
|
||||
R_LOG_DEBUG ("%d: ", i);
|
||||
ut8 type = data[offset + 0];
|
||||
offset += 1;
|
||||
switch (type) {
|
||||
case 0: // Nil
|
||||
R_LOG_DEBUG ("Nil");
|
||||
break;
|
||||
case 1: // Boolean
|
||||
R_LOG_DEBUG ("Boolean %d", data[offset + 0]);
|
||||
offset += 1;
|
||||
break;
|
||||
case (3 | (0 << 4)): // Number
|
||||
{
|
||||
#ifdef LUA_DEBUG
|
||||
ut64 num = parseLuaNumber (data + offset);
|
||||
R_LOG_DEBUG ("Number %f", *((double *) &num));
|
||||
#endif
|
||||
offset += lua53_data.luaNumberSize;
|
||||
}
|
||||
break;
|
||||
case (3 | (1 << 4)): // Integer
|
||||
R_LOG_DEBUG ("Integer %"PFMT64x, parseLuaInt (data + offset));
|
||||
offset += lua53_data.luaIntSize;
|
||||
break;
|
||||
case (4 | (0 << 4)): // Short String
|
||||
case (4 | (1 << 4)): // Long String
|
||||
offset = parseString (data, offset, size, parseStruct);
|
||||
break;
|
||||
default:
|
||||
R_LOG_DEBUG ("Invalid");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
static ut64 parseUpvalues(const ut8 *data, ut64 offset, const ut64 size, ParseStruct *parseStruct){
|
||||
if (offset + lua53_data.intSize >= size) {
|
||||
return 0;
|
||||
}
|
||||
ut64 length = parseInt (data + offset);
|
||||
offset += lua53_data.intSize;
|
||||
|
||||
R_LOG_DEBUG ("Function has %"PFMT64x " Upvalues", length);
|
||||
|
||||
int i;
|
||||
for (i = 0; i < length; i++) {
|
||||
R_LOG_DEBUG ("%d: inStack: %d id: %d", i, data[offset + 0], data[offset + 1]);
|
||||
offset += 2;
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
static ut64 parseProtos(const ut8 *data, ut64 offset, const ut64 size, LuaFunction *func, ParseStruct *parseStruct){
|
||||
if (offset + lua53_data.intSize >= size) {
|
||||
return 0;
|
||||
}
|
||||
ut64 length = parseInt (data + offset);
|
||||
offset += lua53_data.intSize;
|
||||
R_LOG_DEBUG ("Function has %"PFMT64x " Prototypes", length);
|
||||
|
||||
int i;
|
||||
for (i = 0; i < length; i++) {
|
||||
offset = lua53parseFunction (data, offset, size, func, parseStruct);
|
||||
if (offset == 0) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
static ut64 parseDebug(const ut8 *data, ut64 offset, const ut64 size, ParseStruct *parseStruct){
|
||||
if (offset + lua53_data.intSize >= size) {
|
||||
return 0;
|
||||
}
|
||||
ut64 length = parseInt (data + offset);
|
||||
offset += lua53_data.intSize;
|
||||
|
||||
if (length != 0) {
|
||||
R_LOG_DEBUG ("Instruction-Line Mappings %"PFMT64x, length);
|
||||
if (offset + lua53_data.intSize * length >= size) {
|
||||
return 0;
|
||||
}
|
||||
int i;
|
||||
for (i = 0; i < length; i++) {
|
||||
R_LOG_DEBUG ("Instruction %d Line %"PFMT64x, i, parseInt (data + offset));
|
||||
offset += lua53_data.intSize;
|
||||
}
|
||||
}
|
||||
if (offset + lua53_data.intSize >= size) {
|
||||
return 0;
|
||||
}
|
||||
length = parseInt (data + offset);
|
||||
offset += lua53_data.intSize;
|
||||
if (length != 0) {
|
||||
R_LOG_DEBUG ("LiveRanges: %"PFMT64x, length);
|
||||
int i;
|
||||
for (i = 0; i < length; i++) {
|
||||
R_LOG_DEBUG ("LiveRange %d:", i);
|
||||
offset = parseString (data, offset, size, parseStruct);
|
||||
if (offset == 0) {
|
||||
return 0;
|
||||
}
|
||||
#ifdef LUA_DEBUG
|
||||
ut64 num1 = parseInt (data + offset);
|
||||
#endif
|
||||
offset += lua53_data.intSize;
|
||||
#ifdef LUA_DEBUG
|
||||
ut64 num2 = parseInt (data + offset);
|
||||
#endif
|
||||
offset += lua53_data.intSize;
|
||||
}
|
||||
}
|
||||
if (offset + lua53_data.intSize >= size) {
|
||||
return 0;
|
||||
}
|
||||
length = parseInt (data + offset);
|
||||
offset += lua53_data.intSize;
|
||||
if (length != 0) {
|
||||
R_LOG_DEBUG ("Up-Values: %"PFMT64x, length);
|
||||
int i;
|
||||
for (i = 0; i < length; i++) {
|
||||
R_LOG_DEBUG ("Up-Value %d:", i);
|
||||
offset = parseString (data, offset, size, parseStruct);
|
||||
if (offset == 0) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
static ut64 parseString(const ut8 *data, ut64 offset, const ut64 size, ParseStruct *parseStruct){
|
||||
return parseStringR (data, offset, size, 0, 0, parseStruct);
|
||||
}
|
||||
|
||||
static ut64 parseStringR(const ut8 *data, ut64 offset, const ut64 size, char **str_ptr, ut64 *str_len, ParseStruct *parseStruct){
|
||||
ut64 functionNameSize = data[offset + 0];
|
||||
offset += 1;
|
||||
if (functionNameSize == 0xFF) {
|
||||
functionNameSize = parseSize (data + offset);
|
||||
offset += lua53_data.sizeSize;
|
||||
}
|
||||
if (functionNameSize != 0) {
|
||||
if (str_ptr) {
|
||||
*str_ptr = (char *) data + offset;
|
||||
}
|
||||
if (str_len) {
|
||||
*str_len = functionNameSize - 1;
|
||||
}
|
||||
if (parseStruct && parseStruct->onString) {
|
||||
parseStruct->onString (data, offset, functionNameSize - 1, parseStruct);
|
||||
}
|
||||
R_LOG_DEBUG ("String %.*s", (int) (functionNameSize - 1), data + offset);
|
||||
offset += functionNameSize - 1;
|
||||
}
|
||||
return offset;
|
||||
}
|
379
libr/bin/p/bin_lua.c
Normal file
379
libr/bin/p/bin_lua.c
Normal file
@ -0,0 +1,379 @@
|
||||
/* radare - LGPL - Copyright 2009-2022 - pancake */
|
||||
|
||||
#include <r_types.h>
|
||||
#include <r_util.h>
|
||||
#include <r_lib.h>
|
||||
#include <r_bin.h>
|
||||
|
||||
#include "../arch/p/lua/lua53_parser.c"
|
||||
|
||||
#if 0
|
||||
static int finit(void *user) {
|
||||
if (lua53_data.functionList) {
|
||||
r_list_free (lua53_data.functionList);
|
||||
lua53_data.functionList = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool check_bytes(const ut8 *buf, ut64 length);
|
||||
|
||||
static bool check(RBinFile *bf) {
|
||||
Dprintf ("Check\n");
|
||||
const ut8 *bytes = bf? r_buf_buffer (bf->buf): NULL;
|
||||
ut64 sz = bf? r_buf_size (bf->buf): 0;
|
||||
return check_bytes (bytes, sz);
|
||||
}
|
||||
#endif
|
||||
|
||||
static bool check_buffer(RBinFile *bf, RBuffer *b) {
|
||||
if (r_buf_size (b) > 4) {
|
||||
ut8 buf[4];
|
||||
r_buf_read_at (b, 0, buf, sizeof (buf));
|
||||
if (!memcmp (buf, "\x1b\x4c\x75\x61", 4)) {
|
||||
return true;
|
||||
}
|
||||
#if 0
|
||||
ParseStruct parseStruct;
|
||||
ut64 parsedbytes = lua53parseHeader (buf, 0, sizeof (buf), &parseStruct);
|
||||
return parsedbytes != 0;
|
||||
#endif
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool load_buffer(RBinFile *bf, void **bin_obj, RBuffer *b, ut64 loadaddr, Sdb *sdb) {
|
||||
ut8 *buf = malloc (bf->size);
|
||||
if (!buf) {
|
||||
R_LOG_ERROR ("cannot malloc filesize");
|
||||
return false;
|
||||
}
|
||||
r_buf_read_at (b, 0, buf, bf->size);
|
||||
ParseStruct parseStruct;
|
||||
ut64 parsedbytes = lua53parseHeader (buf, 0, sizeof (buf), &parseStruct);
|
||||
free (buf);
|
||||
// eprintf ("PAr %"PFMT64d"\n", parsedbytes);
|
||||
return true;
|
||||
return parsedbytes != 0;
|
||||
#if 0
|
||||
const ut8 *bytes = bf? r_buf_buffer (bf->buf): NULL;
|
||||
ut64 sz = bf? r_buf_size (bf->buf): 0;
|
||||
return check_bytes (bytes, sz);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void addSection(RList *list, const char *name, ut64 addr, ut32 size, bool isFunc) {
|
||||
RBinSection *bs = R_NEW0 (RBinSection);
|
||||
if (!bs) {
|
||||
return;
|
||||
}
|
||||
|
||||
bs->name = strdup (name);
|
||||
bs->vaddr = bs->paddr = addr;
|
||||
bs->size = bs->vsize = size;
|
||||
bs->add = true;
|
||||
bs->is_data = false;
|
||||
bs->bits = isFunc? 8 * lua53_data.instructionSize: 8;
|
||||
if (bs->bits == 0) {
|
||||
bs->bits = 32;
|
||||
}
|
||||
bs->has_strings = !isFunc;
|
||||
bs->arch = strdup ("lua"); // maybe add bs->cpu or use : to separate arch:cpu
|
||||
// bs->cpu = strdup ("5.4"); // maybe add bs->cpu or use : to separate arch:cpu
|
||||
if (isFunc) {
|
||||
bs->perm = R_PERM_RX;
|
||||
} else {
|
||||
bs->perm = R_PERM_R;
|
||||
}
|
||||
bs->is_segment = true;
|
||||
r_list_append (list, bs);
|
||||
}
|
||||
|
||||
static void addSections(LuaFunction *func, ParseStruct *parseStruct){
|
||||
char *string;
|
||||
if (func->name_size == 0 || func->name_ptr == 0) {
|
||||
string = r_str_newf ("0x%"PFMT64x, func->offset);
|
||||
} else {
|
||||
string = r_str_ndup (func->name_ptr, func->name_size);
|
||||
}
|
||||
|
||||
char sb[R_BIN_SIZEOF_STRINGS + 1];
|
||||
|
||||
snprintf (sb, sizeof (sb), "header.%s", string);
|
||||
addSection (parseStruct->data, sb, func->offset, func->code_offset - func->offset, false);
|
||||
|
||||
snprintf (sb, sizeof (sb), "code.%s", string);
|
||||
addSection (parseStruct->data, sb, func->code_offset, func->const_offset - func->code_offset, true); // code section also holds codesize
|
||||
|
||||
snprintf (sb, sizeof (sb), "consts.%s", string);
|
||||
addSection (parseStruct->data, sb, func->const_offset, func->upvalue_offset - func->const_offset, false);
|
||||
|
||||
snprintf (sb, sizeof (sb), "upvalues.%s", string);
|
||||
addSection (parseStruct->data, sb, func->upvalue_offset, func->protos_offset - func->upvalue_offset, false);
|
||||
|
||||
snprintf (sb, sizeof (sb), "debuginfo.%s", string);
|
||||
addSection (parseStruct->data, sb, func->debug_offset, func->offset + func->size - func->debug_offset, false);
|
||||
|
||||
free (string);
|
||||
}
|
||||
|
||||
static RList *sections(RBinFile *bf) {
|
||||
ParseStruct parseStruct = {0};
|
||||
if (!bf) {
|
||||
return NULL;
|
||||
}
|
||||
#if 1
|
||||
ut8 *bytes = malloc (bf->size);
|
||||
if (!bytes) {
|
||||
return NULL;
|
||||
}
|
||||
r_buf_read_at (bf->buf, 0, bytes, bf->size);
|
||||
ut64 sz = bf? r_buf_size (bf->buf): 0;
|
||||
|
||||
memset (&parseStruct, 0, sizeof (parseStruct));
|
||||
parseStruct.onFunction = addSections;
|
||||
|
||||
parseStruct.data = r_list_newf ((RListFree) free);
|
||||
if (!parseStruct.data) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ut64 headersize = 4 + 1 + 1 + 6 + 5 + bytes[15] + bytes[16] + 1;// header + version + format + stringterminators + sizes + integer + number + upvalues
|
||||
addSection (parseStruct.data, "lua-header", 0, headersize, false);
|
||||
|
||||
// parse functions
|
||||
lua53parseFunction (bytes, headersize, sz, 0, &parseStruct);
|
||||
#endif
|
||||
return parseStruct.data;
|
||||
}
|
||||
|
||||
static void addString(const ut8 *buf, ut64 offset, ut64 length, ParseStruct *parseStruct){
|
||||
RBinString *binstring = R_NEW0 (RBinString);
|
||||
|
||||
if (binstring == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
binstring->string = r_str_newlen ((char *) buf + offset, length);
|
||||
binstring->vaddr = binstring->paddr = offset;
|
||||
binstring->ordinal = 0;
|
||||
binstring->size = length;
|
||||
binstring->length = length;
|
||||
r_list_append (parseStruct->data, binstring);
|
||||
}
|
||||
|
||||
static void addSymbol(RList *list, char *name, ut64 addr, ut32 size, const char *type) {
|
||||
RBinSymbol *binSymbol = R_NEW0 (RBinSymbol);
|
||||
if (binSymbol) {
|
||||
binSymbol->name = strdup (name);
|
||||
if (!binSymbol->name) {
|
||||
free (binSymbol);
|
||||
return;
|
||||
}
|
||||
binSymbol->vaddr = binSymbol->paddr = addr;
|
||||
binSymbol->size = size;
|
||||
binSymbol->ordinal = 0;
|
||||
binSymbol->type = type;
|
||||
r_list_append (list, binSymbol);
|
||||
}
|
||||
}
|
||||
|
||||
static void handleFuncSymbol(LuaFunction *func, ParseStruct *parseStruct){
|
||||
RBinSymbol *binSymbol = R_NEW0 (RBinSymbol);
|
||||
|
||||
if (!binSymbol) {
|
||||
return;
|
||||
}
|
||||
|
||||
char *string;
|
||||
if (!func->name_ptr || !func->name_size) {
|
||||
string = malloc (11);
|
||||
sprintf (string, "0x%"PFMT64x, func->offset);
|
||||
} else {
|
||||
string = malloc (func->name_size + 1);
|
||||
memcpy (string, func->name_ptr, func->name_size);
|
||||
int i;
|
||||
for (i = 0; i < func->name_size; i++) {
|
||||
if (string[i] == '@') {
|
||||
string[i] = '_';
|
||||
}
|
||||
}
|
||||
string[func->name_size] = '\0';
|
||||
}
|
||||
char sb[R_BIN_SIZEOF_STRINGS + 1];
|
||||
snprintf (sb, sizeof (sb), "lineDefined.%s", string);
|
||||
addSymbol (parseStruct->data, sb, func->code_offset - 3 - 2 * lua53_data.intSize, lua53_data.intSize, "NUM");
|
||||
snprintf (sb, sizeof (sb), "lastLineDefined.%s", string);
|
||||
addSymbol (parseStruct->data, sb, func->code_offset - 3 - lua53_data.intSize, lua53_data.intSize, "NUM");
|
||||
snprintf (sb, sizeof (sb), "numParams.%s", string);
|
||||
addSymbol (parseStruct->data, sb, func->code_offset - 3, 1, "NUM");
|
||||
snprintf (sb, sizeof (sb), "isVarArg.%s", string);
|
||||
addSymbol (parseStruct->data, sb, func->code_offset - 2, 1, "BOOL");
|
||||
snprintf (sb, sizeof (sb), "maxStackSize.%s", string);
|
||||
addSymbol (parseStruct->data, sb, func->code_offset - 1, 1, "BOOL");
|
||||
|
||||
snprintf (sb, sizeof (sb), "codesize.%s", string);
|
||||
addSymbol (parseStruct->data, sb, func->code_offset, lua53_data.intSize, "NUM");
|
||||
|
||||
snprintf (sb, sizeof (sb), "func.%s", string);
|
||||
addSymbol (parseStruct->data, sb, func->code_offset + lua53_data.intSize, lua53_data.instructionSize * func->code_size, "FUNC");
|
||||
|
||||
snprintf (sb, sizeof (sb), "constsize.%s", string);
|
||||
addSymbol (parseStruct->data, sb, func->const_offset, lua53_data.intSize, "NUM");
|
||||
|
||||
snprintf (sb, sizeof (sb), "upvaluesize.%s", string);
|
||||
addSymbol (parseStruct->data, sb, func->upvalue_offset, lua53_data.intSize, "NUM");
|
||||
|
||||
snprintf (sb, sizeof (sb), "prototypesize.%s", string);
|
||||
addSymbol (parseStruct->data, sb, func->protos_offset, lua53_data.intSize, "NUM");
|
||||
|
||||
free (string);
|
||||
}
|
||||
|
||||
static RList *strings(RBinFile *bf) {
|
||||
ut8 *bytes = malloc (bf->size);
|
||||
if (bytes) {
|
||||
r_buf_read_at (bf->buf, 0, bytes, bf->size);
|
||||
}
|
||||
|
||||
ut64 headersize = 4 + 1 + 1 + 6 + 5 + bytes[15] + bytes[16] + 1;// header + version + format + stringterminators + sizes + integer + number + upvalues
|
||||
|
||||
ParseStruct parseStruct;
|
||||
memset (&parseStruct, 0, sizeof (parseStruct));
|
||||
parseStruct.onString = addString;
|
||||
|
||||
parseStruct.data = r_list_new ();
|
||||
if (!parseStruct.data) {
|
||||
free (bytes);
|
||||
return NULL;
|
||||
}
|
||||
lua53parseFunction (bytes, headersize, bf->size, 0, &parseStruct);
|
||||
|
||||
free (bytes);
|
||||
return parseStruct.data;
|
||||
}
|
||||
|
||||
static RList *symbols(RBinFile *bf) {
|
||||
if (!bf) {
|
||||
return NULL;
|
||||
}
|
||||
ut8 *bytes = malloc (bf->size);
|
||||
if (bytes) {
|
||||
r_buf_read_at (bf->buf, 0, bytes, bf->size);
|
||||
}
|
||||
ut64 sz = bf? r_buf_size (bf->buf): 0;
|
||||
ut64 headersize = 4 + 1 + 1 + 6 + 5 + bytes[15] + bytes[16] + 1;
|
||||
// header + version + format + stringterminators + sizes + integer + number + upvalues
|
||||
|
||||
ParseStruct parseStruct = {0};
|
||||
parseStruct.onFunction = handleFuncSymbol;
|
||||
parseStruct.data = NULL;
|
||||
|
||||
RList *list = r_list_new ();
|
||||
parseStruct.data = list;
|
||||
if (!parseStruct.data) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
addSymbol (list, "lua-header", 0, 4, "NOTYPE");
|
||||
addSymbol (list, "lua-version", 4, 1, "NOTYPE");
|
||||
addSymbol (list, "lua-format", 5, 1, "NOTYPE");
|
||||
addSymbol (list, "stringterminators", 6, 6, "NOTYPE");
|
||||
addSymbol (list, "int-size", 12, 1, "NUM");
|
||||
addSymbol (list, "size-size", 13, 1, "NUM");
|
||||
addSymbol (list, "instruction-size", 14, 1, "NUM");
|
||||
addSymbol (list, "lua-int-size", 15, 1, "NUM");
|
||||
addSymbol (list, "lua-number-size", 16, 1, "NUM");
|
||||
addSymbol (list, "check-int", 17, bytes[15], "NUM");
|
||||
addSymbol (list, "check-number", 17 + bytes[15], bytes[16], "FLOAT");
|
||||
addSymbol (list, "upvalues", 17 + bytes[15] + bytes[16], 1, "NUM");
|
||||
|
||||
lua53parseFunction (bytes, headersize, sz, 0, &parseStruct);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
static RBinInfo *info(RBinFile *bf) {
|
||||
RBinInfo *ret = NULL;
|
||||
if (!(ret = R_NEW0 (RBinInfo))) {
|
||||
return NULL;
|
||||
}
|
||||
ret->file = strdup (bf->file);
|
||||
ret->type = strdup ("lua");
|
||||
ret->os = strdup ("any");
|
||||
ret->machine = strdup ("LUA 5.3 VM");
|
||||
ret->arch = strdup ("lua");
|
||||
ret->bits = lua53_data.instructionSize * 8;
|
||||
if (ret->bits < 1) {
|
||||
ret->bits = 32;
|
||||
} else if (ret->bits != 32) {
|
||||
R_LOG_WARN ("lua vm using %d bits registers is not well tested", ret->bits);
|
||||
}
|
||||
ret->has_va = true;
|
||||
ret->big_endian = false;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void addEntry(LuaFunction *func, ParseStruct *parseStruct){
|
||||
if (!func->parent_func) {
|
||||
RBinAddr *ptr = NULL;
|
||||
if ((ptr = R_NEW0 (RBinAddr))) {
|
||||
ptr->paddr = ptr->vaddr = func->code_offset + lua53_data.intSize;
|
||||
r_list_append (parseStruct->data, ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static RList *entries(RBinFile *bf) {
|
||||
r_return_val_if_fail (bf, NULL);
|
||||
if (bf->size < 20) {
|
||||
return NULL;
|
||||
}
|
||||
ut8 *buf = malloc (bf->size);
|
||||
if (!buf) {
|
||||
R_LOG_ERROR ("cannot malloc filesize");
|
||||
return false;
|
||||
}
|
||||
r_buf_read_at (bf->buf, 0, buf, bf->size);
|
||||
|
||||
// header + version + format + stringterminators + sizes + integer + number + upvalues
|
||||
ut64 headersize = 4 + 1 + 1 + 6 + 5 + buf[15] + buf[16] + 1;
|
||||
|
||||
ParseStruct parseStruct;
|
||||
memset (&parseStruct, 0, sizeof (parseStruct));
|
||||
parseStruct.onFunction = addEntry;
|
||||
parseStruct.data = NULL;
|
||||
|
||||
parseStruct.data = r_list_new ();
|
||||
if (!parseStruct.data) {
|
||||
free (buf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
lua53parseFunction (buf, headersize, bf->size, 0, &parseStruct);
|
||||
|
||||
free (buf);
|
||||
return parseStruct.data;
|
||||
}
|
||||
|
||||
RBinPlugin r_bin_plugin_lua = {
|
||||
.name = "lua",
|
||||
.desc = "Compiled LUA bin plugin (lua 5.3)",
|
||||
.license = "MIT",
|
||||
.author = "pancake",
|
||||
.sections = §ions,
|
||||
.load_buffer = &load_buffer,
|
||||
.check_buffer = &check_buffer,
|
||||
.symbols = &symbols,
|
||||
.strings = &strings,
|
||||
.info = &info,
|
||||
.entries = &entries,
|
||||
};
|
||||
|
||||
#ifndef CORELIB
|
||||
RLibStruct radare_plugin = {
|
||||
.type = R_LIB_TYPE_BIN,
|
||||
.data = &r_bin_plugin_lua,
|
||||
.version = R2_VERSION
|
||||
};
|
||||
#endif
|
10
libr/bin/p/lua.mk
Normal file
10
libr/bin/p/lua.mk
Normal file
@ -0,0 +1,10 @@
|
||||
OBJ_LUA=bin_lua.o
|
||||
|
||||
STATIC_OBJ+=${OBJ_LUA}
|
||||
TARGET_LUA=bin_lua.${EXT_SO}
|
||||
|
||||
ALL_TARGETS+=${TARGET_LUA}
|
||||
|
||||
${TARGET_LUA}: ${OBJ_LUA}
|
||||
${CC} $(call libname,bin_lua) -shared ${CFLAGS} \
|
||||
-o ${TARGET_LUA} ${OBJ_LUA} $(LINK) $(LDFLAGS)
|
@ -273,6 +273,7 @@ extern RArchPlugin r_arch_plugin_nios2;
|
||||
extern RArchPlugin r_arch_plugin_or1k;
|
||||
extern RArchPlugin r_arch_plugin_evm;
|
||||
extern RArchPlugin r_arch_plugin_ws;
|
||||
extern RArchPlugin r_arch_plugin_lua;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@ -901,8 +901,10 @@ extern RBinPlugin r_bin_plugin_dmp64;
|
||||
extern RBinPlugin r_bin_plugin_pyc;
|
||||
extern RBinPlugin r_bin_plugin_off;
|
||||
extern RBinPlugin r_bin_plugin_tic;
|
||||
extern RBinPlugin r_bin_plugin_lua;
|
||||
extern RBinPlugin r_bin_plugin_hunk;
|
||||
extern RBinPlugin r_bin_plugin_xalz;
|
||||
extern RBinPlugin r_bin_plugin_lua;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@ -30,6 +30,8 @@ cd "$(dirname $0)"/..
|
||||
(git grep -n 'for(' libr | grep -v _for | grep -v colorfor) && exit 1
|
||||
(git grep -n 'for (' libr | grep "; ++" | grep -v arch ) && exit 1
|
||||
(git grep -n 'for (int' | grep -v sys/) && exit 1
|
||||
# (git grep -n '){' libr) && exit 1
|
||||
# (git grep -n 'for(' | grep -v test | grep -v shlr | grep -v sys/) && exit 1
|
||||
(git grep -n 'for (long' | grep -v sys/) && exit 1
|
||||
(git grep -n 'for (ut' | grep -v sys/) && exit 1
|
||||
(git grep -n 'for (size_t' | grep -v sys/) && exit 1
|
||||
|
Loading…
Reference in New Issue
Block a user