2 ** DynASM x86 encoding engine.
3 ** Copyright (C) 2005-2012 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
= 4;
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
&7) == 4) 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
&-8) == 0 && (n
!= 4 || (*p
&1) == 0), RANGE_VREG
);
207 if (*p
++ == 1 && *p
== DASM_DISP
) mrm
= n
; continue;
215 n
= *p
++; pl
= D
->lglabels
+ n
;
216 if (n
<= 246) { CKPL(lg
, LG
); goto putrel
; } /* Bkwd rel or global. */
218 if (n
< 0) n
= 0; /* Start new chain for fwd rel if label exists. */
221 case DASM_IMM_PC
: pl
= D
->pclabels
+ va_arg(ap
, int); CKPL(pc
, PC
);
224 if (n
< 0) { /* Label exists. Get label pos and store it. */
228 b
[pos
] = n
; /* Else link to rel chain, anchored at label. */
232 ofs
+= 4; /* Maximum offset needed. */
233 if (action
== DASM_REL_LG
|| action
== DASM_REL_PC
)
234 b
[pos
++] = ofs
; /* Store pass1 offset estimate. */
236 case DASM_LABEL_LG
: pl
= D
->lglabels
+ *p
++; CKPL(lg
, LG
); goto putlabel
;
237 case DASM_LABEL_PC
: pl
= D
->pclabels
+ va_arg(ap
, int); CKPL(pc
, PC
);
239 n
= *pl
; /* n > 0: Collapse rel chain and replace with label pos. */
240 while (n
> 0) { int *pb
= DASM_POS2PTR(D
, n
); n
= *pb
; *pb
= pos
; }
241 *pl
= -pos
; /* Label exists now. */
242 b
[pos
++] = ofs
; /* Store pass1 offset estimate. */
245 ofs
+= *p
++; /* Maximum alignment needed (arg is 2**n-1). */
246 b
[pos
++] = ofs
; /* Store pass1 offset estimate. */
248 case DASM_EXTERN
: p
+= 2; ofs
+= 4; break;
249 case DASM_ESC
: p
++; ofs
++; break;
250 case DASM_MARK
: mrm
= p
[-2]; break;
252 n
= *p
; CK(n
< D
->maxsection
, RANGE_SEC
); D
->section
= &D
->sections
[n
];
253 case DASM_STOP
: goto stop
;
264 /* Pass 2: Link sections, shrink branches/aligns, fix label offsets. */
265 int dasm_link(Dst_DECL
, size_t *szp
)
267 dasm_State
*D
= Dst_REF
;
273 if (D
->status
!= DASM_S_OK
) return D
->status
;
276 for (pc
= 0; pc
*sizeof(int) < D
->pcsize
; pc
++)
277 if (D
->pclabels
[pc
] > 0) return DASM_S_UNDEF_PC
|pc
;
281 { /* Handle globals not defined in this translation unit. */
283 for (idx
= 10; idx
*sizeof(int) < D
->lgsize
; idx
++) {
284 int n
= D
->lglabels
[idx
];
285 /* Undefined label: Collapse rel chain and replace with marker (< 0). */
286 while (n
> 0) { int *pb
= DASM_POS2PTR(D
, n
); n
= *pb
; *pb
= -idx
; }
290 /* Combine all code sections. No support for data sections (yet). */
291 for (secnum
= 0; secnum
< D
->maxsection
; secnum
++) {
292 dasm_Section
*sec
= D
->sections
+ secnum
;
294 int pos
= DASM_SEC2POS(secnum
);
295 int lastpos
= sec
->pos
;
297 while (pos
!= lastpos
) {
298 dasm_ActList p
= D
->actionlist
+ b
[pos
++];
300 int op
, action
= *p
++;
302 case DASM_REL_LG
: p
++; op
= p
[-3]; goto rel_pc
;
303 case DASM_REL_PC
: op
= p
[-2]; rel_pc
: {
304 int shrink
= op
== 0xe9 ? 3 : ((op
&0xf0) == 0x80 ? 4 : 0);
305 if (shrink
) { /* Shrinkable branch opcode? */
306 int lofs
, lpos
= b
[pos
];
307 if (lpos
< 0) goto noshrink
; /* Ext global? */
308 lofs
= *DASM_POS2PTR(D
, lpos
);
309 if (lpos
> pos
) { /* Fwd label: add cumulative section offsets. */
311 for (i
= secnum
; i
< DASM_POS2SEC(lpos
); i
++)
312 lofs
+= D
->sections
[i
].ofs
;
314 lofs
-= ofs
; /* Bkwd label: unfix offset. */
316 lofs
-= b
[pos
+1]; /* Short branch ok? */
317 if (lofs
>= -128-shrink
&& lofs
<= 127) ofs
-= shrink
; /* Yes. */
318 else { noshrink
: shrink
= 0; } /* No, cannot shrink op. */
324 case DASM_SPACE
: case DASM_IMM_LG
: case DASM_VREG
: p
++;
325 case DASM_DISP
: case DASM_IMM_S
: case DASM_IMM_B
: case DASM_IMM_W
:
326 case DASM_IMM_D
: case DASM_IMM_WB
: case DASM_IMM_DB
:
327 case DASM_SETLABEL
: case DASM_REL_A
: case DASM_IMM_PC
: pos
++; break;
328 case DASM_LABEL_LG
: p
++;
329 case DASM_LABEL_PC
: b
[pos
++] += ofs
; break; /* Fix label offset. */
330 case DASM_ALIGN
: ofs
-= (b
[pos
++]+ofs
)&*p
++; break; /* Adjust ofs. */
331 case DASM_EXTERN
: p
+= 2; break;
332 case DASM_ESC
: p
++; break;
333 case DASM_MARK
: break;
334 case DASM_SECTION
: case DASM_STOP
: goto stop
;
339 ofs
+= sec
->ofs
; /* Next section starts right after current section. */
342 D
->codesize
= ofs
; /* Total size of all code sections */
347 #define dasmb(x) *cp++ = (unsigned char)(x)
348 #ifndef DASM_ALIGNED_WRITES
350 do { *((unsigned short *)cp) = (unsigned short)(x); cp+=2; } while (0)
352 do { *((unsigned int *)cp) = (unsigned int)(x); cp+=4; } while (0)
354 #define dasmw(x) do { dasmb(x); dasmb((x)>>8); } while (0)
355 #define dasmd(x) do { dasmw(x); dasmw((x)>>16); } while (0)
358 /* Pass 3: Encode sections. */
359 int dasm_encode(Dst_DECL
, void *buffer
)
361 dasm_State
*D
= Dst_REF
;
362 unsigned char *base
= (unsigned char *)buffer
;
363 unsigned char *cp
= base
;
366 /* Encode all code sections. No support for data sections (yet). */
367 for (secnum
= 0; secnum
< D
->maxsection
; secnum
++) {
368 dasm_Section
*sec
= D
->sections
+ secnum
;
370 int *endb
= sec
->rbuf
+ sec
->pos
;
373 dasm_ActList p
= D
->actionlist
+ *b
++;
374 unsigned char *mark
= NULL
;
377 int n
= (action
>= DASM_DISP
&& action
<= DASM_ALIGN
) ? *b
++ : 0;
379 case DASM_DISP
: if (!mark
) mark
= cp
; {
380 unsigned char *mm
= mark
;
381 if (*p
!= DASM_IMM_DB
&& *p
!= DASM_IMM_WB
) mark
= NULL
;
382 if (n
== 0) { int mrm
= mm
[-1]&7; if (mrm
== 4) mrm
= mm
[0]&7;
383 if (mrm
!= 5) { mm
[-1] -= 0x80; break; } }
384 if (((n
+128) & -256) != 0) goto wd
; else mm
[-1] -= 0x40;
386 case DASM_IMM_S
: case DASM_IMM_B
: wb
: dasmb(n
); break;
387 case DASM_IMM_DB
: if (((n
+128)&-256) == 0) {
388 db
: if (!mark
) mark
= cp
; mark
[-2] += 2; mark
= NULL
; goto wb
;
390 case DASM_IMM_D
: wd
: dasmd(n
); break;
391 case DASM_IMM_WB
: if (((n
+128)&-256) == 0) goto db
; else mark
= NULL
;
392 case DASM_IMM_W
: dasmw(n
); break;
393 case DASM_VREG
: { int t
= *p
++; if (t
>= 2) n
<<=3; cp
[-1] |= n
; break; }
394 case DASM_REL_LG
: p
++; if (n
>= 0) goto rel_pc
;
395 b
++; n
= (int)(ptrdiff_t)D
->globals
[-n
];
396 case DASM_REL_A
: rel_a
: n
-= (int)(ptrdiff_t)(cp
+4); goto wd
; /* !x64 */
397 case DASM_REL_PC
: rel_pc
: {
399 int *pb
= DASM_POS2PTR(D
, n
); if (*pb
< 0) { n
= pb
[1]; goto rel_a
; }
400 n
= *pb
- ((int)(cp
-base
) + 4-shrink
);
401 if (shrink
== 0) goto wd
;
402 if (shrink
== 4) { cp
--; cp
[-1] = *cp
-0x10; } else cp
[-1] = 0xeb;
406 p
++; if (n
< 0) { n
= (int)(ptrdiff_t)D
->globals
[-n
]; goto wd
; }
408 int *pb
= DASM_POS2PTR(D
, n
);
409 n
= *pb
< 0 ? pb
[1] : (*pb
+ (int)(ptrdiff_t)base
);
412 case DASM_LABEL_LG
: {
415 D
->globals
[idx
] = (void *)(base
+ (*p
== DASM_SETLABEL
? *b
: n
));
418 case DASM_LABEL_PC
: case DASM_SETLABEL
: break;
419 case DASM_SPACE
: { int fill
= *p
++; while (n
--) *cp
++ = fill
; break; }
422 while (((cp
-base
) & n
)) *cp
++ = 0x90; /* nop */
424 case DASM_EXTERN
: n
= DASM_EXTERN(Dst
, cp
, p
[1], *p
); p
+= 2; goto wd
;
425 case DASM_MARK
: mark
= cp
; break;
426 case DASM_ESC
: action
= *p
++;
427 default: *cp
++ = action
; break;
428 case DASM_SECTION
: case DASM_STOP
: goto stop
;
435 if (base
+ D
->codesize
!= cp
) /* Check for phase errors. */
440 /* Get PC label offset. */
441 int dasm_getpclabel(Dst_DECL
, unsigned int pc
)
443 dasm_State
*D
= Dst_REF
;
444 if (pc
*sizeof(int) < D
->pcsize
) {
445 int pos
= D
->pclabels
[pc
];
446 if (pos
< 0) return *DASM_POS2PTR(D
, -pos
);
447 if (pos
> 0) return -1; /* Undefined. */
449 return -2; /* Unused or out of range. */
453 /* Optional sanity checker to call between isolated encoding steps. */
454 int dasm_checkstep(Dst_DECL
, int secmatch
)
456 dasm_State
*D
= Dst_REF
;
457 if (D
->status
== DASM_S_OK
) {
459 for (i
= 1; i
<= 9; i
++) {
460 if (D
->lglabels
[i
] > 0) { D
->status
= DASM_S_UNDEF_L
|i
; break; }
464 if (D
->status
== DASM_S_OK
&& secmatch
>= 0 &&
465 D
->section
!= &D
->sections
[secmatch
])
466 D
->status
= DASM_S_MATCH_SEC
|(int)(D
->section
-D
->sections
);