xref: /freebsd/stand/common/reloc_elf.c (revision 3494f7c0)
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 #define FREEBSD_ELF
36 #include <sys/link_elf.h>
37 
38 #include "bootstrap.h"
39 
40 #define COPYOUT(s,d,l)	archsw.arch_copyout((vm_offset_t)(s), d, l)
41 
42 /*
43  * Apply a single intra-module relocation to the data. `relbase' is the
44  * target relocation base for the section (i.e. it corresponds to where
45  * r_offset == 0). `dataaddr' is the relocated address corresponding to
46  * the start of the data, and `len' is the number of bytes.
47  */
48 int
49 __elfN(reloc)(struct elf_file *ef, symaddr_fn *symaddr, const void *reldata,
50     int reltype, Elf_Addr relbase, Elf_Addr dataaddr, void *data, size_t len)
51 {
52 #if (defined(__aarch64__) || defined(__amd64__) || defined(__i386__)) && \
53     __ELF_WORD_SIZE == 64
54 	Elf64_Addr *where, val;
55 	Elf_Addr addend, addr;
56 	Elf_Size rtype;
57 #if defined(__amd64__) || defined(__i386__)
58 	Elf_Size symidx;
59 #endif
60 	const Elf_Rel *rel;
61 	const Elf_Rela *rela;
62 
63 	switch (reltype) {
64 	case ELF_RELOC_REL:
65 		rel = (const Elf_Rel *)reldata;
66 		where = (Elf_Addr *)((char *)data + relbase + rel->r_offset -
67 		    dataaddr);
68 		addend = 0;
69 		rtype = ELF_R_TYPE(rel->r_info);
70 #if defined(__amd64__) || defined(__i386__)
71 		symidx = ELF_R_SYM(rel->r_info);
72 #endif
73 		addend = 0;
74 		break;
75 	case ELF_RELOC_RELA:
76 		rela = (const Elf_Rela *)reldata;
77 		where = (Elf_Addr *)((char *)data + relbase + rela->r_offset -
78 		    dataaddr);
79 		addend = rela->r_addend;
80 		rtype = ELF_R_TYPE(rela->r_info);
81 #if defined(__amd64__) || defined(__i386__)
82 		symidx = ELF_R_SYM(rela->r_info);
83 #endif
84 		break;
85 	default:
86 		return (EINVAL);
87 	}
88 
89 	if ((char *)where < (char *)data || (char *)where >= (char *)data + len)
90 		return (0);
91 
92 	if (reltype == ELF_RELOC_REL)
93 		addend = *where;
94 
95 #if defined(__aarch64__)
96 #define	RELOC_RELATIVE		R_AARCH64_RELATIVE
97 #define	RELOC_IRELATIVE		R_AARCH64_IRELATIVE
98 #elif defined(__amd64__) || defined(__i386__)
99 /* XXX, definitions not available on i386. */
100 #define	R_X86_64_64		1
101 #define	R_X86_64_RELATIVE	8
102 #define	R_X86_64_IRELATIVE	37
103 
104 #define	RELOC_RELATIVE		R_X86_64_RELATIVE
105 #define	RELOC_IRELATIVE		R_X86_64_IRELATIVE
106 #endif
107 
108 	switch (rtype) {
109 	case RELOC_RELATIVE:
110 		addr = (Elf_Addr)addend + relbase;
111 		val = addr;
112 		memcpy(where, &val, sizeof(val));
113 		break;
114 	case RELOC_IRELATIVE:
115 		/* leave it to kernel */
116 		break;
117 #if defined(__amd64__) || defined(__i386__)
118 	case R_X86_64_64:		/* S + A */
119 		addr = symaddr(ef, symidx);
120 		if (addr == 0)
121 			return (ESRCH);
122 		val = addr + addend;
123 		*where = val;
124 		break;
125 #endif
126 	default:
127 		printf("\nunhandled relocation type %u\n", (u_int)rtype);
128 		return (EFTYPE);
129 	}
130 
131 	return (0);
132 #elif defined(__i386__) && __ELF_WORD_SIZE == 32
133 	Elf_Addr addend, addr, *where, val;
134 	Elf_Size rtype, symidx;
135 	const Elf_Rel *rel;
136 	const Elf_Rela *rela;
137 
138 	switch (reltype) {
139 	case ELF_RELOC_REL:
140 		rel = (const Elf_Rel *)reldata;
141 		where = (Elf_Addr *)((char *)data + relbase + rel->r_offset -
142 		    dataaddr);
143 		addend = 0;
144 		rtype = ELF_R_TYPE(rel->r_info);
145 		symidx = ELF_R_SYM(rel->r_info);
146 		addend = 0;
147 		break;
148 	case ELF_RELOC_RELA:
149 		rela = (const Elf_Rela *)reldata;
150 		where = (Elf_Addr *)((char *)data + relbase + rela->r_offset -
151 		    dataaddr);
152 		addend = rela->r_addend;
153 		rtype = ELF_R_TYPE(rela->r_info);
154 		symidx = ELF_R_SYM(rela->r_info);
155 		break;
156 	default:
157 		return (EINVAL);
158 	}
159 
160 	if ((char *)where < (char *)data || (char *)where >= (char *)data + len)
161 		return (0);
162 
163 	if (reltype == ELF_RELOC_REL)
164 		addend = *where;
165 
166 /* XXX, definitions not available on amd64. */
167 #define R_386_32	1	/* Add symbol value. */
168 #define R_386_GLOB_DAT	6	/* Set GOT entry to data address. */
169 #define R_386_RELATIVE	8	/* Add load address of shared object. */
170 #define	R_386_IRELATIVE	42
171 
172 	switch (rtype) {
173 	case R_386_RELATIVE:
174 		addr = addend + relbase;
175 		*where = addr;
176 		break;
177 	case R_386_32:		/* S + A */
178 		addr = symaddr(ef, symidx);
179 		if (addr == 0)
180 			return (ESRCH);
181 		val = addr + addend;
182 		*where = val;
183 		break;
184 	case R_386_IRELATIVE:
185 		/* leave it to kernel */
186 		break;
187 	default:
188 		printf("\nunhandled relocation type %u\n", (u_int)rtype);
189 		return (EFTYPE);
190 	}
191 
192 	return (0);
193 #elif defined(__powerpc__) || defined(__riscv)
194 	Elf_Size w;
195 	const Elf_Rela *rela;
196 
197 	switch (reltype) {
198 	case ELF_RELOC_RELA:
199 		rela = reldata;
200 		if (relbase + rela->r_offset >= dataaddr &&
201 		    relbase + rela->r_offset < dataaddr + len) {
202 			switch (ELF_R_TYPE(rela->r_info)) {
203 #if defined(__powerpc__)
204 			case R_PPC_RELATIVE:
205 #elif defined(__riscv)
206 			case R_RISCV_RELATIVE:
207 #endif
208 				w = relbase + rela->r_addend;
209 				bcopy(&w, (u_char *)data + (relbase +
210 				      rela->r_offset - dataaddr), sizeof(w));
211 				break;
212 			default:
213 				printf("\nunhandled relocation type %u\n",
214 				       (u_int)ELF_R_TYPE(rela->r_info));
215 				return (EFTYPE);
216 			}
217 		}
218 		break;
219 	}
220 
221 	return (0);
222 #else
223 	return (EOPNOTSUPP);
224 #endif
225 }
226