-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdynasm_demo_x86.dasl
245 lines (217 loc) · 7.84 KB
/
dynasm_demo_x86.dasl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
--go@ luajit dynasm.lua *
local ffi = require'ffi' -- required
local dasm = require'dasm' -- required
|.arch ARCH
|
|.if not (X86 or X64)
| .error invalid arch ARCH
|.endif
|
|.actionlist actions // gen. `actions` to pass to dasm.new()
|.externnames externnames // gen `externnames` to pass to dasm.new() to solve extern names
|.section sec1, sec2 // gen. `DASM_SECTION_SEC1`, ..., `DASM_MAXSECTION`
|.globals global_ // gen. `global_<labelname>`, ..., `DASM_MAXGLOBAL`
|.globalnames globalnames // gen. `globalnames` to get the names of global labels
local demo = {} --demo table: {name = codegen_func}
local demos = {} --demo list in code order: {name1, ...}
setmetatable(demo, {__newindex = function(t,k,v) rawset(demo, k, v); demos[#demos+1] = k end})
--each code generator function generates some code into the Dst argument (which is a dasm state),
--and optionally returns a function that knows how to test the resulting code (if nothing is returned,
--the code will be just dumped for inspection).
function demo.literals(Dst)
| mov al, 0xff // number literals are translation-time and are range-checked.
| mov eax, 0xffffffff // this is correct and will result in 0xffffffff.
| mov eax, dword*0x22222222 // size overrides with number literals are also translation-time.
| mov eax, [ebx + ecx * 8 + 0x7fffffff] // this mov form allows +/- 2G literal displacements.
|.if X86
| mov eax, [0xffffffff] // this mov form allows 0..4G literals.
|.else
| mov eax, [0x7fffffff] // this mov form allows only 0..2G literals and you won't get an error!
|.endif
end
function demo.immediate_subst(Dst) --immediate value substitution
| mov al, 0+0xff // expressions are encoding-time and coerced to int32.
| mov eax, 0+0xffffffff // but expressions are also normalized first, so we can pass a full uint32.
| mov eax, [ebx + ecx * 8 + 0x7fffffff] // this mov form allows +/- 2G expression displacements.
|.if X86
| mov eax, [0+0xffffffff] // this mov form allows 0..4G expressions.
|.else
| mov eax, [0+0x7fffffff] // this mov form allows only 0..2G expressions and you won't get an error!
| mov64 eax, [0+0xdeadbeefdeadbeefULL] // mov64 allows full 64bit expressions (they're never literals).
| mov64 rax, 0+0xdeadbeefdeadbeefULL // mov64 allows full 64bit expressions (they're never literals).
|.endif
end
function demo.addr(Dst) --address substution for jump targets
local addr = ffi.arch == 'x86' and 0xdeadbeef or 0xdeadbeefdeadbeefULL
local ptr = ffi.cast("void*", addr)
|.if X86
| jmp &addr // works with numbers
| jmp &ptr // works with pointers too
|.else
| mov64 rax, addr // no `&` on x64 but you can load an address with `mov64`
| mov64 rax, ptr // works with pointers too
|.endif
end
function demo.var_reg(Dst) --variable register numbers
| mov Rd(2), 0 // Rd(2) = edx. this is always encoding-time.
| fld Rf(2) // Rf(2) = st2. this is always encoding-time.
end
function demo.types(Dst) --type macros
if not rawget(_G, '__TYPEDEFS') then
rawset(_G, '__TYPEDEFS', true)
ffi.cdef'typedef struct Type1 {int f1; int f2;} Type1'
ffi.cdef'typedef Type1 Type2'
end
local n,m = 1,1
|.type TP1, Type1, eax
|.type TP2, Type2, ebx
|
| mov eax, TP1:ebx // +0
| mov ebx, TP1 // +0
| mov eax, TP1->f1 // +0
| mov ebx, TP1.f2 // +4
| mov ecx, TP2[n+m]->f1 // +16
| mov edx, TP2[n-2*m].f2 // -4
| mov edx, TP2:ecx[n-2*m].f2 // -4
end
function demo.code_sections(Dst)
|.sec1; mov ax, 1
|.sec2; mov ax, 2
|.sec1; mov bx, 1 // append code to .sec1
|.sec2; mov bx, 2 // append code to .sec2
end
function demo.global_labels(Dst)
|->l1:
| jmp ->l2
|->l2:
| jmp ->l1
end
function demo.local_labels(Dst)
|2:
|1:
| jmp >1
|1:
| jmp <2
end
function demo.label_subst(Dst)
|5:
|->label_subst:
| mov eax, [<5] // encoded as abs. addr. on x86 and as rip-relative on x64
| mov ebx, [->label_subst]
end
function demo.align(Dst)
local n = 0x90
| mov eax, 0x22222222
|.byte n
|.align word // puts nop
|.word bit.bor(bit.lshift(n,8), n) // expressions work too
|.align dword // puts nop
|.space 1+1, 0x90 // size can be variable, filler must be constant
| mov eax, 0x22222222
end
function demo.cond(Dst) --conditional compilation
|.define def1, 1
|.define def2, 1
|.if def1 + def2 == 2 // any lua expression works. note: a 0 result evaluates to false!
| nop
|.elif def2
| .fatal this won't be parsed
|.else
| .fatal this won't be parsed
|.endif
end
function demo.macros(Dst)
|.macro saveregs
|.if X86
| push ebp; push edi; push esi; push ebx
|.else
| push rbp; push rdi; push rsi; push rbx
|.endif
|.endmacro
|
|.macro restoreregs
|.if X86
| pop ebx; pop esi; pop edi; pop ebp
|.else
| pop rbx; pop rsi; pop rdi; pop rbp
|.endif
|.endmacro
|
|.macro cat, s1, s2, s3
| s1 .. s2 .. s3 // concatenation like `##` in C
|.endmacro
|
| saveregs
| restoreregs
| mov eax, 0xffffffff
| cat n, o, p
end
function demo.captures(Dst)
|.capture c1; mov eax, 0x1a; .endcapture
|.capture c2; mov ebx, 0x2a; .endcapture
|.capture c1; mov eax, 0x1b; .endcapture
|.capture c2; mov ebx, 0x2b; .endcapture
|.dumpcapture c1
|.dumpcapture c2
|.dumpcapture c2 // we can dump it again
end
function demo.extern(Dst)
local s1, s2 = 'Hello from %s!\n', 'DynASM'
local p1 = ffi.cast('const char*', s1) -- with `const` we can take addr. of a Lua string without making a copy!
local p2 = ffi.cast('const char*', s2)
if ffi.arch == 'x64' then
ffi.cdef'void printf(...)' --we need this to get printf's address
end
|.if X86 // ptr args in stack in reverse order
| sub esp, 4 // align the stack for OSX: 0 - call:4 - p1:4 - p2:4 = -12
| push &p2
| push &p1
| call extern printf
| add esp, 8 // pop args
| add esp, 4 // dealign
| ret
|.elif WINDOWS // ptr args in rcx, rdx, ...
| sub rsp, 32 // allocate shadow space for printf
| mov64 rcx, p1
| mov64 rdx, p2
| mov64 rax, ffi.C.printf // extern doesn't work on x64
| call rax
| add rsp, 32 // put it back
| ret
|.else // ptr args in rdi, rsi, ...
| sub rsp, 8 // align the stack for OSX: 0 - call:8 = -8
| mov rdi, &p1
| mov rsi, &p2
| mov64 rdx, ffi.C.printf // extern doesn't work on x64
| call rdx
| add rsp, 8
| ret
|.endif
return function(buf)
local _ = s1, s2 --pin the strings
local code = ffi.cast('void __cdecl (*) ()', buf)
code()
end
end
function demo.multiply(Dst)
|.if X86 // int args in stack in reverse order; int ret in eax
| mov eax, [esp+4]
| imul dword [esp+8]
| ret
|.elif WINDOWS // int args in rcx, rdx, ...; int ret in rax
| mov rax, rcx
| imul rdx
| ret
|.else // int args in rdi, rsi, ...; int ret in rax
| mov rax, rdi
| imul rsi
| ret
|.endif
return function(buf)
local mul = ffi.cast('int32_t __cdecl (*) (int32_t x, int32_t y)', buf)
assert(mul(7, -5) == -35)
assert(mul(-4, 0x08000000) == -0x20000000)
print'ok'
end
end
return {demo, demos, actions, externnames, globalnames, DASM_MAXSECTION, DASM_MAXGLOBAL}