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