diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
new file mode 100644
index 0000000..b48dff0
--- /dev/null
+++ b/.github/workflows/build.yml
@@ -0,0 +1,57 @@
+name: Build RafLoader ASI
+
+on:
+ push:
+ branches: [ main, beta ]
+ pull_request:
+ branches: [ main, beta ]
+
+jobs:
+ build:
+ name: Build (${{ matrix.branch }})
+ runs-on: ubuntu-latest
+ strategy:
+ fail-fast: false
+ matrix:
+ branch: [ main, beta ]
+
+ steps:
+ - name: Checkout Code
+ uses: actions/checkout@v4
+ with:
+ ref: ${{ matrix.branch }}
+
+ - name: Install MinGW & Build Tools
+ run: |
+ sudo apt-get update
+ sudo apt-get install -y mingw-w64 g++-mingw-w64-i686 make luajit
+
+ - name: Verify Environment & Tools
+ run: |
+ i686-w64-mingw32-g++ --version
+ make --version
+ luajit -v
+
+ - name: Setup LuaJIT CLI for Builder
+ run: |
+ chmod +x ./lib/luajit/luajit || true
+ cp /usr/bin/luajit ./lib/luajit/luajit || true
+
+ - name: Build Project
+ run: |
+ make clean
+ make
+
+ - name: Prepare Artifact Package
+ run: |
+ mkdir -p release_pack
+ cp build/RafLoader.asi release_pack/
+ cp build/RafLoader_SDK.h release_pack/
+ if [ -d "scripts" ]; then cp -r scripts release_pack/; fi
+
+ - name: Upload Build Artifact
+ uses: actions/upload-artifact@v4
+ with:
+ name: RafLoader-${{ matrix.branch }}-${{ github.sha }}
+ path: release_pack/
+ retention-days: 7
diff --git a/.gitignore b/.gitignore
index f5e0f8c..ef01bc7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,14 +1,16 @@
build/
include/lua/
-jit/
.vscode/
.idea/
compile_commands.json
.DS_Store
Thumbs.db
-.BUILDINFO
-.MTREE
+.BUILDINFO
+.MTREE
.PKGINFO
+*.log
+*.swp
+*.swo
*.o
*.obj
*.exe
@@ -16,10 +18,5 @@ Thumbs.db
*.asi
*.a
*.lib
-*.log
-*.swp
-*.swo
!lib/**/*.a
-!lib/**/*.lib
-!lua51.dll
-lib/luajit/luajit
+!lib/luajit/luajit
diff --git a/Makefile b/Makefile
index 81cb3bb..a01fe7f 100644
--- a/Makefile
+++ b/Makefile
@@ -1,18 +1,26 @@
-VERSION = 0.1.3
+VERSION = 0.2.1
BUILD_DIR = build
CXX = i686-w64-mingw32-g++
-CXXFLAGS = -m32 -std=c++17 -O2 -shared \
+CXXFLAGS = -m32 -std=c++17 -O2 -shared -s \
+ -ffunction-sections -fdata-sections \
-DRAF_VERSION=\"$(VERSION)\" \
- -I./include -I./include/lua -I./lib/luajit -I./lib/minhook -I./lib/imgui
+ -I./include -I./include/lua -I./lib/luajit \
+ -I./lib/minhook -I./lib/minhook/include -I./lib/minhook/src -I./lib/imgui
-LDFLAGS = lib/minhook/MinHook.x86.lib ./lua51.dll \
- -static \
+LDFLAGS = -static -static-libgcc -static-libstdc++ \
+ -Wl,--gc-sections \
+ ./lib/luajit/libluajit.a \
-Wl,--subsystem,windows:5.1 \
-luser32 -lkernel32 -lwinmm -ld3d9 -lgdi32 -ldwmapi
+MINHOOK_SRC = lib/minhook/src/buffer.c \
+ lib/minhook/src/hook.c \
+ lib/minhook/src/trampoline.c \
+ lib/minhook/src/hde/hde32.c
+
IMGUI_SRC = lib/imgui/imgui.cpp \
lib/imgui/imgui_draw.cpp \
lib/imgui/imgui_widgets.cpp \
@@ -20,7 +28,7 @@ IMGUI_SRC = lib/imgui/imgui.cpp \
lib/imgui/imgui_impl_dx9.cpp \
lib/imgui/imgui_impl_win32.cpp
-SRC = src/main.cpp src/thread.cpp src/memory.cpp src/HooksManager/DefaultHooks.cpp src/HooksManager/DataPathHook.cpp src/CrashHandler.cpp src/ConsoleState.cpp src/ImGuiMenu.cpp src/RenderHook.cpp $(IMGUI_SRC)
+SRC = src/main.cpp src/thread.cpp src/memory.cpp src/HooksManager/DefaultHooks.cpp src/CrashHandler.cpp src/ConsoleState.cpp src/ImGuiMenu.cpp src/RenderHook.cpp $(IMGUI_SRC) $(MINHOOK_SRC)
OUT = $(BUILD_DIR)/RafLoader.asi
diff --git a/README.md b/README.md
index d06734f..09d0a33 100644
--- a/README.md
+++ b/README.md
@@ -1,132 +1,210 @@
⚔️ RafLoader (Rise and Fall: Civilization at War)
-
-RafLoader — это lightweight loader для внедрения в **Rise and Fall: Civilization at War** с возможностью расширения логики игры через LuaJIT и C++.
-
-Проект предназначен для моддинга, анализа и изменения поведения игры в рантайме.
+RafLoader is a lightweight ASI loader for **Rise and Fall: Civilization at War**, providing runtime modding through **LuaJIT** and native **C++** APIs.
---
-## 🚀 Что делает RafLoader
+## 🚀 Overview
-После загрузки (`.asi` / DLL) в игру:
+RafLoader injects into the game process and provides:
-- внедряется в процесс **Rise and Fall: Civilization at War**
-- инициализирует систему хуков (MinHook)
-- поднимает LuaJIT окружение
-- подключает встроенные API
-- перехватывает рендер (DirectX9)
-- запускает пользовательские скрипты
-- отображает debug UI поверх игры
+- Native function hooking powered by **MinHook**.
+- Embedded LuaJIT runtime.
+- DirectX9 rendering hook.
+- Automatic Lua script loading.
+- Built-in ImGui debug console.
+- Crash recovery helpers.
+- Per-frame (tick) callbacks.
---
-## 🧩 Основные возможности
+# 🧩 Features
-### 🪝 Перехват функций (Hooks)
-- перехват внутренних функций игры по адресу
-- изменение или полная замена логики игры
-- поддержка нестандартных вызовов (usercall)
-- реализовано через **MinHook**
+## 🪝 Function Hooks
----
+Create native hooks directly from Lua.
-### 💾 Работа с памятью
-- чтение и изменение игровых значений
-- патчинг байтов в коде игры
-- изменение логики без пересборки игры
-- управление защитой памяти (VirtualProtect)
+Supported hook types:
----
+- Standard function hooks.
+- `__usercall` bridge hooks.
+- Damage post hooks.
+
+Example:
+
+```lua
+local hooks = _G.Engine.Hooks
+
+local original
-### ⌨️ Обработка ввода
-- отслеживание нажатий клавиш
-- биндинг действий
-- работает через WinAPI (`GetAsyncKeyState`)
+original = hooks.create(
+ 0x401000,
+ "int (__cdecl *NAME)(int)",
+ function(value)
+ print("Hook:", value)
+ return original(value)
+ end
+)
+```
---
-### 🛡️ Обработка крашей
-- перехват `EXCEPTION_ACCESS_VIOLATION`
-- система recovery point
-- возможность избежать вылетов при ошибках
+## 💾 Memory API
+
+Read and modify game memory.
+
+Available functions:
+
+```lua
+local memory = _G.Engine.Memory
+
+memory.write_nop(address, size)
+memory.patch(address, bytes)
+
+memory.read_int(address)
+memory.read_float(address)
+
+memory.write_int(address, value)
+memory.write_float(address, value)
+```
+
+Example:
+
+```lua
+memory.write_nop(0x401000, 5)
+
+memory.patch(0x401100, {
+ 0x90,
+ 0x90,
+ 0x90
+})
+```
---
-### 🎨 Overlay интерфейс (ImGui)
-- встроенное debug-меню
-- вывод логов
-- работает поверх игры
-- открывается/закрывается клавишей **F1**
+## 🔁 Tick Callbacks
+
+Execute Lua code every game update.
+
+```lua
+local tick = _G.Engine.Tick
+
+tick.add(function()
+ -- Called every frame
+end)
+```
+
+Clear all callbacks:
+
+```lua
+tick.clear()
+```
---
-### 🔁 Tick-система
-- выполнение пользовательского кода каждый кадр
-- используется для логики модов и обновлений
+## 🛡️ Crash Recovery
+
+Register recovery points for known crashes.
+
+```lua
+local crash = _G.Engine.VahCrash
+
+crash.catch(
+ 0x403000,
+ 0x404000
+)
+```
---
-## 🧱 Расширяемость API
+## 📂 Script Loader
-RafLoader API можно использовать не только через Lua:
+All Lua scripts inside the `scripts` directory are loaded automatically.
-- поддержка использования API из **C/C++**
-- возможность писать собственные `.asi` плагины
-- доступ к экспортируемым функциям (`Core_*`)
-- интеграция с другими нативными модулями
+Example structure:
-Это позволяет:
-- комбинировать Lua и нативный код
-- выносить тяжелую логику в C++
-- строить полноценные модульные системы
+```
+RafLoader.asi
+scripts/
+ test.lua
+ cheats.lua
+ ui.lua
+```
+
+Files beginning with `_` or `.` are ignored.
---
-## ⚠️ Ограничения
+## 🎨 Debug Console
+
+Built-in ImGui console.
-- ориентирован на **x86**
-- UI работает только с **DirectX9**
-- требует знания адресов функций/памяти
-- ошибки в хуках могут приводить к крашу
+Features:
+
+- Live log output.
+- Automatic logging from Lua `print()`.
+- Log file (`RafLoader.log`).
+- Toggle with **F1**.
---
-## 💡 Пример
+# 🛠️ Complete Example
```lua
local memory = _G.Engine.Memory
-local input = _G.Engine.Input
local hooks = _G.Engine.Hooks
+local tick = _G.Engine.Tick
local crash = _G.Engine.VahCrash
--- биндим клавишу F
-input.bind(0x46, function()
- print("Applying NOP")
- memory.write_nop(0x401000, 5)
-end)
+memory.write_nop(0x401000, 5)
--- хук
local original
-original = hooks.create(0x401000, "int (__cdecl *NAME)(int)", function(a)
- print("Hooked:", a)
- return original(a)
+
+original = hooks.create(
+ 0x401050,
+ "int (__cdecl *NAME)(int)",
+ function(value)
+ print("Function called:", value)
+ return original(value)
+ end
+)
+
+tick.add(function()
+ -- Executed every frame
end)
--- защита от краша
-crash.catch(0x403000, 0x404000)
+crash.catch(
+ 0x403000,
+ 0x404000
+)
```
---
-## 💡 Преимущества
+# 🧱 Extending RafLoader
+
+The Lua API is exposed through:
+
+```lua
+_G.Engine.Memory
+_G.Engine.Hooks
+_G.Engine.Tick
+_G.Engine.VahCrash
+```
-- Lua + C++ в одном проекте
-- быстрый цикл разработки
-- прямой доступ к внутренностям игры
-- готовая система хуков
-- встроенный UI и логирование
-- crash recovery механизм
+Additional native functionality can be implemented through custom C++ plugins.
+
+---
+
+# ⚠️ Limitations
+
+- x86 only.
+- DirectX9 renderer.
+- Requires knowledge of game memory addresses.
+- Invalid hooks or memory patches may crash the game.
+
+---
+RafLoader provides a compact and lightweight framework for creating Lua-powered mods, runtime patches, hooks, and debugging tools for **Rise and Fall: Civilization at War**.
diff --git a/images/feature.png b/images/feature.png
deleted file mode 100644
index 36491cb..0000000
Binary files a/images/feature.png and /dev/null differ
diff --git a/jit/bc.lua b/jit/bc.lua
new file mode 100644
index 0000000..8014d60
--- /dev/null
+++ b/jit/bc.lua
@@ -0,0 +1,189 @@
+----------------------------------------------------------------------------
+-- LuaJIT bytecode listing module.
+--
+-- Copyright (C) 2005-2026 Mike Pall. All rights reserved.
+-- Released under the MIT license. See Copyright Notice in luajit.h
+----------------------------------------------------------------------------
+--
+-- This module lists the bytecode of a Lua function. If it's loaded by -jbc
+-- it hooks into the parser and lists all functions of a chunk as they
+-- are parsed.
+--
+-- Example usage:
+--
+-- luajit -jbc -e 'local x=0; for i=1,1e6 do x=x+i end; print(x)'
+-- luajit -jbc=- foo.lua
+-- luajit -jbc=foo.list foo.lua
+--
+-- Default output is to stderr. To redirect the output to a file, pass a
+-- filename as an argument (use '-' for stdout) or set the environment
+-- variable LUAJIT_LISTFILE. The file is overwritten every time the module
+-- is started.
+--
+-- This module can also be used programmatically:
+--
+-- local bc = require("jit.bc")
+--
+-- local function foo() print("hello") end
+--
+-- bc.dump(foo) --> -- BYTECODE -- [...]
+-- print(bc.line(foo, 2)) --> 0002 KSTR 1 1 ; "hello"
+--
+-- local out = {
+-- -- Do something with each line:
+-- write = function(t, ...) io.write(...) end,
+-- close = function(t) end,
+-- flush = function(t) end,
+-- }
+-- bc.dump(foo, out)
+--
+------------------------------------------------------------------------------
+
+-- Cache some library functions and objects.
+local jit = require("jit")
+local jutil = require("jit.util")
+local vmdef = require("jit.vmdef")
+local bit = require("bit")
+local sub, gsub, format = string.sub, string.gsub, string.format
+local byte, band, shr = string.byte, bit.band, bit.rshift
+local funcinfo, funcbc, funck = jutil.funcinfo, jutil.funcbc, jutil.funck
+local funcuvname = jutil.funcuvname
+local bcnames = vmdef.bcnames
+local stdout, stderr = io.stdout, io.stderr
+
+------------------------------------------------------------------------------
+
+local function ctlsub(c)
+ if c == "\n" then return "\\n"
+ elseif c == "\r" then return "\\r"
+ elseif c == "\t" then return "\\t"
+ else return format("\\%03d", byte(c))
+ end
+end
+
+-- Return one bytecode line.
+local function bcline(func, pc, prefix)
+ local ins, m = funcbc(func, pc)
+ if not ins then return end
+ local ma, mb, mc = band(m, 7), band(m, 15*8), band(m, 15*128)
+ local a = band(shr(ins, 8), 0xff)
+ local oidx = 6*band(ins, 0xff)
+ local op = sub(bcnames, oidx+1, oidx+6)
+ local s = format("%04d %s %-6s %3s ",
+ pc, prefix or " ", op, ma == 0 and "" or a)
+ local d = shr(ins, 16)
+ if mc == 13*128 then -- BCMjump
+ return format("%s=> %04d\n", s, pc+d-0x7fff)
+ end
+ if mb ~= 0 then
+ d = band(d, 0xff)
+ elseif mc == 0 then
+ return s.."\n"
+ end
+ local kc
+ if mc == 10*128 then -- BCMstr
+ kc = funck(func, -d-1)
+ kc = format(#kc > 40 and '"%.40s"~' or '"%s"', gsub(kc, "%c", ctlsub))
+ elseif mc == 9*128 then -- BCMnum
+ kc = funck(func, d)
+ if op == "TSETM " then kc = kc - 2^52 end
+ elseif mc == 12*128 then -- BCMfunc
+ local fi = funcinfo(funck(func, -d-1))
+ if fi.ffid then
+ kc = vmdef.ffnames[fi.ffid]
+ else
+ kc = fi.loc
+ end
+ elseif mc == 5*128 then -- BCMuv
+ kc = funcuvname(func, d)
+ end
+ if ma == 5 then -- BCMuv
+ local ka = funcuvname(func, a)
+ if kc then kc = ka.." ; "..kc else kc = ka end
+ end
+ if mb ~= 0 then
+ local b = shr(ins, 24)
+ if kc then return format("%s%3d %3d ; %s\n", s, b, d, kc) end
+ return format("%s%3d %3d\n", s, b, d)
+ end
+ if kc then return format("%s%3d ; %s\n", s, d, kc) end
+ if mc == 7*128 and d > 32767 then d = d - 65536 end -- BCMlits
+ return format("%s%3d\n", s, d)
+end
+
+-- Collect branch targets of a function.
+local function bctargets(func)
+ local target = {}
+ for pc=1,1000000000 do
+ local ins, m = funcbc(func, pc)
+ if not ins then break end
+ if band(m, 15*128) == 13*128 then target[pc+shr(ins, 16)-0x7fff] = true end
+ end
+ return target
+end
+
+-- Dump bytecode instructions of a function.
+local function bcdump(func, out, all)
+ if not out then out = stdout end
+ local fi = funcinfo(func)
+ if all and fi.children then
+ for n=-1,-1000000000,-1 do
+ local k = funck(func, n)
+ if not k then break end
+ if type(k) == "proto" then bcdump(k, out, true) end
+ end
+ end
+ out:write(format("-- BYTECODE -- %s-%d\n", fi.loc, fi.lastlinedefined))
+ local target = bctargets(func)
+ for pc=1,1000000000 do
+ local s = bcline(func, pc, target[pc] and "=>")
+ if not s then break end
+ out:write(s)
+ end
+ out:write("\n")
+ out:flush()
+end
+
+------------------------------------------------------------------------------
+
+-- Active flag and output file handle.
+local active, out
+
+-- List handler.
+local function h_list(func)
+ return bcdump(func, out)
+end
+
+-- Detach list handler.
+local function bclistoff()
+ if active then
+ active = false
+ jit.attach(h_list)
+ if out and out ~= stdout and out ~= stderr then out:close() end
+ out = nil
+ end
+end
+
+-- Open the output file and attach list handler.
+local function bcliston(outfile)
+ if active then bclistoff() end
+ if not outfile then outfile = os.getenv("LUAJIT_LISTFILE") end
+ if outfile then
+ out = outfile == "-" and stdout or assert(io.open(outfile, "w"))
+ else
+ out = stderr
+ end
+ jit.attach(h_list, "bc")
+ active = true
+end
+
+-- Public module functions.
+return {
+ line = bcline,
+ dump = bcdump,
+ targets = bctargets,
+ on = bcliston,
+ off = bclistoff,
+ start = bcliston -- For -j command line option.
+}
+
diff --git a/jit/bcsave.lua b/jit/bcsave.lua
new file mode 100644
index 0000000..7d19cb0
--- /dev/null
+++ b/jit/bcsave.lua
@@ -0,0 +1,637 @@
+----------------------------------------------------------------------------
+-- LuaJIT module to save/list bytecode.
+--
+-- Copyright (C) 2005-2026 Mike Pall. All rights reserved.
+-- Released under the MIT license. See Copyright Notice in luajit.h
+----------------------------------------------------------------------------
+--
+-- This module saves or lists the bytecode for an input file.
+-- It's run by the -b command line option.
+--
+------------------------------------------------------------------------------
+
+local jit = require("jit")
+assert(jit.version_num == 20199, "LuaJIT core/library version mismatch")
+local bit = require("bit")
+
+-- Symbol name prefix for LuaJIT bytecode.
+local LJBC_PREFIX = "luaJIT_BC_"
+
+local type, assert = type, assert
+local format = string.format
+local tremove, tconcat = table.remove, table.concat
+
+------------------------------------------------------------------------------
+
+local function usage()
+ io.stderr:write[[
+Save LuaJIT bytecode: luajit -b[options] input output
+ -l Only list bytecode.
+ -s Strip debug info (default).
+ -g Keep debug info.
+ -W Generate 32 bit (non-GC64) bytecode.
+ -X Generate 64 bit (GC64) bytecode.
+ -d Generate bytecode in deterministic manner.
+ -n name Set module name (default: auto-detect from input name).
+ -t type Set output file type (default: auto-detect from output name).
+ -a arch Override architecture for object files (default: native).
+ -o os Override OS for object files (default: native).
+ -F name Override filename (default: input filename).
+ -e chunk Use chunk string as input.
+ -- Stop handling options.
+ - Use stdin as input and/or stdout as output.
+
+File types: c cc h obj o raw (default)
+]]
+ os.exit(1)
+end
+
+local function check(ok, ...)
+ if ok then return ok, ... end
+ io.stderr:write("luajit: ", ...)
+ io.stderr:write("\n")
+ os.exit(1)
+end
+
+local function readfile(ctx, input)
+ if ctx.string then
+ return check(loadstring(input, nil, ctx.mode))
+ elseif ctx.filename then
+ local data
+ if input == "-" then
+ data = io.stdin:read("*a")
+ else
+ local fp = assert(io.open(input, "rb"))
+ data = assert(fp:read("*a"))
+ assert(fp:close())
+ end
+ return check(load(data, ctx.filename, ctx.mode))
+ else
+ if input == "-" then input = nil end
+ return check(loadfile(input, ctx.mode))
+ end
+end
+
+local function savefile(name, mode)
+ if name == "-" then return io.stdout end
+ return check(io.open(name, mode))
+end
+
+local function set_stdout_binary(ffi)
+ ffi.cdef[[int _setmode(int fd, int mode);]]
+ ffi.C._setmode(1, 0x8000)
+end
+
+------------------------------------------------------------------------------
+
+local map_type = {
+ raw = "raw", c = "c", cc = "c", h = "h", o = "obj", obj = "obj",
+}
+
+local map_arch = {
+ x86 = { e = "le", b = 32, m = 3, p = 0x14c, },
+ x64 = { e = "le", b = 64, m = 62, p = 0x8664, },
+ arm = { e = "le", b = 32, m = 40, p = 0x1c0, },
+ arm64 = { e = "le", b = 64, m = 183, p = 0xaa64, },
+ arm64be = { e = "be", b = 64, m = 183, },
+ ppc = { e = "be", b = 32, m = 20, },
+ mips = { e = "be", b = 32, m = 8, f = 0x50001006, },
+ mipsel = { e = "le", b = 32, m = 8, f = 0x50001006, },
+ mips64 = { e = "be", b = 64, m = 8, f = 0x80000007, },
+ mips64el = { e = "le", b = 64, m = 8, f = 0x80000007, },
+ mips64r6 = { e = "be", b = 64, m = 8, f = 0xa0000407, },
+ mips64r6el = { e = "le", b = 64, m = 8, f = 0xa0000407, },
+}
+
+local map_os = {
+ linux = true, windows = true, osx = true, freebsd = true, netbsd = true,
+ openbsd = true, dragonfly = true, solaris = true,
+}
+
+local function checkarg(str, map, err)
+ str = str:lower()
+ local s = check(map[str], "unknown ", err)
+ return type(s) == "string" and s or str
+end
+
+local function detecttype(str)
+ local ext = str:lower():match("%.(%a+)$")
+ return map_type[ext] or "raw"
+end
+
+local function checkmodname(str)
+ check(str:match("^[%w_.%-]+$"), "bad module name")
+ return str:gsub("[%.%-]", "_")
+end
+
+local function detectmodname(str)
+ if type(str) == "string" then
+ local tail = str:match("[^/\\]+$")
+ if tail then str = tail end
+ local head = str:match("^(.*)%.[^.]*$")
+ if head then str = head end
+ str = str:match("^[%w_.%-]+")
+ else
+ str = nil
+ end
+ check(str, "cannot derive module name, use -n name")
+ return str:gsub("[%.%-]", "_")
+end
+
+------------------------------------------------------------------------------
+
+local function bcsave_tail(fp, output, s)
+ local ok, err = fp:write(s)
+ if ok and output ~= "-" then ok, err = fp:close() end
+ check(ok, "cannot write ", output, ": ", err)
+end
+
+local function bcsave_raw(output, s)
+ if output == "-" and jit.os == "Windows" then
+ local ok, ffi = pcall(require, "ffi")
+ check(ok, "FFI library required to write binary file to stdout")
+ set_stdout_binary(ffi)
+ end
+ local fp = savefile(output, "wb")
+ bcsave_tail(fp, output, s)
+end
+
+local function bcsave_c(ctx, output, s)
+ local fp = savefile(output, "w")
+ if ctx.type == "c" then
+ fp:write(format([[
+#ifdef __cplusplus
+extern "C"
+#endif
+#ifdef _WIN32
+__declspec(dllexport)
+#elif (defined(__ELF__) || defined(__MACH__) || defined(__psp2__)) && !((defined(__sun__) && defined(__svr4__)) || defined(__CELLOS_LV2__))
+__attribute__((visibility("default")))
+#endif
+const unsigned char %s%s[] = {
+]], LJBC_PREFIX, ctx.modname))
+ else
+ fp:write(format([[
+#define %s%s_SIZE %d
+static const unsigned char %s%s[] = {
+]], LJBC_PREFIX, ctx.modname, #s, LJBC_PREFIX, ctx.modname))
+ end
+ local t, n, m = {}, 0, 0
+ for i=1,#s do
+ local b = tostring(string.byte(s, i))
+ m = m + #b + 1
+ if m > 78 then
+ fp:write(tconcat(t, ",", 1, n), ",\n")
+ n, m = 0, #b + 1
+ end
+ n = n + 1
+ t[n] = b
+ end
+ bcsave_tail(fp, output, tconcat(t, ",", 1, n).."\n};\n")
+end
+
+local function bcsave_elfobj(ctx, output, s, ffi)
+ ffi.cdef[[
+typedef struct {
+ uint8_t emagic[4], eclass, eendian, eversion, eosabi, eabiversion, epad[7];
+ uint16_t type, machine;
+ uint32_t version;
+ uint32_t entry, phofs, shofs;
+ uint32_t flags;
+ uint16_t ehsize, phentsize, phnum, shentsize, shnum, shstridx;
+} ELF32header;
+typedef struct {
+ uint8_t emagic[4], eclass, eendian, eversion, eosabi, eabiversion, epad[7];
+ uint16_t type, machine;
+ uint32_t version;
+ uint64_t entry, phofs, shofs;
+ uint32_t flags;
+ uint16_t ehsize, phentsize, phnum, shentsize, shnum, shstridx;
+} ELF64header;
+typedef struct {
+ uint32_t name, type, flags, addr, ofs, size, link, info, align, entsize;
+} ELF32sectheader;
+typedef struct {
+ uint32_t name, type;
+ uint64_t flags, addr, ofs, size;
+ uint32_t link, info;
+ uint64_t align, entsize;
+} ELF64sectheader;
+typedef struct {
+ uint32_t name, value, size;
+ uint8_t info, other;
+ uint16_t sectidx;
+} ELF32symbol;
+typedef struct {
+ uint32_t name;
+ uint8_t info, other;
+ uint16_t sectidx;
+ uint64_t value, size;
+} ELF64symbol;
+typedef struct {
+ ELF32header hdr;
+ ELF32sectheader sect[6];
+ ELF32symbol sym[2];
+ uint8_t space[4096];
+} ELF32obj;
+typedef struct {
+ ELF64header hdr;
+ ELF64sectheader sect[6];
+ ELF64symbol sym[2];
+ uint8_t space[4096];
+} ELF64obj;
+]]
+ local symname = LJBC_PREFIX..ctx.modname
+ local ai = assert(map_arch[ctx.arch])
+ local is64, isbe = ai.b == 64, ai.e == "be"
+
+ -- Handle different host/target endianess.
+ local function f32(x) return x end
+ local f16, fofs = f32, f32
+ if ffi.abi("be") ~= isbe then
+ f32 = bit.bswap
+ function f16(x) return bit.rshift(bit.bswap(x), 16) end
+ if is64 then
+ local two32 = ffi.cast("int64_t", 2^32)
+ function fofs(x) return bit.bswap(x)*two32 end
+ else
+ fofs = f32
+ end
+ end
+
+ -- Create ELF object and fill in header.
+ local o = ffi.new(is64 and "ELF64obj" or "ELF32obj")
+ local hdr = o.hdr
+ if ctx.os == "bsd" or ctx.os == "other" then -- Determine native hdr.eosabi.
+ local bf = assert(io.open("/bin/ls", "rb"))
+ local bs = bf:read(9)
+ bf:close()
+ ffi.copy(o, bs, 9)
+ check(hdr.emagic[0] == 127, "no support for writing native object files")
+ else
+ hdr.emagic = "\127ELF"
+ hdr.eosabi = ({ freebsd=9, netbsd=2, openbsd=12, solaris=6 })[ctx.os] or 0
+ end
+ hdr.eclass = is64 and 2 or 1
+ hdr.eendian = isbe and 2 or 1
+ hdr.eversion = 1
+ hdr.type = f16(1)
+ hdr.machine = f16(ai.m)
+ hdr.flags = f32(ai.f or 0)
+ hdr.version = f32(1)
+ hdr.shofs = fofs(ffi.offsetof(o, "sect"))
+ hdr.ehsize = f16(ffi.sizeof(hdr))
+ hdr.shentsize = f16(ffi.sizeof(o.sect[0]))
+ hdr.shnum = f16(6)
+ hdr.shstridx = f16(2)
+
+ -- Fill in sections and symbols.
+ local sofs, ofs = ffi.offsetof(o, "space"), 1
+ for i,name in ipairs{
+ ".symtab", ".shstrtab", ".strtab", ".rodata", ".note.GNU-stack",
+ } do
+ local sect = o.sect[i]
+ sect.align = fofs(1)
+ sect.name = f32(ofs)
+ ffi.copy(o.space+ofs, name)
+ ofs = ofs + #name+1
+ end
+ o.sect[1].type = f32(2) -- .symtab
+ o.sect[1].link = f32(3)
+ o.sect[1].info = f32(1)
+ o.sect[1].align = fofs(8)
+ o.sect[1].ofs = fofs(ffi.offsetof(o, "sym"))
+ o.sect[1].entsize = fofs(ffi.sizeof(o.sym[0]))
+ o.sect[1].size = fofs(ffi.sizeof(o.sym))
+ o.sym[1].name = f32(1)
+ o.sym[1].sectidx = f16(4)
+ o.sym[1].size = fofs(#s)
+ o.sym[1].info = 17
+ o.sect[2].type = f32(3) -- .shstrtab
+ o.sect[2].ofs = fofs(sofs)
+ o.sect[2].size = fofs(ofs)
+ o.sect[3].type = f32(3) -- .strtab
+ o.sect[3].ofs = fofs(sofs + ofs)
+ o.sect[3].size = fofs(#symname+2)
+ ffi.copy(o.space+ofs+1, symname)
+ ofs = ofs + #symname + 2
+ o.sect[4].type = f32(1) -- .rodata
+ o.sect[4].flags = fofs(2)
+ o.sect[4].ofs = fofs(sofs + ofs)
+ o.sect[4].size = fofs(#s)
+ o.sect[5].type = f32(1) -- .note.GNU-stack
+ o.sect[5].ofs = fofs(sofs + ofs + #s)
+
+ -- Write ELF object file.
+ local fp = savefile(output, "wb")
+ fp:write(ffi.string(o, ffi.sizeof(o)-4096+ofs))
+ bcsave_tail(fp, output, s)
+end
+
+local function bcsave_peobj(ctx, output, s, ffi)
+ ffi.cdef[[
+typedef struct {
+ uint16_t arch, nsects;
+ uint32_t time, symtabofs, nsyms;
+ uint16_t opthdrsz, flags;
+} PEheader;
+typedef struct {
+ char name[8];
+ uint32_t vsize, vaddr, size, ofs, relocofs, lineofs;
+ uint16_t nreloc, nline;
+ uint32_t flags;
+} PEsection;
+typedef struct __attribute((packed)) {
+ union {
+ char name[8];
+ uint32_t nameref[2];
+ };
+ uint32_t value;
+ int16_t sect;
+ uint16_t type;
+ uint8_t scl, naux;
+} PEsym;
+typedef struct __attribute((packed)) {
+ uint32_t size;
+ uint16_t nreloc, nline;
+ uint32_t cksum;
+ uint16_t assoc;
+ uint8_t comdatsel, unused[3];
+} PEsymaux;
+typedef struct {
+ PEheader hdr;
+ PEsection sect[2];
+ // Must be an even number of symbol structs.
+ PEsym sym0;
+ PEsymaux sym0aux;
+ PEsym sym1;
+ PEsymaux sym1aux;
+ PEsym sym2;
+ PEsym sym3;
+ uint32_t strtabsize;
+ uint8_t space[4096];
+} PEobj;
+]]
+ local symname = LJBC_PREFIX..ctx.modname
+ local ai = assert(map_arch[ctx.arch])
+ local is64 = ai.b == 64
+ local symexport = " /EXPORT:"..symname..",DATA "
+
+ -- The file format is always little-endian. Swap if the host is big-endian.
+ local function f32(x) return x end
+ local f16 = f32
+ if ffi.abi("be") then
+ f32 = bit.bswap
+ function f16(x) return bit.rshift(bit.bswap(x), 16) end
+ end
+
+ -- Create PE object and fill in header.
+ local o = ffi.new("PEobj")
+ local hdr = o.hdr
+ hdr.arch = f16(assert(ai.p))
+ hdr.nsects = f16(2)
+ hdr.symtabofs = f32(ffi.offsetof(o, "sym0"))
+ hdr.nsyms = f32(6)
+
+ -- Fill in sections and symbols.
+ o.sect[0].name = ".drectve"
+ o.sect[0].size = f32(#symexport)
+ o.sect[0].flags = f32(0x00100a00)
+ o.sym0.sect = f16(1)
+ o.sym0.scl = 3
+ o.sym0.name = ".drectve"
+ o.sym0.naux = 1
+ o.sym0aux.size = f32(#symexport)
+ o.sect[1].name = ".rdata"
+ o.sect[1].size = f32(#s)
+ o.sect[1].flags = f32(0x40300040)
+ o.sym1.sect = f16(2)
+ o.sym1.scl = 3
+ o.sym1.name = ".rdata"
+ o.sym1.naux = 1
+ o.sym1aux.size = f32(#s)
+ o.sym2.sect = f16(2)
+ o.sym2.scl = 2
+ o.sym2.nameref[1] = f32(4)
+ o.sym3.sect = f16(-1)
+ o.sym3.scl = 2
+ o.sym3.value = f32(1)
+ o.sym3.name = "@feat.00" -- Mark as SafeSEH compliant.
+ ffi.copy(o.space, symname)
+ local ofs = #symname + 1
+ o.strtabsize = f32(ofs + 4)
+ o.sect[0].ofs = f32(ffi.offsetof(o, "space") + ofs)
+ ffi.copy(o.space + ofs, symexport)
+ ofs = ofs + #symexport
+ o.sect[1].ofs = f32(ffi.offsetof(o, "space") + ofs)
+
+ -- Write PE object file.
+ local fp = savefile(output, "wb")
+ fp:write(ffi.string(o, ffi.sizeof(o)-4096+ofs))
+ bcsave_tail(fp, output, s)
+end
+
+local function bcsave_machobj(ctx, output, s, ffi)
+ ffi.cdef[[
+typedef struct
+{
+ uint32_t magic, cputype, cpusubtype, filetype, ncmds, sizeofcmds, flags;
+} mach_header;
+typedef struct
+{
+ mach_header; uint32_t reserved;
+} mach_header_64;
+typedef struct {
+ uint32_t cmd, cmdsize;
+ char segname[16];
+ uint64_t vmaddr, vmsize, fileoff, filesize;
+ uint32_t maxprot, initprot, nsects, flags;
+} mach_segment_command_64;
+typedef struct {
+ char sectname[16], segname[16];
+ uint64_t addr, size;
+ uint32_t offset, align, reloff, nreloc, flags;
+ uint32_t reserved1, reserved2, reserved3;
+} mach_section_64;
+typedef struct {
+ uint32_t cmd, cmdsize, symoff, nsyms, stroff, strsize;
+} mach_symtab_command;
+typedef struct {
+ int32_t strx;
+ uint8_t type, sect;
+ uint16_t desc;
+ uint64_t value;
+} mach_nlist_64;
+typedef struct {
+ mach_header_64 hdr;
+ mach_segment_command_64 seg;
+ mach_section_64 sec;
+ mach_symtab_command sym;
+} mach_obj_64;
+typedef struct {
+ mach_nlist_64 sym_entry;
+ uint8_t space[4096];
+} mach_obj_64_tail;
+]]
+ local symname = '_'..LJBC_PREFIX..ctx.modname
+ local cputype, cpusubtype = 0x01000007, 3
+ if ctx.arch ~= "x64" then
+ check(ctx.arch == "arm64", "unsupported architecture for OSX")
+ cputype, cpusubtype = 0x0100000c, 0
+ end
+ local function aligned(v, a) return bit.band(v+a-1, -a) end
+
+ -- Create Mach-O object and fill in header.
+ local o = ffi.new("mach_obj_64")
+ local t = ffi.new("mach_obj_64_tail")
+ local ofs_bc = ffi.sizeof(o)
+ local sz_bc = aligned(#s, 8)
+ local ofs_sym = ofs_bc + sz_bc
+
+ -- Fill in sections and symbols.
+ o.hdr.magic = 0xfeedfacf
+ o.hdr.cputype = cputype
+ o.hdr.cpusubtype = cpusubtype
+ o.hdr.filetype = 1
+ o.hdr.ncmds = 2
+ o.hdr.sizeofcmds = ffi.sizeof(o.seg)+ffi.sizeof(o.sec)+ffi.sizeof(o.sym)
+ o.seg.cmd = 0x19
+ o.seg.cmdsize = ffi.sizeof(o.seg)+ffi.sizeof(o.sec)
+ o.seg.vmsize = #s
+ o.seg.fileoff = ofs_bc
+ o.seg.filesize = #s
+ o.seg.maxprot = 1
+ o.seg.initprot = 1
+ o.seg.nsects = 1
+ ffi.copy(o.sec.sectname, "__data")
+ ffi.copy(o.sec.segname, "__DATA")
+ o.sec.size = #s
+ o.sec.offset = ofs_bc
+ o.sym.cmd = 2
+ o.sym.cmdsize = ffi.sizeof(o.sym)
+ o.sym.symoff = ofs_sym
+ o.sym.nsyms = 1
+ o.sym.stroff = ofs_sym + ffi.offsetof(t, "space")
+ o.sym.strsize = aligned(#symname+2, 8)
+ t.sym_entry.type = 0xf
+ t.sym_entry.sect = 1
+ t.sym_entry.strx = 1
+ ffi.copy(t.space+1, symname)
+
+ -- Write Mach-O object file.
+ local fp = savefile(output, "wb")
+ fp:write(ffi.string(o, ofs_bc))
+ fp:write(s, ("\0"):rep(sz_bc - #s))
+ bcsave_tail(fp, output, ffi.string(t, ffi.offsetof(t, "space") + o.sym.strsize))
+end
+
+local function bcsave_obj(ctx, output, s)
+ local ok, ffi = pcall(require, "ffi")
+ check(ok, "FFI library required to write this file type")
+ if output == "-" and jit.os == "Windows" then
+ set_stdout_binary(ffi)
+ end
+ if ctx.os == "windows" then
+ return bcsave_peobj(ctx, output, s, ffi)
+ elseif ctx.os == "osx" then
+ return bcsave_machobj(ctx, output, s, ffi)
+ else
+ return bcsave_elfobj(ctx, output, s, ffi)
+ end
+end
+
+------------------------------------------------------------------------------
+
+local function bclist(ctx, input, output)
+ local f = readfile(ctx, input)
+ require("jit.bc").dump(f, savefile(output, "w"), true)
+end
+
+local function bcsave(ctx, input, output)
+ local f = readfile(ctx, input)
+ local s = string.dump(f, ctx.mode)
+ local t = ctx.type
+ if not t then
+ t = detecttype(output)
+ ctx.type = t
+ end
+ if t == "raw" then
+ bcsave_raw(output, s)
+ else
+ if not ctx.modname then ctx.modname = detectmodname(input) end
+ if t == "obj" then
+ bcsave_obj(ctx, output, s)
+ else
+ bcsave_c(ctx, output, s)
+ end
+ end
+end
+
+local function docmd(...)
+ local arg = {...}
+ local n = 1
+ local list = false
+ local ctx = {
+ mode = "bt", arch = jit.arch, os = jit.os:lower(),
+ type = false, modname = false, string = false,
+ }
+ local strip = "s"
+ local gc64 = ""
+ while n <= #arg do
+ local a = arg[n]
+ if type(a) == "string" and a:sub(1, 1) == "-" and a ~= "-" then
+ tremove(arg, n)
+ if a == "--" then break end
+ for m=2,#a do
+ local opt = a:sub(m, m)
+ if opt == "l" then
+ list = true
+ elseif opt == "s" then
+ strip = "s"
+ elseif opt == "g" then
+ strip = ""
+ elseif opt == "W" or opt == "X" then
+ gc64 = opt
+ elseif opt == "d" then
+ ctx.mode = ctx.mode .. opt
+ else
+ if arg[n] == nil or m ~= #a then usage() end
+ if opt == "e" then
+ if n ~= 1 then usage() end
+ ctx.string = true
+ elseif opt == "n" then
+ ctx.modname = checkmodname(tremove(arg, n))
+ elseif opt == "t" then
+ ctx.type = checkarg(tremove(arg, n), map_type, "file type")
+ elseif opt == "a" then
+ ctx.arch = checkarg(tremove(arg, n), map_arch, "architecture")
+ elseif opt == "o" then
+ ctx.os = checkarg(tremove(arg, n), map_os, "OS name")
+ elseif opt == "F" then
+ ctx.filename = "@"..tremove(arg, n)
+ else
+ usage()
+ end
+ end
+ end
+ else
+ n = n + 1
+ end
+ end
+ ctx.mode = ctx.mode .. strip .. gc64
+ if list then
+ if #arg == 0 or #arg > 2 then usage() end
+ bclist(ctx, arg[1], arg[2] or "-")
+ else
+ if #arg ~= 2 then usage() end
+ bcsave(ctx, arg[1], arg[2])
+ end
+end
+
+------------------------------------------------------------------------------
+
+-- Public module functions.
+return {
+ start = docmd -- Process -b command line option.
+}
+
diff --git a/jit/dis_arm.lua b/jit/dis_arm.lua
new file mode 100644
index 0000000..0adc799
--- /dev/null
+++ b/jit/dis_arm.lua
@@ -0,0 +1,689 @@
+----------------------------------------------------------------------------
+-- LuaJIT ARM disassembler module.
+--
+-- Copyright (C) 2005-2026 Mike Pall. All rights reserved.
+-- Released under the MIT license. See Copyright Notice in luajit.h
+----------------------------------------------------------------------------
+-- This is a helper module used by the LuaJIT machine code dumper module.
+--
+-- It disassembles most user-mode ARMv7 instructions
+-- NYI: Advanced SIMD and VFP instructions.
+------------------------------------------------------------------------------
+
+local type = type
+local sub, byte, format = string.sub, string.byte, string.format
+local match, gmatch = string.match, string.gmatch
+local concat = table.concat
+local bit = require("bit")
+local band, bor, ror, tohex = bit.band, bit.bor, bit.ror, bit.tohex
+local lshift, rshift, arshift = bit.lshift, bit.rshift, bit.arshift
+
+------------------------------------------------------------------------------
+-- Opcode maps
+------------------------------------------------------------------------------
+
+local map_loadc = {
+ shift = 8, mask = 15,
+ [10] = {
+ shift = 20, mask = 1,
+ [0] = {
+ shift = 23, mask = 3,
+ [0] = "vmovFmDN", "vstmFNdr",
+ _ = {
+ shift = 21, mask = 1,
+ [0] = "vstrFdl",
+ { shift = 16, mask = 15, [13] = "vpushFdr", _ = "vstmdbFNdr", }
+ },
+ },
+ {
+ shift = 23, mask = 3,
+ [0] = "vmovFDNm",
+ { shift = 16, mask = 15, [13] = "vpopFdr", _ = "vldmFNdr", },
+ _ = {
+ shift = 21, mask = 1,
+ [0] = "vldrFdl", "vldmdbFNdr",
+ },
+ },
+ },
+ [11] = {
+ shift = 20, mask = 1,
+ [0] = {
+ shift = 23, mask = 3,
+ [0] = "vmovGmDN", "vstmGNdr",
+ _ = {
+ shift = 21, mask = 1,
+ [0] = "vstrGdl",
+ { shift = 16, mask = 15, [13] = "vpushGdr", _ = "vstmdbGNdr", }
+ },
+ },
+ {
+ shift = 23, mask = 3,
+ [0] = "vmovGDNm",
+ { shift = 16, mask = 15, [13] = "vpopGdr", _ = "vldmGNdr", },
+ _ = {
+ shift = 21, mask = 1,
+ [0] = "vldrGdl", "vldmdbGNdr",
+ },
+ },
+ },
+ _ = {
+ shift = 0, mask = 0 -- NYI ldc, mcrr, mrrc.
+ },
+}
+
+local map_vfps = {
+ shift = 6, mask = 0x2c001,
+ [0] = "vmlaF.dnm", "vmlsF.dnm",
+ [0x04000] = "vnmlsF.dnm", [0x04001] = "vnmlaF.dnm",
+ [0x08000] = "vmulF.dnm", [0x08001] = "vnmulF.dnm",
+ [0x0c000] = "vaddF.dnm", [0x0c001] = "vsubF.dnm",
+ [0x20000] = "vdivF.dnm",
+ [0x24000] = "vfnmsF.dnm", [0x24001] = "vfnmaF.dnm",
+ [0x28000] = "vfmaF.dnm", [0x28001] = "vfmsF.dnm",
+ [0x2c000] = "vmovF.dY",
+ [0x2c001] = {
+ shift = 7, mask = 0x1e01,
+ [0] = "vmovF.dm", "vabsF.dm",
+ [0x0200] = "vnegF.dm", [0x0201] = "vsqrtF.dm",
+ [0x0800] = "vcmpF.dm", [0x0801] = "vcmpeF.dm",
+ [0x0a00] = "vcmpzF.d", [0x0a01] = "vcmpzeF.d",
+ [0x0e01] = "vcvtG.dF.m",
+ [0x1000] = "vcvt.f32.u32Fdm", [0x1001] = "vcvt.f32.s32Fdm",
+ [0x1800] = "vcvtr.u32F.dm", [0x1801] = "vcvt.u32F.dm",
+ [0x1a00] = "vcvtr.s32F.dm", [0x1a01] = "vcvt.s32F.dm",
+ },
+}
+
+local map_vfpd = {
+ shift = 6, mask = 0x2c001,
+ [0] = "vmlaG.dnm", "vmlsG.dnm",
+ [0x04000] = "vnmlsG.dnm", [0x04001] = "vnmlaG.dnm",
+ [0x08000] = "vmulG.dnm", [0x08001] = "vnmulG.dnm",
+ [0x0c000] = "vaddG.dnm", [0x0c001] = "vsubG.dnm",
+ [0x20000] = "vdivG.dnm",
+ [0x24000] = "vfnmsG.dnm", [0x24001] = "vfnmaG.dnm",
+ [0x28000] = "vfmaG.dnm", [0x28001] = "vfmsG.dnm",
+ [0x2c000] = "vmovG.dY",
+ [0x2c001] = {
+ shift = 7, mask = 0x1e01,
+ [0] = "vmovG.dm", "vabsG.dm",
+ [0x0200] = "vnegG.dm", [0x0201] = "vsqrtG.dm",
+ [0x0800] = "vcmpG.dm", [0x0801] = "vcmpeG.dm",
+ [0x0a00] = "vcmpzG.d", [0x0a01] = "vcmpzeG.d",
+ [0x0e01] = "vcvtF.dG.m",
+ [0x1000] = "vcvt.f64.u32GdFm", [0x1001] = "vcvt.f64.s32GdFm",
+ [0x1800] = "vcvtr.u32FdG.m", [0x1801] = "vcvt.u32FdG.m",
+ [0x1a00] = "vcvtr.s32FdG.m", [0x1a01] = "vcvt.s32FdG.m",
+ },
+}
+
+local map_datac = {
+ shift = 24, mask = 1,
+ [0] = {
+ shift = 4, mask = 1,
+ [0] = {
+ shift = 8, mask = 15,
+ [10] = map_vfps,
+ [11] = map_vfpd,
+ -- NYI cdp, mcr, mrc.
+ },
+ {
+ shift = 8, mask = 15,
+ [10] = {
+ shift = 20, mask = 15,
+ [0] = "vmovFnD", "vmovFDn",
+ [14] = "vmsrD",
+ [15] = { shift = 12, mask = 15, [15] = "vmrs", _ = "vmrsD", },
+ },
+ },
+ },
+ "svcT",
+}
+
+local map_loadcu = {
+ shift = 0, mask = 0, -- NYI unconditional CP load/store.
+}
+
+local map_datacu = {
+ shift = 0, mask = 0, -- NYI unconditional CP data.
+}
+
+local map_simddata = {
+ shift = 0, mask = 0, -- NYI SIMD data.
+}
+
+local map_simdload = {
+ shift = 0, mask = 0, -- NYI SIMD load/store, preload.
+}
+
+local map_preload = {
+ shift = 0, mask = 0, -- NYI preload.
+}
+
+local map_media = {
+ shift = 20, mask = 31,
+ [0] = false,
+ { --01
+ shift = 5, mask = 7,
+ [0] = "sadd16DNM", "sasxDNM", "ssaxDNM", "ssub16DNM",
+ "sadd8DNM", false, false, "ssub8DNM",
+ },
+ { --02
+ shift = 5, mask = 7,
+ [0] = "qadd16DNM", "qasxDNM", "qsaxDNM", "qsub16DNM",
+ "qadd8DNM", false, false, "qsub8DNM",
+ },
+ { --03
+ shift = 5, mask = 7,
+ [0] = "shadd16DNM", "shasxDNM", "shsaxDNM", "shsub16DNM",
+ "shadd8DNM", false, false, "shsub8DNM",
+ },
+ false,
+ { --05
+ shift = 5, mask = 7,
+ [0] = "uadd16DNM", "uasxDNM", "usaxDNM", "usub16DNM",
+ "uadd8DNM", false, false, "usub8DNM",
+ },
+ { --06
+ shift = 5, mask = 7,
+ [0] = "uqadd16DNM", "uqasxDNM", "uqsaxDNM", "uqsub16DNM",
+ "uqadd8DNM", false, false, "uqsub8DNM",
+ },
+ { --07
+ shift = 5, mask = 7,
+ [0] = "uhadd16DNM", "uhasxDNM", "uhsaxDNM", "uhsub16DNM",
+ "uhadd8DNM", false, false, "uhsub8DNM",
+ },
+ { --08
+ shift = 5, mask = 7,
+ [0] = "pkhbtDNMU", false, "pkhtbDNMU",
+ { shift = 16, mask = 15, [15] = "sxtb16DMU", _ = "sxtab16DNMU", },
+ "pkhbtDNMU", "selDNM", "pkhtbDNMU",
+ },
+ false,
+ { --0a
+ shift = 5, mask = 7,
+ [0] = "ssatDxMu", "ssat16DxM", "ssatDxMu",
+ { shift = 16, mask = 15, [15] = "sxtbDMU", _ = "sxtabDNMU", },
+ "ssatDxMu", false, "ssatDxMu",
+ },
+ { --0b
+ shift = 5, mask = 7,
+ [0] = "ssatDxMu", "revDM", "ssatDxMu",
+ { shift = 16, mask = 15, [15] = "sxthDMU", _ = "sxtahDNMU", },
+ "ssatDxMu", "rev16DM", "ssatDxMu",
+ },
+ { --0c
+ shift = 5, mask = 7,
+ [3] = { shift = 16, mask = 15, [15] = "uxtb16DMU", _ = "uxtab16DNMU", },
+ },
+ false,
+ { --0e
+ shift = 5, mask = 7,
+ [0] = "usatDwMu", "usat16DwM", "usatDwMu",
+ { shift = 16, mask = 15, [15] = "uxtbDMU", _ = "uxtabDNMU", },
+ "usatDwMu", false, "usatDwMu",
+ },
+ { --0f
+ shift = 5, mask = 7,
+ [0] = "usatDwMu", "rbitDM", "usatDwMu",
+ { shift = 16, mask = 15, [15] = "uxthDMU", _ = "uxtahDNMU", },
+ "usatDwMu", "revshDM", "usatDwMu",
+ },
+ { --10
+ shift = 12, mask = 15,
+ [15] = {
+ shift = 5, mask = 7,
+ "smuadNMS", "smuadxNMS", "smusdNMS", "smusdxNMS",
+ },
+ _ = {
+ shift = 5, mask = 7,
+ [0] = "smladNMSD", "smladxNMSD", "smlsdNMSD", "smlsdxNMSD",
+ },
+ },
+ false, false, false,
+ { --14
+ shift = 5, mask = 7,
+ [0] = "smlaldDNMS", "smlaldxDNMS", "smlsldDNMS", "smlsldxDNMS",
+ },
+ { --15
+ shift = 5, mask = 7,
+ [0] = { shift = 12, mask = 15, [15] = "smmulNMS", _ = "smmlaNMSD", },
+ { shift = 12, mask = 15, [15] = "smmulrNMS", _ = "smmlarNMSD", },
+ false, false, false, false,
+ "smmlsNMSD", "smmlsrNMSD",
+ },
+ false, false,
+ { --18
+ shift = 5, mask = 7,
+ [0] = { shift = 12, mask = 15, [15] = "usad8NMS", _ = "usada8NMSD", },
+ },
+ false,
+ { --1a
+ shift = 5, mask = 3, [2] = "sbfxDMvw",
+ },
+ { --1b
+ shift = 5, mask = 3, [2] = "sbfxDMvw",
+ },
+ { --1c
+ shift = 5, mask = 3,
+ [0] = { shift = 0, mask = 15, [15] = "bfcDvX", _ = "bfiDMvX", },
+ },
+ { --1d
+ shift = 5, mask = 3,
+ [0] = { shift = 0, mask = 15, [15] = "bfcDvX", _ = "bfiDMvX", },
+ },
+ { --1e
+ shift = 5, mask = 3, [2] = "ubfxDMvw",
+ },
+ { --1f
+ shift = 5, mask = 3, [2] = "ubfxDMvw",
+ },
+}
+
+local map_load = {
+ shift = 21, mask = 9,
+ {
+ shift = 20, mask = 5,
+ [0] = "strtDL", "ldrtDL", [4] = "strbtDL", [5] = "ldrbtDL",
+ },
+ _ = {
+ shift = 20, mask = 5,
+ [0] = "strDL", "ldrDL", [4] = "strbDL", [5] = "ldrbDL",
+ }
+}
+
+local map_load1 = {
+ shift = 4, mask = 1,
+ [0] = map_load, map_media,
+}
+
+local map_loadm = {
+ shift = 20, mask = 1,
+ [0] = {
+ shift = 23, mask = 3,
+ [0] = "stmdaNR", "stmNR",
+ { shift = 16, mask = 63, [45] = "pushR", _ = "stmdbNR", }, "stmibNR",
+ },
+ {
+ shift = 23, mask = 3,
+ [0] = "ldmdaNR", { shift = 16, mask = 63, [61] = "popR", _ = "ldmNR", },
+ "ldmdbNR", "ldmibNR",
+ },
+}
+
+local map_data = {
+ shift = 21, mask = 15,
+ [0] = "andDNPs", "eorDNPs", "subDNPs", "rsbDNPs",
+ "addDNPs", "adcDNPs", "sbcDNPs", "rscDNPs",
+ "tstNP", "teqNP", "cmpNP", "cmnNP",
+ "orrDNPs", "movDPs", "bicDNPs", "mvnDPs",
+}
+
+local map_mul = {
+ shift = 21, mask = 7,
+ [0] = "mulNMSs", "mlaNMSDs", "umaalDNMS", "mlsDNMS",
+ "umullDNMSs", "umlalDNMSs", "smullDNMSs", "smlalDNMSs",
+}
+
+local map_sync = {
+ shift = 20, mask = 15, -- NYI: brackets around N. R(D+1) for ldrexd/strexd.
+ [0] = "swpDMN", false, false, false,
+ "swpbDMN", false, false, false,
+ "strexDMN", "ldrexDN", "strexdDN", "ldrexdDN",
+ "strexbDMN", "ldrexbDN", "strexhDN", "ldrexhDN",
+}
+
+local map_mulh = {
+ shift = 21, mask = 3,
+ [0] = { shift = 5, mask = 3,
+ [0] = "smlabbNMSD", "smlatbNMSD", "smlabtNMSD", "smlattNMSD", },
+ { shift = 5, mask = 3,
+ [0] = "smlawbNMSD", "smulwbNMS", "smlawtNMSD", "smulwtNMS", },
+ { shift = 5, mask = 3,
+ [0] = "smlalbbDNMS", "smlaltbDNMS", "smlalbtDNMS", "smlalttDNMS", },
+ { shift = 5, mask = 3,
+ [0] = "smulbbNMS", "smultbNMS", "smulbtNMS", "smulttNMS", },
+}
+
+local map_misc = {
+ shift = 4, mask = 7,
+ -- NYI: decode PSR bits of msr.
+ [0] = { shift = 21, mask = 1, [0] = "mrsD", "msrM", },
+ { shift = 21, mask = 3, "bxM", false, "clzDM", },
+ { shift = 21, mask = 3, "bxjM", },
+ { shift = 21, mask = 3, "blxM", },
+ false,
+ { shift = 21, mask = 3, [0] = "qaddDMN", "qsubDMN", "qdaddDMN", "qdsubDMN", },
+ false,
+ { shift = 21, mask = 3, "bkptK", },
+}
+
+local map_datar = {
+ shift = 4, mask = 9,
+ [9] = {
+ shift = 5, mask = 3,
+ [0] = { shift = 24, mask = 1, [0] = map_mul, map_sync, },
+ { shift = 20, mask = 1, [0] = "strhDL", "ldrhDL", },
+ { shift = 20, mask = 1, [0] = "ldrdDL", "ldrsbDL", },
+ { shift = 20, mask = 1, [0] = "strdDL", "ldrshDL", },
+ },
+ _ = {
+ shift = 20, mask = 25,
+ [16] = { shift = 7, mask = 1, [0] = map_misc, map_mulh, },
+ _ = {
+ shift = 0, mask = 0xffffffff,
+ [bor(0xe1a00000)] = "nop",
+ _ = map_data,
+ }
+ },
+}
+
+local map_datai = {
+ shift = 20, mask = 31, -- NYI: decode PSR bits of msr. Decode imm12.
+ [16] = "movwDW", [20] = "movtDW",
+ [18] = { shift = 0, mask = 0xf00ff, [0] = "nopv6", _ = "msrNW", },
+ [22] = "msrNW",
+ _ = map_data,
+}
+
+local map_branch = {
+ shift = 24, mask = 1,
+ [0] = "bB", "blB"
+}
+
+local map_condins = {
+ [0] = map_datar, map_datai, map_load, map_load1,
+ map_loadm, map_branch, map_loadc, map_datac
+}
+
+-- NYI: setend.
+local map_uncondins = {
+ [0] = false, map_simddata, map_simdload, map_preload,
+ false, "blxB", map_loadcu, map_datacu,
+}
+
+------------------------------------------------------------------------------
+
+local map_gpr = {
+ [0] = "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
+ "r8", "r9", "r10", "r11", "r12", "sp", "lr", "pc",
+}
+
+local map_cond = {
+ [0] = "eq", "ne", "hs", "lo", "mi", "pl", "vs", "vc",
+ "hi", "ls", "ge", "lt", "gt", "le", "al",
+}
+
+local map_shift = { [0] = "lsl", "lsr", "asr", "ror", }
+
+------------------------------------------------------------------------------
+
+-- Output a nicely formatted line with an opcode and operands.
+local function putop(ctx, text, operands)
+ local pos = ctx.pos
+ local extra = ""
+ if ctx.rel then
+ local sym = ctx.symtab[ctx.rel]
+ if sym then
+ extra = "\t->"..sym
+ elseif band(ctx.op, 0x0e000000) ~= 0x0a000000 then
+ extra = "\t; 0x"..tohex(ctx.rel)
+ end
+ end
+ if ctx.hexdump > 0 then
+ ctx.out(format("%08x %s %-5s %s%s\n",
+ ctx.addr+pos, tohex(ctx.op), text, concat(operands, ", "), extra))
+ else
+ ctx.out(format("%08x %-5s %s%s\n",
+ ctx.addr+pos, text, concat(operands, ", "), extra))
+ end
+ ctx.pos = pos + 4
+end
+
+-- Fallback for unknown opcodes.
+local function unknown(ctx)
+ return putop(ctx, ".long", { "0x"..tohex(ctx.op) })
+end
+
+-- Format operand 2 of load/store opcodes.
+local function fmtload(ctx, op, pos)
+ local base = map_gpr[band(rshift(op, 16), 15)]
+ local x, ofs
+ local ext = (band(op, 0x04000000) == 0)
+ if not ext and band(op, 0x02000000) == 0 then
+ ofs = band(op, 4095)
+ if band(op, 0x00800000) == 0 then ofs = -ofs end
+ if base == "pc" then ctx.rel = ctx.addr + pos + 8 + ofs end
+ ofs = "#"..ofs
+ elseif ext and band(op, 0x00400000) ~= 0 then
+ ofs = band(op, 15) + band(rshift(op, 4), 0xf0)
+ if band(op, 0x00800000) == 0 then ofs = -ofs end
+ if base == "pc" then ctx.rel = ctx.addr + pos + 8 + ofs end
+ ofs = "#"..ofs
+ else
+ ofs = map_gpr[band(op, 15)]
+ if ext or band(op, 0xfe0) == 0 then
+ elseif band(op, 0xfe0) == 0x60 then
+ ofs = format("%s, rrx", ofs)
+ else
+ local sh = band(rshift(op, 7), 31)
+ if sh == 0 then sh = 32 end
+ ofs = format("%s, %s #%d", ofs, map_shift[band(rshift(op, 5), 3)], sh)
+ end
+ if band(op, 0x00800000) == 0 then ofs = "-"..ofs end
+ end
+ if ofs == "#0" then
+ x = format("[%s]", base)
+ elseif band(op, 0x01000000) == 0 then
+ x = format("[%s], %s", base, ofs)
+ else
+ x = format("[%s, %s]", base, ofs)
+ end
+ if band(op, 0x01200000) == 0x01200000 then x = x.."!" end
+ return x
+end
+
+-- Format operand 2 of vector load/store opcodes.
+local function fmtvload(ctx, op, pos)
+ local base = map_gpr[band(rshift(op, 16), 15)]
+ local ofs = band(op, 255)*4
+ if band(op, 0x00800000) == 0 then ofs = -ofs end
+ if base == "pc" then ctx.rel = ctx.addr + pos + 8 + ofs end
+ if ofs == 0 then
+ return format("[%s]", base)
+ else
+ return format("[%s, #%d]", base, ofs)
+ end
+end
+
+local function fmtvr(op, vr, sh0, sh1)
+ if vr == "s" then
+ return format("s%d", 2*band(rshift(op, sh0), 15)+band(rshift(op, sh1), 1))
+ else
+ return format("d%d", band(rshift(op, sh0), 15)+band(rshift(op, sh1-4), 16))
+ end
+end
+
+-- Disassemble a single instruction.
+local function disass_ins(ctx)
+ local pos = ctx.pos
+ local b0, b1, b2, b3 = byte(ctx.code, pos+1, pos+4)
+ local op = bor(lshift(b3, 24), lshift(b2, 16), lshift(b1, 8), b0)
+ local operands = {}
+ local suffix = ""
+ local last, name, pat
+ local vr
+ ctx.op = op
+ ctx.rel = nil
+
+ local cond = rshift(op, 28)
+ local opat
+ if cond == 15 then
+ opat = map_uncondins[band(rshift(op, 25), 7)]
+ else
+ if cond ~= 14 then suffix = map_cond[cond] end
+ opat = map_condins[band(rshift(op, 25), 7)]
+ end
+ while type(opat) ~= "string" do
+ if not opat then return unknown(ctx) end
+ opat = opat[band(rshift(op, opat.shift), opat.mask)] or opat._
+ end
+ name, pat = match(opat, "^([a-z0-9]*)(.*)")
+ if sub(pat, 1, 1) == "." then
+ local s2, p2 = match(pat, "^([a-z0-9.]*)(.*)")
+ suffix = suffix..s2
+ pat = p2
+ end
+
+ for p in gmatch(pat, ".") do
+ local x = nil
+ if p == "D" then
+ x = map_gpr[band(rshift(op, 12), 15)]
+ elseif p == "N" then
+ x = map_gpr[band(rshift(op, 16), 15)]
+ elseif p == "S" then
+ x = map_gpr[band(rshift(op, 8), 15)]
+ elseif p == "M" then
+ x = map_gpr[band(op, 15)]
+ elseif p == "d" then
+ x = fmtvr(op, vr, 12, 22)
+ elseif p == "n" then
+ x = fmtvr(op, vr, 16, 7)
+ elseif p == "m" then
+ x = fmtvr(op, vr, 0, 5)
+ elseif p == "P" then
+ if band(op, 0x02000000) ~= 0 then
+ x = ror(band(op, 255), 2*band(rshift(op, 8), 15))
+ else
+ x = map_gpr[band(op, 15)]
+ if band(op, 0xff0) ~= 0 then
+ operands[#operands+1] = x
+ local s = map_shift[band(rshift(op, 5), 3)]
+ local r = nil
+ if band(op, 0xf90) == 0 then
+ if s == "ror" then s = "rrx" else r = "#32" end
+ elseif band(op, 0x10) == 0 then
+ r = "#"..band(rshift(op, 7), 31)
+ else
+ r = map_gpr[band(rshift(op, 8), 15)]
+ end
+ if name == "mov" then name = s; x = r
+ elseif r then x = format("%s %s", s, r)
+ else x = s end
+ end
+ end
+ elseif p == "L" then
+ x = fmtload(ctx, op, pos)
+ elseif p == "l" then
+ x = fmtvload(ctx, op, pos)
+ elseif p == "B" then
+ local addr = ctx.addr + pos + 8 + arshift(lshift(op, 8), 6)
+ if cond == 15 then addr = addr + band(rshift(op, 23), 2) end
+ ctx.rel = addr
+ x = "0x"..tohex(addr)
+ elseif p == "F" then
+ vr = "s"
+ elseif p == "G" then
+ vr = "d"
+ elseif p == "." then
+ suffix = suffix..(vr == "s" and ".f32" or ".f64")
+ elseif p == "R" then
+ if band(op, 0x00200000) ~= 0 and #operands == 1 then
+ operands[1] = operands[1].."!"
+ end
+ local t = {}
+ for i=0,15 do
+ if band(rshift(op, i), 1) == 1 then t[#t+1] = map_gpr[i] end
+ end
+ x = "{"..concat(t, ", ").."}"
+ elseif p == "r" then
+ if band(op, 0x00200000) ~= 0 and #operands == 2 then
+ operands[1] = operands[1].."!"
+ end
+ local s = tonumber(sub(last, 2))
+ local n = band(op, 255)
+ if vr == "d" then n = rshift(n, 1) end
+ operands[#operands] = format("{%s-%s%d}", last, vr, s+n-1)
+ elseif p == "W" then
+ x = band(op, 0x0fff) + band(rshift(op, 4), 0xf000)
+ elseif p == "T" then
+ x = "#0x"..tohex(band(op, 0x00ffffff), 6)
+ elseif p == "U" then
+ x = band(rshift(op, 7), 31)
+ if x == 0 then x = nil end
+ elseif p == "u" then
+ x = band(rshift(op, 7), 31)
+ if band(op, 0x40) == 0 then
+ if x == 0 then x = nil else x = "lsl #"..x end
+ else
+ if x == 0 then x = "asr #32" else x = "asr #"..x end
+ end
+ elseif p == "v" then
+ x = band(rshift(op, 7), 31)
+ elseif p == "w" then
+ x = band(rshift(op, 16), 31)
+ elseif p == "x" then
+ x = band(rshift(op, 16), 31) + 1
+ elseif p == "X" then
+ x = band(rshift(op, 16), 31) - last + 1
+ elseif p == "Y" then
+ x = band(rshift(op, 12), 0xf0) + band(op, 0x0f)
+ elseif p == "K" then
+ x = "#0x"..tohex(band(rshift(op, 4), 0x0000fff0) + band(op, 15), 4)
+ elseif p == "s" then
+ if band(op, 0x00100000) ~= 0 then suffix = "s"..suffix end
+ else
+ assert(false)
+ end
+ if x then
+ last = x
+ if type(x) == "number" then x = "#"..x end
+ operands[#operands+1] = x
+ end
+ end
+
+ return putop(ctx, name..suffix, operands)
+end
+
+------------------------------------------------------------------------------
+
+-- Disassemble a block of code.
+local function disass_block(ctx, ofs, len)
+ if not ofs then ofs = 0 end
+ local stop = len and ofs+len or #ctx.code
+ ctx.pos = ofs
+ ctx.rel = nil
+ while ctx.pos < stop do disass_ins(ctx) end
+end
+
+-- Extended API: create a disassembler context. Then call ctx:disass(ofs, len).
+local function create(code, addr, out)
+ local ctx = {}
+ ctx.code = code
+ ctx.addr = addr or 0
+ ctx.out = out or io.write
+ ctx.symtab = {}
+ ctx.disass = disass_block
+ ctx.hexdump = 8
+ return ctx
+end
+
+-- Simple API: disassemble code (a string) at address and output via out.
+local function disass(code, addr, out)
+ create(code, addr, out):disass()
+end
+
+-- Return register name for RID.
+local function regname(r)
+ if r < 16 then return map_gpr[r] end
+ return "d"..(r-16)
+end
+
+-- Public module functions.
+return {
+ create = create,
+ disass = disass,
+ regname = regname
+}
+
diff --git a/jit/dis_arm64.lua b/jit/dis_arm64.lua
new file mode 100644
index 0000000..896fab7
--- /dev/null
+++ b/jit/dis_arm64.lua
@@ -0,0 +1,1233 @@
+----------------------------------------------------------------------------
+-- LuaJIT ARM64 disassembler module.
+--
+-- Copyright (C) 2005-2026 Mike Pall. All rights reserved.
+-- Released under the MIT license. See Copyright Notice in luajit.h
+--
+-- Contributed by Djordje Kovacevic and Stefan Pejic from RT-RK.com.
+-- Sponsored by Cisco Systems, Inc.
+----------------------------------------------------------------------------
+-- This is a helper module used by the LuaJIT machine code dumper module.
+--
+-- It disassembles most user-mode AArch64 instructions.
+-- NYI: Advanced SIMD and VFP instructions.
+------------------------------------------------------------------------------
+
+local type = type
+local sub, byte, format = string.sub, string.byte, string.format
+local match, gmatch, gsub = string.match, string.gmatch, string.gsub
+local concat = table.concat
+local bit = require("bit")
+local band, bor, bxor, tohex = bit.band, bit.bor, bit.bxor, bit.tohex
+local lshift, rshift, arshift = bit.lshift, bit.rshift, bit.arshift
+local ror = bit.ror
+
+------------------------------------------------------------------------------
+-- Opcode maps
+------------------------------------------------------------------------------
+
+local map_adr = { -- PC-relative addressing.
+ shift = 31, mask = 1,
+ [0] = "adrDBx", "adrpDBx"
+}
+
+local map_addsubi = { -- Add/subtract immediate.
+ shift = 29, mask = 3,
+ [0] = "add|movDNIg", "adds|cmnD0NIg", "subDNIg", "subs|cmpD0NIg",
+}
+
+local map_logi = { -- Logical immediate.
+ shift = 31, mask = 1,
+ [0] = {
+ shift = 22, mask = 1,
+ [0] = {
+ shift = 29, mask = 3,
+ [0] = "andDNig", "orr|movDN0ig", "eorDNig", "ands|tstD0Nig"
+ },
+ false -- unallocated
+ },
+ {
+ shift = 29, mask = 3,
+ [0] = "andDNig", "orr|movDN0ig", "eorDNig", "ands|tstD0Nig"
+ }
+}
+
+local map_movwi = { -- Move wide immediate.
+ shift = 31, mask = 1,
+ [0] = {
+ shift = 22, mask = 1,
+ [0] = {
+ shift = 29, mask = 3,
+ [0] = "movnDWRg", false, "movz|movDYRg", "movkDWRg"
+ }, false -- unallocated
+ },
+ {
+ shift = 29, mask = 3,
+ [0] = "movnDWRg", false, "movz|movDYRg", "movkDWRg"
+ },
+}
+
+local map_bitf = { -- Bitfield.
+ shift = 31, mask = 1,
+ [0] = {
+ shift = 22, mask = 1,
+ [0] = {
+ shift = 29, mask = 3,
+ [0] = "sbfm|sbfiz|sbfx|asr|sxtw|sxth|sxtbDN12w",
+ "bfm|bfi|bfxilDN13w",
+ "ubfm|ubfiz|ubfx|lsr|lsl|uxth|uxtbDN12w"
+ }
+ },
+ {
+ shift = 22, mask = 1,
+ {
+ shift = 29, mask = 3,
+ [0] = "sbfm|sbfiz|sbfx|asr|sxtw|sxth|sxtbDN12x",
+ "bfm|bfi|bfxilDN13x",
+ "ubfm|ubfiz|ubfx|lsr|lsl|uxth|uxtbDN12x"
+ }
+ }
+}
+
+local map_datai = { -- Data processing - immediate.
+ shift = 23, mask = 7,
+ [0] = map_adr, map_adr, map_addsubi, false,
+ map_logi, map_movwi, map_bitf,
+ {
+ shift = 15, mask = 0x1c0c1,
+ [0] = "extr|rorDNM4w", [0x10080] = "extr|rorDNM4x",
+ [0x10081] = "extr|rorDNM4x"
+ }
+}
+
+local map_logsr = { -- Logical, shifted register.
+ shift = 31, mask = 1,
+ [0] = {
+ shift = 15, mask = 1,
+ [0] = {
+ shift = 29, mask = 3,
+ [0] = {
+ shift = 21, mask = 1,
+ [0] = "andDNMSg", "bicDNMSg"
+ },
+ {
+ shift = 21, mask = 1,
+ [0] = "orr|movDN0MSg", "orn|mvnDN0MSg"
+ },
+ {
+ shift = 21, mask = 1,
+ [0] = "eorDNMSg", "eonDNMSg"
+ },
+ {
+ shift = 21, mask = 1,
+ [0] = "ands|tstD0NMSg", "bicsDNMSg"
+ }
+ },
+ false -- unallocated
+ },
+ {
+ shift = 29, mask = 3,
+ [0] = {
+ shift = 21, mask = 1,
+ [0] = "andDNMSg", "bicDNMSg"
+ },
+ {
+ shift = 21, mask = 1,
+ [0] = "orr|movDN0MSg", "orn|mvnDN0MSg"
+ },
+ {
+ shift = 21, mask = 1,
+ [0] = "eorDNMSg", "eonDNMSg"
+ },
+ {
+ shift = 21, mask = 1,
+ [0] = "ands|tstD0NMSg", "bicsDNMSg"
+ }
+ }
+}
+
+local map_assh = {
+ shift = 31, mask = 1,
+ [0] = {
+ shift = 15, mask = 1,
+ [0] = {
+ shift = 29, mask = 3,
+ [0] = {
+ shift = 22, mask = 3,
+ [0] = "addDNMSg", "addDNMSg", "addDNMSg", "addDNMg"
+ },
+ {
+ shift = 22, mask = 3,
+ [0] = "adds|cmnD0NMSg", "adds|cmnD0NMSg",
+ "adds|cmnD0NMSg", "adds|cmnD0NMg"
+ },
+ {
+ shift = 22, mask = 3,
+ [0] = "sub|negDN0MSg", "sub|negDN0MSg", "sub|negDN0MSg", "sub|negDN0Mg"
+ },
+ {
+ shift = 22, mask = 3,
+ [0] = "subs|cmp|negsD0N0MzSg", "subs|cmp|negsD0N0MzSg",
+ "subs|cmp|negsD0N0MzSg", "subs|cmp|negsD0N0Mzg"
+ },
+ },
+ false -- unallocated
+ },
+ {
+ shift = 29, mask = 3,
+ [0] = {
+ shift = 22, mask = 3,
+ [0] = "addDNMSg", "addDNMSg", "addDNMSg", "addDNMg"
+ },
+ {
+ shift = 22, mask = 3,
+ [0] = "adds|cmnD0NMSg", "adds|cmnD0NMSg", "adds|cmnD0NMSg",
+ "adds|cmnD0NMg"
+ },
+ {
+ shift = 22, mask = 3,
+ [0] = "sub|negDN0MSg", "sub|negDN0MSg", "sub|negDN0MSg", "sub|negDN0Mg"
+ },
+ {
+ shift = 22, mask = 3,
+ [0] = "subs|cmp|negsD0N0MzSg", "subs|cmp|negsD0N0MzSg",
+ "subs|cmp|negsD0N0MzSg", "subs|cmp|negsD0N0Mzg"
+ }
+ }
+}
+
+local map_addsubsh = { -- Add/subtract, shifted register.
+ shift = 22, mask = 3,
+ [0] = map_assh, map_assh, map_assh
+}
+
+local map_addsubex = { -- Add/subtract, extended register.
+ shift = 22, mask = 3,
+ [0] = {
+ shift = 29, mask = 3,
+ [0] = "addDNMXg", "adds|cmnD0NMXg", "subDNMXg", "subs|cmpD0NMzXg",
+ }
+}
+
+local map_addsubc = { -- Add/subtract, with carry.
+ shift = 10, mask = 63,
+ [0] = {
+ shift = 29, mask = 3,
+ [0] = "adcDNMg", "adcsDNMg", "sbc|ngcDN0Mg", "sbcs|ngcsDN0Mg",
+ }
+}
+
+local map_ccomp = {
+ shift = 4, mask = 1,
+ [0] = {
+ shift = 10, mask = 3,
+ [0] = { -- Conditional compare register.
+ shift = 29, mask = 3,
+ "ccmnNMVCg", false, "ccmpNMVCg",
+ },
+ [2] = { -- Conditional compare immediate.
+ shift = 29, mask = 3,
+ "ccmnN5VCg", false, "ccmpN5VCg",
+ }
+ }
+}
+
+local map_csel = { -- Conditional select.
+ shift = 11, mask = 1,
+ [0] = {
+ shift = 10, mask = 1,
+ [0] = {
+ shift = 29, mask = 3,
+ [0] = "cselDNMzCg", false, "csinv|cinv|csetmDNMcg", false,
+ },
+ {
+ shift = 29, mask = 3,
+ [0] = "csinc|cinc|csetDNMcg", false, "csneg|cnegDNMcg", false,
+ }
+ }
+}
+
+local map_data1s = { -- Data processing, 1 source.
+ shift = 29, mask = 1,
+ [0] = {
+ shift = 31, mask = 1,
+ [0] = {
+ shift = 10, mask = 0x7ff,
+ [0] = "rbitDNg", "rev16DNg", "revDNw", false, "clzDNg", "clsDNg"
+ },
+ {
+ shift = 10, mask = 0x7ff,
+ [0] = "rbitDNg", "rev16DNg", "rev32DNx", "revDNx", "clzDNg", "clsDNg"
+ }
+ }
+}
+
+local map_data2s = { -- Data processing, 2 sources.
+ shift = 29, mask = 1,
+ [0] = {
+ shift = 10, mask = 63,
+ false, "udivDNMg", "sdivDNMg", false, false, false, false, "lslDNMg",
+ "lsrDNMg", "asrDNMg", "rorDNMg"
+ }
+}
+
+local map_data3s = { -- Data processing, 3 sources.
+ shift = 29, mask = 7,
+ [0] = {
+ shift = 21, mask = 7,
+ [0] = {
+ shift = 15, mask = 1,
+ [0] = "madd|mulDNMA0g", "msub|mnegDNMA0g"
+ }
+ }, false, false, false,
+ {
+ shift = 15, mask = 1,
+ [0] = {
+ shift = 21, mask = 7,
+ [0] = "madd|mulDNMA0g", "smaddl|smullDxNMwA0x", "smulhDNMx", false,
+ false, "umaddl|umullDxNMwA0x", "umulhDNMx"
+ },
+ {
+ shift = 21, mask = 7,
+ [0] = "msub|mnegDNMA0g", "smsubl|smneglDxNMwA0x", false, false,
+ false, "umsubl|umneglDxNMwA0x"
+ }
+ }
+}
+
+local map_datar = { -- Data processing, register.
+ shift = 28, mask = 1,
+ [0] = {
+ shift = 24, mask = 1,
+ [0] = map_logsr,
+ {
+ shift = 21, mask = 1,
+ [0] = map_addsubsh, map_addsubex
+ }
+ },
+ {
+ shift = 21, mask = 15,
+ [0] = map_addsubc, false, map_ccomp, false, map_csel, false,
+ {
+ shift = 30, mask = 1,
+ [0] = map_data2s, map_data1s
+ },
+ false, map_data3s, map_data3s, map_data3s, map_data3s, map_data3s,
+ map_data3s, map_data3s, map_data3s
+ }
+}
+
+local map_lrl = { -- Load register, literal.
+ shift = 26, mask = 1,
+ [0] = {
+ shift = 30, mask = 3,
+ [0] = "ldrDwB", "ldrDxB", "ldrswDxB"
+ },
+ {
+ shift = 30, mask = 3,
+ [0] = "ldrDsB", "ldrDdB"
+ }
+}
+
+local map_lsriind = { -- Load/store register, immediate pre/post-indexed.
+ shift = 30, mask = 3,
+ [0] = {
+ shift = 26, mask = 1,
+ [0] = {
+ shift = 22, mask = 3,
+ [0] = "strbDwzL", "ldrbDwzL", "ldrsbDxzL", "ldrsbDwzL"
+ }
+ },
+ {
+ shift = 26, mask = 1,
+ [0] = {
+ shift = 22, mask = 3,
+ [0] = "strhDwzL", "ldrhDwzL", "ldrshDxzL", "ldrshDwzL"
+ }
+ },
+ {
+ shift = 26, mask = 1,
+ [0] = {
+ shift = 22, mask = 3,
+ [0] = "strDwzL", "ldrDwzL", "ldrswDxzL"
+ },
+ {
+ shift = 22, mask = 3,
+ [0] = "strDszL", "ldrDszL"
+ }
+ },
+ {
+ shift = 26, mask = 1,
+ [0] = {
+ shift = 22, mask = 3,
+ [0] = "strDxzL", "ldrDxzL"
+ },
+ {
+ shift = 22, mask = 3,
+ [0] = "strDdzL", "ldrDdzL"
+ }
+ }
+}
+
+local map_lsriro = {
+ shift = 21, mask = 1,
+ [0] = { -- Load/store register immediate.
+ shift = 10, mask = 3,
+ [0] = { -- Unscaled immediate.
+ shift = 26, mask = 1,
+ [0] = {
+ shift = 30, mask = 3,
+ [0] = {
+ shift = 22, mask = 3,
+ [0] = "sturbDwK", "ldurbDwK"
+ },
+ {
+ shift = 22, mask = 3,
+ [0] = "sturhDwK", "ldurhDwK"
+ },
+ {
+ shift = 22, mask = 3,
+ [0] = "sturDwK", "ldurDwK"
+ },
+ {
+ shift = 22, mask = 3,
+ [0] = "sturDxK", "ldurDxK"
+ }
+ }
+ }, map_lsriind, false, map_lsriind
+ },
+ { -- Load/store register, register offset.
+ shift = 10, mask = 3,
+ [2] = {
+ shift = 26, mask = 1,
+ [0] = {
+ shift = 30, mask = 3,
+ [0] = {
+ shift = 22, mask = 3,
+ [0] = "strbDwO", "ldrbDwO", "ldrsbDxO", "ldrsbDwO"
+ },
+ {
+ shift = 22, mask = 3,
+ [0] = "strhDwO", "ldrhDwO", "ldrshDxO", "ldrshDwO"
+ },
+ {
+ shift = 22, mask = 3,
+ [0] = "strDwO", "ldrDwO", "ldrswDxO"
+ },
+ {
+ shift = 22, mask = 3,
+ [0] = "strDxO", "ldrDxO"
+ }
+ },
+ {
+ shift = 30, mask = 3,
+ [2] = {
+ shift = 22, mask = 3,
+ [0] = "strDsO", "ldrDsO"
+ },
+ [3] = {
+ shift = 22, mask = 3,
+ [0] = "strDdO", "ldrDdO"
+ }
+ }
+ }
+ }
+}
+
+local map_lsp = { -- Load/store register pair, offset.
+ shift = 22, mask = 1,
+ [0] = {
+ shift = 30, mask = 3,
+ [0] = {
+ shift = 26, mask = 1,
+ [0] = "stpDzAzwP", "stpDzAzsP",
+ },
+ {
+ shift = 26, mask = 1,
+ "stpDzAzdP"
+ },
+ {
+ shift = 26, mask = 1,
+ [0] = "stpDzAzxP"
+ }
+ },
+ {
+ shift = 30, mask = 3,
+ [0] = {
+ shift = 26, mask = 1,
+ [0] = "ldpDzAzwP", "ldpDzAzsP",
+ },
+ {
+ shift = 26, mask = 1,
+ [0] = "ldpswDAxP", "ldpDzAzdP"
+ },
+ {
+ shift = 26, mask = 1,
+ [0] = "ldpDzAzxP"
+ }
+ }
+}
+
+local map_ls = { -- Loads and stores.
+ shift = 24, mask = 0x31,
+ [0x10] = map_lrl, [0x30] = map_lsriro,
+ [0x20] = {
+ shift = 23, mask = 3,
+ map_lsp, map_lsp, map_lsp
+ },
+ [0x21] = {
+ shift = 23, mask = 3,
+ map_lsp, map_lsp, map_lsp
+ },
+ [0x31] = {
+ shift = 26, mask = 1,
+ [0] = {
+ shift = 30, mask = 3,
+ [0] = {
+ shift = 22, mask = 3,
+ [0] = "strbDwzU", "ldrbDwzU", "ldrsbDwzU", "ldrsbDxzU"
+ },
+ {
+ shift = 22, mask = 3,
+ [0] = "strhDwzU", "ldrhDwzU", "ldrshDwzU", "ldrshDxzU"
+ },
+ {
+ shift = 22, mask = 3,
+ [0] = "strDwzU", "ldrDwzU", "ldrswDxzU"
+ },
+ {
+ shift = 22, mask = 3,
+ [0] = "strDxzU", "ldrDxzU"
+ }
+ },
+ {
+ shift = 30, mask = 3,
+ [2] = {
+ shift = 22, mask = 3,
+ [0] = "strDszU", "ldrDszU"
+ },
+ [3] = {
+ shift = 22, mask = 3,
+ [0] = "strDdzU", "ldrDdzU"
+ }
+ }
+ },
+}
+
+local map_datafp = { -- Data processing, SIMD and FP.
+ shift = 28, mask = 7,
+ { -- 001
+ shift = 24, mask = 1,
+ [0] = {
+ shift = 21, mask = 1,
+ {
+ shift = 10, mask = 3,
+ [0] = {
+ shift = 12, mask = 1,
+ [0] = {
+ shift = 13, mask = 1,
+ [0] = {
+ shift = 14, mask = 1,
+ [0] = {
+ shift = 15, mask = 1,
+ [0] = { -- FP/int conversion.
+ shift = 31, mask = 1,
+ [0] = {
+ shift = 16, mask = 0xff,
+ [0x20] = "fcvtnsDwNs", [0x21] = "fcvtnuDwNs",
+ [0x22] = "scvtfDsNw", [0x23] = "ucvtfDsNw",
+ [0x24] = "fcvtasDwNs", [0x25] = "fcvtauDwNs",
+ [0x26] = "fmovDwNs", [0x27] = "fmovDsNw",
+ [0x28] = "fcvtpsDwNs", [0x29] = "fcvtpuDwNs",
+ [0x30] = "fcvtmsDwNs", [0x31] = "fcvtmuDwNs",
+ [0x38] = "fcvtzsDwNs", [0x39] = "fcvtzuDwNs",
+ [0x60] = "fcvtnsDwNd", [0x61] = "fcvtnuDwNd",
+ [0x62] = "scvtfDdNw", [0x63] = "ucvtfDdNw",
+ [0x64] = "fcvtasDwNd", [0x65] = "fcvtauDwNd",
+ [0x68] = "fcvtpsDwNd", [0x69] = "fcvtpuDwNd",
+ [0x70] = "fcvtmsDwNd", [0x71] = "fcvtmuDwNd",
+ [0x78] = "fcvtzsDwNd", [0x79] = "fcvtzuDwNd"
+ },
+ {
+ shift = 16, mask = 0xff,
+ [0x20] = "fcvtnsDxNs", [0x21] = "fcvtnuDxNs",
+ [0x22] = "scvtfDsNx", [0x23] = "ucvtfDsNx",
+ [0x24] = "fcvtasDxNs", [0x25] = "fcvtauDxNs",
+ [0x28] = "fcvtpsDxNs", [0x29] = "fcvtpuDxNs",
+ [0x30] = "fcvtmsDxNs", [0x31] = "fcvtmuDxNs",
+ [0x38] = "fcvtzsDxNs", [0x39] = "fcvtzuDxNs",
+ [0x60] = "fcvtnsDxNd", [0x61] = "fcvtnuDxNd",
+ [0x62] = "scvtfDdNx", [0x63] = "ucvtfDdNx",
+ [0x64] = "fcvtasDxNd", [0x65] = "fcvtauDxNd",
+ [0x66] = "fmovDxNd", [0x67] = "fmovDdNx",
+ [0x68] = "fcvtpsDxNd", [0x69] = "fcvtpuDxNd",
+ [0x70] = "fcvtmsDxNd", [0x71] = "fcvtmuDxNd",
+ [0x78] = "fcvtzsDxNd", [0x79] = "fcvtzuDxNd"
+ }
+ }
+ },
+ { -- FP data-processing, 1 source.
+ shift = 31, mask = 1,
+ [0] = {
+ shift = 22, mask = 3,
+ [0] = {
+ shift = 15, mask = 63,
+ [0] = "fmovDNf", "fabsDNf", "fnegDNf",
+ "fsqrtDNf", false, "fcvtDdNs", false, false,
+ "frintnDNf", "frintpDNf", "frintmDNf", "frintzDNf",
+ "frintaDNf", false, "frintxDNf", "frintiDNf",
+ },
+ {
+ shift = 15, mask = 63,
+ [0] = "fmovDNf", "fabsDNf", "fnegDNf",
+ "fsqrtDNf", "fcvtDsNd", false, false, false,
+ "frintnDNf", "frintpDNf", "frintmDNf", "frintzDNf",
+ "frintaDNf", false, "frintxDNf", "frintiDNf",
+ }
+ }
+ }
+ },
+ { -- FP compare.
+ shift = 31, mask = 1,
+ [0] = {
+ shift = 14, mask = 3,
+ [0] = {
+ shift = 23, mask = 1,
+ [0] = {
+ shift = 0, mask = 31,
+ [0] = "fcmpNMf", [8] = "fcmpNZf",
+ [16] = "fcmpeNMf", [24] = "fcmpeNZf",
+ }
+ }
+ }
+ }
+ },
+ { -- FP immediate.
+ shift = 31, mask = 1,
+ [0] = {
+ shift = 5, mask = 31,
+ [0] = {
+ shift = 23, mask = 1,
+ [0] = "fmovDFf"
+ }
+ }
+ }
+ },
+ { -- FP conditional compare.
+ shift = 31, mask = 1,
+ [0] = {
+ shift = 23, mask = 1,
+ [0] = {
+ shift = 4, mask = 1,
+ [0] = "fccmpNMVCf", "fccmpeNMVCf"
+ }
+ }
+ },
+ { -- FP data-processing, 2 sources.
+ shift = 31, mask = 1,
+ [0] = {
+ shift = 23, mask = 1,
+ [0] = {
+ shift = 12, mask = 15,
+ [0] = "fmulDNMf", "fdivDNMf", "faddDNMf", "fsubDNMf",
+ "fmaxDNMf", "fminDNMf", "fmaxnmDNMf", "fminnmDNMf",
+ "fnmulDNMf"
+ }
+ }
+ },
+ { -- FP conditional select.
+ shift = 31, mask = 1,
+ [0] = {
+ shift = 23, mask = 1,
+ [0] = "fcselDNMCf"
+ }
+ }
+ }
+ },
+ { -- FP data-processing, 3 sources.
+ shift = 31, mask = 1,
+ [0] = {
+ shift = 15, mask = 1,
+ [0] = {
+ shift = 21, mask = 5,
+ [0] = "fmaddDNMAf", "fnmaddDNMAf"
+ },
+ {
+ shift = 21, mask = 5,
+ [0] = "fmsubDNMAf", "fnmsubDNMAf"
+ }
+ }
+ }
+ },
+ { -- 010
+ shift = 0, mask = 0x81f8fc00,
+ [0x100e400] = "moviDdG"
+ }
+}
+
+local map_br = { -- Branches, exception generating and system instructions.
+ shift = 29, mask = 7,
+ [0] = "bB",
+ { -- Compare & branch, immediate.
+ shift = 24, mask = 3,
+ [0] = "cbzDBg", "cbnzDBg", "tbzDTBw", "tbnzDTBw"
+ },
+ { -- Conditional branch, immediate.
+ shift = 24, mask = 3,
+ [0] = {
+ shift = 4, mask = 1,
+ [0] = {
+ shift = 0, mask = 15,
+ [0] = "beqB", "bneB", "bhsB", "bloB", "bmiB", "bplB", "bvsB", "bvcB",
+ "bhiB", "blsB", "bgeB", "bltB", "bgtB", "bleB", "balB"
+ }
+ }
+ }, false, "blB",
+ { -- Compare & branch, immediate.
+ shift = 24, mask = 3,
+ [0] = "cbzDBg", "cbnzDBg", "tbzDTBx", "tbnzDTBx"
+ },
+ {
+ shift = 24, mask = 3,
+ [0] = { -- Exception generation.
+ shift = 0, mask = 0xe0001f,
+ [0x200000] = "brkW"
+ },
+ { -- System instructions.
+ shift = 0, mask = 0x3fffff,
+ [0x03201f] = "nop",
+ [0x03245f] = "bti c",
+ [0x03249f] = "bti j",
+ [0x0324df] = "bti jc",
+ },
+ { -- Unconditional branch, register.
+ shift = 0, mask = 0xfffc1f,
+ [0x1f0000] = "brNx", [0x3f0000] = "blrNx",
+ [0x5f0000] = "retNx"
+ },
+ }
+}
+
+local map_init = {
+ shift = 25, mask = 15,
+ [0] = false, false, false, false, map_ls, map_datar, map_ls, map_datafp,
+ map_datai, map_datai, map_br, map_br, map_ls, map_datar, map_ls, map_datafp
+}
+
+------------------------------------------------------------------------------
+
+local map_regs = { x = {}, w = {}, d = {}, s = {} }
+
+for i=0,30 do
+ map_regs.x[i] = "x"..i
+ map_regs.w[i] = "w"..i
+ map_regs.d[i] = "d"..i
+ map_regs.s[i] = "s"..i
+end
+map_regs.x[31] = "sp"
+map_regs.w[31] = "wsp"
+map_regs.d[31] = "d31"
+map_regs.s[31] = "s31"
+
+local map_cond = {
+ [0] = "eq", "ne", "cs", "cc", "mi", "pl", "vs", "vc",
+ "hi", "ls", "ge", "lt", "gt", "le", "al",
+}
+
+local map_shift = { [0] = "lsl", "lsr", "asr", "ror"}
+
+local map_extend = {
+ [0] = "uxtb", "uxth", "uxtw", "uxtx", "sxtb", "sxth", "sxtw", "sxtx",
+}
+
+------------------------------------------------------------------------------
+
+-- Output a nicely formatted line with an opcode and operands.
+local function putop(ctx, text, operands)
+ local pos = ctx.pos
+ local extra = ""
+ if ctx.rel then
+ local sym = ctx.symtab[ctx.rel]
+ if sym then
+ extra = "\t->"..sym
+ end
+ end
+ if ctx.hexdump > 0 then
+ ctx.out(format("%08x %s %-5s %s%s\n",
+ ctx.addr+pos, tohex(ctx.op), text, concat(operands, ", "), extra))
+ else
+ ctx.out(format("%08x %-5s %s%s\n",
+ ctx.addr+pos, text, concat(operands, ", "), extra))
+ end
+ ctx.pos = pos + 4
+end
+
+-- Fallback for unknown opcodes.
+local function unknown(ctx)
+ return putop(ctx, ".long", { "0x"..tohex(ctx.op) })
+end
+
+local function match_reg(p, pat, regnum)
+ return map_regs[match(pat, p.."%w-([xwds])")][regnum]
+end
+
+local function fmt_hex32(x)
+ if x < 0 then
+ return tohex(x)
+ else
+ return format("%x", x)
+ end
+end
+
+local imm13_rep = { 0x55555555, 0x11111111, 0x01010101, 0x00010001, 0x00000001 }
+
+local function decode_imm13(op)
+ local imms = band(rshift(op, 10), 63)
+ local immr = band(rshift(op, 16), 63)
+ if band(op, 0x00400000) == 0 then
+ local len = 5
+ if imms >= 56 then
+ if imms >= 60 then len = 1 else len = 2 end
+ elseif imms >= 48 then len = 3 elseif imms >= 32 then len = 4 end
+ local l = lshift(1, len)-1
+ local s = band(imms, l)
+ local r = band(immr, l)
+ local imm = ror(rshift(-1, 31-s), r)
+ if len ~= 5 then imm = band(imm, lshift(1, l)-1) + rshift(imm, 31-l) end
+ imm = imm * imm13_rep[len]
+ local ix = fmt_hex32(imm)
+ if rshift(op, 31) ~= 0 then
+ return ix..tohex(imm)
+ else
+ return ix
+ end
+ else
+ local lo, hi = -1, 0
+ if imms < 32 then lo = rshift(-1, 31-imms) else hi = rshift(-1, 63-imms) end
+ if immr ~= 0 then
+ lo, hi = ror(lo, immr), ror(hi, immr)
+ local x = immr == 32 and 0 or band(bxor(lo, hi), lshift(-1, 32-immr))
+ lo, hi = bxor(lo, x), bxor(hi, x)
+ if immr >= 32 then lo, hi = hi, lo end
+ end
+ if hi ~= 0 then
+ return fmt_hex32(hi)..tohex(lo)
+ else
+ return fmt_hex32(lo)
+ end
+ end
+end
+
+local function parse_immpc(op, name)
+ if name == "b" or name == "bl" then
+ return arshift(lshift(op, 6), 4)
+ elseif name == "adr" or name == "adrp" then
+ local immlo = band(rshift(op, 29), 3)
+ local immhi = lshift(arshift(lshift(op, 8), 13), 2)
+ return bor(immhi, immlo)
+ elseif name == "tbz" or name == "tbnz" then
+ return lshift(arshift(lshift(op, 13), 18), 2)
+ else
+ return lshift(arshift(lshift(op, 8), 13), 2)
+ end
+end
+
+local function parse_fpimm8(op)
+ local sign = band(op, 0x100000) == 0 and 1 or -1
+ local exp = bxor(rshift(arshift(lshift(op, 12), 5), 24), 0x80) - 131
+ local frac = 16+band(rshift(op, 13), 15)
+ return sign * frac * 2^exp
+end
+
+local function decode_fpmovi(op)
+ local lo = rshift(op, 5)
+ local hi = rshift(op, 9)
+ lo = bor(band(lo, 1) * 0xff, band(lo, 2) * 0x7f80, band(lo, 4) * 0x3fc000,
+ band(lo, 8) * 0x1fe00000)
+ hi = bor(band(hi, 1) * 0xff, band(hi, 0x80) * 0x1fe,
+ band(hi, 0x100) * 0xff00, band(hi, 0x200) * 0x7f8000)
+ if hi ~= 0 then
+ return fmt_hex32(hi)..tohex(lo)
+ else
+ return fmt_hex32(lo)
+ end
+end
+
+local function prefer_bfx(sf, uns, imms, immr)
+ if imms < immr or imms == 31 or imms == 63 then
+ return false
+ end
+ if immr == 0 then
+ if sf == 0 and (imms == 7 or imms == 15) then
+ return false
+ end
+ if sf ~= 0 and uns == 0 and (imms == 7 or imms == 15 or imms == 31) then
+ return false
+ end
+ end
+ return true
+end
+
+-- Disassemble a single instruction.
+local function disass_ins(ctx)
+ local pos = ctx.pos
+ local b0, b1, b2, b3 = byte(ctx.code, pos+1, pos+4)
+ local op = bor(lshift(b3, 24), lshift(b2, 16), lshift(b1, 8), b0)
+ local operands = {}
+ local suffix = ""
+ local last, name, pat
+ local map_reg
+ ctx.op = op
+ ctx.rel = nil
+ last = nil
+ local opat
+ opat = map_init[band(rshift(op, 25), 15)]
+ while type(opat) ~= "string" do
+ if not opat then return unknown(ctx) end
+ opat = opat[band(rshift(op, opat.shift), opat.mask)] or opat._
+ end
+ name, pat = match(opat, "^([a-z0-9]*)(.*)")
+ local altname, pat2 = match(pat, "|([a-z0-9_.|]*)(.*)")
+ if altname then pat = pat2 end
+ if sub(pat, 1, 1) == "." then
+ local s2, p2 = match(pat, "^([a-z0-9.]*)(.*)")
+ suffix = suffix..s2
+ pat = p2
+ end
+
+ local rt = match(pat, "[gf]")
+ if rt then
+ if rt == "g" then
+ map_reg = band(op, 0x80000000) ~= 0 and map_regs.x or map_regs.w
+ else
+ map_reg = band(op, 0x400000) ~= 0 and map_regs.d or map_regs.s
+ end
+ end
+
+ local second0, immr
+
+ for p in gmatch(pat, ".") do
+ local x = nil
+ if p == "D" then
+ local regnum = band(op, 31)
+ x = rt and map_reg[regnum] or match_reg(p, pat, regnum)
+ elseif p == "N" then
+ local regnum = band(rshift(op, 5), 31)
+ x = rt and map_reg[regnum] or match_reg(p, pat, regnum)
+ elseif p == "M" then
+ local regnum = band(rshift(op, 16), 31)
+ x = rt and map_reg[regnum] or match_reg(p, pat, regnum)
+ elseif p == "A" then
+ local regnum = band(rshift(op, 10), 31)
+ x = rt and map_reg[regnum] or match_reg(p, pat, regnum)
+ elseif p == "B" then
+ local addr = ctx.addr + pos + parse_immpc(op, name)
+ ctx.rel = addr
+ x = format("0x%08x", addr)
+ elseif p == "T" then
+ x = bor(band(rshift(op, 26), 32), band(rshift(op, 19), 31))
+ elseif p == "V" then
+ x = band(op, 15)
+ elseif p == "C" then
+ x = map_cond[band(rshift(op, 12), 15)]
+ elseif p == "c" then
+ local rn = band(rshift(op, 5), 31)
+ local rm = band(rshift(op, 16), 31)
+ local cond = band(rshift(op, 12), 15)
+ local invc = bxor(cond, 1)
+ x = map_cond[cond]
+ if altname and cond ~= 14 and cond ~= 15 then
+ local a1, a2 = match(altname, "([^|]*)|(.*)")
+ if rn == rm then
+ local n = #operands
+ operands[n] = nil
+ x = map_cond[invc]
+ if rn ~= 31 then
+ if a1 then name = a1 else name = altname end
+ else
+ operands[n-1] = nil
+ name = a2
+ end
+ end
+ end
+ elseif p == "W" then
+ x = band(rshift(op, 5), 0xffff)
+ elseif p == "Y" then
+ x = band(rshift(op, 5), 0xffff)
+ local hw = band(rshift(op, 21), 3)
+ if altname and (hw == 0 or x ~= 0) then
+ name = altname
+ end
+ elseif p == "L" then
+ local rn = map_regs.x[band(rshift(op, 5), 31)]
+ local imm9 = arshift(lshift(op, 11), 23)
+ if band(op, 0x800) ~= 0 then
+ x = "["..rn..", #"..imm9.."]!"
+ else
+ x = "["..rn.."], #"..imm9
+ end
+ elseif p == "U" then
+ local rn = map_regs.x[band(rshift(op, 5), 31)]
+ local sz = band(rshift(op, 30), 3)
+ local imm12 = lshift(rshift(lshift(op, 10), 20), sz)
+ if imm12 ~= 0 then
+ x = "["..rn..", #"..imm12.."]"
+ else
+ x = "["..rn.."]"
+ end
+ elseif p == "K" then
+ local rn = map_regs.x[band(rshift(op, 5), 31)]
+ local imm9 = arshift(lshift(op, 11), 23)
+ if imm9 ~= 0 then
+ x = "["..rn..", #"..imm9.."]"
+ else
+ x = "["..rn.."]"
+ end
+ elseif p == "O" then
+ local rn, rm = map_regs.x[band(rshift(op, 5), 31)]
+ local m = band(rshift(op, 13), 1)
+ if m == 0 then
+ rm = map_regs.w[band(rshift(op, 16), 31)]
+ else
+ rm = map_regs.x[band(rshift(op, 16), 31)]
+ end
+ x = "["..rn..", "..rm
+ local opt = band(rshift(op, 13), 7)
+ local s = band(rshift(op, 12), 1)
+ local sz = band(rshift(op, 30), 3)
+ -- extension to be applied
+ if opt == 3 then
+ if s == 0 then x = x.."]"
+ else x = x..", lsl #"..sz.."]" end
+ elseif opt == 2 or opt == 6 or opt == 7 then
+ if s == 0 then x = x..", "..map_extend[opt].."]"
+ else x = x..", "..map_extend[opt].." #"..sz.."]" end
+ else
+ x = x.."]"
+ end
+ elseif p == "P" then
+ local sh = 2 + rshift(op, 31 - band(rshift(op, 26), 1))
+ local imm7 = lshift(arshift(lshift(op, 10), 25), sh)
+ local rn = map_regs.x[band(rshift(op, 5), 31)]
+ local ind = band(rshift(op, 23), 3)
+ if ind == 1 then
+ x = "["..rn.."], #"..imm7
+ elseif ind == 2 then
+ if imm7 == 0 then
+ x = "["..rn.."]"
+ else
+ x = "["..rn..", #"..imm7.."]"
+ end
+ elseif ind == 3 then
+ x = "["..rn..", #"..imm7.."]!"
+ end
+ elseif p == "I" then
+ local shf = band(rshift(op, 22), 3)
+ local imm12 = band(rshift(op, 10), 0x0fff)
+ local rn, rd = band(rshift(op, 5), 31), band(op, 31)
+ if altname == "mov" and shf == 0 and imm12 == 0 and (rn == 31 or rd == 31) then
+ name = altname
+ x = nil
+ elseif shf == 0 then
+ x = imm12
+ elseif shf == 1 then
+ x = imm12..", lsl #12"
+ end
+ elseif p == "i" then
+ x = "#0x"..decode_imm13(op)
+ elseif p == "1" then
+ immr = band(rshift(op, 16), 63)
+ x = immr
+ elseif p == "2" then
+ x = band(rshift(op, 10), 63)
+ if altname then
+ local a1, a2, a3, a4, a5, a6 =
+ match(altname, "([^|]*)|([^|]*)|([^|]*)|([^|]*)|([^|]*)|(.*)")
+ local sf = band(rshift(op, 26), 32)
+ local uns = band(rshift(op, 30), 1)
+ if prefer_bfx(sf, uns, x, immr) then
+ name = a2
+ x = x - immr + 1
+ elseif immr == 0 and x == 7 then
+ local n = #operands
+ operands[n] = nil
+ if sf ~= 0 then
+ operands[n-1] = gsub(operands[n-1], "x", "w")
+ end
+ last = operands[n-1]
+ name = a6
+ x = nil
+ elseif immr == 0 and x == 15 then
+ local n = #operands
+ operands[n] = nil
+ if sf ~= 0 then
+ operands[n-1] = gsub(operands[n-1], "x", "w")
+ end
+ last = operands[n-1]
+ name = a5
+ x = nil
+ elseif x == 31 or x == 63 then
+ if x == 31 and immr == 0 and name == "sbfm" then
+ name = a4
+ local n = #operands
+ operands[n] = nil
+ if sf ~= 0 then
+ operands[n-1] = gsub(operands[n-1], "x", "w")
+ end
+ last = operands[n-1]
+ else
+ name = a3
+ end
+ x = nil
+ elseif band(x, 31) ~= 31 and immr == x+1 and name == "ubfm" then
+ name = a4
+ last = "#"..(sf+32 - immr)
+ operands[#operands] = last
+ x = nil
+ elseif x < immr then
+ name = a1
+ last = "#"..(sf+32 - immr)
+ operands[#operands] = last
+ x = x + 1
+ end
+ end
+ elseif p == "3" then
+ x = band(rshift(op, 10), 63)
+ if altname then
+ local a1, a2 = match(altname, "([^|]*)|(.*)")
+ if x < immr then
+ name = a1
+ local sf = band(rshift(op, 26), 32)
+ last = "#"..(sf+32 - immr)
+ operands[#operands] = last
+ x = x + 1
+ else
+ name = a2
+ x = x - immr + 1
+ end
+ end
+ elseif p == "4" then
+ x = band(rshift(op, 10), 63)
+ local rn = band(rshift(op, 5), 31)
+ local rm = band(rshift(op, 16), 31)
+ if altname and rn == rm then
+ local n = #operands
+ operands[n] = nil
+ last = operands[n-1]
+ name = altname
+ end
+ elseif p == "5" then
+ x = band(rshift(op, 16), 31)
+ elseif p == "S" then
+ x = band(rshift(op, 10), 63)
+ if x == 0 then x = nil
+ else x = map_shift[band(rshift(op, 22), 3)].." #"..x end
+ elseif p == "X" then
+ local opt = band(rshift(op, 13), 7)
+ -- Width specifier .
+ if opt ~= 3 and opt ~= 7 then
+ last = map_regs.w[band(rshift(op, 16), 31)]
+ operands[#operands] = last
+ end
+ x = band(rshift(op, 10), 7)
+ -- Extension.
+ if opt == 2 + band(rshift(op, 31), 1) and
+ band(rshift(op, second0 and 5 or 0), 31) == 31 then
+ if x == 0 then x = nil
+ else x = "lsl #"..x end
+ else
+ if x == 0 then x = map_extend[band(rshift(op, 13), 7)]
+ else x = map_extend[band(rshift(op, 13), 7)].." #"..x end
+ end
+ elseif p == "R" then
+ x = band(rshift(op,21), 3)
+ if x == 0 then x = nil
+ else x = "lsl #"..x*16 end
+ elseif p == "z" then
+ local n = #operands
+ if operands[n] == "sp" then operands[n] = "xzr"
+ elseif operands[n] == "wsp" then operands[n] = "wzr"
+ end
+ elseif p == "Z" then
+ x = 0
+ elseif p == "F" then
+ x = parse_fpimm8(op)
+ elseif p == "G" then
+ x = "#0x"..decode_fpmovi(op)
+ elseif p == "g" or p == "f" or p == "x" or p == "w" or
+ p == "d" or p == "s" then
+ -- These are handled in D/N/M/A.
+ elseif p == "0" then
+ if last == "sp" or last == "wsp" then
+ local n = #operands
+ operands[n] = nil
+ last = operands[n-1]
+ if altname then
+ local a1, a2 = match(altname, "([^|]*)|(.*)")
+ if not a1 then
+ name = altname
+ elseif second0 then
+ name, altname = a2, a1
+ else
+ name, altname = a1, a2
+ end
+ end
+ end
+ second0 = true
+ elseif p == " " then
+ operands[#operands+1] = pat:match(" (.*)")
+ break
+ else
+ assert(false)
+ end
+ if x then
+ last = x
+ if type(x) == "number" then x = "#"..x end
+ operands[#operands+1] = x
+ end
+ end
+
+ return putop(ctx, name..suffix, operands)
+end
+
+------------------------------------------------------------------------------
+
+-- Disassemble a block of code.
+local function disass_block(ctx, ofs, len)
+ if not ofs then ofs = 0 end
+ local stop = len and ofs+len or #ctx.code
+ ctx.pos = ofs
+ ctx.rel = nil
+ while ctx.pos < stop do disass_ins(ctx) end
+end
+
+-- Extended API: create a disassembler context. Then call ctx:disass(ofs, len).
+local function create(code, addr, out)
+ local ctx = {}
+ ctx.code = code
+ ctx.addr = addr or 0
+ ctx.out = out or io.write
+ ctx.symtab = {}
+ ctx.disass = disass_block
+ ctx.hexdump = 8
+ return ctx
+end
+
+-- Simple API: disassemble code (a string) at address and output via out.
+local function disass(code, addr, out)
+ create(code, addr, out):disass()
+end
+
+-- Return register name for RID.
+local function regname(r)
+ if r < 32 then return map_regs.x[r] end
+ return map_regs.d[r-32]
+end
+
+-- Public module functions.
+return {
+ create = create,
+ disass = disass,
+ regname = regname
+}
+
diff --git a/jit/dis_arm64be.lua b/jit/dis_arm64be.lua
new file mode 100644
index 0000000..e62a28c
--- /dev/null
+++ b/jit/dis_arm64be.lua
@@ -0,0 +1,12 @@
+----------------------------------------------------------------------------
+-- LuaJIT ARM64BE disassembler wrapper module.
+--
+-- Copyright (C) 2005-2026 Mike Pall. All rights reserved.
+-- Released under the MIT license. See Copyright Notice in luajit.h
+----------------------------------------------------------------------------
+-- ARM64 instructions are always little-endian. So just forward to the
+-- common ARM64 disassembler module. All the interesting stuff is there.
+------------------------------------------------------------------------------
+
+return require((string.match(..., ".*%.") or "").."dis_arm64")
+
diff --git a/jit/dis_mips.lua b/jit/dis_mips.lua
new file mode 100644
index 0000000..fece893
--- /dev/null
+++ b/jit/dis_mips.lua
@@ -0,0 +1,694 @@
+----------------------------------------------------------------------------
+-- LuaJIT MIPS disassembler module.
+--
+-- Copyright (C) 2005-2026 Mike Pall. All rights reserved.
+-- Released under the MIT/X license. See Copyright Notice in luajit.h
+----------------------------------------------------------------------------
+-- This is a helper module used by the LuaJIT machine code dumper module.
+--
+-- It disassembles all standard MIPS32R1/R2 instructions.
+-- Default mode is big-endian, but see: dis_mipsel.lua
+------------------------------------------------------------------------------
+
+local type = type
+local byte, format = string.byte, string.format
+local match, gmatch = string.match, string.gmatch
+local concat = table.concat
+local bit = require("bit")
+local band, bor, tohex = bit.band, bit.bor, bit.tohex
+local lshift, rshift, arshift = bit.lshift, bit.rshift, bit.arshift
+
+------------------------------------------------------------------------------
+-- Extended opcode maps common to all MIPS releases
+------------------------------------------------------------------------------
+
+local map_srl = { shift = 21, mask = 1, [0] = "srlDTA", "rotrDTA", }
+local map_srlv = { shift = 6, mask = 1, [0] = "srlvDTS", "rotrvDTS", }
+
+local map_cop0 = {
+ shift = 25, mask = 1,
+ [0] = {
+ shift = 21, mask = 15,
+ [0] = "mfc0TDW", [4] = "mtc0TDW",
+ [10] = "rdpgprDT",
+ [11] = { shift = 5, mask = 1, [0] = "diT0", "eiT0", },
+ [14] = "wrpgprDT",
+ }, {
+ shift = 0, mask = 63,
+ [1] = "tlbr", [2] = "tlbwi", [6] = "tlbwr", [8] = "tlbp",
+ [24] = "eret", [31] = "deret",
+ [32] = "wait",
+ },
+}
+
+------------------------------------------------------------------------------
+-- Primary and extended opcode maps for MIPS R1-R5
+------------------------------------------------------------------------------
+
+local map_movci = { shift = 16, mask = 1, [0] = "movfDSC", "movtDSC", }
+
+local map_special = {
+ shift = 0, mask = 63,
+ [0] = { shift = 0, mask = -1, [0] = "nop", _ = "sllDTA" },
+ map_movci, map_srl, "sraDTA",
+ "sllvDTS", false, map_srlv, "sravDTS",
+ "jrS", "jalrD1S", "movzDST", "movnDST",
+ "syscallY", "breakY", false, "sync",
+ "mfhiD", "mthiS", "mfloD", "mtloS",
+ "dsllvDST", false, "dsrlvDST", "dsravDST",
+ "multST", "multuST", "divST", "divuST",
+ "dmultST", "dmultuST", "ddivST", "ddivuST",
+ "addDST", "addu|moveDST0", "subDST", "subu|neguDS0T",
+ "andDST", "or|moveDST0", "xorDST", "nor|notDST0",
+ false, false, "sltDST", "sltuDST",
+ "daddDST", "dadduDST", "dsubDST", "dsubuDST",
+ "tgeSTZ", "tgeuSTZ", "tltSTZ", "tltuSTZ",
+ "teqSTZ", false, "tneSTZ", false,
+ "dsllDTA", false, "dsrlDTA", "dsraDTA",
+ "dsll32DTA", false, "dsrl32DTA", "dsra32DTA",
+}
+
+local map_special2 = {
+ shift = 0, mask = 63,
+ [0] = "maddST", "madduST", "mulDST", false,
+ "msubST", "msubuST",
+ [32] = "clzDS", [33] = "cloDS",
+ [63] = "sdbbpY",
+}
+
+local map_bshfl = {
+ shift = 6, mask = 31,
+ [2] = "wsbhDT",
+ [16] = "sebDT",
+ [24] = "sehDT",
+}
+
+local map_dbshfl = {
+ shift = 6, mask = 31,
+ [2] = "dsbhDT",
+ [5] = "dshdDT",
+}
+
+local map_special3 = {
+ shift = 0, mask = 63,
+ [0] = "extTSAK", [1] = "dextmTSAP", [3] = "dextTSAK",
+ [4] = "insTSAL", [6] = "dinsuTSEQ", [7] = "dinsTSAL",
+ [32] = map_bshfl, [36] = map_dbshfl, [59] = "rdhwrTD",
+}
+
+local map_regimm = {
+ shift = 16, mask = 31,
+ [0] = "bltzSB", "bgezSB", "bltzlSB", "bgezlSB",
+ false, false, false, false,
+ "tgeiSI", "tgeiuSI", "tltiSI", "tltiuSI",
+ "teqiSI", false, "tneiSI", false,
+ "bltzalSB", "bgezalSB", "bltzallSB", "bgezallSB",
+ false, false, false, false,
+ false, false, false, false,
+ false, false, false, "synciSO",
+}
+
+local map_cop1s = {
+ shift = 0, mask = 63,
+ [0] = "add.sFGH", "sub.sFGH", "mul.sFGH", "div.sFGH",
+ "sqrt.sFG", "abs.sFG", "mov.sFG", "neg.sFG",
+ "round.l.sFG", "trunc.l.sFG", "ceil.l.sFG", "floor.l.sFG",
+ "round.w.sFG", "trunc.w.sFG", "ceil.w.sFG", "floor.w.sFG",
+ false,
+ { shift = 16, mask = 1, [0] = "movf.sFGC", "movt.sFGC" },
+ "movz.sFGT", "movn.sFGT",
+ false, "recip.sFG", "rsqrt.sFG", false,
+ false, false, false, false,
+ false, false, false, false,
+ false, "cvt.d.sFG", false, false,
+ "cvt.w.sFG", "cvt.l.sFG", "cvt.ps.sFGH", false,
+ false, false, false, false,
+ false, false, false, false,
+ "c.f.sVGH", "c.un.sVGH", "c.eq.sVGH", "c.ueq.sVGH",
+ "c.olt.sVGH", "c.ult.sVGH", "c.ole.sVGH", "c.ule.sVGH",
+ "c.sf.sVGH", "c.ngle.sVGH", "c.seq.sVGH", "c.ngl.sVGH",
+ "c.lt.sVGH", "c.nge.sVGH", "c.le.sVGH", "c.ngt.sVGH",
+}
+
+local map_cop1d = {
+ shift = 0, mask = 63,
+ [0] = "add.dFGH", "sub.dFGH", "mul.dFGH", "div.dFGH",
+ "sqrt.dFG", "abs.dFG", "mov.dFG", "neg.dFG",
+ "round.l.dFG", "trunc.l.dFG", "ceil.l.dFG", "floor.l.dFG",
+ "round.w.dFG", "trunc.w.dFG", "ceil.w.dFG", "floor.w.dFG",
+ false,
+ { shift = 16, mask = 1, [0] = "movf.dFGC", "movt.dFGC" },
+ "movz.dFGT", "movn.dFGT",
+ false, "recip.dFG", "rsqrt.dFG", false,
+ false, false, false, false,
+ false, false, false, false,
+ "cvt.s.dFG", false, false, false,
+ "cvt.w.dFG", "cvt.l.dFG", false, false,
+ false, false, false, false,
+ false, false, false, false,
+ "c.f.dVGH", "c.un.dVGH", "c.eq.dVGH", "c.ueq.dVGH",
+ "c.olt.dVGH", "c.ult.dVGH", "c.ole.dVGH", "c.ule.dVGH",
+ "c.df.dVGH", "c.ngle.dVGH", "c.deq.dVGH", "c.ngl.dVGH",
+ "c.lt.dVGH", "c.nge.dVGH", "c.le.dVGH", "c.ngt.dVGH",
+}
+
+local map_cop1ps = {
+ shift = 0, mask = 63,
+ [0] = "add.psFGH", "sub.psFGH", "mul.psFGH", false,
+ false, "abs.psFG", "mov.psFG", "neg.psFG",
+ false, false, false, false,
+ false, false, false, false,
+ false,
+ { shift = 16, mask = 1, [0] = "movf.psFGC", "movt.psFGC" },
+ "movz.psFGT", "movn.psFGT",
+ false, false, false, false,
+ false, false, false, false,
+ false, false, false, false,
+ "cvt.s.puFG", false, false, false,
+ false, false, false, false,
+ "cvt.s.plFG", false, false, false,
+ "pll.psFGH", "plu.psFGH", "pul.psFGH", "puu.psFGH",
+ "c.f.psVGH", "c.un.psVGH", "c.eq.psVGH", "c.ueq.psVGH",
+ "c.olt.psVGH", "c.ult.psVGH", "c.ole.psVGH", "c.ule.psVGH",
+ "c.psf.psVGH", "c.ngle.psVGH", "c.pseq.psVGH", "c.ngl.psVGH",
+ "c.lt.psVGH", "c.nge.psVGH", "c.le.psVGH", "c.ngt.psVGH",
+}
+
+local map_cop1w = {
+ shift = 0, mask = 63,
+ [32] = "cvt.s.wFG", [33] = "cvt.d.wFG",
+}
+
+local map_cop1l = {
+ shift = 0, mask = 63,
+ [32] = "cvt.s.lFG", [33] = "cvt.d.lFG",
+}
+
+local map_cop1bc = {
+ shift = 16, mask = 3,
+ [0] = "bc1fCB", "bc1tCB", "bc1flCB", "bc1tlCB",
+}
+
+local map_cop1 = {
+ shift = 21, mask = 31,
+ [0] = "mfc1TG", "dmfc1TG", "cfc1TG", "mfhc1TG",
+ "mtc1TG", "dmtc1TG", "ctc1TG", "mthc1TG",
+ map_cop1bc, false, false, false,
+ false, false, false, false,
+ map_cop1s, map_cop1d, false, false,
+ map_cop1w, map_cop1l, map_cop1ps,
+}
+
+local map_cop1x = {
+ shift = 0, mask = 63,
+ [0] = "lwxc1FSX", "ldxc1FSX", false, false,
+ false, "luxc1FSX", false, false,
+ "swxc1FSX", "sdxc1FSX", false, false,
+ false, "suxc1FSX", false, "prefxMSX",
+ false, false, false, false,
+ false, false, false, false,
+ false, false, false, false,
+ false, false, "alnv.psFGHS", false,
+ "madd.sFRGH", "madd.dFRGH", false, false,
+ false, false, "madd.psFRGH", false,
+ "msub.sFRGH", "msub.dFRGH", false, false,
+ false, false, "msub.psFRGH", false,
+ "nmadd.sFRGH", "nmadd.dFRGH", false, false,
+ false, false, "nmadd.psFRGH", false,
+ "nmsub.sFRGH", "nmsub.dFRGH", false, false,
+ false, false, "nmsub.psFRGH", false,
+}
+
+local map_pri = {
+ [0] = map_special, map_regimm, "jJ", "jalJ",
+ "beq|beqz|bST00B", "bne|bnezST0B", "blezSB", "bgtzSB",
+ "addiTSI", "addiu|liTS0I", "sltiTSI", "sltiuTSI",
+ "andiTSU", "ori|liTS0U", "xoriTSU", "luiTU",
+ map_cop0, map_cop1, false, map_cop1x,
+ "beql|beqzlST0B", "bnel|bnezlST0B", "blezlSB", "bgtzlSB",
+ "daddiTSI", "daddiuTSI", false, false,
+ map_special2, "jalxJ", false, map_special3,
+ "lbTSO", "lhTSO", "lwlTSO", "lwTSO",
+ "lbuTSO", "lhuTSO", "lwrTSO", false,
+ "sbTSO", "shTSO", "swlTSO", "swTSO",
+ false, false, "swrTSO", "cacheNSO",
+ "llTSO", "lwc1HSO", "lwc2TSO", "prefNSO",
+ false, "ldc1HSO", "ldc2TSO", "ldTSO",
+ "scTSO", "swc1HSO", "swc2TSO", false,
+ false, "sdc1HSO", "sdc2TSO", "sdTSO",
+}
+
+------------------------------------------------------------------------------
+-- Primary and extended opcode maps for MIPS R6
+------------------------------------------------------------------------------
+
+local map_mul_r6 = { shift = 6, mask = 3, [2] = "mulDST", [3] = "muhDST" }
+local map_mulu_r6 = { shift = 6, mask = 3, [2] = "muluDST", [3] = "muhuDST" }
+local map_div_r6 = { shift = 6, mask = 3, [2] = "divDST", [3] = "modDST" }
+local map_divu_r6 = { shift = 6, mask = 3, [2] = "divuDST", [3] = "moduDST" }
+local map_dmul_r6 = { shift = 6, mask = 3, [2] = "dmulDST", [3] = "dmuhDST" }
+local map_dmulu_r6 = { shift = 6, mask = 3, [2] = "dmuluDST", [3] = "dmuhuDST" }
+local map_ddiv_r6 = { shift = 6, mask = 3, [2] = "ddivDST", [3] = "dmodDST" }
+local map_ddivu_r6 = { shift = 6, mask = 3, [2] = "ddivuDST", [3] = "dmoduDST" }
+
+local map_special_r6 = {
+ shift = 0, mask = 63,
+ [0] = { shift = 0, mask = -1, [0] = "nop", _ = "sllDTA" },
+ false, map_srl, "sraDTA",
+ "sllvDTS", false, map_srlv, "sravDTS",
+ "jrS", "jalrD1S", false, false,
+ "syscallY", "breakY", false, "sync",
+ "clzDS", "cloDS", "dclzDS", "dcloDS",
+ "dsllvDST", "dlsaDSTA", "dsrlvDST", "dsravDST",
+ map_mul_r6, map_mulu_r6, map_div_r6, map_divu_r6,
+ map_dmul_r6, map_dmulu_r6, map_ddiv_r6, map_ddivu_r6,
+ "addDST", "addu|moveDST0", "subDST", "subu|neguDS0T",
+ "andDST", "or|moveDST0", "xorDST", "nor|notDST0",
+ false, false, "sltDST", "sltuDST",
+ "daddDST", "dadduDST", "dsubDST", "dsubuDST",
+ "tgeSTZ", "tgeuSTZ", "tltSTZ", "tltuSTZ",
+ "teqSTZ", "seleqzDST", "tneSTZ", "selnezDST",
+ "dsllDTA", false, "dsrlDTA", "dsraDTA",
+ "dsll32DTA", false, "dsrl32DTA", "dsra32DTA",
+}
+
+local map_bshfl_r6 = {
+ shift = 9, mask = 3,
+ [1] = "alignDSTa",
+ _ = {
+ shift = 6, mask = 31,
+ [0] = "bitswapDT",
+ [2] = "wsbhDT",
+ [16] = "sebDT",
+ [24] = "sehDT",
+ }
+}
+
+local map_dbshfl_r6 = {
+ shift = 9, mask = 3,
+ [1] = "dalignDSTa",
+ _ = {
+ shift = 6, mask = 31,
+ [0] = "dbitswapDT",
+ [2] = "dsbhDT",
+ [5] = "dshdDT",
+ }
+}
+
+local map_special3_r6 = {
+ shift = 0, mask = 63,
+ [0] = "extTSAK", [1] = "dextmTSAP", [3] = "dextTSAK",
+ [4] = "insTSAL", [6] = "dinsuTSEQ", [7] = "dinsTSAL",
+ [32] = map_bshfl_r6, [36] = map_dbshfl_r6, [59] = "rdhwrTD",
+}
+
+local map_regimm_r6 = {
+ shift = 16, mask = 31,
+ [0] = "bltzSB", [1] = "bgezSB",
+ [6] = "dahiSI", [30] = "datiSI",
+ [23] = "sigrieI", [31] = "synciSO",
+}
+
+local map_pcrel_r6 = {
+ shift = 19, mask = 3,
+ [0] = "addiupcS2", "lwpcS2", "lwupcS2", {
+ shift = 18, mask = 1,
+ [0] = "ldpcS3", { shift = 16, mask = 3, [2] = "auipcSI", [3] = "aluipcSI" }
+ }
+}
+
+local map_cop1s_r6 = {
+ shift = 0, mask = 63,
+ [0] = "add.sFGH", "sub.sFGH", "mul.sFGH", "div.sFGH",
+ "sqrt.sFG", "abs.sFG", "mov.sFG", "neg.sFG",
+ "round.l.sFG", "trunc.l.sFG", "ceil.l.sFG", "floor.l.sFG",
+ "round.w.sFG", "trunc.w.sFG", "ceil.w.sFG", "floor.w.sFG",
+ "sel.sFGH", false, false, false,
+ "seleqz.sFGH", "recip.sFG", "rsqrt.sFG", "selnez.sFGH",
+ "maddf.sFGH", "msubf.sFGH", "rint.sFG", "class.sFG",
+ "min.sFGH", "mina.sFGH", "max.sFGH", "maxa.sFGH",
+ false, "cvt.d.sFG", false, false,
+ "cvt.w.sFG", "cvt.l.sFG",
+}
+
+local map_cop1d_r6 = {
+ shift = 0, mask = 63,
+ [0] = "add.dFGH", "sub.dFGH", "mul.dFGH", "div.dFGH",
+ "sqrt.dFG", "abs.dFG", "mov.dFG", "neg.dFG",
+ "round.l.dFG", "trunc.l.dFG", "ceil.l.dFG", "floor.l.dFG",
+ "round.w.dFG", "trunc.w.dFG", "ceil.w.dFG", "floor.w.dFG",
+ "sel.dFGH", false, false, false,
+ "seleqz.dFGH", "recip.dFG", "rsqrt.dFG", "selnez.dFGH",
+ "maddf.dFGH", "msubf.dFGH", "rint.dFG", "class.dFG",
+ "min.dFGH", "mina.dFGH", "max.dFGH", "maxa.dFGH",
+ "cvt.s.dFG", false, false, false,
+ "cvt.w.dFG", "cvt.l.dFG",
+}
+
+local map_cop1w_r6 = {
+ shift = 0, mask = 63,
+ [0] = "cmp.af.sFGH", "cmp.un.sFGH", "cmp.eq.sFGH", "cmp.ueq.sFGH",
+ "cmp.lt.sFGH", "cmp.ult.sFGH", "cmp.le.sFGH", "cmp.ule.sFGH",
+ "cmp.saf.sFGH", "cmp.sun.sFGH", "cmp.seq.sFGH", "cmp.sueq.sFGH",
+ "cmp.slt.sFGH", "cmp.sult.sFGH", "cmp.sle.sFGH", "cmp.sule.sFGH",
+ false, "cmp.or.sFGH", "cmp.une.sFGH", "cmp.ne.sFGH",
+ false, false, false, false,
+ false, "cmp.sor.sFGH", "cmp.sune.sFGH", "cmp.sne.sFGH",
+ false, false, false, false,
+ "cvt.s.wFG", "cvt.d.wFG",
+}
+
+local map_cop1l_r6 = {
+ shift = 0, mask = 63,
+ [0] = "cmp.af.dFGH", "cmp.un.dFGH", "cmp.eq.dFGH", "cmp.ueq.dFGH",
+ "cmp.lt.dFGH", "cmp.ult.dFGH", "cmp.le.dFGH", "cmp.ule.dFGH",
+ "cmp.saf.dFGH", "cmp.sun.dFGH", "cmp.seq.dFGH", "cmp.sueq.dFGH",
+ "cmp.slt.dFGH", "cmp.sult.dFGH", "cmp.sle.dFGH", "cmp.sule.dFGH",
+ false, "cmp.or.dFGH", "cmp.une.dFGH", "cmp.ne.dFGH",
+ false, false, false, false,
+ false, "cmp.sor.dFGH", "cmp.sune.dFGH", "cmp.sne.dFGH",
+ false, false, false, false,
+ "cvt.s.lFG", "cvt.d.lFG",
+}
+
+local map_cop1_r6 = {
+ shift = 21, mask = 31,
+ [0] = "mfc1TG", "dmfc1TG", "cfc1TG", "mfhc1TG",
+ "mtc1TG", "dmtc1TG", "ctc1TG", "mthc1TG",
+ false, "bc1eqzHB", false, false,
+ false, "bc1nezHB", false, false,
+ map_cop1s_r6, map_cop1d_r6, false, false,
+ map_cop1w_r6, map_cop1l_r6,
+}
+
+local function maprs_popTS(rs, rt)
+ if rt == 0 then return 0 elseif rs == 0 then return 1
+ elseif rs == rt then return 2 else return 3 end
+end
+
+local map_pop06_r6 = {
+ maprs = maprs_popTS, [0] = "blezSB", "blezalcTB", "bgezalcTB", "bgeucSTB"
+}
+local map_pop07_r6 = {
+ maprs = maprs_popTS, [0] = "bgtzSB", "bgtzalcTB", "bltzalcTB", "bltucSTB"
+}
+local map_pop26_r6 = {
+ maprs = maprs_popTS, "blezcTB", "bgezcTB", "bgecSTB"
+}
+local map_pop27_r6 = {
+ maprs = maprs_popTS, "bgtzcTB", "bltzcTB", "bltcSTB"
+}
+
+local function maprs_popS(rs, rt)
+ if rs == 0 then return 0 else return 1 end
+end
+
+local map_pop66_r6 = {
+ maprs = maprs_popS, [0] = "jicTI", "beqzcSb"
+}
+local map_pop76_r6 = {
+ maprs = maprs_popS, [0] = "jialcTI", "bnezcSb"
+}
+
+local function maprs_popST(rs, rt)
+ if rs >= rt then return 0 elseif rs == 0 then return 1 else return 2 end
+end
+
+local map_pop10_r6 = {
+ maprs = maprs_popST, [0] = "bovcSTB", "beqzalcTB", "beqcSTB"
+}
+local map_pop30_r6 = {
+ maprs = maprs_popST, [0] = "bnvcSTB", "bnezalcTB", "bnecSTB"
+}
+
+local map_pri_r6 = {
+ [0] = map_special_r6, map_regimm_r6, "jJ", "jalJ",
+ "beq|beqz|bST00B", "bne|bnezST0B", map_pop06_r6, map_pop07_r6,
+ map_pop10_r6, "addiu|liTS0I", "sltiTSI", "sltiuTSI",
+ "andiTSU", "ori|liTS0U", "xoriTSU", "aui|luiTS0U",
+ map_cop0, map_cop1_r6, false, false,
+ false, false, map_pop26_r6, map_pop27_r6,
+ map_pop30_r6, "daddiuTSI", false, false,
+ false, "dauiTSI", false, map_special3_r6,
+ "lbTSO", "lhTSO", false, "lwTSO",
+ "lbuTSO", "lhuTSO", false, false,
+ "sbTSO", "shTSO", false, "swTSO",
+ false, false, false, false,
+ false, "lwc1HSO", "bc#", false,
+ false, "ldc1HSO", map_pop66_r6, "ldTSO",
+ false, "swc1HSO", "balc#", map_pcrel_r6,
+ false, "sdc1HSO", map_pop76_r6, "sdTSO",
+}
+
+------------------------------------------------------------------------------
+
+local map_gpr = {
+ [0] = "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
+ "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
+ "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23",
+ "r24", "r25", "r26", "r27", "r28", "sp", "r30", "ra",
+}
+
+------------------------------------------------------------------------------
+
+-- Output a nicely formatted line with an opcode and operands.
+local function putop(ctx, text, operands)
+ local pos = ctx.pos
+ local extra = ""
+ if ctx.rel then
+ local sym = ctx.symtab[ctx.rel]
+ if sym then extra = "\t->"..sym end
+ end
+ if ctx.hexdump > 0 then
+ ctx.out(format("%08x %s %-7s %s%s\n",
+ ctx.addr+pos, tohex(ctx.op), text, concat(operands, ", "), extra))
+ else
+ ctx.out(format("%08x %-7s %s%s\n",
+ ctx.addr+pos, text, concat(operands, ", "), extra))
+ end
+ ctx.pos = pos + 4
+end
+
+-- Fallback for unknown opcodes.
+local function unknown(ctx)
+ return putop(ctx, ".long", { "0x"..tohex(ctx.op) })
+end
+
+local function get_be(ctx)
+ local pos = ctx.pos
+ local b0, b1, b2, b3 = byte(ctx.code, pos+1, pos+4)
+ return bor(lshift(b0, 24), lshift(b1, 16), lshift(b2, 8), b3)
+end
+
+local function get_le(ctx)
+ local pos = ctx.pos
+ local b0, b1, b2, b3 = byte(ctx.code, pos+1, pos+4)
+ return bor(lshift(b3, 24), lshift(b2, 16), lshift(b1, 8), b0)
+end
+
+-- Disassemble a single instruction.
+local function disass_ins(ctx)
+ local op = ctx:get()
+ local operands = {}
+ local last = nil
+ ctx.op = op
+ ctx.rel = nil
+
+ local opat = ctx.map_pri[rshift(op, 26)]
+ while type(opat) ~= "string" do
+ if not opat then return unknown(ctx) end
+ if opat.maprs then
+ opat = opat[opat.maprs(band(rshift(op,21),31), band(rshift(op,16),31))]
+ else
+ opat = opat[band(rshift(op, opat.shift), opat.mask)] or opat._
+ end
+ end
+ local name, pat = match(opat, "^([a-z0-9_.]*)(.*)")
+ local altname, pat2 = match(pat, "|([a-z0-9_.|]*)(.*)")
+ if altname then pat = pat2 end
+
+ for p in gmatch(pat, ".") do
+ local x = nil
+ if p == "S" then
+ x = map_gpr[band(rshift(op, 21), 31)]
+ elseif p == "T" then
+ x = map_gpr[band(rshift(op, 16), 31)]
+ elseif p == "D" then
+ x = map_gpr[band(rshift(op, 11), 31)]
+ elseif p == "F" then
+ x = "f"..band(rshift(op, 6), 31)
+ elseif p == "G" then
+ x = "f"..band(rshift(op, 11), 31)
+ elseif p == "H" then
+ x = "f"..band(rshift(op, 16), 31)
+ elseif p == "R" then
+ x = "f"..band(rshift(op, 21), 31)
+ elseif p == "A" then
+ x = band(rshift(op, 6), 31)
+ elseif p == "a" then
+ x = band(rshift(op, 6), 7)
+ elseif p == "E" then
+ x = band(rshift(op, 6), 31) + 32
+ elseif p == "M" then
+ x = band(rshift(op, 11), 31)
+ elseif p == "N" then
+ x = band(rshift(op, 16), 31)
+ elseif p == "C" then
+ x = band(rshift(op, 18), 7)
+ if x == 0 then x = nil end
+ elseif p == "K" then
+ x = band(rshift(op, 11), 31) + 1
+ elseif p == "P" then
+ x = band(rshift(op, 11), 31) + 33
+ elseif p == "L" then
+ x = band(rshift(op, 11), 31) - last + 1
+ elseif p == "Q" then
+ x = band(rshift(op, 11), 31) - last + 33
+ elseif p == "I" then
+ x = arshift(lshift(op, 16), 16)
+ elseif p == "2" then
+ x = arshift(lshift(op, 13), 11)
+ elseif p == "3" then
+ x = arshift(lshift(op, 14), 11)
+ elseif p == "U" then
+ x = band(op, 0xffff)
+ elseif p == "O" then
+ local disp = arshift(lshift(op, 16), 16)
+ operands[#operands] = format("%d(%s)", disp, last)
+ elseif p == "X" then
+ local index = map_gpr[band(rshift(op, 16), 31)]
+ operands[#operands] = format("%s(%s)", index, last)
+ elseif p == "B" then
+ x = ctx.addr + ctx.pos + arshift(lshift(op, 16), 14) + 4
+ ctx.rel = x
+ x = format("0x%08x", x)
+ elseif p == "b" then
+ x = ctx.addr + ctx.pos + arshift(lshift(op, 11), 9) + 4
+ ctx.rel = x
+ x = format("0x%08x", x)
+ elseif p == "#" then
+ x = ctx.addr + ctx.pos + arshift(lshift(op, 6), 4) + 4
+ ctx.rel = x
+ x = format("0x%08x", x)
+ elseif p == "J" then
+ local a = ctx.addr + ctx.pos
+ x = a - band(a, 0x0fffffff) + band(op, 0x03ffffff)*4
+ ctx.rel = x
+ x = format("0x%08x", x)
+ elseif p == "V" then
+ x = band(rshift(op, 8), 7)
+ if x == 0 then x = nil end
+ elseif p == "W" then
+ x = band(op, 7)
+ if x == 0 then x = nil end
+ elseif p == "Y" then
+ x = band(rshift(op, 6), 0x000fffff)
+ if x == 0 then x = nil end
+ elseif p == "Z" then
+ x = band(rshift(op, 6), 1023)
+ if x == 0 then x = nil end
+ elseif p == "0" then
+ if last == "r0" or last == 0 then
+ local n = #operands
+ operands[n] = nil
+ last = operands[n-1]
+ if altname then
+ local a1, a2 = match(altname, "([^|]*)|(.*)")
+ if a1 then name, altname = a1, a2
+ else name = altname end
+ end
+ end
+ elseif p == "1" then
+ if last == "ra" then
+ operands[#operands] = nil
+ end
+ else
+ assert(false)
+ end
+ if x then operands[#operands+1] = x; last = x end
+ end
+
+ return putop(ctx, name, operands)
+end
+
+------------------------------------------------------------------------------
+
+-- Disassemble a block of code.
+local function disass_block(ctx, ofs, len)
+ if not ofs then ofs = 0 end
+ local stop = len and ofs+len or #ctx.code
+ stop = stop - stop % 4
+ ctx.pos = ofs - ofs % 4
+ ctx.rel = nil
+ while ctx.pos < stop do disass_ins(ctx) end
+end
+
+-- Extended API: create a disassembler context. Then call ctx:disass(ofs, len).
+local function create(code, addr, out)
+ local ctx = {}
+ ctx.code = code
+ ctx.addr = addr or 0
+ ctx.out = out or io.write
+ ctx.symtab = {}
+ ctx.disass = disass_block
+ ctx.hexdump = 8
+ ctx.get = get_be
+ ctx.map_pri = map_pri
+ return ctx
+end
+
+local function create_el(code, addr, out)
+ local ctx = create(code, addr, out)
+ ctx.get = get_le
+ return ctx
+end
+
+local function create_r6(code, addr, out)
+ local ctx = create(code, addr, out)
+ ctx.map_pri = map_pri_r6
+ return ctx
+end
+
+local function create_r6_el(code, addr, out)
+ local ctx = create(code, addr, out)
+ ctx.get = get_le
+ ctx.map_pri = map_pri_r6
+ return ctx
+end
+
+-- Simple API: disassemble code (a string) at address and output via out.
+local function disass(code, addr, out)
+ create(code, addr, out):disass()
+end
+
+local function disass_el(code, addr, out)
+ create_el(code, addr, out):disass()
+end
+
+local function disass_r6(code, addr, out)
+ create_r6(code, addr, out):disass()
+end
+
+local function disass_r6_el(code, addr, out)
+ create_r6_el(code, addr, out):disass()
+end
+
+-- Return register name for RID.
+local function regname(r)
+ if r < 32 then return map_gpr[r] end
+ return "f"..(r-32)
+end
+
+-- Public module functions.
+return {
+ create = create,
+ create_el = create_el,
+ create_r6 = create_r6,
+ create_r6_el = create_r6_el,
+ disass = disass,
+ disass_el = disass_el,
+ disass_r6 = disass_r6,
+ disass_r6_el = disass_r6_el,
+ regname = regname
+}
+
diff --git a/jit/dis_mips64.lua b/jit/dis_mips64.lua
new file mode 100644
index 0000000..d7d160a
--- /dev/null
+++ b/jit/dis_mips64.lua
@@ -0,0 +1,17 @@
+----------------------------------------------------------------------------
+-- LuaJIT MIPS64 disassembler wrapper module.
+--
+-- Copyright (C) 2005-2026 Mike Pall. All rights reserved.
+-- Released under the MIT license. See Copyright Notice in luajit.h
+----------------------------------------------------------------------------
+-- This module just exports the big-endian functions from the
+-- MIPS disassembler module. All the interesting stuff is there.
+------------------------------------------------------------------------------
+
+local dis_mips = require((string.match(..., ".*%.") or "").."dis_mips")
+return {
+ create = dis_mips.create,
+ disass = dis_mips.disass,
+ regname = dis_mips.regname
+}
+
diff --git a/jit/dis_mips64el.lua b/jit/dis_mips64el.lua
new file mode 100644
index 0000000..06767c0
--- /dev/null
+++ b/jit/dis_mips64el.lua
@@ -0,0 +1,17 @@
+----------------------------------------------------------------------------
+-- LuaJIT MIPS64EL disassembler wrapper module.
+--
+-- Copyright (C) 2005-2026 Mike Pall. All rights reserved.
+-- Released under the MIT license. See Copyright Notice in luajit.h
+----------------------------------------------------------------------------
+-- This module just exports the little-endian functions from the
+-- MIPS disassembler module. All the interesting stuff is there.
+------------------------------------------------------------------------------
+
+local dis_mips = require((string.match(..., ".*%.") or "").."dis_mips")
+return {
+ create = dis_mips.create_el,
+ disass = dis_mips.disass_el,
+ regname = dis_mips.regname
+}
+
diff --git a/jit/dis_mips64r6.lua b/jit/dis_mips64r6.lua
new file mode 100644
index 0000000..ef4a125
--- /dev/null
+++ b/jit/dis_mips64r6.lua
@@ -0,0 +1,17 @@
+----------------------------------------------------------------------------
+-- LuaJIT MIPS64R6 disassembler wrapper module.
+--
+-- Copyright (C) 2005-2026 Mike Pall. All rights reserved.
+-- Released under the MIT license. See Copyright Notice in luajit.h
+----------------------------------------------------------------------------
+-- This module just exports the r6 big-endian functions from the
+-- MIPS disassembler module. All the interesting stuff is there.
+------------------------------------------------------------------------------
+
+local dis_mips = require((string.match(..., ".*%.") or "").."dis_mips")
+return {
+ create = dis_mips.create_r6,
+ disass = dis_mips.disass_r6,
+ regname = dis_mips.regname
+}
+
diff --git a/jit/dis_mips64r6el.lua b/jit/dis_mips64r6el.lua
new file mode 100644
index 0000000..4dc6a54
--- /dev/null
+++ b/jit/dis_mips64r6el.lua
@@ -0,0 +1,17 @@
+----------------------------------------------------------------------------
+-- LuaJIT MIPS64R6EL disassembler wrapper module.
+--
+-- Copyright (C) 2005-2026 Mike Pall. All rights reserved.
+-- Released under the MIT license. See Copyright Notice in luajit.h
+----------------------------------------------------------------------------
+-- This module just exports the r6 little-endian functions from the
+-- MIPS disassembler module. All the interesting stuff is there.
+------------------------------------------------------------------------------
+
+local dis_mips = require((string.match(..., ".*%.") or "").."dis_mips")
+return {
+ create = dis_mips.create_r6_el,
+ disass = dis_mips.disass_r6_el,
+ regname = dis_mips.regname
+}
+
diff --git a/jit/dis_mipsel.lua b/jit/dis_mipsel.lua
new file mode 100644
index 0000000..1da538a
--- /dev/null
+++ b/jit/dis_mipsel.lua
@@ -0,0 +1,17 @@
+----------------------------------------------------------------------------
+-- LuaJIT MIPSEL disassembler wrapper module.
+--
+-- Copyright (C) 2005-2026 Mike Pall. All rights reserved.
+-- Released under the MIT license. See Copyright Notice in luajit.h
+----------------------------------------------------------------------------
+-- This module just exports the little-endian functions from the
+-- MIPS disassembler module. All the interesting stuff is there.
+------------------------------------------------------------------------------
+
+local dis_mips = require((string.match(..., ".*%.") or "").."dis_mips")
+return {
+ create = dis_mips.create_el,
+ disass = dis_mips.disass_el,
+ regname = dis_mips.regname
+}
+
diff --git a/jit/dis_ppc.lua b/jit/dis_ppc.lua
new file mode 100644
index 0000000..d8f4cfb
--- /dev/null
+++ b/jit/dis_ppc.lua
@@ -0,0 +1,591 @@
+----------------------------------------------------------------------------
+-- LuaJIT PPC disassembler module.
+--
+-- Copyright (C) 2005-2026 Mike Pall. All rights reserved.
+-- Released under the MIT/X license. See Copyright Notice in luajit.h
+----------------------------------------------------------------------------
+-- This is a helper module used by the LuaJIT machine code dumper module.
+--
+-- It disassembles all common, non-privileged 32/64 bit PowerPC instructions
+-- plus the e500 SPE instructions and some Cell/Xenon extensions.
+--
+-- NYI: VMX, VMX128
+------------------------------------------------------------------------------
+
+local type = type
+local byte, format = string.byte, string.format
+local match, gmatch, gsub = string.match, string.gmatch, string.gsub
+local concat = table.concat
+local bit = require("bit")
+local band, bor, tohex = bit.band, bit.bor, bit.tohex
+local lshift, rshift, arshift = bit.lshift, bit.rshift, bit.arshift
+
+------------------------------------------------------------------------------
+-- Primary and extended opcode maps
+------------------------------------------------------------------------------
+
+local map_crops = {
+ shift = 1, mask = 1023,
+ [0] = "mcrfXX",
+ [33] = "crnor|crnotCCC=", [129] = "crandcCCC",
+ [193] = "crxor|crclrCCC%", [225] = "crnandCCC",
+ [257] = "crandCCC", [289] = "creqv|crsetCCC%",
+ [417] = "crorcCCC", [449] = "cror|crmoveCCC=",
+ [16] = "b_lrKB", [528] = "b_ctrKB",
+ [150] = "isync",
+}
+
+local map_rlwinm = setmetatable({
+ shift = 0, mask = -1,
+},
+{ __index = function(t, x)
+ local rot = band(rshift(x, 11), 31)
+ local mb = band(rshift(x, 6), 31)
+ local me = band(rshift(x, 1), 31)
+ if mb == 0 and me == 31-rot then
+ return "slwiRR~A."
+ elseif me == 31 and mb == 32-rot then
+ return "srwiRR~-A."
+ else
+ return "rlwinmRR~AAA."
+ end
+ end
+})
+
+local map_rld = {
+ shift = 2, mask = 7,
+ [0] = "rldiclRR~HM.", "rldicrRR~HM.", "rldicRR~HM.", "rldimiRR~HM.",
+ {
+ shift = 1, mask = 1,
+ [0] = "rldclRR~RM.", "rldcrRR~RM.",
+ },
+}
+
+local map_ext = setmetatable({
+ shift = 1, mask = 1023,
+
+ [0] = "cmp_YLRR", [32] = "cmpl_YLRR",
+ [4] = "twARR", [68] = "tdARR",
+
+ [8] = "subfcRRR.", [40] = "subfRRR.",
+ [104] = "negRR.", [136] = "subfeRRR.",
+ [200] = "subfzeRR.", [232] = "subfmeRR.",
+ [520] = "subfcoRRR.", [552] = "subfoRRR.",
+ [616] = "negoRR.", [648] = "subfeoRRR.",
+ [712] = "subfzeoRR.", [744] = "subfmeoRR.",
+
+ [9] = "mulhduRRR.", [73] = "mulhdRRR.", [233] = "mulldRRR.",
+ [457] = "divduRRR.", [489] = "divdRRR.",
+ [745] = "mulldoRRR.",
+ [969] = "divduoRRR.", [1001] = "divdoRRR.",
+
+ [10] = "addcRRR.", [138] = "addeRRR.",
+ [202] = "addzeRR.", [234] = "addmeRR.", [266] = "addRRR.",
+ [522] = "addcoRRR.", [650] = "addeoRRR.",
+ [714] = "addzeoRR.", [746] = "addmeoRR.", [778] = "addoRRR.",
+
+ [11] = "mulhwuRRR.", [75] = "mulhwRRR.", [235] = "mullwRRR.",
+ [459] = "divwuRRR.", [491] = "divwRRR.",
+ [747] = "mullwoRRR.",
+ [971] = "divwouRRR.", [1003] = "divwoRRR.",
+
+ [15] = "iselltRRR", [47] = "iselgtRRR", [79] = "iseleqRRR",
+
+ [144] = { shift = 20, mask = 1, [0] = "mtcrfRZ~", "mtocrfRZ~", },
+ [19] = { shift = 20, mask = 1, [0] = "mfcrR", "mfocrfRZ", },
+ [371] = { shift = 11, mask = 1023, [392] = "mftbR", [424] = "mftbuR", },
+ [339] = {
+ shift = 11, mask = 1023,
+ [32] = "mferR", [256] = "mflrR", [288] = "mfctrR", [16] = "mfspefscrR",
+ },
+ [467] = {
+ shift = 11, mask = 1023,
+ [32] = "mtxerR", [256] = "mtlrR", [288] = "mtctrR", [16] = "mtspefscrR",
+ },
+
+ [20] = "lwarxRR0R", [84] = "ldarxRR0R",
+
+ [21] = "ldxRR0R", [53] = "lduxRRR",
+ [149] = "stdxRR0R", [181] = "stduxRRR",
+ [341] = "lwaxRR0R", [373] = "lwauxRRR",
+
+ [23] = "lwzxRR0R", [55] = "lwzuxRRR",
+ [87] = "lbzxRR0R", [119] = "lbzuxRRR",
+ [151] = "stwxRR0R", [183] = "stwuxRRR",
+ [215] = "stbxRR0R", [247] = "stbuxRRR",
+ [279] = "lhzxRR0R", [311] = "lhzuxRRR",
+ [343] = "lhaxRR0R", [375] = "lhauxRRR",
+ [407] = "sthxRR0R", [439] = "sthuxRRR",
+
+ [54] = "dcbst-R0R", [86] = "dcbf-R0R",
+ [150] = "stwcxRR0R.", [214] = "stdcxRR0R.",
+ [246] = "dcbtst-R0R", [278] = "dcbt-R0R",
+ [310] = "eciwxRR0R", [438] = "ecowxRR0R",
+ [470] = "dcbi-RR",
+
+ [598] = {
+ shift = 21, mask = 3,
+ [0] = "sync", "lwsync", "ptesync",
+ },
+ [758] = "dcba-RR",
+ [854] = "eieio", [982] = "icbi-R0R", [1014] = "dcbz-R0R",
+
+ [26] = "cntlzwRR~", [58] = "cntlzdRR~",
+ [122] = "popcntbRR~",
+ [154] = "prtywRR~", [186] = "prtydRR~",
+
+ [28] = "andRR~R.", [60] = "andcRR~R.", [124] = "nor|notRR~R=.",
+ [284] = "eqvRR~R.", [316] = "xorRR~R.",
+ [412] = "orcRR~R.", [444] = "or|mrRR~R=.", [476] = "nandRR~R.",
+ [508] = "cmpbRR~R",
+
+ [512] = "mcrxrX",
+
+ [532] = "ldbrxRR0R", [660] = "stdbrxRR0R",
+
+ [533] = "lswxRR0R", [597] = "lswiRR0A",
+ [661] = "stswxRR0R", [725] = "stswiRR0A",
+
+ [534] = "lwbrxRR0R", [662] = "stwbrxRR0R",
+ [790] = "lhbrxRR0R", [918] = "sthbrxRR0R",
+
+ [535] = "lfsxFR0R", [567] = "lfsuxFRR",
+ [599] = "lfdxFR0R", [631] = "lfduxFRR",
+ [663] = "stfsxFR0R", [695] = "stfsuxFRR",
+ [727] = "stfdxFR0R", [759] = "stfduxFR0R",
+ [855] = "lfiwaxFR0R",
+ [983] = "stfiwxFR0R",
+
+ [24] = "slwRR~R.",
+
+ [27] = "sldRR~R.", [536] = "srwRR~R.",
+ [792] = "srawRR~R.", [824] = "srawiRR~A.",
+
+ [794] = "sradRR~R.", [826] = "sradiRR~H.", [827] = "sradiRR~H.",
+ [922] = "extshRR~.", [954] = "extsbRR~.", [986] = "extswRR~.",
+
+ [539] = "srdRR~R.",
+},
+{ __index = function(t, x)
+ if band(x, 31) == 15 then return "iselRRRC" end
+ end
+})
+
+local map_ld = {
+ shift = 0, mask = 3,
+ [0] = "ldRRE", "lduRRE", "lwaRRE",
+}
+
+local map_std = {
+ shift = 0, mask = 3,
+ [0] = "stdRRE", "stduRRE",
+}
+
+local map_fps = {
+ shift = 5, mask = 1,
+ {
+ shift = 1, mask = 15,
+ [0] = false, false, "fdivsFFF.", false,
+ "fsubsFFF.", "faddsFFF.", "fsqrtsF-F.", false,
+ "fresF-F.", "fmulsFF-F.", "frsqrtesF-F.", false,
+ "fmsubsFFFF~.", "fmaddsFFFF~.", "fnmsubsFFFF~.", "fnmaddsFFFF~.",
+ }
+}
+
+local map_fpd = {
+ shift = 5, mask = 1,
+ [0] = {
+ shift = 1, mask = 1023,
+ [0] = "fcmpuXFF", [32] = "fcmpoXFF", [64] = "mcrfsXX",
+ [38] = "mtfsb1A.", [70] = "mtfsb0A.", [134] = "mtfsfiA>>-A>",
+ [8] = "fcpsgnFFF.", [40] = "fnegF-F.", [72] = "fmrF-F.",
+ [136] = "fnabsF-F.", [264] = "fabsF-F.",
+ [12] = "frspF-F.",
+ [14] = "fctiwF-F.", [15] = "fctiwzF-F.",
+ [583] = "mffsF.", [711] = "mtfsfZF.",
+ [392] = "frinF-F.", [424] = "frizF-F.",
+ [456] = "fripF-F.", [488] = "frimF-F.",
+ [814] = "fctidF-F.", [815] = "fctidzF-F.", [846] = "fcfidF-F.",
+ },
+ {
+ shift = 1, mask = 15,
+ [0] = false, false, "fdivFFF.", false,
+ "fsubFFF.", "faddFFF.", "fsqrtF-F.", "fselFFFF~.",
+ "freF-F.", "fmulFF-F.", "frsqrteF-F.", false,
+ "fmsubFFFF~.", "fmaddFFFF~.", "fnmsubFFFF~.", "fnmaddFFFF~.",
+ }
+}
+
+local map_spe = {
+ shift = 0, mask = 2047,
+
+ [512] = "evaddwRRR", [514] = "evaddiwRAR~",
+ [516] = "evsubwRRR~", [518] = "evsubiwRAR~",
+ [520] = "evabsRR", [521] = "evnegRR",
+ [522] = "evextsbRR", [523] = "evextshRR", [524] = "evrndwRR",
+ [525] = "evcntlzwRR", [526] = "evcntlswRR",
+
+ [527] = "brincRRR",
+
+ [529] = "evandRRR", [530] = "evandcRRR", [534] = "evxorRRR",
+ [535] = "evor|evmrRRR=", [536] = "evnor|evnotRRR=",
+ [537] = "eveqvRRR", [539] = "evorcRRR", [542] = "evnandRRR",
+
+ [544] = "evsrwuRRR", [545] = "evsrwsRRR",
+ [546] = "evsrwiuRRA", [547] = "evsrwisRRA",
+ [548] = "evslwRRR", [550] = "evslwiRRA",
+ [552] = "evrlwRRR", [553] = "evsplatiRS",
+ [554] = "evrlwiRRA", [555] = "evsplatfiRS",
+ [556] = "evmergehiRRR", [557] = "evmergeloRRR",
+ [558] = "evmergehiloRRR", [559] = "evmergelohiRRR",
+
+ [560] = "evcmpgtuYRR", [561] = "evcmpgtsYRR",
+ [562] = "evcmpltuYRR", [563] = "evcmpltsYRR",
+ [564] = "evcmpeqYRR",
+
+ [632] = "evselRRR", [633] = "evselRRRW",
+ [634] = "evselRRRW", [635] = "evselRRRW",
+ [636] = "evselRRRW", [637] = "evselRRRW",
+ [638] = "evselRRRW", [639] = "evselRRRW",
+
+ [640] = "evfsaddRRR", [641] = "evfssubRRR",
+ [644] = "evfsabsRR", [645] = "evfsnabsRR", [646] = "evfsnegRR",
+ [648] = "evfsmulRRR", [649] = "evfsdivRRR",
+ [652] = "evfscmpgtYRR", [653] = "evfscmpltYRR", [654] = "evfscmpeqYRR",
+ [656] = "evfscfuiR-R", [657] = "evfscfsiR-R",
+ [658] = "evfscfufR-R", [659] = "evfscfsfR-R",
+ [660] = "evfsctuiR-R", [661] = "evfsctsiR-R",
+ [662] = "evfsctufR-R", [663] = "evfsctsfR-R",
+ [664] = "evfsctuizR-R", [666] = "evfsctsizR-R",
+ [668] = "evfststgtYRR", [669] = "evfststltYRR", [670] = "evfststeqYRR",
+
+ [704] = "efsaddRRR", [705] = "efssubRRR",
+ [708] = "efsabsRR", [709] = "efsnabsRR", [710] = "efsnegRR",
+ [712] = "efsmulRRR", [713] = "efsdivRRR",
+ [716] = "efscmpgtYRR", [717] = "efscmpltYRR", [718] = "efscmpeqYRR",
+ [719] = "efscfdR-R",
+ [720] = "efscfuiR-R", [721] = "efscfsiR-R",
+ [722] = "efscfufR-R", [723] = "efscfsfR-R",
+ [724] = "efsctuiR-R", [725] = "efsctsiR-R",
+ [726] = "efsctufR-R", [727] = "efsctsfR-R",
+ [728] = "efsctuizR-R", [730] = "efsctsizR-R",
+ [732] = "efststgtYRR", [733] = "efststltYRR", [734] = "efststeqYRR",
+
+ [736] = "efdaddRRR", [737] = "efdsubRRR",
+ [738] = "efdcfuidR-R", [739] = "efdcfsidR-R",
+ [740] = "efdabsRR", [741] = "efdnabsRR", [742] = "efdnegRR",
+ [744] = "efdmulRRR", [745] = "efddivRRR",
+ [746] = "efdctuidzR-R", [747] = "efdctsidzR-R",
+ [748] = "efdcmpgtYRR", [749] = "efdcmpltYRR", [750] = "efdcmpeqYRR",
+ [751] = "efdcfsR-R",
+ [752] = "efdcfuiR-R", [753] = "efdcfsiR-R",
+ [754] = "efdcfufR-R", [755] = "efdcfsfR-R",
+ [756] = "efdctuiR-R", [757] = "efdctsiR-R",
+ [758] = "efdctufR-R", [759] = "efdctsfR-R",
+ [760] = "efdctuizR-R", [762] = "efdctsizR-R",
+ [764] = "efdtstgtYRR", [765] = "efdtstltYRR", [766] = "efdtsteqYRR",
+
+ [768] = "evlddxRR0R", [769] = "evlddRR8",
+ [770] = "evldwxRR0R", [771] = "evldwRR8",
+ [772] = "evldhxRR0R", [773] = "evldhRR8",
+ [776] = "evlhhesplatxRR0R", [777] = "evlhhesplatRR2",
+ [780] = "evlhhousplatxRR0R", [781] = "evlhhousplatRR2",
+ [782] = "evlhhossplatxRR0R", [783] = "evlhhossplatRR2",
+ [784] = "evlwhexRR0R", [785] = "evlwheRR4",
+ [788] = "evlwhouxRR0R", [789] = "evlwhouRR4",
+ [790] = "evlwhosxRR0R", [791] = "evlwhosRR4",
+ [792] = "evlwwsplatxRR0R", [793] = "evlwwsplatRR4",
+ [796] = "evlwhsplatxRR0R", [797] = "evlwhsplatRR4",
+
+ [800] = "evstddxRR0R", [801] = "evstddRR8",
+ [802] = "evstdwxRR0R", [803] = "evstdwRR8",
+ [804] = "evstdhxRR0R", [805] = "evstdhRR8",
+ [816] = "evstwhexRR0R", [817] = "evstwheRR4",
+ [820] = "evstwhoxRR0R", [821] = "evstwhoRR4",
+ [824] = "evstwwexRR0R", [825] = "evstwweRR4",
+ [828] = "evstwwoxRR0R", [829] = "evstwwoRR4",
+
+ [1027] = "evmhessfRRR", [1031] = "evmhossfRRR", [1032] = "evmheumiRRR",
+ [1033] = "evmhesmiRRR", [1035] = "evmhesmfRRR", [1036] = "evmhoumiRRR",
+ [1037] = "evmhosmiRRR", [1039] = "evmhosmfRRR", [1059] = "evmhessfaRRR",
+ [1063] = "evmhossfaRRR", [1064] = "evmheumiaRRR", [1065] = "evmhesmiaRRR",
+ [1067] = "evmhesmfaRRR", [1068] = "evmhoumiaRRR", [1069] = "evmhosmiaRRR",
+ [1071] = "evmhosmfaRRR", [1095] = "evmwhssfRRR", [1096] = "evmwlumiRRR",
+ [1100] = "evmwhumiRRR", [1101] = "evmwhsmiRRR", [1103] = "evmwhsmfRRR",
+ [1107] = "evmwssfRRR", [1112] = "evmwumiRRR", [1113] = "evmwsmiRRR",
+ [1115] = "evmwsmfRRR", [1127] = "evmwhssfaRRR", [1128] = "evmwlumiaRRR",
+ [1132] = "evmwhumiaRRR", [1133] = "evmwhsmiaRRR", [1135] = "evmwhsmfaRRR",
+ [1139] = "evmwssfaRRR", [1144] = "evmwumiaRRR", [1145] = "evmwsmiaRRR",
+ [1147] = "evmwsmfaRRR",
+
+ [1216] = "evaddusiaawRR", [1217] = "evaddssiaawRR",
+ [1218] = "evsubfusiaawRR", [1219] = "evsubfssiaawRR",
+ [1220] = "evmraRR",
+ [1222] = "evdivwsRRR", [1223] = "evdivwuRRR",
+ [1224] = "evaddumiaawRR", [1225] = "evaddsmiaawRR",
+ [1226] = "evsubfumiaawRR", [1227] = "evsubfsmiaawRR",
+
+ [1280] = "evmheusiaawRRR", [1281] = "evmhessiaawRRR",
+ [1283] = "evmhessfaawRRR", [1284] = "evmhousiaawRRR",
+ [1285] = "evmhossiaawRRR", [1287] = "evmhossfaawRRR",
+ [1288] = "evmheumiaawRRR", [1289] = "evmhesmiaawRRR",
+ [1291] = "evmhesmfaawRRR", [1292] = "evmhoumiaawRRR",
+ [1293] = "evmhosmiaawRRR", [1295] = "evmhosmfaawRRR",
+ [1320] = "evmhegumiaaRRR", [1321] = "evmhegsmiaaRRR",
+ [1323] = "evmhegsmfaaRRR", [1324] = "evmhogumiaaRRR",
+ [1325] = "evmhogsmiaaRRR", [1327] = "evmhogsmfaaRRR",
+ [1344] = "evmwlusiaawRRR", [1345] = "evmwlssiaawRRR",
+ [1352] = "evmwlumiaawRRR", [1353] = "evmwlsmiaawRRR",
+ [1363] = "evmwssfaaRRR", [1368] = "evmwumiaaRRR",
+ [1369] = "evmwsmiaaRRR", [1371] = "evmwsmfaaRRR",
+ [1408] = "evmheusianwRRR", [1409] = "evmhessianwRRR",
+ [1411] = "evmhessfanwRRR", [1412] = "evmhousianwRRR",
+ [1413] = "evmhossianwRRR", [1415] = "evmhossfanwRRR",
+ [1416] = "evmheumianwRRR", [1417] = "evmhesmianwRRR",
+ [1419] = "evmhesmfanwRRR", [1420] = "evmhoumianwRRR",
+ [1421] = "evmhosmianwRRR", [1423] = "evmhosmfanwRRR",
+ [1448] = "evmhegumianRRR", [1449] = "evmhegsmianRRR",
+ [1451] = "evmhegsmfanRRR", [1452] = "evmhogumianRRR",
+ [1453] = "evmhogsmianRRR", [1455] = "evmhogsmfanRRR",
+ [1472] = "evmwlusianwRRR", [1473] = "evmwlssianwRRR",
+ [1480] = "evmwlumianwRRR", [1481] = "evmwlsmianwRRR",
+ [1491] = "evmwssfanRRR", [1496] = "evmwumianRRR",
+ [1497] = "evmwsmianRRR", [1499] = "evmwsmfanRRR",
+}
+
+local map_pri = {
+ [0] = false, false, "tdiARI", "twiARI",
+ map_spe, false, false, "mulliRRI",
+ "subficRRI", false, "cmpl_iYLRU", "cmp_iYLRI",
+ "addicRRI", "addic.RRI", "addi|liRR0I", "addis|lisRR0I",
+ "b_KBJ", "sc", "bKJ", map_crops,
+ "rlwimiRR~AAA.", map_rlwinm, false, "rlwnmRR~RAA.",
+ "oriNRR~U", "orisRR~U", "xoriRR~U", "xorisRR~U",
+ "andi.RR~U", "andis.RR~U", map_rld, map_ext,
+ "lwzRRD", "lwzuRRD", "lbzRRD", "lbzuRRD",
+ "stwRRD", "stwuRRD", "stbRRD", "stbuRRD",
+ "lhzRRD", "lhzuRRD", "lhaRRD", "lhauRRD",
+ "sthRRD", "sthuRRD", "lmwRRD", "stmwRRD",
+ "lfsFRD", "lfsuFRD", "lfdFRD", "lfduFRD",
+ "stfsFRD", "stfsuFRD", "stfdFRD", "stfduFRD",
+ false, false, map_ld, map_fps,
+ false, false, map_std, map_fpd,
+}
+
+------------------------------------------------------------------------------
+
+local map_gpr = {
+ [0] = "r0", "sp", "r2", "r3", "r4", "r5", "r6", "r7",
+ "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
+ "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23",
+ "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31",
+}
+
+local map_cond = { [0] = "lt", "gt", "eq", "so", "ge", "le", "ne", "ns", }
+
+-- Format a condition bit.
+local function condfmt(cond)
+ if cond <= 3 then
+ return map_cond[band(cond, 3)]
+ else
+ return format("4*cr%d+%s", rshift(cond, 2), map_cond[band(cond, 3)])
+ end
+end
+
+------------------------------------------------------------------------------
+
+-- Output a nicely formatted line with an opcode and operands.
+local function putop(ctx, text, operands)
+ local pos = ctx.pos
+ local extra = ""
+ if ctx.rel then
+ local sym = ctx.symtab[ctx.rel]
+ if sym then extra = "\t->"..sym end
+ end
+ if ctx.hexdump > 0 then
+ ctx.out(format("%08x %s %-7s %s%s\n",
+ ctx.addr+pos, tohex(ctx.op), text, concat(operands, ", "), extra))
+ else
+ ctx.out(format("%08x %-7s %s%s\n",
+ ctx.addr+pos, text, concat(operands, ", "), extra))
+ end
+ ctx.pos = pos + 4
+end
+
+-- Fallback for unknown opcodes.
+local function unknown(ctx)
+ return putop(ctx, ".long", { "0x"..tohex(ctx.op) })
+end
+
+-- Disassemble a single instruction.
+local function disass_ins(ctx)
+ local pos = ctx.pos
+ local b0, b1, b2, b3 = byte(ctx.code, pos+1, pos+4)
+ local op = bor(lshift(b0, 24), lshift(b1, 16), lshift(b2, 8), b3)
+ local operands = {}
+ local last = nil
+ local rs = 21
+ ctx.op = op
+ ctx.rel = nil
+
+ local opat = map_pri[rshift(b0, 2)]
+ while type(opat) ~= "string" do
+ if not opat then return unknown(ctx) end
+ opat = opat[band(rshift(op, opat.shift), opat.mask)]
+ end
+ local name, pat = match(opat, "^([a-z0-9_.]*)(.*)")
+ local altname, pat2 = match(pat, "|([a-z0-9_.]*)(.*)")
+ if altname then pat = pat2 end
+
+ for p in gmatch(pat, ".") do
+ local x = nil
+ if p == "R" then
+ x = map_gpr[band(rshift(op, rs), 31)]
+ rs = rs - 5
+ elseif p == "F" then
+ x = "f"..band(rshift(op, rs), 31)
+ rs = rs - 5
+ elseif p == "A" then
+ x = band(rshift(op, rs), 31)
+ rs = rs - 5
+ elseif p == "S" then
+ x = arshift(lshift(op, 27-rs), 27)
+ rs = rs - 5
+ elseif p == "I" then
+ x = arshift(lshift(op, 16), 16)
+ elseif p == "U" then
+ x = band(op, 0xffff)
+ elseif p == "D" or p == "E" then
+ local disp = arshift(lshift(op, 16), 16)
+ if p == "E" then disp = band(disp, -4) end
+ if last == "r0" then last = "0" end
+ operands[#operands] = format("%d(%s)", disp, last)
+ elseif p >= "2" and p <= "8" then
+ local disp = band(rshift(op, rs), 31) * p
+ if last == "r0" then last = "0" end
+ operands[#operands] = format("%d(%s)", disp, last)
+ elseif p == "H" then
+ x = band(rshift(op, rs), 31) + lshift(band(op, 2), 4)
+ rs = rs - 5
+ elseif p == "M" then
+ x = band(rshift(op, rs), 31) + band(op, 0x20)
+ elseif p == "C" then
+ x = condfmt(band(rshift(op, rs), 31))
+ rs = rs - 5
+ elseif p == "B" then
+ local bo = rshift(op, 21)
+ local cond = band(rshift(op, 16), 31)
+ local cn = ""
+ rs = rs - 10
+ if band(bo, 4) == 0 then
+ cn = band(bo, 2) == 0 and "dnz" or "dz"
+ if band(bo, 0x10) == 0 then
+ cn = cn..(band(bo, 8) == 0 and "f" or "t")
+ end
+ if band(bo, 0x10) == 0 then x = condfmt(cond) end
+ name = name..(band(bo, 1) == band(rshift(op, 15), 1) and "-" or "+")
+ elseif band(bo, 0x10) == 0 then
+ cn = map_cond[band(cond, 3) + (band(bo, 8) == 0 and 4 or 0)]
+ if cond > 3 then x = "cr"..rshift(cond, 2) end
+ name = name..(band(bo, 1) == band(rshift(op, 15), 1) and "-" or "+")
+ end
+ name = gsub(name, "_", cn)
+ elseif p == "J" then
+ x = arshift(lshift(op, 27-rs), 29-rs)*4
+ if band(op, 2) == 0 then x = ctx.addr + pos + x end
+ ctx.rel = x
+ x = "0x"..tohex(x)
+ elseif p == "K" then
+ if band(op, 1) ~= 0 then name = name.."l" end
+ if band(op, 2) ~= 0 then name = name.."a" end
+ elseif p == "X" or p == "Y" then
+ x = band(rshift(op, rs+2), 7)
+ if x == 0 and p == "Y" then x = nil else x = "cr"..x end
+ rs = rs - 5
+ elseif p == "W" then
+ x = "cr"..band(op, 7)
+ elseif p == "Z" then
+ x = band(rshift(op, rs-4), 255)
+ rs = rs - 10
+ elseif p == ">" then
+ operands[#operands] = rshift(operands[#operands], 1)
+ elseif p == "0" then
+ if last == "r0" then
+ operands[#operands] = nil
+ if altname then name = altname end
+ end
+ elseif p == "L" then
+ name = gsub(name, "_", band(op, 0x00200000) ~= 0 and "d" or "w")
+ elseif p == "." then
+ if band(op, 1) == 1 then name = name.."." end
+ elseif p == "N" then
+ if op == 0x60000000 then name = "nop"; break end
+ elseif p == "~" then
+ local n = #operands
+ operands[n-1], operands[n] = operands[n], operands[n-1]
+ elseif p == "=" then
+ local n = #operands
+ if last == operands[n-1] then
+ operands[n] = nil
+ name = altname
+ end
+ elseif p == "%" then
+ local n = #operands
+ if last == operands[n-1] and last == operands[n-2] then
+ operands[n] = nil
+ operands[n-1] = nil
+ name = altname
+ end
+ elseif p == "-" then
+ rs = rs - 5
+ else
+ assert(false)
+ end
+ if x then operands[#operands+1] = x; last = x end
+ end
+
+ return putop(ctx, name, operands)
+end
+
+------------------------------------------------------------------------------
+
+-- Disassemble a block of code.
+local function disass_block(ctx, ofs, len)
+ if not ofs then ofs = 0 end
+ local stop = len and ofs+len or #ctx.code
+ stop = stop - stop % 4
+ ctx.pos = ofs - ofs % 4
+ ctx.rel = nil
+ while ctx.pos < stop do disass_ins(ctx) end
+end
+
+-- Extended API: create a disassembler context. Then call ctx:disass(ofs, len).
+local function create(code, addr, out)
+ local ctx = {}
+ ctx.code = code
+ ctx.addr = addr or 0
+ ctx.out = out or io.write
+ ctx.symtab = {}
+ ctx.disass = disass_block
+ ctx.hexdump = 8
+ return ctx
+end
+
+-- Simple API: disassemble code (a string) at address and output via out.
+local function disass(code, addr, out)
+ create(code, addr, out):disass()
+end
+
+-- Return register name for RID.
+local function regname(r)
+ if r < 32 then return map_gpr[r] end
+ return "f"..(r-32)
+end
+
+-- Public module functions.
+return {
+ create = create,
+ disass = disass,
+ regname = regname
+}
+
diff --git a/jit/dis_x64.lua b/jit/dis_x64.lua
new file mode 100644
index 0000000..42ae48b
--- /dev/null
+++ b/jit/dis_x64.lua
@@ -0,0 +1,17 @@
+----------------------------------------------------------------------------
+-- LuaJIT x64 disassembler wrapper module.
+--
+-- Copyright (C) 2005-2026 Mike Pall. All rights reserved.
+-- Released under the MIT license. See Copyright Notice in luajit.h
+----------------------------------------------------------------------------
+-- This module just exports the 64 bit functions from the combined
+-- x86/x64 disassembler module. All the interesting stuff is there.
+------------------------------------------------------------------------------
+
+local dis_x86 = require((string.match(..., ".*%.") or "").."dis_x86")
+return {
+ create = dis_x86.create64,
+ disass = dis_x86.disass64,
+ regname = dis_x86.regname64
+}
+
diff --git a/jit/dis_x86.lua b/jit/dis_x86.lua
new file mode 100644
index 0000000..80bf721
--- /dev/null
+++ b/jit/dis_x86.lua
@@ -0,0 +1,971 @@
+----------------------------------------------------------------------------
+-- LuaJIT x86/x64 disassembler module.
+--
+-- Copyright (C) 2005-2026 Mike Pall. All rights reserved.
+-- Released under the MIT license. See Copyright Notice in luajit.h
+----------------------------------------------------------------------------
+-- This is a helper module used by the LuaJIT machine code dumper module.
+--
+-- Sending small code snippets to an external disassembler and mixing the
+-- output with our own stuff was too fragile. So I had to bite the bullet
+-- and write yet another x86 disassembler. Oh well ...
+--
+-- The output format is very similar to what ndisasm generates. But it has
+-- been developed independently by looking at the opcode tables from the
+-- Intel and AMD manuals. The supported instruction set is quite extensive
+-- and reflects what a current generation Intel or AMD CPU implements in
+-- 32 bit and 64 bit mode. Yes, this includes MMX, SSE, SSE2, SSE3, SSSE3,
+-- SSE4.1, SSE4.2, SSE4a, AVX, AVX2 and even privileged and hypervisor
+-- (VMX/SVM) instructions.
+--
+-- Notes:
+-- * The (useless) a16 prefix, 3DNow and pre-586 opcodes are unsupported.
+-- * No attempt at optimization has been made -- it's fast enough for my needs.
+------------------------------------------------------------------------------
+
+local type = type
+local sub, byte, format = string.sub, string.byte, string.format
+local match, gmatch, gsub = string.match, string.gmatch, string.gsub
+local lower, rep = string.lower, string.rep
+local bit = require("bit")
+local tohex = bit.tohex
+
+-- Map for 1st opcode byte in 32 bit mode. Ugly? Well ... read on.
+local map_opc1_32 = {
+--0x
+[0]="addBmr","addVmr","addBrm","addVrm","addBai","addVai","push es","pop es",
+"orBmr","orVmr","orBrm","orVrm","orBai","orVai","push cs","opc2*",
+--1x
+"adcBmr","adcVmr","adcBrm","adcVrm","adcBai","adcVai","push ss","pop ss",
+"sbbBmr","sbbVmr","sbbBrm","sbbVrm","sbbBai","sbbVai","push ds","pop ds",
+--2x
+"andBmr","andVmr","andBrm","andVrm","andBai","andVai","es:seg","daa",
+"subBmr","subVmr","subBrm","subVrm","subBai","subVai","cs:seg","das",
+--3x
+"xorBmr","xorVmr","xorBrm","xorVrm","xorBai","xorVai","ss:seg","aaa",
+"cmpBmr","cmpVmr","cmpBrm","cmpVrm","cmpBai","cmpVai","ds:seg","aas",
+--4x
+"incVR","incVR","incVR","incVR","incVR","incVR","incVR","incVR",
+"decVR","decVR","decVR","decVR","decVR","decVR","decVR","decVR",
+--5x
+"pushUR","pushUR","pushUR","pushUR","pushUR","pushUR","pushUR","pushUR",
+"popUR","popUR","popUR","popUR","popUR","popUR","popUR","popUR",
+--6x
+"sz*pushaw,pusha","sz*popaw,popa","boundVrm","arplWmr",
+"fs:seg","gs:seg","o16:","a16",
+"pushUi","imulVrmi","pushBs","imulVrms",
+"insb","insVS","outsb","outsVS",
+--7x
+"joBj","jnoBj","jbBj","jnbBj","jzBj","jnzBj","jbeBj","jaBj",
+"jsBj","jnsBj","jpeBj","jpoBj","jlBj","jgeBj","jleBj","jgBj",
+--8x
+"arith!Bmi","arith!Vmi","arith!Bmi","arith!Vms",
+"testBmr","testVmr","xchgBrm","xchgVrm",
+"movBmr","movVmr","movBrm","movVrm",
+"movVmg","leaVrm","movWgm","popUm",
+--9x
+"nop*xchgVaR|pause|xchgWaR|repne nop","xchgVaR","xchgVaR","xchgVaR",
+"xchgVaR","xchgVaR","xchgVaR","xchgVaR",
+"sz*cbw,cwde,cdqe","sz*cwd,cdq,cqo","call farViw","wait",
+"sz*pushfw,pushf","sz*popfw,popf","sahf","lahf",
+--Ax
+"movBao","movVao","movBoa","movVoa",
+"movsb","movsVS","cmpsb","cmpsVS",
+"testBai","testVai","stosb","stosVS",
+"lodsb","lodsVS","scasb","scasVS",
+--Bx
+"movBRi","movBRi","movBRi","movBRi","movBRi","movBRi","movBRi","movBRi",
+"movVRI","movVRI","movVRI","movVRI","movVRI","movVRI","movVRI","movVRI",
+--Cx
+"shift!Bmu","shift!Vmu","retBw","ret","vex*3$lesVrm","vex*2$ldsVrm","movBmi","movVmi",
+"enterBwu","leave","retfBw","retf","int3","intBu","into","iretVS",
+--Dx
+"shift!Bm1","shift!Vm1","shift!Bmc","shift!Vmc","aamBu","aadBu","salc","xlatb",
+"fp*0","fp*1","fp*2","fp*3","fp*4","fp*5","fp*6","fp*7",
+--Ex
+"loopneBj","loopeBj","loopBj","sz*jcxzBj,jecxzBj,jrcxzBj",
+"inBau","inVau","outBua","outVua",
+"callVj","jmpVj","jmp farViw","jmpBj","inBad","inVad","outBda","outVda",
+--Fx
+"lock:","int1","repne:rep","rep:","hlt","cmc","testb!Bm","testv!Vm",
+"clc","stc","cli","sti","cld","std","incb!Bm","incd!Vm",
+}
+assert(#map_opc1_32 == 255)
+
+-- Map for 1st opcode byte in 64 bit mode (overrides only).
+local map_opc1_64 = setmetatable({
+ [0x06]=false, [0x07]=false, [0x0e]=false,
+ [0x16]=false, [0x17]=false, [0x1e]=false, [0x1f]=false,
+ [0x27]=false, [0x2f]=false, [0x37]=false, [0x3f]=false,
+ [0x60]=false, [0x61]=false, [0x62]=false, [0x63]="movsxdVrDmt", [0x67]="a32:",
+ [0x40]="rex*", [0x41]="rex*b", [0x42]="rex*x", [0x43]="rex*xb",
+ [0x44]="rex*r", [0x45]="rex*rb", [0x46]="rex*rx", [0x47]="rex*rxb",
+ [0x48]="rex*w", [0x49]="rex*wb", [0x4a]="rex*wx", [0x4b]="rex*wxb",
+ [0x4c]="rex*wr", [0x4d]="rex*wrb", [0x4e]="rex*wrx", [0x4f]="rex*wrxb",
+ [0x82]=false, [0x9a]=false, [0xc4]="vex*3", [0xc5]="vex*2", [0xce]=false,
+ [0xd4]=false, [0xd5]=false, [0xd6]=false, [0xea]=false,
+}, { __index = map_opc1_32 })
+
+-- Map for 2nd opcode byte (0F xx). True CISC hell. Hey, I told you.
+-- Prefix dependent MMX/SSE opcodes: (none)|rep|o16|repne, -|F3|66|F2
+local map_opc2 = {
+--0x
+[0]="sldt!Dmp","sgdt!Ump","larVrm","lslVrm",nil,"syscall","clts","sysret",
+"invd","wbinvd",nil,"ud1",nil,"$prefetch!Bm","femms","3dnowMrmu",
+--1x
+"movupsXrm|movssXrvm|movupdXrm|movsdXrvm",
+"movupsXmr|movssXmvr|movupdXmr|movsdXmvr",
+"movhlpsXrm$movlpsXrm|movsldupXrm|movlpdXrm|movddupXrm",
+"movlpsXmr||movlpdXmr",
+"unpcklpsXrvm||unpcklpdXrvm",
+"unpckhpsXrvm||unpckhpdXrvm",
+"movlhpsXrm$movhpsXrm|movshdupXrm|movhpdXrm",
+"movhpsXmr||movhpdXmr",
+"$prefetcht!Bm","hintnopVm","hintnopVm","hintnopVm",
+"hintnopVm","hintnopVm","endbr*hintnopVm","hintnopVm",
+--2x
+"movUmx$","movUmy$","movUxm$","movUym$","movUmz$",nil,"movUzm$",nil,
+"movapsXrm||movapdXrm",
+"movapsXmr||movapdXmr",
+"cvtpi2psXrMm|cvtsi2ssXrvVmt|cvtpi2pdXrMm|cvtsi2sdXrvVmt",
+"movntpsXmr|movntssXmr|movntpdXmr|movntsdXmr",
+"cvttps2piMrXm|cvttss2siVrXm|cvttpd2piMrXm|cvttsd2siVrXm",
+"cvtps2piMrXm|cvtss2siVrXm|cvtpd2piMrXm|cvtsd2siVrXm",
+"ucomissXrm||ucomisdXrm",
+"comissXrm||comisdXrm",
+--3x
+"wrmsr","rdtsc","rdmsr","rdpmc","sysenter","sysexit",nil,"getsec",
+"opc3*38",nil,"opc3*3a",nil,nil,nil,nil,nil,
+--4x
+"cmovoVrm","cmovnoVrm","cmovbVrm","cmovnbVrm",
+"cmovzVrm","cmovnzVrm","cmovbeVrm","cmovaVrm",
+"cmovsVrm","cmovnsVrm","cmovpeVrm","cmovpoVrm",
+"cmovlVrm","cmovgeVrm","cmovleVrm","cmovgVrm",
+--5x
+"movmskpsVrXm$||movmskpdVrXm$","sqrtpsXrm|sqrtssXrm|sqrtpdXrm|sqrtsdXrm",
+"rsqrtpsXrm|rsqrtssXrvm","rcppsXrm|rcpssXrvm",
+"andpsXrvm||andpdXrvm","andnpsXrvm||andnpdXrvm",
+"orpsXrvm||orpdXrvm","xorpsXrvm||xorpdXrvm",
+"addpsXrvm|addssXrvm|addpdXrvm|addsdXrvm","mulpsXrvm|mulssXrvm|mulpdXrvm|mulsdXrvm",
+"cvtps2pdXrm|cvtss2sdXrvm|cvtpd2psXrm|cvtsd2ssXrvm",
+"cvtdq2psXrm|cvttps2dqXrm|cvtps2dqXrm",
+"subpsXrvm|subssXrvm|subpdXrvm|subsdXrvm","minpsXrvm|minssXrvm|minpdXrvm|minsdXrvm",
+"divpsXrvm|divssXrvm|divpdXrvm|divsdXrvm","maxpsXrvm|maxssXrvm|maxpdXrvm|maxsdXrvm",
+--6x
+"punpcklbwPrvm","punpcklwdPrvm","punpckldqPrvm","packsswbPrvm",
+"pcmpgtbPrvm","pcmpgtwPrvm","pcmpgtdPrvm","packuswbPrvm",
+"punpckhbwPrvm","punpckhwdPrvm","punpckhdqPrvm","packssdwPrvm",
+"||punpcklqdqXrvm","||punpckhqdqXrvm",
+"movPrVSm","movqMrm|movdquXrm|movdqaXrm",
+--7x
+"pshufwMrmu|pshufhwXrmu|pshufdXrmu|pshuflwXrmu","pshiftw!Pvmu",
+"pshiftd!Pvmu","pshiftq!Mvmu||pshiftdq!Xvmu",
+"pcmpeqbPrvm","pcmpeqwPrvm","pcmpeqdPrvm","emms*|",
+"vmreadUmr||extrqXmuu$|insertqXrmuu$","vmwriteUrm||extrqXrm$|insertqXrm$",
+nil,nil,
+"||haddpdXrvm|haddpsXrvm","||hsubpdXrvm|hsubpsXrvm",
+"movVSmMr|movqXrm|movVSmXr","movqMmr|movdquXmr|movdqaXmr",
+--8x
+"joVj","jnoVj","jbVj","jnbVj","jzVj","jnzVj","jbeVj","jaVj",
+"jsVj","jnsVj","jpeVj","jpoVj","jlVj","jgeVj","jleVj","jgVj",
+--9x
+"setoBm","setnoBm","setbBm","setnbBm","setzBm","setnzBm","setbeBm","setaBm",
+"setsBm","setnsBm","setpeBm","setpoBm","setlBm","setgeBm","setleBm","setgBm",
+--Ax
+"push fs","pop fs","cpuid","btVmr","shldVmru","shldVmrc",nil,nil,
+"push gs","pop gs","rsm","btsVmr","shrdVmru","shrdVmrc","fxsave!Dmp","imulVrm",
+--Bx
+"cmpxchgBmr","cmpxchgVmr","$lssVrm","btrVmr",
+"$lfsVrm","$lgsVrm","movzxVrBmt","movzxVrWmt",
+"|popcntVrm","ud2Dp","bt!Vmu","btcVmr",
+"bsfVrm","bsrVrm|lzcntVrm|bsrWrm","movsxVrBmt","movsxVrWmt",
+--Cx
+"xaddBmr","xaddVmr",
+"cmppsXrvmu|cmpssXrvmu|cmppdXrvmu|cmpsdXrvmu","$movntiVmr|",
+"pinsrwPrvWmu","pextrwDrPmu",
+"shufpsXrvmu||shufpdXrvmu","$cmpxchg!Qmp",
+"bswapVR","bswapVR","bswapVR","bswapVR","bswapVR","bswapVR","bswapVR","bswapVR",
+--Dx
+"||addsubpdXrvm|addsubpsXrvm","psrlwPrvm","psrldPrvm","psrlqPrvm",
+"paddqPrvm","pmullwPrvm",
+"|movq2dqXrMm|movqXmr|movdq2qMrXm$","pmovmskbVrMm||pmovmskbVrXm",
+"psubusbPrvm","psubuswPrvm","pminubPrvm","pandPrvm",
+"paddusbPrvm","padduswPrvm","pmaxubPrvm","pandnPrvm",
+--Ex
+"pavgbPrvm","psrawPrvm","psradPrvm","pavgwPrvm",
+"pmulhuwPrvm","pmulhwPrvm",
+"|cvtdq2pdXrm|cvttpd2dqXrm|cvtpd2dqXrm","$movntqMmr||$movntdqXmr",
+"psubsbPrvm","psubswPrvm","pminswPrvm","porPrvm",
+"paddsbPrvm","paddswPrvm","pmaxswPrvm","pxorPrvm",
+--Fx
+"|||lddquXrm","psllwPrvm","pslldPrvm","psllqPrvm",
+"pmuludqPrvm","pmaddwdPrvm","psadbwPrvm","maskmovqMrm||maskmovdquXrm$",
+"psubbPrvm","psubwPrvm","psubdPrvm","psubqPrvm",
+"paddbPrvm","paddwPrvm","padddPrvm","ud",
+}
+assert(map_opc2[255] == "ud")
+
+-- Map for three-byte opcodes. Can't wait for their next invention.
+local map_opc3 = {
+["38"] = { -- [66] 0f 38 xx
+--0x
+[0]="pshufbPrvm","phaddwPrvm","phadddPrvm","phaddswPrvm",
+"pmaddubswPrvm","phsubwPrvm","phsubdPrvm","phsubswPrvm",
+"psignbPrvm","psignwPrvm","psigndPrvm","pmulhrswPrvm",
+"||permilpsXrvm","||permilpdXrvm",nil,nil,
+--1x
+"||pblendvbXrma",nil,nil,nil,
+"||blendvpsXrma","||blendvpdXrma","||permpsXrvm","||ptestXrm",
+"||broadcastssXrm","||broadcastsdXrm","||broadcastf128XrlXm",nil,
+"pabsbPrm","pabswPrm","pabsdPrm",nil,
+--2x
+"||pmovsxbwXrm","||pmovsxbdXrm","||pmovsxbqXrm","||pmovsxwdXrm",
+"||pmovsxwqXrm","||pmovsxdqXrm",nil,nil,
+"||pmuldqXrvm","||pcmpeqqXrvm","||$movntdqaXrm","||packusdwXrvm",
+"||maskmovpsXrvm","||maskmovpdXrvm","||maskmovpsXmvr","||maskmovpdXmvr",
+--3x
+"||pmovzxbwXrm","||pmovzxbdXrm","||pmovzxbqXrm","||pmovzxwdXrm",
+"||pmovzxwqXrm","||pmovzxdqXrm","||permdXrvm","||pcmpgtqXrvm",
+"||pminsbXrvm","||pminsdXrvm","||pminuwXrvm","||pminudXrvm",
+"||pmaxsbXrvm","||pmaxsdXrvm","||pmaxuwXrvm","||pmaxudXrvm",
+--4x
+"||pmulddXrvm","||phminposuwXrm",nil,nil,
+nil,"||psrlvVSXrvm","||psravdXrvm","||psllvVSXrvm",
+--5x
+[0x58] = "||pbroadcastdXrlXm",[0x59] = "||pbroadcastqXrlXm",
+[0x5a] = "||broadcasti128XrlXm",
+--7x
+[0x78] = "||pbroadcastbXrlXm",[0x79] = "||pbroadcastwXrlXm",
+--8x
+[0x8c] = "||pmaskmovXrvVSm",
+[0x8e] = "||pmaskmovVSmXvr",
+--9x
+[0x96] = "||fmaddsub132pHXrvm",[0x97] = "||fmsubadd132pHXrvm",
+[0x98] = "||fmadd132pHXrvm",[0x99] = "||fmadd132sHXrvm",
+[0x9a] = "||fmsub132pHXrvm",[0x9b] = "||fmsub132sHXrvm",
+[0x9c] = "||fnmadd132pHXrvm",[0x9d] = "||fnmadd132sHXrvm",
+[0x9e] = "||fnmsub132pHXrvm",[0x9f] = "||fnmsub132sHXrvm",
+--Ax
+[0xa6] = "||fmaddsub213pHXrvm",[0xa7] = "||fmsubadd213pHXrvm",
+[0xa8] = "||fmadd213pHXrvm",[0xa9] = "||fmadd213sHXrvm",
+[0xaa] = "||fmsub213pHXrvm",[0xab] = "||fmsub213sHXrvm",
+[0xac] = "||fnmadd213pHXrvm",[0xad] = "||fnmadd213sHXrvm",
+[0xae] = "||fnmsub213pHXrvm",[0xaf] = "||fnmsub213sHXrvm",
+--Bx
+[0xb6] = "||fmaddsub231pHXrvm",[0xb7] = "||fmsubadd231pHXrvm",
+[0xb8] = "||fmadd231pHXrvm",[0xb9] = "||fmadd231sHXrvm",
+[0xba] = "||fmsub231pHXrvm",[0xbb] = "||fmsub231sHXrvm",
+[0xbc] = "||fnmadd231pHXrvm",[0xbd] = "||fnmadd231sHXrvm",
+[0xbe] = "||fnmsub231pHXrvm",[0xbf] = "||fnmsub231sHXrvm",
+--Dx
+[0xdc] = "||aesencXrvm", [0xdd] = "||aesenclastXrvm",
+[0xde] = "||aesdecXrvm", [0xdf] = "||aesdeclastXrvm",
+--Fx
+[0xf0] = "|||crc32TrBmt",[0xf1] = "|||crc32TrVmt",
+[0xf7] = "| sarxVrmv| shlxVrmv| shrxVrmv",
+},
+
+["3a"] = { -- [66] 0f 3a xx
+--0x
+[0x00]="||permqXrmu","||permpdXrmu","||pblenddXrvmu",nil,
+"||permilpsXrmu","||permilpdXrmu","||perm2f128Xrvmu",nil,
+"||roundpsXrmu","||roundpdXrmu","||roundssXrvmu","||roundsdXrvmu",
+"||blendpsXrvmu","||blendpdXrvmu","||pblendwXrvmu","palignrPrvmu",
+--1x
+nil,nil,nil,nil,
+"||pextrbVmXru","||pextrwVmXru","||pextrVmSXru","||extractpsVmXru",
+"||insertf128XrvlXmu","||extractf128XlXmYru",nil,nil,
+nil,nil,nil,nil,
+--2x
+"||pinsrbXrvVmu","||insertpsXrvmu","||pinsrXrvVmuS",nil,
+--3x
+[0x38] = "||inserti128Xrvmu",[0x39] = "||extracti128XlXmYru",
+--4x
+[0x40] = "||dppsXrvmu",
+[0x41] = "||dppdXrvmu",
+[0x42] = "||mpsadbwXrvmu",
+[0x44] = "||pclmulqdqXrvmu",
+[0x46] = "||perm2i128Xrvmu",
+[0x4a] = "||blendvpsXrvmb",[0x4b] = "||blendvpdXrvmb",
+[0x4c] = "||pblendvbXrvmb",
+--6x
+[0x60] = "||pcmpestrmXrmu",[0x61] = "||pcmpestriXrmu",
+[0x62] = "||pcmpistrmXrmu",[0x63] = "||pcmpistriXrmu",
+[0xdf] = "||aeskeygenassistXrmu",
+--Fx
+[0xf0] = "||| rorxVrmu",
+},
+}
+
+-- Map for VMX/SVM opcodes 0F 01 C0-FF (sgdt group with register operands).
+local map_opcvm = {
+[0xc1]="vmcall",[0xc2]="vmlaunch",[0xc3]="vmresume",[0xc4]="vmxoff",
+[0xc8]="monitor",[0xc9]="mwait",
+[0xd8]="vmrun",[0xd9]="vmmcall",[0xda]="vmload",[0xdb]="vmsave",
+[0xdc]="stgi",[0xdd]="clgi",[0xde]="skinit",[0xdf]="invlpga",
+[0xf8]="swapgs",[0xf9]="rdtscp",
+}
+
+-- Map for FP opcodes. And you thought stack machines are simple?
+local map_opcfp = {
+-- D8-DF 00-BF: opcodes with a memory operand.
+-- D8
+[0]="faddFm","fmulFm","fcomFm","fcompFm","fsubFm","fsubrFm","fdivFm","fdivrFm",
+"fldFm",nil,"fstFm","fstpFm","fldenvVm","fldcwWm","fnstenvVm","fnstcwWm",
+-- DA
+"fiaddDm","fimulDm","ficomDm","ficompDm",
+"fisubDm","fisubrDm","fidivDm","fidivrDm",
+-- DB
+"fildDm","fisttpDm","fistDm","fistpDm",nil,"fld twordFmp",nil,"fstp twordFmp",
+-- DC
+"faddGm","fmulGm","fcomGm","fcompGm","fsubGm","fsubrGm","fdivGm","fdivrGm",
+-- DD
+"fldGm","fisttpQm","fstGm","fstpGm","frstorDmp",nil,"fnsaveDmp","fnstswWm",
+-- DE
+"fiaddWm","fimulWm","ficomWm","ficompWm",
+"fisubWm","fisubrWm","fidivWm","fidivrWm",
+-- DF
+"fildWm","fisttpWm","fistWm","fistpWm",
+"fbld twordFmp","fildQm","fbstp twordFmp","fistpQm",
+-- xx C0-FF: opcodes with a pseudo-register operand.
+-- D8
+"faddFf","fmulFf","fcomFf","fcompFf","fsubFf","fsubrFf","fdivFf","fdivrFf",
+-- D9
+"fldFf","fxchFf",{"fnop"},nil,
+{"fchs","fabs",nil,nil,"ftst","fxam"},
+{"fld1","fldl2t","fldl2e","fldpi","fldlg2","fldln2","fldz"},
+{"f2xm1","fyl2x","fptan","fpatan","fxtract","fprem1","fdecstp","fincstp"},
+{"fprem","fyl2xp1","fsqrt","fsincos","frndint","fscale","fsin","fcos"},
+-- DA
+"fcmovbFf","fcmoveFf","fcmovbeFf","fcmovuFf",nil,{nil,"fucompp"},nil,nil,
+-- DB
+"fcmovnbFf","fcmovneFf","fcmovnbeFf","fcmovnuFf",
+{nil,nil,"fnclex","fninit"},"fucomiFf","fcomiFf",nil,
+-- DC
+"fadd toFf","fmul toFf",nil,nil,
+"fsub toFf","fsubr toFf","fdivr toFf","fdiv toFf",
+-- DD
+"ffreeFf",nil,"fstFf","fstpFf","fucomFf","fucompFf",nil,nil,
+-- DE
+"faddpFf","fmulpFf",nil,{nil,"fcompp"},
+"fsubrpFf","fsubpFf","fdivrpFf","fdivpFf",
+-- DF
+nil,nil,nil,nil,{"fnstsw ax"},"fucomipFf","fcomipFf",nil,
+}
+assert(map_opcfp[126] == "fcomipFf")
+
+-- Map for opcode groups. The subkey is sp from the ModRM byte.
+local map_opcgroup = {
+ arith = { "add", "or", "adc", "sbb", "and", "sub", "xor", "cmp" },
+ shift = { "rol", "ror", "rcl", "rcr", "shl", "shr", "sal", "sar" },
+ testb = { "testBmi", "testBmi", "not", "neg", "mul", "imul", "div", "idiv" },
+ testv = { "testVmi", "testVmi", "not", "neg", "mul", "imul", "div", "idiv" },
+ incb = { "inc", "dec" },
+ incd = { "inc", "dec", "callUmp", "$call farDmp",
+ "jmpUmp", "$jmp farDmp", "pushUm" },
+ sldt = { "sldt", "str", "lldt", "ltr", "verr", "verw" },
+ sgdt = { "vm*$sgdt", "vm*$sidt", "$lgdt", "vm*$lidt",
+ "smsw", nil, "lmsw", "vm*$invlpg" },
+ bt = { nil, nil, nil, nil, "bt", "bts", "btr", "btc" },
+ cmpxchg = { nil, "sz*,cmpxchg8bQmp,cmpxchg16bXmp", nil, nil,
+ nil, nil, "vmptrld|vmxon|vmclear", "vmptrst" },
+ pshiftw = { nil, nil, "psrlw", nil, "psraw", nil, "psllw" },
+ pshiftd = { nil, nil, "psrld", nil, "psrad", nil, "pslld" },
+ pshiftq = { nil, nil, "psrlq", nil, nil, nil, "psllq" },
+ pshiftdq = { nil, nil, "psrlq", "psrldq", nil, nil, "psllq", "pslldq" },
+ fxsave = { "$fxsave", "$fxrstor", "$ldmxcsr", "$stmxcsr",
+ nil, "lfenceDp$", "mfenceDp$", "sfenceDp$clflush" },
+ prefetch = { "prefetch", "prefetchw" },
+ prefetcht = { "prefetchnta", "prefetcht0", "prefetcht1", "prefetcht2" },
+}
+
+------------------------------------------------------------------------------
+
+-- Maps for register names.
+local map_regs = {
+ B = { "al", "cl", "dl", "bl", "ah", "ch", "dh", "bh",
+ "r8b", "r9b", "r10b", "r11b", "r12b", "r13b", "r14b", "r15b" },
+ B64 = { "al", "cl", "dl", "bl", "spl", "bpl", "sil", "dil",
+ "r8b", "r9b", "r10b", "r11b", "r12b", "r13b", "r14b", "r15b" },
+ W = { "ax", "cx", "dx", "bx", "sp", "bp", "si", "di",
+ "r8w", "r9w", "r10w", "r11w", "r12w", "r13w", "r14w", "r15w" },
+ D = { "eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi",
+ "r8d", "r9d", "r10d", "r11d", "r12d", "r13d", "r14d", "r15d" },
+ Q = { "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi",
+ "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15" },
+ M = { "mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7",
+ "mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7" }, -- No x64 ext!
+ X = { "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7",
+ "xmm8", "xmm9", "xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15" },
+ Y = { "ymm0", "ymm1", "ymm2", "ymm3", "ymm4", "ymm5", "ymm6", "ymm7",
+ "ymm8", "ymm9", "ymm10", "ymm11", "ymm12", "ymm13", "ymm14", "ymm15" },
+}
+local map_segregs = { "es", "cs", "ss", "ds", "fs", "gs", "segr6", "segr7" }
+
+-- Maps for size names.
+local map_sz2n = {
+ B = 1, W = 2, D = 4, Q = 8, M = 8, X = 16, Y = 32,
+}
+local map_sz2prefix = {
+ B = "byte", W = "word", D = "dword",
+ Q = "qword",
+ M = "qword", X = "xword", Y = "yword",
+ F = "dword", G = "qword", -- No need for sizes/register names for these two.
+}
+
+------------------------------------------------------------------------------
+
+-- Output a nicely formatted line with an opcode and operands.
+local function putop(ctx, text, operands)
+ local code, pos, hex = ctx.code, ctx.pos, ""
+ local hmax = ctx.hexdump
+ if hmax > 0 then
+ for i=ctx.start,pos-1 do
+ hex = hex..format("%02X", byte(code, i, i))
+ end
+ if #hex > hmax then hex = sub(hex, 1, hmax)..". "
+ else hex = hex..rep(" ", hmax-#hex+2) end
+ end
+ if operands then text = text.." "..operands end
+ if ctx.o16 then text = "o16 "..text; ctx.o16 = false end
+ if ctx.a32 then text = "a32 "..text; ctx.a32 = false end
+ if ctx.rep then text = ctx.rep.." "..text; ctx.rep = false end
+ if ctx.rex then
+ local t = (ctx.rexw and "w" or "")..(ctx.rexr and "r" or "")..
+ (ctx.rexx and "x" or "")..(ctx.rexb and "b" or "")..
+ (ctx.vexl and "l" or "")
+ if ctx.vexv and ctx.vexv ~= 0 then t = t.."v"..ctx.vexv end
+ if t ~= "" then text = ctx.rex.."."..t.." "..gsub(text, "^ ", "")
+ elseif ctx.rex == "vex" then text = gsub("v"..text, "^v ", "") end
+ ctx.rexw = false; ctx.rexr = false; ctx.rexx = false; ctx.rexb = false
+ ctx.rex = false; ctx.vexl = false; ctx.vexv = false
+ end
+ if ctx.seg then
+ local text2, n = gsub(text, "%[", "["..ctx.seg..":")
+ if n == 0 then text = ctx.seg.." "..text else text = text2 end
+ ctx.seg = false
+ end
+ if ctx.lock then text = "lock "..text; ctx.lock = false end
+ local imm = ctx.imm
+ if imm then
+ local sym = ctx.symtab[imm]
+ if sym then text = text.."\t->"..sym end
+ end
+ ctx.out(format("%08x %s%s\n", ctx.addr+ctx.start, hex, text))
+ ctx.mrm = false
+ ctx.vexv = false
+ ctx.start = pos
+ ctx.imm = nil
+end
+
+-- Clear all prefix flags.
+local function clearprefixes(ctx)
+ ctx.o16 = false; ctx.seg = false; ctx.lock = false; ctx.rep = false
+ ctx.rexw = false; ctx.rexr = false; ctx.rexx = false; ctx.rexb = false
+ ctx.rex = false; ctx.a32 = false; ctx.vexl = false
+end
+
+-- Fallback for incomplete opcodes at the end.
+local function incomplete(ctx)
+ ctx.pos = ctx.stop+1
+ clearprefixes(ctx)
+ return putop(ctx, "(incomplete)")
+end
+
+-- Fallback for unknown opcodes.
+local function unknown(ctx)
+ clearprefixes(ctx)
+ return putop(ctx, "(unknown)")
+end
+
+-- Return an immediate of the specified size.
+local function getimm(ctx, pos, n)
+ if pos+n-1 > ctx.stop then return incomplete(ctx) end
+ local code = ctx.code
+ if n == 1 then
+ local b1 = byte(code, pos, pos)
+ return b1
+ elseif n == 2 then
+ local b1, b2 = byte(code, pos, pos+1)
+ return b1+b2*256
+ else
+ local b1, b2, b3, b4 = byte(code, pos, pos+3)
+ local imm = b1+b2*256+b3*65536+b4*16777216
+ ctx.imm = imm
+ return imm
+ end
+end
+
+-- Process pattern string and generate the operands.
+local function putpat(ctx, name, pat)
+ local operands, regs, sz, mode, sp, rm, sc, rx, sdisp
+ local code, pos, stop, vexl = ctx.code, ctx.pos, ctx.stop, ctx.vexl
+
+ -- Chars used: 1DFGHIMPQRSTUVWXYabcdfgijlmoprstuvwxyz
+ for p in gmatch(pat, ".") do
+ local x = nil
+ if p == "V" or p == "U" then
+ if ctx.rexw then sz = "Q"; ctx.rexw = false
+ elseif ctx.o16 then sz = "W"; ctx.o16 = false
+ elseif p == "U" and ctx.x64 then sz = "Q"
+ else sz = "D" end
+ regs = map_regs[sz]
+ elseif p == "T" then
+ if ctx.rexw then sz = "Q"; ctx.rexw = false else sz = "D" end
+ regs = map_regs[sz]
+ elseif p == "B" then
+ sz = "B"
+ regs = ctx.rex and map_regs.B64 or map_regs.B
+ elseif match(p, "[WDQMXYFG]") then
+ sz = p
+ if sz == "X" and vexl then sz = "Y"; ctx.vexl = false end
+ regs = map_regs[sz]
+ elseif p == "P" then
+ sz = ctx.o16 and "X" or "M"; ctx.o16 = false
+ if sz == "X" and vexl then sz = "Y"; ctx.vexl = false end
+ regs = map_regs[sz]
+ elseif p == "H" then
+ name = name..(ctx.rexw and "d" or "s")
+ ctx.rexw = false
+ elseif p == "S" then
+ name = name..lower(sz)
+ elseif p == "s" then
+ local imm = getimm(ctx, pos, 1); if not imm then return end
+ x = imm <= 127 and format("+0x%02x", imm)
+ or format("-0x%02x", 256-imm)
+ pos = pos+1
+ elseif p == "u" then
+ local imm = getimm(ctx, pos, 1); if not imm then return end
+ x = format("0x%02x", imm)
+ pos = pos+1
+ elseif p == "b" then
+ local imm = getimm(ctx, pos, 1); if not imm then return end
+ x = regs[imm/16+1]
+ pos = pos+1
+ elseif p == "w" then
+ local imm = getimm(ctx, pos, 2); if not imm then return end
+ x = format("0x%x", imm)
+ pos = pos+2
+ elseif p == "o" then -- [offset]
+ if ctx.x64 then
+ local imm1 = getimm(ctx, pos, 4); if not imm1 then return end
+ local imm2 = getimm(ctx, pos+4, 4); if not imm2 then return end
+ x = format("[0x%08x%08x]", imm2, imm1)
+ pos = pos+8
+ else
+ local imm = getimm(ctx, pos, 4); if not imm then return end
+ x = format("[0x%08x]", imm)
+ pos = pos+4
+ end
+ elseif p == "i" or p == "I" then
+ local n = map_sz2n[sz]
+ if n == 8 and ctx.x64 and p == "I" then
+ local imm1 = getimm(ctx, pos, 4); if not imm1 then return end
+ local imm2 = getimm(ctx, pos+4, 4); if not imm2 then return end
+ x = format("0x%08x%08x", imm2, imm1)
+ else
+ if n == 8 then n = 4 end
+ local imm = getimm(ctx, pos, n); if not imm then return end
+ if sz == "Q" and (imm < 0 or imm > 0x7fffffff) then
+ imm = (0xffffffff+1)-imm
+ x = format(imm > 65535 and "-0x%08x" or "-0x%x", imm)
+ else
+ x = format(imm > 65535 and "0x%08x" or "0x%x", imm)
+ end
+ end
+ pos = pos+n
+ elseif p == "j" then
+ local n = map_sz2n[sz]
+ if n == 8 then n = 4 end
+ local imm = getimm(ctx, pos, n); if not imm then return end
+ if sz == "B" and imm > 127 then imm = imm-256
+ elseif imm > 2147483647 then imm = imm-4294967296 end
+ pos = pos+n
+ imm = imm + pos + ctx.addr
+ if imm > 4294967295 and not ctx.x64 then imm = imm-4294967296 end
+ ctx.imm = imm
+ if sz == "W" then
+ x = format("word 0x%04x", imm%65536)
+ elseif ctx.x64 then
+ local lo = imm % 0x1000000
+ x = format("0x%02x%06x", (imm-lo) / 0x1000000, lo)
+ else
+ x = "0x"..tohex(imm)
+ end
+ elseif p == "R" then
+ local r = byte(code, pos-1, pos-1)%8
+ if ctx.rexb then r = r + 8; ctx.rexb = false end
+ x = regs[r+1]
+ elseif p == "a" then x = regs[1]
+ elseif p == "c" then x = "cl"
+ elseif p == "d" then x = "dx"
+ elseif p == "1" then x = "1"
+ else
+ if not mode then
+ mode = ctx.mrm
+ if not mode then
+ if pos > stop then return incomplete(ctx) end
+ mode = byte(code, pos, pos)
+ pos = pos+1
+ end
+ rm = mode%8; mode = (mode-rm)/8
+ sp = mode%8; mode = (mode-sp)/8
+ sdisp = ""
+ if mode < 3 then
+ if rm == 4 then
+ if pos > stop then return incomplete(ctx) end
+ sc = byte(code, pos, pos)
+ pos = pos+1
+ rm = sc%8; sc = (sc-rm)/8
+ rx = sc%8; sc = (sc-rx)/8
+ if ctx.rexx then rx = rx + 8; ctx.rexx = false end
+ if rx == 4 then rx = nil end
+ end
+ if mode > 0 or rm == 5 then
+ local dsz = mode
+ if dsz ~= 1 then dsz = 4 end
+ local disp = getimm(ctx, pos, dsz); if not disp then return end
+ if mode == 0 then rm = nil end
+ if rm or rx or (not sc and ctx.x64 and not ctx.a32) then
+ if dsz == 1 and disp > 127 then
+ sdisp = format("-0x%x", 256-disp)
+ elseif disp >= 0 and disp <= 0x7fffffff then
+ sdisp = format("+0x%x", disp)
+ else
+ sdisp = format("-0x%x", (0xffffffff+1)-disp)
+ end
+ else
+ sdisp = format(ctx.x64 and not ctx.a32 and
+ not (disp >= 0 and disp <= 0x7fffffff)
+ and "0xffffffff%08x" or "0x%08x", disp)
+ end
+ pos = pos+dsz
+ end
+ end
+ if rm and ctx.rexb then rm = rm + 8; ctx.rexb = false end
+ if ctx.rexr then sp = sp + 8; ctx.rexr = false end
+ end
+ if p == "m" then
+ if mode == 3 then x = regs[rm+1]
+ else
+ local aregs = ctx.a32 and map_regs.D or ctx.aregs
+ local srm, srx = "", ""
+ if rm then srm = aregs[rm+1]
+ elseif not sc and ctx.x64 and not ctx.a32 then srm = "rip" end
+ ctx.a32 = false
+ if rx then
+ if rm then srm = srm.."+" end
+ srx = aregs[rx+1]
+ if sc > 0 then srx = srx.."*"..(2^sc) end
+ end
+ x = format("[%s%s%s]", srm, srx, sdisp)
+ end
+ if mode < 3 and
+ (not match(pat, "[aRrgp]") or match(pat, "t")) then -- Yuck.
+ x = map_sz2prefix[sz].." "..x
+ end
+ elseif p == "r" then x = regs[sp+1]
+ elseif p == "g" then x = map_segregs[sp+1]
+ elseif p == "p" then -- Suppress prefix.
+ elseif p == "f" then x = "st"..rm
+ elseif p == "x" then
+ if sp == 0 and ctx.lock and not ctx.x64 then
+ x = "CR8"; ctx.lock = false
+ else
+ x = "CR"..sp
+ end
+ elseif p == "v" then
+ if ctx.vexv then
+ x = regs[ctx.vexv+1]; ctx.vexv = false
+ end
+ elseif p == "y" then x = "DR"..sp
+ elseif p == "z" then x = "TR"..sp
+ elseif p == "l" then vexl = false
+ elseif p == "t" then
+ else
+ error("bad pattern `"..pat.."'")
+ end
+ end
+ if x then operands = operands and operands..", "..x or x end
+ end
+ ctx.pos = pos
+ return putop(ctx, name, operands)
+end
+
+-- Forward declaration.
+local map_act
+
+-- Fetch and cache MRM byte.
+local function getmrm(ctx)
+ local mrm = ctx.mrm
+ if not mrm then
+ local pos = ctx.pos
+ if pos > ctx.stop then return nil end
+ mrm = byte(ctx.code, pos, pos)
+ ctx.pos = pos+1
+ ctx.mrm = mrm
+ end
+ return mrm
+end
+
+-- Dispatch to handler depending on pattern.
+local function dispatch(ctx, opat, patgrp)
+ if not opat then return unknown(ctx) end
+ if match(opat, "%|") then -- MMX/SSE variants depending on prefix.
+ local p
+ if ctx.rep then
+ p = ctx.rep=="rep" and "%|([^%|]*)" or "%|[^%|]*%|[^%|]*%|([^%|]*)"
+ ctx.rep = false
+ elseif ctx.o16 then p = "%|[^%|]*%|([^%|]*)"; ctx.o16 = false
+ else p = "^[^%|]*" end
+ opat = match(opat, p)
+ if not opat then return unknown(ctx) end
+-- ctx.rep = false; ctx.o16 = false
+ --XXX fails for 66 f2 0f 38 f1 06 crc32 eax,WORD PTR [esi]
+ --XXX remove in branches?
+ end
+ if match(opat, "%$") then -- reg$mem variants.
+ local mrm = getmrm(ctx); if not mrm then return incomplete(ctx) end
+ opat = match(opat, mrm >= 192 and "^[^%$]*" or "%$(.*)")
+ if opat == "" then return unknown(ctx) end
+ end
+ if opat == "" then return unknown(ctx) end
+ local name, pat = match(opat, "^([a-z0-9 ]*)(.*)")
+ if pat == "" and patgrp then pat = patgrp end
+ return map_act[sub(pat, 1, 1)](ctx, name, pat)
+end
+
+-- Get a pattern from an opcode map and dispatch to handler.
+local function dispatchmap(ctx, opcmap)
+ local pos = ctx.pos
+ local opat = opcmap[byte(ctx.code, pos, pos)]
+ pos = pos + 1
+ ctx.pos = pos
+ return dispatch(ctx, opat)
+end
+
+-- Map for action codes. The key is the first char after the name.
+map_act = {
+ -- Simple opcodes without operands.
+ [""] = function(ctx, name, pat)
+ return putop(ctx, name)
+ end,
+
+ -- Operand size chars fall right through.
+ B = putpat, W = putpat, D = putpat, Q = putpat,
+ V = putpat, U = putpat, T = putpat,
+ M = putpat, X = putpat, P = putpat,
+ F = putpat, G = putpat, Y = putpat,
+ H = putpat,
+
+ -- Collect prefixes.
+ [":"] = function(ctx, name, pat)
+ ctx[pat == ":" and name or sub(pat, 2)] = name
+ if ctx.pos - ctx.start > 5 then return unknown(ctx) end -- Limit #prefixes.
+ end,
+
+ -- Chain to special handler specified by name.
+ ["*"] = function(ctx, name, pat)
+ return map_act[name](ctx, name, sub(pat, 2))
+ end,
+
+ -- Use named subtable for opcode group.
+ ["!"] = function(ctx, name, pat)
+ local mrm = getmrm(ctx); if not mrm then return incomplete(ctx) end
+ return dispatch(ctx, map_opcgroup[name][((mrm-(mrm%8))/8)%8+1], sub(pat, 2))
+ end,
+
+ -- o16,o32[,o64] variants.
+ sz = function(ctx, name, pat)
+ if ctx.o16 then ctx.o16 = false
+ else
+ pat = match(pat, ",(.*)")
+ if ctx.rexw then
+ local p = match(pat, ",(.*)")
+ if p then pat = p; ctx.rexw = false end
+ end
+ end
+ pat = match(pat, "^[^,]*")
+ return dispatch(ctx, pat)
+ end,
+
+ -- Two-byte opcode dispatch.
+ opc2 = function(ctx, name, pat)
+ return dispatchmap(ctx, map_opc2)
+ end,
+
+ -- Three-byte opcode dispatch.
+ opc3 = function(ctx, name, pat)
+ return dispatchmap(ctx, map_opc3[pat])
+ end,
+
+ -- VMX/SVM dispatch.
+ vm = function(ctx, name, pat)
+ return dispatch(ctx, map_opcvm[ctx.mrm])
+ end,
+
+ -- Special NOP for endbr64/endbr32.
+ endbr = function(ctx, name, pat)
+ if ctx.rep then
+ local pos = ctx.pos
+ local b = byte(ctx.code, pos)
+ local text
+ if b == 0xfa then text = "endbr64"
+ elseif b == 0xfb then text = "endbr64"
+ end
+ if text then
+ ctx.pos = pos + 1
+ ctx.rep = nil
+ return putop(ctx, text)
+ end
+ end
+ return dispatch(ctx, pat)
+ end,
+
+ -- Floating point opcode dispatch.
+ fp = function(ctx, name, pat)
+ local mrm = getmrm(ctx); if not mrm then return incomplete(ctx) end
+ local rm = mrm%8
+ local idx = pat*8 + ((mrm-rm)/8)%8
+ if mrm >= 192 then idx = idx + 64 end
+ local opat = map_opcfp[idx]
+ if type(opat) == "table" then opat = opat[rm+1] end
+ return dispatch(ctx, opat)
+ end,
+
+ -- REX prefix.
+ rex = function(ctx, name, pat)
+ if ctx.rex then return unknown(ctx) end -- Only 1 REX or VEX prefix allowed.
+ for p in gmatch(pat, ".") do ctx["rex"..p] = true end
+ ctx.rex = "rex"
+ end,
+
+ -- VEX prefix.
+ vex = function(ctx, name, pat)
+ if ctx.rex then return unknown(ctx) end -- Only 1 REX or VEX prefix allowed.
+ ctx.rex = "vex"
+ local pos = ctx.pos
+ if ctx.mrm then
+ ctx.mrm = nil
+ pos = pos-1
+ end
+ local b = byte(ctx.code, pos, pos)
+ if not b then return incomplete(ctx) end
+ pos = pos+1
+ if b < 128 then ctx.rexr = true end
+ local m = 1
+ if pat == "3" then
+ m = b%32; b = (b-m)/32
+ local nb = b%2; b = (b-nb)/2
+ if nb == 0 then ctx.rexb = true end
+ local nx = b%2
+ if nx == 0 then ctx.rexx = true end
+ b = byte(ctx.code, pos, pos)
+ if not b then return incomplete(ctx) end
+ pos = pos+1
+ if b >= 128 then ctx.rexw = true end
+ end
+ ctx.pos = pos
+ local map
+ if m == 1 then map = map_opc2
+ elseif m == 2 then map = map_opc3["38"]
+ elseif m == 3 then map = map_opc3["3a"]
+ else return unknown(ctx) end
+ local p = b%4; b = (b-p)/4
+ if p == 1 then ctx.o16 = "o16"
+ elseif p == 2 then ctx.rep = "rep"
+ elseif p == 3 then ctx.rep = "repne" end
+ local l = b%2; b = (b-l)/2
+ if l ~= 0 then ctx.vexl = true end
+ ctx.vexv = (-1-b)%16
+ return dispatchmap(ctx, map)
+ end,
+
+ -- Special case for nop with REX prefix.
+ nop = function(ctx, name, pat)
+ return dispatch(ctx, ctx.rex and pat or "nop")
+ end,
+
+ -- Special case for 0F 77.
+ emms = function(ctx, name, pat)
+ if ctx.rex ~= "vex" then
+ return putop(ctx, "emms")
+ elseif ctx.vexl then
+ ctx.vexl = false
+ return putop(ctx, "zeroall")
+ else
+ return putop(ctx, "zeroupper")
+ end
+ end,
+}
+
+------------------------------------------------------------------------------
+
+-- Disassemble a block of code.
+local function disass_block(ctx, ofs, len)
+ if not ofs then ofs = 0 end
+ local stop = len and ofs+len or #ctx.code
+ ofs = ofs + 1
+ ctx.start = ofs
+ ctx.pos = ofs
+ ctx.stop = stop
+ ctx.imm = nil
+ ctx.mrm = false
+ clearprefixes(ctx)
+ while ctx.pos <= stop do dispatchmap(ctx, ctx.map1) end
+ if ctx.pos ~= ctx.start then incomplete(ctx) end
+end
+
+-- Extended API: create a disassembler context. Then call ctx:disass(ofs, len).
+local function create(code, addr, out)
+ local ctx = {}
+ ctx.code = code
+ ctx.addr = (addr or 0) - 1
+ ctx.out = out or io.write
+ ctx.symtab = {}
+ ctx.disass = disass_block
+ ctx.hexdump = 16
+ ctx.x64 = false
+ ctx.map1 = map_opc1_32
+ ctx.aregs = map_regs.D
+ return ctx
+end
+
+local function create64(code, addr, out)
+ local ctx = create(code, addr, out)
+ ctx.x64 = true
+ ctx.map1 = map_opc1_64
+ ctx.aregs = map_regs.Q
+ return ctx
+end
+
+-- Simple API: disassemble code (a string) at address and output via out.
+local function disass(code, addr, out)
+ create(code, addr, out):disass()
+end
+
+local function disass64(code, addr, out)
+ create64(code, addr, out):disass()
+end
+
+-- Return register name for RID.
+local function regname(r)
+ if r < 8 then return map_regs.D[r+1] end
+ return map_regs.X[r-7]
+end
+
+local function regname64(r)
+ if r < 16 then return map_regs.Q[r+1] end
+ return map_regs.X[r-15]
+end
+
+-- Public module functions.
+return {
+ create = create,
+ create64 = create64,
+ disass = disass,
+ disass64 = disass64,
+ regname = regname,
+ regname64 = regname64
+}
+
diff --git a/jit/dump.lua b/jit/dump.lua
new file mode 100644
index 0000000..6a700bb
--- /dev/null
+++ b/jit/dump.lua
@@ -0,0 +1,730 @@
+----------------------------------------------------------------------------
+-- LuaJIT compiler dump module.
+--
+-- Copyright (C) 2005-2026 Mike Pall. All rights reserved.
+-- Released under the MIT license. See Copyright Notice in luajit.h
+----------------------------------------------------------------------------
+--
+-- This module can be used to debug the JIT compiler itself. It dumps the
+-- code representations and structures used in various compiler stages.
+--
+-- Example usage:
+--
+-- luajit -jdump -e "local x=0; for i=1,1e6 do x=x+i end; print(x)"
+-- luajit -jdump=im -e "for i=1,1000 do for j=1,1000 do end end" | less -R
+-- luajit -jdump=is myapp.lua | less -R
+-- luajit -jdump=-b myapp.lua
+-- luajit -jdump=+aH,myapp.html myapp.lua
+-- luajit -jdump=ixT,myapp.dump myapp.lua
+--
+-- The first argument specifies the dump mode. The second argument gives
+-- the output file name. Default output is to stdout, unless the environment
+-- variable LUAJIT_DUMPFILE is set. The file is overwritten every time the
+-- module is started.
+--
+-- Different features can be turned on or off with the dump mode. If the
+-- mode starts with a '+', the following features are added to the default
+-- set of features; a '-' removes them. Otherwise the features are replaced.
+--
+-- The following dump features are available (* marks the default):
+--
+-- * t Print a line for each started, ended or aborted trace (see also -jv).
+-- * b Dump the traced bytecode.
+-- * i Dump the IR (intermediate representation).
+-- r Augment the IR with register/stack slots.
+-- s Dump the snapshot map.
+-- * m Dump the generated machine code.
+-- x Print each taken trace exit.
+-- X Print each taken trace exit and the contents of all registers.
+-- a Print the IR of aborted traces, too.
+--
+-- The output format can be set with the following characters:
+--
+-- T Plain text output.
+-- A ANSI-colored text output
+-- H Colorized HTML + CSS output.
+--
+-- The default output format is plain text. It's set to ANSI-colored text
+-- if the COLORTERM variable is set. Note: this is independent of any output
+-- redirection, which is actually considered a feature.
+--
+-- You probably want to use less -R to enjoy viewing ANSI-colored text from
+-- a pipe or a file. Add this to your ~/.bashrc: export LESS="-R"
+--
+------------------------------------------------------------------------------
+
+-- Cache some library functions and objects.
+local jit = require("jit")
+local jutil = require("jit.util")
+local vmdef = require("jit.vmdef")
+local funcinfo, funcbc = jutil.funcinfo, jutil.funcbc
+local traceinfo, traceir, tracek = jutil.traceinfo, jutil.traceir, jutil.tracek
+local tracemc, tracesnap = jutil.tracemc, jutil.tracesnap
+local traceexitstub, ircalladdr = jutil.traceexitstub, jutil.ircalladdr
+local bit = require("bit")
+local band, shr, tohex = bit.band, bit.rshift, bit.tohex
+local sub, gsub, format = string.sub, string.gsub, string.format
+local byte, rep = string.byte, string.rep
+local type, tostring = type, tostring
+local stdout, stderr = io.stdout, io.stderr
+
+-- Load other modules on-demand.
+local bcline, disass
+
+-- Active flag, output file handle and dump mode.
+local active, out, dumpmode
+
+------------------------------------------------------------------------------
+
+local symtabmt = { __index = false }
+local symtab = {}
+local nexitsym = 0
+
+-- Fill nested symbol table with per-trace exit stub addresses.
+local function fillsymtab_tr(tr, nexit)
+ local t = {}
+ symtabmt.__index = t
+ if jit.arch:sub(1, 4) == "mips" then
+ t[traceexitstub(tr, 0)] = "exit"
+ return
+ end
+ for i=0,nexit-1 do
+ local addr = traceexitstub(tr, i)
+ if addr < 0 then addr = addr + 2^32 end
+ t[addr] = tostring(i)
+ end
+ local addr = traceexitstub(tr, nexit)
+ if addr then t[addr] = "stack_check" end
+end
+
+-- Fill symbol table with trace exit stub addresses.
+local function fillsymtab(tr, nexit)
+ local t = symtab
+ if nexitsym == 0 then
+ local maskaddr = jit.arch == "arm" and -2
+ local ircall = vmdef.ircall
+ for i=0,#ircall do
+ local addr = ircalladdr(i)
+ if addr ~= 0 then
+ if maskaddr then addr = band(addr, maskaddr) end
+ if addr < 0 then addr = addr + 2^32 end
+ t[addr] = ircall[i]
+ end
+ end
+ end
+ if nexitsym == 1000000 then -- Per-trace exit stubs.
+ fillsymtab_tr(tr, nexit)
+ elseif nexit > nexitsym then -- Shared exit stubs.
+ for i=nexitsym,nexit-1 do
+ local addr = traceexitstub(i)
+ if addr == nil then -- Fall back to per-trace exit stubs.
+ fillsymtab_tr(tr, nexit)
+ setmetatable(symtab, symtabmt)
+ nexit = 1000000
+ break
+ end
+ if addr < 0 then addr = addr + 2^32 end
+ t[addr] = tostring(i)
+ end
+ nexitsym = nexit
+ end
+ return t
+end
+
+local function dumpwrite(s)
+ out:write(s)
+end
+
+-- Disassemble machine code.
+local function dump_mcode(tr)
+ local info = traceinfo(tr)
+ if not info then return end
+ local mcode, addr, loop = tracemc(tr)
+ if not mcode then return end
+ if not disass then disass = require("jit.dis_"..jit.arch) end
+ if addr < 0 then addr = addr + 2^32 end
+ out:write("---- TRACE ", tr, " mcode ", #mcode, "\n")
+ local ctx = disass.create(mcode, addr, dumpwrite)
+ ctx.hexdump = 0
+ ctx.symtab = fillsymtab(tr, info.nexit)
+ if loop ~= 0 then
+ symtab[addr+loop] = "LOOP"
+ ctx:disass(0, loop)
+ out:write("->LOOP:\n")
+ ctx:disass(loop, #mcode-loop)
+ symtab[addr+loop] = nil
+ else
+ ctx:disass(0, #mcode)
+ end
+end
+
+------------------------------------------------------------------------------
+
+local irtype_text = {
+ [0] = "nil",
+ "fal",
+ "tru",
+ "lud",
+ "str",
+ "p32",
+ "thr",
+ "pro",
+ "fun",
+ "p64",
+ "cdt",
+ "tab",
+ "udt",
+ "flt",
+ "num",
+ "i8 ",
+ "u8 ",
+ "i16",
+ "u16",
+ "int",
+ "u32",
+ "i64",
+ "u64",
+ "sfp",
+}
+
+local colortype_ansi = {
+ [0] = "%s",
+ "%s",
+ "%s",
+ "\027[36m%s\027[m",
+ "\027[32m%s\027[m",
+ "%s",
+ "\027[1m%s\027[m",
+ "%s",
+ "\027[1m%s\027[m",
+ "%s",
+ "\027[33m%s\027[m",
+ "\027[31m%s\027[m",
+ "\027[36m%s\027[m",
+ "\027[34m%s\027[m",
+ "\027[34m%s\027[m",
+ "\027[35m%s\027[m",
+ "\027[35m%s\027[m",
+ "\027[35m%s\027[m",
+ "\027[35m%s\027[m",
+ "\027[35m%s\027[m",
+ "\027[35m%s\027[m",
+ "\027[35m%s\027[m",
+ "\027[35m%s\027[m",
+ "\027[35m%s\027[m",
+}
+
+local function colorize_text(s)
+ return s
+end
+
+local function colorize_ansi(s, t, extra)
+ local out = format(colortype_ansi[t], s)
+ if extra then out = "\027[3m"..out end
+ return out
+end
+
+local irtype_ansi = setmetatable({},
+ { __index = function(tab, t)
+ local s = colorize_ansi(irtype_text[t], t); tab[t] = s; return s; end })
+
+local html_escape = { ["<"] = "<", [">"] = ">", ["&"] = "&", }
+
+local function colorize_html(s, t, extra)
+ s = gsub(s, "[<>&]", html_escape)
+ return format('%s',
+ irtype_text[t], extra and " irt_extra" or "", s)
+end
+
+local irtype_html = setmetatable({},
+ { __index = function(tab, t)
+ local s = colorize_html(irtype_text[t], t); tab[t] = s; return s; end })
+
+local header_html = [[
+
+]]
+
+local colorize, irtype
+
+-- Lookup tables to convert some literals into names.
+local litname = {
+ ["SLOAD "] = setmetatable({}, { __index = function(t, mode)
+ local s = ""
+ if band(mode, 1) ~= 0 then s = s.."P" end
+ if band(mode, 2) ~= 0 then s = s.."F" end
+ if band(mode, 4) ~= 0 then s = s.."T" end
+ if band(mode, 8) ~= 0 then s = s.."C" end
+ if band(mode, 16) ~= 0 then s = s.."R" end
+ if band(mode, 32) ~= 0 then s = s.."I" end
+ if band(mode, 64) ~= 0 then s = s.."K" end
+ t[mode] = s
+ return s
+ end}),
+ ["XLOAD "] = { [0] = "", "R", "V", "RV", "U", "RU", "VU", "RVU", },
+ ["CONV "] = setmetatable({}, { __index = function(t, mode)
+ local s = irtype[band(mode, 31)]
+ s = irtype[band(shr(mode, 5), 31)].."."..s
+ if band(mode, 0x800) ~= 0 then s = s.." sext" end
+ local c = shr(mode, 12)
+ if c == 1 then s = s.." none"
+ elseif c == 2 then s = s.." index"
+ elseif c == 3 then s = s.." check" end
+ t[mode] = s
+ return s
+ end}),
+ ["FLOAD "] = vmdef.irfield,
+ ["FREF "] = vmdef.irfield,
+ ["FPMATH"] = vmdef.irfpm,
+ ["TMPREF"] = { [0] = "", "IN", "OUT", "INOUT", "", "", "OUT2", "INOUT2" },
+ ["BUFHDR"] = { [0] = "RESET", "APPEND", "WRITE" },
+ ["TOSTR "] = { [0] = "INT", "NUM", "CHAR" },
+}
+
+local function ctlsub(c)
+ if c == "\n" then return "\\n"
+ elseif c == "\r" then return "\\r"
+ elseif c == "\t" then return "\\t"
+ else return format("\\%03d", byte(c))
+ end
+end
+
+local function fmtfunc(func, pc)
+ local fi = funcinfo(func, pc)
+ if fi.loc then
+ return fi.loc
+ elseif fi.ffid then
+ return vmdef.ffnames[fi.ffid]
+ elseif fi.addr then
+ return format("C:%x", fi.addr)
+ else
+ return "(?)"
+ end
+end
+
+local function formatk(tr, idx, sn)
+ local k, t, slot = tracek(tr, idx)
+ local tn = type(k)
+ local s
+ if tn == "number" then
+ if t < 12 then
+ s = k == 0 and "NULL" or format("[0x%08x]", k)
+ elseif band(sn or 0, 0x30000) ~= 0 then
+ s = band(sn, 0x20000) ~= 0 and "contpc" or "ftsz"
+ elseif k == 2^52+2^51 then
+ s = "bias"
+ else
+ s = format(0 < k and k < 0x1p-1026 and "%+a" or "%+.14g", k)
+ end
+ elseif tn == "string" then
+ s = format(#k > 20 and '"%.20s"~' or '"%s"', gsub(k, "%c", ctlsub))
+ elseif tn == "function" then
+ s = fmtfunc(k)
+ elseif tn == "table" then
+ s = format("{%p}", k)
+ elseif tn == "userdata" then
+ if t == 12 then
+ s = format("userdata:%p", k)
+ else
+ s = format("[%p]", k)
+ if s == "[NULL]" then s = "NULL" end
+ end
+ elseif t == 21 then -- int64_t
+ s = sub(tostring(k), 1, -3)
+ if sub(s, 1, 1) ~= "-" then s = "+"..s end
+ elseif sn == 0x1057fff then -- SNAP(1, SNAP_FRAME | SNAP_NORESTORE, REF_NIL)
+ return "----" -- Special case for LJ_FR2 slot 1.
+ else
+ s = tostring(k) -- For primitives.
+ end
+ s = colorize(format("%-4s", s), t, band(sn or 0, 0x100000) ~= 0)
+ if slot then
+ s = format("%s @%d", s, slot)
+ end
+ return s
+end
+
+local function printsnap(tr, snap)
+ local n = 2
+ for s=0,snap[1]-1 do
+ local sn = snap[n]
+ if shr(sn, 24) == s then
+ n = n + 1
+ local ref = band(sn, 0xffff) - 0x8000 -- REF_BIAS
+ if ref < 0 then
+ out:write(formatk(tr, ref, sn))
+ elseif band(sn, 0x80000) ~= 0 then -- SNAP_SOFTFPNUM
+ out:write(colorize(format("%04d/%04d", ref, ref+1), 14))
+ else
+ local m, ot, op1, op2 = traceir(tr, ref)
+ out:write(colorize(format("%04d", ref), band(ot, 31), band(sn, 0x100000) ~= 0))
+ end
+ out:write(band(sn, 0x10000) == 0 and " " or "|") -- SNAP_FRAME
+ else
+ out:write("---- ")
+ end
+ end
+ out:write("]\n")
+end
+
+-- Dump snapshots (not interleaved with IR).
+local function dump_snap(tr)
+ out:write("---- TRACE ", tr, " snapshots\n")
+ for i=0,1000000000 do
+ local snap = tracesnap(tr, i)
+ if not snap then break end
+ out:write(format("#%-3d %04d [ ", i, snap[0]))
+ printsnap(tr, snap)
+ end
+end
+
+-- Return a register name or stack slot for a rid/sp location.
+local function ridsp_name(ridsp, ins)
+ if not disass then disass = require("jit.dis_"..jit.arch) end
+ local rid, slot = band(ridsp, 0xff), shr(ridsp, 8)
+ if rid == 253 or rid == 254 then
+ return (slot == 0 or slot == 255) and " {sink" or format(" {%04d", ins-slot)
+ end
+ if ridsp > 255 then return format("[%x]", slot*4) end
+ if rid < 128 then return disass.regname(rid) end
+ return ""
+end
+
+-- Dump CALL* function ref and return optional ctype.
+local function dumpcallfunc(tr, ins)
+ local ctype
+ if ins > 0 then
+ local m, ot, op1, op2 = traceir(tr, ins)
+ if band(ot, 31) == 0 then -- nil type means CARG(func, ctype).
+ ins = op1
+ ctype = formatk(tr, op2)
+ end
+ end
+ if ins < 0 then
+ out:write(format("[0x%x](", tonumber((tracek(tr, ins)))))
+ else
+ out:write(format("%04d (", ins))
+ end
+ return ctype
+end
+
+-- Recursively gather CALL* args and dump them.
+local function dumpcallargs(tr, ins)
+ if ins < 0 then
+ out:write(formatk(tr, ins))
+ else
+ local m, ot, op1, op2 = traceir(tr, ins)
+ local oidx = 6*shr(ot, 8)
+ local op = sub(vmdef.irnames, oidx+1, oidx+6)
+ if op == "CARG " then
+ dumpcallargs(tr, op1)
+ if op2 < 0 then
+ out:write(" ", formatk(tr, op2))
+ else
+ out:write(" ", format("%04d", op2))
+ end
+ else
+ out:write(format("%04d", ins))
+ end
+ end
+end
+
+-- Dump IR and interleaved snapshots.
+local function dump_ir(tr, dumpsnap, dumpreg)
+ local info = traceinfo(tr)
+ if not info then return end
+ local nins = info.nins
+ out:write("---- TRACE ", tr, " IR\n")
+ local irnames = vmdef.irnames
+ local snapref = 65536
+ local snap, snapno
+ if dumpsnap then
+ snap = tracesnap(tr, 0)
+ snapref = snap[0]
+ snapno = 0
+ end
+ for ins=1,nins do
+ if ins >= snapref then
+ if dumpreg then
+ out:write(format(".... SNAP #%-3d [ ", snapno))
+ else
+ out:write(format(".... SNAP #%-3d [ ", snapno))
+ end
+ printsnap(tr, snap)
+ snapno = snapno + 1
+ snap = tracesnap(tr, snapno)
+ snapref = snap and snap[0] or 65536
+ end
+ local m, ot, op1, op2, ridsp = traceir(tr, ins)
+ local oidx, t = 6*shr(ot, 8), band(ot, 31)
+ local op = sub(irnames, oidx+1, oidx+6)
+ if op == "LOOP " then
+ if dumpreg then
+ out:write(format("%04d ------------ LOOP ------------\n", ins))
+ else
+ out:write(format("%04d ------ LOOP ------------\n", ins))
+ end
+ elseif op ~= "NOP " and op ~= "CARG " and
+ (dumpreg or op ~= "RENAME") then
+ local rid = band(ridsp, 255)
+ if dumpreg then
+ out:write(format("%04d %-6s", ins, ridsp_name(ridsp, ins)))
+ else
+ out:write(format("%04d ", ins))
+ end
+ out:write(format("%s%s %s %s ",
+ (rid == 254 or rid == 253) and "}" or
+ (band(ot, 128) == 0 and " " or ">"),
+ band(ot, 64) == 0 and " " or "+",
+ irtype[t], op))
+ local m1, m2 = band(m, 3), band(m, 3*4)
+ if sub(op, 1, 4) == "CALL" then
+ local ctype
+ if m2 == 1*4 then -- op2 == IRMlit
+ out:write(format("%-10s (", vmdef.ircall[op2]))
+ else
+ ctype = dumpcallfunc(tr, op2)
+ end
+ if op1 ~= -1 then dumpcallargs(tr, op1) end
+ out:write(")")
+ if ctype then out:write(" ctype ", ctype) end
+ elseif op == "CNEW " and op2 == -1 then
+ out:write(formatk(tr, op1))
+ elseif m1 ~= 3 then -- op1 != IRMnone
+ if op1 < 0 then
+ out:write(formatk(tr, op1))
+ else
+ out:write(format(m1 == 0 and "%04d" or "#%-3d", op1))
+ end
+ if m2 ~= 3*4 then -- op2 != IRMnone
+ if m2 == 1*4 then -- op2 == IRMlit
+ local litn = litname[op]
+ if litn and litn[op2] then
+ out:write(" ", litn[op2])
+ elseif op == "UREFO " or op == "UREFC " then
+ out:write(format(" #%-3d", shr(op2, 8)))
+ else
+ out:write(format(" #%-3d", op2))
+ end
+ elseif op2 < 0 then
+ out:write(" ", formatk(tr, op2))
+ else
+ out:write(format(" %04d", op2))
+ end
+ end
+ end
+ out:write("\n")
+ end
+ end
+ if snap then
+ if dumpreg then
+ out:write(format(".... SNAP #%-3d [ ", snapno))
+ else
+ out:write(format(".... SNAP #%-3d [ ", snapno))
+ end
+ printsnap(tr, snap)
+ end
+end
+
+------------------------------------------------------------------------------
+
+local recprefix = ""
+local recdepth = 0
+
+-- Format trace error message.
+local function fmterr(err, info)
+ if type(err) == "number" then
+ if type(info) == "function" then info = fmtfunc(info) end
+ local fmt = vmdef.traceerr[err]
+ if fmt == "NYI: bytecode %s" then
+ local oidx = 6 * info
+ info = sub(vmdef.bcnames, oidx+1, oidx+6)
+ end
+ err = format(fmt, info)
+ end
+ return err
+end
+
+-- Dump trace states.
+local function dump_trace(what, tr, func, pc, otr, oex)
+ if what == "stop" or (what == "abort" and dumpmode.a) then
+ if dumpmode.i then dump_ir(tr, dumpmode.s, dumpmode.r and what == "stop")
+ elseif dumpmode.s then dump_snap(tr) end
+ if dumpmode.m then dump_mcode(tr) end
+ end
+ if what == "start" then
+ if dumpmode.H then out:write('\n') end
+ out:write("---- TRACE ", tr, " ", what)
+ if otr then out:write(" ", otr, "/", oex == -1 and "stitch" or oex) end
+ out:write(" ", fmtfunc(func, pc), "\n")
+ elseif what == "stop" or what == "abort" then
+ out:write("---- TRACE ", tr, " ", what)
+ if what == "abort" then
+ out:write(" ", fmtfunc(func, pc), " -- ", fmterr(otr, oex), "\n")
+ else
+ local info = traceinfo(tr)
+ local link, ltype = info.link, info.linktype
+ if link == tr or link == 0 then
+ out:write(" -> ", ltype, "\n")
+ elseif ltype == "root" then
+ out:write(" -> ", link, "\n")
+ else
+ out:write(" -> ", link, " ", ltype, "\n")
+ end
+ end
+ if dumpmode.H then out:write("\n\n") else out:write("\n") end
+ else
+ if what == "flush" then symtab, nexitsym = {}, 0 end
+ out:write("---- TRACE ", what, "\n\n")
+ end
+ out:flush()
+end
+
+-- Dump recorded bytecode.
+local function dump_record(tr, func, pc, depth)
+ if depth ~= recdepth then
+ recdepth = depth
+ recprefix = rep(" .", depth)
+ end
+ local line
+ if pc >= 0 then
+ line = bcline(func, pc, recprefix)
+ if dumpmode.H then line = gsub(line, "[<>&]", html_escape) end
+ else
+ line = "0000 "..recprefix.." FUNCC \n"
+ end
+ if pc <= 0 then
+ out:write(sub(line, 1, -2), " ; ", fmtfunc(func), "\n")
+ else
+ out:write(line)
+ end
+ if pc >= 0 and band(funcbc(func, pc), 0xff) < 16 then -- ORDER BC
+ out:write(bcline(func, pc+1, recprefix)) -- Write JMP for cond.
+ end
+end
+
+------------------------------------------------------------------------------
+
+local gpr64 = jit.arch:match("64")
+local fprmips32 = jit.arch == "mips" or jit.arch == "mipsel"
+
+-- Dump taken trace exits.
+local function dump_texit(tr, ex, ngpr, nfpr, ...)
+ out:write("---- TRACE ", tr, " exit ", ex, "\n")
+ if dumpmode.X then
+ local regs = {...}
+ if gpr64 then
+ for i=1,ngpr do
+ out:write(format(" %016x", regs[i]))
+ if i % 4 == 0 then out:write("\n") end
+ end
+ else
+ for i=1,ngpr do
+ out:write(" ", tohex(regs[i]))
+ if i % 8 == 0 then out:write("\n") end
+ end
+ end
+ if fprmips32 then
+ for i=1,nfpr,2 do
+ out:write(format(" %+17.14g", regs[ngpr+i]))
+ if i % 8 == 7 then out:write("\n") end
+ end
+ else
+ for i=1,nfpr do
+ out:write(format(" %+17.14g", regs[ngpr+i]))
+ if i % 4 == 0 then out:write("\n") end
+ end
+ end
+ end
+end
+
+------------------------------------------------------------------------------
+
+-- Detach dump handlers.
+local function dumpoff()
+ if active then
+ active = false
+ jit.attach(dump_texit)
+ jit.attach(dump_record)
+ jit.attach(dump_trace)
+ if out and out ~= stdout and out ~= stderr then out:close() end
+ out = nil
+ end
+end
+
+-- Open the output file and attach dump handlers.
+local function dumpon(opt, outfile)
+ if active then dumpoff() end
+
+ local term = os.getenv("TERM")
+ local colormode = (term and term:match("color") or os.getenv("COLORTERM")) and "A" or "T"
+ if opt then
+ opt = gsub(opt, "[TAH]", function(mode) colormode = mode; return ""; end)
+ end
+
+ local m = { t=true, b=true, i=true, m=true, }
+ if opt and opt ~= "" then
+ local o = sub(opt, 1, 1)
+ if o ~= "+" and o ~= "-" then m = {} end
+ for i=1,#opt do m[sub(opt, i, i)] = (o ~= "-") end
+ end
+ dumpmode = m
+
+ if m.t or m.b or m.i or m.s or m.m then
+ jit.attach(dump_trace, "trace")
+ end
+ if m.b then
+ jit.attach(dump_record, "record")
+ if not bcline then bcline = require("jit.bc").line end
+ end
+ if m.x or m.X then
+ jit.attach(dump_texit, "texit")
+ end
+
+ if not outfile then outfile = os.getenv("LUAJIT_DUMPFILE") end
+ if outfile then
+ out = outfile == "-" and stdout or assert(io.open(outfile, "w"))
+ else
+ out = stdout
+ end
+
+ m[colormode] = true
+ if colormode == "A" then
+ colorize = colorize_ansi
+ irtype = irtype_ansi
+ elseif colormode == "H" then
+ colorize = colorize_html
+ irtype = irtype_html
+ out:write(header_html)
+ else
+ colorize = colorize_text
+ irtype = irtype_text
+ end
+
+ active = true
+end
+
+-- Public module functions.
+return {
+ on = dumpon,
+ off = dumpoff,
+ start = dumpon -- For -j command line option.
+}
+
diff --git a/jit/p.lua b/jit/p.lua
new file mode 100644
index 0000000..9d938ce
--- /dev/null
+++ b/jit/p.lua
@@ -0,0 +1,309 @@
+----------------------------------------------------------------------------
+-- LuaJIT profiler.
+--
+-- Copyright (C) 2005-2026 Mike Pall. All rights reserved.
+-- Released under the MIT license. See Copyright Notice in luajit.h
+----------------------------------------------------------------------------
+--
+-- This module is a simple command line interface to the built-in
+-- low-overhead profiler of LuaJIT.
+--
+-- The lower-level API of the profiler is accessible via the "jit.profile"
+-- module or the luaJIT_profile_* C API.
+--
+-- Example usage:
+--
+-- luajit -jp myapp.lua
+-- luajit -jp=s myapp.lua
+-- luajit -jp=-s myapp.lua
+-- luajit -jp=vl myapp.lua
+-- luajit -jp=G,profile.txt myapp.lua
+--
+-- The following dump features are available:
+--
+-- f Stack dump: function name, otherwise module:line. Default mode.
+-- F Stack dump: ditto, but always prepend module.
+-- l Stack dump: module:line.
+-- stack dump depth (callee < caller). Default: 1.
+-- - Inverse stack dump depth (caller > callee).
+-- s Split stack dump after first stack level. Implies abs(depth) >= 2.
+-- p Show full path for module names.
+-- v Show VM states. Can be combined with stack dumps, e.g. vf or fv.
+-- z Show zones. Can be combined with stack dumps, e.g. zf or fz.
+-- r Show raw sample counts. Default: show percentages.
+-- a Annotate excerpts from source code files.
+-- A Annotate complete source code files.
+-- G Produce raw output suitable for graphical tools (e.g. flame graphs).
+-- m Minimum sample percentage to be shown. Default: 3.
+-- i Sampling interval in milliseconds. Default: 10.
+--
+----------------------------------------------------------------------------
+
+-- Cache some library functions and objects.
+local jit = require("jit")
+local profile = require("jit.profile")
+local vmdef = require("jit.vmdef")
+local math = math
+local pairs, ipairs, tonumber, floor = pairs, ipairs, tonumber, math.floor
+local sort, format = table.sort, string.format
+local stdout = io.stdout
+local zone -- Load jit.zone module on demand.
+
+-- Output file handle.
+local out
+
+------------------------------------------------------------------------------
+
+local prof_ud
+local prof_states, prof_split, prof_min, prof_raw, prof_fmt, prof_depth
+local prof_ann, prof_count1, prof_count2, prof_samples
+
+local map_vmmode = {
+ N = "Compiled",
+ I = "Interpreted",
+ C = "C code",
+ G = "Garbage Collector",
+ J = "JIT Compiler",
+}
+
+-- Profiler callback.
+local function prof_cb(th, samples, vmmode)
+ prof_samples = prof_samples + samples
+ local key_stack, key_stack2, key_state
+ -- Collect keys for sample.
+ if prof_states then
+ if prof_states == "v" then
+ key_state = map_vmmode[vmmode] or vmmode
+ else
+ key_state = zone:get() or "(none)"
+ end
+ end
+ if prof_fmt then
+ key_stack = profile.dumpstack(th, prof_fmt, prof_depth)
+ key_stack = key_stack:gsub("%[builtin#(%d+)%]", function(x)
+ return vmdef.ffnames[tonumber(x)]
+ end)
+ if prof_split == 2 then
+ local k1, k2 = key_stack:match("(.-) [<>] (.*)")
+ if k2 then key_stack, key_stack2 = k1, k2 end
+ elseif prof_split == 3 then
+ key_stack2 = profile.dumpstack(th, "l", 1)
+ end
+ end
+ -- Order keys.
+ local k1, k2
+ if prof_split == 1 then
+ if key_state then
+ k1 = key_state
+ if key_stack then k2 = key_stack end
+ end
+ elseif key_stack then
+ k1 = key_stack
+ if key_stack2 then k2 = key_stack2 elseif key_state then k2 = key_state end
+ end
+ -- Coalesce samples in one or two levels.
+ if k1 then
+ local t1 = prof_count1
+ t1[k1] = (t1[k1] or 0) + samples
+ if k2 then
+ local t2 = prof_count2
+ local t3 = t2[k1]
+ if not t3 then t3 = {}; t2[k1] = t3 end
+ t3[k2] = (t3[k2] or 0) + samples
+ end
+ end
+end
+
+------------------------------------------------------------------------------
+
+-- Show top N list.
+local function prof_top(count1, count2, samples, indent)
+ local t, n = {}, 0
+ for k in pairs(count1) do
+ n = n + 1
+ t[n] = k
+ end
+ sort(t, function(a, b) return count1[a] > count1[b] end)
+ for i=1,n do
+ local k = t[i]
+ local v = count1[k]
+ local pct = floor(v*100/samples + 0.5)
+ if pct < prof_min then break end
+ if not prof_raw then
+ out:write(format("%s%2d%% %s\n", indent, pct, k))
+ elseif prof_raw == "r" then
+ out:write(format("%s%5d %s\n", indent, v, k))
+ else
+ out:write(format("%s %d\n", k, v))
+ end
+ if count2 then
+ local r = count2[k]
+ if r then
+ prof_top(r, nil, v, (prof_split == 3 or prof_split == 1) and " -- " or
+ (prof_depth < 0 and " -> " or " <- "))
+ end
+ end
+ end
+end
+
+-- Annotate source code
+local function prof_annotate(count1, samples)
+ local files = {}
+ local ms = 0
+ for k, v in pairs(count1) do
+ local pct = floor(v*100/samples + 0.5)
+ ms = math.max(ms, v)
+ if pct >= prof_min then
+ local file, line = k:match("^(.*):(%d+)$")
+ if not file then file = k; line = 0 end
+ local fl = files[file]
+ if not fl then fl = {}; files[file] = fl; files[#files+1] = file end
+ line = tonumber(line)
+ fl[line] = prof_raw and v or pct
+ end
+ end
+ sort(files)
+ local fmtv, fmtn = " %3d%% | %s\n", " | %s\n"
+ if prof_raw then
+ local n = math.max(5, math.ceil(math.log10(ms)))
+ fmtv = "%"..n.."d | %s\n"
+ fmtn = (" "):rep(n).." | %s\n"
+ end
+ local ann = prof_ann
+ for _, file in ipairs(files) do
+ local f0 = file:byte()
+ if f0 == 40 or f0 == 91 then
+ out:write(format("\n====== %s ======\n[Cannot annotate non-file]\n", file))
+ break
+ end
+ local fp, err = io.open(file)
+ if not fp then
+ out:write(format("====== ERROR: %s: %s\n", file, err))
+ break
+ end
+ out:write(format("\n====== %s ======\n", file))
+ local fl = files[file]
+ local n, show = 1, false
+ if ann ~= 0 then
+ for i=1,ann do
+ if fl[i] then show = true; out:write("@@ 1 @@\n"); break end
+ end
+ end
+ for line in fp:lines() do
+ if line:byte() == 27 then
+ out:write("[Cannot annotate bytecode file]\n")
+ break
+ end
+ local v = fl[n]
+ if ann ~= 0 then
+ local v2 = fl[n+ann]
+ if show then
+ if v2 then show = n+ann elseif v then show = n
+ elseif show+ann < n then show = false end
+ elseif v2 then
+ show = n+ann
+ out:write(format("@@ %d @@\n", n))
+ end
+ if not show then goto next end
+ end
+ if v then
+ out:write(format(fmtv, v, line))
+ else
+ out:write(format(fmtn, line))
+ end
+ ::next::
+ n = n + 1
+ end
+ fp:close()
+ end
+end
+
+------------------------------------------------------------------------------
+
+-- Finish profiling and dump result.
+local function prof_finish()
+ if prof_ud then
+ profile.stop()
+ local samples = prof_samples
+ if samples == 0 then
+ if prof_raw ~= true then out:write("[No samples collected]\n") end
+ elseif prof_ann then
+ prof_annotate(prof_count1, samples)
+ else
+ prof_top(prof_count1, prof_count2, samples, "")
+ end
+ prof_count1 = nil
+ prof_count2 = nil
+ prof_ud = nil
+ if out ~= stdout then out:close() end
+ end
+end
+
+-- Start profiling.
+local function prof_start(mode)
+ local interval = ""
+ mode = mode:gsub("i%d*", function(s) interval = s; return "" end)
+ prof_min = 3
+ mode = mode:gsub("m(%d+)", function(s) prof_min = tonumber(s); return "" end)
+ prof_depth = 1
+ mode = mode:gsub("%-?%d+", function(s) prof_depth = tonumber(s); return "" end)
+ local m = {}
+ for c in mode:gmatch(".") do m[c] = c end
+ prof_states = m.z or m.v
+ if prof_states == "z" then zone = require("jit.zone") end
+ local scope = m.l or m.f or m.F or (prof_states and "" or "f")
+ local flags = (m.p or "")
+ prof_raw = m.r
+ if m.s then
+ prof_split = 2
+ if prof_depth == -1 or m["-"] then prof_depth = -2
+ elseif prof_depth == 1 then prof_depth = 2 end
+ elseif mode:find("[fF].*l") then
+ scope = "l"
+ prof_split = 3
+ else
+ prof_split = (scope == "" or mode:find("[zv].*[lfF]")) and 1 or 0
+ end
+ prof_ann = m.A and 0 or (m.a and 3)
+ if prof_ann then
+ scope = "l"
+ prof_fmt = "pl"
+ prof_split = 0
+ prof_depth = 1
+ elseif m.G and scope ~= "" then
+ prof_fmt = flags..scope.."Z;"
+ prof_depth = -100
+ prof_raw = true
+ prof_min = 0
+ elseif scope == "" then
+ prof_fmt = false
+ else
+ local sc = prof_split == 3 and m.f or m.F or scope
+ prof_fmt = flags..sc..(prof_depth >= 0 and "Z < " or "Z > ")
+ end
+ prof_count1 = {}
+ prof_count2 = {}
+ prof_samples = 0
+ profile.start(scope:lower()..interval, prof_cb)
+ prof_ud = newproxy(true)
+ getmetatable(prof_ud).__gc = prof_finish
+end
+
+------------------------------------------------------------------------------
+
+local function start(mode, outfile)
+ if not outfile then outfile = os.getenv("LUAJIT_PROFILEFILE") end
+ if outfile then
+ out = outfile == "-" and stdout or assert(io.open(outfile, "w"))
+ else
+ out = stdout
+ end
+ prof_start(mode or "f")
+end
+
+-- Public module functions.
+return {
+ start = start, -- For -j command line option.
+ stop = prof_finish
+}
+
diff --git a/jit/v.lua b/jit/v.lua
new file mode 100644
index 0000000..69443d3
--- /dev/null
+++ b/jit/v.lua
@@ -0,0 +1,174 @@
+----------------------------------------------------------------------------
+-- Verbose mode of the LuaJIT compiler.
+--
+-- Copyright (C) 2005-2026 Mike Pall. All rights reserved.
+-- Released under the MIT license. See Copyright Notice in luajit.h
+----------------------------------------------------------------------------
+--
+-- This module shows verbose information about the progress of the
+-- JIT compiler. It prints one line for each generated trace. This module
+-- is useful to see which code has been compiled or where the compiler
+-- punts and falls back to the interpreter.
+--
+-- Example usage:
+--
+-- luajit -jv -e "for i=1,1000 do for j=1,1000 do end end"
+-- luajit -jv=myapp.out myapp.lua
+--
+-- Default output is to stderr. To redirect the output to a file, pass a
+-- filename as an argument (use '-' for stdout) or set the environment
+-- variable LUAJIT_VERBOSEFILE. The file is overwritten every time the
+-- module is started.
+--
+-- The output from the first example should look like this:
+--
+-- [TRACE 1 (command line):1 loop]
+-- [TRACE 2 (1/3) (command line):1 -> 1]
+--
+-- The first number in each line is the internal trace number. Next are
+-- the file name ('(command line)') and the line number (':1') where the
+-- trace has started. Side traces also show the parent trace number and
+-- the exit number where they are attached to in parentheses ('(1/3)').
+-- An arrow at the end shows where the trace links to ('-> 1'), unless
+-- it loops to itself.
+--
+-- In this case the inner loop gets hot and is traced first, generating
+-- a root trace. Then the last exit from the 1st trace gets hot, too,
+-- and triggers generation of the 2nd trace. The side trace follows the
+-- path along the outer loop and *around* the inner loop, back to its
+-- start, and then links to the 1st trace. Yes, this may seem unusual,
+-- if you know how traditional compilers work. Trace compilers are full
+-- of surprises like this -- have fun! :-)
+--
+-- Aborted traces are shown like this:
+--
+-- [TRACE --- foo.lua:44 -- leaving loop in root trace at foo:lua:50]
+--
+-- Don't worry -- trace aborts are quite common, even in programs which
+-- can be fully compiled. The compiler may retry several times until it
+-- finds a suitable trace.
+--
+-- Of course this doesn't work with features that are not-yet-implemented
+-- (NYI error messages). The VM simply falls back to the interpreter. This
+-- may not matter at all if the particular trace is not very high up in
+-- the CPU usage profile. Oh, and the interpreter is quite fast, too.
+--
+-- Also check out the -jdump module, which prints all the gory details.
+--
+------------------------------------------------------------------------------
+
+-- Cache some library functions and objects.
+local jit = require("jit")
+local jutil = require("jit.util")
+local vmdef = require("jit.vmdef")
+local funcinfo, traceinfo = jutil.funcinfo, jutil.traceinfo
+local type, sub, format = type, string.sub, string.format
+local stdout, stderr = io.stdout, io.stderr
+
+-- Active flag and output file handle.
+local active, out
+
+------------------------------------------------------------------------------
+
+local startloc, startex
+
+local function fmtfunc(func, pc)
+ local fi = funcinfo(func, pc)
+ if fi.loc then
+ return fi.loc
+ elseif fi.ffid then
+ return vmdef.ffnames[fi.ffid]
+ elseif fi.addr then
+ return format("C:%x", fi.addr)
+ else
+ return "(?)"
+ end
+end
+
+-- Format trace error message.
+local function fmterr(err, info)
+ if type(err) == "number" then
+ if type(info) == "function" then info = fmtfunc(info) end
+ local fmt = vmdef.traceerr[err]
+ if fmt == "NYI: bytecode %s" then
+ local oidx = 6 * info
+ info = sub(vmdef.bcnames, oidx+1, oidx+6)
+ end
+ err = format(fmt, info)
+ end
+ return err
+end
+
+-- Dump trace states.
+local function dump_trace(what, tr, func, pc, otr, oex)
+ if what == "start" then
+ startloc = fmtfunc(func, pc)
+ startex = otr and "("..otr.."/"..(oex == -1 and "stitch" or oex)..") " or ""
+ else
+ if what == "abort" then
+ local loc = fmtfunc(func, pc)
+ if loc ~= startloc then
+ out:write(format("[TRACE --- %s%s -- %s at %s]\n",
+ startex, startloc, fmterr(otr, oex), loc))
+ else
+ out:write(format("[TRACE --- %s%s -- %s]\n",
+ startex, startloc, fmterr(otr, oex)))
+ end
+ elseif what == "stop" then
+ local info = traceinfo(tr)
+ local link, ltype = info.link, info.linktype
+ if ltype == "interpreter" then
+ out:write(format("[TRACE %3s %s%s -- fallback to interpreter]\n",
+ tr, startex, startloc))
+ elseif ltype == "stitch" then
+ out:write(format("[TRACE %3s %s%s %s %s]\n",
+ tr, startex, startloc, ltype, fmtfunc(func, pc)))
+ elseif link == tr or link == 0 then
+ out:write(format("[TRACE %3s %s%s %s]\n",
+ tr, startex, startloc, ltype))
+ elseif ltype == "root" then
+ out:write(format("[TRACE %3s %s%s -> %d]\n",
+ tr, startex, startloc, link))
+ else
+ out:write(format("[TRACE %3s %s%s -> %d %s]\n",
+ tr, startex, startloc, link, ltype))
+ end
+ else
+ out:write(format("[TRACE %s]\n", what))
+ end
+ out:flush()
+ end
+end
+
+------------------------------------------------------------------------------
+
+-- Detach dump handlers.
+local function dumpoff()
+ if active then
+ active = false
+ jit.attach(dump_trace)
+ if out and out ~= stdout and out ~= stderr then out:close() end
+ out = nil
+ end
+end
+
+-- Open the output file and attach dump handlers.
+local function dumpon(outfile)
+ if active then dumpoff() end
+ if not outfile then outfile = os.getenv("LUAJIT_VERBOSEFILE") end
+ if outfile then
+ out = outfile == "-" and stdout or assert(io.open(outfile, "w"))
+ else
+ out = stderr
+ end
+ jit.attach(dump_trace, "trace")
+ active = true
+end
+
+-- Public module functions.
+return {
+ on = dumpon,
+ off = dumpoff,
+ start = dumpon -- For -j command line option.
+}
+
diff --git a/jit/vmdef.lua b/jit/vmdef.lua
new file mode 100644
index 0000000..c0b7d3a
--- /dev/null
+++ b/jit/vmdef.lua
@@ -0,0 +1,391 @@
+-- This is a generated file. DO NOT EDIT!
+
+assert(require("jit").version == "LuaJIT 2.1.1771261233", "LuaJIT core/library version mismatch")
+
+return {
+
+bcnames = "ISLT ISGE ISLE ISGT ISEQV ISNEV ISEQS ISNES ISEQN ISNEN ISEQP ISNEP ISTC ISFC IST ISF ISTYPEISNUM MOV NOT UNM LEN ADDVN SUBVN MULVN DIVVN MODVN ADDNV SUBNV MULNV DIVNV MODNV ADDVV SUBVV MULVV DIVVV MODVV POW CAT KSTR KCDATAKSHORTKNUM KPRI KNIL UGET USETV USETS USETN USETP UCLO FNEW TNEW TDUP GGET GSET TGETV TGETS TGETB TGETR TSETV TSETS TSETB TSETM TSETR CALLM CALL CALLMTCALLT ITERC ITERN VARG ISNEXTRETM RET RET0 RET1 FORI JFORI FORL IFORL JFORL ITERL IITERLJITERLLOOP ILOOP JLOOP JMP FUNCF IFUNCFJFUNCFFUNCV IFUNCVJFUNCVFUNCC FUNCCW",
+
+irnames = "LT GE LE GT ULT UGE ULE UGT EQ NE ABC RETF NOP BASE PVAL GCSTEPHIOP LOOP USE PHI RENAMEPROF KPRI KINT KGC KPTR KKPTR KNULL KNUM KINT64KSLOT BNOT BSWAP BAND BOR BXOR BSHL BSHR BSAR BROL BROR ADD SUB MUL DIV MOD POW NEG ABS LDEXP MIN MAX FPMATHADDOV SUBOV MULOV AREF HREFK HREF NEWREFUREFO UREFC FREF TMPREFSTRREFLREF ALOAD HLOAD ULOAD FLOAD XLOAD SLOAD VLOAD ALEN ASTOREHSTOREUSTOREFSTOREXSTORESNEW XSNEW TNEW TDUP CNEW CNEWI BUFHDRBUFPUTBUFSTRTBAR OBAR XBAR CONV TOBIT TOSTR STRTO CALLN CALLA CALLL CALLS CALLXSCARG ",
+
+irfpm = { [0]="floor", "ceil", "trunc", "sqrt", "log", "log2", "other", },
+
+irfield = { [0]="str.len", "func.env", "func.pc", "func.ffid", "thread.env", "tab.meta", "tab.array", "tab.node", "tab.asize", "tab.hmask", "tab.nomm", "udata.meta", "udata.udtype", "udata.file", "sbuf.w", "sbuf.e", "sbuf.b", "sbuf.l", "sbuf.ref", "sbuf.r", "cdata.ctypeid", "cdata.ptr", "cdata.int", "cdata.int64", "cdata.int64_4", },
+
+ircall = {
+[0]="lj_str_cmp",
+"lj_str_find",
+"lj_str_new",
+"lj_strscan_num",
+"lj_strfmt_int",
+"lj_strfmt_num",
+"lj_strfmt_char",
+"lj_strfmt_putint",
+"lj_strfmt_putnum",
+"lj_strfmt_putquoted",
+"lj_strfmt_putfxint",
+"lj_strfmt_putfnum_int",
+"lj_strfmt_putfnum_uint",
+"lj_strfmt_putfnum",
+"lj_strfmt_putfstr",
+"lj_strfmt_putfchar",
+"lj_buf_putmem",
+"lj_buf_putstr",
+"lj_buf_putchar",
+"lj_buf_putstr_reverse",
+"lj_buf_putstr_lower",
+"lj_buf_putstr_upper",
+"lj_buf_putstr_rep",
+"lj_buf_puttab",
+"lj_bufx_set",
+"lj_bufx_more",
+"lj_serialize_put",
+"lj_serialize_get",
+"lj_serialize_encode",
+"lj_serialize_decode",
+"lj_buf_tostr",
+"lj_tab_new_ah",
+"lj_tab_new1",
+"lj_tab_dup",
+"lj_tab_clear",
+"lj_tab_newkey",
+"lj_tab_keyindex",
+"lj_vm_next",
+"lj_tab_len",
+"lj_tab_len_hint",
+"lj_gc_step_jit",
+"lj_gc_barrieruv",
+"lj_mem_newgco",
+"lj_prng_u64d",
+"lj_vm_modi",
+"log10",
+"exp",
+"sin",
+"cos",
+"tan",
+"asin",
+"acos",
+"atan",
+"sinh",
+"cosh",
+"tanh",
+"fputc",
+"fwrite",
+"fflush",
+"lj_vm_floor",
+"lj_vm_ceil",
+"lj_vm_trunc",
+"sqrt",
+"log",
+"lj_vm_log2",
+"pow",
+"atan2",
+"ldexp",
+"lj_vm_tobit",
+"softfp_add",
+"softfp_sub",
+"softfp_mul",
+"softfp_div",
+"softfp_cmp",
+"softfp_i2d",
+"softfp_d2i",
+"lj_vm_sfmin",
+"lj_vm_sfmax",
+"lj_vm_tointg",
+"softfp_ui2d",
+"softfp_f2d",
+"softfp_d2f",
+"softfp_i2f",
+"softfp_ui2f",
+"softfp_f2i",
+"fp64_l2d",
+"fp64_ul2d",
+"fp64_l2f",
+"fp64_ul2f",
+"lj_vm_num2u64",
+"lj_carith_divi64",
+"lj_carith_divu64",
+"lj_carith_modi64",
+"lj_carith_modu64",
+"lj_carith_powi64",
+"lj_carith_powu64",
+"lj_cdata_newv",
+"lj_cdata_setfin",
+"strlen",
+"memcpy",
+"memset",
+"lj_vm_errno",
+"lj_carith_mul64",
+"lj_carith_shl64",
+"lj_carith_shr64",
+"lj_carith_sar64",
+"lj_carith_rol64",
+"lj_carith_ror64",
+},
+
+traceerr = {
+[0]="error thrown or hook called during recording",
+"trace too short",
+"trace too long",
+"trace too deep",
+"too many snapshots",
+"blacklisted",
+"retry recording",
+"NYI: bytecode %s",
+"leaving loop in root trace",
+"inner loop in root trace",
+"loop unroll limit reached",
+"bad argument type",
+"JIT compilation disabled for function",
+"call unroll limit reached",
+"down-recursion, restarting",
+"NYI: unsupported variant of FastFunc %s",
+"NYI: return to lower frame",
+"store with nil or NaN key",
+"missing metamethod",
+"looping index lookup",
+"NYI: mixed sparse/dense table",
+"symbol not in cache",
+"NYI: unsupported C type conversion",
+"NYI: unsupported C function type",
+"guard would always fail",
+"too many PHIs",
+"persistent type instability",
+"failed to allocate mcode memory",
+"machine code too long",
+"hit mcode limit (retrying)",
+"too many spill slots",
+"inconsistent register allocation",
+"NYI: cannot assemble IR instruction %d",
+"NYI: PHI shuffling too complex",
+"NYI: register coalescing too complex",
+},
+
+ffnames = {
+[0]="Lua",
+"C",
+"assert",
+"type",
+"next",
+"pairs",
+"ipairs_aux",
+"ipairs",
+"getmetatable",
+"setmetatable",
+"getfenv",
+"setfenv",
+"rawget",
+"rawset",
+"rawequal",
+"unpack",
+"select",
+"tonumber",
+"tostring",
+"error",
+"pcall",
+"xpcall",
+"loadfile",
+"load",
+"loadstring",
+"dofile",
+"gcinfo",
+"collectgarbage",
+"newproxy",
+"print",
+"coroutine.status",
+"coroutine.running",
+"coroutine.isyieldable",
+"coroutine.create",
+"coroutine.yield",
+"coroutine.resume",
+"coroutine.wrap_aux",
+"coroutine.wrap",
+"math.abs",
+"math.floor",
+"math.ceil",
+"math.sqrt",
+"math.log10",
+"math.exp",
+"math.sin",
+"math.cos",
+"math.tan",
+"math.asin",
+"math.acos",
+"math.atan",
+"math.sinh",
+"math.cosh",
+"math.tanh",
+"math.frexp",
+"math.modf",
+"math.log",
+"math.atan2",
+"math.pow",
+"math.fmod",
+"math.ldexp",
+"math.min",
+"math.max",
+"math.random",
+"math.randomseed",
+"bit.tobit",
+"bit.bnot",
+"bit.bswap",
+"bit.lshift",
+"bit.rshift",
+"bit.arshift",
+"bit.rol",
+"bit.ror",
+"bit.band",
+"bit.bor",
+"bit.bxor",
+"bit.tohex",
+"string.byte",
+"string.char",
+"string.sub",
+"string.rep",
+"string.reverse",
+"string.lower",
+"string.upper",
+"string.dump",
+"string.find",
+"string.match",
+"string.gmatch_aux",
+"string.gmatch",
+"string.gsub",
+"string.format",
+"table.maxn",
+"table.insert",
+"table.concat",
+"table.sort",
+"table.new",
+"table.clear",
+"io.method.close",
+"io.method.read",
+"io.method.write",
+"io.method.flush",
+"io.method.seek",
+"io.method.setvbuf",
+"io.method.lines",
+"io.method.__gc",
+"io.method.__tostring",
+"io.open",
+"io.popen",
+"io.tmpfile",
+"io.close",
+"io.read",
+"io.write",
+"io.flush",
+"io.input",
+"io.output",
+"io.lines",
+"io.type",
+"os.execute",
+"os.remove",
+"os.rename",
+"os.tmpname",
+"os.getenv",
+"os.exit",
+"os.clock",
+"os.date",
+"os.time",
+"os.difftime",
+"os.setlocale",
+"debug.getregistry",
+"debug.getmetatable",
+"debug.setmetatable",
+"debug.getfenv",
+"debug.setfenv",
+"debug.getinfo",
+"debug.getlocal",
+"debug.setlocal",
+"debug.getupvalue",
+"debug.setupvalue",
+"debug.upvalueid",
+"debug.upvaluejoin",
+"debug.sethook",
+"debug.gethook",
+"debug.debug",
+"debug.traceback",
+"jit.on",
+"jit.off",
+"jit.flush",
+"jit.status",
+"jit.security",
+"jit.attach",
+"jit.util.funcinfo",
+"jit.util.funcbc",
+"jit.util.funck",
+"jit.util.funcuvname",
+"jit.util.traceinfo",
+"jit.util.traceir",
+"jit.util.tracek",
+"jit.util.tracesnap",
+"jit.util.tracemc",
+"jit.util.traceexitstub",
+"jit.util.ircalladdr",
+"jit.opt.start",
+"jit.profile.start",
+"jit.profile.stop",
+"jit.profile.dumpstack",
+"ffi.meta.__index",
+"ffi.meta.__newindex",
+"ffi.meta.__eq",
+"ffi.meta.__len",
+"ffi.meta.__lt",
+"ffi.meta.__le",
+"ffi.meta.__concat",
+"ffi.meta.__call",
+"ffi.meta.__add",
+"ffi.meta.__sub",
+"ffi.meta.__mul",
+"ffi.meta.__div",
+"ffi.meta.__mod",
+"ffi.meta.__pow",
+"ffi.meta.__unm",
+"ffi.meta.__tostring",
+"ffi.meta.__pairs",
+"ffi.meta.__ipairs",
+"ffi.clib.__index",
+"ffi.clib.__newindex",
+"ffi.clib.__gc",
+"ffi.callback.free",
+"ffi.callback.set",
+"ffi.cdef",
+"ffi.new",
+"ffi.cast",
+"ffi.typeof",
+"ffi.typeinfo",
+"ffi.istype",
+"ffi.sizeof",
+"ffi.alignof",
+"ffi.offsetof",
+"ffi.errno",
+"ffi.string",
+"ffi.copy",
+"ffi.fill",
+"ffi.abi",
+"ffi.metatype",
+"ffi.gc",
+"ffi.load",
+"buffer.method.free",
+"buffer.method.reset",
+"buffer.method.skip",
+"buffer.method.set",
+"buffer.method.put",
+"buffer.method.putf",
+"buffer.method.get",
+"buffer.method.putcdata",
+"buffer.method.reserve",
+"buffer.method.commit",
+"buffer.method.ref",
+"buffer.method.encode",
+"buffer.method.decode",
+"buffer.method.__gc",
+"buffer.method.__tostring",
+"buffer.method.__len",
+"buffer.new",
+"buffer.encode",
+"buffer.decode",
+},
+
+}
+
diff --git a/jit/zone.lua b/jit/zone.lua
new file mode 100644
index 0000000..0ed6935
--- /dev/null
+++ b/jit/zone.lua
@@ -0,0 +1,45 @@
+----------------------------------------------------------------------------
+-- LuaJIT profiler zones.
+--
+-- Copyright (C) 2005-2026 Mike Pall. All rights reserved.
+-- Released under the MIT license. See Copyright Notice in luajit.h
+----------------------------------------------------------------------------
+--
+-- This module implements a simple hierarchical zone model.
+--
+-- Example usage:
+--
+-- local zone = require("jit.zone")
+-- zone("AI")
+-- ...
+-- zone("A*")
+-- ...
+-- print(zone:get()) --> "A*"
+-- ...
+-- zone()
+-- ...
+-- print(zone:get()) --> "AI"
+-- ...
+-- zone()
+--
+----------------------------------------------------------------------------
+
+local remove = table.remove
+
+return setmetatable({
+ flush = function(t)
+ for i=#t,1,-1 do t[i] = nil end
+ end,
+ get = function(t)
+ return t[#t]
+ end
+}, {
+ __call = function(t, zone)
+ if zone then
+ t[#t+1] = zone
+ else
+ return (assert(remove(t), "empty zone stack"))
+ end
+ end
+})
+
diff --git a/lib/luajit/libluajit-5.1.dll.a b/lib/luajit/libluajit-5.1.dll.a
deleted file mode 100644
index cbcb3bd..0000000
Binary files a/lib/luajit/libluajit-5.1.dll.a and /dev/null differ
diff --git a/lib/luajit/libluajit.a b/lib/luajit/libluajit.a
new file mode 100644
index 0000000..34f40f2
Binary files /dev/null and b/lib/luajit/libluajit.a differ
diff --git a/lib/luajit/luajit b/lib/luajit/luajit
new file mode 100755
index 0000000..324c137
Binary files /dev/null and b/lib/luajit/luajit differ
diff --git a/lib/minhook/MinHook.x86.lib b/lib/minhook/MinHook.x86.lib
deleted file mode 100644
index 5f74a50..0000000
Binary files a/lib/minhook/MinHook.x86.lib and /dev/null differ
diff --git a/lib/minhook/MinHook.h b/lib/minhook/include/MinHook.h
similarity index 100%
rename from lib/minhook/MinHook.h
rename to lib/minhook/include/MinHook.h
diff --git a/lib/minhook/src/buffer.c b/lib/minhook/src/buffer.c
new file mode 100644
index 0000000..55412b0
--- /dev/null
+++ b/lib/minhook/src/buffer.c
@@ -0,0 +1,312 @@
+/*
+ * MinHook - The Minimalistic API Hooking Library for x64/x86
+ * Copyright (C) 2009-2017 Tsuda Kageyu.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include
+#include "buffer.h"
+
+// Size of each memory block. (= page size of VirtualAlloc)
+#define MEMORY_BLOCK_SIZE 0x1000
+
+// Max range for seeking a memory block. (= 1024MB)
+#define MAX_MEMORY_RANGE 0x40000000
+
+// Memory protection flags to check the executable address.
+#define PAGE_EXECUTE_FLAGS \
+ (PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY)
+
+// Memory slot.
+typedef struct _MEMORY_SLOT
+{
+ union
+ {
+ struct _MEMORY_SLOT *pNext;
+ UINT8 buffer[MEMORY_SLOT_SIZE];
+ };
+} MEMORY_SLOT, *PMEMORY_SLOT;
+
+// Memory block info. Placed at the head of each block.
+typedef struct _MEMORY_BLOCK
+{
+ struct _MEMORY_BLOCK *pNext;
+ PMEMORY_SLOT pFree; // First element of the free slot list.
+ UINT usedCount;
+} MEMORY_BLOCK, *PMEMORY_BLOCK;
+
+//-------------------------------------------------------------------------
+// Global Variables:
+//-------------------------------------------------------------------------
+
+// First element of the memory block list.
+PMEMORY_BLOCK g_pMemoryBlocks;
+
+//-------------------------------------------------------------------------
+VOID InitializeBuffer(VOID)
+{
+ // Nothing to do for now.
+}
+
+//-------------------------------------------------------------------------
+VOID UninitializeBuffer(VOID)
+{
+ PMEMORY_BLOCK pBlock = g_pMemoryBlocks;
+ g_pMemoryBlocks = NULL;
+
+ while (pBlock)
+ {
+ PMEMORY_BLOCK pNext = pBlock->pNext;
+ VirtualFree(pBlock, 0, MEM_RELEASE);
+ pBlock = pNext;
+ }
+}
+
+//-------------------------------------------------------------------------
+#if defined(_M_X64) || defined(__x86_64__)
+static LPVOID FindPrevFreeRegion(LPVOID pAddress, LPVOID pMinAddr, DWORD dwAllocationGranularity)
+{
+ ULONG_PTR tryAddr = (ULONG_PTR)pAddress;
+
+ // Round down to the allocation granularity.
+ tryAddr -= tryAddr % dwAllocationGranularity;
+
+ // Start from the previous allocation granularity multiply.
+ tryAddr -= dwAllocationGranularity;
+
+ while (tryAddr >= (ULONG_PTR)pMinAddr)
+ {
+ MEMORY_BASIC_INFORMATION mbi;
+ if (VirtualQuery((LPVOID)tryAddr, &mbi, sizeof(mbi)) == 0)
+ break;
+
+ if (mbi.State == MEM_FREE)
+ return (LPVOID)tryAddr;
+
+ if ((ULONG_PTR)mbi.AllocationBase < dwAllocationGranularity)
+ break;
+
+ tryAddr = (ULONG_PTR)mbi.AllocationBase - dwAllocationGranularity;
+ }
+
+ return NULL;
+}
+#endif
+
+//-------------------------------------------------------------------------
+#if defined(_M_X64) || defined(__x86_64__)
+static LPVOID FindNextFreeRegion(LPVOID pAddress, LPVOID pMaxAddr, DWORD dwAllocationGranularity)
+{
+ ULONG_PTR tryAddr = (ULONG_PTR)pAddress;
+
+ // Round down to the allocation granularity.
+ tryAddr -= tryAddr % dwAllocationGranularity;
+
+ // Start from the next allocation granularity multiply.
+ tryAddr += dwAllocationGranularity;
+
+ while (tryAddr <= (ULONG_PTR)pMaxAddr)
+ {
+ MEMORY_BASIC_INFORMATION mbi;
+ if (VirtualQuery((LPVOID)tryAddr, &mbi, sizeof(mbi)) == 0)
+ break;
+
+ if (mbi.State == MEM_FREE)
+ return (LPVOID)tryAddr;
+
+ tryAddr = (ULONG_PTR)mbi.BaseAddress + mbi.RegionSize;
+
+ // Round up to the next allocation granularity.
+ tryAddr += dwAllocationGranularity - 1;
+ tryAddr -= tryAddr % dwAllocationGranularity;
+ }
+
+ return NULL;
+}
+#endif
+
+//-------------------------------------------------------------------------
+static PMEMORY_BLOCK GetMemoryBlock(LPVOID pOrigin)
+{
+ PMEMORY_BLOCK pBlock;
+#if defined(_M_X64) || defined(__x86_64__)
+ ULONG_PTR minAddr;
+ ULONG_PTR maxAddr;
+
+ SYSTEM_INFO si;
+ GetSystemInfo(&si);
+ minAddr = (ULONG_PTR)si.lpMinimumApplicationAddress;
+ maxAddr = (ULONG_PTR)si.lpMaximumApplicationAddress;
+
+ // pOrigin ± 512MB
+ if ((ULONG_PTR)pOrigin > MAX_MEMORY_RANGE && minAddr < (ULONG_PTR)pOrigin - MAX_MEMORY_RANGE)
+ minAddr = (ULONG_PTR)pOrigin - MAX_MEMORY_RANGE;
+
+ if (maxAddr > (ULONG_PTR)pOrigin + MAX_MEMORY_RANGE)
+ maxAddr = (ULONG_PTR)pOrigin + MAX_MEMORY_RANGE;
+
+ // Make room for MEMORY_BLOCK_SIZE bytes.
+ maxAddr -= MEMORY_BLOCK_SIZE - 1;
+#endif
+
+ // Look the registered blocks for a reachable one.
+ for (pBlock = g_pMemoryBlocks; pBlock != NULL; pBlock = pBlock->pNext)
+ {
+#if defined(_M_X64) || defined(__x86_64__)
+ // Ignore the blocks too far.
+ if ((ULONG_PTR)pBlock < minAddr || (ULONG_PTR)pBlock >= maxAddr)
+ continue;
+#endif
+ // The block has at least one unused slot.
+ if (pBlock->pFree != NULL)
+ return pBlock;
+ }
+
+#if defined(_M_X64) || defined(__x86_64__)
+ // Alloc a new block above if not found.
+ {
+ LPVOID pAlloc = pOrigin;
+ while ((ULONG_PTR)pAlloc >= minAddr)
+ {
+ pAlloc = FindPrevFreeRegion(pAlloc, (LPVOID)minAddr, si.dwAllocationGranularity);
+ if (pAlloc == NULL)
+ break;
+
+ pBlock = (PMEMORY_BLOCK)VirtualAlloc(
+ pAlloc, MEMORY_BLOCK_SIZE, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
+ if (pBlock != NULL)
+ break;
+ }
+ }
+
+ // Alloc a new block below if not found.
+ if (pBlock == NULL)
+ {
+ LPVOID pAlloc = pOrigin;
+ while ((ULONG_PTR)pAlloc <= maxAddr)
+ {
+ pAlloc = FindNextFreeRegion(pAlloc, (LPVOID)maxAddr, si.dwAllocationGranularity);
+ if (pAlloc == NULL)
+ break;
+
+ pBlock = (PMEMORY_BLOCK)VirtualAlloc(
+ pAlloc, MEMORY_BLOCK_SIZE, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
+ if (pBlock != NULL)
+ break;
+ }
+ }
+#else
+ // In x86 mode, a memory block can be placed anywhere.
+ pBlock = (PMEMORY_BLOCK)VirtualAlloc(
+ NULL, MEMORY_BLOCK_SIZE, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
+#endif
+
+ if (pBlock != NULL)
+ {
+ // Build a linked list of all the slots.
+ PMEMORY_SLOT pSlot = (PMEMORY_SLOT)pBlock + 1;
+ pBlock->pFree = NULL;
+ pBlock->usedCount = 0;
+ do
+ {
+ pSlot->pNext = pBlock->pFree;
+ pBlock->pFree = pSlot;
+ pSlot++;
+ } while ((ULONG_PTR)pSlot - (ULONG_PTR)pBlock <= MEMORY_BLOCK_SIZE - MEMORY_SLOT_SIZE);
+
+ pBlock->pNext = g_pMemoryBlocks;
+ g_pMemoryBlocks = pBlock;
+ }
+
+ return pBlock;
+}
+
+//-------------------------------------------------------------------------
+LPVOID AllocateBuffer(LPVOID pOrigin)
+{
+ PMEMORY_SLOT pSlot;
+ PMEMORY_BLOCK pBlock = GetMemoryBlock(pOrigin);
+ if (pBlock == NULL)
+ return NULL;
+
+ // Remove an unused slot from the list.
+ pSlot = pBlock->pFree;
+ pBlock->pFree = pSlot->pNext;
+ pBlock->usedCount++;
+#ifdef _DEBUG
+ // Fill the slot with INT3 for debugging.
+ memset(pSlot, 0xCC, sizeof(MEMORY_SLOT));
+#endif
+ return pSlot;
+}
+
+//-------------------------------------------------------------------------
+VOID FreeBuffer(LPVOID pBuffer)
+{
+ PMEMORY_BLOCK pBlock = g_pMemoryBlocks;
+ PMEMORY_BLOCK pPrev = NULL;
+ ULONG_PTR pTargetBlock = ((ULONG_PTR)pBuffer / MEMORY_BLOCK_SIZE) * MEMORY_BLOCK_SIZE;
+
+ while (pBlock != NULL)
+ {
+ if ((ULONG_PTR)pBlock == pTargetBlock)
+ {
+ PMEMORY_SLOT pSlot = (PMEMORY_SLOT)pBuffer;
+#ifdef _DEBUG
+ // Clear the released slot for debugging.
+ memset(pSlot, 0x00, sizeof(MEMORY_SLOT));
+#endif
+ // Restore the released slot to the list.
+ pSlot->pNext = pBlock->pFree;
+ pBlock->pFree = pSlot;
+ pBlock->usedCount--;
+
+ // Free if unused.
+ if (pBlock->usedCount == 0)
+ {
+ if (pPrev)
+ pPrev->pNext = pBlock->pNext;
+ else
+ g_pMemoryBlocks = pBlock->pNext;
+
+ VirtualFree(pBlock, 0, MEM_RELEASE);
+ }
+
+ break;
+ }
+
+ pPrev = pBlock;
+ pBlock = pBlock->pNext;
+ }
+}
+
+//-------------------------------------------------------------------------
+BOOL IsExecutableAddress(LPVOID pAddress)
+{
+ MEMORY_BASIC_INFORMATION mi;
+ VirtualQuery(pAddress, &mi, sizeof(mi));
+
+ return (mi.State == MEM_COMMIT && (mi.Protect & PAGE_EXECUTE_FLAGS));
+}
diff --git a/lib/minhook/src/buffer.h b/lib/minhook/src/buffer.h
new file mode 100644
index 0000000..204d551
--- /dev/null
+++ b/lib/minhook/src/buffer.h
@@ -0,0 +1,42 @@
+/*
+ * MinHook - The Minimalistic API Hooking Library for x64/x86
+ * Copyright (C) 2009-2017 Tsuda Kageyu.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+// Size of each memory slot.
+#if defined(_M_X64) || defined(__x86_64__)
+ #define MEMORY_SLOT_SIZE 64
+#else
+ #define MEMORY_SLOT_SIZE 32
+#endif
+
+VOID InitializeBuffer(VOID);
+VOID UninitializeBuffer(VOID);
+LPVOID AllocateBuffer(LPVOID pOrigin);
+VOID FreeBuffer(LPVOID pBuffer);
+BOOL IsExecutableAddress(LPVOID pAddress);
diff --git a/lib/minhook/src/hde/hde32.c b/lib/minhook/src/hde/hde32.c
new file mode 100644
index 0000000..eb6af9b
--- /dev/null
+++ b/lib/minhook/src/hde/hde32.c
@@ -0,0 +1,324 @@
+/*
+ * Hacker Disassembler Engine 32 C
+ * Copyright (c) 2008-2009, Vyacheslav Patkov.
+ * All rights reserved.
+ *
+ */
+
+#if defined(_M_IX86) || defined(__i386__)
+
+#include
+#include "hde32.h"
+#include "table32.h"
+
+unsigned int hde32_disasm(const void *code, hde32s *hs)
+{
+ uint8_t x, c, *p = (uint8_t *)code, cflags, opcode, pref = 0;
+ uint8_t *ht = hde32_table, m_mod, m_reg, m_rm, disp_size = 0;
+
+ memset(hs, 0, sizeof(hde32s));
+
+ for (x = 16; x; x--)
+ switch (c = *p++) {
+ case 0xf3:
+ hs->p_rep = c;
+ pref |= PRE_F3;
+ break;
+ case 0xf2:
+ hs->p_rep = c;
+ pref |= PRE_F2;
+ break;
+ case 0xf0:
+ hs->p_lock = c;
+ pref |= PRE_LOCK;
+ break;
+ case 0x26: case 0x2e: case 0x36:
+ case 0x3e: case 0x64: case 0x65:
+ hs->p_seg = c;
+ pref |= PRE_SEG;
+ break;
+ case 0x66:
+ hs->p_66 = c;
+ pref |= PRE_66;
+ break;
+ case 0x67:
+ hs->p_67 = c;
+ pref |= PRE_67;
+ break;
+ default:
+ goto pref_done;
+ }
+ pref_done:
+
+ hs->flags = (uint32_t)pref << 23;
+
+ if (!pref)
+ pref |= PRE_NONE;
+
+ if ((hs->opcode = c) == 0x0f) {
+ hs->opcode2 = c = *p++;
+ ht += DELTA_OPCODES;
+ } else if (c >= 0xa0 && c <= 0xa3) {
+ if (pref & PRE_67)
+ pref |= PRE_66;
+ else
+ pref &= ~PRE_66;
+ }
+
+ opcode = c;
+ cflags = ht[ht[opcode / 4] + (opcode % 4)];
+
+ if (cflags == C_ERROR) {
+ hs->flags |= F_ERROR | F_ERROR_OPCODE;
+ cflags = 0;
+ if ((opcode & -3) == 0x24)
+ cflags++;
+ }
+
+ x = 0;
+ if (cflags & C_GROUP) {
+ uint16_t t;
+ t = *(uint16_t *)(ht + (cflags & 0x7f));
+ cflags = (uint8_t)t;
+ x = (uint8_t)(t >> 8);
+ }
+
+ if (hs->opcode2) {
+ ht = hde32_table + DELTA_PREFIXES;
+ if (ht[ht[opcode / 4] + (opcode % 4)] & pref)
+ hs->flags |= F_ERROR | F_ERROR_OPCODE;
+ }
+
+ if (cflags & C_MODRM) {
+ hs->flags |= F_MODRM;
+ hs->modrm = c = *p++;
+ hs->modrm_mod = m_mod = c >> 6;
+ hs->modrm_rm = m_rm = c & 7;
+ hs->modrm_reg = m_reg = (c & 0x3f) >> 3;
+
+ if (x && ((x << m_reg) & 0x80))
+ hs->flags |= F_ERROR | F_ERROR_OPCODE;
+
+ if (!hs->opcode2 && opcode >= 0xd9 && opcode <= 0xdf) {
+ uint8_t t = opcode - 0xd9;
+ if (m_mod == 3) {
+ ht = hde32_table + DELTA_FPU_MODRM + t*8;
+ t = ht[m_reg] << m_rm;
+ } else {
+ ht = hde32_table + DELTA_FPU_REG;
+ t = ht[t] << m_reg;
+ }
+ if (t & 0x80)
+ hs->flags |= F_ERROR | F_ERROR_OPCODE;
+ }
+
+ if (pref & PRE_LOCK) {
+ if (m_mod == 3) {
+ hs->flags |= F_ERROR | F_ERROR_LOCK;
+ } else {
+ uint8_t *table_end, op = opcode;
+ if (hs->opcode2) {
+ ht = hde32_table + DELTA_OP2_LOCK_OK;
+ table_end = ht + DELTA_OP_ONLY_MEM - DELTA_OP2_LOCK_OK;
+ } else {
+ ht = hde32_table + DELTA_OP_LOCK_OK;
+ table_end = ht + DELTA_OP2_LOCK_OK - DELTA_OP_LOCK_OK;
+ op &= -2;
+ }
+ for (; ht != table_end; ht++)
+ if (*ht++ == op) {
+ if (!((*ht << m_reg) & 0x80))
+ goto no_lock_error;
+ else
+ break;
+ }
+ hs->flags |= F_ERROR | F_ERROR_LOCK;
+ no_lock_error:
+ ;
+ }
+ }
+
+ if (hs->opcode2) {
+ switch (opcode) {
+ case 0x20: case 0x22:
+ m_mod = 3;
+ if (m_reg > 4 || m_reg == 1)
+ goto error_operand;
+ else
+ goto no_error_operand;
+ case 0x21: case 0x23:
+ m_mod = 3;
+ if (m_reg == 4 || m_reg == 5)
+ goto error_operand;
+ else
+ goto no_error_operand;
+ }
+ } else {
+ switch (opcode) {
+ case 0x8c:
+ if (m_reg > 5)
+ goto error_operand;
+ else
+ goto no_error_operand;
+ case 0x8e:
+ if (m_reg == 1 || m_reg > 5)
+ goto error_operand;
+ else
+ goto no_error_operand;
+ }
+ }
+
+ if (m_mod == 3) {
+ uint8_t *table_end;
+ if (hs->opcode2) {
+ ht = hde32_table + DELTA_OP2_ONLY_MEM;
+ table_end = ht + sizeof(hde32_table) - DELTA_OP2_ONLY_MEM;
+ } else {
+ ht = hde32_table + DELTA_OP_ONLY_MEM;
+ table_end = ht + DELTA_OP2_ONLY_MEM - DELTA_OP_ONLY_MEM;
+ }
+ for (; ht != table_end; ht += 2)
+ if (*ht++ == opcode) {
+ if ((*ht++ & pref) && !((*ht << m_reg) & 0x80))
+ goto error_operand;
+ else
+ break;
+ }
+ goto no_error_operand;
+ } else if (hs->opcode2) {
+ switch (opcode) {
+ case 0x50: case 0xd7: case 0xf7:
+ if (pref & (PRE_NONE | PRE_66))
+ goto error_operand;
+ break;
+ case 0xd6:
+ if (pref & (PRE_F2 | PRE_F3))
+ goto error_operand;
+ break;
+ case 0xc5:
+ goto error_operand;
+ }
+ goto no_error_operand;
+ } else
+ goto no_error_operand;
+
+ error_operand:
+ hs->flags |= F_ERROR | F_ERROR_OPERAND;
+ no_error_operand:
+
+ c = *p++;
+ if (m_reg <= 1) {
+ if (opcode == 0xf6)
+ cflags |= C_IMM8;
+ else if (opcode == 0xf7)
+ cflags |= C_IMM_P66;
+ }
+
+ switch (m_mod) {
+ case 0:
+ if (pref & PRE_67) {
+ if (m_rm == 6)
+ disp_size = 2;
+ } else
+ if (m_rm == 5)
+ disp_size = 4;
+ break;
+ case 1:
+ disp_size = 1;
+ break;
+ case 2:
+ disp_size = 2;
+ if (!(pref & PRE_67))
+ disp_size <<= 1;
+ break;
+ }
+
+ if (m_mod != 3 && m_rm == 4 && !(pref & PRE_67)) {
+ hs->flags |= F_SIB;
+ p++;
+ hs->sib = c;
+ hs->sib_scale = c >> 6;
+ hs->sib_index = (c & 0x3f) >> 3;
+ if ((hs->sib_base = c & 7) == 5 && !(m_mod & 1))
+ disp_size = 4;
+ }
+
+ p--;
+ switch (disp_size) {
+ case 1:
+ hs->flags |= F_DISP8;
+ hs->disp.disp8 = *p;
+ break;
+ case 2:
+ hs->flags |= F_DISP16;
+ hs->disp.disp16 = *(uint16_t *)p;
+ break;
+ case 4:
+ hs->flags |= F_DISP32;
+ hs->disp.disp32 = *(uint32_t *)p;
+ break;
+ }
+ p += disp_size;
+ } else if (pref & PRE_LOCK)
+ hs->flags |= F_ERROR | F_ERROR_LOCK;
+
+ if (cflags & C_IMM_P66) {
+ if (cflags & C_REL32) {
+ if (pref & PRE_66) {
+ hs->flags |= F_IMM16 | F_RELATIVE;
+ hs->imm.imm16 = *(uint16_t *)p;
+ p += 2;
+ goto disasm_done;
+ }
+ goto rel32_ok;
+ }
+ if (pref & PRE_66) {
+ hs->flags |= F_IMM16;
+ hs->imm.imm16 = *(uint16_t *)p;
+ p += 2;
+ } else {
+ hs->flags |= F_IMM32;
+ hs->imm.imm32 = *(uint32_t *)p;
+ p += 4;
+ }
+ }
+
+ if (cflags & C_IMM16) {
+ if (hs->flags & F_IMM32) {
+ hs->flags |= F_IMM16;
+ hs->disp.disp16 = *(uint16_t *)p;
+ } else if (hs->flags & F_IMM16) {
+ hs->flags |= F_2IMM16;
+ hs->disp.disp16 = *(uint16_t *)p;
+ } else {
+ hs->flags |= F_IMM16;
+ hs->imm.imm16 = *(uint16_t *)p;
+ }
+ p += 2;
+ }
+ if (cflags & C_IMM8) {
+ hs->flags |= F_IMM8;
+ hs->imm.imm8 = *p++;
+ }
+
+ if (cflags & C_REL32) {
+ rel32_ok:
+ hs->flags |= F_IMM32 | F_RELATIVE;
+ hs->imm.imm32 = *(uint32_t *)p;
+ p += 4;
+ } else if (cflags & C_REL8) {
+ hs->flags |= F_IMM8 | F_RELATIVE;
+ hs->imm.imm8 = *p++;
+ }
+
+ disasm_done:
+
+ if ((hs->len = (uint8_t)(p-(uint8_t *)code)) > 15) {
+ hs->flags |= F_ERROR | F_ERROR_LENGTH;
+ hs->len = 15;
+ }
+
+ return (unsigned int)hs->len;
+}
+
+#endif // defined(_M_IX86) || defined(__i386__)
diff --git a/lib/minhook/src/hde/hde32.h b/lib/minhook/src/hde/hde32.h
new file mode 100644
index 0000000..1112450
--- /dev/null
+++ b/lib/minhook/src/hde/hde32.h
@@ -0,0 +1,105 @@
+/*
+ * Hacker Disassembler Engine 32
+ * Copyright (c) 2006-2009, Vyacheslav Patkov.
+ * All rights reserved.
+ *
+ * hde32.h: C/C++ header file
+ *
+ */
+
+#ifndef _HDE32_H_
+#define _HDE32_H_
+
+/* stdint.h - C99 standard header
+ * http://en.wikipedia.org/wiki/stdint.h
+ *
+ * if your compiler doesn't contain "stdint.h" header (for
+ * example, Microsoft Visual C++), you can download file:
+ * http://www.azillionmonkeys.com/qed/pstdint.h
+ * and change next line to:
+ * #include "pstdint.h"
+ */
+#include "pstdint.h"
+
+#define F_MODRM 0x00000001
+#define F_SIB 0x00000002
+#define F_IMM8 0x00000004
+#define F_IMM16 0x00000008
+#define F_IMM32 0x00000010
+#define F_DISP8 0x00000020
+#define F_DISP16 0x00000040
+#define F_DISP32 0x00000080
+#define F_RELATIVE 0x00000100
+#define F_2IMM16 0x00000800
+#define F_ERROR 0x00001000
+#define F_ERROR_OPCODE 0x00002000
+#define F_ERROR_LENGTH 0x00004000
+#define F_ERROR_LOCK 0x00008000
+#define F_ERROR_OPERAND 0x00010000
+#define F_PREFIX_REPNZ 0x01000000
+#define F_PREFIX_REPX 0x02000000
+#define F_PREFIX_REP 0x03000000
+#define F_PREFIX_66 0x04000000
+#define F_PREFIX_67 0x08000000
+#define F_PREFIX_LOCK 0x10000000
+#define F_PREFIX_SEG 0x20000000
+#define F_PREFIX_ANY 0x3f000000
+
+#define PREFIX_SEGMENT_CS 0x2e
+#define PREFIX_SEGMENT_SS 0x36
+#define PREFIX_SEGMENT_DS 0x3e
+#define PREFIX_SEGMENT_ES 0x26
+#define PREFIX_SEGMENT_FS 0x64
+#define PREFIX_SEGMENT_GS 0x65
+#define PREFIX_LOCK 0xf0
+#define PREFIX_REPNZ 0xf2
+#define PREFIX_REPX 0xf3
+#define PREFIX_OPERAND_SIZE 0x66
+#define PREFIX_ADDRESS_SIZE 0x67
+
+#pragma pack(push,1)
+
+typedef struct {
+ uint8_t len;
+ uint8_t p_rep;
+ uint8_t p_lock;
+ uint8_t p_seg;
+ uint8_t p_66;
+ uint8_t p_67;
+ uint8_t opcode;
+ uint8_t opcode2;
+ uint8_t modrm;
+ uint8_t modrm_mod;
+ uint8_t modrm_reg;
+ uint8_t modrm_rm;
+ uint8_t sib;
+ uint8_t sib_scale;
+ uint8_t sib_index;
+ uint8_t sib_base;
+ union {
+ uint8_t imm8;
+ uint16_t imm16;
+ uint32_t imm32;
+ } imm;
+ union {
+ uint8_t disp8;
+ uint16_t disp16;
+ uint32_t disp32;
+ } disp;
+ uint32_t flags;
+} hde32s;
+
+#pragma pack(pop)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* __cdecl */
+unsigned int hde32_disasm(const void *code, hde32s *hs);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _HDE32_H_ */
diff --git a/lib/minhook/src/hde/hde64.c b/lib/minhook/src/hde/hde64.c
new file mode 100644
index 0000000..ce773b1
--- /dev/null
+++ b/lib/minhook/src/hde/hde64.c
@@ -0,0 +1,335 @@
+/*
+ * Hacker Disassembler Engine 64 C
+ * Copyright (c) 2008-2009, Vyacheslav Patkov.
+ * All rights reserved.
+ *
+ */
+
+#if defined(_M_X64) || defined(__x86_64__)
+
+#include
+#include "hde64.h"
+#include "table64.h"
+
+unsigned int hde64_disasm(const void *code, hde64s *hs)
+{
+ uint8_t x, c, *p = (uint8_t *)code, cflags, opcode, pref = 0;
+ uint8_t *ht = hde64_table, m_mod, m_reg, m_rm, disp_size = 0;
+ uint8_t op64 = 0;
+
+ memset(hs, 0, sizeof(hde64s));
+
+ for (x = 16; x; x--)
+ switch (c = *p++) {
+ case 0xf3:
+ hs->p_rep = c;
+ pref |= PRE_F3;
+ break;
+ case 0xf2:
+ hs->p_rep = c;
+ pref |= PRE_F2;
+ break;
+ case 0xf0:
+ hs->p_lock = c;
+ pref |= PRE_LOCK;
+ break;
+ case 0x26: case 0x2e: case 0x36:
+ case 0x3e: case 0x64: case 0x65:
+ hs->p_seg = c;
+ pref |= PRE_SEG;
+ break;
+ case 0x66:
+ hs->p_66 = c;
+ pref |= PRE_66;
+ break;
+ case 0x67:
+ hs->p_67 = c;
+ pref |= PRE_67;
+ break;
+ default:
+ goto pref_done;
+ }
+ pref_done:
+
+ hs->flags = (uint32_t)pref << 23;
+
+ if (!pref)
+ pref |= PRE_NONE;
+
+ if ((c & 0xf0) == 0x40) {
+ hs->flags |= F_PREFIX_REX;
+ if ((hs->rex_w = (c & 0xf) >> 3) && (*p & 0xf8) == 0xb8)
+ op64++;
+ hs->rex_r = (c & 7) >> 2;
+ hs->rex_x = (c & 3) >> 1;
+ hs->rex_b = c & 1;
+ if (((c = *p++) & 0xf0) == 0x40) {
+ opcode = c;
+ goto error_opcode;
+ }
+ }
+
+ if ((hs->opcode = c) == 0x0f) {
+ hs->opcode2 = c = *p++;
+ ht += DELTA_OPCODES;
+ } else if (c >= 0xa0 && c <= 0xa3) {
+ op64++;
+ if (pref & PRE_67)
+ pref |= PRE_66;
+ else
+ pref &= ~PRE_66;
+ }
+
+ opcode = c;
+ cflags = ht[ht[opcode / 4] + (opcode % 4)];
+
+ if (cflags == C_ERROR) {
+ error_opcode:
+ hs->flags |= F_ERROR | F_ERROR_OPCODE;
+ cflags = 0;
+ if ((opcode & -3) == 0x24)
+ cflags++;
+ }
+
+ x = 0;
+ if (cflags & C_GROUP) {
+ uint16_t t;
+ t = *(uint16_t *)(ht + (cflags & 0x7f));
+ cflags = (uint8_t)t;
+ x = (uint8_t)(t >> 8);
+ }
+
+ if (hs->opcode2) {
+ ht = hde64_table + DELTA_PREFIXES;
+ if (ht[ht[opcode / 4] + (opcode % 4)] & pref)
+ hs->flags |= F_ERROR | F_ERROR_OPCODE;
+ }
+
+ if (cflags & C_MODRM) {
+ hs->flags |= F_MODRM;
+ hs->modrm = c = *p++;
+ hs->modrm_mod = m_mod = c >> 6;
+ hs->modrm_rm = m_rm = c & 7;
+ hs->modrm_reg = m_reg = (c & 0x3f) >> 3;
+
+ if (x && ((x << m_reg) & 0x80))
+ hs->flags |= F_ERROR | F_ERROR_OPCODE;
+
+ if (!hs->opcode2 && opcode >= 0xd9 && opcode <= 0xdf) {
+ uint8_t t = opcode - 0xd9;
+ if (m_mod == 3) {
+ ht = hde64_table + DELTA_FPU_MODRM + t*8;
+ t = ht[m_reg] << m_rm;
+ } else {
+ ht = hde64_table + DELTA_FPU_REG;
+ t = ht[t] << m_reg;
+ }
+ if (t & 0x80)
+ hs->flags |= F_ERROR | F_ERROR_OPCODE;
+ }
+
+ if (pref & PRE_LOCK) {
+ if (m_mod == 3) {
+ hs->flags |= F_ERROR | F_ERROR_LOCK;
+ } else {
+ uint8_t *table_end, op = opcode;
+ if (hs->opcode2) {
+ ht = hde64_table + DELTA_OP2_LOCK_OK;
+ table_end = ht + DELTA_OP_ONLY_MEM - DELTA_OP2_LOCK_OK;
+ } else {
+ ht = hde64_table + DELTA_OP_LOCK_OK;
+ table_end = ht + DELTA_OP2_LOCK_OK - DELTA_OP_LOCK_OK;
+ op &= -2;
+ }
+ for (; ht != table_end; ht++)
+ if (*ht++ == op) {
+ if (!((*ht << m_reg) & 0x80))
+ goto no_lock_error;
+ else
+ break;
+ }
+ hs->flags |= F_ERROR | F_ERROR_LOCK;
+ no_lock_error:
+ ;
+ }
+ }
+
+ if (hs->opcode2) {
+ switch (opcode) {
+ case 0x20: case 0x22:
+ m_mod = 3;
+ if (m_reg > 4 || m_reg == 1)
+ goto error_operand;
+ else
+ goto no_error_operand;
+ case 0x21: case 0x23:
+ m_mod = 3;
+ if (m_reg == 4 || m_reg == 5)
+ goto error_operand;
+ else
+ goto no_error_operand;
+ }
+ } else {
+ switch (opcode) {
+ case 0x8c:
+ if (m_reg > 5)
+ goto error_operand;
+ else
+ goto no_error_operand;
+ case 0x8e:
+ if (m_reg == 1 || m_reg > 5)
+ goto error_operand;
+ else
+ goto no_error_operand;
+ }
+ }
+
+ if (m_mod == 3) {
+ uint8_t *table_end;
+ if (hs->opcode2) {
+ ht = hde64_table + DELTA_OP2_ONLY_MEM;
+ table_end = ht + sizeof(hde64_table) - DELTA_OP2_ONLY_MEM;
+ } else {
+ ht = hde64_table + DELTA_OP_ONLY_MEM;
+ table_end = ht + DELTA_OP2_ONLY_MEM - DELTA_OP_ONLY_MEM;
+ }
+ for (; ht != table_end; ht += 2)
+ if (*ht++ == opcode) {
+ if ((*ht++ & pref) && !((*ht << m_reg) & 0x80))
+ goto error_operand;
+ else
+ break;
+ }
+ goto no_error_operand;
+ } else if (hs->opcode2) {
+ switch (opcode) {
+ case 0x50: case 0xd7: case 0xf7:
+ if (pref & (PRE_NONE | PRE_66))
+ goto error_operand;
+ break;
+ case 0xd6:
+ if (pref & (PRE_F2 | PRE_F3))
+ goto error_operand;
+ break;
+ case 0xc5:
+ goto error_operand;
+ }
+ goto no_error_operand;
+ } else
+ goto no_error_operand;
+
+ error_operand:
+ hs->flags |= F_ERROR | F_ERROR_OPERAND;
+ no_error_operand:
+
+ c = *p++;
+ if (m_reg <= 1) {
+ if (opcode == 0xf6)
+ cflags |= C_IMM8;
+ else if (opcode == 0xf7)
+ cflags |= C_IMM_P66;
+ }
+
+ switch (m_mod) {
+ case 0:
+ if (pref & PRE_67) {
+ if (m_rm == 6)
+ disp_size = 2;
+ } else
+ if (m_rm == 5)
+ disp_size = 4;
+ break;
+ case 1:
+ disp_size = 1;
+ break;
+ case 2:
+ disp_size = 2;
+ if (!(pref & PRE_67))
+ disp_size <<= 1;
+ break;
+ }
+
+ if (m_mod != 3 && m_rm == 4) {
+ hs->flags |= F_SIB;
+ p++;
+ hs->sib = c;
+ hs->sib_scale = c >> 6;
+ hs->sib_index = (c & 0x3f) >> 3;
+ if ((hs->sib_base = c & 7) == 5 && !(m_mod & 1))
+ disp_size = 4;
+ }
+
+ p--;
+ switch (disp_size) {
+ case 1:
+ hs->flags |= F_DISP8;
+ hs->disp.disp8 = *p;
+ break;
+ case 2:
+ hs->flags |= F_DISP16;
+ hs->disp.disp16 = *(uint16_t *)p;
+ break;
+ case 4:
+ hs->flags |= F_DISP32;
+ hs->disp.disp32 = *(uint32_t *)p;
+ break;
+ }
+ p += disp_size;
+ } else if (pref & PRE_LOCK)
+ hs->flags |= F_ERROR | F_ERROR_LOCK;
+
+ if (cflags & C_IMM_P66) {
+ if (cflags & C_REL32) {
+ if (pref & PRE_66) {
+ hs->flags |= F_IMM16 | F_RELATIVE;
+ hs->imm.imm16 = *(uint16_t *)p;
+ p += 2;
+ goto disasm_done;
+ }
+ goto rel32_ok;
+ }
+ if (op64) {
+ hs->flags |= F_IMM64;
+ hs->imm.imm64 = *(uint64_t *)p;
+ p += 8;
+ } else if (!(pref & PRE_66)) {
+ hs->flags |= F_IMM32;
+ hs->imm.imm32 = *(uint32_t *)p;
+ p += 4;
+ } else
+ goto imm16_ok;
+ }
+
+
+ if (cflags & C_IMM16) {
+ imm16_ok:
+ hs->flags |= F_IMM16;
+ hs->imm.imm16 = *(uint16_t *)p;
+ p += 2;
+ }
+ if (cflags & C_IMM8) {
+ hs->flags |= F_IMM8;
+ hs->imm.imm8 = *p++;
+ }
+
+ if (cflags & C_REL32) {
+ rel32_ok:
+ hs->flags |= F_IMM32 | F_RELATIVE;
+ hs->imm.imm32 = *(uint32_t *)p;
+ p += 4;
+ } else if (cflags & C_REL8) {
+ hs->flags |= F_IMM8 | F_RELATIVE;
+ hs->imm.imm8 = *p++;
+ }
+
+ disasm_done:
+
+ if ((hs->len = (uint8_t)(p-(uint8_t *)code)) > 15) {
+ hs->flags |= F_ERROR | F_ERROR_LENGTH;
+ hs->len = 15;
+ }
+
+ return (unsigned int)hs->len;
+}
+
+#endif // defined(_M_X64) || defined(__x86_64__)
diff --git a/lib/minhook/src/hde/hde64.h b/lib/minhook/src/hde/hde64.h
new file mode 100644
index 0000000..ecbf4df
--- /dev/null
+++ b/lib/minhook/src/hde/hde64.h
@@ -0,0 +1,112 @@
+/*
+ * Hacker Disassembler Engine 64
+ * Copyright (c) 2008-2009, Vyacheslav Patkov.
+ * All rights reserved.
+ *
+ * hde64.h: C/C++ header file
+ *
+ */
+
+#ifndef _HDE64_H_
+#define _HDE64_H_
+
+/* stdint.h - C99 standard header
+ * http://en.wikipedia.org/wiki/stdint.h
+ *
+ * if your compiler doesn't contain "stdint.h" header (for
+ * example, Microsoft Visual C++), you can download file:
+ * http://www.azillionmonkeys.com/qed/pstdint.h
+ * and change next line to:
+ * #include "pstdint.h"
+ */
+#include "pstdint.h"
+
+#define F_MODRM 0x00000001
+#define F_SIB 0x00000002
+#define F_IMM8 0x00000004
+#define F_IMM16 0x00000008
+#define F_IMM32 0x00000010
+#define F_IMM64 0x00000020
+#define F_DISP8 0x00000040
+#define F_DISP16 0x00000080
+#define F_DISP32 0x00000100
+#define F_RELATIVE 0x00000200
+#define F_ERROR 0x00001000
+#define F_ERROR_OPCODE 0x00002000
+#define F_ERROR_LENGTH 0x00004000
+#define F_ERROR_LOCK 0x00008000
+#define F_ERROR_OPERAND 0x00010000
+#define F_PREFIX_REPNZ 0x01000000
+#define F_PREFIX_REPX 0x02000000
+#define F_PREFIX_REP 0x03000000
+#define F_PREFIX_66 0x04000000
+#define F_PREFIX_67 0x08000000
+#define F_PREFIX_LOCK 0x10000000
+#define F_PREFIX_SEG 0x20000000
+#define F_PREFIX_REX 0x40000000
+#define F_PREFIX_ANY 0x7f000000
+
+#define PREFIX_SEGMENT_CS 0x2e
+#define PREFIX_SEGMENT_SS 0x36
+#define PREFIX_SEGMENT_DS 0x3e
+#define PREFIX_SEGMENT_ES 0x26
+#define PREFIX_SEGMENT_FS 0x64
+#define PREFIX_SEGMENT_GS 0x65
+#define PREFIX_LOCK 0xf0
+#define PREFIX_REPNZ 0xf2
+#define PREFIX_REPX 0xf3
+#define PREFIX_OPERAND_SIZE 0x66
+#define PREFIX_ADDRESS_SIZE 0x67
+
+#pragma pack(push,1)
+
+typedef struct {
+ uint8_t len;
+ uint8_t p_rep;
+ uint8_t p_lock;
+ uint8_t p_seg;
+ uint8_t p_66;
+ uint8_t p_67;
+ uint8_t rex;
+ uint8_t rex_w;
+ uint8_t rex_r;
+ uint8_t rex_x;
+ uint8_t rex_b;
+ uint8_t opcode;
+ uint8_t opcode2;
+ uint8_t modrm;
+ uint8_t modrm_mod;
+ uint8_t modrm_reg;
+ uint8_t modrm_rm;
+ uint8_t sib;
+ uint8_t sib_scale;
+ uint8_t sib_index;
+ uint8_t sib_base;
+ union {
+ uint8_t imm8;
+ uint16_t imm16;
+ uint32_t imm32;
+ uint64_t imm64;
+ } imm;
+ union {
+ uint8_t disp8;
+ uint16_t disp16;
+ uint32_t disp32;
+ } disp;
+ uint32_t flags;
+} hde64s;
+
+#pragma pack(pop)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* __cdecl */
+unsigned int hde64_disasm(const void *code, hde64s *hs);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _HDE64_H_ */
diff --git a/lib/minhook/src/hde/pstdint.h b/lib/minhook/src/hde/pstdint.h
new file mode 100644
index 0000000..84d82a0
--- /dev/null
+++ b/lib/minhook/src/hde/pstdint.h
@@ -0,0 +1,39 @@
+/*
+ * MinHook - The Minimalistic API Hooking Library for x64/x86
+ * Copyright (C) 2009-2017 Tsuda Kageyu. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include
+
+// Integer types for HDE.
+typedef INT8 int8_t;
+typedef INT16 int16_t;
+typedef INT32 int32_t;
+typedef INT64 int64_t;
+typedef UINT8 uint8_t;
+typedef UINT16 uint16_t;
+typedef UINT32 uint32_t;
+typedef UINT64 uint64_t;
diff --git a/lib/minhook/src/hde/table32.h b/lib/minhook/src/hde/table32.h
new file mode 100644
index 0000000..7b3e12e
--- /dev/null
+++ b/lib/minhook/src/hde/table32.h
@@ -0,0 +1,73 @@
+/*
+ * Hacker Disassembler Engine 32 C
+ * Copyright (c) 2008-2009, Vyacheslav Patkov.
+ * All rights reserved.
+ *
+ */
+
+#define C_NONE 0x00
+#define C_MODRM 0x01
+#define C_IMM8 0x02
+#define C_IMM16 0x04
+#define C_IMM_P66 0x10
+#define C_REL8 0x20
+#define C_REL32 0x40
+#define C_GROUP 0x80
+#define C_ERROR 0xff
+
+#define PRE_ANY 0x00
+#define PRE_NONE 0x01
+#define PRE_F2 0x02
+#define PRE_F3 0x04
+#define PRE_66 0x08
+#define PRE_67 0x10
+#define PRE_LOCK 0x20
+#define PRE_SEG 0x40
+#define PRE_ALL 0xff
+
+#define DELTA_OPCODES 0x4a
+#define DELTA_FPU_REG 0xf1
+#define DELTA_FPU_MODRM 0xf8
+#define DELTA_PREFIXES 0x130
+#define DELTA_OP_LOCK_OK 0x1a1
+#define DELTA_OP2_LOCK_OK 0x1b9
+#define DELTA_OP_ONLY_MEM 0x1cb
+#define DELTA_OP2_ONLY_MEM 0x1da
+
+unsigned char hde32_table[] = {
+ 0xa3,0xa8,0xa3,0xa8,0xa3,0xa8,0xa3,0xa8,0xa3,0xa8,0xa3,0xa8,0xa3,0xa8,0xa3,
+ 0xa8,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xac,0xaa,0xb2,0xaa,0x9f,0x9f,
+ 0x9f,0x9f,0xb5,0xa3,0xa3,0xa4,0xaa,0xaa,0xba,0xaa,0x96,0xaa,0xa8,0xaa,0xc3,
+ 0xc3,0x96,0x96,0xb7,0xae,0xd6,0xbd,0xa3,0xc5,0xa3,0xa3,0x9f,0xc3,0x9c,0xaa,
+ 0xaa,0xac,0xaa,0xbf,0x03,0x7f,0x11,0x7f,0x01,0x7f,0x01,0x3f,0x01,0x01,0x90,
+ 0x82,0x7d,0x97,0x59,0x59,0x59,0x59,0x59,0x7f,0x59,0x59,0x60,0x7d,0x7f,0x7f,
+ 0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x9a,0x88,0x7d,
+ 0x59,0x50,0x50,0x50,0x50,0x59,0x59,0x59,0x59,0x61,0x94,0x61,0x9e,0x59,0x59,
+ 0x85,0x59,0x92,0xa3,0x60,0x60,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,
+ 0x59,0x59,0x9f,0x01,0x03,0x01,0x04,0x03,0xd5,0x03,0xcc,0x01,0xbc,0x03,0xf0,
+ 0x10,0x10,0x10,0x10,0x50,0x50,0x50,0x50,0x14,0x20,0x20,0x20,0x20,0x01,0x01,
+ 0x01,0x01,0xc4,0x02,0x10,0x00,0x00,0x00,0x00,0x01,0x01,0xc0,0xc2,0x10,0x11,
+ 0x02,0x03,0x11,0x03,0x03,0x04,0x00,0x00,0x14,0x00,0x02,0x00,0x00,0xc6,0xc8,
+ 0x02,0x02,0x02,0x02,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0xff,0xca,
+ 0x01,0x01,0x01,0x00,0x06,0x00,0x04,0x00,0xc0,0xc2,0x01,0x01,0x03,0x01,0xff,
+ 0xff,0x01,0x00,0x03,0xc4,0xc4,0xc6,0x03,0x01,0x01,0x01,0xff,0x03,0x03,0x03,
+ 0xc8,0x40,0x00,0x0a,0x00,0x04,0x00,0x00,0x00,0x00,0x7f,0x00,0x33,0x01,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0xff,0xbf,0xff,0xff,0x00,0x00,0x00,0x00,0x07,0x00,
+ 0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0xff,0xff,0x00,0x00,0x00,0xbf,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x7f,0x00,0x00,0xff,0x4a,0x4a,0x4a,0x4a,0x4b,0x52,0x4a,0x4a,0x4a,0x4a,0x4f,
+ 0x4c,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a,0x55,0x45,0x40,0x4a,0x4a,0x4a,
+ 0x45,0x59,0x4d,0x46,0x4a,0x5d,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a,
+ 0x4a,0x4a,0x4a,0x4a,0x4a,0x61,0x63,0x67,0x4e,0x4a,0x4a,0x6b,0x6d,0x4a,0x4a,
+ 0x45,0x6d,0x4a,0x4a,0x44,0x45,0x4a,0x4a,0x00,0x00,0x00,0x02,0x0d,0x06,0x06,
+ 0x06,0x06,0x0e,0x00,0x00,0x00,0x00,0x06,0x06,0x06,0x00,0x06,0x06,0x02,0x06,
+ 0x00,0x0a,0x0a,0x07,0x07,0x06,0x02,0x05,0x05,0x02,0x02,0x00,0x00,0x04,0x04,
+ 0x04,0x04,0x00,0x00,0x00,0x0e,0x05,0x06,0x06,0x06,0x01,0x06,0x00,0x00,0x08,
+ 0x00,0x10,0x00,0x18,0x00,0x20,0x00,0x28,0x00,0x30,0x00,0x80,0x01,0x82,0x01,
+ 0x86,0x00,0xf6,0xcf,0xfe,0x3f,0xab,0x00,0xb0,0x00,0xb1,0x00,0xb3,0x00,0xba,
+ 0xf8,0xbb,0x00,0xc0,0x00,0xc1,0x00,0xc7,0xbf,0x62,0xff,0x00,0x8d,0xff,0x00,
+ 0xc4,0xff,0x00,0xc5,0xff,0x00,0xff,0xff,0xeb,0x01,0xff,0x0e,0x12,0x08,0x00,
+ 0x13,0x09,0x00,0x16,0x08,0x00,0x17,0x09,0x00,0x2b,0x09,0x00,0xae,0xff,0x07,
+ 0xb2,0xff,0x00,0xb4,0xff,0x00,0xb5,0xff,0x00,0xc3,0x01,0x00,0xc7,0xff,0xbf,
+ 0xe7,0x08,0x00,0xf0,0x02,0x00
+};
diff --git a/lib/minhook/src/hde/table64.h b/lib/minhook/src/hde/table64.h
new file mode 100644
index 0000000..01d4541
--- /dev/null
+++ b/lib/minhook/src/hde/table64.h
@@ -0,0 +1,74 @@
+/*
+ * Hacker Disassembler Engine 64 C
+ * Copyright (c) 2008-2009, Vyacheslav Patkov.
+ * All rights reserved.
+ *
+ */
+
+#define C_NONE 0x00
+#define C_MODRM 0x01
+#define C_IMM8 0x02
+#define C_IMM16 0x04
+#define C_IMM_P66 0x10
+#define C_REL8 0x20
+#define C_REL32 0x40
+#define C_GROUP 0x80
+#define C_ERROR 0xff
+
+#define PRE_ANY 0x00
+#define PRE_NONE 0x01
+#define PRE_F2 0x02
+#define PRE_F3 0x04
+#define PRE_66 0x08
+#define PRE_67 0x10
+#define PRE_LOCK 0x20
+#define PRE_SEG 0x40
+#define PRE_ALL 0xff
+
+#define DELTA_OPCODES 0x4a
+#define DELTA_FPU_REG 0xfd
+#define DELTA_FPU_MODRM 0x104
+#define DELTA_PREFIXES 0x13c
+#define DELTA_OP_LOCK_OK 0x1ae
+#define DELTA_OP2_LOCK_OK 0x1c6
+#define DELTA_OP_ONLY_MEM 0x1d8
+#define DELTA_OP2_ONLY_MEM 0x1e7
+
+unsigned char hde64_table[] = {
+ 0xa5,0xaa,0xa5,0xb8,0xa5,0xaa,0xa5,0xaa,0xa5,0xb8,0xa5,0xb8,0xa5,0xb8,0xa5,
+ 0xb8,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xac,0xc0,0xcc,0xc0,0xa1,0xa1,
+ 0xa1,0xa1,0xb1,0xa5,0xa5,0xa6,0xc0,0xc0,0xd7,0xda,0xe0,0xc0,0xe4,0xc0,0xea,
+ 0xea,0xe0,0xe0,0x98,0xc8,0xee,0xf1,0xa5,0xd3,0xa5,0xa5,0xa1,0xea,0x9e,0xc0,
+ 0xc0,0xc2,0xc0,0xe6,0x03,0x7f,0x11,0x7f,0x01,0x7f,0x01,0x3f,0x01,0x01,0xab,
+ 0x8b,0x90,0x64,0x5b,0x5b,0x5b,0x5b,0x5b,0x92,0x5b,0x5b,0x76,0x90,0x92,0x92,
+ 0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x6a,0x73,0x90,
+ 0x5b,0x52,0x52,0x52,0x52,0x5b,0x5b,0x5b,0x5b,0x77,0x7c,0x77,0x85,0x5b,0x5b,
+ 0x70,0x5b,0x7a,0xaf,0x76,0x76,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,
+ 0x5b,0x5b,0x86,0x01,0x03,0x01,0x04,0x03,0xd5,0x03,0xd5,0x03,0xcc,0x01,0xbc,
+ 0x03,0xf0,0x03,0x03,0x04,0x00,0x50,0x50,0x50,0x50,0xff,0x20,0x20,0x20,0x20,
+ 0x01,0x01,0x01,0x01,0xc4,0x02,0x10,0xff,0xff,0xff,0x01,0x00,0x03,0x11,0xff,
+ 0x03,0xc4,0xc6,0xc8,0x02,0x10,0x00,0xff,0xcc,0x01,0x01,0x01,0x00,0x00,0x00,
+ 0x00,0x01,0x01,0x03,0x01,0xff,0xff,0xc0,0xc2,0x10,0x11,0x02,0x03,0x01,0x01,
+ 0x01,0xff,0xff,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xff,0xff,0xff,0xff,0x10,
+ 0x10,0x10,0x10,0x02,0x10,0x00,0x00,0xc6,0xc8,0x02,0x02,0x02,0x02,0x06,0x00,
+ 0x04,0x00,0x02,0xff,0x00,0xc0,0xc2,0x01,0x01,0x03,0x03,0x03,0xca,0x40,0x00,
+ 0x0a,0x00,0x04,0x00,0x00,0x00,0x00,0x7f,0x00,0x33,0x01,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0xff,0xbf,0xff,0xff,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0xff,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,
+ 0x00,0x00,0x00,0xbf,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7f,0x00,0x00,
+ 0xff,0x40,0x40,0x40,0x40,0x41,0x49,0x40,0x40,0x40,0x40,0x4c,0x42,0x40,0x40,
+ 0x40,0x40,0x40,0x40,0x40,0x40,0x4f,0x44,0x53,0x40,0x40,0x40,0x44,0x57,0x43,
+ 0x5c,0x40,0x60,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,
+ 0x40,0x40,0x64,0x66,0x6e,0x6b,0x40,0x40,0x6a,0x46,0x40,0x40,0x44,0x46,0x40,
+ 0x40,0x5b,0x44,0x40,0x40,0x00,0x00,0x00,0x00,0x06,0x06,0x06,0x06,0x01,0x06,
+ 0x06,0x02,0x06,0x06,0x00,0x06,0x00,0x0a,0x0a,0x00,0x00,0x00,0x02,0x07,0x07,
+ 0x06,0x02,0x0d,0x06,0x06,0x06,0x0e,0x05,0x05,0x02,0x02,0x00,0x00,0x04,0x04,
+ 0x04,0x04,0x05,0x06,0x06,0x06,0x00,0x00,0x00,0x0e,0x00,0x00,0x08,0x00,0x10,
+ 0x00,0x18,0x00,0x20,0x00,0x28,0x00,0x30,0x00,0x80,0x01,0x82,0x01,0x86,0x00,
+ 0xf6,0xcf,0xfe,0x3f,0xab,0x00,0xb0,0x00,0xb1,0x00,0xb3,0x00,0xba,0xf8,0xbb,
+ 0x00,0xc0,0x00,0xc1,0x00,0xc7,0xbf,0x62,0xff,0x00,0x8d,0xff,0x00,0xc4,0xff,
+ 0x00,0xc5,0xff,0x00,0xff,0xff,0xeb,0x01,0xff,0x0e,0x12,0x08,0x00,0x13,0x09,
+ 0x00,0x16,0x08,0x00,0x17,0x09,0x00,0x2b,0x09,0x00,0xae,0xff,0x07,0xb2,0xff,
+ 0x00,0xb4,0xff,0x00,0xb5,0xff,0x00,0xc3,0x01,0x00,0xc7,0xff,0xbf,0xe7,0x08,
+ 0x00,0xf0,0x02,0x00
+};
diff --git a/lib/minhook/src/hook.c b/lib/minhook/src/hook.c
new file mode 100644
index 0000000..68a006a
--- /dev/null
+++ b/lib/minhook/src/hook.c
@@ -0,0 +1,939 @@
+/*
+ * MinHook - The Minimalistic API Hooking Library for x64/x86
+ * Copyright (C) 2009-2017 Tsuda Kageyu.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include
+#include
+#include
+
+#include "../include/MinHook.h"
+#include "buffer.h"
+#include "trampoline.h"
+
+#ifndef ARRAYSIZE
+ #define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
+#endif
+
+// Initial capacity of the HOOK_ENTRY buffer.
+#define INITIAL_HOOK_CAPACITY 32
+
+// Initial capacity of the thread IDs buffer.
+#define INITIAL_THREAD_CAPACITY 128
+
+// Special hook position values.
+#define INVALID_HOOK_POS UINT_MAX
+#define ALL_HOOKS_POS UINT_MAX
+
+// Freeze() action argument defines.
+#define ACTION_DISABLE 0
+#define ACTION_ENABLE 1
+#define ACTION_APPLY_QUEUED 2
+
+// Thread access rights for suspending/resuming threads.
+#define THREAD_ACCESS \
+ (THREAD_SUSPEND_RESUME | THREAD_GET_CONTEXT | THREAD_QUERY_INFORMATION | THREAD_SET_CONTEXT)
+
+// Hook information.
+typedef struct _HOOK_ENTRY
+{
+ LPVOID pTarget; // Address of the target function.
+ LPVOID pDetour; // Address of the detour or relay function.
+ LPVOID pTrampoline; // Address of the trampoline function.
+ UINT8 backup[8]; // Original prologue of the target function.
+
+ UINT8 patchAbove : 1; // Uses the hot patch area.
+ UINT8 isEnabled : 1; // Enabled.
+ UINT8 queueEnable : 1; // Queued for enabling/disabling when != isEnabled.
+
+ UINT nIP : 4; // Count of the instruction boundaries.
+ UINT8 oldIPs[8]; // Instruction boundaries of the target function.
+ UINT8 newIPs[8]; // Instruction boundaries of the trampoline function.
+} HOOK_ENTRY, *PHOOK_ENTRY;
+
+// Suspended threads for Freeze()/Unfreeze().
+typedef struct _FROZEN_THREADS
+{
+ LPDWORD pItems; // Data heap
+ UINT capacity; // Size of allocated data heap, items
+ UINT size; // Actual number of data items
+} FROZEN_THREADS, *PFROZEN_THREADS;
+
+//-------------------------------------------------------------------------
+// Global Variables:
+//-------------------------------------------------------------------------
+
+// Spin lock flag for EnterSpinLock()/LeaveSpinLock().
+volatile LONG g_isLocked = FALSE;
+
+// Private heap handle. If not NULL, this library is initialized.
+HANDLE g_hHeap = NULL;
+
+// Hook entries.
+struct
+{
+ PHOOK_ENTRY pItems; // Data heap
+ UINT capacity; // Size of allocated data heap, items
+ UINT size; // Actual number of data items
+} g_hooks;
+
+//-------------------------------------------------------------------------
+// Returns INVALID_HOOK_POS if not found.
+static UINT FindHookEntry(LPVOID pTarget)
+{
+ UINT i;
+ for (i = 0; i < g_hooks.size; ++i)
+ {
+ if ((ULONG_PTR)pTarget == (ULONG_PTR)g_hooks.pItems[i].pTarget)
+ return i;
+ }
+
+ return INVALID_HOOK_POS;
+}
+
+//-------------------------------------------------------------------------
+static PHOOK_ENTRY AddHookEntry()
+{
+ if (g_hooks.pItems == NULL)
+ {
+ g_hooks.capacity = INITIAL_HOOK_CAPACITY;
+ g_hooks.pItems = (PHOOK_ENTRY)HeapAlloc(
+ g_hHeap, 0, g_hooks.capacity * sizeof(HOOK_ENTRY));
+ if (g_hooks.pItems == NULL)
+ return NULL;
+ }
+ else if (g_hooks.size >= g_hooks.capacity)
+ {
+ PHOOK_ENTRY p = (PHOOK_ENTRY)HeapReAlloc(
+ g_hHeap, 0, g_hooks.pItems, (g_hooks.capacity * 2) * sizeof(HOOK_ENTRY));
+ if (p == NULL)
+ return NULL;
+
+ g_hooks.capacity *= 2;
+ g_hooks.pItems = p;
+ }
+
+ return &g_hooks.pItems[g_hooks.size++];
+}
+
+//-------------------------------------------------------------------------
+static VOID DeleteHookEntry(UINT pos)
+{
+ if (pos < g_hooks.size - 1)
+ g_hooks.pItems[pos] = g_hooks.pItems[g_hooks.size - 1];
+
+ g_hooks.size--;
+
+ if (g_hooks.capacity / 2 >= INITIAL_HOOK_CAPACITY && g_hooks.capacity / 2 >= g_hooks.size)
+ {
+ PHOOK_ENTRY p = (PHOOK_ENTRY)HeapReAlloc(
+ g_hHeap, 0, g_hooks.pItems, (g_hooks.capacity / 2) * sizeof(HOOK_ENTRY));
+ if (p == NULL)
+ return;
+
+ g_hooks.capacity /= 2;
+ g_hooks.pItems = p;
+ }
+}
+
+//-------------------------------------------------------------------------
+static DWORD_PTR FindOldIP(PHOOK_ENTRY pHook, DWORD_PTR ip)
+{
+ UINT i;
+
+ if (pHook->patchAbove && ip == ((DWORD_PTR)pHook->pTarget - sizeof(JMP_REL)))
+ return (DWORD_PTR)pHook->pTarget;
+
+ for (i = 0; i < pHook->nIP; ++i)
+ {
+ if (ip == ((DWORD_PTR)pHook->pTrampoline + pHook->newIPs[i]))
+ return (DWORD_PTR)pHook->pTarget + pHook->oldIPs[i];
+ }
+
+#if defined(_M_X64) || defined(__x86_64__)
+ // Check relay function.
+ if (ip == (DWORD_PTR)pHook->pDetour)
+ return (DWORD_PTR)pHook->pTarget;
+#endif
+
+ return 0;
+}
+
+//-------------------------------------------------------------------------
+static DWORD_PTR FindNewIP(PHOOK_ENTRY pHook, DWORD_PTR ip)
+{
+ UINT i;
+ for (i = 0; i < pHook->nIP; ++i)
+ {
+ if (ip == ((DWORD_PTR)pHook->pTarget + pHook->oldIPs[i]))
+ return (DWORD_PTR)pHook->pTrampoline + pHook->newIPs[i];
+ }
+
+ return 0;
+}
+
+//-------------------------------------------------------------------------
+static VOID ProcessThreadIPs(HANDLE hThread, UINT pos, UINT action)
+{
+ // If the thread suspended in the overwritten area,
+ // move IP to the proper address.
+
+ CONTEXT c;
+#if defined(_M_X64) || defined(__x86_64__)
+ DWORD64 *pIP = &c.Rip;
+#else
+ DWORD *pIP = &c.Eip;
+#endif
+ UINT count;
+
+ c.ContextFlags = CONTEXT_CONTROL;
+ if (!GetThreadContext(hThread, &c))
+ return;
+
+ if (pos == ALL_HOOKS_POS)
+ {
+ pos = 0;
+ count = g_hooks.size;
+ }
+ else
+ {
+ count = pos + 1;
+ }
+
+ for (; pos < count; ++pos)
+ {
+ PHOOK_ENTRY pHook = &g_hooks.pItems[pos];
+ BOOL enable;
+ DWORD_PTR ip;
+
+ switch (action)
+ {
+ case ACTION_DISABLE:
+ enable = FALSE;
+ break;
+
+ case ACTION_ENABLE:
+ enable = TRUE;
+ break;
+
+ default: // ACTION_APPLY_QUEUED
+ enable = pHook->queueEnable;
+ break;
+ }
+ if (pHook->isEnabled == enable)
+ continue;
+
+ if (enable)
+ ip = FindNewIP(pHook, *pIP);
+ else
+ ip = FindOldIP(pHook, *pIP);
+
+ if (ip != 0)
+ {
+ *pIP = ip;
+ SetThreadContext(hThread, &c);
+ }
+ }
+}
+
+//-------------------------------------------------------------------------
+static BOOL EnumerateThreads(PFROZEN_THREADS pThreads)
+{
+ BOOL succeeded = FALSE;
+
+ HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
+ if (hSnapshot != INVALID_HANDLE_VALUE)
+ {
+ THREADENTRY32 te;
+ te.dwSize = sizeof(THREADENTRY32);
+ if (Thread32First(hSnapshot, &te))
+ {
+ succeeded = TRUE;
+ do
+ {
+ if (te.dwSize >= (FIELD_OFFSET(THREADENTRY32, th32OwnerProcessID) + sizeof(DWORD))
+ && te.th32OwnerProcessID == GetCurrentProcessId()
+ && te.th32ThreadID != GetCurrentThreadId())
+ {
+ if (pThreads->pItems == NULL)
+ {
+ pThreads->capacity = INITIAL_THREAD_CAPACITY;
+ pThreads->pItems
+ = (LPDWORD)HeapAlloc(g_hHeap, 0, pThreads->capacity * sizeof(DWORD));
+ if (pThreads->pItems == NULL)
+ {
+ succeeded = FALSE;
+ break;
+ }
+ }
+ else if (pThreads->size >= pThreads->capacity)
+ {
+ LPDWORD p;
+ pThreads->capacity *= 2;
+ p = (LPDWORD)HeapReAlloc(
+ g_hHeap, 0, pThreads->pItems, pThreads->capacity * sizeof(DWORD));
+ if (p == NULL)
+ {
+ succeeded = FALSE;
+ break;
+ }
+
+ pThreads->pItems = p;
+ }
+ pThreads->pItems[pThreads->size++] = te.th32ThreadID;
+ }
+
+ te.dwSize = sizeof(THREADENTRY32);
+ } while (Thread32Next(hSnapshot, &te));
+
+ if (succeeded && GetLastError() != ERROR_NO_MORE_FILES)
+ succeeded = FALSE;
+
+ if (!succeeded && pThreads->pItems != NULL)
+ {
+ HeapFree(g_hHeap, 0, pThreads->pItems);
+ pThreads->pItems = NULL;
+ }
+ }
+ CloseHandle(hSnapshot);
+ }
+
+ return succeeded;
+}
+
+//-------------------------------------------------------------------------
+static MH_STATUS Freeze(PFROZEN_THREADS pThreads, UINT pos, UINT action)
+{
+ MH_STATUS status = MH_OK;
+
+ pThreads->pItems = NULL;
+ pThreads->capacity = 0;
+ pThreads->size = 0;
+ if (!EnumerateThreads(pThreads))
+ {
+ status = MH_ERROR_MEMORY_ALLOC;
+ }
+ else if (pThreads->pItems != NULL)
+ {
+ UINT i;
+ for (i = 0; i < pThreads->size; ++i)
+ {
+ HANDLE hThread = OpenThread(THREAD_ACCESS, FALSE, pThreads->pItems[i]);
+ BOOL suspended = FALSE;
+ if (hThread != NULL)
+ {
+ DWORD result = SuspendThread(hThread);
+ if (result != 0xFFFFFFFF)
+ {
+ suspended = TRUE;
+ ProcessThreadIPs(hThread, pos, action);
+ }
+ CloseHandle(hThread);
+ }
+
+ if (!suspended)
+ {
+ // Mark thread as not suspended, so it's not resumed later on.
+ pThreads->pItems[i] = 0;
+ }
+ }
+ }
+
+ return status;
+}
+
+//-------------------------------------------------------------------------
+static VOID Unfreeze(PFROZEN_THREADS pThreads)
+{
+ if (pThreads->pItems != NULL)
+ {
+ UINT i;
+ for (i = 0; i < pThreads->size; ++i)
+ {
+ DWORD threadId = pThreads->pItems[i];
+ if (threadId != 0)
+ {
+ HANDLE hThread = OpenThread(THREAD_ACCESS, FALSE, threadId);
+ if (hThread != NULL)
+ {
+ ResumeThread(hThread);
+ CloseHandle(hThread);
+ }
+ }
+ }
+
+ HeapFree(g_hHeap, 0, pThreads->pItems);
+ }
+}
+
+//-------------------------------------------------------------------------
+static MH_STATUS EnableHookLL(UINT pos, BOOL enable)
+{
+ PHOOK_ENTRY pHook = &g_hooks.pItems[pos];
+ DWORD oldProtect;
+ SIZE_T patchSize = sizeof(JMP_REL);
+ LPBYTE pPatchTarget = (LPBYTE)pHook->pTarget;
+
+ if (pHook->patchAbove)
+ {
+ pPatchTarget -= sizeof(JMP_REL);
+ patchSize += sizeof(JMP_REL_SHORT);
+ }
+
+ if (!VirtualProtect(pPatchTarget, patchSize, PAGE_EXECUTE_READWRITE, &oldProtect))
+ return MH_ERROR_MEMORY_PROTECT;
+
+ if (enable)
+ {
+ PJMP_REL pJmp = (PJMP_REL)pPatchTarget;
+ pJmp->opcode = 0xE9;
+ pJmp->operand = (UINT32)((LPBYTE)pHook->pDetour - (pPatchTarget + sizeof(JMP_REL)));
+
+ if (pHook->patchAbove)
+ {
+ PJMP_REL_SHORT pShortJmp = (PJMP_REL_SHORT)pHook->pTarget;
+ pShortJmp->opcode = 0xEB;
+ pShortJmp->operand = (UINT8)(0 - (sizeof(JMP_REL_SHORT) + sizeof(JMP_REL)));
+ }
+ }
+ else
+ {
+ if (pHook->patchAbove)
+ memcpy(pPatchTarget, pHook->backup, sizeof(JMP_REL) + sizeof(JMP_REL_SHORT));
+ else
+ memcpy(pPatchTarget, pHook->backup, sizeof(JMP_REL));
+ }
+
+ VirtualProtect(pPatchTarget, patchSize, oldProtect, &oldProtect);
+
+ // Just-in-case measure.
+ FlushInstructionCache(GetCurrentProcess(), pPatchTarget, patchSize);
+
+ pHook->isEnabled = enable;
+ pHook->queueEnable = enable;
+
+ return MH_OK;
+}
+
+//-------------------------------------------------------------------------
+static MH_STATUS EnableAllHooksLL(BOOL enable)
+{
+ MH_STATUS status = MH_OK;
+ UINT i, first = INVALID_HOOK_POS;
+
+ for (i = 0; i < g_hooks.size; ++i)
+ {
+ if (g_hooks.pItems[i].isEnabled != enable)
+ {
+ first = i;
+ break;
+ }
+ }
+
+ if (first != INVALID_HOOK_POS)
+ {
+ FROZEN_THREADS threads;
+ status = Freeze(&threads, ALL_HOOKS_POS, enable ? ACTION_ENABLE : ACTION_DISABLE);
+ if (status == MH_OK)
+ {
+ for (i = first; i < g_hooks.size; ++i)
+ {
+ if (g_hooks.pItems[i].isEnabled != enable)
+ {
+ status = EnableHookLL(i, enable);
+ if (status != MH_OK)
+ break;
+ }
+ }
+
+ Unfreeze(&threads);
+ }
+ }
+
+ return status;
+}
+
+//-------------------------------------------------------------------------
+static VOID EnterSpinLock(VOID)
+{
+ SIZE_T spinCount = 0;
+
+ // Wait until the flag is FALSE.
+ while (InterlockedCompareExchange(&g_isLocked, TRUE, FALSE) != FALSE)
+ {
+ // No need to generate a memory barrier here, since InterlockedCompareExchange()
+ // generates a full memory barrier itself.
+
+ // Prevent the loop from being too busy.
+ if (spinCount < 32)
+ Sleep(0);
+ else
+ Sleep(1);
+
+ spinCount++;
+ }
+}
+
+//-------------------------------------------------------------------------
+static VOID LeaveSpinLock(VOID)
+{
+ // No need to generate a memory barrier here, since InterlockedExchange()
+ // generates a full memory barrier itself.
+
+ InterlockedExchange(&g_isLocked, FALSE);
+}
+
+//-------------------------------------------------------------------------
+MH_STATUS WINAPI MH_Initialize(VOID)
+{
+ MH_STATUS status = MH_OK;
+
+ EnterSpinLock();
+
+ if (g_hHeap == NULL)
+ {
+ g_hHeap = HeapCreate(0, 0, 0);
+ if (g_hHeap != NULL)
+ {
+ // Initialize the internal function buffer.
+ InitializeBuffer();
+ }
+ else
+ {
+ status = MH_ERROR_MEMORY_ALLOC;
+ }
+ }
+ else
+ {
+ status = MH_ERROR_ALREADY_INITIALIZED;
+ }
+
+ LeaveSpinLock();
+
+ return status;
+}
+
+//-------------------------------------------------------------------------
+MH_STATUS WINAPI MH_Uninitialize(VOID)
+{
+ MH_STATUS status = MH_OK;
+
+ EnterSpinLock();
+
+ if (g_hHeap != NULL)
+ {
+ status = EnableAllHooksLL(FALSE);
+ if (status == MH_OK)
+ {
+ // Free the internal function buffer.
+
+ // HeapFree is actually not required, but some tools detect a false
+ // memory leak without HeapFree.
+
+ UninitializeBuffer();
+
+ HeapFree(g_hHeap, 0, g_hooks.pItems);
+ HeapDestroy(g_hHeap);
+
+ g_hHeap = NULL;
+
+ g_hooks.pItems = NULL;
+ g_hooks.capacity = 0;
+ g_hooks.size = 0;
+ }
+ }
+ else
+ {
+ status = MH_ERROR_NOT_INITIALIZED;
+ }
+
+ LeaveSpinLock();
+
+ return status;
+}
+
+//-------------------------------------------------------------------------
+MH_STATUS WINAPI MH_CreateHook(LPVOID pTarget, LPVOID pDetour, LPVOID *ppOriginal)
+{
+ MH_STATUS status = MH_OK;
+
+ EnterSpinLock();
+
+ if (g_hHeap != NULL)
+ {
+ if (IsExecutableAddress(pTarget) && IsExecutableAddress(pDetour))
+ {
+ UINT pos = FindHookEntry(pTarget);
+ if (pos == INVALID_HOOK_POS)
+ {
+ LPVOID pBuffer = AllocateBuffer(pTarget);
+ if (pBuffer != NULL)
+ {
+ TRAMPOLINE ct;
+
+ ct.pTarget = pTarget;
+ ct.pDetour = pDetour;
+ ct.pTrampoline = pBuffer;
+ if (CreateTrampolineFunction(&ct))
+ {
+ PHOOK_ENTRY pHook = AddHookEntry();
+ if (pHook != NULL)
+ {
+ pHook->pTarget = ct.pTarget;
+#if defined(_M_X64) || defined(__x86_64__)
+ pHook->pDetour = ct.pRelay;
+#else
+ pHook->pDetour = ct.pDetour;
+#endif
+ pHook->pTrampoline = ct.pTrampoline;
+ pHook->patchAbove = ct.patchAbove;
+ pHook->isEnabled = FALSE;
+ pHook->queueEnable = FALSE;
+ pHook->nIP = ct.nIP;
+ memcpy(pHook->oldIPs, ct.oldIPs, ARRAYSIZE(ct.oldIPs));
+ memcpy(pHook->newIPs, ct.newIPs, ARRAYSIZE(ct.newIPs));
+
+ // Back up the target function.
+
+ if (ct.patchAbove)
+ {
+ memcpy(
+ pHook->backup,
+ (LPBYTE)pTarget - sizeof(JMP_REL),
+ sizeof(JMP_REL) + sizeof(JMP_REL_SHORT));
+ }
+ else
+ {
+ memcpy(pHook->backup, pTarget, sizeof(JMP_REL));
+ }
+
+ if (ppOriginal != NULL)
+ *ppOriginal = pHook->pTrampoline;
+ }
+ else
+ {
+ status = MH_ERROR_MEMORY_ALLOC;
+ }
+ }
+ else
+ {
+ status = MH_ERROR_UNSUPPORTED_FUNCTION;
+ }
+
+ if (status != MH_OK)
+ {
+ FreeBuffer(pBuffer);
+ }
+ }
+ else
+ {
+ status = MH_ERROR_MEMORY_ALLOC;
+ }
+ }
+ else
+ {
+ status = MH_ERROR_ALREADY_CREATED;
+ }
+ }
+ else
+ {
+ status = MH_ERROR_NOT_EXECUTABLE;
+ }
+ }
+ else
+ {
+ status = MH_ERROR_NOT_INITIALIZED;
+ }
+
+ LeaveSpinLock();
+
+ return status;
+}
+
+//-------------------------------------------------------------------------
+MH_STATUS WINAPI MH_RemoveHook(LPVOID pTarget)
+{
+ MH_STATUS status = MH_OK;
+
+ EnterSpinLock();
+
+ if (g_hHeap != NULL)
+ {
+ UINT pos = FindHookEntry(pTarget);
+ if (pos != INVALID_HOOK_POS)
+ {
+ if (g_hooks.pItems[pos].isEnabled)
+ {
+ FROZEN_THREADS threads;
+ status = Freeze(&threads, pos, ACTION_DISABLE);
+ if (status == MH_OK)
+ {
+ status = EnableHookLL(pos, FALSE);
+
+ Unfreeze(&threads);
+ }
+ }
+
+ if (status == MH_OK)
+ {
+ FreeBuffer(g_hooks.pItems[pos].pTrampoline);
+ DeleteHookEntry(pos);
+ }
+ }
+ else
+ {
+ status = MH_ERROR_NOT_CREATED;
+ }
+ }
+ else
+ {
+ status = MH_ERROR_NOT_INITIALIZED;
+ }
+
+ LeaveSpinLock();
+
+ return status;
+}
+
+//-------------------------------------------------------------------------
+static MH_STATUS EnableHook(LPVOID pTarget, BOOL enable)
+{
+ MH_STATUS status = MH_OK;
+
+ EnterSpinLock();
+
+ if (g_hHeap != NULL)
+ {
+ if (pTarget == MH_ALL_HOOKS)
+ {
+ status = EnableAllHooksLL(enable);
+ }
+ else
+ {
+ UINT pos = FindHookEntry(pTarget);
+ if (pos != INVALID_HOOK_POS)
+ {
+ if (g_hooks.pItems[pos].isEnabled != enable)
+ {
+ FROZEN_THREADS threads;
+ status = Freeze(&threads, pos, ACTION_ENABLE);
+ if (status == MH_OK)
+ {
+ status = EnableHookLL(pos, enable);
+
+ Unfreeze(&threads);
+ }
+ }
+ else
+ {
+ status = enable ? MH_ERROR_ENABLED : MH_ERROR_DISABLED;
+ }
+ }
+ else
+ {
+ status = MH_ERROR_NOT_CREATED;
+ }
+ }
+ }
+ else
+ {
+ status = MH_ERROR_NOT_INITIALIZED;
+ }
+
+ LeaveSpinLock();
+
+ return status;
+}
+
+//-------------------------------------------------------------------------
+MH_STATUS WINAPI MH_EnableHook(LPVOID pTarget)
+{
+ return EnableHook(pTarget, TRUE);
+}
+
+//-------------------------------------------------------------------------
+MH_STATUS WINAPI MH_DisableHook(LPVOID pTarget)
+{
+ return EnableHook(pTarget, FALSE);
+}
+
+//-------------------------------------------------------------------------
+static MH_STATUS QueueHook(LPVOID pTarget, BOOL queueEnable)
+{
+ MH_STATUS status = MH_OK;
+
+ EnterSpinLock();
+
+ if (g_hHeap != NULL)
+ {
+ if (pTarget == MH_ALL_HOOKS)
+ {
+ UINT i;
+ for (i = 0; i < g_hooks.size; ++i)
+ g_hooks.pItems[i].queueEnable = queueEnable;
+ }
+ else
+ {
+ UINT pos = FindHookEntry(pTarget);
+ if (pos != INVALID_HOOK_POS)
+ {
+ g_hooks.pItems[pos].queueEnable = queueEnable;
+ }
+ else
+ {
+ status = MH_ERROR_NOT_CREATED;
+ }
+ }
+ }
+ else
+ {
+ status = MH_ERROR_NOT_INITIALIZED;
+ }
+
+ LeaveSpinLock();
+
+ return status;
+}
+
+//-------------------------------------------------------------------------
+MH_STATUS WINAPI MH_QueueEnableHook(LPVOID pTarget)
+{
+ return QueueHook(pTarget, TRUE);
+}
+
+//-------------------------------------------------------------------------
+MH_STATUS WINAPI MH_QueueDisableHook(LPVOID pTarget)
+{
+ return QueueHook(pTarget, FALSE);
+}
+
+//-------------------------------------------------------------------------
+MH_STATUS WINAPI MH_ApplyQueued(VOID)
+{
+ MH_STATUS status = MH_OK;
+ UINT i, first = INVALID_HOOK_POS;
+
+ EnterSpinLock();
+
+ if (g_hHeap != NULL)
+ {
+ for (i = 0; i < g_hooks.size; ++i)
+ {
+ if (g_hooks.pItems[i].isEnabled != g_hooks.pItems[i].queueEnable)
+ {
+ first = i;
+ break;
+ }
+ }
+
+ if (first != INVALID_HOOK_POS)
+ {
+ FROZEN_THREADS threads;
+ status = Freeze(&threads, ALL_HOOKS_POS, ACTION_APPLY_QUEUED);
+ if (status == MH_OK)
+ {
+ for (i = first; i < g_hooks.size; ++i)
+ {
+ PHOOK_ENTRY pHook = &g_hooks.pItems[i];
+ if (pHook->isEnabled != pHook->queueEnable)
+ {
+ status = EnableHookLL(i, pHook->queueEnable);
+ if (status != MH_OK)
+ break;
+ }
+ }
+
+ Unfreeze(&threads);
+ }
+ }
+ }
+ else
+ {
+ status = MH_ERROR_NOT_INITIALIZED;
+ }
+
+ LeaveSpinLock();
+
+ return status;
+}
+
+//-------------------------------------------------------------------------
+MH_STATUS WINAPI MH_CreateHookApiEx(
+ LPCWSTR pszModule, LPCSTR pszProcName, LPVOID pDetour,
+ LPVOID *ppOriginal, LPVOID *ppTarget)
+{
+ HMODULE hModule;
+ LPVOID pTarget;
+
+ hModule = GetModuleHandleW(pszModule);
+ if (hModule == NULL)
+ return MH_ERROR_MODULE_NOT_FOUND;
+
+ pTarget = (LPVOID)GetProcAddress(hModule, pszProcName);
+ if (pTarget == NULL)
+ return MH_ERROR_FUNCTION_NOT_FOUND;
+
+ if (ppTarget != NULL)
+ *ppTarget = pTarget;
+
+ return MH_CreateHook(pTarget, pDetour, ppOriginal);
+}
+
+//-------------------------------------------------------------------------
+MH_STATUS WINAPI MH_CreateHookApi(
+ LPCWSTR pszModule, LPCSTR pszProcName, LPVOID pDetour, LPVOID *ppOriginal)
+{
+ return MH_CreateHookApiEx(pszModule, pszProcName, pDetour, ppOriginal, NULL);
+}
+
+//-------------------------------------------------------------------------
+const char *WINAPI MH_StatusToString(MH_STATUS status)
+{
+#define MH_ST2STR(x) \
+ case x: \
+ return #x;
+
+ switch (status) {
+ MH_ST2STR(MH_UNKNOWN)
+ MH_ST2STR(MH_OK)
+ MH_ST2STR(MH_ERROR_ALREADY_INITIALIZED)
+ MH_ST2STR(MH_ERROR_NOT_INITIALIZED)
+ MH_ST2STR(MH_ERROR_ALREADY_CREATED)
+ MH_ST2STR(MH_ERROR_NOT_CREATED)
+ MH_ST2STR(MH_ERROR_ENABLED)
+ MH_ST2STR(MH_ERROR_DISABLED)
+ MH_ST2STR(MH_ERROR_NOT_EXECUTABLE)
+ MH_ST2STR(MH_ERROR_UNSUPPORTED_FUNCTION)
+ MH_ST2STR(MH_ERROR_MEMORY_ALLOC)
+ MH_ST2STR(MH_ERROR_MEMORY_PROTECT)
+ MH_ST2STR(MH_ERROR_MODULE_NOT_FOUND)
+ MH_ST2STR(MH_ERROR_FUNCTION_NOT_FOUND)
+ }
+
+#undef MH_ST2STR
+
+ return "(unknown)";
+}
diff --git a/lib/minhook/src/trampoline.c b/lib/minhook/src/trampoline.c
new file mode 100644
index 0000000..d4d6f99
--- /dev/null
+++ b/lib/minhook/src/trampoline.c
@@ -0,0 +1,320 @@
+/*
+ * MinHook - The Minimalistic API Hooking Library for x64/x86
+ * Copyright (C) 2009-2017 Tsuda Kageyu.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include
+
+#if defined(_MSC_VER) && !defined(MINHOOK_DISABLE_INTRINSICS)
+ #define ALLOW_INTRINSICS
+ #include
+#endif
+
+#ifndef ARRAYSIZE
+ #define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
+#endif
+
+#if defined(_M_X64) || defined(__x86_64__)
+ #include "./hde/hde64.h"
+ typedef hde64s HDE;
+ #define HDE_DISASM(code, hs) hde64_disasm(code, hs)
+#else
+ #include "./hde/hde32.h"
+ typedef hde32s HDE;
+ #define HDE_DISASM(code, hs) hde32_disasm(code, hs)
+#endif
+
+#include "trampoline.h"
+#include "buffer.h"
+
+// Maximum size of a trampoline function.
+#if defined(_M_X64) || defined(__x86_64__)
+ #define TRAMPOLINE_MAX_SIZE (MEMORY_SLOT_SIZE - sizeof(JMP_ABS))
+#else
+ #define TRAMPOLINE_MAX_SIZE MEMORY_SLOT_SIZE
+#endif
+
+//-------------------------------------------------------------------------
+static BOOL IsCodePadding(LPBYTE pInst, UINT size)
+{
+ UINT i;
+
+ if (pInst[0] != 0x00 && pInst[0] != 0x90 && pInst[0] != 0xCC)
+ return FALSE;
+
+ for (i = 1; i < size; ++i)
+ {
+ if (pInst[i] != pInst[0])
+ return FALSE;
+ }
+ return TRUE;
+}
+
+//-------------------------------------------------------------------------
+BOOL CreateTrampolineFunction(PTRAMPOLINE ct)
+{
+#if defined(_M_X64) || defined(__x86_64__)
+ CALL_ABS call = {
+ 0xFF, 0x15, 0x00000002, // FF15 00000002: CALL [RIP+8]
+ 0xEB, 0x08, // EB 08: JMP +10
+ 0x0000000000000000ULL // Absolute destination address
+ };
+ JMP_ABS jmp = {
+ 0xFF, 0x25, 0x00000000, // FF25 00000000: JMP [RIP+6]
+ 0x0000000000000000ULL // Absolute destination address
+ };
+ JCC_ABS jcc = {
+ 0x70, 0x0E, // 7* 0E: J** +16
+ 0xFF, 0x25, 0x00000000, // FF25 00000000: JMP [RIP+6]
+ 0x0000000000000000ULL // Absolute destination address
+ };
+#else
+ CALL_REL call = {
+ 0xE8, // E8 xxxxxxxx: CALL +5+xxxxxxxx
+ 0x00000000 // Relative destination address
+ };
+ JMP_REL jmp = {
+ 0xE9, // E9 xxxxxxxx: JMP +5+xxxxxxxx
+ 0x00000000 // Relative destination address
+ };
+ JCC_REL jcc = {
+ 0x0F, 0x80, // 0F8* xxxxxxxx: J** +6+xxxxxxxx
+ 0x00000000 // Relative destination address
+ };
+#endif
+
+ UINT8 oldPos = 0;
+ UINT8 newPos = 0;
+ ULONG_PTR jmpDest = 0; // Destination address of an internal jump.
+ BOOL finished = FALSE; // Is the function completed?
+#if defined(_M_X64) || defined(__x86_64__)
+ UINT8 instBuf[16];
+#endif
+
+ ct->patchAbove = FALSE;
+ ct->nIP = 0;
+
+ do
+ {
+ HDE hs;
+ UINT copySize;
+ LPVOID pCopySrc;
+ ULONG_PTR pOldInst = (ULONG_PTR)ct->pTarget + oldPos;
+ ULONG_PTR pNewInst = (ULONG_PTR)ct->pTrampoline + newPos;
+
+ copySize = HDE_DISASM((LPVOID)pOldInst, &hs);
+ if (hs.flags & F_ERROR)
+ return FALSE;
+
+ pCopySrc = (LPVOID)pOldInst;
+ if (oldPos >= sizeof(JMP_REL))
+ {
+ // The trampoline function is long enough.
+ // Complete the function with the jump to the target function.
+#if defined(_M_X64) || defined(__x86_64__)
+ jmp.address = pOldInst;
+#else
+ jmp.operand = (UINT32)(pOldInst - (pNewInst + sizeof(jmp)));
+#endif
+ pCopySrc = &jmp;
+ copySize = sizeof(jmp);
+
+ finished = TRUE;
+ }
+#if defined(_M_X64) || defined(__x86_64__)
+ else if ((hs.modrm & 0xC7) == 0x05)
+ {
+ // Instructions using RIP relative addressing. (ModR/M = 00???101B)
+
+ // Modify the RIP relative address.
+ PUINT32 pRelAddr;
+
+ // Avoid using memcpy to reduce the footprint.
+#ifndef ALLOW_INTRINSICS
+ memcpy(instBuf, (LPBYTE)pOldInst, copySize);
+#else
+ __movsb(instBuf, (LPBYTE)pOldInst, copySize);
+#endif
+ pCopySrc = instBuf;
+
+ // Relative address is stored at (instruction length - immediate value length - 4).
+ pRelAddr = (PUINT32)(instBuf + hs.len - ((hs.flags & 0x3C) >> 2) - 4);
+ *pRelAddr
+ = (UINT32)((pOldInst + hs.len + (INT32)hs.disp.disp32) - (pNewInst + hs.len));
+
+ // Complete the function if JMP (FF /4).
+ if (hs.opcode == 0xFF && hs.modrm_reg == 4)
+ finished = TRUE;
+ }
+#endif
+ else if (hs.opcode == 0xE8)
+ {
+ // Direct relative CALL
+ ULONG_PTR dest = pOldInst + hs.len + (INT32)hs.imm.imm32;
+#if defined(_M_X64) || defined(__x86_64__)
+ call.address = dest;
+#else
+ call.operand = (UINT32)(dest - (pNewInst + sizeof(call)));
+#endif
+ pCopySrc = &call;
+ copySize = sizeof(call);
+ }
+ else if ((hs.opcode & 0xFD) == 0xE9)
+ {
+ // Direct relative JMP (EB or E9)
+ ULONG_PTR dest = pOldInst + hs.len;
+
+ if (hs.opcode == 0xEB) // isShort jmp
+ dest += (INT8)hs.imm.imm8;
+ else
+ dest += (INT32)hs.imm.imm32;
+
+ // Simply copy an internal jump.
+ if ((ULONG_PTR)ct->pTarget <= dest
+ && dest < ((ULONG_PTR)ct->pTarget + sizeof(JMP_REL)))
+ {
+ if (jmpDest < dest)
+ jmpDest = dest;
+ }
+ else
+ {
+#if defined(_M_X64) || defined(__x86_64__)
+ jmp.address = dest;
+#else
+ jmp.operand = (UINT32)(dest - (pNewInst + sizeof(jmp)));
+#endif
+ pCopySrc = &jmp;
+ copySize = sizeof(jmp);
+
+ // Exit the function if it is not in the branch.
+ finished = (pOldInst >= jmpDest);
+ }
+ }
+ else if ((hs.opcode & 0xF0) == 0x70
+ || (hs.opcode & 0xFC) == 0xE0
+ || (hs.opcode2 & 0xF0) == 0x80)
+ {
+ // Direct relative Jcc
+ ULONG_PTR dest = pOldInst + hs.len;
+
+ if ((hs.opcode & 0xF0) == 0x70 // Jcc
+ || (hs.opcode & 0xFC) == 0xE0) // LOOPNZ/LOOPZ/LOOP/JECXZ
+ dest += (INT8)hs.imm.imm8;
+ else
+ dest += (INT32)hs.imm.imm32;
+
+ // Simply copy an internal jump.
+ if ((ULONG_PTR)ct->pTarget <= dest
+ && dest < ((ULONG_PTR)ct->pTarget + sizeof(JMP_REL)))
+ {
+ if (jmpDest < dest)
+ jmpDest = dest;
+ }
+ else if ((hs.opcode & 0xFC) == 0xE0)
+ {
+ // LOOPNZ/LOOPZ/LOOP/JCXZ/JECXZ to the outside are not supported.
+ return FALSE;
+ }
+ else
+ {
+ UINT8 cond = ((hs.opcode != 0x0F ? hs.opcode : hs.opcode2) & 0x0F);
+#if defined(_M_X64) || defined(__x86_64__)
+ // Invert the condition in x64 mode to simplify the conditional jump logic.
+ jcc.opcode = 0x71 ^ cond;
+ jcc.address = dest;
+#else
+ jcc.opcode1 = 0x80 | cond;
+ jcc.operand = (UINT32)(dest - (pNewInst + sizeof(jcc)));
+#endif
+ pCopySrc = &jcc;
+ copySize = sizeof(jcc);
+ }
+ }
+ else if ((hs.opcode & 0xFE) == 0xC2)
+ {
+ // RET (C2 or C3)
+
+ // Complete the function if not in a branch.
+ finished = (pOldInst >= jmpDest);
+ }
+
+ // Can't alter the instruction length in a branch.
+ if (pOldInst < jmpDest && copySize != hs.len)
+ return FALSE;
+
+ // Trampoline function is too large.
+ if ((newPos + copySize) > TRAMPOLINE_MAX_SIZE)
+ return FALSE;
+
+ // Trampoline function has too many instructions.
+ if (ct->nIP >= ARRAYSIZE(ct->oldIPs))
+ return FALSE;
+
+ ct->oldIPs[ct->nIP] = oldPos;
+ ct->newIPs[ct->nIP] = newPos;
+ ct->nIP++;
+
+ // Avoid using memcpy to reduce the footprint.
+#ifndef ALLOW_INTRINSICS
+ memcpy((LPBYTE)ct->pTrampoline + newPos, pCopySrc, copySize);
+#else
+ __movsb((LPBYTE)ct->pTrampoline + newPos, (LPBYTE)pCopySrc, copySize);
+#endif
+ newPos += copySize;
+ oldPos += hs.len;
+ } while (!finished);
+
+ // Is there enough place for a long jump?
+ if (oldPos < sizeof(JMP_REL)
+ && !IsCodePadding((LPBYTE)ct->pTarget + oldPos, sizeof(JMP_REL) - oldPos))
+ {
+ // Is there enough place for a short jump?
+ if (oldPos < sizeof(JMP_REL_SHORT)
+ && !IsCodePadding((LPBYTE)ct->pTarget + oldPos, sizeof(JMP_REL_SHORT) - oldPos))
+ {
+ return FALSE;
+ }
+
+ // Can we place the long jump above the function?
+ if (!IsExecutableAddress((LPBYTE)ct->pTarget - sizeof(JMP_REL)))
+ return FALSE;
+
+ if (!IsCodePadding((LPBYTE)ct->pTarget - sizeof(JMP_REL), sizeof(JMP_REL)))
+ return FALSE;
+
+ ct->patchAbove = TRUE;
+ }
+
+#if defined(_M_X64) || defined(__x86_64__)
+ // Create a relay function.
+ jmp.address = (ULONG_PTR)ct->pDetour;
+
+ ct->pRelay = (LPBYTE)ct->pTrampoline + newPos;
+ memcpy(ct->pRelay, &jmp, sizeof(jmp));
+#endif
+
+ return TRUE;
+}
diff --git a/lib/minhook/src/trampoline.h b/lib/minhook/src/trampoline.h
new file mode 100644
index 0000000..bdffdac
--- /dev/null
+++ b/lib/minhook/src/trampoline.h
@@ -0,0 +1,105 @@
+/*
+ * MinHook - The Minimalistic API Hooking Library for x64/x86
+ * Copyright (C) 2009-2017 Tsuda Kageyu.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#pragma pack(push, 1)
+
+// Structs for writing x86/x64 instructions.
+
+// 8-bit relative jump.
+typedef struct _JMP_REL_SHORT
+{
+ UINT8 opcode; // EB xx: JMP +2+xx
+ UINT8 operand;
+} JMP_REL_SHORT, *PJMP_REL_SHORT;
+
+// 32-bit direct relative jump/call.
+typedef struct _JMP_REL
+{
+ UINT8 opcode; // E9/E8 xxxxxxxx: JMP/CALL +5+xxxxxxxx
+ UINT32 operand; // Relative destination address
+} JMP_REL, *PJMP_REL, CALL_REL;
+
+// 64-bit indirect absolute jump.
+typedef struct _JMP_ABS
+{
+ UINT8 opcode0; // FF25 00000000: JMP [+6]
+ UINT8 opcode1;
+ UINT32 dummy;
+ UINT64 address; // Absolute destination address
+} JMP_ABS, *PJMP_ABS;
+
+// 64-bit indirect absolute call.
+typedef struct _CALL_ABS
+{
+ UINT8 opcode0; // FF15 00000002: CALL [+6]
+ UINT8 opcode1;
+ UINT32 dummy0;
+ UINT8 dummy1; // EB 08: JMP +10
+ UINT8 dummy2;
+ UINT64 address; // Absolute destination address
+} CALL_ABS;
+
+// 32-bit direct relative conditional jumps.
+typedef struct _JCC_REL
+{
+ UINT8 opcode0; // 0F8* xxxxxxxx: J** +6+xxxxxxxx
+ UINT8 opcode1;
+ UINT32 operand; // Relative destination address
+} JCC_REL;
+
+// 64bit indirect absolute conditional jumps that x64 lacks.
+typedef struct _JCC_ABS
+{
+ UINT8 opcode; // 7* 0E: J** +16
+ UINT8 dummy0;
+ UINT8 dummy1; // FF25 00000000: JMP [+6]
+ UINT8 dummy2;
+ UINT32 dummy3;
+ UINT64 address; // Absolute destination address
+} JCC_ABS;
+
+#pragma pack(pop)
+
+typedef struct _TRAMPOLINE
+{
+ LPVOID pTarget; // [In] Address of the target function.
+ LPVOID pDetour; // [In] Address of the detour function.
+ LPVOID pTrampoline; // [In] Buffer address for the trampoline and relay function.
+
+#if defined(_M_X64) || defined(__x86_64__)
+ LPVOID pRelay; // [Out] Address of the relay function.
+#endif
+ BOOL patchAbove; // [Out] Should use the hot patch area?
+ UINT nIP; // [Out] Number of the instruction boundaries.
+ UINT8 oldIPs[8]; // [Out] Instruction boundaries of the target function.
+ UINT8 newIPs[8]; // [Out] Instruction boundaries of the trampoline function.
+} TRAMPOLINE, *PTRAMPOLINE;
+
+BOOL CreateTrampolineFunction(PTRAMPOLINE ct);
diff --git a/lua/ScriptsLoader.lua b/lua/ScriptsLoader.lua
index 26900b9..3afa810 100644
--- a/lua/ScriptsLoader.lua
+++ b/lua/ScriptsLoader.lua
@@ -1,34 +1,39 @@
local ffi = require("ffi")
return function(folder)
- local mask = folder .. "/*.lua"
- local find_data = ffi.new("WIN32_FIND_DATAA")
+ local mask = folder .. "/*.lua"
+ local find_data = ffi.new("WIN32_FIND_DATAA")
- local hFind = ffi.C.FindFirstFileA(mask, find_data)
+ local hFind = ffi.C.FindFirstFileA(mask, find_data)
- if hFind == ffi.cast("void*", -1) then
- print("[Loader] Nothing found!")
- return
- end
+ if hFind == ffi.cast("void*", -1) then
+ print("[Loader] No scripts found.")
+ return
+ end
- repeat
- local file_name = ffi.string(find_data.cFileName)
- local first_char = file_name:sub(1, 1)
+ repeat
+ local file_name = ffi.string(find_data.cFileName)
- if file_name ~= "." and file_name ~= ".." and first_char ~= "." and first_char ~= "_" then
- local mod_name = file_name:sub(1, -5)
+ if file_name ~= "" then
+ local first_char = file_name:sub(1, 1)
- local require_path = mod_name
+ if file_name ~= "." and file_name ~= ".." and first_char ~= "." and first_char ~= "_" then
+ local module = file_name:sub(1, -5)
- print("[Loader] Loading: " .. file_name .. "...")
+ print("[Loader] Loading " .. file_name)
- local ok, err = pcall(require, require_path)
- if not ok then
- print("[Loader] Error in " .. file_name .. ":" .. err)
- end
- else
- print("[Loader] Skipped file: " .. file_name)
- end
- until not ffi.C.FindNextFileA(hFind, find_data)
- ffi.C.FindClose(hFind)
+ local ok, err = pcall(require, module)
+
+ if not ok then
+ print("[Loader] Error loading " .. file_name .. ":")
+ print(err)
+ end
+ else
+ print("[Loader] Skipped " .. file_name)
+ end
+ end
+
+ until not ffi.C.FindNextFileA(hFind, find_data)
+
+ ffi.C.FindClose(hFind)
end
diff --git a/lua/api/VahCrash.lua b/lua/api/VahCrash.lua
index e85b0c4..e9095a2 100644
--- a/lua/api/VahCrash.lua
+++ b/lua/api/VahCrash.lua
@@ -1,9 +1,9 @@
local ffi = require("ffi")
return {
- catch = function(crash_addr, safe_addr)
- print(string.format("[CrashHandler] Protection established: 0x%X -> jump to 0x%X", crash_addr, safe_addr))
+ catch = function(crash_addr, safe_addr)
+ print(string.format("[CrashHandler] Recovery: 0x%X -> 0x%X", crash_addr, safe_addr))
- _G.Core.Core_RegisterRecoveryPoint(crash_addr, safe_addr)
- end
+ _G.Core.Core_RegisterRecoveryPoint(crash_addr, safe_addr)
+ end,
}
diff --git a/lua/api/hooks.lua b/lua/api/hooks.lua
index e1c5854..28f986a 100644
--- a/lua/api/hooks.lua
+++ b/lua/api/hooks.lua
@@ -4,34 +4,59 @@ _G.ActiveHooks = _G.ActiveHooks or {}
_G.ActiveCallbacks = _G.ActiveCallbacks or {}
return {
- create = function(address, signature, callback)
- local type_name = "Hook_" .. string.format("%X", address)
- local cdef_str = "typedef " .. string.gsub(signature, "NAME", type_name) .. ";"
- ffi.cdef(cdef_str)
+ create = function(address, signature, callback)
+ assert(type(signature) == "string", "signature must be a string")
+ assert(type(callback) == "function", "callback must be a function")
- local detour = ffi.cast(type_name, callback)
- _G.ActiveHooks[type_name] = detour
+ local type_name = "Hook_" .. string.format("%X", address)
+ local cdef_str = "typedef " .. signature:gsub("NAME", type_name) .. ";"
- local orig = _G.Core.Core_CreateHook(ffi.cast("void*", address), ffi.cast("void*", detour))
- if orig ~= nil then
- return ffi.cast(type_name, orig)
- end
- return nil
- end,
+ ffi.cdef(cdef_str)
- create_usercall = function(address, callback)
- local cb = ffi.cast("int (__cdecl *)(int, int, void*, void*, int, int)", callback)
+ local detour = ffi.cast(type_name, callback)
+ _G.ActiveHooks[type_name] = detour
- _G.ActiveCallbacks[tostring(address)] = cb
+ local orig = _G.Core.Core_CreateHook(ffi.cast("void*", address), ffi.cast("void*", detour))
- local orig = _G.Core.CreateUsercallBridge(ffi.cast("void*", address), cb)
+ if orig == nil then
+ print(string.format("[Hooks] Failed to hook 0x%X", address))
+ return nil
+ end
- if orig ~= nil then
- print(string.format("[Lua_Hook] Usercall bridge installed at 0x%X", address))
- return orig
- end
+ print(string.format("[Hooks] Hook installed at 0x%X", address))
- print(string.format("[Lua_Hook] ERROR installing usercall at 0x%X", address))
- return nil
- end
+ return ffi.cast(type_name, orig)
+ end,
+
+ create_usercall = function(address, callback)
+ assert(type(callback) == "function", "callback must be a function")
+
+ local cb = ffi.cast("int (__cdecl *)(int32_t, int32_t, int32_t, int32_t, int32_t, int32_t)", callback)
+
+ _G.ActiveCallbacks[tostring(address)] = cb
+
+ local orig = _G.Core.CreateUsercallBridge(ffi.cast("void*", address), cb)
+
+ if orig == nil then
+ print(string.format("[Hooks] Failed to create usercall bridge at 0x%X", address))
+ end
+
+ return orig
+ end,
+
+ create_damage_hook = function(address, callback)
+ assert(type(callback) == "function", "callback must be a function")
+
+ local cb = ffi.cast("void (__cdecl *)(void*, int, int, int, int)", callback)
+
+ _G.ActiveCallbacks["DamagePostHook"] = cb
+
+ local orig = _G.Core.CreateDamagePostHook(ffi.cast("void*", address), cb)
+
+ if orig == nil then
+ print(string.format("[Hooks] Failed to create damage hook at 0x%X", address))
+ end
+
+ return orig
+ end,
}
diff --git a/lua/api/input.lua b/lua/api/input.lua
deleted file mode 100644
index 97cab7a..0000000
--- a/lua/api/input.lua
+++ /dev/null
@@ -1,32 +0,0 @@
-local ffi = require("ffi")
-
-ffi.cdef [[
- short GetAsyncKeyState(int vKey);
-]]
-
-local binds = {}
-local states = {}
-
-local function on_tick()
- for key, cb in pairs(binds) do
- local state = bit.band(ffi.C.GetAsyncKeyState(key), 0x8000) ~= 0
- if state and not states[key] then
- cb()
- end
- states[key] = state
- end
-end
-
-_G._CoreTickCallback = ffi.cast("void (*)()", on_tick)
-_G.Core.Core_RegisterTickCallback(_G._CoreTickCallback)
-
-return {
- bind = function(key, callback)
- binds[key] = callback
- states[key] = false
- end,
- unbind = function(key)
- binds[key] = nil
- states[key] = nil
- end
-}
diff --git a/lua/api/memory.lua b/lua/api/memory.lua
index 2fb36d4..fe9d58b 100644
--- a/lua/api/memory.lua
+++ b/lua/api/memory.lua
@@ -1,38 +1,50 @@
local ffi = require("ffi")
return {
- -- Забивает NOP-ами (0x90)
- write_nop = function(address, size)
- print(string.format("[Memory] NOP patch: 0x%X (Size: %d)", address, size))
- local nops = ffi.new("uint8_t[?]", size)
- ffi.fill(nops, size, 0x90)
- return _G.Core.Core_PatchMemory(address, nops, size)
- end,
-
- -- Пишет произвольные байты (например: {0xEB, 0x01})
- patch = function(address, bytes)
- print(string.format("[Memory] Byte patch: 0x%X (Bytes: %d)", address, #bytes))
- local b = ffi.new("uint8_t[?]", #bytes, bytes)
- return _G.Core.Core_PatchMemory(address, b, #bytes)
- end,
-
- -- Чтение значений
- read_int = function(addr)
- return ffi.cast("int32_t*", addr)[0]
- end,
-
- read_float = function(addr)
- return ffi.cast("float*", addr)[0]
- end,
-
- -- Запись значений
- write_int = function(addr, val)
- print(string.format("[Memory] Write Int32: %d -> 0x%X", val, addr))
- ffi.cast("int32_t*", addr)[0] = val
- end,
-
- write_float = function(addr, val)
- print(string.format("[Memory] Write Float: %.2f -> 0x%X", val, addr))
- ffi.cast("float*", addr)[0] = val
- end
+
+ write_nop = function(address, size)
+ assert(size > 0, "size must be > 0")
+
+ print(string.format("[Memory] NOP patch: 0x%X (%d bytes)", address, size))
+
+ local nops = ffi.new("uint8_t[?]", size)
+ ffi.fill(nops, size, 0x90)
+
+ return _G.Core.Core_PatchMemory(address, nops, size)
+ end,
+
+ patch = function(address, bytes)
+ assert(type(bytes) == "table", "bytes must be a table")
+ assert(#bytes > 0, "bytes cannot be empty")
+
+ print(string.format("[Memory] Byte patch: 0x%X (%d bytes)", address, #bytes))
+
+ local buffer = ffi.new("uint8_t[?]", #bytes)
+
+ for i = 1, #bytes do
+ buffer[i - 1] = bytes[i]
+ end
+
+ return _G.Core.Core_PatchMemory(address, buffer, #bytes)
+ end,
+
+ read_int = function(addr)
+ return ffi.cast("int32_t*", addr)[0]
+ end,
+
+ read_float = function(addr)
+ return ffi.cast("float*", addr)[0]
+ end,
+
+ write_int = function(addr, val)
+ print(string.format("[Memory] Write Int32: %d -> 0x%X", val, addr))
+
+ ffi.cast("int32_t*", addr)[0] = val
+ end,
+
+ write_float = function(addr, val)
+ print(string.format("[Memory] Write Float: %.2f -> 0x%X", val, addr))
+
+ ffi.cast("float*", addr)[0] = val
+ end,
}
diff --git a/lua/api/tick.lua b/lua/api/tick.lua
new file mode 100644
index 0000000..37d4984
--- /dev/null
+++ b/lua/api/tick.lua
@@ -0,0 +1,33 @@
+local ffi = require("ffi")
+
+local callbacks = {}
+local tick_cb_anchor
+
+tick_cb_anchor = ffi.cast("void (*)(void)", function()
+ for i = 1, #callbacks do
+ local ok, err = pcall(callbacks[i])
+
+ if not ok then
+ print("[Tick] Callback error:")
+ print(err)
+ end
+ end
+end)
+
+_G.TickCallback_KeepAlive = tick_cb_anchor
+_G.Core.Core_RegisterTickCallback(tick_cb_anchor)
+
+return {
+ add = function(callback_fn)
+ assert(type(callback_fn) == "function", "callback_fn must be a function")
+
+ callbacks[#callbacks + 1] = callback_fn
+
+ print(string.format("[Tick] Callback registered. Total: %d", #callbacks))
+ end,
+
+ clear = function()
+ callbacks = {}
+ print("[Tick] All callbacks cleared.")
+ end,
+}
diff --git a/lua/core.lua b/lua/core.lua
index 99b8d95..ef44b49 100644
--- a/lua/core.lua
+++ b/lua/core.lua
@@ -1,6 +1,6 @@
local ffi = require("ffi")
-ffi.cdef [[
+ffi.cdef([[
typedef struct {
uint32_t dwFileAttributes;
uint32_t ftCreationTime[2];
@@ -21,33 +21,41 @@ ffi.cdef [[
void Core_Log(const char *text);
bool __cdecl Core_PatchMemory(uintptr_t address, const uint8_t* newBytes, size_t size);
void* __cdecl Core_CreateHook(void* target, void* detour);
- void* CreateUsercallBridge(void* targetAddress, void* luaCallback);
+ void* CreateDamagePostHook(void* targetAddress, void* luaCallback);
void __cdecl Core_RegisterRecoveryPoint(uintptr_t crashAddr, uintptr_t safeAddr);
-
void __cdecl Core_RegisterTickCallback(void* cb);
-]]
+]])
+
+_G.Core = ffi.load("RafLoader.asi")
-_G.Core = ffi.load("RafLoader.asi")
-package.path = "./scripts/?.lua;?.lua"
+package.path = "./scripts/?.lua;?.lua;" .. package.path
_G.ActiveHooks = {}
-_G.Engine = {}
+_G.Engine = {}
-_G.print = function(...)
- local args = { ... }
- local str = {}
- for i, v in ipairs(args) do table.insert(str, tostring(v)) end
- _G.Core.Core_Log(table.concat(str, "\t"))
-end
+_G.print = function(...)
+ local args = {}
+ for i = 1, select("#", ...) do
+ args[#args + 1] = tostring(select(i, ...))
+ end
--- API
-_G.Engine.Memory = require("memory")
-_G.Engine.Hooks = require("hooks")
+ Core.Core_Log(table.concat(args, "\t"))
+end
+
+_G.Engine.Tick = require("tick")
+_G.Engine.Memory = require("memory")
+_G.Engine.Hooks = require("hooks")
_G.Engine.VahCrash = require("VahCrash")
-_G.Engine.Input = require("input")
-local loader = require("ScriptsLoader")
+local loader = require("ScriptsLoader")
+
+print("[Core] System ready. API composed.")
-print("[Core] System ready. API composed. \n")
-loader("scripts")
+local scripts_loaded = false
+_G.Engine.Tick.add(function()
+ if not scripts_loaded then
+ scripts_loaded = true
+ loader("scripts")
+ end
+end)
diff --git a/lua51.dll b/lua51.dll
deleted file mode 100644
index a725e02..0000000
Binary files a/lua51.dll and /dev/null differ
diff --git a/src/ConsoleState.cpp b/src/ConsoleState.cpp
index 8c8c45c..61e3fd7 100644
--- a/src/ConsoleState.cpp
+++ b/src/ConsoleState.cpp
@@ -1,5 +1,9 @@
#define LOG_SYSTEM_IMPLEMENTED
+
#include "ConsoleState.h"
+
+#include
+#include
#include
bool GIsConsoleOpen = false;
@@ -10,10 +14,19 @@ extern "C" __declspec(dllexport) void __cdecl Core_Log(const char *text) {
if (!text)
return;
- std::lock_guard lock(GLogMutex);
- GLogBuffer.push_back(std::string(" ") + text);
+ {
+ std::lock_guard lock(GLogMutex);
+ GLogBuffer.emplace_back(text);
+ }
::printf("%s\n", text);
+
+ static std::ofstream logFile("RafLoader.log", std::ios::app);
+
+ if (logFile.is_open()) {
+ logFile << text << '\n';
+ logFile.flush();
+ }
}
extern "C" __declspec(dllexport) void __cdecl Core_ToggleConsole(bool state) {
diff --git a/src/CrashHandler.cpp b/src/CrashHandler.cpp
index 2f835ac..d48a69e 100644
--- a/src/CrashHandler.cpp
+++ b/src/CrashHandler.cpp
@@ -1,8 +1,10 @@
#include