kobj_boot.c revision 986fd29a0dc13f7608ef7f508f6e700bd7bc2720
2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License, Version 1.0 only
2N/A * (the "License"). You may not use this file except in compliance
2N/A * with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A
2N/A/*
2N/A * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
2N/A * Use is subject to license terms.
2N/A */
2N/A
2N/A#pragma ident "%Z%%M% %I% %E% SMI"
2N/A
2N/A/*
2N/A * Bootstrap the linker/loader.
2N/A */
2N/A
2N/A#include <sys/types.h>
2N/A#include <sys/bootconf.h>
2N/A#include <sys/elf.h>
2N/A#include <sys/link.h>
2N/A#include <sys/auxv.h>
2N/A#include <sys/kobj.h>
2N/A#include <sys/kobj_impl.h>
2N/A#include <sys/archsystm.h>
2N/A#include <sys/kdi.h>
2N/A
2N/A/*
2N/A * We don't use the global offset table, but
2N/A * ld may throw in an UNDEFINED reference in
2N/A * our symbol table.
2N/A */
2N/A#pragma weak _GLOBAL_OFFSET_TABLE_
2N/A
2N/A#define MASK(n) ((1l<<(n))-1l)
2N/A#define IN_RANGE(v, n) ((-(1l<<((n)-1l))) <= (v) && (v) < (1l<<((n)-1l)))
2N/A
2N/A#define roundup ALIGN
2N/A
2N/A/*
2N/A * Note: doflush() must be inlined, since we call it before we
2N/A * have relocated ourselves.
2N/A */
2N/A
2N/A/*
2N/A * Boot transfers control here. At this point,
2N/A * we haven't relocated our own symbols, so the
2N/A * world (as we know it) is pretty small right now.
2N/A */
2N/Avoid
2N/A_kobj_boot(
2N/A void *romp,
2N/A kdi_debugvec_t *dvec,
2N/A struct bootops *bootops,
2N/A Boot *ebp)
2N/A{
2N/A Shdr *section[24]; /* cache */
2N/A val_t bootaux[BA_NUM];
2N/A Phdr *phdr;
2N/A auxv_t *auxv = NULL;
2N/A uint_t sh, sh_num, sh_size;
2N/A uintptr_t end, edata = 0;
2N/A int i;
2N/A
2N/A for (i = 0; i < BA_NUM; i++)
2N/A bootaux[i].ba_val = NULL;
2N/A /*
2N/A * Check the bootstrap vector.
2N/A */
2N/A for (; ebp->eb_tag != EB_NULL; ebp++) {
2N/A switch (ebp->eb_tag) {
2N/A case EB_AUXV:
2N/A auxv = (auxv_t *)ebp->eb_un.eb_ptr;
2N/A break;
2N/A case EB_DYNAMIC:
2N/A bootaux[BA_DYNAMIC].ba_ptr = (void *)ebp->eb_un.eb_ptr;
2N/A break;
2N/A }
2N/A }
2N/A if (auxv == NULL)
2N/A return;
2N/A /*
2N/A * Now the aux vector.
2N/A */
2N/A for (; auxv->a_type != AT_NULL; auxv++) {
2N/A switch (auxv->a_type) {
2N/A case AT_PHDR:
2N/A bootaux[BA_PHDR].ba_ptr = auxv->a_un.a_ptr;
2N/A break;
2N/A case AT_PHENT:
2N/A bootaux[BA_PHENT].ba_val = auxv->a_un.a_val;
2N/A break;
2N/A case AT_PHNUM:
2N/A bootaux[BA_PHNUM].ba_val = auxv->a_un.a_val;
2N/A break;
2N/A case AT_PAGESZ:
2N/A bootaux[BA_PAGESZ].ba_val = auxv->a_un.a_val;
2N/A break;
2N/A case AT_SUN_LDELF:
2N/A bootaux[BA_LDELF].ba_ptr = auxv->a_un.a_ptr;
2N/A break;
2N/A case AT_SUN_LDSHDR:
2N/A bootaux[BA_LDSHDR].ba_ptr = auxv->a_un.a_ptr;
2N/A break;
2N/A case AT_SUN_LDNAME:
2N/A bootaux[BA_LDNAME].ba_ptr = auxv->a_un.a_ptr;
2N/A break;
2N/A case AT_SUN_LPAGESZ:
2N/A bootaux[BA_LPAGESZ].ba_val = auxv->a_un.a_val;
2N/A break;
2N/A case AT_SUN_IFLUSH:
2N/A bootaux[BA_IFLUSH].ba_val = auxv->a_un.a_val;
2N/A break;
2N/A case AT_SUN_CPU:
2N/A bootaux[BA_CPU].ba_ptr = auxv->a_un.a_ptr;
2N/A break;
2N/A case AT_ENTRY:
2N/A bootaux[BA_ENTRY].ba_ptr = auxv->a_un.a_ptr;
2N/A break;
2N/A }
2N/A }
2N/A
2N/A sh = (uint_t)(uintptr_t)bootaux[BA_LDSHDR].ba_ptr;
2N/A sh_num = ((Ehdr *)bootaux[BA_LDELF].ba_ptr)->e_shnum;
2N/A sh_size = ((Ehdr *)bootaux[BA_LDELF].ba_ptr)->e_shentsize;
2N/A /*
2N/A * Build cache table for section addresses.
2N/A */
2N/A for (i = 0; i < sh_num; i++) {
2N/A section[i] = (Shdr *)(uintptr_t)sh;
2N/A sh += sh_size;
2N/A }
2N/A /*
2N/A * Find the end of data
2N/A * (to allocate bss)
2N/A */
2N/A phdr = (Phdr *)bootaux[BA_PHDR].ba_ptr;
2N/A for (i = 0; i < bootaux[BA_PHNUM].ba_val; i++) {
2N/A if (phdr->p_type == PT_LOAD &&
2N/A (phdr->p_flags & PF_W) && (phdr->p_flags & PF_X)) {
2N/A edata = end = phdr->p_vaddr + phdr->p_memsz;
2N/A break;
2N/A }
2N/A phdr = (Phdr *)((uintptr_t)phdr +
2N/A bootaux[BA_PHENT].ba_val);
2N/A }
2N/A if (edata == NULL)
2N/A return;
2N/A
2N/A /*
2N/A * Find the symbol table, and then loop
2N/A * through the symbols adjusting their
2N/A * values to reflect where the sections
2N/A * were loaded.
2N/A */
2N/A for (i = 1; i < sh_num; i++) {
2N/A Shdr *shp;
2N/A Sym *sp;
2N/A uint_t off;
2N/A
2N/A shp = section[i];
2N/A if (shp->sh_type != SHT_SYMTAB)
2N/A continue;
2N/A
2N/A for (off = 0; off < shp->sh_size; off += shp->sh_entsize) {
2N/A sp = (Sym *)(shp->sh_addr + off);
2N/A
2N/A if (sp->st_shndx == SHN_ABS ||
2N/A sp->st_shndx == SHN_UNDEF)
2N/A continue;
2N/A /*
2N/A * Assign the addresses for COMMON
2N/A * symbols even though we haven't
2N/A * actually allocated bss yet.
2N/A */
2N/A if (sp->st_shndx == SHN_COMMON) {
2N/A end = ALIGN(end, sp->st_value);
2N/A sp->st_value = end;
2N/A /*
2N/A * Squirrel it away for later.
2N/A */
2N/A if (bootaux[BA_BSS].ba_val == 0)
2N/A bootaux[BA_BSS].ba_val = end;
2N/A end += sp->st_size;
2N/A continue;
2N/A } else if (sp->st_shndx > (Half)sh_num)
2N/A return;
2N/A
2N/A /*
2N/A * Symbol's new address.
2N/A */
2N/A sp->st_value += section[sp->st_shndx]->sh_addr;
2N/A }
2N/A }
2N/A /*
2N/A * Allocate bss for COMMON, if any.
2N/A */
2N/A if (end > edata) {
2N/A uintptr_t va, bva;
2N/A uintptr_t asize;
2N/A size_t align;
2N/A
2N/A if (bootaux[BA_LPAGESZ].ba_val) {
2N/A asize = bootaux[BA_LPAGESZ].ba_val;
2N/A align = bootaux[BA_LPAGESZ].ba_val;
2N/A } else {
2N/A asize = bootaux[BA_PAGESZ].ba_val;
2N/A align = BO_NO_ALIGN;
2N/A }
2N/A va = roundup(edata, asize);
2N/A bva = roundup(end, asize);
2N/A
2N/A if (bva > va) {
2N/A boot_cell_t args[7];
2N/A int (*bsys_1275_call)(void *);
2N/A char allocstr[6];
2N/A
2N/A bsys_1275_call =
2N/A (int (*)(void *))bootops->bsys_1275_call;
2N/A allocstr[0] = 'a';
2N/A allocstr[1] = 'l';
2N/A allocstr[2] = 'l';
2N/A allocstr[3] = 'o';
2N/A allocstr[4] = 'c';
2N/A allocstr[5] = '\0';
2N/A args[0] = boot_ptr2cell(allocstr);
2N/A args[1] = 3;
2N/A args[2] = 1;
2N/A args[3] = boot_ptr2cell((caddr_t)va);
2N/A args[4] = boot_size2cell(bva - va);
2N/A args[5] = boot_int2cell(align);
2N/A (void) (bsys_1275_call)(args);
2N/A bva = (uintptr_t)((caddr_t)boot_ptr2cell(args[6]));
2N/A if (bva == NULL)
2N/A return;
2N/A }
2N/A /*
2N/A * Zero it.
2N/A */
2N/A for (va = edata; va < end; va++)
2N/A *(char *)va = 0;
2N/A /*
2N/A * Update the size of data.
2N/A */
2N/A phdr->p_memsz += (end - edata);
2N/A }
2N/A /*
2N/A * Relocate our own symbols. We'll handle the
2N/A * undefined symbols later.
2N/A */
2N/A for (i = 1; i < sh_num; i++) {
2N/A Shdr *rshp, *shp, *ssp;
2N/A uintptr_t baseaddr, reladdr, rend;
2N/A int relocsize;
2N/A
2N/A rshp = section[i];
2N/A
2N/A if (rshp->sh_type != SHT_RELA)
2N/A continue;
2N/A /*
2N/A * Get the section being relocated
2N/A * and the symbol table.
2N/A */
2N/A shp = section[rshp->sh_info];
2N/A ssp = section[rshp->sh_link];
2N/A
2N/A reladdr = rshp->sh_addr;
2N/A baseaddr = shp->sh_addr;
2N/A rend = reladdr + rshp->sh_size;
2N/A relocsize = rshp->sh_entsize;
2N/A /*
2N/A * Loop through relocations.
2N/A */
2N/A while (reladdr < rend) {
2N/A Sym *symref;
2N/A Rela *reloc;
2N/A register unsigned long stndx;
2N/A unsigned int off, *offptr;
2N/A long addend, value;
2N/A int rtype;
2N/A
2N/A reloc = (Rela *)reladdr;
2N/A off = reloc->r_offset;
2N/A addend = (long)reloc->r_addend;
2N/A rtype = ELF_R_TYPE(reloc->r_info);
2N/A stndx = ELF_R_SYM(reloc->r_info);
2N/A
2N/A reladdr += relocsize;
2N/A
2N/A if (rtype == R_SPARC_NONE) {
2N/A continue;
2N/A }
2N/A off += baseaddr;
2N/A /*
2N/A * if R_SPARC_RELATIVE, simply add base addr
2N/A * to reloc location
2N/A */
2N/A if (rtype == R_SPARC_RELATIVE) {
2N/A value = baseaddr;
2N/A } else {
2N/A register unsigned int symoff, symsize;
2N/A
2N/A symsize = ssp->sh_entsize;
2N/A
2N/A for (symoff = 0; stndx; stndx--)
2N/A symoff += symsize;
2N/A symref = (Sym *)(ssp->sh_addr + symoff);
2N/A
2N/A /*
2N/A * Check for bad symbol index.
2N/A */
2N/A if (symoff > ssp->sh_size)
2N/A return;
2N/A /*
2N/A * Just bind our own symbols at this point.
2N/A */
2N/A if (symref->st_shndx == SHN_UNDEF) {
2N/A continue;
2N/A }
2N/A
2N/A value = symref->st_value;
2N/A if (ELF_ST_BIND(symref->st_info) !=
2N/A STB_LOCAL) {
2N/A /*
2N/A * If PC-relative, subtract ref addr.
2N/A */
2N/A if (rtype == R_SPARC_PC10 ||
2N/A rtype == R_SPARC_PC22 ||
2N/A rtype == R_SPARC_DISP8 ||
2N/A rtype == R_SPARC_DISP16 ||
2N/A rtype == R_SPARC_DISP32 ||
2N/A rtype == R_SPARC_WPLT30 ||
2N/A rtype == R_SPARC_WDISP30 ||
2N/A rtype == R_SPARC_WDISP22 ||
2N/A rtype == R_SPARC_WDISP16 ||
2N/A rtype == R_SPARC_WDISP19)
2N/A value -= off;
2N/A }
2N/A }
2N/A if (rtype != R_SPARC_UA32 && (off & 3) != 0)
2N/A return;
2N/A offptr = (unsigned int *)(uintptr_t)off;
2N/A /*
2N/A * insert value calculated at reference point
2N/A * 3 cases - normal byte order aligned, normal byte
2N/A * order unaligned, and byte swapped
2N/A * for the swapped and unaligned cases we insert value
2N/A * a byte at a time
2N/A */
2N/A switch (rtype) {
2N/A case 40123:
2N/A /*
2N/A * This isn't a real relocation type.
2N/A * It just confuses the compiler
2N/A * sufficiently that it can't generate
2N/A * a jump table -- tee hee
2N/A */
2N/A return;
2N/A case R_SPARC_GLOB_DAT: /* 32bit word aligned */
2N/A case R_SPARC_RELATIVE:
2N/A case R_SPARC_DISP32:
2N/A case R_SPARC_32:
2N/A /*
2N/A * 7/19/89 rmk adding current value of
2N/A * *offptr to value. Should not be needed,
2N/A * since this is a RELA type and *offptr
2N/A * should be in addend
2N/A */
2N/A *offptr = *offptr + value + addend;
2N/A break;
2N/A case R_SPARC_8:
2N/A case R_SPARC_DISP8:
2N/A value += addend;
2N/A if (IN_RANGE(value, 8))
2N/A *offptr |= value;
2N/A else
2N/A return;
2N/A break;
2N/A case R_SPARC_LO10:
2N/A case R_SPARC_PC10:
2N/A value += addend;
2N/A value &= MASK(10);
2N/A *offptr |= value;
2N/A break;
2N/A case R_SPARC_13:
2N/A value += addend;
2N/A if (IN_RANGE(value, 13))
2N/A *offptr |= value;
2N/A else
2N/A return;
2N/A break;
2N/A case R_SPARC_16:
2N/A case R_SPARC_DISP16:
2N/A value += addend;
2N/A if (IN_RANGE(value, 16))
2N/A *offptr |= value;
2N/A else
2N/A return;
2N/A break;
2N/A case R_SPARC_22:
2N/A value += addend;
2N/A if (IN_RANGE(value, 22))
2N/A *offptr |= value;
2N/A else
2N/A return;
2N/A break;
2N/A case R_SPARC_PC22:
2N/A value += addend;
2N/A value = (unsigned)value >> 10;
2N/A if (IN_RANGE(value, 22))
2N/A *offptr |= value;
2N/A else
2N/A return;
2N/A break;
2N/A case R_SPARC_WDISP22:
2N/A value += addend;
2N/A value = (unsigned)value >> 2;
2N/A if (IN_RANGE(value, 22))
2N/A *offptr |= value;
2N/A else
2N/A return;
2N/A break;
2N/A case R_SPARC_HI22:
2N/A value += addend;
2N/A value = (unsigned)value >> 10;
2N/A *offptr |= value;
2N/A break;
2N/A case R_SPARC_WDISP30:
2N/A case R_SPARC_WPLT30:
2N/A value = (unsigned)value >> 2;
2N/A *offptr |= value;
2N/A break;
2N/A case R_SPARC_UA32: {
2N/A union {
2N/A uint32_t l;
2N/A char c[4];
2N/A } symval;
2N/A
2N/A symval.l = value + addend;
2N/A
2N/A ((char *)(uintptr_t)off)[0] = symval.c[0];
2N/A ((char *)(uintptr_t)off)[1] = symval.c[1];
2N/A ((char *)(uintptr_t)off)[2] = symval.c[2];
2N/A ((char *)(uintptr_t)off)[3] = symval.c[3];
2N/A break;
2N/A }
2N/A case R_SPARC_10:
2N/A value += addend;
2N/A if (IN_RANGE(value, 10))
2N/A *offptr |= value;
2N/A else
2N/A return;
2N/A break;
2N/A case R_SPARC_11:
2N/A value += addend;
2N/A if (IN_RANGE(value, 11))
2N/A *offptr |= value;
2N/A else
2N/A return;
2N/A break;
2N/A case R_SPARC_WDISP16:
2N/A value += addend;
2N/A value = (unsigned)value >> 2;
2N/A if (IN_RANGE(value, 16)) {
2N/A *offptr |= (((value & 0xc000) << 6)
2N/A | (value & 0x3fff));
2N/A } else
2N/A return;
2N/A break;
2N/A case R_SPARC_WDISP19:
2N/A value += addend;
2N/A value = (unsigned)value >> 2;
2N/A if (IN_RANGE(value, 19))
2N/A *offptr |= value;
2N/A else
2N/A return;
2N/A break;
2N/A case R_SPARC_5:
2N/A value += addend;
2N/A if (IN_RANGE(value, 5))
2N/A *offptr |= value;
2N/A else
2N/A return;
2N/A break;
2N/A case R_SPARC_6:
2N/A value += addend;
2N/A if (IN_RANGE(value, 6))
2N/A *offptr |= value;
2N/A else
2N/A return;
2N/A break;
2N/A case R_SPARC_7:
2N/A value += addend;
2N/A if (IN_RANGE(value, 7))
2N/A *offptr |= value;
2N/A else
2N/A return;
2N/A break;
2N/A case R_SPARC_LM22:
2N/A value += addend;
2N/A value = (unsigned)value >> 10;
2N/A *offptr |= value;
2N/A break;
2N/A case R_SPARC_HH22:
2N/A value += addend;
2N/A value = value >> 42;
2N/A *offptr |= value;
2N/A break;
2N/A case R_SPARC_HM10:
2N/A value += addend;
2N/A value = (value >> 32) & 0x3ff;
2N/A *offptr |= value;
2N/A break;
2N/A case R_SPARC_64:
2N/A value += addend;
2N/A *(unsigned long *)offptr |= value;
2N/A break;
2N/A default:
2N/A return;
2N/A }
2N/A if (bootaux[BA_IFLUSH].ba_val)
2N/A doflush((caddr_t)offptr);
2N/A /*
2N/A * We only need to do it once.
2N/A */
2N/A reloc->r_info = ELF_R_INFO(stndx, R_SPARC_NONE);
2N/A } /* while */
2N/A }
2N/A
2N/A /*
2N/A * Done relocating all of our *defined*
2N/A * symbols, so we hand off.
2N/A */
2N/A kobj_init(romp, dvec, bootops, bootaux);
2N/A}
2N/A