kobj_boot.c revision e498729e15e121c426eb82534224f2cce4b2e020
0N/A/*
0N/A * CDDL HEADER START
0N/A *
0N/A * The contents of this file are subject to the terms of the
0N/A * Common Development and Distribution License (the "License").
0N/A * You may not use this file except in compliance with the License.
0N/A *
0N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
0N/A * or http://www.opensolaris.org/os/licensing.
0N/A * See the License for the specific language governing permissions
0N/A * and limitations under the License.
0N/A *
0N/A * When distributing Covered Code, include this CDDL HEADER in each
0N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
0N/A * If applicable, add the following below this CDDL HEADER, with the
0N/A * fields enclosed by brackets "[]" replaced with your own identifying
0N/A * information: Portions Copyright [yyyy] [name of copyright owner]
0N/A *
0N/A * CDDL HEADER END
0N/A */
0N/A/*
0N/A * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
0N/A * Use is subject to license terms.
0N/A */
0N/A
0N/A#pragma ident "%Z%%M% %I% %E% SMI"
0N/A
0N/A/*
0N/A * Bootstrap the linker/loader.
0N/A */
0N/A
0N/A#include <sys/types.h>
0N/A#include <sys/bootconf.h>
0N/A#include <sys/link.h>
0N/A#include <sys/auxv.h>
0N/A#include <sys/kobj.h>
0N/A#include <sys/elf.h>
0N/A#include <sys/bootsvcs.h>
0N/A#include <sys/kobj_impl.h>
0N/A
0N/A#if !defined(__GNUC__)
0N/A
0N/A/*
0N/A * We don't use the global offset table, but
0N/A * ld may throw in an UNDEFINED reference in
0N/A * our symbol table.
0N/A */
0N/A
0N/A#pragma weak _GLOBAL_OFFSET_TABLE_
0N/A
0N/A#else
0N/A
0N/A/*
0N/A * We -do- use the global offset table, but only by
0N/A * accident -- when you tell gcc to emit PIC code,
0N/A * it -always- generates a reference to the GOT in
0N/A * a register, even if the compilation unit never
0N/A * uses it.
0N/A *
0N/A * Rumoured to be fixed in a later version of gcc..
0N/A */
0N/A
0N/Along _GLOBAL_OFFSET_TABLE_[1];
0N/A
0N/A#endif
0N/A
0N/A#define MASK(n) ((1<<(n))-1)
0N/A#define IN_RANGE(v, n) ((-(1<<((n)-1))) <= (v) && (v) < (1<<((n)-1)))
0N/A
0N/A#define roundup ALIGN
0N/A
0N/A/*
0N/A * Boot transfers control here. At this point,
0N/A * we haven't relocated our own symbols, so the
0N/A * world (as we know it) is pretty small right now.
0N/A */
0N/Avoid
0N/A_kobj_boot(
0N/A struct boot_syscalls *syscallp,
0N/A void *dvec,
0N/A struct bootops *bootops,
0N/A Boot *ebp)
0N/A{
0N/A Shdr *section[24]; /* cache */
0N/A val_t bootaux[BA_NUM];
0N/A struct bootops *bop;
0N/A Phdr *phdr;
0N/A auxv_t *auxv = NULL;
0N/A Shdr *sh;
0N/A Half sh_num;
0N/A uint_t end, edata = 0;
0N/A int i;
0N/A
0N/A bop = (dvec) ? *(struct bootops **)bootops : bootops;
0N/A
0N/A for (i = 0; i < BA_NUM; i++)
0N/A bootaux[i].ba_val = NULL;
0N/A
0N/A /*
0N/A * Check the bootstrap vector.
0N/A */
0N/A for (; ebp->eb_tag != EB_NULL; ebp++) {
0N/A switch (ebp->eb_tag) {
0N/A case EB_AUXV:
0N/A auxv = (auxv_t *)ebp->eb_un.eb_ptr;
0N/A break;
0N/A case EB_DYNAMIC:
0N/A bootaux[BA_DYNAMIC].ba_ptr = (void *)ebp->eb_un.eb_ptr;
0N/A break;
0N/A default:
0N/A break;
0N/A }
0N/A }
0N/A
0N/A if (auxv == NULL)
0N/A return;
0N/A
0N/A /*
0N/A * Now the aux vector.
0N/A */
0N/A for (; auxv->a_type != AT_NULL; auxv++) {
0N/A switch (auxv->a_type) {
0N/A case AT_PHDR:
0N/A bootaux[BA_PHDR].ba_ptr = auxv->a_un.a_ptr;
0N/A break;
0N/A case AT_PHENT:
0N/A bootaux[BA_PHENT].ba_val = auxv->a_un.a_val;
0N/A break;
0N/A case AT_PHNUM:
0N/A bootaux[BA_PHNUM].ba_val = auxv->a_un.a_val;
0N/A break;
0N/A case AT_PAGESZ:
0N/A bootaux[BA_PAGESZ].ba_val = auxv->a_un.a_val;
0N/A break;
0N/A case AT_SUN_LDELF:
0N/A bootaux[BA_LDELF].ba_ptr = auxv->a_un.a_ptr;
0N/A break;
0N/A case AT_SUN_LDSHDR:
0N/A bootaux[BA_LDSHDR].ba_ptr = auxv->a_un.a_ptr;
0N/A break;
0N/A case AT_SUN_LDNAME:
0N/A bootaux[BA_LDNAME].ba_ptr = auxv->a_un.a_ptr;
0N/A break;
0N/A case AT_SUN_LPAGESZ:
0N/A bootaux[BA_LPAGESZ].ba_val = auxv->a_un.a_val;
0N/A break;
0N/A case AT_SUN_CPU:
0N/A bootaux[BA_CPU].ba_ptr = auxv->a_un.a_ptr;
0N/A break;
0N/A case AT_SUN_MMU:
0N/A bootaux[BA_MMU].ba_ptr = auxv->a_un.a_ptr;
0N/A break;
0N/A case AT_ENTRY:
0N/A bootaux[BA_ENTRY].ba_ptr = auxv->a_un.a_ptr;
0N/A break;
0N/A default:
0N/A break;
0N/A }
0N/A }
0N/A
0N/A sh = (Shdr *)bootaux[BA_LDSHDR].ba_ptr;
0N/A sh_num = ((Ehdr *)bootaux[BA_LDELF].ba_ptr)->e_shnum;
0N/A /*
0N/A * Build cache table for section addresses.
0N/A */
0N/A for (i = 0; i < sh_num; i++) {
0N/A section[i] = sh++;
0N/A }
0N/A
0N/A /*
0N/A * Find the end of data
0N/A * (to allocate bss)
0N/A */
0N/A phdr = (Phdr *)bootaux[BA_PHDR].ba_ptr;
0N/A for (i = 0; i < bootaux[BA_PHNUM].ba_val; i++) {
0N/A if (phdr->p_type == PT_LOAD &&
0N/A (phdr->p_flags & PF_W) && (phdr->p_flags & PF_X)) {
0N/A edata = end = phdr->p_vaddr + phdr->p_memsz;
0N/A break;
0N/A }
0N/A phdr = (Phdr *)((ulong_t)phdr + bootaux[BA_PHENT].ba_val);
0N/A }
0N/A if (edata == NULL)
0N/A return;
0N/A
0N/A /*
0N/A * Find the symbol table, and then loop
0N/A * through the symbols adjusting their
0N/A * values to reflect where the sections
0N/A * were loaded.
0N/A */
0N/A for (i = 1; i < sh_num; i++) {
0N/A Shdr *shp;
0N/A Sym *sp;
0N/A uint_t off;
0N/A
0N/A shp = section[i];
0N/A if (shp->sh_type != SHT_SYMTAB)
0N/A continue;
0N/A
0N/A for (off = 0; off < shp->sh_size; off += shp->sh_entsize) {
0N/A sp = (Sym *)(shp->sh_addr + off);
0N/A
0N/A if (sp->st_shndx == SHN_ABS ||
0N/A sp->st_shndx == SHN_UNDEF)
0N/A continue;
0N/A /*
0N/A * Assign the addresses for COMMON
0N/A * symbols even though we haven't
0N/A * actually allocated bss yet.
0N/A */
0N/A if (sp->st_shndx == SHN_COMMON) {
0N/A end = ALIGN(end, sp->st_value);
0N/A sp->st_value = end;
0N/A /*
0N/A * Squirrel it away for later.
0N/A */
0N/A if (bootaux[BA_BSS].ba_val == 0)
0N/A bootaux[BA_BSS].ba_val = end;
0N/A end += sp->st_size;
0N/A continue;
0N/A } else if (sp->st_shndx > (Half)sh_num) {
0N/A BSVC_PUTCHAR(syscallp, '>');
0N/A return;
0N/A }
0N/A
0N/A /*
0N/A * Symbol's new address.
0N/A */
0N/A sp->st_value += section[sp->st_shndx]->sh_addr;
0N/A }
0N/A }
0N/A
0N/A /*
* Allocate bss for COMMON, if any.
*/
if (end > edata) {
unsigned long va, bva;
unsigned long asize;
unsigned long align;
if (bootaux[BA_LPAGESZ].ba_val) {
asize = bootaux[BA_LPAGESZ].ba_val;
align = bootaux[BA_LPAGESZ].ba_val;
} else {
asize = bootaux[BA_PAGESZ].ba_val;
align = BO_NO_ALIGN;
}
va = roundup(edata, asize);
bva = roundup(end, asize);
if (bva > va) {
bva = (unsigned long)BOP_ALLOC(bop, (caddr_t)va,
bva - va, align);
if (bva == NULL)
return;
}
/*
* Zero it.
*/
for (va = edata; va < end; va++)
*(char *)va = 0;
/*
* Update the size of data.
*/
phdr->p_memsz += (end - edata);
}
/*
* Relocate our own symbols. We'll handle the
* undefined symbols later.
*/
for (i = 1; i < sh_num; i++) {
Shdr *rshp, *shp, *ssp;
unsigned long baseaddr, reladdr, rend;
int relocsize;
rshp = section[i];
if (rshp->sh_type != SHT_REL)
continue;
/*
* Get the section being relocated
* and the symbol table.
*/
shp = section[rshp->sh_info];
ssp = section[rshp->sh_link];
reladdr = rshp->sh_addr;
baseaddr = shp->sh_addr;
rend = reladdr + rshp->sh_size;
relocsize = rshp->sh_entsize;
/*
* Loop through relocations.
*/
while (reladdr < rend) {
Sym *symref;
Rel *reloc;
unsigned long stndx;
unsigned long off, *offptr;
long value;
int rtype;
reloc = (Rel *)reladdr;
off = reloc->r_offset;
rtype = ELF32_R_TYPE(reloc->r_info);
stndx = ELF32_R_SYM(reloc->r_info);
reladdr += relocsize;
if (rtype == R_386_NONE) {
continue;
}
off += baseaddr;
if (rtype == R_386_RELATIVE) {
/*
* add base addr to reloc location
*/
value = baseaddr;
} else {
unsigned int symoff, symsize;
symsize = ssp->sh_entsize;
for (symoff = 0; stndx; stndx--)
symoff += symsize;
symref = (Sym *)(ssp->sh_addr + symoff);
/*
* Check for bad symbol index.
*/
if (symoff > ssp->sh_size)
return;
/*
* Just bind our own symbols at this point.
*/
if (symref->st_shndx == SHN_UNDEF) {
continue;
}
value = symref->st_value;
if (ELF32_ST_BIND(symref->st_info) !=
STB_LOCAL) {
/*
* If PC-relative, subtract ref addr.
*/
if (rtype == R_386_PC32 ||
rtype == R_386_PLT32 ||
rtype == R_386_GOTPC)
value -= off;
}
}
offptr = (unsigned long *)off;
/*
* insert value calculated at reference point
* 2 cases - normal byte order aligned, normal byte
* order unaligned.
*/
switch (rtype) {
case R_386_PC32:
case R_386_32:
case R_386_PLT32:
case R_386_RELATIVE:
*offptr += value;
break;
/*
* For now, ignore GOT references ...
*/
case R_386_GOTPC:
#if defined(DEBUG) && !defined(__GNUC__)
BSVC_PUTCHAR(syscallp, 'p');
#endif
break;
case R_386_GOTOFF:
BSVC_PUTCHAR(syscallp, 'g');
break;
default:
BSVC_PUTCHAR(syscallp, 'r');
return;
}
/*
* We only need to do it once.
*/
reloc->r_info = ELF32_R_INFO(stndx, R_386_NONE);
} /* while */
}
/*
* Done relocating all of our *defined*
* symbols, so we hand off.
*/
kobj_init(syscallp, dvec, bootops, bootaux);
}