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