Skip to content

Instantly share code, notes, and snippets.

@ManuelBlanc
Last active February 27, 2023 06:24
Show Gist options
  • Save ManuelBlanc/a6396fd3eb47db087d994e2787723aed to your computer and use it in GitHub Desktop.
Save ManuelBlanc/a6396fd3eb47db087d994e2787723aed to your computer and use it in GitHub Desktop.
BP Test
commit 8f3872a422beafcc9fc4c4d36e7518be58fb6a8b
Author: ManuelBlanc <manuel.blanc@estudiante.uam.es>
Date: Mon Feb 27 06:59:29 2023 +0100
Implement BC_TRAP and jit.util.setbreakpoint
diff --git a/src/lib_debug.c b/src/lib_debug.c
index 3af7a353..e02070f1 100644
--- a/src/lib_debug.c
+++ b/src/lib_debug.c
@@ -292,7 +292,10 @@ static void hookf(lua_State *L, lua_Debug *ar)
(L->top++)->u64 = KEY_HOOK;
lua_rawget(L, LUA_REGISTRYINDEX);
if (lua_isfunction(L, -1)) {
- lua_pushstring(L, hooknames[(int)ar->event]);
+ if (ar->event >= 0)
+ lua_pushstring(L, hooknames[(int)ar->event]);
+ else
+ lua_pushinteger(L, -(int)ar->event);
if (ar->currentline >= 0)
lua_pushinteger(L, ar->currentline);
else lua_pushnil(L);
diff --git a/src/lib_jit.c b/src/lib_jit.c
index 2867d420..2e16c317 100644
--- a/src/lib_jit.c
+++ b/src/lib_jit.c
@@ -275,6 +275,53 @@ LJLIB_CF(jit_util_funcuvname)
return 0;
}
+static int setbreakpoint(lua_State *L, GCtab *t, GCproto *pt, BCLine line, uint16_t ud)
+{
+ if (line < pt->firstline || line > pt->firstline + pt->numline)
+ return 0;
+ int set = 0;
+ BCPos pc;
+ for (pc = 1; pc <= pt->sizebc; ++pc) {
+ BCLine pcline = lj_debug_line(pt, pc);
+ if (pcline == line) {
+ BCIns *bc = proto_bc(pt)+pc;
+ TValue key, val;
+ setrawlightudV(&key, bc);
+ setintV(&val, *bc);
+ copyTV(L, lj_tab_set(L, t, &key), &val);
+ *bc = BCINS_AD(BC_TRAP, 0, ud);
+ set = 1;
+ break;
+ }
+ }
+ if (pt->flags & PROTO_CHILD) {
+ MSize i, sizekgc = pt->sizekgc;
+ GCRef *kr = mref(pt->k, GCRef) - (ptrdiff_t)sizekgc;
+ for (i = 0; i < sizekgc; i++, kr++) {
+ GCobj *o = gcref(*kr);
+ if (o->gch.gct == ~LJ_TPROTO)
+ set |= setbreakpoint(L, t, &o->pt, line, ud);
+ }
+ }
+ return set;
+}
+
+/* local set = jit.util.setbreakpoint(func, idx) */
+LJLIB_CF(jit_util_setbreakpoint)
+{
+ GCfunc *fn = lj_lib_checkfunc(L, 1);
+ if (!isluafunc(fn))
+ lj_err_arg(L, 1, LJ_ERR_NOLFUNC);
+ BCLine line = (BCLine)lj_lib_checkint(L, 2);
+ uint16_t ud = (uint16_t)lj_lib_checkint(L, 3);
+ TValue key;
+ key.u64 = KEY_DEBUG_BCLUT;
+ cTValue *t = lj_tab_get(L, tabV(registry(L)), &key);
+ int set = setbreakpoint(L, tabV(t), funcproto(fn), line, ud);
+ setboolV(L->top++, set);
+ return 1;
+}
+
/* -- Reflection API for traces ------------------------------------------- */
#if LJ_HASJIT
@@ -756,6 +803,10 @@ LUALIB_API int luaopen_jit(lua_State *L)
LJ_LIB_REG(L, "jit.opt", jit_opt);
#endif
L->top -= 2;
+
+ TValue key;
+ key.u64 = KEY_DEBUG_BCLUT;
+ settabV(L, lj_tab_set(L, tabV(registry(L)), &key), lj_tab_new(L, 0, 0));
return 1;
}
diff --git a/src/lj_bc.h b/src/lj_bc.h
index 02356e5b..e6f7623d 100644
--- a/src/lj_bc.h
+++ b/src/lj_bc.h
@@ -187,14 +187,16 @@
_(JMP, rbase, ___, jump, ___) \
\
/* Function headers. I/J = interp/JIT, F/V/C = fixarg/vararg/C func. */ \
- _(FUNCF, rbase, ___, ___, ___) \
+ _(FUNCF, rbase, ___, ___, ___) \
_(IFUNCF, rbase, ___, ___, ___) \
_(JFUNCF, rbase, ___, lit, ___) \
- _(FUNCV, rbase, ___, ___, ___) \
+ _(FUNCV, rbase, ___, ___, ___) \
_(IFUNCV, rbase, ___, ___, ___) \
_(JFUNCV, rbase, ___, lit, ___) \
- _(FUNCC, rbase, ___, ___, ___) \
- _(FUNCCW, rbase, ___, ___, ___)
+ _(FUNCC, rbase, ___, ___, ___) \
+ _(FUNCCW, rbase, ___, ___, ___) \
+ /* Extensions. */ \
+ _(TRAP, base, ___, ___, ___)
/* Bytecode opcode numbers. */
typedef enum {
diff --git a/src/lj_dispatch.c b/src/lj_dispatch.c
index ded382aa..f3c5b0d0 100644
--- a/src/lj_dispatch.c
+++ b/src/lj_dispatch.c
@@ -400,7 +400,7 @@ static BCReg cur_topslot(GCproto *pt, const BCIns *pc, uint32_t nres)
}
/* Instruction dispatch. Used by instr/line/return hooks or when recording. */
-void LJ_FASTCALL lj_dispatch_ins(lua_State *L, const BCIns *pc)
+void LJ_FASTCALL lj_dispatch_ins(lua_State *L, BCIns *pc)
{
ERRNO_SAVE
GCfunc *fn = curr_func(L);
@@ -440,8 +440,19 @@ void LJ_FASTCALL lj_dispatch_ins(lua_State *L, const BCIns *pc)
L->top = L->base + slots; /* Fix top again. */
}
}
- if ((g->hookmask & LUA_MASKRET) && bc_isret(bc_op(pc[-1])))
+ BCOp op = bc_op(pc[-1]);
+ if ((g->hookmask & LUA_MASKRET) && bc_isret(op))
callhook(L, LUA_HOOKRET, -1);
+ else if (op == BC_TRAP) {
+ TValue key;
+ key.u64 = KEY_DEBUG_BCLUT;
+ cTValue *t = lj_tab_get(L, tabV(registry(L)), &key);
+ setrawlightudV(&key, pc-1);
+ cTValue *val = lj_tab_get(L, tabV(t), &key);
+ int ud = -(int)bc_d(pc[-1]);
+ pc[-1] = numberVint(val);
+ callhook(L, ud, -1);
+ }
ERRNO_RESTORE
}
diff --git a/src/lj_dispatch.h b/src/lj_dispatch.h
index 52762eea..7194afc6 100644
--- a/src/lj_dispatch.h
+++ b/src/lj_dispatch.h
@@ -135,7 +135,7 @@ LJ_FUNC void lj_dispatch_init_hotcount(global_State *g);
LJ_FUNC void lj_dispatch_update(global_State *g);
/* Instruction dispatch callback for hooks or when recording. */
-LJ_FUNCA void LJ_FASTCALL lj_dispatch_ins(lua_State *L, const BCIns *pc);
+LJ_FUNCA void LJ_FASTCALL lj_dispatch_ins(lua_State *L, BCIns *pc);
LJ_FUNCA ASMFunction LJ_FASTCALL lj_dispatch_call(lua_State *L, const BCIns*pc);
#if LJ_HASJIT
LJ_FUNCA void LJ_FASTCALL lj_dispatch_stitch(jit_State *J, const BCIns *pc);
diff --git a/src/lj_jit.h b/src/lj_jit.h
index 7f081730..57fdde0d 100644
--- a/src/lj_jit.h
+++ b/src/lj_jit.h
@@ -527,4 +527,6 @@ typedef struct jit_State {
#endif
#endif
+#define KEY_DEBUG_BCLUT (U64x(80000000,00000000)|'T')
+
#endif
diff --git a/src/vm_x64.dasc b/src/vm_x64.dasc
index 03d96557..0bafab02 100644
--- a/src/vm_x64.dasc
+++ b/src/vm_x64.dasc
@@ -4685,6 +4685,26 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop)
| jmp ->vm_returnc
break;
+ case BC_TRAP:
+ | ins_AD
+ | movzx RDd, byte [DISPATCH+DISPATCH_GL(hookmask)]
+ | test RDL, HOOK_ACTIVE // Hook already active?
+ | jnz >1
+ | mov L:RB, SAVE_L
+ | mov SAVE_PC, PC
+ | mov L:RB->base, BASE
+ | mov CARG2, PC
+ | mov CARG1, L:RB
+ | // SAVE_PC must hold the _previous_ PC. The callee updates it with PC.
+ | call extern lj_dispatch_ins // (lua_State *L, const BCIns *pc)
+ |1:
+ | mov BASE, L:RB->base
+ | movzx OP, PC_OP
+ | movzx RAd, PC_RA
+ | movzx RDd, PC_RD
+ | jmp aword [DISPATCH+OP*8+GG_DISP2STATIC] // Re-dispatch to static ins.
+ break;
+
/* ---------------------------------------------------------------------- */
default:
local CODESTR = [=[#!/usr/bin/env luajit
local a = 1
local b = (1+3) .. string.sub("abc", 2, 3) .. "def"
local c = 3
print(a,b,c)
--local function child() print(4,5,6) end local d = false
]=]
-- void lua_breakpoint(lua_State* L, int funcindex, int line, int enabled)
-- https://www.freelists.org/post/luajit/OP-HALT-debugger-patch-for-LuaJIT
-- https://marketplace.visualstudio.com/items?itemName=devCAT.lua-debug
-- https://github.com/fsfod/LuaJIT/commits/features/breakpoints
-- http://lua-users.org/lists/lua-l/2007-03/msg00006.html
-- http://lua-users.org/lists/lua-l/2018-05/msg00115.html
-- http://lua-users.org/lists/lua-l/2007-03/msg00006.html
-- https://groups.google.com/g/openresty/c/6EVoYdPr0I8
local func = loadstring(CODESTR, "code.lua")
local function EXPERR(expected, fn, ...)
local ok, err = pcall(fn, ...)
if ok or err ~= expected then
local msg = "EXPERR failed!"
msg = msg.."\nExpected: "..expected
msg = msg.."\nReceived:"..ok and "!SUCCESS!" or err
error(msg)
end
end
local setbreakpoint = require("jit.util").setbreakpoint
local function printf(fmt,...) print(string.format("[INFO] "..fmt,...)) end
EXPERR("bad argument #1 to '?' (function expected, got no value)", setbreakpoint)
EXPERR("bad argument #1 to '?' (Lua function expected)", setbreakpoint, print)
EXPERR("bad argument #2 to '?' (number expected, got no value)", setbreakpoint, func)
EXPERR("bad argument #3 to '?' (number expected, got no value)", setbreakpoint, func, -999)
--EXPERR("bad argument #2 to '?' (index out of range)", setbreakpoint, func, 1e50)
local line = tonumber(..., 10)
if not line then io.stderr:write(arg[0]..": no argument\n") return end
local bc = require("jit.bc")
bc.dump(func, nil, true)
printf("Setting breakpoint at line %d", line)
local ok = setbreakpoint(func, line, 123)
printf("Success? %s ", ok and "YES" or "NO")
if not ok then
return
printf("Done.")
end
printf("NEW DUMP:")
bc.dump(func, nil, true)
print "Setting the hook..."
debug.sethook(function()
printf("In the hook!")
end, "", 999999999)
print("Before call")
func()
printf("After call")
printf("AFTER DUMP:")
bc.dump(func, nil, true)
printf("Done.")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment