support goto statement
[neatcc.git] / out.c
blobfb0bab255de42d7eee23a2d712fb0ad56aa64b98
1 #include <elf.h>
2 #include <string.h>
3 #include <unistd.h>
4 #include "gen.h"
6 #define ALIGN(x, a) (((x) + (a) - 1) & ~((a) - 1))
8 #define MAXSECS (1 << 7)
9 #define MAXSYMS (1 << 10)
10 #define MAXRELA (1 << 10)
11 #define SEC_SYMS 1
12 #define SEC_SYMSTR 2
13 #define SEC_DAT 3
14 #define SEC_BSS 4
15 #define SEC_BEG 5
17 static Elf64_Ehdr ehdr;
18 static Elf64_Shdr shdr[MAXSECS];
19 static int nshdr = SEC_BEG;
20 static Elf64_Sym syms[MAXSYMS];
21 static int nsyms = 1;
22 static char symstr[MAXSYMS * 8];
23 static int nsymstr = 1;
24 static int bsslen;
25 static char dat[SECSIZE];
26 static int datlen;
28 static struct sec {
29 char buf[SECSIZE];
30 int len;
31 Elf64_Sym *sym;
32 Elf64_Rela rela[MAXRELA];
33 int nrela;
34 Elf64_Shdr *sec_shdr;
35 Elf64_Shdr *rel_shdr;
36 } secs[MAXSECS];
37 static int nsecs;
38 static struct sec *sec;
40 static char *putstr(char *s, char *r)
42 while (*r)
43 *s++ = *r++;
44 *s++ = '\0';
45 return s;
48 static int sym_find(char *name)
50 int i;
51 for (i = 0; i < nsyms; i++)
52 if (!strcmp(name, symstr + syms[i].st_name))
53 return i;
54 return -1;
57 static Elf64_Sym *put_sym(char *name)
59 int found = name ? sym_find(name) : -1;
60 Elf64_Sym *sym = found != -1 ? &syms[found] : &syms[nsyms++];
61 if (!name)
62 sym->st_name = 0;
63 if (name && found == -1) {
64 sym->st_name = nsymstr;
65 nsymstr = putstr(symstr + nsymstr, name) - symstr;
67 return sym;
70 #define S_BIND(global) ((global) ? STB_GLOBAL : STB_LOCAL)
72 long out_func_beg(char *name, int global)
74 sec = &secs[nsecs++];
75 sec->sym = put_sym(name);
76 sec->sym->st_shndx = nshdr;
77 sec->sym->st_info = ELF64_ST_INFO(S_BIND(global), STT_FUNC);
78 sec->sec_shdr = &shdr[nshdr++];
79 sec->rel_shdr = &shdr[nshdr++];
80 sec->rel_shdr->sh_link = SEC_SYMS;
81 sec->rel_shdr->sh_info = sec->sec_shdr - shdr;
82 return sec->sym - syms;
85 void out_func_end(char *buf, int len)
87 memcpy(sec->buf, buf, len);
88 sec->len = len;
89 sec->sym->st_size = len;
92 void out_rela(long addr, int off, int rel)
94 Elf64_Rela *rela = &sec->rela[sec->nrela++];
95 rela->r_offset = off;
96 rela->r_info = ELF64_R_INFO(addr, rel ? R_X86_64_PC32 : R_X86_64_32);
99 #define SYMLOCAL(i) (ELF64_ST_BIND(syms[i].st_info) & STB_LOCAL)
101 static int syms_sort(void)
103 int mv[MAXSYMS];
104 int i, j;
105 int glob_beg;
106 for (i = 0; i < nsyms; i++)
107 mv[i] = i;
108 i = 1;
109 j = nsyms - 1;
110 while (1) {
111 Elf64_Sym t;
112 while (j > i && !SYMLOCAL(j))
113 j--;
114 while (i < j && SYMLOCAL(i))
115 i++;
116 if (i >= j)
117 break;
118 t = syms[j];
119 syms[i] = syms[j];
120 syms[i] = t;
121 mv[i] = j;
122 mv[j] = i;
124 glob_beg = i;
125 for (i = 0; i < nsecs; i++) {
126 for (j = 0; j < secs[i].nrela; i++) {
127 Elf64_Rela *rela = &sec[i].rela[j];
128 int sym = ELF64_R_SYM(rela->r_info);
129 int type = ELF64_R_TYPE(rela->r_info);
130 rela->r_info = ELF64_R_INFO(mv[sym], type);
133 return glob_beg;
136 void out_write(int fd)
138 Elf64_Shdr *symstr_shdr = &shdr[SEC_SYMSTR];
139 Elf64_Shdr *syms_shdr = &shdr[SEC_SYMS];
140 Elf64_Shdr *dat_shdr = &shdr[SEC_DAT];
141 Elf64_Shdr *bss_shdr = &shdr[SEC_BSS];
142 unsigned long offset = sizeof(ehdr);
143 int i;
145 ehdr.e_ident[0] = 0x7f;
146 ehdr.e_ident[1] = 'E';
147 ehdr.e_ident[2] = 'L';
148 ehdr.e_ident[3] = 'F';
149 ehdr.e_ident[4] = ELFCLASS64;
150 ehdr.e_ident[5] = ELFDATA2LSB;
151 ehdr.e_ident[6] = EV_CURRENT;
152 ehdr.e_type = ET_REL;
153 ehdr.e_machine = EM_X86_64;
154 ehdr.e_version = EV_CURRENT;
155 ehdr.e_ehsize = sizeof(ehdr);
156 ehdr.e_shentsize = sizeof(shdr[0]);
157 ehdr.e_shoff = offset;
158 ehdr.e_shnum = nshdr;
159 ehdr.e_shstrndx = SEC_SYMSTR;
160 offset += sizeof(shdr[0]) * nshdr;
162 syms_shdr->sh_type = SHT_SYMTAB;
163 syms_shdr->sh_offset = offset;
164 syms_shdr->sh_size = nsyms * sizeof(syms[0]);
165 syms_shdr->sh_entsize = sizeof(syms[0]);
166 syms_shdr->sh_link = SEC_SYMSTR;
167 syms_shdr->sh_info = syms_sort();
168 offset += syms_shdr->sh_size;
170 dat_shdr->sh_type = SHT_PROGBITS;
171 dat_shdr->sh_flags = SHF_WRITE;
172 dat_shdr->sh_offset = offset;
173 dat_shdr->sh_size = datlen;
174 dat_shdr->sh_entsize = 1;
175 dat_shdr->sh_addralign = 8;
176 offset += dat_shdr->sh_size;
178 bss_shdr->sh_type = SHT_NOBITS;
179 bss_shdr->sh_flags = SHF_ALLOC | SHF_WRITE;
180 bss_shdr->sh_offset = offset;
181 bss_shdr->sh_size = bsslen;
182 bss_shdr->sh_entsize = 1;
183 bss_shdr->sh_addralign = 8;
185 symstr_shdr->sh_type = SHT_STRTAB;
186 symstr_shdr->sh_offset = offset;
187 symstr_shdr->sh_size = nsymstr;
188 symstr_shdr->sh_entsize = 1;
189 offset += symstr_shdr->sh_size;
191 for (i = 0; i < nsecs; i++) {
192 struct sec *sec = &secs[i];
194 sec->sec_shdr->sh_type = SHT_PROGBITS;
195 sec->sec_shdr->sh_flags = SHF_EXECINSTR;
196 sec->sec_shdr->sh_offset = offset;
197 sec->sec_shdr->sh_size = sec->len;
198 sec->sec_shdr->sh_entsize = 1;
199 offset += sec->sec_shdr->sh_size;
201 sec->rel_shdr->sh_type = SHT_RELA;
202 sec->rel_shdr->sh_offset = offset;
203 sec->rel_shdr->sh_size = sec->nrela * sizeof(sec->rela[0]);
204 sec->rel_shdr->sh_entsize = sizeof(sec->rela[0]);
205 offset += sec->rel_shdr->sh_size;
208 write(fd, &ehdr, sizeof(ehdr));
209 write(fd, shdr, sizeof(shdr[0]) * nshdr);
210 write(fd, syms, sizeof(syms[0]) * nsyms);
211 write(fd, dat, datlen);
212 write(fd, symstr, nsymstr);
213 for (i = 0; i < nsecs; i++) {
214 struct sec *sec = &secs[i];
215 write(fd, sec->buf, sec->len);
216 write(fd, sec->rela, sec->nrela * sizeof(sec->rela[0]));
220 long o_mkvar(char *name, int size, int global)
222 Elf64_Sym *sym = put_sym(name);
223 sym->st_shndx = SEC_BSS;
224 sym->st_value = bsslen;
225 sym->st_size = size;
226 sym->st_info = ELF64_ST_INFO(S_BIND(global), STT_OBJECT);
227 bsslen = ALIGN(bsslen + size, 8);
228 return sym - syms;
231 long o_mkundef(char *name)
233 Elf64_Sym *sym = put_sym(name);
234 sym->st_shndx = SHN_UNDEF;
235 sym->st_info = ELF64_ST_INFO(STB_GLOBAL, STT_FUNC);
236 return sym - syms;
239 long o_mkdat(char *name, char *buf, int len, int global)
241 Elf64_Sym *sym = put_sym(name);
242 sym->st_shndx = SEC_DAT;
243 sym->st_value = datlen;
244 sym->st_size = len;
245 sym->st_info = ELF64_ST_INFO(S_BIND(global), STT_OBJECT);
246 memcpy(dat + datlen, buf, len);
247 datlen = ALIGN(datlen + len, 8);
248 return sym - syms;