support passing function arguments
[neatcc.git] / out.c
blobd18ff64d3d311a990c6399cdd6d40eed41553f41
1 #include <unistd.h>
2 #include <elf.h>
3 #include "tok.h"
5 #define MAXSECS (1 << 7)
6 #define MAXSYMS (1 << 10)
8 #define SECSIZE (1 << 12)
9 #define MAXRELA (1 << 10)
11 #define SEC_SYMS 1
12 #define SEC_SYMSTR 2
13 #define SEC_BEG 3
15 static Elf64_Ehdr ehdr;
16 static Elf64_Shdr shdr[MAXSECS];
17 static int nshdr = SEC_BEG;
18 static Elf64_Sym syms[MAXSYMS];
19 static int nsyms;
20 static char symstr[MAXSYMS * 8];
21 static int nsymstr = 1;
23 static struct sec {
24 char buf[SECSIZE];
25 int len;
26 Elf64_Rela rela[MAXRELA];
27 int nrela;
28 Elf64_Shdr *sec_shdr;
29 Elf64_Shdr *rel_shdr;
30 } secs[MAXSECS];
31 static int nsecs;
32 static struct sec *sec;
34 #define MAXTEMP (1 << 12)
35 #define TMP_CONST 1
36 #define TMP_ADDR 2
38 static char *cur;
39 static long sp;
40 static struct tmp {
41 long addr;
42 int type;
43 } tmp[MAXTEMP];
44 static int ntmp;
46 static void putint(char *s, long n, int l)
48 while (l--) {
49 *s++ = n;
50 n >>= 8;
54 static char *putstr(char *s, char *r)
56 while (*r)
57 *s++ = *r++;
58 *s++ = '\0';
59 return s;
62 static Elf64_Sym *put_sym(char *name)
64 Elf64_Sym *sym = &syms[nsyms++];
65 sym->st_name = nsymstr;
66 nsymstr = putstr(symstr + nsymstr, name) - symstr;
67 sym->st_info = ELF64_ST_INFO(STB_GLOBAL, STT_FUNC);
68 return sym;
71 static void os(char *s, int n)
73 while (n--)
74 *cur++ = *s++;
77 static void oi(long n, int l)
79 while (l--) {
80 *cur++ = n;
81 n >>= 8;
85 static int tmp_pop(void)
87 os("\x48\x8b\x85", 3); /* mov top(%rbp), %rax */
88 oi(-tmp[--ntmp].addr, 4);
89 return tmp[ntmp].type;
92 static void tmp_push(int type)
94 tmp[ntmp].addr = sp;
95 tmp[ntmp].type = type;
96 sp += 8;
97 os("\x48\x89\x85", 3); /* mov %rax, top(%rbp) */
98 oi(-tmp[ntmp++].addr, 4);
102 void o_func_beg(char *name)
104 Elf64_Sym *sym = put_sym(name);
105 sec = &secs[nsecs++];
106 sym->st_shndx = nshdr;
107 sec->sec_shdr = &shdr[nshdr++];
108 sec->rel_shdr = &shdr[nshdr++];
109 sec->rel_shdr->sh_link = SEC_SYMS;
110 sec->rel_shdr->sh_info = sec->sec_shdr - shdr;
111 cur = sec->buf;
112 os("\x55", 1);
113 os("\x48\x89\xe5", 3);
114 sp = 8;
115 ntmp = 0;
116 os("\x48\x83\xec", 3);
117 oi(56, 1);
120 void o_num(int n)
122 os("\xb8", 1);
123 oi(n, 4);
124 tmp_push(TMP_CONST);
127 static void deref(void)
129 os("\x48\x8b\x00", 3); /* mov (%rax), %rax */
132 void o_deref(void)
134 if (tmp_pop() == TMP_ADDR)
135 deref();
136 tmp_push(TMP_ADDR);
139 void o_ret(int ret)
141 if (ret) {
142 if (tmp_pop() == TMP_ADDR)
143 deref();
144 } else {
145 os("\x48\x31\xc0", 3); /* xor %rax, %rax */
147 os("\xc9\xc3", 2); /* leave; ret; */
150 void o_func_end(void)
152 os("\xc9\xc3", 2); /* leave; ret; */
153 sec->len = cur - sec->buf;
156 void o_local(long addr)
158 os("\x48\x89\xe8", 3); /* mov %rbp, %rax */
159 os("\x48\x05", 2); /* add $addr, %rax */
160 oi(-addr, 4);
161 tmp_push(TMP_ADDR);
164 long o_mklocal(void)
166 long addr = sp;
167 sp += 8;
168 return addr;
171 void o_rmlocal(long addr)
173 sp = addr;
176 long o_arg(int i)
178 char mov[3];
179 long addr = o_mklocal();
180 mov[0] = "\x48\x48\x48\x48\x4c\x4c"[i];
181 mov[1] = '\x89';
182 mov[2] = "\xbd\xb5\x95\x8d\x85\x8d"[i];
183 os(mov, 3); /* mov %xxx, addr(%rbp) */
184 oi(-addr, 4);
185 return addr;
188 void o_assign(void)
190 if (tmp_pop() == TMP_ADDR)
191 deref();
192 os("\x48\x89\xc3", 3); /* mov %rax, %rbx */
193 tmp_pop();
194 os("\x48\x89\x18", 3); /* mov %rbx, (%rax) */
197 static long codeaddr(void)
199 return cur - sec->buf;
202 long o_mklabel(void)
204 return codeaddr();
207 void o_jz(long addr)
209 os("\x48\x85\xc0", 3); /* test %rax, %rax */
210 os("\x0f\x84", 2); /* jz $addr */
211 oi(codeaddr() - addr - 4, 4);
214 long o_stubjz(void)
216 o_jz(codeaddr());
217 return cur - sec->buf - 4;
220 void o_filljz(long addr)
222 putint(sec->buf + addr, codeaddr() - addr - 4, 4);
225 void out_init(void)
229 static int sym_find(char *name)
231 int i;
232 for (i = 0; i < nsyms; i++)
233 if (!strcmp(name, symstr + syms[i].st_name))
234 return i;
235 return nsyms;
238 void o_symaddr(char *name)
240 Elf64_Rela *rela = &sec->rela[sec->nrela++];
241 os("\x48\xc7\xc0", 3); /* mov $addr, %rax */
242 rela->r_offset = codeaddr();
243 rela->r_info = ELF64_R_INFO(sym_find(name), R_X86_64_32);
244 oi(0, 4);
245 tmp_push(TMP_ADDR);
248 static void setarg(int i)
250 char mov[3];
251 mov[0] = "\x48\x48\x48\x48\x49\x49"[i];
252 mov[1] = '\x89';
253 mov[2] = "\xc7\xc6\xc2\xc1\xc0\xc1"[i];
254 os(mov, 3); /* mov %rax, %xxx */
257 void o_call(int argc)
259 int i;
260 if (!argc)
261 os("\x48\x31\xc0", 3); /* xor %rax, %rax */
262 for (i = 0; i < argc; i++) {
263 if (tmp_pop() == TMP_ADDR)
264 deref();
265 setarg(argc - i - 1);
267 tmp_pop();
268 os("\xff\xd0", 2); /* callq *%rax */
269 tmp_push(TMP_CONST);
272 void out_write(int fd)
274 Elf64_Shdr *symstr_shdr = &shdr[SEC_SYMSTR];
275 Elf64_Shdr *syms_shdr = &shdr[SEC_SYMS];
276 unsigned long offset = sizeof(ehdr);
277 int i;
279 ehdr.e_ident[0] = 0x7f;
280 ehdr.e_ident[1] = 'E';
281 ehdr.e_ident[2] = 'L';
282 ehdr.e_ident[3] = 'F';
283 ehdr.e_ident[4] = ELFCLASS64;
284 ehdr.e_ident[5] = ELFDATA2LSB;
285 ehdr.e_ident[6] = EV_CURRENT;
286 ehdr.e_type = ET_REL;
287 ehdr.e_machine = EM_X86_64;
288 ehdr.e_version = EV_CURRENT;
289 ehdr.e_ehsize = sizeof(ehdr);
290 ehdr.e_shentsize = sizeof(shdr[0]);
291 ehdr.e_shoff = offset;
292 ehdr.e_shnum = nshdr;
293 ehdr.e_shstrndx = SEC_SYMSTR;
294 offset += sizeof(shdr[0]) * nshdr;
296 syms_shdr->sh_type = SHT_SYMTAB;
297 syms_shdr->sh_offset = offset;
298 syms_shdr->sh_size = nsyms * sizeof(syms[0]);
299 syms_shdr->sh_entsize = sizeof(syms[0]);
300 syms_shdr->sh_link = SEC_SYMSTR;
301 offset += syms_shdr->sh_size;
303 symstr_shdr->sh_type = SHT_STRTAB;
304 symstr_shdr->sh_offset = offset;
305 symstr_shdr->sh_size = nsymstr;
306 symstr_shdr->sh_entsize = 1;
307 offset += symstr_shdr->sh_size;
309 for (i = 0; i < nsecs; i++) {
310 struct sec *sec = &secs[i];
312 sec->sec_shdr->sh_type = SHT_PROGBITS;
313 sec->sec_shdr->sh_flags = SHF_EXECINSTR;
314 sec->sec_shdr->sh_offset = offset;
315 sec->sec_shdr->sh_size = sec->len;
316 sec->sec_shdr->sh_entsize = 1;
317 offset += sec->sec_shdr->sh_size;
319 sec->rel_shdr->sh_type = SHT_RELA;
320 sec->rel_shdr->sh_offset = offset;
321 sec->rel_shdr->sh_size = sec->nrela * sizeof(sec->rela[0]);
322 sec->rel_shdr->sh_entsize = sizeof(sec->rela[0]);
323 offset += sec->rel_shdr->sh_size;
326 write(fd, &ehdr, sizeof(ehdr));
327 write(fd, shdr, sizeof(shdr[0]) * nshdr);
328 write(fd, syms, sizeof(syms[0]) * nsyms);
329 write(fd, symstr, nsymstr);
330 for (i = 0; i < nsecs; i++) {
331 struct sec *sec = &secs[i];
332 write(fd, sec->buf, sec->len);
333 write(fd, sec->rela, sec->nrela * sizeof(sec->rela[0]));