2 ** DynASM x86 encoding engine.
3 ** Copyright (C) 2005-2016 Mike Pall. All rights reserved.
4 ** Released under the MIT license. See dynasm.lua for full copyright notice.
12 #define DASM_ARCH "x86"
15 #define DASM_EXTERN(a,b,c,d) 0
18 /* Action definitions. DASM_STOP must be 255. */
21 DASM_IMM_S
, DASM_IMM_B
, DASM_IMM_W
, DASM_IMM_D
, DASM_IMM_WB
, DASM_IMM_DB
,
22 DASM_VREG
, DASM_SPACE
, DASM_SETLABEL
, DASM_REL_A
, DASM_REL_LG
, DASM_REL_PC
,
23 DASM_IMM_LG
, DASM_IMM_PC
, DASM_LABEL_LG
, DASM_LABEL_PC
, DASM_ALIGN
,
24 DASM_EXTERN
, DASM_ESC
, DASM_MARK
, DASM_SECTION
, DASM_STOP
27 /* Maximum number of section buffer positions for a single dasm_put() call. */
28 #define DASM_MAXSECPOS 25
30 /* DynASM encoder status codes. Action list offset or number are or'ed in. */
31 #define DASM_S_OK 0x00000000
32 #define DASM_S_NOMEM 0x01000000
33 #define DASM_S_PHASE 0x02000000
34 #define DASM_S_MATCH_SEC 0x03000000
35 #define DASM_S_RANGE_I 0x11000000
36 #define DASM_S_RANGE_SEC 0x12000000
37 #define DASM_S_RANGE_LG 0x13000000
38 #define DASM_S_RANGE_PC 0x14000000
39 #define DASM_S_RANGE_VREG 0x15000000
40 #define DASM_S_UNDEF_L 0x21000000
41 #define DASM_S_UNDEF_PC 0x22000000
43 /* Macros to convert positions (8 bit section + 24 bit index). */
44 #define DASM_POS2IDX(pos) ((pos)&0x00ffffff)
45 #define DASM_POS2BIAS(pos) ((pos)&0xff000000)
46 #define DASM_SEC2POS(sec) ((sec)<<24)
47 #define DASM_POS2SEC(pos) ((pos)>>24)
48 #define DASM_POS2PTR(D, pos) (D->sections[DASM_POS2SEC(pos)].rbuf + (pos))
50 /* Action list type. */
51 typedef const unsigned char *dasm_ActList
;
53 /* Per-section structure. */
54 typedef struct dasm_Section
{
55 int *rbuf
; /* Biased buffer pointer (negative section bias). */
56 int *buf
; /* True buffer pointer. */
57 size_t bsize
; /* Buffer size in bytes. */
58 int pos
; /* Biased buffer position. */
59 int epos
; /* End of biased buffer position - max single put. */
60 int ofs
; /* Byte offset into section. */
63 /* Core structure holding the DynASM encoding state. */
65 size_t psize
; /* Allocated size of this structure. */
66 dasm_ActList actionlist
; /* Current actionlist pointer. */
67 int *lglabels
; /* Local/global chain/pos ptrs. */
69 int *pclabels
; /* PC label chains/pos ptrs. */
71 void **globals
; /* Array of globals (bias -10). */
72 dasm_Section
*section
; /* Pointer to active section. */
73 size_t codesize
; /* Total size of all code sections. */
74 int maxsection
; /* 0 <= sectionidx < maxsection. */
75 int status
; /* Status code. */
76 dasm_Section sections
[1]; /* All sections. Alloc-extended. */
79 /* The size of the core structure depends on the max. number of sections. */
80 #define DASM_PSZ(ms) (sizeof(dasm_State)+(ms-1)*sizeof(dasm_Section))
83 /* Initialize DynASM state. */
84 void dasm_init(Dst_DECL
, int maxsection
)
90 DASM_M_GROW(Dst
, struct dasm_State
, Dst_REF
, psz
, DASM_PSZ(maxsection
));
98 D
->maxsection
= maxsection
;
99 for (i
= 0; i
< maxsection
; i
++) {
100 D
->sections
[i
].buf
= NULL
; /* Need this for pass3. */
101 D
->sections
[i
].rbuf
= D
->sections
[i
].buf
- DASM_SEC2POS(i
);
102 D
->sections
[i
].bsize
= 0;
103 D
->sections
[i
].epos
= 0; /* Wrong, but is recalculated after resize. */
107 /* Free DynASM state. */
108 void dasm_free(Dst_DECL
)
110 dasm_State
*D
= Dst_REF
;
112 for (i
= 0; i
< D
->maxsection
; i
++)
113 if (D
->sections
[i
].buf
)
114 DASM_M_FREE(Dst
, D
->sections
[i
].buf
, D
->sections
[i
].bsize
);
115 if (D
->pclabels
) DASM_M_FREE(Dst
, D
->pclabels
, D
->pcsize
);
116 if (D
->lglabels
) DASM_M_FREE(Dst
, D
->lglabels
, D
->lgsize
);
117 DASM_M_FREE(Dst
, D
, D
->psize
);
120 /* Setup global label array. Must be called before dasm_setup(). */
121 void dasm_setupglobal(Dst_DECL
, void **gl
, unsigned int maxgl
)
123 dasm_State
*D
= Dst_REF
;
124 D
->globals
= gl
- 10; /* Negative bias to compensate for locals. */
125 DASM_M_GROW(Dst
, int, D
->lglabels
, D
->lgsize
, (10+maxgl
)*sizeof(int));
128 /* Grow PC label array. Can be called after dasm_setup(), too. */
129 void dasm_growpc(Dst_DECL
, unsigned int maxpc
)
131 dasm_State
*D
= Dst_REF
;
132 size_t osz
= D
->pcsize
;
133 DASM_M_GROW(Dst
, int, D
->pclabels
, D
->pcsize
, maxpc
*sizeof(int));
134 memset((void *)(((unsigned char *)D
->pclabels
)+osz
), 0, D
->pcsize
-osz
);
138 void dasm_setup(Dst_DECL
, const void *actionlist
)
140 dasm_State
*D
= Dst_REF
;
142 D
->actionlist
= (dasm_ActList
)actionlist
;
143 D
->status
= DASM_S_OK
;
144 D
->section
= &D
->sections
[0];
145 memset((void *)D
->lglabels
, 0, D
->lgsize
);
146 if (D
->pclabels
) memset((void *)D
->pclabels
, 0, D
->pcsize
);
147 for (i
= 0; i
< D
->maxsection
; i
++) {
148 D
->sections
[i
].pos
= DASM_SEC2POS(i
);
149 D
->sections
[i
].ofs
= 0;
157 D->status = DASM_S_##st|(int)(p-D->actionlist-1); return; } } while (0)
158 #define CKPL(kind, st) \
159 do { if ((size_t)((char *)pl-(char *)D->kind##labels) >= D->kind##size) { \
160 D->status=DASM_S_RANGE_##st|(int)(p-D->actionlist-1); return; } } while (0)
162 #define CK(x, st) ((void)0)
163 #define CKPL(kind, st) ((void)0)
166 /* Pass 1: Store actions and args, link branches/labels, estimate offsets. */
167 void dasm_put(Dst_DECL
, int start
, ...)
170 dasm_State
*D
= Dst_REF
;
171 dasm_ActList p
= D
->actionlist
+ start
;
172 dasm_Section
*sec
= D
->section
;
173 int pos
= sec
->pos
, ofs
= sec
->ofs
, mrm
= -1;
176 if (pos
>= sec
->epos
) {
177 DASM_M_GROW(Dst
, int, sec
->buf
, sec
->bsize
,
178 sec
->bsize
+ 2*DASM_MAXSECPOS
*sizeof(int));
179 sec
->rbuf
= sec
->buf
- DASM_POS2BIAS(pos
);
180 sec
->epos
= (int)sec
->bsize
/sizeof(int) - DASM_MAXSECPOS
+DASM_POS2BIAS(pos
);
189 if (action
< DASM_DISP
) {
191 } else if (action
<= DASM_REL_A
) {
192 int n
= va_arg(ap
, int);
196 if (n
== 0) { if (mrm
< 0) mrm
= p
[-2]; if ((mrm
&7) != 5) break; }
197 case DASM_IMM_DB
: if (((n
+128)&-256) == 0) goto ob
;
198 case DASM_REL_A
: /* Assumes ptrdiff_t is int. !x64 */
199 case DASM_IMM_D
: ofs
+= 4; break;
200 case DASM_IMM_S
: CK(((n
+128)&-256) == 0, RANGE_I
); goto ob
;
201 case DASM_IMM_B
: CK((n
&-256) == 0, RANGE_I
); ob
: ofs
++; break;
202 case DASM_IMM_WB
: if (((n
+128)&-256) == 0) goto ob
;
203 case DASM_IMM_W
: CK((n
&-65536) == 0, RANGE_I
); ofs
+= 2; break;
204 case DASM_SPACE
: p
++; ofs
+= n
; break;
205 case DASM_SETLABEL
: b
[pos
-2] = -0x40000000; break; /* Neg. label ofs. */
206 case DASM_VREG
: CK((n
&-16) == 0 && (n
!= 4 || (*p
>>5) != 2), RANGE_VREG
);
207 if (*p
< 0x40 && p
[1] == DASM_DISP
) mrm
= n
;
208 if (*p
< 0x20 && (n
&7) == 4) ofs
++;
209 switch ((*p
++ >> 3) & 3) {
210 case 3: n
|= b
[pos
-3];
211 case 2: n
|= b
[pos
-2];
212 case 1: if (n
<= 7) { b
[pos
-1] |= 0x10; ofs
--; }
222 n
= *p
++; pl
= D
->lglabels
+ n
;
223 /* Bkwd rel or global. */
224 if (n
<= 246) { CK(n
>=10||*pl
<0, RANGE_LG
); CKPL(lg
, LG
); goto putrel
; }
226 if (n
< 0) n
= 0; /* Start new chain for fwd rel if label exists. */
229 case DASM_IMM_PC
: pl
= D
->pclabels
+ va_arg(ap
, int); CKPL(pc
, PC
);
232 if (n
< 0) { /* Label exists. Get label pos and store it. */
236 b
[pos
] = n
; /* Else link to rel chain, anchored at label. */
240 ofs
+= 4; /* Maximum offset needed. */
241 if (action
== DASM_REL_LG
|| action
== DASM_REL_PC
)
242 b
[pos
++] = ofs
; /* Store pass1 offset estimate. */
244 case DASM_LABEL_LG
: pl
= D
->lglabels
+ *p
++; CKPL(lg
, LG
); goto putlabel
;
245 case DASM_LABEL_PC
: pl
= D
->pclabels
+ va_arg(ap
, int); CKPL(pc
, PC
);
247 n
= *pl
; /* n > 0: Collapse rel chain and replace with label pos. */
248 while (n
> 0) { int *pb
= DASM_POS2PTR(D
, n
); n
= *pb
; *pb
= pos
; }
249 *pl
= -pos
; /* Label exists now. */
250 b
[pos
++] = ofs
; /* Store pass1 offset estimate. */
253 ofs
+= *p
++; /* Maximum alignment needed (arg is 2**n-1). */
254 b
[pos
++] = ofs
; /* Store pass1 offset estimate. */
256 case DASM_EXTERN
: p
+= 2; ofs
+= 4; break;
257 case DASM_ESC
: p
++; ofs
++; break;
258 case DASM_MARK
: mrm
= p
[-2]; break;
260 n
= *p
; CK(n
< D
->maxsection
, RANGE_SEC
); D
->section
= &D
->sections
[n
];
261 case DASM_STOP
: goto stop
;
272 /* Pass 2: Link sections, shrink branches/aligns, fix label offsets. */
273 int dasm_link(Dst_DECL
, size_t *szp
)
275 dasm_State
*D
= Dst_REF
;
281 if (D
->status
!= DASM_S_OK
) return D
->status
;
284 for (pc
= 0; pc
*sizeof(int) < D
->pcsize
; pc
++)
285 if (D
->pclabels
[pc
] > 0) return DASM_S_UNDEF_PC
|pc
;
289 { /* Handle globals not defined in this translation unit. */
291 for (idx
= 10; idx
*sizeof(int) < D
->lgsize
; idx
++) {
292 int n
= D
->lglabels
[idx
];
293 /* Undefined label: Collapse rel chain and replace with marker (< 0). */
294 while (n
> 0) { int *pb
= DASM_POS2PTR(D
, n
); n
= *pb
; *pb
= -idx
; }
298 /* Combine all code sections. No support for data sections (yet). */
299 for (secnum
= 0; secnum
< D
->maxsection
; secnum
++) {
300 dasm_Section
*sec
= D
->sections
+ secnum
;
302 int pos
= DASM_SEC2POS(secnum
);
303 int lastpos
= sec
->pos
;
305 while (pos
!= lastpos
) {
306 dasm_ActList p
= D
->actionlist
+ b
[pos
++];
308 int op
, action
= *p
++;
310 case DASM_REL_LG
: p
++; op
= p
[-3]; goto rel_pc
;
311 case DASM_REL_PC
: op
= p
[-2]; rel_pc
: {
312 int shrink
= op
== 0xe9 ? 3 : ((op
&0xf0) == 0x80 ? 4 : 0);
313 if (shrink
) { /* Shrinkable branch opcode? */
314 int lofs
, lpos
= b
[pos
];
315 if (lpos
< 0) goto noshrink
; /* Ext global? */
316 lofs
= *DASM_POS2PTR(D
, lpos
);
317 if (lpos
> pos
) { /* Fwd label: add cumulative section offsets. */
319 for (i
= secnum
; i
< DASM_POS2SEC(lpos
); i
++)
320 lofs
+= D
->sections
[i
].ofs
;
322 lofs
-= ofs
; /* Bkwd label: unfix offset. */
324 lofs
-= b
[pos
+1]; /* Short branch ok? */
325 if (lofs
>= -128-shrink
&& lofs
<= 127) ofs
-= shrink
; /* Yes. */
326 else { noshrink
: shrink
= 0; } /* No, cannot shrink op. */
332 case DASM_SPACE
: case DASM_IMM_LG
: case DASM_VREG
: p
++;
333 case DASM_DISP
: case DASM_IMM_S
: case DASM_IMM_B
: case DASM_IMM_W
:
334 case DASM_IMM_D
: case DASM_IMM_WB
: case DASM_IMM_DB
:
335 case DASM_SETLABEL
: case DASM_REL_A
: case DASM_IMM_PC
: pos
++; break;
336 case DASM_LABEL_LG
: p
++;
337 case DASM_LABEL_PC
: b
[pos
++] += ofs
; break; /* Fix label offset. */
338 case DASM_ALIGN
: ofs
-= (b
[pos
++]+ofs
)&*p
++; break; /* Adjust ofs. */
339 case DASM_EXTERN
: p
+= 2; break;
340 case DASM_ESC
: p
++; break;
341 case DASM_MARK
: break;
342 case DASM_SECTION
: case DASM_STOP
: goto stop
;
347 ofs
+= sec
->ofs
; /* Next section starts right after current section. */
350 D
->codesize
= ofs
; /* Total size of all code sections */
355 #define dasmb(x) *cp++ = (unsigned char)(x)
356 #ifndef DASM_ALIGNED_WRITES
358 do { *((unsigned short *)cp) = (unsigned short)(x); cp+=2; } while (0)
360 do { *((unsigned int *)cp) = (unsigned int)(x); cp+=4; } while (0)
362 #define dasmw(x) do { dasmb(x); dasmb((x)>>8); } while (0)
363 #define dasmd(x) do { dasmw(x); dasmw((x)>>16); } while (0)
366 /* Pass 3: Encode sections. */
367 int dasm_encode(Dst_DECL
, void *buffer
)
369 dasm_State
*D
= Dst_REF
;
370 unsigned char *base
= (unsigned char *)buffer
;
371 unsigned char *cp
= base
;
374 /* Encode all code sections. No support for data sections (yet). */
375 for (secnum
= 0; secnum
< D
->maxsection
; secnum
++) {
376 dasm_Section
*sec
= D
->sections
+ secnum
;
378 int *endb
= sec
->rbuf
+ sec
->pos
;
381 dasm_ActList p
= D
->actionlist
+ *b
++;
382 unsigned char *mark
= NULL
;
385 int n
= (action
>= DASM_DISP
&& action
<= DASM_ALIGN
) ? *b
++ : 0;
387 case DASM_DISP
: if (!mark
) mark
= cp
; {
388 unsigned char *mm
= mark
;
389 if (*p
!= DASM_IMM_DB
&& *p
!= DASM_IMM_WB
) mark
= NULL
;
390 if (n
== 0) { int mrm
= mm
[-1]&7; if (mrm
== 4) mrm
= mm
[0]&7;
391 if (mrm
!= 5) { mm
[-1] -= 0x80; break; } }
392 if (((n
+128) & -256) != 0) goto wd
; else mm
[-1] -= 0x40;
394 case DASM_IMM_S
: case DASM_IMM_B
: wb
: dasmb(n
); break;
395 case DASM_IMM_DB
: if (((n
+128)&-256) == 0) {
396 db
: if (!mark
) mark
= cp
; mark
[-2] += 2; mark
= NULL
; goto wb
;
398 case DASM_IMM_D
: wd
: dasmd(n
); break;
399 case DASM_IMM_WB
: if (((n
+128)&-256) == 0) goto db
; else mark
= NULL
;
400 case DASM_IMM_W
: dasmw(n
); break;
403 unsigned char *ex
= cp
- (t
&7);
404 if ((n
& 8) && t
< 0xa0) {
405 if (*ex
& 0x80) ex
[1] ^= 0x20 << (t
>>6); else *ex
^= 1 << (t
>>6);
407 } else if (n
& 0x10) {
409 *ex
= 0xc5; ex
[1] = (ex
[1] & 0x80) | ex
[2]; ex
+= 2;
411 while (++ex
< cp
) ex
[-1] = *ex
;
416 if (t
>= 0xc0) n
<<= 4;
417 else if (t
>= 0x40) n
<<= 3;
418 else if (n
== 4 && t
< 0x20) { cp
[-1] ^= n
; *cp
++ = 0x20; }
422 case DASM_REL_LG
: p
++; if (n
>= 0) goto rel_pc
;
423 b
++; n
= (int)(ptrdiff_t)D
->globals
[-n
];
424 case DASM_REL_A
: rel_a
: n
-= (int)(ptrdiff_t)(cp
+4); goto wd
; /* !x64 */
425 case DASM_REL_PC
: rel_pc
: {
427 int *pb
= DASM_POS2PTR(D
, n
); if (*pb
< 0) { n
= pb
[1]; goto rel_a
; }
428 n
= *pb
- ((int)(cp
-base
) + 4-shrink
);
429 if (shrink
== 0) goto wd
;
430 if (shrink
== 4) { cp
--; cp
[-1] = *cp
-0x10; } else cp
[-1] = 0xeb;
434 p
++; if (n
< 0) { n
= (int)(ptrdiff_t)D
->globals
[-n
]; goto wd
; }
436 int *pb
= DASM_POS2PTR(D
, n
);
437 n
= *pb
< 0 ? pb
[1] : (*pb
+ (int)(ptrdiff_t)base
);
440 case DASM_LABEL_LG
: {
443 D
->globals
[idx
] = (void *)(base
+ (*p
== DASM_SETLABEL
? *b
: n
));
446 case DASM_LABEL_PC
: case DASM_SETLABEL
: break;
447 case DASM_SPACE
: { int fill
= *p
++; while (n
--) *cp
++ = fill
; break; }
450 while (((cp
-base
) & n
)) *cp
++ = 0x90; /* nop */
452 case DASM_EXTERN
: n
= DASM_EXTERN(Dst
, cp
, p
[1], *p
); p
+= 2; goto wd
;
453 case DASM_MARK
: mark
= cp
; break;
454 case DASM_ESC
: action
= *p
++;
455 default: *cp
++ = action
; break;
456 case DASM_SECTION
: case DASM_STOP
: goto stop
;
463 if (base
+ D
->codesize
!= cp
) /* Check for phase errors. */
468 /* Get PC label offset. */
469 int dasm_getpclabel(Dst_DECL
, unsigned int pc
)
471 dasm_State
*D
= Dst_REF
;
472 if (pc
*sizeof(int) < D
->pcsize
) {
473 int pos
= D
->pclabels
[pc
];
474 if (pos
< 0) return *DASM_POS2PTR(D
, -pos
);
475 if (pos
> 0) return -1; /* Undefined. */
477 return -2; /* Unused or out of range. */
481 /* Optional sanity checker to call between isolated encoding steps. */
482 int dasm_checkstep(Dst_DECL
, int secmatch
)
484 dasm_State
*D
= Dst_REF
;
485 if (D
->status
== DASM_S_OK
) {
487 for (i
= 1; i
<= 9; i
++) {
488 if (D
->lglabels
[i
] > 0) { D
->status
= DASM_S_UNDEF_L
|i
; break; }
492 if (D
->status
== DASM_S_OK
&& secmatch
>= 0 &&
493 D
->section
!= &D
->sections
[secmatch
])
494 D
->status
= DASM_S_MATCH_SEC
|(int)(D
->section
-D
->sections
);