1134458Siedowse/*-
2134458Siedowse * Copyright (c) 2003 Jake Burkholder.
3134458Siedowse * Copyright 1996-1998 John D. Polstra.
4134458Siedowse * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
5134458Siedowse * Copyright (c) 1998 Peter Wemm <peter@freebsd.org>
6134458Siedowse * All rights reserved.
7134458Siedowse *
8134458Siedowse * Redistribution and use in source and binary forms, with or without
9134458Siedowse * modification, are permitted provided that the following conditions
10134458Siedowse * are met:
11134458Siedowse * 1. Redistributions of source code must retain the above copyright
12134458Siedowse *    notice, this list of conditions and the following disclaimer.
13134458Siedowse * 2. Redistributions in binary form must reproduce the above copyright
14134458Siedowse *    notice, this list of conditions and the following disclaimer in the
15134458Siedowse *    documentation and/or other materials provided with the distribution.
16134458Siedowse *
17134458Siedowse * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18134458Siedowse * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19134458Siedowse * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20134458Siedowse * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21134458Siedowse * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22134458Siedowse * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23134458Siedowse * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24134458Siedowse * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25134458Siedowse * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26134458Siedowse * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27134458Siedowse * SUCH DAMAGE.
28134458Siedowse */
29134458Siedowse
30134458Siedowse#include <sys/cdefs.h>
31134458Siedowse__FBSDID("$FreeBSD$");
32134458Siedowse
33134458Siedowse#include <sys/types.h>
34134458Siedowse#include <machine/elf.h>
35134458Siedowse
36134458Siedowse#include <errno.h>
37134458Siedowse#include <stand.h>
38134458Siedowse
39134458Siedowse#define FREEBSD_ELF
40134458Siedowse#include <link.h>
41134458Siedowse
42134458Siedowse#include "bootstrap.h"
43134458Siedowse
44134458Siedowse#define COPYOUT(s,d,l)	archsw.arch_copyout((vm_offset_t)(s), d, l)
45134458Siedowse
46134458Siedowse/*
47134458Siedowse * Apply a single intra-module relocation to the data. `relbase' is the
48134458Siedowse * target relocation base for the section (i.e. it corresponds to where
49134458Siedowse * r_offset == 0). `dataaddr' is the relocated address corresponding to
50134458Siedowse * the start of the data, and `len' is the number of bytes.
51134458Siedowse */
52134458Siedowseint
53134458Siedowse__elfN(reloc)(struct elf_file *ef, symaddr_fn *symaddr, const void *reldata,
54134458Siedowse    int reltype, Elf_Addr relbase, Elf_Addr dataaddr, void *data, size_t len)
55134458Siedowse{
56134458Siedowse#ifdef __sparc__
57153504Smarcel	Elf_Size w;
58134458Siedowse	const Elf_Rela *a;
59134458Siedowse
60134458Siedowse	switch (reltype) {
61134458Siedowse	case ELF_RELOC_RELA:
62134458Siedowse		a = reldata;
63134458Siedowse		 if (relbase + a->r_offset >= dataaddr &&
64134458Siedowse		     relbase + a->r_offset < dataaddr + len) {
65134458Siedowse			switch (ELF_R_TYPE(a->r_info)) {
66134458Siedowse			case R_SPARC_RELATIVE:
67134458Siedowse				w = relbase + a->r_addend;
68134458Siedowse				bcopy(&w, (u_char *)data + (relbase +
69134458Siedowse				    a->r_offset - dataaddr), sizeof(w));
70134458Siedowse				break;
71134458Siedowse			default:
72134458Siedowse				printf("\nunhandled relocation type %u\n",
73134458Siedowse				    (u_int)ELF_R_TYPE(a->r_info));
74134458Siedowse				return (EFTYPE);
75134458Siedowse			}
76134458Siedowse		}
77134458Siedowse		break;
78134458Siedowse	}
79134458Siedowse
80134458Siedowse	return (0);
81223695Sdfr#elif (defined(__i386__) || defined(__amd64__)) && __ELF_WORD_SIZE == 64
82134458Siedowse	Elf64_Addr *where, val;
83134458Siedowse	Elf_Addr addend, addr;
84153504Smarcel	Elf_Size rtype, symidx;
85134458Siedowse	const Elf_Rel *rel;
86134458Siedowse	const Elf_Rela *rela;
87134458Siedowse
88134458Siedowse	switch (reltype) {
89134458Siedowse	case ELF_RELOC_REL:
90134458Siedowse		rel = (const Elf_Rel *)reldata;
91134458Siedowse		where = (Elf_Addr *)((char *)data + relbase + rel->r_offset -
92134458Siedowse		    dataaddr);
93134458Siedowse		addend = 0;
94134458Siedowse		rtype = ELF_R_TYPE(rel->r_info);
95134458Siedowse		symidx = ELF_R_SYM(rel->r_info);
96134458Siedowse		addend = 0;
97134458Siedowse		break;
98134458Siedowse	case ELF_RELOC_RELA:
99134458Siedowse		rela = (const Elf_Rela *)reldata;
100134458Siedowse		where = (Elf_Addr *)((char *)data + relbase + rela->r_offset -
101134458Siedowse		    dataaddr);
102134458Siedowse		addend = rela->r_addend;
103134458Siedowse		rtype = ELF_R_TYPE(rela->r_info);
104134458Siedowse		symidx = ELF_R_SYM(rela->r_info);
105134458Siedowse		break;
106134458Siedowse	default:
107134458Siedowse		return (EINVAL);
108134458Siedowse	}
109134458Siedowse
110134458Siedowse	if ((char *)where < (char *)data || (char *)where >= (char *)data + len)
111134458Siedowse		return (0);
112134458Siedowse
113134458Siedowse	if (reltype == ELF_RELOC_REL)
114134458Siedowse		addend = *where;
115134458Siedowse
116134458Siedowse/* XXX, definitions not available on i386. */
117134458Siedowse#define	R_X86_64_64		1
118134458Siedowse#define	R_X86_64_RELATIVE	8
119134458Siedowse
120134458Siedowse	switch (rtype) {
121134458Siedowse	case R_X86_64_64:		/* S + A */
122134458Siedowse		addr = symaddr(ef, symidx);
123134458Siedowse		if (addr == 0)
124134458Siedowse			return (ESRCH);
125134458Siedowse		val = addr + addend;
126134458Siedowse		*where = val;
127134458Siedowse		break;
128134458Siedowse	case R_X86_64_RELATIVE:
129134458Siedowse		addr = (Elf_Addr)addend + relbase;
130134458Siedowse		val = addr;
131134458Siedowse		*where = val;
132134458Siedowse		break;
133134458Siedowse	default:
134134458Siedowse		printf("\nunhandled relocation type %u\n", (u_int)rtype);
135134458Siedowse		return (EFTYPE);
136134458Siedowse	}
137134458Siedowse
138134458Siedowse	return (0);
139134458Siedowse#elif defined(__i386__) && __ELF_WORD_SIZE == 32
140134458Siedowse	Elf_Addr addend, addr, *where, val;
141153504Smarcel	Elf_Size rtype, symidx;
142134458Siedowse	const Elf_Rel *rel;
143134458Siedowse	const Elf_Rela *rela;
144134458Siedowse
145134458Siedowse	switch (reltype) {
146134458Siedowse	case ELF_RELOC_REL:
147134458Siedowse		rel = (const Elf_Rel *)reldata;
148134458Siedowse		where = (Elf_Addr *)((char *)data + relbase + rel->r_offset -
149134458Siedowse		    dataaddr);
150134458Siedowse		addend = 0;
151134458Siedowse		rtype = ELF_R_TYPE(rel->r_info);
152134458Siedowse		symidx = ELF_R_SYM(rel->r_info);
153134458Siedowse		addend = 0;
154134458Siedowse		break;
155134458Siedowse	case ELF_RELOC_RELA:
156134458Siedowse		rela = (const Elf_Rela *)reldata;
157134458Siedowse		where = (Elf_Addr *)((char *)data + relbase + rela->r_offset -
158134458Siedowse		    dataaddr);
159134458Siedowse		addend = rela->r_addend;
160134458Siedowse		rtype = ELF_R_TYPE(rela->r_info);
161134458Siedowse		symidx = ELF_R_SYM(rela->r_info);
162134458Siedowse		break;
163134458Siedowse	default:
164134458Siedowse		return (EINVAL);
165134458Siedowse	}
166134458Siedowse
167134458Siedowse	if ((char *)where < (char *)data || (char *)where >= (char *)data + len)
168134458Siedowse		return (0);
169134458Siedowse
170134458Siedowse	if (reltype == ELF_RELOC_REL)
171134458Siedowse		addend = *where;
172134458Siedowse
173134458Siedowse/* XXX, definitions not available on amd64. */
174134458Siedowse#define R_386_32	1	/* Add symbol value. */
175134458Siedowse#define R_386_GLOB_DAT	6	/* Set GOT entry to data address. */
176134458Siedowse#define R_386_RELATIVE	8	/* Add load address of shared object. */
177134458Siedowse
178134458Siedowse	switch (rtype) {
179134458Siedowse	case R_386_RELATIVE:
180134458Siedowse		addr = addend + relbase;
181134458Siedowse		*where = addr;
182134458Siedowse		break;
183134458Siedowse	case R_386_32:		/* S + A */
184134458Siedowse		addr = symaddr(ef, symidx);
185134458Siedowse		if (addr == 0)
186134458Siedowse			return (ESRCH);
187134458Siedowse		val = addr + addend;
188134458Siedowse		*where = val;
189134458Siedowse		break;
190134458Siedowse	default:
191134458Siedowse		printf("\nunhandled relocation type %u\n", (u_int)rtype);
192134458Siedowse		return (EFTYPE);
193134458Siedowse	}
194134458Siedowse
195134458Siedowse	return (0);
196241036Sandreast#elif defined(__powerpc__)
197241036Sandreast	Elf_Size w;
198241036Sandreast	const Elf_Rela *rela;
199241036Sandreast
200241036Sandreast	switch (reltype) {
201241036Sandreast	case ELF_RELOC_RELA:
202241036Sandreast		rela = reldata;
203241036Sandreast		if (relbase + rela->r_offset >= dataaddr &&
204241036Sandreast		    relbase + rela->r_offset < dataaddr + len) {
205241036Sandreast			switch (ELF_R_TYPE(rela->r_info)) {
206241036Sandreast			case R_PPC_RELATIVE:
207241036Sandreast				w = relbase + rela->r_addend;
208241036Sandreast				bcopy(&w, (u_char *)data + (relbase +
209241036Sandreast				      rela->r_offset - dataaddr), sizeof(w));
210241036Sandreast				break;
211241036Sandreast			default:
212241036Sandreast				printf("\nunhandled relocation type %u\n",
213241036Sandreast				       (u_int)ELF_R_TYPE(rela->r_info));
214241036Sandreast				return (EFTYPE);
215241036Sandreast			}
216241036Sandreast		}
217241036Sandreast		break;
218241036Sandreast	}
219241036Sandreast
220241036Sandreast	return (0);
221134458Siedowse#else
222134458Siedowse	return (EOPNOTSUPP);
223134458Siedowse#endif
224134458Siedowse}
225