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