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