experimental risc-like x86_64 port of neatcc
[neatcc.git] / out.c
blobe9da0fcf1fe91ceba5333f63c5e587f095601ee9
1 #include <elf.h>
2 #include <string.h>
3 #include <unistd.h>
4 #include "out.h"
6 #define ALIGN(x, a) (((x) + (a) - 1) & ~((a) - 1))
8 #define MAXSYMS (1 << 12)
9 #define MAXRELA (1 << 12)
10 #define SEC_TEXT 1
11 #define SEC_RELA 2
12 #define SEC_SYMS 3
13 #define SEC_SYMSTR 4
14 #define SEC_DAT 5
15 #define SEC_DATRELA 6
16 #define SEC_BSS 7
17 #define NSECS 8
19 static Elf64_Ehdr ehdr;
20 static Elf64_Shdr shdr[NSECS];
21 static Elf64_Sym syms[MAXSYMS];
22 static int nsyms = 1;
23 static char symstr[MAXSYMS * 8];
24 static int nsymstr = 1;
26 static Elf64_Rela datrela[MAXRELA];
27 static int ndatrela;
28 static Elf64_Rela rela[MAXRELA];
29 static int nrela;
31 static int symstr_add(char *name)
33 int len = strlen(name) + 1;
34 strcpy(symstr + nsymstr, name);
35 nsymstr += len;
36 return nsymstr - len;
39 static int sym_find(char *name)
41 int i;
42 for (i = 0; i < nsyms; i++)
43 if (!strcmp(name, symstr + syms[i].st_name))
44 return i;
45 return -1;
48 static Elf64_Sym *put_sym(char *name)
50 int found = sym_find(name);
51 Elf64_Sym *sym = found != -1 ? &syms[found] : &syms[nsyms++];
52 if (found >= 0)
53 return sym;
54 sym->st_name = symstr_add(name);
55 sym->st_shndx = SHN_UNDEF;
56 sym->st_info = ELF64_ST_INFO(STB_GLOBAL, STT_FUNC);
57 return sym;
60 #define SYMLOCAL(i) (ELF64_ST_BIND(syms[i].st_info) == STB_LOCAL)
62 static void mvrela(int *mv, Elf64_Rela *rela, int nrela)
64 int i;
65 for (i = 0; i < nrela; i++) {
66 int sym = ELF64_R_SYM(rela[i].r_info);
67 int type = ELF64_R_TYPE(rela[i].r_info);
68 rela[i].r_info = ELF64_R_INFO(mv[sym], type);
72 static int syms_sort(void)
74 int mv[MAXSYMS];
75 int i, j;
76 int glob_beg = 1;
77 for (i = 0; i < nsyms; i++)
78 mv[i] = i;
79 i = 1;
80 j = nsyms - 1;
81 while (1) {
82 Elf64_Sym t;
83 while (i < j && SYMLOCAL(i))
84 i++;
85 while (j >= i && !SYMLOCAL(j))
86 j--;
87 if (i >= j)
88 break;
89 t = syms[j];
90 syms[j] = syms[i];
91 syms[i] = t;
92 mv[i] = j;
93 mv[j] = i;
95 glob_beg = j + 1;
96 mvrela(mv, rela, nrela);
97 mvrela(mv, datrela, ndatrela);
98 return glob_beg;
101 void out_init(int flags)
105 void out_sym(char *name, int flags, int off, int len)
107 Elf64_Sym *sym = put_sym(name);
108 int type = (flags & OUT_CS) ? STT_FUNC : STT_OBJECT;
109 int bind = (flags & OUT_GLOB) ? STB_GLOBAL : STB_LOCAL;
110 if (flags & OUT_CS)
111 sym->st_shndx = SEC_TEXT;
112 if (flags & OUT_DS)
113 sym->st_shndx = SEC_DAT;
114 if (flags & OUT_BSS)
115 sym->st_shndx = SEC_BSS;
116 sym->st_info = ELF64_ST_INFO(bind, type);
117 sym->st_value = off;
118 sym->st_size = len;
121 static int rel_type(int flags)
123 return flags & OUT_REL ? R_X86_64_PC32 : R_X86_64_64;
126 static void out_csrel(int idx, int off, int flags)
128 Elf64_Rela *r = &rela[nrela++];
129 r->r_offset = off;
130 r->r_info = ELF64_R_INFO(idx, rel_type(flags));
133 static void out_dsrel(int idx, int off, int flags)
135 Elf64_Rela *r = &datrela[ndatrela++];
136 r->r_offset = off;
137 r->r_info = ELF64_R_INFO(idx, rel_type(flags));
140 void out_rel(char *name, int flags, int off)
142 Elf64_Sym *sym = put_sym(name);
143 int idx = sym - syms;
144 if (flags & OUT_DS)
145 out_dsrel(idx, off, flags);
146 else
147 out_csrel(idx, off, flags);
150 static int bss_len(void)
152 int len = 0;
153 int i;
154 for (i = 0; i < nsyms; i++) {
155 int end = syms[i].st_value + syms[i].st_size;
156 if (syms[i].st_shndx == SEC_BSS)
157 if (len < end)
158 len = end;
160 return len;
163 void out_write(int fd, char *cs, int cslen, char *ds, int dslen)
165 Elf64_Shdr *text_shdr = &shdr[SEC_TEXT];
166 Elf64_Shdr *rela_shdr = &shdr[SEC_RELA];
167 Elf64_Shdr *symstr_shdr = &shdr[SEC_SYMSTR];
168 Elf64_Shdr *syms_shdr = &shdr[SEC_SYMS];
169 Elf64_Shdr *dat_shdr = &shdr[SEC_DAT];
170 Elf64_Shdr *datrela_shdr = &shdr[SEC_DATRELA];
171 Elf64_Shdr *bss_shdr = &shdr[SEC_BSS];
172 unsigned long offset = sizeof(ehdr);
174 /* workaround for the idiotic gnuld; use neatld instead! */
175 text_shdr->sh_name = symstr_add(".cs");
176 rela_shdr->sh_name = symstr_add(".rel.cs");
177 dat_shdr->sh_name = symstr_add(".ds");
178 datrela_shdr->sh_name = symstr_add(".rel.ds");
180 ehdr.e_ident[0] = 0x7f;
181 ehdr.e_ident[1] = 'E';
182 ehdr.e_ident[2] = 'L';
183 ehdr.e_ident[3] = 'F';
184 ehdr.e_ident[4] = ELFCLASS64;
185 ehdr.e_ident[5] = ELFDATA2LSB;
186 ehdr.e_ident[6] = EV_CURRENT;
187 ehdr.e_type = ET_REL;
188 ehdr.e_machine = EM_X86_64;
189 ehdr.e_version = EV_CURRENT;
190 ehdr.e_ehsize = sizeof(ehdr);
191 ehdr.e_shentsize = sizeof(shdr[0]);
192 ehdr.e_shoff = offset;
193 ehdr.e_shnum = NSECS;
194 ehdr.e_shstrndx = SEC_SYMSTR;
195 offset += sizeof(shdr[0]) * NSECS;
197 text_shdr->sh_type = SHT_PROGBITS;
198 text_shdr->sh_flags = SHF_EXECINSTR | SHF_ALLOC;
199 text_shdr->sh_offset = offset;
200 text_shdr->sh_size = cslen;
201 text_shdr->sh_entsize = 1;
202 text_shdr->sh_addralign = OUT_ALIGNMENT;
203 offset += text_shdr->sh_size;
205 rela_shdr->sh_type = SHT_RELA;
206 rela_shdr->sh_link = SEC_SYMS;
207 rela_shdr->sh_info = SEC_TEXT;
208 rela_shdr->sh_offset = offset;
209 rela_shdr->sh_size = nrela * sizeof(rela[0]);
210 rela_shdr->sh_entsize = sizeof(rela[0]);
211 offset += rela_shdr->sh_size;
213 syms_shdr->sh_type = SHT_SYMTAB;
214 syms_shdr->sh_offset = offset;
215 syms_shdr->sh_size = nsyms * sizeof(syms[0]);
216 syms_shdr->sh_entsize = sizeof(syms[0]);
217 syms_shdr->sh_link = SEC_SYMSTR;
218 syms_shdr->sh_info = syms_sort();
219 offset += syms_shdr->sh_size;
221 dat_shdr->sh_type = SHT_PROGBITS;
222 dat_shdr->sh_flags = SHF_ALLOC | SHF_WRITE;
223 dat_shdr->sh_offset = offset;
224 dat_shdr->sh_size = dslen;
225 dat_shdr->sh_entsize = 1;
226 dat_shdr->sh_addralign = OUT_ALIGNMENT;
227 offset += dat_shdr->sh_size;
229 datrela_shdr->sh_type = SHT_RELA;
230 datrela_shdr->sh_offset = offset;
231 datrela_shdr->sh_size = ndatrela * sizeof(datrela[0]);
232 datrela_shdr->sh_entsize = sizeof(datrela[0]);
233 datrela_shdr->sh_link = SEC_SYMS;
234 datrela_shdr->sh_info = SEC_DAT;
235 offset += datrela_shdr->sh_size;
237 bss_shdr->sh_type = SHT_NOBITS;
238 bss_shdr->sh_flags = SHF_ALLOC | SHF_WRITE;
239 bss_shdr->sh_offset = offset;
240 bss_shdr->sh_size = bss_len();
241 bss_shdr->sh_entsize = 1;
242 bss_shdr->sh_addralign = OUT_ALIGNMENT;
244 symstr_shdr->sh_type = SHT_STRTAB;
245 symstr_shdr->sh_offset = offset;
246 symstr_shdr->sh_size = nsymstr;
247 symstr_shdr->sh_entsize = 1;
248 offset += symstr_shdr->sh_size;
250 write(fd, &ehdr, sizeof(ehdr));
251 write(fd, shdr, NSECS * sizeof(shdr[0]));
252 write(fd, cs, cslen);
253 write(fd, rela, nrela * sizeof(rela[0]));
254 write(fd, syms, nsyms * sizeof(syms[0]));
255 write(fd, ds, dslen);
256 write(fd, datrela, ndatrela * sizeof(datrela[0]));
257 write(fd, symstr, nsymstr);