nld: loaded sections should not share a page
[ld.git] / nld.c
blobfad1f8c7020c883caefe54a996bad852d68223de
1 /*
2  * NEATLD ARM/X86(-64) STATIC LINKER
3  *
4  * Copyright (C) 2010-2018 Ali Gholami Rudi
5  *
6  * Permission to use, copy, modify, and/or distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 #include <ctype.h>
19 #include <elf.h>
20 #include <fcntl.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <sys/stat.h>
24 #include <string.h>
25 #include <unistd.h>
27 #define I_CS            0
28 #define I_DS            1
29 #define I_BSS           2
31 static unsigned long sec_vaddr[3] = {0x800000}; /* virtual address of sections */
32 static unsigned long sec_laddr[3] = {0x800000}; /* load address of sections */
33 static int sec_set[3] = {1};                    /* set address for section */
34 static int secalign = 16;                       /* section alignment */
35 static char *entry = "_start";                  /* entry symbol */
36 static int e_machine;                           /* target machine */
37 static int e_flags;                             /* elf ehdr flags */
39 #define MAXSECS         (1 << 10)
40 #define MAXOBJS         (1 << 7)
41 #define MAXSYMS         (1 << 12)
42 #define PAGE_SIZE       (1 << 12)
43 #define PAGE_MASK       (PAGE_SIZE - 1)
44 #define MAXFILES        (1 << 10)
45 #define MAXPHDRS        4
47 #define MAX(a, b)       ((a) > (b) ? (a) : (b))
48 #define ALIGN(x, a)     (((x) + (a) - 1) & ~((a) - 1))
50 /* simplified elf struct and macro names */
51 #ifdef __x86_64__
52 #  define Elf_Phdr      Elf64_Phdr
53 #  define Elf_Ehdr      Elf64_Ehdr
54 #  define Elf_Shdr      Elf64_Shdr
55 #  define Elf_Sym       Elf64_Sym
56 #  define Elf_Rela      Elf64_Rela
57 #  define SHT_REL_      SHT_RELA
58 #  define REL_ADDEND(r) ((r)->r_addend)
59 #  define ELF_ST_INFO   ELF64_ST_INFO
60 #  define ELF_ST_BIND   ELF64_ST_BIND
61 #  define ELF_R_SYM     ELF64_R_SYM
62 #  define ELF_R_TYPE    ELF64_R_TYPE
63 #  define ELF_ST_TYPE   ELF64_ST_TYPE
64 #else
65 #  define Elf_Phdr      Elf32_Phdr
66 #  define Elf_Ehdr      Elf32_Ehdr
67 #  define Elf_Shdr      Elf32_Shdr
68 #  define Elf_Sym       Elf32_Sym
69 #  define Elf_Rela      Elf32_Rel
70 #  define SHT_REL_      SHT_REL
71 #  define REL_ADDEND(r) (0)
72 #  define ELF_ST_INFO   ELF32_ST_INFO
73 #  define ELF_ST_BIND   ELF32_ST_BIND
74 #  define ELF_R_SYM     ELF32_R_SYM
75 #  define ELF_R_TYPE    ELF32_R_TYPE
76 #  define ELF_ST_TYPE   ELF32_ST_TYPE
77 #endif
79 struct obj {
80         char *mem;
81         Elf_Ehdr *ehdr;
82         Elf_Shdr *shdr;
83         Elf_Sym *syms;
84         int nsyms;
85         char *symstr;
86         char *shstr;
89 struct secmap {
90         Elf_Shdr *o_shdr;
91         struct obj *obj;
92         unsigned long vaddr;
93         unsigned long faddr;
96 struct bss_sym {
97         Elf_Sym *sym;
98         int off;
101 struct outelf {
102         Elf_Ehdr ehdr;
103         Elf_Phdr phdr[MAXSECS];
104         int nph;
105         struct secmap secs[MAXSECS];
106         int nsecs;
107         struct obj objs[MAXOBJS];
108         int nobjs;
110         /* code section */
111         unsigned long code_addr;
113         /* bss section */
114         struct bss_sym bss_syms[MAXSYMS];
115         int nbss_syms;
116         unsigned long bss_vaddr;
117         int bss_len;
119         /* symtab section */
120         Elf_Shdr shdr[MAXSECS];
121         int nsh;
122         char symstr[MAXSYMS];
123         Elf_Sym syms[MAXSYMS];
124         int nsyms;
125         int nsymstr;
126         unsigned long shdr_faddr;
127         unsigned long syms_faddr;
128         unsigned long symstr_faddr;
131 static int nosyms = 0;
133 static Elf_Sym *obj_find(struct obj *obj, char *name)
135         int i;
136         for (i = 0; i < obj->nsyms; i++) {
137                 Elf_Sym *sym = &obj->syms[i];
138                 if (ELF_ST_BIND(sym->st_info) == STB_LOCAL ||
139                                 sym->st_shndx == SHN_UNDEF)
140                         continue;
141                 if (!strcmp(name, obj->symstr + sym->st_name))
142                         return sym;
143         }
144         return NULL;
147 static void obj_init(struct obj *obj, char *mem)
149         int i;
150         obj->mem = mem;
151         obj->ehdr = (void *) mem;
152         obj->shdr = (void *) (mem + obj->ehdr->e_shoff);
153         obj->shstr = mem + obj->shdr[obj->ehdr->e_shstrndx].sh_offset;
154         for (i = 0; i < obj->ehdr->e_shnum; i++) {
155                 if (obj->shdr[i].sh_type != SHT_SYMTAB)
156                         continue;
157                 obj->symstr = mem + obj->shdr[obj->shdr[i].sh_link].sh_offset;
158                 obj->syms = (void *) (mem + obj->shdr[i].sh_offset);
159                 obj->nsyms = obj->shdr[i].sh_size / sizeof(*obj->syms);
160         }
163 static void outelf_init(struct outelf *oe)
165         memset(oe, 0, sizeof(*oe));
166         oe->ehdr.e_ident[0] = 0x7f;
167         oe->ehdr.e_ident[1] = 'E';
168         oe->ehdr.e_ident[2] = 'L';
169         oe->ehdr.e_ident[3] = 'F';
170         oe->ehdr.e_ident[4] = sizeof(long) == 8 ? ELFCLASS64 : ELFCLASS32;
171         oe->ehdr.e_ident[5] = ELFDATA2LSB;
172         oe->ehdr.e_ident[6] = EV_CURRENT;
173         oe->ehdr.e_type = ET_EXEC;
174         oe->ehdr.e_version = EV_CURRENT;
175         oe->ehdr.e_shstrndx = SHN_UNDEF;
176         oe->ehdr.e_ehsize = sizeof(oe->ehdr);
177         oe->ehdr.e_phentsize = sizeof(oe->phdr[0]);
178         oe->ehdr.e_shentsize = sizeof(Elf_Shdr);
181 static struct secmap *outelf_mapping(struct outelf *oe, Elf_Shdr *shdr)
183         int i;
184         for (i = 0; i < oe->nsecs; i++)
185                 if (oe->secs[i].o_shdr == shdr)
186                         return &oe->secs[i];
187         return NULL;
190 static int outelf_find(struct outelf *oe, char *name,
191                         struct obj **sym_obj, Elf_Sym **sym_sym)
193         int i;
194         for (i = 0; i < oe->nobjs; i++) {
195                 struct obj *obj = &oe->objs[i];
196                 Elf_Sym *sym;
197                 if ((sym = obj_find(obj, name))) {
198                         *sym_obj = obj;
199                         *sym_sym = sym;
200                         return 0;
201                 }
202         }
203         return 1;
206 static unsigned long bss_addr(struct outelf *oe, Elf_Sym *sym)
208         int i;
209         for (i = 0; i < oe->nbss_syms; i++)
210                 if (oe->bss_syms[i].sym == sym)
211                         return oe->bss_vaddr + oe->bss_syms[i].off;
212         return 0;
215 static void die(char *msg)
217         fprintf(stderr, "%s\n", msg);
218         exit(1);
221 static void die_undef(char *name)
223         fprintf(stderr, "%s undefined\n", name);
224         exit(1);
227 static unsigned long symval(struct outelf *oe, struct obj *obj, Elf_Sym *sym)
229         struct secmap *sec;
230         char *name = obj ? obj->symstr + sym->st_name : NULL;
231         int s_idx, s_off;
232         switch (ELF_ST_TYPE(sym->st_info)) {
233         case STT_SECTION:
234                 if ((sec = outelf_mapping(oe, &obj->shdr[sym->st_shndx])))
235                         return sec->vaddr;
236                 break;
237         case STT_NOTYPE:
238         case STT_OBJECT:
239         case STT_FUNC:
240                 if (name && *name && sym->st_shndx == SHN_UNDEF)
241                         if (outelf_find(oe, name, &obj, &sym))
242                                 die_undef(name);
243                 if (sym->st_shndx == SHN_COMMON)
244                         return bss_addr(oe, sym);
245                 s_idx = sym->st_shndx;
246                 s_off = sym->st_value;
247                 sec = outelf_mapping(oe, &obj->shdr[s_idx]);
248                 if ((sec = outelf_mapping(oe, &obj->shdr[s_idx])))
249                         return sec->vaddr + s_off;
250         }
251         return 0;
254 static unsigned long outelf_addr(struct outelf *oe, char *name)
256         struct obj *obj;
257         Elf_Sym *sym;
258         if (outelf_find(oe, name, &obj, &sym))
259                 die_undef(name);
260         return symval(oe, obj, sym);
263 #define REL_ARM         0x10000
264 #define REL_X64         0x20000
265 #define REL_X86         0x40000
267 static int arch_rel(int r)
269         if (e_machine == EM_ARM)
270                 return REL_ARM | r;
271         if (e_machine == EM_X86_64)
272                 return REL_X64 | r;
273         if (e_machine == EM_386)
274                 return REL_X86 | r;
275         return 0;
278 static void outelf_reloc_sec(struct outelf *oe, int o_idx, int s_idx)
280         struct obj *obj = &oe->objs[o_idx];
281         Elf_Shdr *rel_shdr = &obj->shdr[s_idx];
282         Elf_Rela *rels = (void *) obj->mem + obj->shdr[s_idx].sh_offset;
283         Elf_Shdr *other_shdr = &obj->shdr[rel_shdr->sh_info];
284         void *other = (void *) obj->mem + other_shdr->sh_offset;
285         int nrels = rel_shdr->sh_size / sizeof(*rels);
286         unsigned long addr;
287         int i;
288         for (i = 0; i < nrels; i++) {
289                 Elf_Rela *rel = (void *) &rels[i];
290                 int sym_idx = ELF_R_SYM(rel->r_info);
291                 Elf_Sym *sym = &obj->syms[sym_idx];
292                 unsigned long val = symval(oe, obj, sym) + REL_ADDEND(rel);
293                 unsigned long *dst = other + rel->r_offset;
294                 switch (arch_rel(ELF_R_TYPE(rel->r_info))) {
295                 case REL_ARM | R_ARM_NONE:
296                 case REL_X86 | R_386_NONE:
297                 case REL_X64 | R_X86_64_NONE:
298                         break;
299                 case REL_ARM | R_ARM_ABS16:
300                 case REL_X86 | R_386_16:
301                 case REL_X64 | R_X86_64_16:
302                         *(unsigned short *) dst += val;
303                         break;
304                 case REL_ARM | R_ARM_ABS32:
305                 case REL_X86 | R_386_32:
306                 case REL_X64 | R_X86_64_32:
307                 case REL_X64 | R_X86_64_32S:
308                         *(unsigned int *) dst += val;
309                         break;
310                 case REL_X64 | R_X86_64_64:
311                         *dst += val;
312                         break;
313                 case REL_ARM | R_ARM_REL32:
314                 case REL_ARM | R_ARM_PLT32:
315                 case REL_X86 | R_386_PLT32:
316                 case REL_X86 | R_386_PC32:
317                 case REL_X64 | R_X86_64_PC32:
318                         addr = outelf_mapping(oe, other_shdr)->vaddr +
319                                 rel->r_offset;
320                         *(unsigned int *) dst += val - addr;
321                         break;
322                 case REL_ARM | R_ARM_PC24:
323                         addr = outelf_mapping(oe, other_shdr)->vaddr +
324                                 rel->r_offset;
325                         *dst = (*dst & 0xff000000) |
326                                         ((*dst + ((val - addr) >> 2)) & 0x00ffffff);
327                         break;
328                 default:
329                         die("neatld: unknown relocation type!");
330                 }
331         }
334 static void outelf_reloc(struct outelf *oe)
336         int i, j;
337         for (i = 0; i < oe->nobjs; i++) {
338                 struct obj *obj = &oe->objs[i];
339                 for (j = 0; j < obj->ehdr->e_shnum; j++)
340                         if (obj->shdr[j].sh_type == SHT_REL_)
341                                 outelf_reloc_sec(oe, i, j);
342         }
345 static void alloc_bss(struct outelf *oe, Elf_Sym *sym)
347         int n = oe->nbss_syms++;
348         int off = ALIGN(oe->bss_len, MAX(sym->st_value, 4));
349         oe->bss_syms[n].sym = sym;
350         oe->bss_syms[n].off = off;
351         oe->bss_len = off + sym->st_size;
354 static void outelf_bss(struct outelf *oe)
356         int i, j;
357         for (i = 0; i < oe->nobjs; i++) {
358                 struct obj *obj = &oe->objs[i];
359                 for (j = 0; j < obj->nsyms; j++)
360                         if (obj->syms[j].st_shndx == SHN_COMMON)
361                                 alloc_bss(oe, &obj->syms[j]);
362         }
365 #define SEC_CODE(s)     ((s)->sh_flags & SHF_EXECINSTR)
366 #define SEC_BSS(s)      ((s)->sh_type == SHT_NOBITS)
367 #define SEC_DATA(s)     (!SEC_CODE(s) && !SEC_BSS(s))
369 static char *putstr(char *d, char *s)
371         while (*s)
372                 *d++ = *s++;
373         *d++ = '\0';
374         return d;
377 static void build_symtab(struct outelf *oe)
379         int i, j;
380         char *symstr = oe->symstr;
381         Elf_Sym *syms = oe->syms;
382         Elf_Shdr *sym_shdr = &oe->shdr[1];
383         Elf_Shdr *str_shdr = &oe->shdr[2];
384         int n = 1;
385         char *s = putstr(symstr, "");
386         int faddr = oe->shdr_faddr;
387         oe->nsh = 3;
388         for (i = 0; i < oe->nobjs; i++) {
389                 struct obj *obj = &oe->objs[i];
390                 for (j = 0; j < obj->nsyms; j++) {
391                         Elf_Sym *sym = &obj->syms[j];
392                         int type = ELF_ST_TYPE(sym->st_info);
393                         int bind = ELF_ST_BIND(sym->st_info);
394                         char *name = obj->symstr + sym->st_name;
395                         if (!*name || bind == STB_LOCAL ||
396                                         sym->st_shndx == SHN_UNDEF)
397                                 continue;
398                         syms[n].st_name = s - symstr;
399                         s = putstr(s, name);
400                         syms[n].st_info = ELF_ST_INFO(bind, type);
401                         syms[n].st_value = symval(oe, obj, sym);
402                         syms[n].st_size = sym->st_size;
403                         syms[n].st_shndx = SHN_ABS;
404                         n++;
405                 }
406         }
407         oe->nsymstr = s - symstr;
408         oe->nsyms = n;
410         oe->shdr_faddr = faddr;
411         faddr += oe->nsh * sizeof(oe->shdr[0]);
412         oe->syms_faddr = faddr;
413         faddr += oe->nsyms * sizeof(oe->syms[0]);
414         oe->symstr_faddr = faddr;
415         faddr += oe->nsymstr;
417         oe->ehdr.e_shstrndx = str_shdr - oe->shdr;
418         oe->ehdr.e_shoff = oe->shdr_faddr;
419         oe->ehdr.e_shnum = oe->nsh;
421         str_shdr->sh_name = 0;
422         str_shdr->sh_type = SHT_STRTAB;
423         str_shdr->sh_offset = oe->symstr_faddr;
424         str_shdr->sh_size = oe->nsymstr;
426         sym_shdr->sh_name = 0;
427         sym_shdr->sh_type = SHT_SYMTAB;
428         sym_shdr->sh_entsize = sizeof(oe->syms[0]);
429         sym_shdr->sh_offset = oe->syms_faddr;
430         sym_shdr->sh_size = oe->nsyms * sizeof(oe->syms[0]);
431         sym_shdr->sh_link = str_shdr - oe->shdr;
432         sym_shdr->sh_info = 0;
435 static void outelf_write(struct outelf *oe, int fd)
437         int i;
438         oe->ehdr.e_entry = outelf_addr(oe, entry) -
439                                 sec_vaddr[I_CS] + sec_laddr[I_CS];
440         if (!nosyms)
441                 build_symtab(oe);
442         oe->ehdr.e_phnum = oe->nph;
443         oe->ehdr.e_phoff = sizeof(oe->ehdr);
444         oe->ehdr.e_machine = e_machine;
445         oe->ehdr.e_flags = e_flags;
446         lseek(fd, 0, SEEK_SET);
447         write(fd, &oe->ehdr, sizeof(oe->ehdr));
448         write(fd, &oe->phdr, oe->nph * sizeof(oe->phdr[0]));
449         for (i = 0; i < oe->nsecs; i++) {
450                 struct secmap *sec = &oe->secs[i];
451                 char *buf = sec->obj->mem + sec->o_shdr->sh_offset;
452                 int len = sec->o_shdr->sh_size;
453                 if (SEC_BSS(sec->o_shdr))
454                         continue;
455                 lseek(fd, sec->faddr, SEEK_SET);
456                 write(fd, buf, len);
457         }
458         if (!nosyms) {
459                 lseek(fd, oe->shdr_faddr, SEEK_SET);
460                 write(fd, &oe->shdr, oe->nsh * sizeof(oe->shdr[0]));
461                 lseek(fd, oe->syms_faddr, SEEK_SET);
462                 write(fd, &oe->syms, oe->nsyms * sizeof(oe->syms[0]));
463                 lseek(fd, oe->symstr_faddr, SEEK_SET);
464                 write(fd, &oe->symstr, oe->nsymstr);
465         }
468 static void outelf_add(struct outelf *oe, char *mem)
470         Elf_Ehdr *ehdr = (void *) mem;
471         Elf_Shdr *shdr = (void *) (mem + ehdr->e_shoff);
472         struct obj *obj;
473         int i;
474         if (ehdr->e_type != ET_REL)
475                 return;
476         e_machine = ehdr->e_machine;
477         e_flags = ehdr->e_flags;
478         if (oe->nobjs >= MAXOBJS)
479                 die("neatld: MAXOBJS reached!");
480         obj = &oe->objs[oe->nobjs++];
481         obj_init(obj, mem);
482         for (i = 0; i < ehdr->e_shnum; i++) {
483                 struct secmap *sec;
484                 if (!(shdr[i].sh_flags & 0x7))
485                         continue;
486                 if (oe->nsecs >= MAXSECS)
487                         die("neatld: MAXSECS reached!");
488                 sec = &oe->secs[oe->nsecs++];
489                 sec->o_shdr = &shdr[i];
490                 sec->obj = obj;
491         }
494 static int link_cs(struct outelf *oe, Elf_Phdr *phdr, unsigned long faddr,
495                         unsigned long vaddr, unsigned long laddr, int len)
497         int i;
498         for (i = 0; i < oe->nsecs; i++) {
499                 struct secmap *sec = &oe->secs[i];
500                 int alignment = MAX(sec->o_shdr->sh_addralign, 4);
501                 if (!SEC_CODE(sec->o_shdr))
502                         continue;
503                 len = ALIGN(vaddr + len, alignment) - vaddr;
504                 sec->vaddr = vaddr + len;
505                 sec->faddr = faddr + len;
506                 len += sec->o_shdr->sh_size;
507         }
508         phdr->p_type = PT_LOAD;
509         phdr->p_flags = PF_R | PF_W | PF_X;
510         phdr->p_vaddr = vaddr;
511         phdr->p_paddr = laddr;
512         phdr->p_offset = faddr;
513         phdr->p_filesz = len;
514         phdr->p_memsz = len;
515         phdr->p_align = PAGE_SIZE;
516         return len;
519 static int link_ds(struct outelf *oe, Elf_Phdr *phdr, unsigned long faddr,
520                         unsigned long vaddr, unsigned long laddr)
522         int len = 0;
523         int i;
524         for (i = 0; i < oe->nsecs; i++) {
525                 struct secmap *sec = &oe->secs[i];
526                 if (!SEC_DATA(sec->o_shdr))
527                         continue;
528                 sec->vaddr = vaddr + len;
529                 sec->faddr = faddr + len;
530                 len += sec->o_shdr->sh_size;
531         }
532         len = ALIGN(len, 4);
533         phdr->p_type = PT_LOAD;
534         phdr->p_flags = PF_R | PF_W | PF_X;
535         phdr->p_align = PAGE_SIZE;
536         phdr->p_vaddr = vaddr;
537         phdr->p_paddr = laddr;
538         phdr->p_filesz = len;
539         phdr->p_memsz = len;
540         phdr->p_offset = faddr;
541         return len;
544 static int link_bss(struct outelf *oe, Elf_Phdr *phdr,
545                         unsigned long faddr, unsigned long vaddr, int len)
547         int i;
548         for (i = 0; i < oe->nsecs; i++) {
549                 struct secmap *sec = &oe->secs[i];
550                 int alignment = MAX(sec->o_shdr->sh_addralign, 4);
551                 if (!SEC_BSS(sec->o_shdr))
552                         continue;
553                 len = ALIGN(vaddr + len, alignment) - vaddr;
554                 sec->vaddr = vaddr + len;
555                 sec->faddr = faddr;
556                 len += sec->o_shdr->sh_size;
557         }
558         phdr->p_type = PT_LOAD;
559         phdr->p_flags = PF_R | PF_W;
560         phdr->p_vaddr = vaddr;
561         phdr->p_paddr = vaddr;
562         phdr->p_offset = faddr;
563         phdr->p_filesz = 0;
564         phdr->p_memsz = len;
565         phdr->p_align = PAGE_SIZE;
566         return len;
569 static void outelf_link(struct outelf *oe)
571         unsigned long faddr, vaddr, laddr;
572         int len;
573         len = ALIGN(sizeof(oe->ehdr) + MAXPHDRS * sizeof(oe->phdr[0]), secalign);
574         faddr = len & ~PAGE_MASK;
575         vaddr = sec_vaddr[I_CS];
576         laddr = sec_laddr[I_CS];
577         len = link_cs(oe, &oe->phdr[0], faddr, vaddr, laddr, len & PAGE_MASK);
579         len = ALIGN(faddr + len, secalign) - faddr;
580         faddr += len;
581         vaddr = sec_set[I_DS] ? sec_vaddr[I_DS] | (faddr & PAGE_MASK) :
582                 vaddr + PAGE_SIZE + len;
583         laddr = sec_set[I_DS] ? sec_laddr[I_DS] | (faddr & PAGE_MASK) :
584                 laddr + PAGE_SIZE + len;
585         len = link_ds(oe, &oe->phdr[1], faddr, vaddr, laddr);
587         len = ALIGN(faddr + len, secalign) - faddr;
588         faddr += len;
589         vaddr = sec_set[I_BSS] ? sec_vaddr[I_BSS] | (faddr & PAGE_MASK) :
590                 vaddr + PAGE_SIZE + len;
591         outelf_bss(oe);
592         oe->bss_vaddr = vaddr;
593         len = link_bss(oe, &oe->phdr[2], faddr, vaddr, oe->bss_len);
595         oe->nph = 3;
596         outelf_reloc(oe);
597         oe->shdr_faddr = faddr;
600 struct arhdr {
601         char ar_name[16];
602         char ar_date[12];
603         char ar_uid[6];
604         char ar_gid[6];
605         char ar_mode[8];
606         char ar_size[10];
607         char ar_fmag[2];
610 static int get_be32(unsigned char *s)
612         return s[3] | (s[2] << 8) | (s[1] << 16) | (s[0] << 24);
615 static int sym_undef(struct outelf *oe, char *name)
617         int i, j;
618         int undef = 0;
619         for (i = 0; i < oe->nobjs; i++) {
620                 struct obj *obj = &oe->objs[i];
621                 for (j = 0; j < obj->nsyms; j++) {
622                         Elf_Sym *sym = &obj->syms[j];
623                         if (ELF_ST_BIND(sym->st_info) == STB_LOCAL)
624                                 continue;
625                         if (strcmp(name, obj->symstr + sym->st_name))
626                                 continue;
627                         if (sym->st_shndx != SHN_UNDEF)
628                                 return 0;
629                         undef = 1;
630                 }
631         }
632         return undef;
635 static int outelf_ar_link(struct outelf *oe, char *ar, int base)
637         char *ar_index;
638         char *ar_name;
639         int nsyms = get_be32((void *) ar);
640         int added = 0;
641         int i;
642         ar_index = ar + 4;
643         ar_name = ar_index + nsyms * 4;
644         for (i = 0; i < nsyms; i++) {
645                 int off = get_be32((void *) ar_index + i * 4) +
646                                 sizeof(struct arhdr);
647                 if (sym_undef(oe, ar_name)) {
648                         outelf_add(oe, ar - base + off);
649                         added++;
650                 }
651                 ar_name = strchr(ar_name, '\0') + 1;
652         }
653         return added;
656 static void outelf_archive(struct outelf *oe, char *ar)
658         char *beg = ar;
660         /* skip magic */
661         ar += 8;
662         for(;;) {
663                 struct arhdr *hdr = (void *) ar;
664                 char *name = hdr->ar_name;
665                 int size;
666                 ar += sizeof(*hdr);
667                 hdr->ar_size[sizeof(hdr->ar_size) - 1] = '\0';
668                 size = atoi(hdr->ar_size);
669                 size = (size + 1) & ~1;
670                 if (name[0] == '/' && name[1] == ' ') {
671                         while (outelf_ar_link(oe, ar, ar - beg))
672                                 ;
673                         return;
674                 }
675                 if (name[0] == '/' && name[1] == '/' && name[2] == ' ')
676                         outelf_add(oe, ar);
677                 ar += size;
678         }
681 static long filesize(int fd)
683         struct stat stat;
684         fstat(fd, &stat);
685         return stat.st_size;
688 static char *fileread(char *path)
690         int fd = open(path, O_RDONLY);
691         int size = filesize(fd);
692         char *buf = malloc(size);
693         read(fd, buf, size);
694         close(fd);
695         return buf;
698 static int is_ar(char *path)
700         int len = strlen(path);
701         return len > 2 && path[len - 2] == '.' && path[len - 1] == 'a';
704 #define LIBDIRS         (1 << 5)
705 #define PATHLEN         (1 << 8)
707 static char *libdirs[LIBDIRS] = {"/lib"};
708 static int nlibdirs = 1;
710 static int lib_find(char *path, char *lib)
712         struct stat st;
713         int i;
714         for (i = 0; i < nlibdirs; i++) {
715                 sprintf(path, "%s/lib%s.a", libdirs[i], lib);
716                 if (!stat(path, &st))
717                         return 0;
718         }
719         return 1;
722 static unsigned long hexnum(char *s)
724         unsigned long n = 0;
725         if (s[0] == '0' && s[1] == 'x')
726                 s += 2;
727         for (; isdigit(*s) || isalpha(*s); s++) {
728                 n <<= 4;
729                 n |= isdigit(*s) ? *s - '0' : tolower(*s) - 'a' + 10;
730         }
731         return n;
734 static void set_addr(int sec, char *arg)
736         int idx = I_CS;
737         char *sep = strchr(arg, ':');
738         if (sec == 'd')
739                 idx = I_DS;
740         if (sec == 'b')
741                 idx = I_BSS;
742         sec_vaddr[idx] = hexnum(arg);
743         sec_laddr[idx] = sep ? hexnum(sep + 1) : sec_vaddr[idx];
744         sec_set[idx] = 1;
747 static char *obj_add(struct outelf *oe, char *path)
749         char *buf = fileread(path);
750         if (!buf)
751                 die("neatld: cannot open object!");
752         if (is_ar(path))
753                 outelf_archive(oe, buf);
754         else
755                 outelf_add(oe, buf);
756         return buf;
759 int main(int argc, char **argv)
761         char out[PATHLEN] = "a.out";
762         struct outelf oe;
763         char *mem[MAXFILES];
764         int nmem = 0;
765         int fd;
766         int i = 0;
767         if (argc < 2)
768                 die("neatld: no object given!");
769         outelf_init(&oe);
771         while (++i < argc) {
772                 if (argv[i][0] != '-') {
773                         mem[nmem++] = obj_add(&oe, argv[i]);
774                         continue;
775                 }
776                 if (argv[i][1] == 'l') {
777                         char path[PATHLEN];
778                         if (lib_find(path, argv[i] + 2))
779                                 die("neatld: cannot find library!");
780                         mem[nmem++] = obj_add(&oe, path);
781                         continue;
782                 }
783                 if (argv[i][1] == 'L') {
784                         libdirs[nlibdirs++] = argv[i][2] ? argv[i] + 2 : argv[++i];
785                         continue;
786                 }
787                 if (argv[i][1] == 'o') {
788                         strcpy(out, argv[i][2] ? argv[i] + 2 : argv[++i]);
789                         continue;
790                 }
791                 if (argv[i][1] == 's') {
792                         nosyms = 1;
793                         continue;
794                 }
795                 if (argv[i][1] == 'g')
796                         continue;
797                 if (argv[i][1] == 'm') {
798                         char sec = argv[i][2];
799                         char *arg = argv[i][3] == '=' ? argv[i] + 4 : argv[++i];
800                         set_addr(sec, arg);
801                         continue;
802                 }
803                 if (argv[i][1] == 'p') {
804                         secalign = PAGE_SIZE;
805                         continue;
806                 }
807                 if (argv[i][1] == 'e') {
808                         entry = argv[i][2] ? argv[i] + 2 : argv[++i];
809                         continue;
810                 }
811                 if (argv[i][1] == 'h') {
812                         printf("Usage: neatld [options] objects\n");
813                         return 1;
814                 }
815         }
816         outelf_link(&oe);
817         fd = open(out, O_WRONLY | O_TRUNC | O_CREAT, 0700);
818         if (fd < 0)
819                 die("neatld: failed to create the output!");
820         outelf_write(&oe, fd);
821         close(fd);
822         for (i = 0; i < nmem; i++)
823                 free(mem[i]);
824         return 0;