1/*-
2 * Copyright (c) 2003 Jake Burkholder.
3 * Copyright 1996-1998 John D. Polstra.
4 * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
5 * Copyright (c) 1998 Peter Wemm <peter@freebsd.org>
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#include <sys/types.h>
31#include <machine/elf.h>
32
33#include <stand.h>
34
35#include <sys/link_elf.h>
36
37#include "bootstrap.h"
38
39#define COPYOUT(s,d,l)	archsw.arch_copyout((vm_offset_t)(s), d, l)
40
41/*
42 * Apply a single intra-module relocation to the data. `relbase' is the
43 * target relocation base for the section (i.e. it corresponds to where
44 * r_offset == 0). `dataaddr' is the relocated address corresponding to
45 * the start of the data, and `len' is the number of bytes.
46 */
47int
48__elfN(reloc)(struct elf_file *ef, symaddr_fn *symaddr, const void *reldata,
49    int reltype, Elf_Addr relbase, Elf_Addr dataaddr, void *data, size_t len)
50{
51#if (defined(__aarch64__) || defined(__amd64__) || defined(__i386__)) && \
52    __ELF_WORD_SIZE == 64
53	Elf64_Addr *where, val;
54	Elf_Addr addend, addr;
55	Elf_Size rtype;
56#if defined(__amd64__) || defined(__i386__)
57	Elf_Size symidx;
58#endif
59	const Elf_Rel *rel;
60	const Elf_Rela *rela;
61
62	switch (reltype) {
63	case ELF_RELOC_REL:
64		rel = (const Elf_Rel *)reldata;
65		where = (Elf_Addr *)((char *)data + relbase + rel->r_offset -
66		    dataaddr);
67		addend = 0;
68		rtype = ELF_R_TYPE(rel->r_info);
69#if defined(__amd64__) || defined(__i386__)
70		symidx = ELF_R_SYM(rel->r_info);
71#endif
72		addend = 0;
73		break;
74	case ELF_RELOC_RELA:
75		rela = (const Elf_Rela *)reldata;
76		where = (Elf_Addr *)((char *)data + relbase + rela->r_offset -
77		    dataaddr);
78		addend = rela->r_addend;
79		rtype = ELF_R_TYPE(rela->r_info);
80#if defined(__amd64__) || defined(__i386__)
81		symidx = ELF_R_SYM(rela->r_info);
82#endif
83		break;
84	default:
85		return (EINVAL);
86	}
87
88	if ((char *)where < (char *)data || (char *)where >= (char *)data + len)
89		return (0);
90
91	if (reltype == ELF_RELOC_REL)
92		addend = *where;
93
94#if defined(__aarch64__)
95#define	RELOC_RELATIVE		R_AARCH64_RELATIVE
96#define	RELOC_IRELATIVE		R_AARCH64_IRELATIVE
97#elif defined(__amd64__) || defined(__i386__)
98/* XXX, definitions not available on i386. */
99#define	R_X86_64_64		1
100#define	R_X86_64_RELATIVE	8
101#define	R_X86_64_IRELATIVE	37
102
103#define	RELOC_RELATIVE		R_X86_64_RELATIVE
104#define	RELOC_IRELATIVE		R_X86_64_IRELATIVE
105#endif
106
107	switch (rtype) {
108	case RELOC_RELATIVE:
109		addr = (Elf_Addr)addend + relbase;
110		val = addr;
111		memcpy(where, &val, sizeof(val));
112		break;
113	case RELOC_IRELATIVE:
114		/* leave it to kernel */
115		break;
116#if defined(__amd64__) || defined(__i386__)
117	case R_X86_64_64:		/* S + A */
118		addr = symaddr(ef, symidx);
119		if (addr == 0)
120			return (ESRCH);
121		val = addr + addend;
122		*where = val;
123		break;
124#endif
125	default:
126		printf("\nunhandled relocation type %u\n", (u_int)rtype);
127		return (EFTYPE);
128	}
129
130	return (0);
131#elif defined(__i386__) && __ELF_WORD_SIZE == 32
132	Elf_Addr addend, addr, *where, val;
133	Elf_Size rtype, symidx;
134	const Elf_Rel *rel;
135	const Elf_Rela *rela;
136
137	switch (reltype) {
138	case ELF_RELOC_REL:
139		rel = (const Elf_Rel *)reldata;
140		where = (Elf_Addr *)((char *)data + relbase + rel->r_offset -
141		    dataaddr);
142		addend = 0;
143		rtype = ELF_R_TYPE(rel->r_info);
144		symidx = ELF_R_SYM(rel->r_info);
145		addend = 0;
146		break;
147	case ELF_RELOC_RELA:
148		rela = (const Elf_Rela *)reldata;
149		where = (Elf_Addr *)((char *)data + relbase + rela->r_offset -
150		    dataaddr);
151		addend = rela->r_addend;
152		rtype = ELF_R_TYPE(rela->r_info);
153		symidx = ELF_R_SYM(rela->r_info);
154		break;
155	default:
156		return (EINVAL);
157	}
158
159	if ((char *)where < (char *)data || (char *)where >= (char *)data + len)
160		return (0);
161
162	if (reltype == ELF_RELOC_REL)
163		addend = *where;
164
165/* XXX, definitions not available on amd64. */
166#define R_386_32	1	/* Add symbol value. */
167#define R_386_GLOB_DAT	6	/* Set GOT entry to data address. */
168#define R_386_RELATIVE	8	/* Add load address of shared object. */
169#define	R_386_IRELATIVE	42
170
171	switch (rtype) {
172	case R_386_RELATIVE:
173		addr = addend + relbase;
174		*where = addr;
175		break;
176	case R_386_32:		/* S + A */
177		addr = symaddr(ef, symidx);
178		if (addr == 0)
179			return (ESRCH);
180		val = addr + addend;
181		*where = val;
182		break;
183	case R_386_IRELATIVE:
184		/* leave it to kernel */
185		break;
186	default:
187		printf("\nunhandled relocation type %u\n", (u_int)rtype);
188		return (EFTYPE);
189	}
190
191	return (0);
192#elif defined(__powerpc__) || defined(__riscv)
193	Elf_Size w;
194	const Elf_Rela *rela;
195
196	switch (reltype) {
197	case ELF_RELOC_RELA:
198		rela = reldata;
199		if (relbase + rela->r_offset >= dataaddr &&
200		    relbase + rela->r_offset < dataaddr + len) {
201			switch (ELF_R_TYPE(rela->r_info)) {
202#if defined(__powerpc__)
203			case R_PPC_RELATIVE:
204#elif defined(__riscv)
205			case R_RISCV_RELATIVE:
206#endif
207				w = relbase + rela->r_addend;
208				bcopy(&w, (u_char *)data + (relbase +
209				      rela->r_offset - dataaddr), sizeof(w));
210				break;
211			default:
212				printf("\nunhandled relocation type %u\n",
213				       (u_int)ELF_R_TYPE(rela->r_info));
214				return (EFTYPE);
215			}
216		}
217		break;
218	}
219
220	return (0);
221#else
222	return (EOPNOTSUPP);
223#endif
224}
225