summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/.gitignore1
-rw-r--r--lib/bc.lua182
-rw-r--r--lib/dis_x64.lua19
-rw-r--r--lib/dis_x86.lua824
-rw-r--r--lib/dump.lua567
-rw-r--r--lib/v.lua156
6 files changed, 1749 insertions, 0 deletions
diff --git a/lib/.gitignore b/lib/.gitignore
new file mode 100644
index 00000000..500e2855
--- /dev/null
+++ b/lib/.gitignore
@@ -0,0 +1 @@
vmdef.lua
diff --git a/lib/bc.lua b/lib/bc.lua
new file mode 100644
index 00000000..532f2493
--- /dev/null
+++ b/lib/bc.lua
@@ -0,0 +1,182 @@
1----------------------------------------------------------------------------
2-- LuaJIT bytecode listing module.
3--
4-- Copyright (C) 2005-2009 Mike Pall. All rights reserved.
5-- Released under the MIT/X license. See Copyright Notice in luajit.h
6----------------------------------------------------------------------------
7--
8-- This module lists the bytecode of a Lua function. If it's loaded by -jbc
9-- it hooks into the parser and lists all functions of a chunk as they
10-- are parsed.
11--
12-- Example usage:
13--
14-- luajit -jbc -e 'local x=0; for i=1,1e6 do x=x+i end; print(x)'
15-- luajit -jbc=- foo.lua
16-- luajit -jbc=foo.list foo.lua
17--
18-- Default output is to stderr. To redirect the output to a file, pass a
19-- filename as an argument (use '-' for stdout) or set the environment
20-- variable LUAJIT_LISTFILE. The file is overwritten every time the module
21-- is started.
22--
23-- This module can also be used programmatically:
24--
25-- local bc = require("jit.bc")
26--
27-- local function foo() print("hello") end
28--
29-- bc.dump(foo) --> -- BYTECODE -- [...]
30-- print(bc.line(foo, 2)) --> 0002 KSTR 1 1 ; "hello"
31--
32-- local out = {
33-- -- Do something wich each line:
34-- write = function(t, ...) io.write(...) end,
35-- close = function(t) end,
36-- flush = function(t) end,
37-- }
38-- bc.dump(foo, out)
39--
40------------------------------------------------------------------------------
41
42-- Cache some library functions and objects.
43local jit = require("jit")
44assert(jit.version_num == 20000, "LuaJIT core/library version mismatch")
45local jutil = require("jit.util")
46local vmdef = require("jit.vmdef")
47local bit = require("bit")
48local sub, gsub, format = string.sub, string.gsub, string.format
49local byte, band, shr = string.byte, bit.band, bit.rshift
50local funcinfo, funcbc, funck = jutil.funcinfo, jutil.funcbc, jutil.funck
51local funcuvname = jutil.funcuvname
52local bcnames = vmdef.bcnames
53local stdout, stderr = io.stdout, io.stderr
54
55------------------------------------------------------------------------------
56
57local function ctlsub(c)
58 if c == "\n" then return "\\n"
59 elseif c == "\r" then return "\\r"
60 elseif c == "\t" then return "\\t"
61 elseif c == "\r" then return "\\r"
62 else return format("\\%03d", byte(c))
63 end
64end
65
66-- Return one bytecode line.
67local function bcline(func, pc, prefix)
68 local ins, m = funcbc(func, pc)
69 if not ins then return end
70 local ma, mb, mc = band(m, 7), band(m, 15*8), band(m, 15*128)
71 local a = band(shr(ins, 8), 0xff)
72 local oidx = 6*band(ins, 0xff)
73 local s = format("%04d %s %-6s %3s ",
74 pc, prefix or " ", sub(bcnames, oidx+1, oidx+6), ma == 0 and "" or a)
75 local d = shr(ins, 16)
76 if mc == 13*128 then -- BCMjump
77 if ma == 0 then
78 return format("%s=> %04d\n", sub(s, 1, -3), pc+d-0x7fff)
79 end
80 return format("%s=> %04d\n", s, pc+d-0x7fff)
81 end
82 if mb ~= 0 then d = band(d, 0xff) end
83 local kc
84 if mc == 10*128 then -- BCMstr
85 kc = funck(func, -d-1)
86 kc = format(#kc > 40 and '"%.40s"~' or '"%s"', gsub(kc, "%c", ctlsub))
87 elseif mc == 9*128 then -- BCMnum
88 kc = funck(func, d)
89 elseif mc == 12*128 then -- BCMfunc
90 local fi = funcinfo(funck(func, -d-1))
91 if fi.ffid then
92 kc = vmdef.ffnames[fi.ffid]
93 else
94 kc = fi.loc
95 end
96 elseif mc == 5*128 then -- BCMuv
97 kc = funcuvname(func, d)
98 end
99 if ma == 5 then -- BCMuv
100 local ka = funcuvname(func, a)
101 if kc then kc = ka.." ; "..kc else kc = ka end
102 end
103 if mb ~= 0 then
104 local b = shr(ins, 24)
105 if kc then return format("%s%3d %3d ; %s\n", s, b, d, kc) end
106 return format("%s%3d %3d\n", s, b, d)
107 end
108 if kc then return format("%s%3d ; %s\n", s, d, kc) end
109 if mc == 7*128 and d > 32767 then d = d - 65536 end -- BCMlits
110 return format("%s%3d\n", s, d)
111end
112
113-- Collect branch targets of a function.
114local function bctargets(func)
115 local target = {}
116 for pc=1,1000000000 do
117 local ins, m = funcbc(func, pc)
118 if not ins then break end
119 if band(m, 15*128) == 13*128 then target[pc+shr(ins, 16)-0x7fff] = true end
120 end
121 return target
122end
123
124-- Dump bytecode instructions of a function.
125local function bcdump(func, out)
126 if not out then out = stdout end
127 local fi = funcinfo(func)
128 out:write(format("-- BYTECODE -- %s-%d\n", fi.loc, fi.lastlinedefined))
129 local target = bctargets(func)
130 for pc=1,1000000000 do
131 local s = bcline(func, pc, target[pc] and "=>")
132 if not s then break end
133 out:write(s)
134 end
135 out:write("\n")
136 out:flush()
137end
138
139------------------------------------------------------------------------------
140
141-- Active flag and output file handle.
142local active, out
143
144-- List handler.
145local function h_list(func)
146 return bcdump(func, out)
147end
148
149-- Detach list handler.
150local function bclistoff()
151 if active then
152 active = false
153 jit.attach(h_list)
154 if out and out ~= stdout and out ~= stderr then out:close() end
155 out = nil
156 end
157end
158
159-- Open the output file and attach list handler.
160local function bcliston(outfile)
161 if active then bclistoff() end
162 if not outfile then outfile = os.getenv("LUAJIT_LISTFILE") end
163 if outfile then
164 out = outfile == "-" and stdout or assert(io.open(outfile, "w"))
165 else
166 out = stderr
167 end
168 jit.attach(h_list, "bc")
169 active = true
170end
171
172-- Public module functions.
173module(...)
174
175line = bcline
176dump = bcdump
177targets = bctargets
178
179on = bcliston
180off = bclistoff
181start = bcliston -- For -j command line option.
182
diff --git a/lib/dis_x64.lua b/lib/dis_x64.lua
new file mode 100644
index 00000000..da3d63f8
--- /dev/null
+++ b/lib/dis_x64.lua
@@ -0,0 +1,19 @@
1----------------------------------------------------------------------------
2-- LuaJIT x64 disassembler wrapper module.
3--
4-- Copyright (C) 2005-2009 Mike Pall. All rights reserved.
5-- Released under the MIT/X license. See Copyright Notice in luajit.h
6----------------------------------------------------------------------------
7-- This module just exports the 64 bit functions from the combined
8-- x86/x64 disassembler module. All the interesting stuff is there.
9------------------------------------------------------------------------------
10
11local require = require
12
13module(...)
14
15local dis_x86 = require(_PACKAGE.."dis_x86")
16
17create = dis_x86.create64
18disass = dis_x86.disass64
19
diff --git a/lib/dis_x86.lua b/lib/dis_x86.lua
new file mode 100644
index 00000000..8f127bee
--- /dev/null
+++ b/lib/dis_x86.lua
@@ -0,0 +1,824 @@
1----------------------------------------------------------------------------
2-- LuaJIT x86/x64 disassembler module.
3--
4-- Copyright (C) 2005-2009 Mike Pall. All rights reserved.
5-- Released under the MIT/X license. See Copyright Notice in luajit.h
6----------------------------------------------------------------------------
7-- This is a helper module used by the LuaJIT machine code dumper module.
8--
9-- Sending small code snippets to an external disassembler and mixing the
10-- output with our own stuff was too fragile. So I had to bite the bullet
11-- and write yet another x86 disassembler. Oh well ...
12--
13-- The output format is very similar to what ndisasm generates. But it has
14-- been developed independently by looking at the opcode tables from the
15-- Intel and AMD manuals. The supported instruction set is quite extensive
16-- and reflects what a current generation Intel or AMD CPU implements in
17-- 32 bit and 64 bit mode. Yes, this includes MMX, SSE, SSE2, SSE3, SSSE3,
18-- SSE4.1, SSE4.2, SSE4a and even privileged and hypervisor (VMX/SVM)
19-- instructions.
20--
21-- Notes:
22-- * The (useless) a16 prefix, 3DNow and pre-586 opcodes are unsupported.
23-- * No attempt at optimization has been made -- it's fast enough for my needs.
24-- * The public API may change when more architectures are added.
25------------------------------------------------------------------------------
26
27local type = type
28local sub, byte, format = string.sub, string.byte, string.format
29local match, gmatch, gsub = string.match, string.gmatch, string.gsub
30local lower, rep = string.lower, string.rep
31
32-- Map for 1st opcode byte in 32 bit mode. Ugly? Well ... read on.
33local map_opc1_32 = {
34--0x
35[0]="addBmr","addVmr","addBrm","addVrm","addBai","addVai","push es","pop es",
36"orBmr","orVmr","orBrm","orVrm","orBai","orVai","push cs","opc2*",
37--1x
38"adcBmr","adcVmr","adcBrm","adcVrm","adcBai","adcVai","push ss","pop ss",
39"sbbBmr","sbbVmr","sbbBrm","sbbVrm","sbbBai","sbbVai","push ds","pop ds",
40--2x
41"andBmr","andVmr","andBrm","andVrm","andBai","andVai","es:seg","daa",
42"subBmr","subVmr","subBrm","subVrm","subBai","subVai","cs:seg","das",
43--3x
44"xorBmr","xorVmr","xorBrm","xorVrm","xorBai","xorVai","ss:seg","aaa",
45"cmpBmr","cmpVmr","cmpBrm","cmpVrm","cmpBai","cmpVai","ds:seg","aas",
46--4x
47"incVR","incVR","incVR","incVR","incVR","incVR","incVR","incVR",
48"decVR","decVR","decVR","decVR","decVR","decVR","decVR","decVR",
49--5x
50"pushUR","pushUR","pushUR","pushUR","pushUR","pushUR","pushUR","pushUR",
51"popUR","popUR","popUR","popUR","popUR","popUR","popUR","popUR",
52--6x
53"sz*pushaw,pusha","sz*popaw,popa","boundVrm","arplWmr",
54"fs:seg","gs:seg","o16:","a16",
55"pushUi","imulVrmi","pushBs","imulVrms",
56"insb","insVS","outsb","outsVS",
57--7x
58"joBj","jnoBj","jbBj","jnbBj","jzBj","jnzBj","jbeBj","jaBj",
59"jsBj","jnsBj","jpeBj","jpoBj","jlBj","jgeBj","jleBj","jgBj",
60--8x
61"arith!Bmi","arith!Vmi","arith!Bmi","arith!Vms",
62"testBmr","testVmr","xchgBrm","xchgVrm",
63"movBmr","movVmr","movBrm","movVrm",
64"movVmg","leaVrm","movWgm","popUm",
65--9x
66"nop*xchgVaR|pause|xchgWaR|repne nop","xchgVaR","xchgVaR","xchgVaR",
67"xchgVaR","xchgVaR","xchgVaR","xchgVaR",
68"sz*cbw,cwde,cdqe","sz*cwd,cdq,cqo","call farViw","wait",
69"sz*pushfw,pushf","sz*popfw,popf","sahf","lahf",
70--Ax
71"movBao","movVao","movBoa","movVoa",
72"movsb","movsVS","cmpsb","cmpsVS",
73"testBai","testVai","stosb","stosVS",
74"lodsb","lodsVS","scasb","scasVS",
75--Bx
76"movBRi","movBRi","movBRi","movBRi","movBRi","movBRi","movBRi","movBRi",
77"movVRI","movVRI","movVRI","movVRI","movVRI","movVRI","movVRI","movVRI",
78--Cx
79"shift!Bmu","shift!Vmu","retBw","ret","$lesVrm","$ldsVrm","movBmi","movVmi",
80"enterBwu","leave","retfBw","retf","int3","intBu","into","iretVS",
81--Dx
82"shift!Bm1","shift!Vm1","shift!Bmc","shift!Vmc","aamBu","aadBu","salc","xlatb",
83"fp*0","fp*1","fp*2","fp*3","fp*4","fp*5","fp*6","fp*7",
84--Ex
85"loopneBj","loopeBj","loopBj","sz*jcxzBj,jecxzBj,jrcxzBj",
86"inBau","inVau","outBua","outVua",
87"callVj","jmpVj","jmp farViw","jmpBj","inBad","inVad","outBda","outVda",
88--Fx
89"lock:","int1","repne:rep","rep:","hlt","cmc","testb!Bm","testv!Vm",
90"clc","stc","cli","sti","cld","std","incb!Bm","incd!Vm",
91}
92assert(#map_opc1_32 == 255)
93
94-- Map for 1st opcode byte in 64 bit mode (overrides only).
95local map_opc1_64 = setmetatable({
96 [0x06]=false, [0x07]=false, [0x0e]=false,
97 [0x16]=false, [0x17]=false, [0x1e]=false, [0x1f]=false,
98 [0x27]=false, [0x2f]=false, [0x37]=false, [0x3f]=false,
99 [0x60]=false, [0x61]=false, [0x62]=false, [0x63]="movsxdVrDmt", [0x67]="a32:",
100 [0x40]="rex*", [0x41]="rex*b", [0x42]="rex*x", [0x43]="rex*xb",
101 [0x44]="rex*r", [0x45]="rex*rb", [0x46]="rex*rx", [0x47]="rex*rxb",
102 [0x48]="rex*w", [0x49]="rex*wb", [0x4a]="rex*wx", [0x4b]="rex*wxb",
103 [0x4c]="rex*wr", [0x4d]="rex*wrb", [0x4e]="rex*wrx", [0x4f]="rex*wrxb",
104 [0x82]=false, [0x9a]=false, [0xc4]=false, [0xc5]=false, [0xce]=false,
105 [0xd4]=false, [0xd5]=false, [0xd6]=false, [0xea]=false,
106}, { __index = map_opc1_32 })
107
108-- Map for 2nd opcode byte (0F xx). True CISC hell. Hey, I told you.
109-- Prefix dependent MMX/SSE opcodes: (none)|rep|o16|repne, -|F3|66|F2
110local map_opc2 = {
111--0x
112[0]="sldt!Dmp","sgdt!Ump","larVrm","lslVrm",nil,"syscall","clts","sysret",
113"invd","wbinvd",nil,"ud1",nil,"$prefetch!Bm","femms","3dnowMrmu",
114--1x
115"movupsXrm|movssXrm|movupdXrm|movsdXrm",
116"movupsXmr|movssXmr|movupdXmr|movsdXmr",
117"movhlpsXrm$movlpsXrm|movsldupXrm|movlpdXrm|movddupXrm",
118"movlpsXmr||movlpdXmr",
119"unpcklpsXrm||unpcklpdXrm",
120"unpckhpsXrm||unpckhpdXrm",
121"movlhpsXrm$movhpsXrm|movshdupXrm|movhpdXrm",
122"movhpsXmr||movhpdXmr",
123"$prefetcht!Bm","hintnopVm","hintnopVm","hintnopVm",
124"hintnopVm","hintnopVm","hintnopVm","hintnopVm",
125--2x
126"movUmx$","movUmy$","movUxm$","movUym$","movUmz$",nil,"movUzm$",nil,
127"movapsXrm||movapdXrm",
128"movapsXmr||movapdXmr",
129"cvtpi2psXrMm|cvtsi2ssXrVm|cvtpi2pdXrMm|cvtsi2sdXrVm",
130"movntpsXmr|movntssXmr|movntpdXmr|movntsdXmr",
131"cvttps2piMrXm|cvttss2siVrXm|cvttpd2piMrXm|cvttsd2siVrXm",
132"cvtps2piMrXm|cvtss2siVrXm|cvtpd2piMrXm|cvtsd2siVrXm",
133"ucomissXrm||ucomisdXrm",
134"comissXrm||comisdXrm",
135--3x
136"wrmsr","rdtsc","rdmsr","rdpmc","sysenter","sysexit",nil,"getsec",
137"opc3*38",nil,"opc3*3a",nil,nil,nil,nil,nil,
138--4x
139"cmovoVrm","cmovnoVrm","cmovbVrm","cmovnbVrm",
140"cmovzVrm","cmovnzVrm","cmovbeVrm","cmovaVrm",
141"cmovsVrm","cmovnsVrm","cmovpeVrm","cmovpoVrm",
142"cmovlVrm","cmovgeVrm","cmovleVrm","cmovgVrm",
143--5x
144"movmskpsVrXm$||movmskpdVrXm$","sqrtpsXrm|sqrtssXrm|sqrtpdXrm|sqrtsdXrm",
145"rsqrtpsXrm|rsqrtssXrm","rcppsXrm|rcpssXrm",
146"andpsXrm||andpdXrm","andnpsXrm||andnpdXrm",
147"orpsXrm||orpdXrm","xorpsXrm||xorpdXrm",
148"addpsXrm|addssXrm|addpdXrm|addsdXrm","mulpsXrm|mulssXrm|mulpdXrm|mulsdXrm",
149"cvtps2pdXrm|cvtss2sdXrm|cvtpd2psXrm|cvtsd2ssXrm",
150"cvtdq2psXrm|cvttps2dqXrm|cvtps2dqXrm",
151"subpsXrm|subssXrm|subpdXrm|subsdXrm","minpsXrm|minssXrm|minpdXrm|minsdXrm",
152"divpsXrm|divssXrm|divpdXrm|divsdXrm","maxpsXrm|maxssXrm|maxpdXrm|maxsdXrm",
153--6x
154"punpcklbwPrm","punpcklwdPrm","punpckldqPrm","packsswbPrm",
155"pcmpgtbPrm","pcmpgtwPrm","pcmpgtdPrm","packuswbPrm",
156"punpckhbwPrm","punpckhwdPrm","punpckhdqPrm","packssdwPrm",
157"||punpcklqdqXrm","||punpckhqdqXrm",
158"movPrVSm","movqMrm|movdquXrm|movdqaXrm",
159--7x
160"pshufwMrmu|pshufhwXrmu|pshufdXrmu|pshuflwXrmu","pshiftw!Pmu",
161"pshiftd!Pmu","pshiftq!Mmu||pshiftdq!Xmu",
162"pcmpeqbPrm","pcmpeqwPrm","pcmpeqdPrm","emms|",
163"vmreadUmr||extrqXmuu$|insertqXrmuu$","vmwriteUrm||extrqXrm$|insertqXrm$",
164nil,nil,
165"||haddpdXrm|haddpsXrm","||hsubpdXrm|hsubpsXrm",
166"movVSmMr|movqXrm|movVSmXr","movqMmr|movdquXmr|movdqaXmr",
167--8x
168"joVj","jnoVj","jbVj","jnbVj","jzVj","jnzVj","jbeVj","jaVj",
169"jsVj","jnsVj","jpeVj","jpoVj","jlVj","jgeVj","jleVj","jgVj",
170--9x
171"setoBm","setnoBm","setbBm","setnbBm","setzBm","setnzBm","setbeBm","setaBm",
172"setsBm","setnsBm","setpeBm","setpoBm","setlBm","setgeBm","setleBm","setgBm",
173--Ax
174"push fs","pop fs","cpuid","btVmr","shldVmru","shldVmrc",nil,nil,
175"push gs","pop gs","rsm","btsVmr","shrdVmru","shrdVmrc","fxsave!Dmp","imulVrm",
176--Bx
177"cmpxchgBmr","cmpxchgVmr","$lssVrm","btrVmr",
178"$lfsVrm","$lgsVrm","movzxVrBmt","movzxVrWmt",
179"|popcntVrm","ud2Dp","bt!Vmu","btcVmr",
180"bsfVrm","bsrVrm|lzcntVrm|bsrWrm","movsxVrBmt","movsxVrWmt",
181--Cx
182"xaddBmr","xaddVmr",
183"cmppsXrmu|cmpssXrmu|cmppdXrmu|cmpsdXrmu","$movntiVmr|",
184"pinsrwPrWmu","pextrwDrPmu",
185"shufpsXrmu||shufpdXrmu","$cmpxchg!Qmp",
186"bswapVR","bswapVR","bswapVR","bswapVR","bswapVR","bswapVR","bswapVR","bswapVR",
187--Dx
188"||addsubpdXrm|addsubpsXrm","psrlwPrm","psrldPrm","psrlqPrm",
189"paddqPrm","pmullwPrm",
190"|movq2dqXrMm|movqXmr|movdq2qMrXm$","pmovmskbVrMm||pmovmskbVrXm",
191"psubusbPrm","psubuswPrm","pminubPrm","pandPrm",
192"paddusbPrm","padduswPrm","pmaxubPrm","pandnPrm",
193--Ex
194"pavgbPrm","psrawPrm","psradPrm","pavgwPrm",
195"pmulhuwPrm","pmulhwPrm",
196"|cvtdq2pdXrm|cvttpd2dqXrm|cvtpd2dqXrm","$movntqMmr||$movntdqXmr",
197"psubsbPrm","psubswPrm","pminswPrm","porPrm",
198"paddsbPrm","paddswPrm","pmaxswPrm","pxorPrm",
199--Fx
200"|||lddquXrm","psllwPrm","pslldPrm","psllqPrm",
201"pmuludqPrm","pmaddwdPrm","psadbwPrm","maskmovqMrm||maskmovdquXrm$",
202"psubbPrm","psubwPrm","psubdPrm","psubqPrm",
203"paddbPrm","paddwPrm","padddPrm","ud",
204}
205assert(map_opc2[255] == "ud")
206
207-- Map for three-byte opcodes. Can't wait for their next invention.
208local map_opc3 = {
209["38"] = { -- [66] 0f 38 xx
210--0x
211[0]="pshufbPrm","phaddwPrm","phadddPrm","phaddswPrm",
212"pmaddubswPrm","phsubwPrm","phsubdPrm","phsubswPrm",
213"psignbPrm","psignwPrm","psigndPrm","pmulhrswPrm",
214nil,nil,nil,nil,
215--1x
216"||pblendvbXrma",nil,nil,nil,
217"||blendvpsXrma","||blendvpdXrma",nil,"||ptestXrm",
218nil,nil,nil,nil,
219"pabsbPrm","pabswPrm","pabsdPrm",nil,
220--2x
221"||pmovsxbwXrm","||pmovsxbdXrm","||pmovsxbqXrm","||pmovsxwdXrm",
222"||pmovsxwqXrm","||pmovsxdqXrm",nil,nil,
223"||pmuldqXrm","||pcmpeqqXrm","||$movntdqaXrm","||packusdwXrm",
224nil,nil,nil,nil,
225--3x
226"||pmovzxbwXrm","||pmovzxbdXrm","||pmovzxbqXrm","||pmovzxwdXrm",
227"||pmovzxwqXrm","||pmovzxdqXrm",nil,"||pcmpgtqXrm",
228"||pminsbXrm","||pminsdXrm","||pminuwXrm","||pminudXrm",
229"||pmaxsbXrm","||pmaxsdXrm","||pmaxuwXrm","||pmaxudXrm",
230--4x
231"||pmulddXrm","||phminposuwXrm",
232--Fx
233[0xf0] = "|||crc32TrBmt",[0xf1] = "|||crc32TrVmt",
234},
235
236["3a"] = { -- [66] 0f 3a xx
237--0x
238[0x00]=nil,nil,nil,nil,nil,nil,nil,nil,
239"||roundpsXrmu","||roundpdXrmu","||roundssXrmu","||roundsdXrmu",
240"||blendpsXrmu","||blendpdXrmu","||pblendwXrmu","palignrPrmu",
241--1x
242nil,nil,nil,nil,
243"||pextrbVmXru","||pextrwVmXru","||pextrVmSXru","||extractpsVmXru",
244nil,nil,nil,nil,nil,nil,nil,nil,
245--2x
246"||pinsrbXrVmu","||insertpsXrmu","||pinsrXrVmuS",nil,
247--4x
248[0x40] = "||dppsXrmu",
249[0x41] = "||dppdXrmu",
250[0x42] = "||mpsadbwXrmu",
251--6x
252[0x60] = "||pcmpestrmXrmu",[0x61] = "||pcmpestriXrmu",
253[0x62] = "||pcmpistrmXrmu",[0x63] = "||pcmpistriXrmu",
254},
255}
256
257-- Map for VMX/SVM opcodes 0F 01 C0-FF (sgdt group with register operands).
258local map_opcvm = {
259[0xc1]="vmcall",[0xc2]="vmlaunch",[0xc3]="vmresume",[0xc4]="vmxoff",
260[0xc8]="monitor",[0xc9]="mwait",
261[0xd8]="vmrun",[0xd9]="vmmcall",[0xda]="vmload",[0xdb]="vmsave",
262[0xdc]="stgi",[0xdd]="clgi",[0xde]="skinit",[0xdf]="invlpga",
263[0xf8]="swapgs",[0xf9]="rdtscp",
264}
265
266-- Map for FP opcodes. And you thought stack machines are simple?
267local map_opcfp = {
268-- D8-DF 00-BF: opcodes with a memory operand.
269-- D8
270[0]="faddFm","fmulFm","fcomFm","fcompFm","fsubFm","fsubrFm","fdivFm","fdivrFm",
271"fldFm",nil,"fstFm","fstpFm","fldenvVm","fldcwWm","fnstenvVm","fnstcwWm",
272-- DA
273"fiaddDm","fimulDm","ficomDm","ficompDm",
274"fisubDm","fisubrDm","fidivDm","fidivrDm",
275-- DB
276"fildDm","fisttpDm","fistDm","fistpDm",nil,"fld twordFmp",nil,"fstp twordFmp",
277-- DC
278"faddGm","fmulGm","fcomGm","fcompGm","fsubGm","fsubrGm","fdivGm","fdivrGm",
279-- DD
280"fldGm","fisttpQm","fstGm","fstpGm","frstorDmp",nil,"fnsaveDmp","fnstswWm",
281-- DE
282"fiaddWm","fimulWm","ficomWm","ficompWm",
283"fisubWm","fisubrWm","fidivWm","fidivrWm",
284-- DF
285"fildWm","fisttpWm","fistWm","fistpWm",
286"fbld twordFmp","fildQm","fbstp twordFmp","fistpQm",
287-- xx C0-FF: opcodes with a pseudo-register operand.
288-- D8
289"faddFf","fmulFf","fcomFf","fcompFf","fsubFf","fsubrFf","fdivFf","fdivrFf",
290-- D9
291"fldFf","fxchFf",{"fnop"},nil,
292{"fchs","fabs",nil,nil,"ftst","fxam"},
293{"fld1","fldl2t","fldl2e","fldpi","fldlg2","fldln2","fldz"},
294{"f2xm1","fyl2x","fptan","fpatan","fxtract","fprem1","fdecstp","fincstp"},
295{"fprem","fyl2xp1","fsqrt","fsincos","frndint","fscale","fsin","fcos"},
296-- DA
297"fcmovbFf","fcmoveFf","fcmovbeFf","fcmovuFf",nil,{nil,"fucompp"},nil,nil,
298-- DB
299"fcmovnbFf","fcmovneFf","fcmovnbeFf","fcmovnuFf",
300{nil,nil,"fnclex","fninit"},"fucomiFf","fcomiFf",nil,
301-- DC
302"fadd toFf","fmul toFf",nil,nil,
303"fsub toFf","fsubr toFf","fdivr toFf","fdiv toFf",
304-- DD
305"ffreeFf",nil,"fstFf","fstpFf","fucomFf","fucompFf",nil,nil,
306-- DE
307"faddpFf","fmulpFf",nil,{nil,"fcompp"},
308"fsubrpFf","fsubpFf","fdivrpFf","fdivpFf",
309-- DF
310nil,nil,nil,nil,{"fnstsw ax"},"fucomipFf","fcomipFf",nil,
311}
312assert(map_opcfp[126] == "fcomipFf")
313
314-- Map for opcode groups. The subkey is sp from the ModRM byte.
315local map_opcgroup = {
316 arith = { "add", "or", "adc", "sbb", "and", "sub", "xor", "cmp" },
317 shift = { "rol", "ror", "rcl", "rcr", "shl", "shr", "sal", "sar" },
318 testb = { "testBmi", "testBmi", "not", "neg", "mul", "imul", "div", "idiv" },
319 testv = { "testVmi", "testVmi", "not", "neg", "mul", "imul", "div", "idiv" },
320 incb = { "inc", "dec" },
321 incd = { "inc", "dec", "callDmp", "$call farDmp",
322 "jmpDmp", "$jmp farDmp", "pushUm" },
323 sldt = { "sldt", "str", "lldt", "ltr", "verr", "verw" },
324 sgdt = { "vm*$sgdt", "vm*$sidt", "$lgdt", "vm*$lidt",
325 "smsw", nil, "lmsw", "vm*$invlpg" },
326 bt = { nil, nil, nil, nil, "bt", "bts", "btr", "btc" },
327 cmpxchg = { nil, "sz*,cmpxchg8bQmp,cmpxchg16bXmp", nil, nil,
328 nil, nil, "vmptrld|vmxon|vmclear", "vmptrst" },
329 pshiftw = { nil, nil, "psrlw", nil, "psraw", nil, "psllw" },
330 pshiftd = { nil, nil, "psrld", nil, "psrad", nil, "pslld" },
331 pshiftq = { nil, nil, "psrlq", nil, nil, nil, "psllq" },
332 pshiftdq = { nil, nil, "psrlq", "psrldq", nil, nil, "psllq", "pslldq" },
333 fxsave = { "$fxsave", "$fxrstor", "$ldmxcsr", "$stmxcsr",
334 nil, "lfenceDp$", "mfenceDp$", "sfenceDp$clflush" },
335 prefetch = { "prefetch", "prefetchw" },
336 prefetcht = { "prefetchnta", "prefetcht0", "prefetcht1", "prefetcht2" },
337}
338
339------------------------------------------------------------------------------
340
341-- Maps for register names.
342local map_regs = {
343 B = { "al", "cl", "dl", "bl", "ah", "ch", "dh", "bh",
344 "r8b", "r9b", "r10b", "r11b", "r12b", "r13b", "r14b", "r15b" },
345 B64 = { "al", "cl", "dl", "bl", "spl", "bpl", "sil", "dil",
346 "r8b", "r9b", "r10b", "r11b", "r12b", "r13b", "r14b", "r15b" },
347 W = { "ax", "cx", "dx", "bx", "sp", "bp", "si", "di",
348 "r8w", "r9w", "r10w", "r11w", "r12w", "r13w", "r14w", "r15w" },
349 D = { "eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi",
350 "r8d", "r9d", "r10d", "r11d", "r12d", "r13d", "r14d", "r15d" },
351 Q = { "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi",
352 "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15" },
353 M = { "mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7",
354 "mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7" }, -- No x64 ext!
355 X = { "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7",
356 "xmm8", "xmm9", "xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15" },
357}
358local map_segregs = { "es", "cs", "ss", "ds", "fs", "gs", "segr6", "segr7" }
359
360-- Maps for size names.
361local map_sz2n = {
362 B = 1, W = 2, D = 4, Q = 8, M = 8, X = 16,
363}
364local map_sz2prefix = {
365 B = "byte", W = "word", D = "dword",
366 Q = "qword",
367 M = "qword", X = "xword",
368 F = "dword", G = "qword", -- No need for sizes/register names for these two.
369}
370
371------------------------------------------------------------------------------
372
373-- Output a nicely formatted line with an opcode and operands.
374local function putop(ctx, text, operands)
375 local code, pos, hex = ctx.code, ctx.pos, ""
376 local hmax = ctx.hexdump
377 if hmax > 0 then
378 for i=ctx.start,pos-1 do
379 hex = hex..format("%02X", byte(code, i, i))
380 end
381 if #hex > hmax then hex = sub(hex, 1, hmax)..". "
382 else hex = hex..rep(" ", hmax-#hex+2) end
383 end
384 if operands then text = text.." "..operands end
385 if ctx.o16 then text = "o16 "..text; ctx.o16 = false end
386 if ctx.a32 then text = "a32 "..text; ctx.a32 = false end
387 if ctx.rep then text = ctx.rep.." "..text; ctx.rep = false end
388 if ctx.rex then
389 local t = (ctx.rexw and "w" or "")..(ctx.rexr and "r" or "")..
390 (ctx.rexx and "x" or "")..(ctx.rexb and "b" or "")
391 if t ~= "" then text = "rex."..t.." "..text end
392 ctx.rexw = false; ctx.rexr = false; ctx.rexx = false; ctx.rexb = false
393 ctx.rex = false
394 end
395 if ctx.seg then
396 local text2, n = gsub(text, "%[", "["..ctx.seg..":")
397 if n == 0 then text = ctx.seg.." "..text else text = text2 end
398 ctx.seg = false
399 end
400 if ctx.lock then text = "lock "..text; ctx.lock = false end
401 local imm = ctx.imm
402 if imm then
403 local sym = ctx.symtab[imm]
404 if sym then text = text.."\t->"..sym end
405 end
406 ctx.out(format("%08x %s%s\n", ctx.addr+ctx.start, hex, text))
407 ctx.mrm = false
408 ctx.start = pos
409 ctx.imm = nil
410end
411
412-- Clear all prefix flags.
413local function clearprefixes(ctx)
414 ctx.o16 = false; ctx.seg = false; ctx.lock = false; ctx.rep = false
415 ctx.rexw = false; ctx.rexr = false; ctx.rexx = false; ctx.rexb = false
416 ctx.rex = false; ctx.a32 = false
417end
418
419-- Fallback for incomplete opcodes at the end.
420local function incomplete(ctx)
421 ctx.pos = ctx.stop+1
422 clearprefixes(ctx)
423 return putop(ctx, "(incomplete)")
424end
425
426-- Fallback for unknown opcodes.
427local function unknown(ctx)
428 clearprefixes(ctx)
429 return putop(ctx, "(unknown)")
430end
431
432-- Return an immediate of the specified size.
433local function getimm(ctx, pos, n)
434 if pos+n-1 > ctx.stop then return incomplete(ctx) end
435 local code = ctx.code
436 if n == 1 then
437 local b1 = byte(code, pos, pos)
438 return b1
439 elseif n == 2 then
440 local b1, b2 = byte(code, pos, pos+1)
441 return b1+b2*256
442 else
443 local b1, b2, b3, b4 = byte(code, pos, pos+3)
444 local imm = b1+b2*256+b3*65536+b4*16777216
445 ctx.imm = imm
446 return imm
447 end
448end
449
450-- Process pattern string and generate the operands.
451local function putpat(ctx, name, pat)
452 local operands, regs, sz, mode, sp, rm, sc, rx, sdisp
453 local code, pos, stop = ctx.code, ctx.pos, ctx.stop
454
455 -- Chars used: 1DFGIMPQRSTUVWXacdfgijmoprstuwxyz
456 for p in gmatch(pat, ".") do
457 local x = nil
458 if p == "V" or p == "U" then
459 if ctx.rexw then sz = "Q"; ctx.rexw = false
460 elseif ctx.o16 then sz = "W"; ctx.o16 = false
461 elseif p == "U" and ctx.x64 then sz = "Q"
462 else sz = "D" end
463 regs = map_regs[sz]
464 elseif p == "T" then
465 if ctx.rexw then sz = "Q"; ctx.rexw = false else sz = "D" end
466 regs = map_regs[sz]
467 elseif p == "B" then
468 sz = "B"
469 regs = ctx.rex and map_regs.B64 or map_regs.B
470 elseif match(p, "[WDQMXFG]") then
471 sz = p
472 regs = map_regs[sz]
473 elseif p == "P" then
474 sz = ctx.o16 and "X" or "M"; ctx.o16 = false
475 regs = map_regs[sz]
476 elseif p == "S" then
477 name = name..lower(sz)
478 elseif p == "s" then
479 local imm = getimm(ctx, pos, 1); if not imm then return end
480 x = imm <= 127 and format("+0x%02x", imm)
481 or format("-0x%02x", 256-imm)
482 pos = pos+1
483 elseif p == "u" then
484 local imm = getimm(ctx, pos, 1); if not imm then return end
485 x = format("0x%02x", imm)
486 pos = pos+1
487 elseif p == "w" then
488 local imm = getimm(ctx, pos, 2); if not imm then return end
489 x = format("0x%x", imm)
490 pos = pos+2
491 elseif p == "o" then -- [offset]
492 if ctx.x64 then
493 local imm1 = getimm(ctx, pos, 4); if not imm1 then return end
494 local imm2 = getimm(ctx, pos+4, 4); if not imm2 then return end
495 x = format("[0x%08x%08x]", imm2, imm1)
496 pos = pos+8
497 else
498 local imm = getimm(ctx, pos, 4); if not imm then return end
499 x = format("[0x%08x]", imm)
500 pos = pos+4
501 end
502 elseif p == "i" or p == "I" then
503 local n = map_sz2n[sz]
504 if n == 8 and ctx.x64 and p == "I" then
505 local imm1 = getimm(ctx, pos, 4); if not imm1 then return end
506 local imm2 = getimm(ctx, pos+4, 4); if not imm2 then return end
507 x = format("0x%08x%08x", imm2, imm1)
508 else
509 if n == 8 then n = 4 end
510 local imm = getimm(ctx, pos, n); if not imm then return end
511 if sz == "Q" and (imm < 0 or imm > 0x7fffffff) then
512 imm = (0xffffffff+1)-imm
513 x = format(imm > 65535 and "-0x%08x" or "-0x%x", imm)
514 else
515 x = format(imm > 65535 and "0x%08x" or "0x%x", imm)
516 end
517 end
518 pos = pos+n
519 elseif p == "j" then
520 local n = map_sz2n[sz]
521 if n == 8 then n = 4 end
522 local imm = getimm(ctx, pos, n); if not imm then return end
523 if sz == "B" and imm > 127 then imm = imm-256
524 elseif imm > 2147483647 then imm = imm-4294967296 end
525 pos = pos+n
526 imm = imm + pos + ctx.addr
527 if imm > 4294967295 and not ctx.x64 then imm = imm-4294967296 end
528 ctx.imm = imm
529 if sz == "W" then
530 x = format("word 0x%04x", imm%65536)
531 elseif ctx.x64 then
532 local lo = imm % 0x1000000
533 x = format("0x%02x%06x", (imm-lo) / 0x1000000, lo)
534 else
535 x = format("0x%08x", imm)
536 end
537 elseif p == "R" then
538 local r = byte(code, pos-1, pos-1)%8
539 if ctx.rexb then r = r + 8; ctx.rexb = false end
540 x = regs[r+1]
541 elseif p == "a" then x = regs[1]
542 elseif p == "c" then x = "cl"
543 elseif p == "d" then x = "dx"
544 elseif p == "1" then x = "1"
545 else
546 if not mode then
547 mode = ctx.mrm
548 if not mode then
549 if pos > stop then return incomplete(ctx) end
550 mode = byte(code, pos, pos)
551 pos = pos+1
552 end
553 rm = mode%8; mode = (mode-rm)/8
554 sp = mode%8; mode = (mode-sp)/8
555 sdisp = ""
556 if mode < 3 then
557 if rm == 4 then
558 if pos > stop then return incomplete(ctx) end
559 sc = byte(code, pos, pos)
560 pos = pos+1
561 rm = sc%8; sc = (sc-rm)/8
562 rx = sc%8; sc = (sc-rx)/8
563 if ctx.rexx then rx = rx + 8; ctx.rexx = false end
564 if rx == 4 then rx = nil end
565 end
566 if mode > 0 or rm == 5 then
567 local dsz = mode
568 if dsz ~= 1 then dsz = 4 end
569 local disp = getimm(ctx, pos, dsz); if not disp then return end
570 if mode == 0 then rm = nil end
571 if rm or rx or (not sc and ctx.x64 and not ctx.a32) then
572 if dsz == 1 and disp > 127 then
573 sdisp = format("-0x%x", 256-disp)
574 elseif disp >= 0 and disp <= 0x7fffffff then
575 sdisp = format("+0x%x", disp)
576 else
577 sdisp = format("-0x%x", (0xffffffff+1)-disp)
578 end
579 else
580 sdisp = format(ctx.x64 and not ctx.a32 and
581 not (disp >= 0 and disp <= 0x7fffffff)
582 and "0xffffffff%08x" or "0x%08x", disp)
583 end
584 pos = pos+dsz
585 end
586 end
587 if rm and ctx.rexb then rm = rm + 8; ctx.rexb = false end
588 if ctx.rexr then sp = sp + 8; ctx.rexr = false end
589 end
590 if p == "m" then
591 if mode == 3 then x = regs[rm+1]
592 else
593 local aregs = ctx.a32 and map_regs.D or ctx.aregs
594 local srm, srx = "", ""
595 if rm then srm = aregs[rm+1]
596 elseif not sc and ctx.x64 and not ctx.a32 then srm = "rip" end
597 ctx.a32 = false
598 if rx then
599 if rm then srm = srm.."+" end
600 srx = aregs[rx+1]
601 if sc > 0 then srx = srx.."*"..(2^sc) end
602 end
603 x = format("[%s%s%s]", srm, srx, sdisp)
604 end
605 if mode < 3 and
606 (not match(pat, "[aRrgp]") or match(pat, "t")) then -- Yuck.
607 x = map_sz2prefix[sz].." "..x
608 end
609 elseif p == "r" then x = regs[sp+1]
610 elseif p == "g" then x = map_segregs[sp+1]
611 elseif p == "p" then -- Suppress prefix.
612 elseif p == "f" then x = "st"..rm
613 elseif p == "x" then
614 if sp == 0 and ctx.lock and not ctx.x64 then
615 x = "CR8"; ctx.lock = false
616 else
617 x = "CR"..sp
618 end
619 elseif p == "y" then x = "DR"..sp
620 elseif p == "z" then x = "TR"..sp
621 elseif p == "t" then
622 else
623 error("bad pattern `"..pat.."'")
624 end
625 end
626 if x then operands = operands and operands..", "..x or x end
627 end
628 ctx.pos = pos
629 return putop(ctx, name, operands)
630end
631
632-- Forward declaration.
633local map_act
634
635-- Fetch and cache MRM byte.
636local function getmrm(ctx)
637 local mrm = ctx.mrm
638 if not mrm then
639 local pos = ctx.pos
640 if pos > ctx.stop then return nil end
641 mrm = byte(ctx.code, pos, pos)
642 ctx.pos = pos+1
643 ctx.mrm = mrm
644 end
645 return mrm
646end
647
648-- Dispatch to handler depending on pattern.
649local function dispatch(ctx, opat, patgrp)
650 if not opat then return unknown(ctx) end
651 if match(opat, "%|") then -- MMX/SSE variants depending on prefix.
652 local p
653 if ctx.rep then
654 p = ctx.rep=="rep" and "%|([^%|]*)" or "%|[^%|]*%|[^%|]*%|([^%|]*)"
655 ctx.rep = false
656 elseif ctx.o16 then p = "%|[^%|]*%|([^%|]*)"; ctx.o16 = false
657 else p = "^[^%|]*" end
658 opat = match(opat, p)
659 if not opat then return unknown(ctx) end
660-- ctx.rep = false; ctx.o16 = false
661 --XXX fails for 66 f2 0f 38 f1 06 crc32 eax,WORD PTR [esi]
662 --XXX remove in branches?
663 end
664 if match(opat, "%$") then -- reg$mem variants.
665 local mrm = getmrm(ctx); if not mrm then return incomplete(ctx) end
666 opat = match(opat, mrm >= 192 and "^[^%$]*" or "%$(.*)")
667 if opat == "" then return unknown(ctx) end
668 end
669 if opat == "" then return unknown(ctx) end
670 local name, pat = match(opat, "^([a-z0-9 ]*)(.*)")
671 if pat == "" and patgrp then pat = patgrp end
672 return map_act[sub(pat, 1, 1)](ctx, name, pat)
673end
674
675-- Get a pattern from an opcode map and dispatch to handler.
676local function dispatchmap(ctx, opcmap)
677 local pos = ctx.pos
678 local opat = opcmap[byte(ctx.code, pos, pos)]
679 pos = pos + 1
680 ctx.pos = pos
681 return dispatch(ctx, opat)
682end
683
684-- Map for action codes. The key is the first char after the name.
685map_act = {
686 -- Simple opcodes without operands.
687 [""] = function(ctx, name, pat)
688 return putop(ctx, name)
689 end,
690
691 -- Operand size chars fall right through.
692 B = putpat, W = putpat, D = putpat, Q = putpat,
693 V = putpat, U = putpat, T = putpat,
694 M = putpat, X = putpat, P = putpat,
695 F = putpat, G = putpat,
696
697 -- Collect prefixes.
698 [":"] = function(ctx, name, pat)
699 ctx[pat == ":" and name or sub(pat, 2)] = name
700 if ctx.pos - ctx.start > 5 then return unknown(ctx) end -- Limit #prefixes.
701 end,
702
703 -- Chain to special handler specified by name.
704 ["*"] = function(ctx, name, pat)
705 return map_act[name](ctx, name, sub(pat, 2))
706 end,
707
708 -- Use named subtable for opcode group.
709 ["!"] = function(ctx, name, pat)
710 local mrm = getmrm(ctx); if not mrm then return incomplete(ctx) end
711 return dispatch(ctx, map_opcgroup[name][((mrm-(mrm%8))/8)%8+1], sub(pat, 2))
712 end,
713
714 -- o16,o32[,o64] variants.
715 sz = function(ctx, name, pat)
716 if ctx.o16 then ctx.o16 = false
717 else
718 pat = match(pat, ",(.*)")
719 if ctx.rexw then
720 local p = match(pat, ",(.*)")
721 if p then pat = p; ctx.rexw = false end
722 end
723 end
724 pat = match(pat, "^[^,]*")
725 return dispatch(ctx, pat)
726 end,
727
728 -- Two-byte opcode dispatch.
729 opc2 = function(ctx, name, pat)
730 return dispatchmap(ctx, map_opc2)
731 end,
732
733 -- Three-byte opcode dispatch.
734 opc3 = function(ctx, name, pat)
735 return dispatchmap(ctx, map_opc3[pat])
736 end,
737
738 -- VMX/SVM dispatch.
739 vm = function(ctx, name, pat)
740 return dispatch(ctx, map_opcvm[ctx.mrm])
741 end,
742
743 -- Floating point opcode dispatch.
744 fp = function(ctx, name, pat)
745 local mrm = getmrm(ctx); if not mrm then return incomplete(ctx) end
746 local rm = mrm%8
747 local idx = pat*8 + ((mrm-rm)/8)%8
748 if mrm >= 192 then idx = idx + 64 end
749 local opat = map_opcfp[idx]
750 if type(opat) == "table" then opat = opat[rm+1] end
751 return dispatch(ctx, opat)
752 end,
753
754 -- REX prefix.
755 rex = function(ctx, name, pat)
756 if ctx.rex then return unknown(ctx) end -- Only 1 REX prefix allowed.
757 for p in gmatch(pat, ".") do ctx["rex"..p] = true end
758 ctx.rex = true
759 end,
760
761 -- Special case for nop with REX prefix.
762 nop = function(ctx, name, pat)
763 return dispatch(ctx, ctx.rex and pat or "nop")
764 end,
765}
766
767------------------------------------------------------------------------------
768
769-- Disassemble a block of code.
770local function disass_block(ctx, ofs, len)
771 if not ofs then ofs = 0 end
772 local stop = len and ofs+len or #ctx.code
773 ofs = ofs + 1
774 ctx.start = ofs
775 ctx.pos = ofs
776 ctx.stop = stop
777 ctx.imm = nil
778 ctx.mrm = false
779 clearprefixes(ctx)
780 while ctx.pos <= stop do dispatchmap(ctx, ctx.map1) end
781 if ctx.pos ~= ctx.start then incomplete(ctx) end
782end
783
784-- Extended API: create a disassembler context. Then call ctx:disass(ofs, len).
785local function create_(code, addr, out)
786 local ctx = {}
787 ctx.code = code
788 ctx.addr = (addr or 0) - 1
789 ctx.out = out or io.write
790 ctx.symtab = {}
791 ctx.disass = disass_block
792 ctx.hexdump = 16
793 ctx.x64 = false
794 ctx.map1 = map_opc1_32
795 ctx.aregs = map_regs.D
796 return ctx
797end
798
799local function create64_(code, addr, out)
800 local ctx = create_(code, addr, out)
801 ctx.x64 = true
802 ctx.map1 = map_opc1_64
803 ctx.aregs = map_regs.Q
804 return ctx
805end
806
807-- Simple API: disassemble code (a string) at address and output via out.
808local function disass_(code, addr, out)
809 create_(code, addr, out):disass()
810end
811
812local function disass64_(code, addr, out)
813 create64_(code, addr, out):disass()
814end
815
816
817-- Public module functions.
818module(...)
819
820create = create_
821create64 = create64_
822disass = disass_
823disass64 = disass64_
824
diff --git a/lib/dump.lua b/lib/dump.lua
new file mode 100644
index 00000000..9fde87c1
--- /dev/null
+++ b/lib/dump.lua
@@ -0,0 +1,567 @@
1----------------------------------------------------------------------------
2-- LuaJIT compiler dump module.
3--
4-- Copyright (C) 2005-2009 Mike Pall. All rights reserved.
5-- Released under the MIT/X license. See Copyright Notice in luajit.h
6----------------------------------------------------------------------------
7--
8-- This module can be used to debug the JIT compiler itself. It dumps the
9-- code representations and structures used in various compiler stages.
10--
11-- Example usage:
12--
13-- luajit -jdump -e "local x=0; for i=1,1e6 do x=x+i end; print(x)"
14-- luajit -jdump=im -e "for i=1,1000 do for j=1,1000 do end end" | less -R
15-- luajit -jdump=is myapp.lua | less -R
16-- luajit -jdump=-b myapp.lua
17-- luajit -jdump=+aH,myapp.html myapp.lua
18-- luajit -jdump=ixT,myapp.dump myapp.lua
19--
20-- The first argument specifies the dump mode. The second argument gives
21-- the output file name. Default output is to stdout, unless the environment
22-- variable LUAJIT_DUMPFILE is set. The file is overwritten every time the
23-- module is started.
24--
25-- Different features can be turned on or off with the dump mode. If the
26-- mode starts with a '+', the following features are added to the default
27-- set of features; a '-' removes them. Otherwise the features are replaced.
28--
29-- The following dump features are available (* marks the default):
30--
31-- * t Print a line for each started, ended or aborted trace (see also -jv).
32-- * b Dump the traced bytecode.
33-- * i Dump the IR (intermediate representation).
34-- r Augment the IR with register/stack slots.
35-- s Dump the snapshot map.
36-- * m Dump the generated machine code.
37-- x Print each taken trace exit.
38-- X Print each taken trace exit and the contents of all registers.
39--
40-- The output format can be set with the following characters:
41--
42-- T Plain text output.
43-- A ANSI-colored text output
44-- H Colorized HTML + CSS output.
45--
46-- The default output format is plain text. It's set to ANSI-colored text
47-- if the COLORTERM variable is set. Note: this is independent of any output
48-- redirection, which is actually considered a feature.
49--
50-- You probably want to use less -R to enjoy viewing ANSI-colored text from
51-- a pipe or a file. Add this to your ~/.bashrc: export LESS="-R"
52--
53------------------------------------------------------------------------------
54
55-- Cache some library functions and objects.
56local jit = require("jit")
57assert(jit.version_num == 20000, "LuaJIT core/library version mismatch")
58local jutil = require("jit.util")
59local vmdef = require("jit.vmdef")
60local funcinfo, funcbc = jutil.funcinfo, jutil.funcbc
61local traceinfo, traceir, tracek = jutil.traceinfo, jutil.traceir, jutil.tracek
62local tracemc, traceexitstub = jutil.tracemc, jutil.traceexitstub
63local tracesnap = jutil.tracesnap
64local bit = require("bit")
65local band, shl, shr = bit.band, bit.lshift, bit.rshift
66local sub, gsub, format = string.sub, string.gsub, string.format
67local byte, char, rep = string.byte, string.char, string.rep
68local type, tostring = type, tostring
69local stdout, stderr = io.stdout, io.stderr
70
71-- Load other modules on-demand.
72local bcline, discreate
73
74-- Active flag, output file handle and dump mode.
75local active, out, dumpmode
76
77------------------------------------------------------------------------------
78
79local symtab = {}
80local nexitsym = 0
81
82-- Fill symbol table with trace exit addresses.
83local function fillsymtab(nexit)
84 local t = symtab
85 if nexit > nexitsym then
86 for i=nexitsym,nexit-1 do t[traceexitstub(i)] = tostring(i) end
87 nexitsym = nexit
88 end
89 return t
90end
91
92local function dumpwrite(s)
93 out:write(s)
94end
95
96-- Disassemble machine code.
97local function dump_mcode(tr)
98 local info = traceinfo(tr)
99 if not info then return end
100 local mcode, addr, loop = tracemc(tr)
101 if not mcode then return end
102 if not discreate then
103 discreate = require("jit.dis_"..jit.arch).create
104 end
105 out:write("---- TRACE ", tr, " mcode ", #mcode, "\n")
106 local ctx = discreate(mcode, addr, dumpwrite)
107 ctx.hexdump = 0
108 ctx.symtab = fillsymtab(info.nexit)
109 if loop ~= 0 then
110 symtab[addr+loop] = "LOOP"
111 ctx:disass(0, loop)
112 out:write("->LOOP:\n")
113 ctx:disass(loop, #mcode-loop)
114 symtab[addr+loop] = nil
115 else
116 ctx:disass(0, #mcode)
117 end
118end
119
120------------------------------------------------------------------------------
121
122local irtype_text = {
123 [0] = "nil",
124 "fal",
125 "tru",
126 "lud",
127 "str",
128 "ptr",
129 "thr",
130 "pro",
131 "fun",
132 "t09",
133 "tab",
134 "udt",
135 "num",
136 "int",
137 "i8 ",
138 "u8 ",
139 "i16",
140 "u16",
141}
142
143local colortype_ansi = {
144 [0] = "%s",
145 "%s",
146 "%s",
147 "%s",
148 "\027[32m%s\027[m",
149 "%s",
150 "\027[1m%s\027[m",
151 "%s",
152 "\027[1m%s\027[m",
153 "%s",
154 "\027[31m%s\027[m",
155 "\027[36m%s\027[m",
156 "\027[34m%s\027[m",
157 "\027[35m%s\027[m",
158 "\027[35m%s\027[m",
159 "\027[35m%s\027[m",
160 "\027[35m%s\027[m",
161 "\027[35m%s\027[m",
162}
163
164local function colorize_text(s, t)
165 return s
166end
167
168local function colorize_ansi(s, t)
169 return format(colortype_ansi[t], s)
170end
171
172local irtype_ansi = setmetatable({},
173 { __index = function(tab, t)
174 local s = colorize_ansi(irtype_text[t], t); tab[t] = s; return s; end })
175
176local html_escape = { ["<"] = "&lt;", [">"] = "&gt;", ["&"] = "&amp;", }
177
178local function colorize_html(s, t)
179 s = gsub(s, "[<>&]", html_escape)
180 return format('<span class="irt_%s">%s</span>', irtype_text[t], s)
181end
182
183local irtype_html = setmetatable({},
184 { __index = function(tab, t)
185 local s = colorize_html(irtype_text[t], t); tab[t] = s; return s; end })
186
187local header_html = [[
188<style type="text/css">
189background { background: #ffffff; color: #000000; }
190pre.ljdump {
191font-size: 10pt;
192background: #f0f4ff;
193color: #000000;
194border: 1px solid #bfcfff;
195padding: 0.5em;
196margin-left: 2em;
197margin-right: 2em;
198}
199span.irt_str { color: #00a000; }
200span.irt_thr, span.irt_fun { color: #404040; font-weight: bold; }
201span.irt_tab { color: #c00000; }
202span.irt_udt { color: #00c0c0; }
203span.irt_num { color: #0000c0; }
204span.irt_int { color: #c000c0; }
205</style>
206]]
207
208local colorize, irtype
209
210-- Lookup table to convert some literals into names.
211local litname = {
212 ["SLOAD "] = { [0] = "", "I", "R", "RI", "P", "PI", "PR", "PRI", },
213 ["XLOAD "] = { [0] = "", "unaligned", },
214 ["TOINT "] = { [0] = "check", "index", "", },
215 ["FLOAD "] = vmdef.irfield,
216 ["FREF "] = vmdef.irfield,
217 ["FPMATH"] = vmdef.irfpm,
218}
219
220local function ctlsub(c)
221 if c == "\n" then return "\\n"
222 elseif c == "\r" then return "\\r"
223 elseif c == "\t" then return "\\t"
224 elseif c == "\r" then return "\\r"
225 else return format("\\%03d", byte(c))
226 end
227end
228
229local function formatk(tr, idx)
230 local k, t, slot = tracek(tr, idx)
231 local tn = type(k)
232 local s
233 if tn == "number" then
234 if k == 2^52+2^51 then
235 s = "bias"
236 else
237 s = format("%+.14g", k)
238 end
239 elseif tn == "string" then
240 s = format(#k > 20 and '"%.20s"~' or '"%s"', gsub(k, "%c", ctlsub))
241 elseif tn == "function" then
242 local fi = funcinfo(k)
243 if fi.ffid then
244 s = vmdef.ffnames[fi.ffid]
245 else
246 s = fi.loc
247 end
248 elseif tn == "table" then
249 s = format("{%p}", k)
250 elseif tn == "userdata" then
251 if t == 11 then
252 s = format("userdata:%p", k)
253 else
254 s = format("[%p]", k)
255 if s == "[0x00000000]" then s = "NULL" end
256 end
257 else
258 s = tostring(k) -- For primitives.
259 end
260 s = colorize(format("%-4s", s), t)
261 if slot then
262 s = format("%s @%d", s, slot)
263 end
264 return s
265end
266
267local function printsnap(tr, snap)
268 for i=1,#snap do
269 local ref = snap[i]
270 if not ref then
271 out:write("---- ")
272 elseif ref < 0 then
273 out:write(formatk(tr, ref), " ")
274 else
275 local m, ot, op1, op2 = traceir(tr, ref)
276 local t = band(ot, 15)
277 local sep = " "
278 if t == 8 then
279 local oidx = 6*shr(ot, 8)
280 local op = sub(vmdef.irnames, oidx+1, oidx+6)
281 if op == "FRAME " then
282 sep = "|"
283 end
284 end
285 out:write(colorize(format("%04d", ref), t), sep)
286 end
287 end
288 out:write("]\n")
289end
290
291-- Dump snapshots (not interleaved with IR).
292local function dump_snap(tr)
293 out:write("---- TRACE ", tr, " snapshots\n")
294 for i=0,1000000000 do
295 local snap = tracesnap(tr, i)
296 if not snap then break end
297 out:write(format("#%-3d %04d [ ", i, snap[0]))
298 printsnap(tr, snap)
299 end
300end
301
302-- NYI: should really get the register map from the disassembler.
303local reg_map = {
304 [0] = "eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi",
305 "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7",
306}
307
308-- Return a register name or stack slot for a rid/sp location.
309local function ridsp_name(ridsp)
310 local rid = band(ridsp, 0xff)
311 if ridsp > 255 then return format("[%x]", shr(ridsp, 8)*4) end
312 if rid < 128 then return reg_map[rid] end
313 return ""
314end
315
316-- Dump IR and interleaved snapshots.
317local function dump_ir(tr, dumpsnap, dumpreg)
318 local info = traceinfo(tr)
319 if not info then return end
320 local nins = info.nins
321 out:write("---- TRACE ", tr, " IR\n")
322 local irnames = vmdef.irnames
323 local snapref = 65536
324 local snap, snapno
325 if dumpsnap then
326 snap = tracesnap(tr, 0)
327 snapref = snap[0]
328 snapno = 0
329 end
330 for ins=1,nins do
331 if ins >= snapref then
332 if dumpreg then
333 out:write(format(".... SNAP #%-3d [ ", snapno))
334 else
335 out:write(format(".... SNAP #%-3d [ ", snapno))
336 end
337 printsnap(tr, snap)
338 snapno = snapno + 1
339 snap = tracesnap(tr, snapno)
340 snapref = snap and snap[0] or 65536
341 end
342 local m, ot, op1, op2, ridsp = traceir(tr, ins)
343 local oidx, t = 6*shr(ot, 8), band(ot, 31)
344 local op = sub(irnames, oidx+1, oidx+6)
345 if op == "LOOP " then
346 if dumpreg then
347 out:write(format("%04d ------------ LOOP ------------\n", ins))
348 else
349 out:write(format("%04d ------ LOOP ------------\n", ins))
350 end
351 elseif op ~= "NOP " and (dumpreg or op ~= "RENAME") then
352 if dumpreg then
353 out:write(format("%04d %-5s ", ins, ridsp_name(ridsp)))
354 else
355 out:write(format("%04d ", ins))
356 end
357 out:write(format("%s%s %s %s ",
358 band(ot, 64) == 0 and " " or ">",
359 band(ot, 128) == 0 and " " or "+",
360 irtype[t], op))
361 local m1 = band(m, 3)
362 if m1 ~= 3 then -- op1 != IRMnone
363 if op1 < 0 then
364 out:write(formatk(tr, op1))
365 else
366 out:write(format(m1 == 0 and "%04d" or "#%-3d", op1))
367 end
368 local m2 = band(m, 3*4)
369 if m2 ~= 3*4 then -- op2 != IRMnone
370 if m2 == 1*4 then -- op2 == IRMlit
371 local litn = litname[op]
372 if litn and litn[op2] then
373 out:write(" ", litn[op2])
374 else
375 out:write(format(" #%-3d", op2))
376 end
377 elseif op2 < 0 then
378 out:write(" ", formatk(tr, op2))
379 else
380 out:write(format(" %04d", op2))
381 end
382 end
383 end
384 out:write("\n")
385 end
386 end
387 if snap then
388 if dumpreg then
389 out:write(format(".... SNAP #%-3d [ ", snapno))
390 else
391 out:write(format(".... SNAP #%-3d [ ", snapno))
392 end
393 printsnap(tr, snap)
394 end
395end
396
397------------------------------------------------------------------------------
398
399local recprefix = ""
400local recdepth = 0
401
402-- Format trace error message.
403local function fmterr(err, info)
404 if type(err) == "number" then
405 if type(info) == "function" then
406 local fi = funcinfo(info)
407 if fi.ffid then
408 info = vmdef.ffnames[fi.ffid]
409 else
410 info = fi.loc
411 end
412 end
413 err = format(vmdef.traceerr[err], info)
414 end
415 return err
416end
417
418-- Dump trace states.
419local function dump_trace(what, tr, func, pc, otr, oex)
420 if what == "stop" or (what == "abort" and dumpmode.a) then
421 if dumpmode.i then dump_ir(tr, dumpmode.s, dumpmode.r and what == "stop")
422 elseif dumpmode.s then dump_snap(tr) end
423 if dumpmode.m then dump_mcode(tr) end
424 end
425 if what == "start" then
426 if dumpmode.H then out:write('<pre class="ljdump">\n') end
427 out:write("---- TRACE ", tr, " ", what)
428 if otr then out:write(" ", otr, "/", oex) end
429 local fi = funcinfo(func, pc)
430 out:write(" ", fi.loc, "\n")
431 recprefix = ""
432 reclevel = 0
433 elseif what == "stop" or what == "abort" then
434 out:write("---- TRACE ", tr, " ", what)
435 recprefix = nil
436 if what == "abort" then
437 local fi = funcinfo(func, pc)
438 out:write(" ", fi.loc, " -- ", fmterr(otr, oex), "\n")
439 else
440 local link = traceinfo(tr).link
441 if link == tr then
442 link = "loop"
443 elseif link == 0 then
444 link = "interpreter"
445 end
446 out:write(" -> ", link, "\n")
447 end
448 if dumpmode.H then out:write("</pre>\n\n") else out:write("\n") end
449 else
450 out:write("---- TRACE ", what, "\n\n")
451 end
452 out:flush()
453end
454
455-- Dump recorded bytecode.
456local function dump_record(tr, func, pc, depth, callee)
457 if depth ~= recdepth then
458 recdepth = depth
459 recprefix = rep(" .", depth)
460 end
461 local line = bcline(func, pc, recprefix)
462 if dumpmode.H then line = gsub(line, "[<>&]", html_escape) end
463 if type(callee) == "function" then
464 local fi = funcinfo(callee)
465 if fi.ffid then
466 out:write(sub(line, 1, -2), " ; ", vmdef.ffnames[fi.ffid], "\n")
467 else
468 out:write(sub(line, 1, -2), " ; ", fi.loc, "\n")
469 end
470 else
471 out:write(line)
472 end
473 if band(funcbc(func, pc), 0xff) < 16 then -- Write JMP for cond. ORDER BC
474 out:write(bcline(func, pc+1, recprefix))
475 end
476end
477
478------------------------------------------------------------------------------
479
480-- Dump taken trace exits.
481local function dump_texit(tr, ex, ngpr, nfpr, ...)
482 out:write("---- TRACE ", tr, " exit ", ex, "\n")
483 if dumpmode.X then
484 local regs = {...}
485 for i=1,ngpr do
486 out:write(format(" %08x", regs[i]))
487 if i % 8 == 0 then out:write("\n") end
488 end
489 for i=1,nfpr do
490 out:write(format(" %+17.14g", regs[ngpr+i]))
491 if i % 4 == 0 then out:write("\n") end
492 end
493 end
494end
495
496------------------------------------------------------------------------------
497
498-- Detach dump handlers.
499local function dumpoff()
500 if active then
501 active = false
502 jit.attach(dump_texit)
503 jit.attach(dump_record)
504 jit.attach(dump_trace)
505 if out and out ~= stdout and out ~= stderr then out:close() end
506 out = nil
507 end
508end
509
510-- Open the output file and attach dump handlers.
511local function dumpon(opt, outfile)
512 if active then dumpoff() end
513
514 local colormode = os.getenv("COLORTERM") and "A" or "T"
515 if opt then
516 opt = gsub(opt, "[TAH]", function(mode) colormode = mode; return ""; end)
517 end
518
519 local m = { t=true, b=true, i=true, m=true, }
520 if opt and opt ~= "" then
521 local o = sub(opt, 1, 1)
522 if o ~= "+" and o ~= "-" then m = {} end
523 for i=1,#opt do m[sub(opt, i, i)] = (o ~= "-") end
524 end
525 dumpmode = m
526
527 if m.t or m.b or m.i or m.s or m.m then
528 jit.attach(dump_trace, "trace")
529 end
530 if m.b then
531 jit.attach(dump_record, "record")
532 if not bcline then bcline = require("jit.bc").line end
533 end
534 if m.x or m.X then
535 jit.attach(dump_texit, "texit")
536 end
537
538 if not outfile then outfile = os.getenv("LUAJIT_DUMPFILE") end
539 if outfile then
540 out = outfile == "-" and stdout or assert(io.open(outfile, "w"))
541 else
542 out = stdout
543 end
544
545 m[colormode] = true
546 if colormode == "A" then
547 colorize = colorize_ansi
548 irtype = irtype_ansi
549 elseif colormode == "H" then
550 colorize = colorize_html
551 irtype = irtype_html
552 out:write(header_html)
553 else
554 colorize = colorize_text
555 irtype = irtype_text
556 end
557
558 active = true
559end
560
561-- Public module functions.
562module(...)
563
564on = dumpon
565off = dumpoff
566start = dumpon -- For -j command line option.
567
diff --git a/lib/v.lua b/lib/v.lua
new file mode 100644
index 00000000..39fb8ed5
--- /dev/null
+++ b/lib/v.lua
@@ -0,0 +1,156 @@
1----------------------------------------------------------------------------
2-- Verbose mode of the LuaJIT compiler.
3--
4-- Copyright (C) 2005-2009 Mike Pall. All rights reserved.
5-- Released under the MIT/X license. See Copyright Notice in luajit.h
6----------------------------------------------------------------------------
7--
8-- This module shows verbose information about the progress of the
9-- JIT compiler. It prints one line for each generated trace. This module
10-- is useful to see which code has been compiled or where the compiler
11-- punts and falls back to the interpreter.
12--
13-- Example usage:
14--
15-- luajit -jv -e "for i=1,1000 do for j=1,1000 do end end"
16-- luajit -jv=myapp.out myapp.lua
17--
18-- Default output is to stderr. To redirect the output to a file, pass a
19-- filename as an argument (use '-' for stdout) or set the environment
20-- variable LUAJIT_VERBOSEFILE. The file is overwritten every time the
21-- module is started.
22--
23-- The output from the first example should look like this:
24--
25-- [TRACE 1 (command line):1]
26-- [TRACE 2 (1/3) (command line):1 -> 1]
27--
28-- The first number in each line is the internal trace number. Next are
29-- the file name ('(command line)') and the line number (':1') where the
30-- trace has started. Side traces also show the parent trace number and
31-- the exit number where they are attached to in parentheses ('(1/3)').
32-- An arrow at the end shows where the trace links to ('-> 1'), unless
33-- it loops to itself.
34--
35-- In this case the inner loop gets hot and is traced first, generating
36-- a root trace. Then the last exit from the 1st trace gets hot, too,
37-- and triggers generation of the 2nd trace. The side trace follows the
38-- path along the outer loop and *around* the inner loop, back to its
39-- start, and then links to the 1st trace. Yes, this may seem unusual,
40-- if you know how traditional compilers work. Trace compilers are full
41-- of surprises like this -- have fun! :-)
42--
43-- Aborted traces are shown like this:
44--
45-- [TRACE --- foo.lua:44 -- leaving loop in root trace at foo:lua:50]
46--
47-- Don't worry -- trace aborts are quite common, even in programs which
48-- can be fully compiled. The compiler may retry several times until it
49-- finds a suitable trace.
50--
51-- Of course this doesn't work with features that are not-yet-implemented
52-- (NYI error messages). The VM simply falls back to the interpreter. This
53-- may not matter at all if the particular trace is not very high up in
54-- the CPU usage profile. Oh, and the interpreter is quite fast, too.
55--
56-- Also check out the -jdump module, which prints all the gory details.
57--
58------------------------------------------------------------------------------
59
60-- Cache some library functions and objects.
61local jit = require("jit")
62assert(jit.version_num == 20000, "LuaJIT core/library version mismatch")
63local jutil = require("jit.util")
64local vmdef = require("jit.vmdef")
65local funcinfo, traceinfo = jutil.funcinfo, jutil.traceinfo
66local type, format = type, string.format
67local stdout, stderr = io.stdout, io.stderr
68
69-- Active flag and output file handle.
70local active, out
71
72------------------------------------------------------------------------------
73
74local startloc, startex
75
76-- Format trace error message.
77local function fmterr(err, info)
78 if type(err) == "number" then
79 if type(info) == "function" then
80 local fi = funcinfo(info)
81 if fi.ffid then
82 info = vmdef.ffnames[fi.ffid]
83 else
84 info = fi.loc
85 end
86 end
87 err = format(vmdef.traceerr[err], info)
88 end
89 return err
90end
91
92-- Dump trace states.
93local function dump_trace(what, tr, func, pc, otr, oex)
94 if what == "start" then
95 startloc = funcinfo(func, pc).loc
96 startex = otr and "("..otr.."/"..oex..") " or ""
97 else
98 if what == "abort" then
99 local loc = funcinfo(func, pc).loc
100 if loc ~= startloc then
101 out:write(format("[TRACE --- %s%s -- %s at %s]\n",
102 startex, startloc, fmterr(otr, oex), loc))
103 else
104 out:write(format("[TRACE --- %s%s -- %s]\n",
105 startex, startloc, fmterr(otr, oex)))
106 end
107 elseif what == "stop" then
108 local link = traceinfo(tr).link
109 if link == 0 then
110 out:write(format("[TRACE %3s %s%s -- fallback to interpreter]\n",
111 tr, startex, startloc))
112 elseif link == tr then
113 out:write(format("[TRACE %3s %s%s]\n", tr, startex, startloc))
114 else
115 out:write(format("[TRACE %3s %s%s -> %d]\n",
116 tr, startex, startloc, link))
117 end
118 else
119 out:write(format("[TRACE %s]\n", what))
120 end
121 out:flush()
122 end
123end
124
125------------------------------------------------------------------------------
126
127-- Detach dump handlers.
128local function dumpoff()
129 if active then
130 active = false
131 jit.attach(dump_trace)
132 if out and out ~= stdout and out ~= stderr then out:close() end
133 out = nil
134 end
135end
136
137-- Open the output file and attach dump handlers.
138local function dumpon(outfile)
139 if active then dumpoff() end
140 if not outfile then outfile = os.getenv("LUAJIT_VERBOSEFILE") end
141 if outfile then
142 out = outfile == "-" and stdout or assert(io.open(outfile, "w"))
143 else
144 out = stderr
145 end
146 jit.attach(dump_trace, "trace")
147 active = true
148end
149
150-- Public module functions.
151module(...)
152
153on = dumpon
154off = dumpoff
155start = dumpon -- For -j command line option.
156