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