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