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