1 /*
2 ** DynASM s390x encoding engine.
3 ** Copyright (C) 2005-2017 Mike Pall. All rights reserved.
4 ** Released under the MIT license. See dynasm.lua for full copyright notice.
5 */
6
7 #include <stddef.h>
8 #include <stdarg.h>
9 #include <string.h>
10 #include <stdlib.h>
11
12 #define DASM_ARCH "s390x"
13
14 #ifndef DASM_EXTERN
15 #define DASM_EXTERN(a,b,c,d) 0
16 #endif
17
18 /* Action definitions. */
19 enum {
20 DASM_STOP, DASM_SECTION, DASM_ESC, DASM_REL_EXT,
21 /* The following actions need a buffer position. */
22 DASM_ALIGN, DASM_REL_LG, DASM_LABEL_LG,
23 /* The following actions also have an argument. */
24 DASM_REL_PC, DASM_LABEL_PC,
25 DASM_DISP12, DASM_DISP20,
26 DASM_IMM8, DASM_IMM16, DASM_IMM32,
27 DASM_LEN8R,DASM_LEN4HR,DASM_LEN4LR,
28 DASM__MAX
29 };
30
31 /* Maximum number of section buffer positions for a single dasm_put() call. */
32 #define DASM_MAXSECPOS 25
33
34 /* DynASM encoder status codes. Action list offset or number are or'ed in. */
35 #define DASM_S_OK 0x00000000
36 #define DASM_S_NOMEM 0x01000000
37 #define DASM_S_PHASE 0x02000000
38 #define DASM_S_MATCH_SEC 0x03000000
39 #define DASM_S_RANGE_I 0x11000000
40 #define DASM_S_RANGE_SEC 0x12000000
41 #define DASM_S_RANGE_LG 0x13000000
42 #define DASM_S_RANGE_PC 0x14000000
43 #define DASM_S_RANGE_REL 0x15000000
44 #define DASM_S_UNDEF_LG 0x21000000
45 #define DASM_S_UNDEF_PC 0x22000000
46
47 /* Macros to convert positions (8 bit section + 24 bit index). */
48 #define DASM_POS2IDX(pos) ((pos)&0x00ffffff)
49 #define DASM_POS2BIAS(pos) ((pos)&0xff000000)
50 #define DASM_SEC2POS(sec) ((sec)<<24)
51 #define DASM_POS2SEC(pos) ((pos)>>24)
52 #define DASM_POS2PTR(D, pos) (D->sections[DASM_POS2SEC(pos)].rbuf + (pos))
53
54 /* Action list type. */
55 typedef const unsigned short *dasm_ActList;
56
57 /* Per-section structure. */
58 typedef struct dasm_Section {
59 int *rbuf; /* Biased buffer pointer (negative section bias). */
60 int *buf; /* True buffer pointer. */
61 size_t bsize; /* Buffer size in bytes. */
62 int pos; /* Biased buffer position. */
63 int epos; /* End of biased buffer position - max single put. */
64 int ofs; /* Byte offset into section. */
65 } dasm_Section;
66
67 /* Core structure holding the DynASM encoding state. */
68 struct dasm_State {
69 size_t psize; /* Allocated size of this structure. */
70 dasm_ActList actionlist; /* Current actionlist pointer. */
71 int *lglabels; /* Local/global chain/pos ptrs. */
72 size_t lgsize;
73 int *pclabels; /* PC label chains/pos ptrs. */
74 size_t pcsize;
75 void **globals; /* Array of globals (bias -10). */
76 dasm_Section *section; /* Pointer to active section. */
77 size_t codesize; /* Total size of all code sections. */
78 int maxsection; /* 0 <= sectionidx < maxsection. */
79 int status; /* Status code. */
80 dasm_Section sections[1]; /* All sections. Alloc-extended. */
81 };
82
83 /* The size of the core structure depends on the max. number of sections. */
84 #define DASM_PSZ(ms) (sizeof(dasm_State)+(ms-1)*sizeof(dasm_Section))
85
86
87 /* Initialize DynASM state. */
dasm_init(Dst_DECL,int maxsection)88 void dasm_init(Dst_DECL, int maxsection)
89 {
90 dasm_State *D;
91 size_t psz = 0;
92 int i;
93 Dst_REF = NULL;
94 DASM_M_GROW(Dst, struct dasm_State, Dst_REF, psz, DASM_PSZ(maxsection));
95 D = Dst_REF;
96 D->psize = psz;
97 D->lglabels = NULL;
98 D->lgsize = 0;
99 D->pclabels = NULL;
100 D->pcsize = 0;
101 D->globals = NULL;
102 D->maxsection = maxsection;
103 for (i = 0; i < maxsection; i++) {
104 D->sections[i].buf = NULL; /* Need this for pass3. */
105 D->sections[i].rbuf = D->sections[i].buf - DASM_SEC2POS(i);
106 D->sections[i].bsize = 0;
107 D->sections[i].epos = 0; /* Wrong, but is recalculated after resize. */
108 }
109 }
110
111 /* Free DynASM state. */
dasm_free(Dst_DECL)112 void dasm_free(Dst_DECL)
113 {
114 dasm_State *D = Dst_REF;
115 int i;
116 for (i = 0; i < D->maxsection; i++)
117 if (D->sections[i].buf)
118 DASM_M_FREE(Dst, D->sections[i].buf, D->sections[i].bsize);
119 if (D->pclabels)
120 DASM_M_FREE(Dst, D->pclabels, D->pcsize);
121 if (D->lglabels)
122 DASM_M_FREE(Dst, D->lglabels, D->lgsize);
123 DASM_M_FREE(Dst, D, D->psize);
124 }
125
126 /* Setup global label array. Must be called before dasm_setup(). */
dasm_setupglobal(Dst_DECL,void ** gl,unsigned int maxgl)127 void dasm_setupglobal(Dst_DECL, void **gl, unsigned int maxgl)
128 {
129 dasm_State *D = Dst_REF;
130 D->globals = gl - 10; /* Negative bias to compensate for locals. */
131 DASM_M_GROW(Dst, int, D->lglabels, D->lgsize, (10 + maxgl) * sizeof(int));
132 }
133
134 /* Grow PC label array. Can be called after dasm_setup(), too. */
dasm_growpc(Dst_DECL,unsigned int maxpc)135 void dasm_growpc(Dst_DECL, unsigned int maxpc)
136 {
137 dasm_State *D = Dst_REF;
138 size_t osz = D->pcsize;
139 DASM_M_GROW(Dst, int, D->pclabels, D->pcsize, maxpc * sizeof(int));
140 memset((void *)(((unsigned char *)D->pclabels) + osz), 0, D->pcsize - osz);
141 }
142
143 /* Setup encoder. */
dasm_setup(Dst_DECL,const void * actionlist)144 void dasm_setup(Dst_DECL, const void *actionlist)
145 {
146 dasm_State *D = Dst_REF;
147 int i;
148 D->actionlist = (dasm_ActList) actionlist;
149 D->status = DASM_S_OK;
150 D->section = &D->sections[0];
151 memset((void *)D->lglabels, 0, D->lgsize);
152 if (D->pclabels)
153 memset((void *)D->pclabels, 0, D->pcsize);
154 for (i = 0; i < D->maxsection; i++) {
155 D->sections[i].pos = DASM_SEC2POS(i);
156 D->sections[i].ofs = 0;
157 }
158 }
159
160
161 #ifdef DASM_CHECKS
162 #define CK(x, st) \
163 do { if (!(x)) { \
164 D->status = DASM_S_##st|(p-D->actionlist-1); return; } } while (0)
165 #define CKPL(kind, st) \
166 do { if ((size_t)((char *)pl-(char *)D->kind##labels) >= D->kind##size) { \
167 D->status = DASM_S_RANGE_##st|(p-D->actionlist-1); return; } } while (0)
168 #else
169 #define CK(x, st) ((void)0)
170 #define CKPL(kind, st) ((void)0)
171 #endif
172
173 /* Pass 1: Store actions and args, link branches/labels, estimate offsets. */
dasm_put(Dst_DECL,int start,...)174 void dasm_put(Dst_DECL, int start, ...)
175 {
176 va_list ap;
177 dasm_State *D = Dst_REF;
178 dasm_ActList p = D->actionlist + start;
179 dasm_Section *sec = D->section;
180 int pos = sec->pos, ofs = sec->ofs;
181 int *b;
182
183 if (pos >= sec->epos) {
184 DASM_M_GROW(Dst, int, sec->buf, sec->bsize,
185 sec->bsize + 2 * DASM_MAXSECPOS * sizeof(int));
186 sec->rbuf = sec->buf - DASM_POS2BIAS(pos);
187 sec->epos =
188 (int)sec->bsize / sizeof(int) - DASM_MAXSECPOS + DASM_POS2BIAS(pos);
189 }
190
191 b = sec->rbuf;
192 b[pos++] = start;
193
194 va_start(ap, start);
195 while (1) {
196 unsigned short ins = *p++;
197 unsigned short action = ins;
198 if (action >= DASM__MAX) {
199 ofs += 2;
200 continue;
201 }
202
203 int *pl, n = action >= DASM_REL_PC ? va_arg(ap, int) : 0;
204 switch (action) {
205 case DASM_STOP:
206 goto stop;
207 case DASM_SECTION:
208 n = *p++ & 255;
209 CK(n < D->maxsection, RANGE_SEC);
210 D->section = &D->sections[n];
211 goto stop;
212 case DASM_ESC:
213 p++;
214 ofs += 2;
215 break;
216 case DASM_REL_EXT:
217 p++;
218 ofs += 4;
219 break;
220 case DASM_ALIGN:
221 ofs += *p++;
222 b[pos++] = ofs;
223 break;
224 case DASM_REL_LG:
225 if (p[-2] >> 12 == 0xc) { /* RIL instruction needs 32-bit immediate. */
226 ofs += 2;
227 }
228 n = *p++ - 10;
229 pl = D->lglabels + n;
230 /* Bkwd rel or global. */
231 if (n >= 0) {
232 CK(n >= 10 || *pl < 0, RANGE_LG);
233 CKPL(lg, LG);
234 goto putrel;
235 }
236 pl += 10;
237 n = *pl;
238 if (n < 0)
239 n = 0; /* Start new chain for fwd rel if label exists. */
240 goto linkrel;
241 case DASM_REL_PC:
242 if (p[-2] >> 12 == 0xc) { /* RIL instruction needs 32-bit immediate. */
243 ofs += 2;
244 }
245 pl = D->pclabels + n;
246 CKPL(pc, PC);
247 putrel:
248 n = *pl;
249 if (n < 0) { /* Label exists. Get label pos and store it. */
250 b[pos] = -n;
251 } else {
252 linkrel:
253 b[pos] = n; /* Else link to rel chain, anchored at label. */
254 *pl = pos;
255 }
256 ofs += 2;
257 pos++;
258 break;
259 case DASM_LABEL_LG:
260 pl = D->lglabels + *p++ - 10;
261 CKPL(lg, LG);
262 goto putlabel;
263 case DASM_LABEL_PC:
264 pl = D->pclabels + n;
265 CKPL(pc, PC);
266 putlabel:
267 n = *pl; /* n > 0: Collapse rel chain and replace with label pos. */
268 while (n > 0) {
269 int *pb = DASM_POS2PTR(D, n);
270 n = *pb;
271 *pb = pos;
272 }
273 *pl = -pos; /* Label exists now. */
274 b[pos++] = ofs; /* Store pass1 offset estimate. */
275 break;
276 case DASM_IMM8:
277 b[pos++] = n;
278 break;
279 case DASM_IMM16:
280 CK(((short)n) == n || ((unsigned short)n) == n, RANGE_I); /* TODO: is this the right way to handle unsigned immediates? */
281 ofs += 2;
282 b[pos++] = n;
283 break;
284 case DASM_IMM32:
285 ofs += 4;
286 b[pos++] = n;
287 break;
288 case DASM_DISP20:
289 CK(-(1 << 19) <= n && n < (1 << 19), RANGE_I);
290 b[pos++] = n;
291 break;
292 case DASM_DISP12:
293 CK((n >> 12) == 0, RANGE_I);
294 b[pos++] = n;
295 break;
296 case DASM_LEN8R:
297 CK(n >= 1 && n <= 256, RANGE_I);
298 b[pos++] = n;
299 break;
300 case DASM_LEN4HR:
301 case DASM_LEN4LR:
302 CK(n >= 1 && n <= 128, RANGE_I);
303 b[pos++] = n;
304 break;
305 }
306 }
307 stop:
308 va_end(ap);
309 sec->pos = pos;
310 sec->ofs = ofs;
311 }
312
313 #undef CK
314
315 /* Pass 2: Link sections, shrink aligns, fix label offsets. */
dasm_link(Dst_DECL,size_t * szp)316 int dasm_link(Dst_DECL, size_t * szp)
317 {
318 dasm_State *D = Dst_REF;
319 int secnum;
320 int ofs = 0;
321
322 #ifdef DASM_CHECKS
323 *szp = 0;
324 if (D->status != DASM_S_OK)
325 return D->status;
326 {
327 int pc;
328 for (pc = 0; pc * sizeof(int) < D->pcsize; pc++)
329 if (D->pclabels[pc] > 0)
330 return DASM_S_UNDEF_PC | pc;
331 }
332 #endif
333
334 { /* Handle globals not defined in this translation unit. */
335 int idx;
336 for (idx = 20; idx * sizeof(int) < D->lgsize; idx++) {
337 int n = D->lglabels[idx];
338 /* Undefined label: Collapse rel chain and replace with marker (< 0). */
339 while (n > 0) {
340 int *pb = DASM_POS2PTR(D, n);
341 n = *pb;
342 *pb = -idx;
343 }
344 }
345 }
346
347 /* Combine all code sections. No support for data sections (yet). */
348 for (secnum = 0; secnum < D->maxsection; secnum++) {
349 dasm_Section *sec = D->sections + secnum;
350 int *b = sec->rbuf;
351 int pos = DASM_SEC2POS(secnum);
352 int lastpos = sec->pos;
353
354 while (pos != lastpos) {
355 dasm_ActList p = D->actionlist + b[pos++];
356 while (1) {
357 unsigned short ins = *p++;
358 unsigned short action = ins;
359 switch (action) {
360 case DASM_STOP:
361 case DASM_SECTION:
362 goto stop;
363 case DASM_ESC:
364 p++;
365 break;
366 case DASM_REL_EXT:
367 p++;
368 break;
369 case DASM_ALIGN:
370 ofs -= (b[pos++] + ofs) & *p++;
371 break;
372 case DASM_REL_LG:
373 case DASM_REL_PC:
374 p++;
375 pos++;
376 break;
377 case DASM_LABEL_LG:
378 case DASM_LABEL_PC:
379 p++;
380 b[pos++] += ofs;
381 break;
382 case DASM_IMM8:
383 case DASM_IMM16:
384 case DASM_IMM32:
385 case DASM_DISP20:
386 case DASM_DISP12:
387 case DASM_LEN8R:
388 case DASM_LEN4HR:
389 case DASM_LEN4LR:
390 pos++;
391 break;
392 }
393 }
394 stop:(void)0;
395 }
396 ofs += sec->ofs; /* Next section starts right after current section. */
397 }
398
399 D->codesize = ofs; /* Total size of all code sections */
400 *szp = ofs;
401 return DASM_S_OK;
402 }
403
404 #ifdef DASM_CHECKS
405 #define CK(x, st) \
406 do { if (!(x)) return DASM_S_##st|(p-D->actionlist-1); } while (0)
407 #else
408 #define CK(x, st) ((void)0)
409 #endif
410
411 /* Pass 3: Encode sections. */
dasm_encode(Dst_DECL,void * buffer)412 int dasm_encode(Dst_DECL, void *buffer)
413 {
414 dasm_State *D = Dst_REF;
415 char *base = (char *)buffer;
416 unsigned short *cp = (unsigned short *)buffer;
417 int secnum;
418
419 /* Encode all code sections. No support for data sections (yet). */
420 for (secnum = 0; secnum < D->maxsection; secnum++) {
421 dasm_Section *sec = D->sections + secnum;
422 int *b = sec->buf;
423 int *endb = sec->rbuf + sec->pos;
424
425 while (b != endb) {
426 dasm_ActList p = D->actionlist + *b++;
427 while (1) {
428 unsigned short ins = *p++;
429 unsigned short action = ins;
430 int n = (action >= DASM_ALIGN && action < DASM__MAX) ? *b++ : 0;
431 switch (action) {
432 case DASM_STOP:
433 case DASM_SECTION:
434 goto stop;
435 case DASM_ESC:
436 *cp++ = *p++;
437 break;
438 case DASM_REL_EXT:
439 n = DASM_EXTERN(Dst, (unsigned char *)cp, *p++, 1) - 4;
440 goto patchrel;
441 case DASM_ALIGN:
442 ins = *p++;
443 /* TODO: emit 4-byte noprs instead of 2-byte nops where possible. */
444 while ((((char *)cp - base) & ins))
445 *cp++ = 0x0700; /* nop */
446 break;
447 case DASM_REL_LG:
448 CK(n >= 0, UNDEF_LG);
449 case DASM_REL_PC:
450 CK(n >= 0, UNDEF_PC);
451 n = *DASM_POS2PTR(D, n) - (int)((char *)cp - base);
452 p++; /* skip argument */
453 patchrel:
454 /* Offsets are halfword aligned (so need to be halved). */
455 n += 2; /* Offset is relative to start of instruction. */
456 if (cp[-1] >> 12 == 0xc) {
457 *cp++ = n >> 17;
458 } else {
459 CK(-(1 << 16) <= n && n < (1 << 16) && (n & 1) == 0, RANGE_LG);
460 }
461 *cp++ = n >> 1;
462 break;
463 case DASM_LABEL_LG:
464 ins = *p++;
465 if (ins >= 20)
466 D->globals[ins - 10] = (void *)(base + n);
467 break;
468 case DASM_LABEL_PC:
469 break;
470 case DASM_IMM8:
471 cp[-1] |= n & 0xff;
472 break;
473 case DASM_IMM16:
474 *cp++ = n;
475 break;
476 case DASM_IMM32:
477 *cp++ = n >> 16;
478 *cp++ = n;
479 break;
480 case DASM_DISP20:
481 cp[-2] |= n & 0xfff;
482 cp[-1] |= (n >> 4) & 0xff00;
483 break;
484 case DASM_DISP12:
485 cp[-1] |= n & 0xfff;
486 break;
487 case DASM_LEN8R:
488 cp[-1] |= (n - 1) & 0xff;
489 break;
490 case DASM_LEN4HR:
491 cp[-1] |= ((n - 1) << 4) & 0xf0;
492 break;
493 case DASM_LEN4LR:
494 cp[-1] |= (n - 1) & 0x0f;
495 break;
496 default:
497 *cp++ = ins;
498 break;
499 }
500 }
501 stop:(void)0;
502 }
503 }
504
505 if (base + D->codesize != (char *)cp) /* Check for phase errors. */
506 return DASM_S_PHASE;
507 return DASM_S_OK;
508 }
509
510 #undef CK
511
512 /* Get PC label offset. */
dasm_getpclabel(Dst_DECL,unsigned int pc)513 int dasm_getpclabel(Dst_DECL, unsigned int pc)
514 {
515 dasm_State *D = Dst_REF;
516 if (pc * sizeof(int) < D->pcsize) {
517 int pos = D->pclabels[pc];
518 if (pos < 0)
519 return *DASM_POS2PTR(D, -pos);
520 if (pos > 0)
521 return -1; /* Undefined. */
522 }
523 return -2; /* Unused or out of range. */
524 }
525
526 #ifdef DASM_CHECKS
527 /* Optional sanity checker to call between isolated encoding steps. */
dasm_checkstep(Dst_DECL,int secmatch)528 int dasm_checkstep(Dst_DECL, int secmatch)
529 {
530 dasm_State *D = Dst_REF;
531 if (D->status == DASM_S_OK) {
532 int i;
533 for (i = 1; i <= 9; i++) {
534 if (D->lglabels[i] > 0) {
535 D->status = DASM_S_UNDEF_LG | i;
536 break;
537 }
538 D->lglabels[i] = 0;
539 }
540 }
541 if (D->status == DASM_S_OK && secmatch >= 0 &&
542 D->section != &D->sections[secmatch])
543 D->status = DASM_S_MATCH_SEC | (D->section - D->sections);
544 return D->status;
545 }
546 #endif
547
548