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