xref: /freebsd/stand/common/reloc_elf.c (revision 02e9120893770924227138ba49df1edb3896112a)
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  */
47 int
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