reloc_elf.c revision 223695
1219393Sadrian/*-
2219393Sadrian * Copyright (c) 2003 Jake Burkholder.
3219393Sadrian * Copyright 1996-1998 John D. Polstra.
4219393Sadrian * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
5219393Sadrian * Copyright (c) 1998 Peter Wemm <peter@freebsd.org>
6219393Sadrian * All rights reserved.
7219393Sadrian *
8219393Sadrian * Redistribution and use in source and binary forms, with or without
9219393Sadrian * modification, are permitted provided that the following conditions
10219393Sadrian * are met:
11219393Sadrian * 1. Redistributions of source code must retain the above copyright
12219393Sadrian *    notice, this list of conditions and the following disclaimer.
13219393Sadrian * 2. Redistributions in binary form must reproduce the above copyright
14219393Sadrian *    notice, this list of conditions and the following disclaimer in the
15219393Sadrian *    documentation and/or other materials provided with the distribution.
16219393Sadrian *
17219393Sadrian * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18219393Sadrian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19219393Sadrian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20219393Sadrian * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21219393Sadrian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22219393Sadrian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23219393Sadrian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24219393Sadrian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25219393Sadrian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26219393Sadrian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27219393Sadrian * SUCH DAMAGE.
28219393Sadrian */
29219393Sadrian
30219393Sadrian#include <sys/cdefs.h>
31219393Sadrian__FBSDID("$FreeBSD: head/sys/boot/common/reloc_elf.c 223695 2011-06-30 16:08:56Z dfr $");
32219393Sadrian
33219393Sadrian#include <sys/types.h>
34219393Sadrian#include <machine/elf.h>
35219393Sadrian
36219393Sadrian#include <errno.h>
37219393Sadrian#include <stand.h>
38219393Sadrian
39219393Sadrian#define FREEBSD_ELF
40219393Sadrian#include <link.h>
41219393Sadrian
42219393Sadrian#include "bootstrap.h"
43219393Sadrian
44219393Sadrian#define COPYOUT(s,d,l)	archsw.arch_copyout((vm_offset_t)(s), d, l)
45219393Sadrian
46219393Sadrian/*
47219393Sadrian * Apply a single intra-module relocation to the data. `relbase' is the
48219393Sadrian * target relocation base for the section (i.e. it corresponds to where
49219393Sadrian * r_offset == 0). `dataaddr' is the relocated address corresponding to
50219393Sadrian * the start of the data, and `len' is the number of bytes.
51219393Sadrian */
52219393Sadrianint
53219393Sadrian__elfN(reloc)(struct elf_file *ef, symaddr_fn *symaddr, const void *reldata,
54219393Sadrian    int reltype, Elf_Addr relbase, Elf_Addr dataaddr, void *data, size_t len)
55219393Sadrian{
56219393Sadrian#ifdef __sparc__
57219393Sadrian	Elf_Size w;
58219393Sadrian	const Elf_Rela *a;
59219393Sadrian
60219393Sadrian	switch (reltype) {
61219393Sadrian	case ELF_RELOC_RELA:
62219393Sadrian		a = reldata;
63219393Sadrian		 if (relbase + a->r_offset >= dataaddr &&
64219393Sadrian		     relbase + a->r_offset < dataaddr + len) {
65219393Sadrian			switch (ELF_R_TYPE(a->r_info)) {
66219393Sadrian			case R_SPARC_RELATIVE:
67219393Sadrian				w = relbase + a->r_addend;
68219393Sadrian				bcopy(&w, (u_char *)data + (relbase +
69219393Sadrian				    a->r_offset - dataaddr), sizeof(w));
70219393Sadrian				break;
71219393Sadrian			default:
72219393Sadrian				printf("\nunhandled relocation type %u\n",
73219393Sadrian				    (u_int)ELF_R_TYPE(a->r_info));
74219393Sadrian				return (EFTYPE);
75219393Sadrian			}
76219393Sadrian		}
77219393Sadrian		break;
78219393Sadrian	}
79219393Sadrian
80219393Sadrian	return (0);
81219393Sadrian#elif (defined(__i386__) || defined(__amd64__)) && __ELF_WORD_SIZE == 64
82219393Sadrian	Elf64_Addr *where, val;
83219393Sadrian	Elf_Addr addend, addr;
84219393Sadrian	Elf_Size rtype, symidx;
85219393Sadrian	const Elf_Rel *rel;
86219393Sadrian	const Elf_Rela *rela;
87219393Sadrian
88219393Sadrian	switch (reltype) {
89219393Sadrian	case ELF_RELOC_REL:
90219393Sadrian		rel = (const Elf_Rel *)reldata;
91219393Sadrian		where = (Elf_Addr *)((char *)data + relbase + rel->r_offset -
92219393Sadrian		    dataaddr);
93219393Sadrian		addend = 0;
94219393Sadrian		rtype = ELF_R_TYPE(rel->r_info);
95219393Sadrian		symidx = ELF_R_SYM(rel->r_info);
96219393Sadrian		addend = 0;
97219393Sadrian		break;
98219393Sadrian	case ELF_RELOC_RELA:
99219393Sadrian		rela = (const Elf_Rela *)reldata;
100219393Sadrian		where = (Elf_Addr *)((char *)data + relbase + rela->r_offset -
101219393Sadrian		    dataaddr);
102219393Sadrian		addend = rela->r_addend;
103219393Sadrian		rtype = ELF_R_TYPE(rela->r_info);
104219393Sadrian		symidx = ELF_R_SYM(rela->r_info);
105219393Sadrian		break;
106219393Sadrian	default:
107219393Sadrian		return (EINVAL);
108219393Sadrian	}
109219393Sadrian
110219393Sadrian	if ((char *)where < (char *)data || (char *)where >= (char *)data + len)
111219393Sadrian		return (0);
112219393Sadrian
113219393Sadrian	if (reltype == ELF_RELOC_REL)
114219393Sadrian		addend = *where;
115219393Sadrian
116219393Sadrian/* XXX, definitions not available on i386. */
117219393Sadrian#define	R_X86_64_64		1
118219393Sadrian#define	R_X86_64_RELATIVE	8
119219393Sadrian
120219393Sadrian	switch (rtype) {
121219393Sadrian	case R_X86_64_64:		/* S + A */
122219393Sadrian		addr = symaddr(ef, symidx);
123219393Sadrian		if (addr == 0)
124219393Sadrian			return (ESRCH);
125219393Sadrian		val = addr + addend;
126219393Sadrian		*where = val;
127219393Sadrian		break;
128219393Sadrian	case R_X86_64_RELATIVE:
129219393Sadrian		addr = (Elf_Addr)addend + relbase;
130219393Sadrian		val = addr;
131219393Sadrian		*where = val;
132219393Sadrian		break;
133219393Sadrian	default:
134219393Sadrian		printf("\nunhandled relocation type %u\n", (u_int)rtype);
135219393Sadrian		return (EFTYPE);
136219393Sadrian	}
137219393Sadrian
138219393Sadrian	return (0);
139219393Sadrian#elif defined(__i386__) && __ELF_WORD_SIZE == 32
140219393Sadrian	Elf_Addr addend, addr, *where, val;
141219393Sadrian	Elf_Size rtype, symidx;
142219393Sadrian	const Elf_Rel *rel;
143219393Sadrian	const Elf_Rela *rela;
144219393Sadrian
145219393Sadrian	switch (reltype) {
146219393Sadrian	case ELF_RELOC_REL:
147219393Sadrian		rel = (const Elf_Rel *)reldata;
148219393Sadrian		where = (Elf_Addr *)((char *)data + relbase + rel->r_offset -
149219393Sadrian		    dataaddr);
150219393Sadrian		addend = 0;
151219393Sadrian		rtype = ELF_R_TYPE(rel->r_info);
152219393Sadrian		symidx = ELF_R_SYM(rel->r_info);
153219393Sadrian		addend = 0;
154219393Sadrian		break;
155219393Sadrian	case ELF_RELOC_RELA:
156219393Sadrian		rela = (const Elf_Rela *)reldata;
157219393Sadrian		where = (Elf_Addr *)((char *)data + relbase + rela->r_offset -
158219393Sadrian		    dataaddr);
159219393Sadrian		addend = rela->r_addend;
160219393Sadrian		rtype = ELF_R_TYPE(rela->r_info);
161219393Sadrian		symidx = ELF_R_SYM(rela->r_info);
162219393Sadrian		break;
163219444Sadrian	default:
164219444Sadrian		return (EINVAL);
165219444Sadrian	}
166219444Sadrian
167219444Sadrian	if ((char *)where < (char *)data || (char *)where >= (char *)data + len)
168219444Sadrian		return (0);
169219444Sadrian
170219444Sadrian	if (reltype == ELF_RELOC_REL)
171219444Sadrian		addend = *where;
172219444Sadrian
173219444Sadrian/* XXX, definitions not available on amd64. */
174219444Sadrian#define R_386_32	1	/* Add symbol value. */
175219444Sadrian#define R_386_GLOB_DAT	6	/* Set GOT entry to data address. */
176219444Sadrian#define R_386_RELATIVE	8	/* Add load address of shared object. */
177219444Sadrian
178219444Sadrian	switch (rtype) {
179219444Sadrian	case R_386_RELATIVE:
180219444Sadrian		addr = addend + relbase;
181219444Sadrian		*where = addr;
182219444Sadrian		break;
183219444Sadrian	case R_386_32:		/* S + A */
184219444Sadrian		addr = symaddr(ef, symidx);
185219444Sadrian		if (addr == 0)
186219444Sadrian			return (ESRCH);
187219444Sadrian		val = addr + addend;
188219444Sadrian		*where = val;
189219444Sadrian		break;
190219444Sadrian	default:
191219444Sadrian		printf("\nunhandled relocation type %u\n", (u_int)rtype);
192219444Sadrian		return (EFTYPE);
193219444Sadrian	}
194219444Sadrian
195219444Sadrian	return (0);
196219444Sadrian#else
197219444Sadrian	return (EOPNOTSUPP);
198219444Sadrian#endif
199219444Sadrian}
200219444Sadrian