gen: simplify branch instructions by removing o_filljmp()
[neatcc.git] / out.c
blob91f62538f451d6272ae34e872d9749412888d2b7
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 Elf32_Ehdr ehdr;
20 static Elf32_Shdr shdr[NSECS];
21 static Elf32_Sym syms[MAXSYMS];
22 static int nsyms = 1;
23 static char symstr[MAXSYMS * 8];
24 static int nsymstr = 1;
26 static Elf32_Rel datrela[MAXRELA];
27 static int ndatrela;
28 static Elf32_Rel 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 Elf32_Sym *put_sym(char *name)
50 int found = sym_find(name);
51 Elf32_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 = ELF32_ST_INFO(STB_GLOBAL, STT_FUNC);
57 return sym;
60 #define SYMLOCAL(i) (ELF32_ST_BIND(syms[i].st_info) == STB_LOCAL)
62 static void mvrela(int *mv, Elf32_Rel *rela, int nrela)
64 int i;
65 for (i = 0; i < nrela; i++) {
66 int sym = ELF32_R_SYM(rela[i].r_info);
67 int type = ELF32_R_TYPE(rela[i].r_info);
68 rela[i].r_info = ELF32_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 Elf32_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 Elf32_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 = ELF32_ST_INFO(bind, type);
117 sym->st_value = off;
118 sym->st_size = len;
121 static int rel_type(int flags)
123 #ifdef NEATCC_ARM
124 if (flags & OUT_REL24)
125 return R_ARM_PC24;
126 return flags & OUT_REL ? R_ARM_REL32 : R_ARM_ABS32;
127 #else
128 return flags & OUT_REL ? R_386_PC32 : R_386_32;
129 #endif
132 static void out_csrel(int idx, int off, int flags)
134 Elf32_Rel *r = &rela[nrela++];
135 r->r_offset = off;
136 r->r_info = ELF32_R_INFO(idx, rel_type(flags));
139 static void out_dsrel(int idx, int off, int flags)
141 Elf32_Rel *r = &datrela[ndatrela++];
142 r->r_offset = off;
143 r->r_info = ELF32_R_INFO(idx, rel_type(flags));
146 void out_rel(char *name, int flags, int off)
148 Elf32_Sym *sym = put_sym(name);
149 int idx = sym - syms;
150 if (flags & OUT_DS)
151 out_dsrel(idx, off, flags);
152 else
153 out_csrel(idx, off, flags);
156 static int bss_len(void)
158 int len = 0;
159 int i;
160 for (i = 0; i < nsyms; i++) {
161 int end = syms[i].st_value + syms[i].st_size;
162 if (syms[i].st_shndx == SEC_BSS)
163 if (len < end)
164 len = end;
166 return len;
169 void out_write(int fd, char *cs, int cslen, char *ds, int dslen)
171 Elf32_Shdr *text_shdr = &shdr[SEC_TEXT];
172 Elf32_Shdr *rela_shdr = &shdr[SEC_RELA];
173 Elf32_Shdr *symstr_shdr = &shdr[SEC_SYMSTR];
174 Elf32_Shdr *syms_shdr = &shdr[SEC_SYMS];
175 Elf32_Shdr *dat_shdr = &shdr[SEC_DAT];
176 Elf32_Shdr *datrela_shdr = &shdr[SEC_DATRELA];
177 Elf32_Shdr *bss_shdr = &shdr[SEC_BSS];
178 unsigned long offset = sizeof(ehdr);
180 /* workaround for the idiotic gnuld; use neatld instead! */
181 text_shdr->sh_name = symstr_add(".cs");
182 rela_shdr->sh_name = symstr_add(".rel.cs");
183 dat_shdr->sh_name = symstr_add(".ds");
184 datrela_shdr->sh_name = symstr_add(".rel.ds");
186 ehdr.e_ident[0] = 0x7f;
187 ehdr.e_ident[1] = 'E';
188 ehdr.e_ident[2] = 'L';
189 ehdr.e_ident[3] = 'F';
190 ehdr.e_ident[4] = ELFCLASS32;
191 ehdr.e_ident[5] = ELFDATA2LSB;
192 ehdr.e_ident[6] = EV_CURRENT;
193 ehdr.e_type = ET_REL;
194 #ifdef NEATCC_ARM
195 ehdr.e_machine = EM_ARM;
196 ehdr.e_flags = EF_ARM_EABI_VER4;
197 #else
198 ehdr.e_machine = EM_386;
199 #endif
200 ehdr.e_version = EV_CURRENT;
201 ehdr.e_ehsize = sizeof(ehdr);
202 ehdr.e_shentsize = sizeof(shdr[0]);
203 ehdr.e_shoff = offset;
204 ehdr.e_shnum = NSECS;
205 ehdr.e_shstrndx = SEC_SYMSTR;
206 offset += sizeof(shdr[0]) * NSECS;
208 text_shdr->sh_type = SHT_PROGBITS;
209 text_shdr->sh_flags = SHF_EXECINSTR | SHF_ALLOC;
210 text_shdr->sh_offset = offset;
211 text_shdr->sh_size = cslen;
212 text_shdr->sh_entsize = 1;
213 text_shdr->sh_addralign = OUT_ALIGNMENT;
214 offset += text_shdr->sh_size;
216 rela_shdr->sh_type = SHT_REL;
217 rela_shdr->sh_link = SEC_SYMS;
218 rela_shdr->sh_info = SEC_TEXT;
219 rela_shdr->sh_offset = offset;
220 rela_shdr->sh_size = nrela * sizeof(rela[0]);
221 rela_shdr->sh_entsize = sizeof(rela[0]);
222 offset += rela_shdr->sh_size;
224 syms_shdr->sh_type = SHT_SYMTAB;
225 syms_shdr->sh_offset = offset;
226 syms_shdr->sh_size = nsyms * sizeof(syms[0]);
227 syms_shdr->sh_entsize = sizeof(syms[0]);
228 syms_shdr->sh_link = SEC_SYMSTR;
229 syms_shdr->sh_info = syms_sort();
230 offset += syms_shdr->sh_size;
232 dat_shdr->sh_type = SHT_PROGBITS;
233 dat_shdr->sh_flags = SHF_ALLOC | SHF_WRITE;
234 dat_shdr->sh_offset = offset;
235 dat_shdr->sh_size = dslen;
236 dat_shdr->sh_entsize = 1;
237 dat_shdr->sh_addralign = OUT_ALIGNMENT;
238 offset += dat_shdr->sh_size;
240 datrela_shdr->sh_type = SHT_REL;
241 datrela_shdr->sh_offset = offset;
242 datrela_shdr->sh_size = ndatrela * sizeof(datrela[0]);
243 datrela_shdr->sh_entsize = sizeof(datrela[0]);
244 datrela_shdr->sh_link = SEC_SYMS;
245 datrela_shdr->sh_info = SEC_DAT;
246 offset += datrela_shdr->sh_size;
248 bss_shdr->sh_type = SHT_NOBITS;
249 bss_shdr->sh_flags = SHF_ALLOC | SHF_WRITE;
250 bss_shdr->sh_offset = offset;
251 bss_shdr->sh_size = bss_len();
252 bss_shdr->sh_entsize = 1;
253 bss_shdr->sh_addralign = OUT_ALIGNMENT;
255 symstr_shdr->sh_type = SHT_STRTAB;
256 symstr_shdr->sh_offset = offset;
257 symstr_shdr->sh_size = nsymstr;
258 symstr_shdr->sh_entsize = 1;
259 offset += symstr_shdr->sh_size;
261 write(fd, &ehdr, sizeof(ehdr));
262 write(fd, shdr, NSECS * sizeof(shdr[0]));
263 write(fd, cs, cslen);
264 write(fd, rela, nrela * sizeof(rela[0]));
265 write(fd, syms, nsyms * sizeof(syms[0]));
266 write(fd, ds, dslen);
267 write(fd, datrela, ndatrela * sizeof(datrela[0]));
268 write(fd, symstr, nsymstr);