Fix indentation.
[dragonfly.git] / sys / kern / link_aout.c
1 /*-
2  * Copyright (c) 1997 Doug Rabson
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD: src/sys/kern/link_aout.c,v 1.26 1999/12/24 15:33:36 bde Exp $
27  * $DragonFly: src/sys/kern/link_aout.c,v 1.24 2008/02/06 22:37:46 nth Exp $
28  */
29
30 #define FREEBSD_AOUT    1
31
32 #include <sys/param.h>
33 #include <sys/kernel.h>
34 #include <sys/systm.h>
35 #include <sys/types.h>
36 #include <sys/malloc.h>
37 #include <sys/proc.h>
38 #include <sys/nlookup.h>
39 #include <sys/fcntl.h>
40 #include <sys/vnode.h>
41 #include <sys/linker.h>
42
43 #include <vm/vm_zone.h>
44
45 #ifndef __ELF__
46 #include <vm/vm.h>
47 #include <vm/pmap.h>
48 #include <machine/vmparam.h>
49 #endif
50
51 #include <machine/exec.h>
52 #include <sys/imgact_aout.h>
53 #include <machine/reloc.h>
54 #define _AOUT_INCLUDE_
55 #include <sys/nlist_aout.h>
56 #include <sys/link_aout.h>
57
58 static int              link_aout_load_module(const char*, linker_file_t*);
59
60 static int              link_aout_load_file(const char*, linker_file_t*);
61
62 static int              link_aout_lookup_symbol(linker_file_t, const char*,
63                                                 c_linker_sym_t*);
64 static int              link_aout_symbol_values(linker_file_t file, c_linker_sym_t sym,
65                                                 linker_symval_t* symval);
66 static int              link_aout_search_symbol(linker_file_t lf, caddr_t value,
67                                                 c_linker_sym_t* sym, long* diffp);
68 static void             link_aout_unload_file(linker_file_t);
69 static void             link_aout_unload_module(linker_file_t);
70 static int              link_aout_lookup_set(linker_file_t lf, const char *name,
71                                 void ***startp, void ***stopp, int *countp);
72
73 static struct linker_class_ops link_aout_class_ops = {
74     link_aout_load_module,
75 };
76
77 static struct linker_file_ops link_aout_file_ops = {
78     link_aout_lookup_symbol,
79     link_aout_symbol_values,
80     link_aout_search_symbol,
81     link_aout_unload_file,
82     link_aout_lookup_set
83 };
84 static struct linker_file_ops link_aout_module_ops = {
85     link_aout_lookup_symbol,
86     link_aout_symbol_values,
87     link_aout_search_symbol,
88     link_aout_unload_module,
89     link_aout_lookup_set
90 };
91
92 typedef struct aout_file {
93     char*               address;        /* Load address */
94     struct _dynamic*    dynamic;        /* Symbol table etc. */
95 } *aout_file_t;
96
97 static int              load_dependancies(linker_file_t lf);
98 static int              relocate_file(linker_file_t lf);
99
100 /*
101  * The kernel symbol table starts here.
102  */
103 extern struct _dynamic _DYNAMIC;
104
105 static void
106 link_aout_init(void* arg)
107 {
108 #ifndef __ELF__
109     struct _dynamic* dp = &_DYNAMIC;
110 #endif
111
112     linker_add_class("a.out", NULL, &link_aout_class_ops);
113
114 #ifndef __ELF__
115     if (dp) {
116         aout_file_t af;
117
118         af = kmalloc(sizeof(struct aout_file), M_LINKER, M_NOWAIT | M_ZERO);
119         if (af == NULL)
120             panic("link_aout_init: Can't create linker structures for kernel");
121
122         af->address = 0;
123         af->dynamic = dp;
124         linker_kernel_file =
125             linker_make_file(kernelname, af, &link_aout_file_ops);
126         if (linker_kernel_file == NULL)
127             panic("link_aout_init: Can't create linker structures for kernel");
128         linker_kernel_file->address = (caddr_t) KERNBASE;
129         linker_kernel_file->size = -(long)linker_kernel_file->address;
130         linker_current_file = linker_kernel_file;
131         linker_kernel_file->flags |= LINKER_FILE_LINKED;
132     }
133 #endif
134 }
135
136 SYSINIT(link_aout, SI_BOOT2_KLD, SI_ORDER_THIRD, link_aout_init, 0);
137
138 static int
139 link_aout_load_module(const char* filename, linker_file_t* result)
140 {
141     caddr_t             modptr, baseptr;
142     char                *type;
143     struct exec         *ehdr;
144     aout_file_t         af;
145     linker_file_t       lf;
146     int                 error;
147     
148     /* Look to see if we have the module preloaded. */
149     if ((modptr = preload_search_by_name(filename)) == NULL)
150         return(link_aout_load_file(filename, result));
151
152     /* It's preloaded, check we can handle it and collect information. */
153     if (((type = (char *)preload_search_info(modptr, MODINFO_TYPE)) == NULL) ||
154         strcmp(type, "a.out module") ||
155         ((baseptr = preload_search_info(modptr, MODINFO_ADDR)) == NULL) ||
156         ((ehdr = (struct exec *)preload_search_info(modptr, MODINFO_METADATA | MODINFOMD_AOUTEXEC)) == NULL))
157         return(0);                      /* we can't handle this */
158
159     /* Looks like we can handle this one */
160     af = kmalloc(sizeof(struct aout_file), M_LINKER, M_WAITOK | M_ZERO);
161     af->address = baseptr;
162
163     /* Assume _DYNAMIC is the first data item. */
164     af->dynamic = (struct _dynamic*)(af->address + ehdr->a_text);
165     if (af->dynamic->d_version != LD_VERSION_BSD) {
166         kfree(af, M_LINKER);
167         return(0);                      /* we can't handle this */
168     }
169     af->dynamic->d_un.d_sdt = (struct section_dispatch_table *)
170         ((char *)af->dynamic->d_un.d_sdt + (vm_offset_t)af->address);
171
172     /* Register with kld */
173     lf = linker_make_file(filename, af, &link_aout_module_ops);
174     if (lf == NULL) {
175         kfree(af, M_LINKER);
176         return(ENOMEM);
177     }
178     lf->address = af->address;
179     lf->size = ehdr->a_text + ehdr->a_data + ehdr->a_bss;
180
181     /* Try to load dependancies */
182     if (((error = load_dependancies(lf)) != 0) ||
183         ((error = relocate_file(lf)) != 0)) {
184         linker_file_unload(lf);
185         return(error);
186     }
187     lf->flags |= LINKER_FILE_LINKED;
188     *result = lf;
189     return(0);
190 }
191
192 static int
193 link_aout_load_file(const char* filename, linker_file_t* result)
194 {
195     struct nlookupdata nd;
196     struct thread *td = curthread;
197     struct proc *p = td->td_proc;
198     struct vnode *vp;
199     int error = 0;
200     int resid;
201     struct exec header;
202     aout_file_t af;
203     linker_file_t lf;
204     char *pathname;
205
206     KKASSERT(p != NULL);
207
208     if (p->p_ucred == NULL) {
209         kprintf("link_aout_load_file: cannot load '%s' from filesystem"
210                 " this early\n", filename);
211         return ENOENT;
212     }
213
214     pathname = linker_search_path(filename);
215     if (pathname == NULL)
216         return ENOENT;
217     error = nlookup_init(&nd, pathname, UIO_SYSSPACE, NLC_FOLLOW|NLC_LOCKVP);
218     if (error == 0)
219         error = vn_open(&nd, NULL, FREAD, 0);
220     kfree(pathname, M_LINKER);
221     if (error) {
222         nlookup_done(&nd);
223         return error;
224     }
225     vp = nd.nl_open_vp;
226     nd.nl_open_vp = NULL;
227     nlookup_done(&nd);
228
229     /*
230      * Read the a.out header from the file.
231      */
232     error = vn_rdwr(UIO_READ, vp, (void*) &header, sizeof header, 0,
233                     UIO_SYSSPACE, IO_NODELOCKED, p->p_ucred, &resid);
234     if (error)
235         goto out;
236
237     if (N_BADMAG(header) || !(N_GETFLAG(header) & EX_DYNAMIC))
238         goto out;
239
240     /*
241      * We have an a.out file, so make some space to read it in.
242      */
243     af = kmalloc(sizeof(struct aout_file), M_LINKER, M_WAITOK | M_ZERO);
244     af->address = kmalloc(header.a_text + header.a_data + header.a_bss,
245                          M_LINKER, M_WAITOK);
246     
247     /*
248      * Read the text and data sections and zero the bss.
249      */
250     error = vn_rdwr(UIO_READ, vp, (void*) af->address,
251                     header.a_text + header.a_data, 0,
252                     UIO_SYSSPACE, IO_NODELOCKED, p->p_ucred, &resid);
253     if (error)
254         goto out;
255     bzero(af->address + header.a_text + header.a_data, header.a_bss);
256
257     /*
258      * Assume _DYNAMIC is the first data item.
259      */
260     af->dynamic = (struct _dynamic*) (af->address + header.a_text);
261     if (af->dynamic->d_version != LD_VERSION_BSD) {
262         kfree(af->address, M_LINKER);
263         kfree(af, M_LINKER);
264         goto out;
265     }
266     af->dynamic->d_un.d_sdt = (struct section_dispatch_table *)
267         ((char *)af->dynamic->d_un.d_sdt + (vm_offset_t)af->address);
268
269     lf = linker_make_file(filename, af, &link_aout_file_ops);
270     if (lf == NULL) {
271         kfree(af->address, M_LINKER);
272         kfree(af, M_LINKER);
273         error = ENOMEM;
274         goto out;
275     }
276     lf->address = af->address;
277     lf->size = header.a_text + header.a_data + header.a_bss;
278
279     if ((error = load_dependancies(lf)) != 0
280         || (error = relocate_file(lf)) != 0) {
281         linker_file_unload(lf);
282         goto out;
283     }
284
285     lf->flags |= LINKER_FILE_LINKED;
286     *result = lf;
287
288 out:
289     vn_unlock(vp);
290     vn_close(vp, FREAD);
291
292     return error;
293 }
294
295 static void
296 link_aout_unload_file(linker_file_t file)
297 {
298     aout_file_t af = file->priv;
299
300     if (af) {
301         if (af->address)
302             kfree(af->address, M_LINKER);
303         kfree(af, M_LINKER);
304     }
305 }
306
307 static void
308 link_aout_unload_module(linker_file_t file)
309 {
310     aout_file_t af = file->priv;
311
312     if (af)
313         kfree(af, M_LINKER);
314     if (file->filename)
315         preload_delete_name(file->filename);
316 }
317
318 #define AOUT_RELOC(af, type, off) (type*) ((af)->address + (off))
319
320 static int
321 load_dependancies(linker_file_t lf)
322 {
323     aout_file_t af = lf->priv;
324     linker_file_t lfdep;
325     long off;
326     struct sod* sodp;
327     char* name;
328     char* filename = 0;
329     int error = 0;
330
331     /*
332      * All files are dependant on /kernel.
333      */
334     if (linker_kernel_file) {
335         linker_kernel_file->refs++;
336         linker_file_add_dependancy(lf, linker_kernel_file);
337     }
338
339     off = LD_NEED(af->dynamic);
340
341     /*
342      * Load the dependancies.
343      */
344     while (off != 0) {
345         sodp = AOUT_RELOC(af, struct sod, off);
346         name = AOUT_RELOC(af, char, sodp->sod_name);
347
348         error = linker_load_file(name, &lfdep);
349         if (error)
350             goto out;
351         linker_file_add_dependancy(lf, lfdep);
352         off = sodp->sod_next;
353     }
354
355 out:
356     if (filename)
357         kfree(filename, M_TEMP);
358     return error;
359 }
360
361 /*
362  * XXX i386 dependant.
363  */
364 static long
365 read_relocation(struct relocation_info* r, char* addr)
366 {
367     int length = r->r_length;
368     if (length == 0)
369         return *(u_char*) addr;
370     else if (length == 1)
371         return *(u_short*) addr;
372     else if (length == 2)
373         return *(u_int*) addr;
374     else
375         kprintf("link_aout: unsupported relocation size %d\n", r->r_length);
376     return 0;
377 }
378
379 static void
380 write_relocation(struct relocation_info* r, char* addr, long value)
381 {
382     int length = r->r_length;
383     if (length == 0)
384         *(u_char*) addr = value;
385     else if (length == 1)
386         *(u_short*) addr = value;
387     else if (length == 2)
388         *(u_int*) addr = value;
389     else
390         kprintf("link_aout: unsupported relocation size %d\n", r->r_length);
391 }
392
393 static int
394 relocate_file(linker_file_t lf)
395 {
396     aout_file_t af = lf->priv;
397     struct relocation_info* rel;
398     struct relocation_info* erel;
399     struct relocation_info* r;
400     struct nzlist* symbolbase;
401     char* stringbase;
402     struct nzlist* np;
403     char* sym;
404     long relocation;
405
406     rel = AOUT_RELOC(af, struct relocation_info, LD_REL(af->dynamic));
407     erel = AOUT_RELOC(af, struct relocation_info,
408                       LD_REL(af->dynamic) + LD_RELSZ(af->dynamic));
409     symbolbase = AOUT_RELOC(af, struct nzlist, LD_SYMBOL(af->dynamic));
410     stringbase = AOUT_RELOC(af, char, LD_STRINGS(af->dynamic));
411
412     for (r = rel; r < erel; r++) {
413         char* addr;
414
415         if (r->r_address == 0)
416             break;
417
418         addr = AOUT_RELOC(af, char, r->r_address);
419         if (r->r_extern) {
420             np = &symbolbase[r->r_symbolnum];
421             sym = &stringbase[np->nz_strx];
422
423             if (sym[0] != '_') {
424                 kprintf("link_aout: bad symbol name %s\n", sym);
425                 kprintf("link_aout: symbol %s not found\n", sym);
426                 return ENOENT;
427             } else {
428                 if (linker_file_lookup_symbol(lf, sym + 1,
429                     (np->nz_type != (N_SETV+N_EXT)), (caddr_t *)&relocation)) {
430                         kprintf("link_aout: symbol %s not found\n", sym);
431                         return ENOENT;
432                 }
433             }
434             
435             relocation += read_relocation(r, addr);
436
437             if (r->r_jmptable) {
438                 kprintf("link_aout: can't cope with jump table relocations\n");
439                 continue;
440             }
441
442             if (r->r_pcrel)
443                 relocation -= (intptr_t) af->address;
444
445             if (r->r_copy) {
446                 kprintf("link_aout: can't cope with copy relocations\n");
447                 continue;
448             }
449             
450             write_relocation(r, addr, relocation);
451         } else {
452             write_relocation(r, addr,
453                              (intptr_t)(read_relocation(r, addr) + af->address));
454         }
455         
456     }
457
458     return 0;
459 }
460
461 static long
462 symbol_hash_value(aout_file_t af, const char* name)
463 {
464     long hashval;
465     const char* p;
466
467     hashval = '_';              /* fake a starting '_' for C symbols */
468     for (p = name; *p; p++)
469         hashval = (hashval << 1) + *p;
470
471     return (hashval & 0x7fffffff) % LD_BUCKETS(af->dynamic);
472 }
473
474 int
475 link_aout_lookup_symbol(linker_file_t file, const char* name,
476                         c_linker_sym_t* sym)
477 {
478     aout_file_t af = file->priv;
479     long hashval;
480     struct rrs_hash* hashbase;
481     struct nzlist* symbolbase;
482     char* stringbase;
483     struct rrs_hash* hp;
484     struct nzlist* np;
485     char* cp;
486
487     if (LD_BUCKETS(af->dynamic) == 0)
488         return 0;
489
490     hashbase = AOUT_RELOC(af, struct rrs_hash, LD_HASH(af->dynamic));
491     symbolbase = AOUT_RELOC(af, struct nzlist, LD_SYMBOL(af->dynamic));
492     stringbase = AOUT_RELOC(af, char, LD_STRINGS(af->dynamic));
493
494 restart:
495     hashval = symbol_hash_value(af, name);
496     hp = &hashbase[hashval];
497     if (hp->rh_symbolnum == -1)
498         return ENOENT;
499
500     while (hp) {
501         np = (struct nzlist *) &symbolbase[hp->rh_symbolnum];
502         cp = stringbase + np->nz_strx;
503         /*
504          * Note: we fake the leading '_' for C symbols.
505          */
506         if (cp[0] == '_' && !strcmp(cp + 1, name))
507             break;
508
509         if (hp->rh_next == 0)
510             hp = NULL;
511         else
512             hp = &hashbase[hp->rh_next];
513     }
514
515     if (hp == NULL)
516         /*
517          * Not found.
518          */
519         return ENOENT;
520
521     /*
522      * Check for an aliased symbol, whatever that is.
523      */
524     if (np->nz_type == N_INDR+N_EXT) {
525         name = stringbase + (++np)->nz_strx + 1; /* +1 for '_' */
526         goto restart;
527     }
528
529     /*
530      * Check this is an actual definition of the symbol.
531      */
532     if (np->nz_value == 0)
533         return ENOENT;
534
535     if (np->nz_type == N_UNDF+N_EXT && np->nz_value != 0) {
536         if (np->nz_other == AUX_FUNC)
537             /* weak function */
538             return ENOENT;
539     }
540
541     *sym = (linker_sym_t) np;
542
543     return 0;
544 }
545
546
547 static int
548 link_aout_symbol_values(linker_file_t file, c_linker_sym_t sym,
549                         linker_symval_t* symval)
550 {
551     aout_file_t af = file->priv;
552     const struct nzlist* np = (const struct nzlist*) sym;
553     char* stringbase;
554     long numsym = LD_STABSZ(af->dynamic) / sizeof(struct nzlist);
555     struct nzlist *symbase;
556
557     /* Is it one of ours?  It could be another module... */
558     symbase = AOUT_RELOC(af, struct nzlist, LD_SYMBOL(af->dynamic));
559     if (np < symbase)
560         return ENOENT;
561     if ((np - symbase) > numsym)
562         return ENOENT;
563
564     stringbase = AOUT_RELOC(af, char, LD_STRINGS(af->dynamic));
565
566     symval->name = stringbase + np->nz_strx + 1; /* +1 for '_' */
567     if (np->nz_type == N_UNDF+N_EXT && np->nz_value != 0) {
568         symval->value = 0;
569         symval->size = np->nz_value;
570     } else {
571         symval->value = AOUT_RELOC(af, char, np->nz_value);
572         symval->size = np->nz_size;
573     }
574     return 0;
575 }
576
577 static int
578 link_aout_search_symbol(linker_file_t lf, caddr_t value,
579                         c_linker_sym_t* sym, long* diffp)
580 {
581         aout_file_t af = lf->priv;
582         u_long off = (uintptr_t) (void *) value;
583         u_long diff = off;
584         u_long sp_nz_value;
585         struct nzlist* sp;
586         struct nzlist* ep;
587         struct nzlist* best = 0;
588
589         for (sp = AOUT_RELOC(af, struct nzlist, LD_SYMBOL(af->dynamic)),
590                  ep = (struct nzlist *) ((caddr_t) sp + LD_STABSZ(af->dynamic));
591              sp < ep; sp++) {
592                 if (sp->nz_name == 0)
593                         continue;
594                 sp_nz_value = sp->nz_value + (uintptr_t) (void *) af->address;
595                 if (off >= sp_nz_value) {
596                         if (off - sp_nz_value < diff) {
597                                 diff = off - sp_nz_value;
598                                 best = sp;
599                                 if (diff == 0)
600                                         break;
601                         } else if (off - sp_nz_value == diff) {
602                                 best = sp;
603                         }
604                 }
605         }
606         if (best == 0)
607                 *diffp = off;
608         else
609                 *diffp = diff;
610         *sym = (linker_sym_t) best;
611
612         return 0;
613 }
614
615 /*
616  * Look up a linker set on an a.out + gnu LD system.
617  */
618
619 struct generic_linker_set {
620         int     ls_length;
621         void    *ls_items[1];
622 };
623
624 static int
625 link_aout_lookup_set(linker_file_t lf, const char *name,
626                     void ***startp, void ***stopp, int *countp)
627 {
628         c_linker_sym_t sym;
629         linker_symval_t symval;
630         void **start, **stop;
631         int error, count;
632         struct generic_linker_set *setp;
633
634         error = link_aout_lookup_symbol(lf, name, &sym);
635         if (error)
636                return error;
637         link_aout_symbol_values(lf, sym, &symval);
638         if (symval.value == 0)
639                return ESRCH;
640         setp = (struct generic_linker_set *)symval.value;
641         count = setp->ls_length;
642         start = &setp->ls_items[0];
643         stop = &setp->ls_items[count];
644         if (startp)
645                *startp = start;
646         if (stopp)
647                *stopp = stop;
648         if (countp)
649                *countp = count;
650         return 0;
651 }