Fix LuaJIT build script to add dependency files for luajit executable.

The luajit executable should now be able to generate Lua bytecode files in Bin/ directory under the project root directory or in any directory when Urho3D SDK is installed in the filesystem.
This commit is contained in:
Yao Wei Tjong 姚伟忠 2014-02-08 00:27:09 +08:00
parent 6bc7ad853e
commit c5fe4a5698
14 changed files with 4331 additions and 7 deletions

1
.gitignore vendored
View File

@ -9,6 +9,7 @@ Bin/*
!Bin/*.sh
Bin/*.app/
Bin/*.app.dSYM/
Bin/Data/LuaScripts/jit/
*-Bin/
Lib/
*-Lib/

View File

@ -101,8 +101,7 @@ endif ()
# Add definitions for GLEW
if (NOT IOS AND NOT ANDROID AND NOT RASPI AND USE_OPENGL)
add_definitions (-DGLEW_STATIC)
add_definitions (-DGLEW_NO_GLU)
add_definitions (-DGLEW_STATIC -DGLEW_NO_GLU)
endif ()
# Enable AngelScript by default
@ -121,6 +120,10 @@ if (ENABLE_LUAJIT)
set (JIT JIT)
# Implied ENABLE_LUA
set (ENABLE_LUA 1)
# Adjust LuaJIT default search path as necessary (adapted from LuaJIT's original Makefile)
if (NOT CMAKE_INSTALL_PREFIX STREQUAL /usr/local)
add_definitions (-DLUA_XROOT="${CMAKE_INSTALL_PREFIX}/")
endif ()
endif ()
if (ENABLE_LUA)
add_definitions (-DENABLE_LUA)

View File

@ -209,15 +209,15 @@ set (TARGET_NAME LuaJIT)
# Macro for generating source file
macro (generate_source name mode)
set (GEN_SRC ${CMAKE_CURRENT_BINARY_DIR}/generated/${name})
set (GEN_SRCS ${GEN_SRCS} ${GEN_SRC})
set (GEN_SRCS ${GEN_SRCS} ${GEN_SRC})
add_custom_command (OUTPUT ${GEN_SRC}
COMMAND ${PROJECT_ROOT_DIR}/Bin/${BUILDVM_X} -m ${mode} -o ${GEN_SRC} ${ARGN} DEPENDS ${BUILDVM_DEP} ${ARGN}
COMMAND ${PROJECT_ROOT_DIR}/Bin/${BUILDVM_X} -m ${mode} -o ${GEN_SRC} ${ARGN}
DEPENDS ${BUILDVM_DEP} ${ARGN}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMENT "Generating buildvm output: ${name}")
endmacro ()
# Define generated source files
file (MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/generated)
if (WIN32)
set (LJVM_MODE peobj)
set (LJVM_BOUT lj_vm.obj)
@ -243,6 +243,7 @@ generate_source (${LJVM_BOUT} ${LJVM_MODE})
foreach (MODE bcdef ffdef libdef recdef)
generate_source (lj_${MODE}.h ${MODE} ${LJLIB_C})
endforeach ()
generate_source (vmdef.lua vmdef ${LJLIB_C})
generate_source (lj_folddef.h folddef src/lj_opt_fold.c)
# Define source files
@ -285,4 +286,19 @@ if (NOT IOS AND (NOT DEFINED ENABLE_TOOLS OR ENABLE_TOOLS))
# Setup target
setup_executable ()
adjust_target_name () # Adjust to intended target output name
# Define post build steps
set (LUAJIT_DEP_DIR ${PROJECT_ROOT_DIR}/Bin/Data/LuaScripts/jit)
add_custom_command (TARGET ${TARGET_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/src/jit ${LUAJIT_DEP_DIR}
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_BINARY_DIR}/generated/vmdef.lua ${LUAJIT_DEP_DIR}
COMMENT "Copying dependency files for luajit standalone executable")
# Install dependency files required by luajit
install (DIRECTORY ${LUAJIT_DEP_DIR} DESTINATION ${DEST_RUNTIME_DIR}/Data/LuaScripts USE_SOURCE_PERMISSIONS)
endif ()
# Add Data/LuaScripts directory into the LuaJIT module search path
file (TO_NATIVE_PATH Data/LuaScripts/ LUA_RDIR) # Relative directory
file (TO_NATIVE_PATH ${CMAKE_INSTALL_PREFIX}/${DEST_RUNTIME_DIR}/Data/LuaScripts/ LUA_IDIR) # Installation directory
add_definitions (-DLUA_RDIR="${LUA_RDIR}" -DLUA_IDIR="${LUA_IDIR}")

191
Source/ThirdParty/LuaJIT/src/jit/bc.lua vendored Normal file
View File

@ -0,0 +1,191 @@
----------------------------------------------------------------------------
-- LuaJIT bytecode listing module.
--
-- Copyright (C) 2005-2013 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")
assert(jit.version_num == 20002, "LuaJIT core/library version mismatch")
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.
module(...)
line = bcline
dump = bcdump
targets = bctargets
on = bcliston
off = bclistoff
start = bcliston -- For -j command line option.

View File

@ -0,0 +1,659 @@
----------------------------------------------------------------------------
-- LuaJIT module to save/list bytecode.
--
-- Copyright (C) 2005-2013 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 == 20002, "LuaJIT core/library version mismatch")
local bit = require("bit")
-- Symbol name prefix for LuaJIT bytecode.
local LJBC_PREFIX = "luaJIT_BC_"
------------------------------------------------------------------------------
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.
-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).
-e chunk Use chunk string as input.
-- Stop handling options.
- Use stdin as input and/or stdout as output.
File types: c 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(input)
if type(input) == "function" then return input end
if input == "-" then input = nil end
return check(loadfile(input))
end
local function savefile(name, mode)
if name == "-" then return io.stdout end
return check(io.open(name, mode))
end
------------------------------------------------------------------------------
local map_type = {
raw = "raw", c = "c", h = "h", o = "obj", obj = "obj",
}
local map_arch = {
x86 = true, x64 = true, arm = true, ppc = true, ppcspe = true,
mips = true, mipsel = true,
}
local map_os = {
linux = true, windows = true, osx = true, freebsd = true, netbsd = true,
openbsd = true, solaris = true,
}
local function checkarg(str, map, err)
str = string.lower(str)
local s = check(map[str], "unknown ", err)
return s == true and str or s
end
local function detecttype(str)
local ext = string.match(string.lower(str), "%.(%a+)$")
return map_type[ext] or "raw"
end
local function checkmodname(str)
check(string.match(str, "^[%w_.%-]+$"), "bad module name")
return string.gsub(str, "[%.%-]", "_")
end
local function detectmodname(str)
if type(str) == "string" then
local tail = string.match(str, "[^/\\]+$")
if tail then str = tail end
local head = string.match(str, "^(.*)%.[^.]*$")
if head then str = head end
str = string.match(str, "^[%w_.%-]+")
else
str = nil
end
check(str, "cannot derive module name, use -n name")
return string.gsub(str, "[%.%-]", "_")
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)
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(string.format([[
#ifdef _cplusplus
extern "C"
#endif
#ifdef _WIN32
__declspec(dllexport)
#endif
const char %s%s[] = {
]], LJBC_PREFIX, ctx.modname))
else
fp:write(string.format([[
#define %s%s_SIZE %d
static const 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(table.concat(t, ",", 1, n), ",\n")
n, m = 0, #b + 1
end
n = n + 1
t[n] = b
end
bcsave_tail(fp, output, table.concat(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 is64, isbe = false, false
if ctx.arch == "x64" then
is64 = true
elseif ctx.arch == "ppc" or ctx.arch == "ppcspe" or ctx.arch == "mips" then
isbe = true
end
-- 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(({ x86=3, x64=62, arm=40, ppc=20, ppcspe=20, mips=8, mipsel=8 })[ctx.arch])
if ctx.arch == "mips" or ctx.arch == "mipsel" then
hdr.flags = 0x50001006
end
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+1)
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 is64 = false
if ctx.arch == "x86" then
symname = "_"..symname
elseif ctx.arch == "x64" then
is64 = true
end
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(({ x86=0x14c, x64=0x8664, arm=0x1c0, ppc=0x1f2, mips=0x366, mipsel=0x366 })[ctx.arch])
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];
uint32_t vmaddr, vmsize, fileoff, filesize;
uint32_t maxprot, initprot, nsects, flags;
} mach_segment_command;
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];
uint32_t addr, size;
uint32_t offset, align, reloff, nreloc, flags;
uint32_t reserved1, reserved2;
} mach_section;
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;
int16_t desc;
uint32_t value;
} mach_nlist;
typedef struct {
uint32_t strx;
uint8_t type, sect;
uint16_t desc;
uint64_t value;
} mach_nlist_64;
typedef struct
{
uint32_t magic, nfat_arch;
} mach_fat_header;
typedef struct
{
uint32_t cputype, cpusubtype, offset, size, align;
} mach_fat_arch;
typedef struct {
struct {
mach_header hdr;
mach_segment_command seg;
mach_section sec;
mach_symtab_command sym;
} arch[1];
mach_nlist sym_entry;
uint8_t space[4096];
} mach_obj;
typedef struct {
struct {
mach_header_64 hdr;
mach_segment_command_64 seg;
mach_section_64 sec;
mach_symtab_command sym;
} arch[1];
mach_nlist_64 sym_entry;
uint8_t space[4096];
} mach_obj_64;
typedef struct {
mach_fat_header fat;
mach_fat_arch fat_arch[4];
struct {
mach_header hdr;
mach_segment_command seg;
mach_section sec;
mach_symtab_command sym;
} arch[4];
mach_nlist sym_entry;
uint8_t space[4096];
} mach_fat_obj;
]]
local symname = '_'..LJBC_PREFIX..ctx.modname
local isfat, is64, align, mobj = false, false, 4, "mach_obj"
if ctx.arch == "x64" then
is64, align, mobj = true, 8, "mach_obj_64"
elseif ctx.arch == "arm" then
isfat, mobj = true, "mach_fat_obj"
else
check(ctx.arch == "x86", "unsupported architecture for OSX")
end
local function aligned(v, a) return bit.band(v+a-1, -a) end
local be32 = bit.bswap -- Mach-O FAT is BE, supported archs are LE.
-- Create Mach-O object and fill in header.
local o = ffi.new(mobj)
local mach_size = aligned(ffi.offsetof(o, "space")+#symname+2, align)
local cputype = ({ x86={7}, x64={0x01000007}, arm={7,12,12,12} })[ctx.arch]
local cpusubtype = ({ x86={3}, x64={3}, arm={3,6,9,11} })[ctx.arch]
if isfat then
o.fat.magic = be32(0xcafebabe)
o.fat.nfat_arch = be32(#cpusubtype)
end
-- Fill in sections and symbols.
for i=0,#cpusubtype-1 do
local ofs = 0
if isfat then
local a = o.fat_arch[i]
a.cputype = be32(cputype[i+1])
a.cpusubtype = be32(cpusubtype[i+1])
-- Subsequent slices overlap each other to share data.
ofs = ffi.offsetof(o, "arch") + i*ffi.sizeof(o.arch[0])
a.offset = be32(ofs)
a.size = be32(mach_size-ofs+#s)
end
local a = o.arch[i]
a.hdr.magic = is64 and 0xfeedfacf or 0xfeedface
a.hdr.cputype = cputype[i+1]
a.hdr.cpusubtype = cpusubtype[i+1]
a.hdr.filetype = 1
a.hdr.ncmds = 2
a.hdr.sizeofcmds = ffi.sizeof(a.seg)+ffi.sizeof(a.sec)+ffi.sizeof(a.sym)
a.seg.cmd = is64 and 0x19 or 0x1
a.seg.cmdsize = ffi.sizeof(a.seg)+ffi.sizeof(a.sec)
a.seg.vmsize = #s
a.seg.fileoff = mach_size-ofs
a.seg.filesize = #s
a.seg.maxprot = 1
a.seg.initprot = 1
a.seg.nsects = 1
ffi.copy(a.sec.sectname, "__data")
ffi.copy(a.sec.segname, "__DATA")
a.sec.size = #s
a.sec.offset = mach_size-ofs
a.sym.cmd = 2
a.sym.cmdsize = ffi.sizeof(a.sym)
a.sym.symoff = ffi.offsetof(o, "sym_entry")-ofs
a.sym.nsyms = 1
a.sym.stroff = ffi.offsetof(o, "sym_entry")+ffi.sizeof(o.sym_entry)-ofs
a.sym.strsize = aligned(#symname+2, align)
end
o.sym_entry.type = 0xf
o.sym_entry.sect = 1
o.sym_entry.strx = 1
ffi.copy(o.space+1, symname)
-- Write Macho-O object file.
local fp = savefile(output, "wb")
fp:write(ffi.string(o, mach_size))
bcsave_tail(fp, output, s)
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 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(input, output)
local f = readfile(input)
require("jit.bc").dump(f, savefile(output, "w"), true)
end
local function bcsave(ctx, input, output)
local f = readfile(input)
local s = string.dump(f, ctx.strip)
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 = {
strip = true, arch = jit.arch, os = string.lower(jit.os),
type = false, modname = false,
}
while n <= #arg do
local a = arg[n]
if type(a) == "string" and string.sub(a, 1, 1) == "-" and a ~= "-" then
table.remove(arg, n)
if a == "--" then break end
for m=2,#a do
local opt = string.sub(a, m, m)
if opt == "l" then
list = true
elseif opt == "s" then
ctx.strip = true
elseif opt == "g" then
ctx.strip = false
else
if arg[n] == nil or m ~= #a then usage() end
if opt == "e" then
if n ~= 1 then usage() end
arg[1] = check(loadstring(arg[1]))
elseif opt == "n" then
ctx.modname = checkmodname(table.remove(arg, n))
elseif opt == "t" then
ctx.type = checkarg(table.remove(arg, n), map_type, "file type")
elseif opt == "a" then
ctx.arch = checkarg(table.remove(arg, n), map_arch, "architecture")
elseif opt == "o" then
ctx.os = checkarg(table.remove(arg, n), map_os, "OS name")
else
usage()
end
end
end
else
n = n + 1
end
end
if list then
if #arg == 0 or #arg > 2 then usage() end
bclist(arg[1], arg[2] or "-")
else
if #arg ~= 2 then usage() end
bcsave(ctx, arg[1], arg[2])
end
end
------------------------------------------------------------------------------
-- Public module functions.
module(...)
start = docmd -- Process -b command line option.

View File

@ -0,0 +1,689 @@
----------------------------------------------------------------------------
-- LuaJIT ARM disassembler module.
--
-- Copyright (C) 2005-2013 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, gsub = string.match, string.gmatch, string.gsub
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.
module(...)
create = create_
disass = disass_
regname = regname_

View File

@ -0,0 +1,428 @@
----------------------------------------------------------------------------
-- LuaJIT MIPS disassembler module.
--
-- Copyright (C) 2005-2013 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 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, tohex = bit.band, bit.bor, bit.tohex
local lshift, rshift, arshift = bit.lshift, bit.rshift, bit.arshift
------------------------------------------------------------------------------
-- Primary and extended opcode maps
------------------------------------------------------------------------------
local map_movci = { shift = 16, mask = 1, [0] = "movfDSC", "movtDSC", }
local map_srl = { shift = 21, mask = 1, [0] = "srlDTA", "rotrDTA", }
local map_srlv = { shift = 6, mask = 1, [0] = "srlvDTS", "rotrvDTS", }
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",
false, false, false, false,
"multST", "multuST", "divST", "divuST",
false, false, false, false,
"addDST", "addu|moveDST0", "subDST", "subu|neguDS0T",
"andDST", "orDST", "xorDST", "nor|notDST0",
false, false, "sltDST", "sltuDST",
false, false, false, false,
"tgeSTZ", "tgeuSTZ", "tltSTZ", "tltuSTZ",
"teqSTZ", false, "tneSTZ",
}
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_special3 = {
shift = 0, mask = 63,
[0] = "extTSAK", [4] = "insTSAL",
[32] = map_bshfl,
[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_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",
},
}
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", false, "cfc1TG", "mfhc1TG",
"mtc1TG", false, "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",
false, false, false, false,
map_special2, false, 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", false,
"scTSO", "swc1HSO", "swc2TSO", false,
false, "sdc1HSO", "sdc2TSO", false,
}
------------------------------------------------------------------------------
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 = map_pri[rshift(op, 26)]
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
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 == "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 == "L" then
x = band(rshift(op, 11), 31) - last + 1
elseif p == "I" then
x = arshift(lshift(op, 16), 16)
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), 16)*4 + 4
ctx.rel = x
x = "0x"..tohex(x)
elseif p == "J" then
x = band(ctx.addr + ctx.pos, 0xf0000000) + band(op, 0x03ffffff)*4
ctx.rel = x
x = "0x"..tohex(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
return ctx
end
local function create_el_(code, addr, out)
local ctx = create_(code, addr, out)
ctx.get = get_le
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
-- 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.
module(...)
create = create_
create_el = create_el_
disass = disass_
disass_el = disass_el_
regname = regname_

View File

@ -0,0 +1,20 @@
----------------------------------------------------------------------------
-- LuaJIT MIPSEL disassembler wrapper module.
--
-- Copyright (C) 2005-2013 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 require = require
module(...)
local dis_mips = require(_PACKAGE.."dis_mips")
create = dis_mips.create_el
disass = dis_mips.disass_el
regname = dis_mips.regname

View File

@ -0,0 +1,591 @@
----------------------------------------------------------------------------
-- LuaJIT PPC disassembler module.
--
-- Copyright (C) 2005-2013 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 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, 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.
module(...)
create = create_
disass = disass_
regname = regname_

View File

@ -0,0 +1,20 @@
----------------------------------------------------------------------------
-- LuaJIT x64 disassembler wrapper module.
--
-- Copyright (C) 2005-2013 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 require = require
module(...)
local dis_x86 = require(_PACKAGE.."dis_x86")
create = dis_x86.create64
disass = dis_x86.disass64
regname = dis_x86.regname64

View File

@ -0,0 +1,836 @@
----------------------------------------------------------------------------
-- LuaJIT x86/x64 disassembler module.
--
-- Copyright (C) 2005-2013 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 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.
-- * The public API may change when more architectures are added.
------------------------------------------------------------------------------
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
-- 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","$lesVrm","$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]=false, [0xc5]=false, [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|movssXrm|movupdXrm|movsdXrm",
"movupsXmr|movssXmr|movupdXmr|movsdXmr",
"movhlpsXrm$movlpsXrm|movsldupXrm|movlpdXrm|movddupXrm",
"movlpsXmr||movlpdXmr",
"unpcklpsXrm||unpcklpdXrm",
"unpckhpsXrm||unpckhpdXrm",
"movlhpsXrm$movhpsXrm|movshdupXrm|movhpdXrm",
"movhpsXmr||movhpdXmr",
"$prefetcht!Bm","hintnopVm","hintnopVm","hintnopVm",
"hintnopVm","hintnopVm","hintnopVm","hintnopVm",
--2x
"movUmx$","movUmy$","movUxm$","movUym$","movUmz$",nil,"movUzm$",nil,
"movapsXrm||movapdXrm",
"movapsXmr||movapdXmr",
"cvtpi2psXrMm|cvtsi2ssXrVmt|cvtpi2pdXrMm|cvtsi2sdXrVmt",
"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|rsqrtssXrm","rcppsXrm|rcpssXrm",
"andpsXrm||andpdXrm","andnpsXrm||andnpdXrm",
"orpsXrm||orpdXrm","xorpsXrm||xorpdXrm",
"addpsXrm|addssXrm|addpdXrm|addsdXrm","mulpsXrm|mulssXrm|mulpdXrm|mulsdXrm",
"cvtps2pdXrm|cvtss2sdXrm|cvtpd2psXrm|cvtsd2ssXrm",
"cvtdq2psXrm|cvttps2dqXrm|cvtps2dqXrm",
"subpsXrm|subssXrm|subpdXrm|subsdXrm","minpsXrm|minssXrm|minpdXrm|minsdXrm",
"divpsXrm|divssXrm|divpdXrm|divsdXrm","maxpsXrm|maxssXrm|maxpdXrm|maxsdXrm",
--6x
"punpcklbwPrm","punpcklwdPrm","punpckldqPrm","packsswbPrm",
"pcmpgtbPrm","pcmpgtwPrm","pcmpgtdPrm","packuswbPrm",
"punpckhbwPrm","punpckhwdPrm","punpckhdqPrm","packssdwPrm",
"||punpcklqdqXrm","||punpckhqdqXrm",
"movPrVSm","movqMrm|movdquXrm|movdqaXrm",
--7x
"pshufwMrmu|pshufhwXrmu|pshufdXrmu|pshuflwXrmu","pshiftw!Pmu",
"pshiftd!Pmu","pshiftq!Mmu||pshiftdq!Xmu",
"pcmpeqbPrm","pcmpeqwPrm","pcmpeqdPrm","emms|",
"vmreadUmr||extrqXmuu$|insertqXrmuu$","vmwriteUrm||extrqXrm$|insertqXrm$",
nil,nil,
"||haddpdXrm|haddpsXrm","||hsubpdXrm|hsubpsXrm",
"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",
"cmppsXrmu|cmpssXrmu|cmppdXrmu|cmpsdXrmu","$movntiVmr|",
"pinsrwPrWmu","pextrwDrPmu",
"shufpsXrmu||shufpdXrmu","$cmpxchg!Qmp",
"bswapVR","bswapVR","bswapVR","bswapVR","bswapVR","bswapVR","bswapVR","bswapVR",
--Dx
"||addsubpdXrm|addsubpsXrm","psrlwPrm","psrldPrm","psrlqPrm",
"paddqPrm","pmullwPrm",
"|movq2dqXrMm|movqXmr|movdq2qMrXm$","pmovmskbVrMm||pmovmskbVrXm",
"psubusbPrm","psubuswPrm","pminubPrm","pandPrm",
"paddusbPrm","padduswPrm","pmaxubPrm","pandnPrm",
--Ex
"pavgbPrm","psrawPrm","psradPrm","pavgwPrm",
"pmulhuwPrm","pmulhwPrm",
"|cvtdq2pdXrm|cvttpd2dqXrm|cvtpd2dqXrm","$movntqMmr||$movntdqXmr",
"psubsbPrm","psubswPrm","pminswPrm","porPrm",
"paddsbPrm","paddswPrm","pmaxswPrm","pxorPrm",
--Fx
"|||lddquXrm","psllwPrm","pslldPrm","psllqPrm",
"pmuludqPrm","pmaddwdPrm","psadbwPrm","maskmovqMrm||maskmovdquXrm$",
"psubbPrm","psubwPrm","psubdPrm","psubqPrm",
"paddbPrm","paddwPrm","padddPrm","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]="pshufbPrm","phaddwPrm","phadddPrm","phaddswPrm",
"pmaddubswPrm","phsubwPrm","phsubdPrm","phsubswPrm",
"psignbPrm","psignwPrm","psigndPrm","pmulhrswPrm",
nil,nil,nil,nil,
--1x
"||pblendvbXrma",nil,nil,nil,
"||blendvpsXrma","||blendvpdXrma",nil,"||ptestXrm",
nil,nil,nil,nil,
"pabsbPrm","pabswPrm","pabsdPrm",nil,
--2x
"||pmovsxbwXrm","||pmovsxbdXrm","||pmovsxbqXrm","||pmovsxwdXrm",
"||pmovsxwqXrm","||pmovsxdqXrm",nil,nil,
"||pmuldqXrm","||pcmpeqqXrm","||$movntdqaXrm","||packusdwXrm",
nil,nil,nil,nil,
--3x
"||pmovzxbwXrm","||pmovzxbdXrm","||pmovzxbqXrm","||pmovzxwdXrm",
"||pmovzxwqXrm","||pmovzxdqXrm",nil,"||pcmpgtqXrm",
"||pminsbXrm","||pminsdXrm","||pminuwXrm","||pminudXrm",
"||pmaxsbXrm","||pmaxsdXrm","||pmaxuwXrm","||pmaxudXrm",
--4x
"||pmulddXrm","||phminposuwXrm",
--Fx
[0xf0] = "|||crc32TrBmt",[0xf1] = "|||crc32TrVmt",
},
["3a"] = { -- [66] 0f 3a xx
--0x
[0x00]=nil,nil,nil,nil,nil,nil,nil,nil,
"||roundpsXrmu","||roundpdXrmu","||roundssXrmu","||roundsdXrmu",
"||blendpsXrmu","||blendpdXrmu","||pblendwXrmu","palignrPrmu",
--1x
nil,nil,nil,nil,
"||pextrbVmXru","||pextrwVmXru","||pextrVmSXru","||extractpsVmXru",
nil,nil,nil,nil,nil,nil,nil,nil,
--2x
"||pinsrbXrVmu","||insertpsXrmu","||pinsrXrVmuS",nil,
--4x
[0x40] = "||dppsXrmu",
[0x41] = "||dppdXrmu",
[0x42] = "||mpsadbwXrmu",
--6x
[0x60] = "||pcmpestrmXrmu",[0x61] = "||pcmpestriXrmu",
[0x62] = "||pcmpistrmXrmu",[0x63] = "||pcmpistriXrmu",
},
}
-- 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" },
}
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,
}
local map_sz2prefix = {
B = "byte", W = "word", D = "dword",
Q = "qword",
M = "qword", X = "xword",
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 "")
if t ~= "" then text = "rex."..t.." "..text end
ctx.rexw = false; ctx.rexr = false; ctx.rexx = false; ctx.rexb = false
ctx.rex = 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.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
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 = ctx.code, ctx.pos, ctx.stop
-- Chars used: 1DFGIMPQRSTUVWXacdfgijmoprstuwxyz
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, "[WDQMXFG]") then
sz = p
regs = map_regs[sz]
elseif p == "P" then
sz = ctx.o16 and "X" or "M"; ctx.o16 = false
regs = map_regs[sz]
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 == "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 = format("0x%08x", 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 == "y" then x = "DR"..sp
elseif p == "z" then x = "TR"..sp
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,
-- 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,
-- 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 prefix allowed.
for p in gmatch(pat, ".") do ctx["rex"..p] = true end
ctx.rex = true
end,
-- Special case for nop with REX prefix.
nop = function(ctx, name, pat)
return dispatch(ctx, ctx.rex and pat or "nop")
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.
module(...)
create = create_
create64 = create64_
disass = disass_
disass64 = disass64_
regname = regname_
regname64 = regname64_

View File

@ -0,0 +1,700 @@
----------------------------------------------------------------------------
-- LuaJIT compiler dump module.
--
-- Copyright (C) 2005-2013 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.
--
-- 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")
assert(jit.version_num == 20002, "LuaJIT core/library version mismatch")
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, shl, shr = bit.band, bit.lshift, bit.rshift
local sub, gsub, format = string.sub, string.gsub, string.format
local byte, char, rep = string.byte, string.char, 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 == "mips" or jit.arch == "mipsel" then
t[traceexitstub(tr, 0)] = "exit"
return
end
for i=0,nexit-1 do
local addr = traceexitstub(tr, i)
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 ircall = vmdef.ircall
for i=0,#ircall do
local addr = ircalladdr(i)
if addr ~= 0 then 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
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
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, t)
return s
end
local function colorize_ansi(s, t)
return format(colortype_ansi[t], s)
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 = { ["<"] = "&lt;", [">"] = "&gt;", ["&"] = "&amp;", }
local function colorize_html(s, t)
s = gsub(s, "[<>&]", html_escape)
return format('<span class="irt_%s">%s</span>', irtype_text[t], 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 = [[
<style type="text/css">
background { background: #ffffff; color: #000000; }
pre.ljdump {
font-size: 10pt;
background: #f0f4ff;
color: #000000;
border: 1px solid #bfcfff;
padding: 0.5em;
margin-left: 2em;
margin-right: 2em;
}
span.irt_str { color: #00a000; }
span.irt_thr, span.irt_fun { color: #404040; font-weight: bold; }
span.irt_tab { color: #c00000; }
span.irt_udt, span.irt_lud { color: #00c0c0; }
span.irt_num { color: #4040c0; }
span.irt_int, span.irt_i8, span.irt_u8, span.irt_i16, span.irt_u16 { color: #b040b0; }
</style>
]]
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
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, 0x400) ~= 0 then s = s.." trunc"
elseif band(mode, 0x800) ~= 0 then s = s.." sext" end
local c = shr(mode, 14)
if 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,
}
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)
local k, t, slot = tracek(tr, idx)
local tn = type(k)
local s
if tn == "number" then
if k == 2^52+2^51 then
s = "bias"
else
s = format("%+.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 == "[0x00000000]" 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
else
s = tostring(k) -- For primitives.
end
s = colorize(format("%-4s", s), t)
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))
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)))
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
err = format(vmdef.traceerr[err], 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('<pre class="ljdump">\n') end
out:write("---- TRACE ", tr, " ", what)
if otr then out:write(" ", otr, "/", oex) end
out:write(" ", fmtfunc(func, pc), "\n")
recprefix = ""
elseif what == "stop" or what == "abort" then
out:write("---- TRACE ", tr, " ", what)
recprefix = nil
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("</pre>\n\n") else out:write("\n") end
else
out:write("---- TRACE ", what, "\n\n")
end
out:flush()
end
-- Dump recorded bytecode.
local function dump_record(tr, func, pc, depth, callee)
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"
callee = func
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
------------------------------------------------------------------------------
-- 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 jit.arch == "x64" 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(format(" %08x", regs[i]))
if i % 8 == 0 then out:write("\n") end
end
end
if jit.arch == "mips" or jit.arch == "mipsel" 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 colormode = 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.
module(...)
on = dumpon
off = dumpoff
start = dumpon -- For -j command line option.

167
Source/ThirdParty/LuaJIT/src/jit/v.lua vendored Normal file
View File

@ -0,0 +1,167 @@
----------------------------------------------------------------------------
-- Verbose mode of the LuaJIT compiler.
--
-- Copyright (C) 2005-2013 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")
assert(jit.version_num == 20002, "LuaJIT core/library version mismatch")
local jutil = require("jit.util")
local vmdef = require("jit.vmdef")
local funcinfo, traceinfo = jutil.funcinfo, jutil.traceinfo
local type, format = type, 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
err = format(vmdef.traceerr[err], 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..") " 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 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.
module(...)
on = dumpon
off = dumpoff
start = dumpon -- For -j command line option.

View File

@ -3,6 +3,8 @@
** Copyright (C) 2005-2013 Mike Pall. See Copyright Notice in luajit.h
*/
// Modified by Yao Wei Tjong for Urho3D
#ifndef luaconf_h
#define luaconf_h
@ -10,6 +12,7 @@
#include <stddef.h>
/* Default path for loading Lua and C modules with require(). */
// Urho3D: add Urho3D-specific directories into search path
#if defined(_WIN32)
/*
** In Windows, any exclamation mark ('!') in the path is replaced by the
@ -18,7 +21,7 @@
#define LUA_LDIR "!\\lua\\"
#define LUA_CDIR "!\\"
#define LUA_PATH_DEFAULT \
".\\?.lua;" LUA_LDIR"?.lua;" LUA_LDIR"?\\init.lua;"
".\\?.lua;" "!\\"LUA_RDIR"?.lua;" LUA_IDIR"?.lua;" LUA_LDIR"?.lua;" LUA_LDIR"?\\init.lua;"
#define LUA_CPATH_DEFAULT \
".\\?.dll;" LUA_CDIR"?.dll;" LUA_CDIR"loadall.dll"
#else
@ -40,7 +43,7 @@
#define LUA_XCPATH
#endif
#define LUA_PATH_DEFAULT \
"./?.lua;" LUA_JDIR"?.lua;" LUA_LDIR"?.lua;" LUA_LDIR"?/init.lua" LUA_XPATH
"./?.lua;" "./"LUA_RDIR"?.lua;" LUA_IDIR"?.lua;" LUA_JDIR"?.lua;" LUA_LDIR"?.lua;" LUA_LDIR"?/init.lua" LUA_XPATH
#define LUA_CPATH_DEFAULT \
"./?.so;" LUA_CDIR"?.so;" LUA_XCPATH LUA_CDIR"loadall.so"
#endif