kobj_boot.c revision 580:70dfd36fd02c
1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26#pragma ident	"%Z%%M%	%I%	%E% SMI"
27
28/*
29 * Bootstrap the linker/loader.
30 */
31
32#include <sys/types.h>
33#include <sys/bootconf.h>
34#include <sys/link.h>
35#include <sys/auxv.h>
36#include <sys/kobj.h>
37#include <sys/elf.h>
38#include <sys/bootsvcs.h>
39#include <sys/kobj_impl.h>
40
41#if !defined(__GNUC__)
42
43/*
44 * We don't use the global offset table, but
45 * ld may throw in an UNDEFINED reference in
46 * our symbol table.
47 */
48
49#pragma weak _GLOBAL_OFFSET_TABLE_
50
51#else
52
53/*
54 * We -do- use the global offset table, but only by
55 * accident -- when you tell gcc to emit PIC code,
56 * it -always- generates a reference to the GOT in
57 * a register, even if the compilation unit never
58 * uses it.
59 *
60 * Rumoured to be fixed in a later version of gcc..
61 */
62
63long	_GLOBAL_OFFSET_TABLE_[1];
64
65#endif
66
67#define	MASK(n)		((1<<(n))-1)
68#define	IN_RANGE(v, n)	((-(1<<((n)-1))) <= (v) && (v) < (1<<((n)-1)))
69
70#define	roundup		ALIGN
71
72/*
73 * Boot transfers control here. At this point,
74 * we haven't relocated our own symbols, so the
75 * world (as we know it) is pretty small right now.
76 */
77void
78_kobj_boot(
79	struct boot_syscalls *syscallp,
80	void *dvec,
81	struct bootops *bootops,
82	Boot *ebp)
83{
84	Shdr *section[24];	/* cache */
85	val_t bootaux[BA_NUM];
86	struct bootops *bop;
87	Phdr *phdr;
88	auxv_t *auxv = NULL;
89	Shdr *sh;
90	Half sh_num;
91	uint_t end, edata = 0;
92	int i;
93
94	bop = (dvec) ? *(struct bootops **)bootops : bootops;
95
96	for (i = 0; i < BA_NUM; i++)
97		bootaux[i].ba_val = NULL;
98
99	/*
100	 * Check the bootstrap vector.
101	 */
102	for (; ebp->eb_tag != EB_NULL; ebp++) {
103		switch (ebp->eb_tag) {
104		case EB_AUXV:
105			auxv = (auxv_t *)ebp->eb_un.eb_ptr;
106			break;
107		case EB_DYNAMIC:
108			bootaux[BA_DYNAMIC].ba_ptr = (void *)ebp->eb_un.eb_ptr;
109			break;
110		default:
111			break;
112		}
113	}
114
115	if (auxv == NULL)
116		return;
117
118	/*
119	 * Now the aux vector.
120	 */
121	for (; auxv->a_type != AT_NULL; auxv++) {
122		switch (auxv->a_type) {
123		case AT_PHDR:
124			bootaux[BA_PHDR].ba_ptr = auxv->a_un.a_ptr;
125			break;
126		case AT_PHENT:
127			bootaux[BA_PHENT].ba_val = auxv->a_un.a_val;
128			break;
129		case AT_PHNUM:
130			bootaux[BA_PHNUM].ba_val = auxv->a_un.a_val;
131			break;
132		case AT_PAGESZ:
133			bootaux[BA_PAGESZ].ba_val = auxv->a_un.a_val;
134			break;
135		case AT_SUN_LDELF:
136			bootaux[BA_LDELF].ba_ptr = auxv->a_un.a_ptr;
137			break;
138		case AT_SUN_LDSHDR:
139			bootaux[BA_LDSHDR].ba_ptr = auxv->a_un.a_ptr;
140			break;
141		case AT_SUN_LDNAME:
142			bootaux[BA_LDNAME].ba_ptr = auxv->a_un.a_ptr;
143			break;
144		case AT_SUN_LPAGESZ:
145			bootaux[BA_LPAGESZ].ba_val = auxv->a_un.a_val;
146			break;
147		case AT_SUN_CPU:
148			bootaux[BA_CPU].ba_ptr = auxv->a_un.a_ptr;
149			break;
150		case AT_SUN_MMU:
151			bootaux[BA_MMU].ba_ptr = auxv->a_un.a_ptr;
152			break;
153		case AT_ENTRY:
154			bootaux[BA_ENTRY].ba_ptr = auxv->a_un.a_ptr;
155			break;
156		default:
157			break;
158		}
159	}
160
161	sh = (Shdr *)bootaux[BA_LDSHDR].ba_ptr;
162	sh_num = ((Ehdr *)bootaux[BA_LDELF].ba_ptr)->e_shnum;
163	/*
164	 * Build cache table for section addresses.
165	 */
166	for (i = 0; i < sh_num; i++) {
167		section[i] = sh++;
168	}
169
170	/*
171	 * Find the end of data
172	 * (to allocate bss)
173	 */
174	phdr = (Phdr *)bootaux[BA_PHDR].ba_ptr;
175	for (i = 0; i < bootaux[BA_PHNUM].ba_val; i++) {
176		if (phdr->p_type == PT_LOAD &&
177		    (phdr->p_flags & PF_W) && (phdr->p_flags & PF_X)) {
178			edata = end = phdr->p_vaddr + phdr->p_memsz;
179			break;
180		}
181		phdr = (Phdr *)((ulong_t)phdr + bootaux[BA_PHENT].ba_val);
182	}
183	if (edata == NULL)
184		return;
185
186	/*
187	 * Find the symbol table, and then loop
188	 * through the symbols adjusting their
189	 * values to reflect where the sections
190	 * were loaded.
191	 */
192	for (i = 1; i < sh_num; i++) {
193		Shdr *shp;
194		Sym *sp;
195		uint_t off;
196
197		shp = section[i];
198		if (shp->sh_type != SHT_SYMTAB)
199			continue;
200
201		for (off = 0; off < shp->sh_size; off += shp->sh_entsize) {
202			sp = (Sym *)(shp->sh_addr + off);
203
204			if (sp->st_shndx == SHN_ABS ||
205			    sp->st_shndx == SHN_UNDEF)
206				continue;
207			/*
208			 * Assign the addresses for COMMON
209			 * symbols even though we haven't
210			 * actually allocated bss yet.
211			 */
212			if (sp->st_shndx == SHN_COMMON) {
213				end = ALIGN(end, sp->st_value);
214				sp->st_value = end;
215				/*
216				 * Squirrel it away for later.
217				 */
218				if (bootaux[BA_BSS].ba_val == 0)
219					bootaux[BA_BSS].ba_val = end;
220				end += sp->st_size;
221				continue;
222			} else if (sp->st_shndx > (Half)sh_num) {
223				BSVC_PUTCHAR(syscallp, '>');
224				return;
225			}
226
227			/*
228			 * Symbol's new address.
229			 */
230			sp->st_value += section[sp->st_shndx]->sh_addr;
231		}
232	}
233
234	/*
235	 * Allocate bss for COMMON, if any.
236	 */
237	if (end > edata) {
238		unsigned long va, bva;
239		unsigned long asize;
240		unsigned long align;
241
242		if (bootaux[BA_LPAGESZ].ba_val) {
243			asize = bootaux[BA_LPAGESZ].ba_val;
244			align = bootaux[BA_LPAGESZ].ba_val;
245		} else {
246			asize = bootaux[BA_PAGESZ].ba_val;
247			align = BO_NO_ALIGN;
248		}
249		va = roundup(edata, asize);
250		bva = roundup(end, asize);
251
252		if (bva > va) {
253			bva = (unsigned long)BOP_ALLOC(bop, (caddr_t)va,
254				bva - va, align);
255			if (bva == NULL)
256				return;
257		}
258		/*
259		 * Zero it.
260		 */
261		for (va = edata; va < end; va++)
262			*(char *)va = 0;
263		/*
264		 * Update the size of data.
265		 */
266		phdr->p_memsz += (end - edata);
267	}
268
269	/*
270	 * Relocate our own symbols.  We'll handle the
271	 * undefined symbols later.
272	 */
273	for (i = 1; i < sh_num; i++) {
274		Shdr *rshp, *shp, *ssp;
275		unsigned long baseaddr, reladdr, rend;
276		int relocsize;
277
278		rshp = section[i];
279
280		if (rshp->sh_type != SHT_REL)
281			continue;
282		/*
283		 * Get the section being relocated
284		 * and the symbol table.
285		 */
286		shp = section[rshp->sh_info];
287		ssp = section[rshp->sh_link];
288
289		reladdr = rshp->sh_addr;
290		baseaddr = shp->sh_addr;
291		rend = reladdr + rshp->sh_size;
292		relocsize = rshp->sh_entsize;
293		/*
294		 * Loop through relocations.
295		 */
296		while (reladdr < rend) {
297			Sym *symref;
298			Rel *reloc;
299			unsigned long stndx;
300			unsigned long off, *offptr;
301			long value;
302			int rtype;
303
304			reloc = (Rel *)reladdr;
305			off = reloc->r_offset;
306			rtype = ELF32_R_TYPE(reloc->r_info);
307			stndx = ELF32_R_SYM(reloc->r_info);
308
309			reladdr += relocsize;
310
311			if (rtype == R_386_NONE) {
312				continue;
313			}
314			off += baseaddr;
315
316			if (rtype == R_386_RELATIVE) {
317				/*
318				 * add base addr to reloc location
319				 */
320				value = baseaddr;
321			} else {
322				unsigned int symoff, symsize;
323
324				symsize = ssp->sh_entsize;
325
326				for (symoff = 0; stndx; stndx--)
327					symoff += symsize;
328				symref = (Sym *)(ssp->sh_addr + symoff);
329
330				/*
331				 * Check for bad symbol index.
332				 */
333				if (symoff > ssp->sh_size)
334					return;
335
336				/*
337				 * Just bind our own symbols at this point.
338				 */
339				if (symref->st_shndx == SHN_UNDEF) {
340					continue;
341				}
342
343				value = symref->st_value;
344				if (ELF32_ST_BIND(symref->st_info) !=
345				    STB_LOCAL) {
346					/*
347					 * If PC-relative, subtract ref addr.
348					 */
349					if (rtype == R_386_PC32 ||
350					    rtype == R_386_PLT32 ||
351					    rtype == R_386_GOTPC)
352						value -= off;
353				}
354			}
355			offptr = (unsigned long *)off;
356			/*
357			 * insert value calculated at reference point
358			 * 2 cases - normal byte order aligned, normal byte
359			 * order unaligned.
360			 */
361			switch (rtype) {
362			case R_386_PC32:
363			case R_386_32:
364			case R_386_PLT32:
365			case R_386_RELATIVE:
366				*offptr += value;
367				break;
368
369			/*
370			 * For now, ignore GOT references ...
371			 */
372
373			case R_386_GOTPC:
374#if defined(DEBUG)
375				BSVC_PUTCHAR(syscallp, 'p');
376#endif
377				break;
378			case R_386_GOTOFF:
379				BSVC_PUTCHAR(syscallp, 'g');
380				break;
381			default:
382				BSVC_PUTCHAR(syscallp, 'r');
383				return;
384			}
385			/*
386			 * We only need to do it once.
387			 */
388			reloc->r_info = ELF32_R_INFO(stndx, R_386_NONE);
389		} /* while */
390	}
391
392	/*
393	 * Done relocating all of our *defined*
394	 * symbols, so we hand off.
395	 */
396	kobj_init(syscallp, dvec, bootops, bootaux);
397}
398