xref: /openbsd/libexec/ld.so/mips64/rtld_machine.c (revision 404b540a)
1 /*	$OpenBSD: rtld_machine.c,v 1.11 2008/04/09 21:45:26 kurt Exp $ */
2 
3 /*
4  * Copyright (c) 1998-2004 Opsycon AB, Sweden.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
16  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
19  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  *
27  */
28 
29 #define _DYN_LOADER
30 
31 #include <sys/types.h>
32 #include <sys/mman.h>
33 
34 #include <link.h>
35 #include "resolve.h"
36 #include "syscall.h"
37 #include "archdep.h"
38 
39 int
40 _dl_md_reloc(elf_object_t *object, int rel, int relsz)
41 {
42 	int	i;
43 	int	numrel;
44 	int	fails = 0;
45 	struct load_list *load_list;
46 	Elf64_Addr loff;
47 	Elf64_Addr ooff;
48 	Elf64_Addr got_start, got_end;
49 	Elf64_Rel  *relocs;
50 	const Elf64_Sym *sym, *this;
51 
52 	loff = object->obj_base;
53 	numrel = object->Dyn.info[relsz] / sizeof(Elf64_Rel);
54 	relocs = (Elf64_Rel *)(object->Dyn.info[rel]);
55 
56 	if (relocs == NULL)
57 		return(0);
58 
59 	/*
60 	 * Change protection of all write protected segments in the
61 	 * object so we can do relocations in the .rodata section.
62 	 * After relocation restore protection.
63 	 */
64 	load_list = object->load_list;
65 	while (load_list != NULL) {
66 		if ((load_list->prot & PROT_WRITE) == 0)
67 			_dl_mprotect(load_list->start, load_list->size,
68 			    load_list->prot|PROT_WRITE);
69 		load_list = load_list->next;
70 	}
71 
72 	/* XXX We need the got limits to know if reloc is in got. */
73 	/* XXX Relocs against the got should not include the STUB address! */
74 	this = NULL;
75 	got_start = 0;
76 	got_end = 0;
77 	ooff = _dl_find_symbol("__got_start", &this,
78 	    SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL, object, NULL);
79 	if (this != NULL)
80 		got_start = ooff + this->st_value;
81 	this = NULL;
82 	ooff = _dl_find_symbol("__got_end", &this,
83 	    SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL, object, NULL);
84 	if (this != NULL)
85 		got_end = ooff + this->st_value;
86 
87 	DL_DEB(("relocating %d\n", numrel));
88 	for (i = 0; i < numrel; i++, relocs++) {
89 		Elf64_Addr r_addr = relocs->r_offset + loff;
90 		const char *symn;
91 		int type;
92 
93 		if (ELF64_R_SYM(relocs->r_info) == 0xffffff)
94 			continue;
95 
96 		ooff = 0;
97 		sym = object->dyn.symtab;
98 		sym += ELF64_R_SYM(relocs->r_info);
99 		symn = object->dyn.strtab + sym->st_name;
100 		type = ELF64_R_TYPE(relocs->r_info);
101 
102 		this = NULL;
103 		if (ELF64_R_SYM(relocs->r_info) &&
104 		    !(ELF64_ST_BIND(sym->st_info) == STB_LOCAL &&
105 		    ELF64_ST_TYPE (sym->st_info) == STT_NOTYPE)) {
106 			ooff = _dl_find_symbol(symn, &this,
107 			SYM_SEARCH_ALL | SYM_WARNNOTFOUND | SYM_PLT,
108 			sym, object, NULL);
109 
110 			if (this == NULL) {
111 				if (ELF_ST_BIND(sym->st_info) != STB_WEAK)
112 					fails++;
113 				continue;
114 			}
115 		}
116 
117 		switch (ELF64_R_TYPE(relocs->r_info)) {
118 			/* XXX Handle non aligned relocs. .eh_frame
119 			 * XXX in libstdc++ seems to have them... */
120 			u_int64_t robj;
121 
122 		case R_MIPS_REL32_64:
123 			if (ELF64_ST_BIND(sym->st_info) == STB_LOCAL &&
124 			    (ELF64_ST_TYPE(sym->st_info) == STT_SECTION ||
125 			    ELF64_ST_TYPE(sym->st_info) == STT_NOTYPE) ) {
126 				if ((long)r_addr & 7) {
127 					_dl_bcopy((char *)r_addr, &robj, sizeof(robj));
128 					robj += loff + sym->st_value;
129 					_dl_bcopy(&robj, (char *)r_addr, sizeof(robj));
130 				} else {
131 					*(u_int64_t *)r_addr += loff + sym->st_value;
132 				}
133 			} else if (this && ((long)r_addr & 7)) {
134 				_dl_bcopy((char *)r_addr, &robj, sizeof(robj));
135 				robj += this->st_value + ooff;
136 				_dl_bcopy(&robj, (char *)r_addr, sizeof(robj));
137 			} else if (this) {
138 				*(u_int64_t *)r_addr += this->st_value + ooff;
139 			}
140 			break;
141 
142 		case R_MIPS_NONE:
143 			break;
144 
145 		default:
146 			_dl_printf("%s: unsupported relocation '%d'\n",
147 			    _dl_progname, ELF64_R_TYPE(relocs->r_info));
148 			_dl_exit(1);
149 		}
150 	}
151 	DL_DEB(("done %d fails\n", fails));
152 	load_list = object->load_list;
153 	while (load_list != NULL) {
154 		if ((load_list->prot & PROT_WRITE) == 0)
155 			_dl_mprotect(load_list->start, load_list->size,
156 			    load_list->prot);
157 		load_list = load_list->next;
158 	}
159 	return(fails);
160 }
161 
162 /*
163  *	Relocate the Global Offset Table (GOT). Currently we don't
164  *	do lazy evaluation here because the GNU linker doesn't
165  *	follow the ABI spec which says that if an external symbol
166  *	is referenced by other relocations than CALL16 and 26 it
167  *	should not be given a stub and have a zero value in the
168  *	symbol table. By not doing so, we can't use pointers to
169  *	external functions and use them in comparisons...
170  */
171 int
172 _dl_md_reloc_got(elf_object_t *object, int lazy)
173 {
174 	int	i, n;
175 	Elf64_Addr loff;
176 	Elf64_Addr ooff;
177 	Elf64_Addr *gotp;
178 	const Elf64_Sym  *symp;
179 	const Elf64_Sym  *this;
180 	const char *strt;
181 
182 	if (object->status & STAT_GOT_DONE)
183 		return (0);
184 
185 	lazy = 0;	/* XXX Fix ld before enabling lazy */
186 	loff = object->obj_base;
187 	strt = object->dyn.strtab;
188 	gotp = object->dyn.pltgot;
189 	n = object->Dyn.info[DT_MIPS_LOCAL_GOTNO - DT_LOPROC + DT_NUM];
190 
191 	DL_DEB(("loff: '%p'\n", loff));
192 	/*
193 	 *  Set up pointers for run time (lazy) resolving.
194 	 */
195 	gotp[0] = (long)_dl_rt_resolve;
196 	if (gotp[1] & 0x0000000080000000) {
197 		gotp[1] = (long)object | 0x0000000080000000;
198 	}
199 
200 	/*  First do all local references. */
201 	for (i = ((gotp[1] & 0x0000000080000000) ? 2 : 1); i < n; i++) {
202 		gotp[i] += loff;
203 	}
204 
205 	gotp += n;
206 
207 	symp =  object->dyn.symtab;
208 	symp += object->Dyn.info[DT_MIPS_GOTSYM - DT_LOPROC + DT_NUM];
209 	n =  object->Dyn.info[DT_MIPS_SYMTABNO - DT_LOPROC + DT_NUM] -
210 	    object->Dyn.info[DT_MIPS_GOTSYM - DT_LOPROC + DT_NUM];
211 
212 	this = NULL;
213 	object->plt_size = 0;
214 	object->got_size = 0;
215 	ooff = _dl_find_symbol("__got_start", &this,
216 	    SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL, object, NULL);
217 	if (this != NULL)
218 		object->got_start = ooff + this->st_value;
219 
220 	this = NULL;
221 	ooff = _dl_find_symbol("__got_end", &this,
222 	    SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL, object, NULL);
223 	if (this != NULL)
224 		object->got_size = ooff + this->st_value  - object->got_start;
225 
226 	/*
227 	 *  Then do all global references according to the ABI.
228 	 *  Quickstart is not yet implemented.
229 	 */
230 	while (n--) {
231 		if (symp->st_shndx == SHN_UNDEF &&
232 		    ELF64_ST_TYPE(symp->st_info) == STT_FUNC) {
233 			if (symp->st_value == 0 || !lazy) {
234 				this = 0;
235 				ooff = _dl_find_symbol(strt + symp->st_name,
236 				    &this,
237 				    SYM_SEARCH_ALL|SYM_NOWARNNOTFOUND|SYM_PLT,
238 				    symp, object, NULL);
239 				if (this)
240 					*gotp = this->st_value + ooff;
241 			} else
242 				*gotp = symp->st_value + ooff;
243 		} else if (symp->st_shndx == SHN_COMMON ||
244 			symp->st_shndx == SHN_UNDEF) {
245 			this = 0;
246 			ooff = _dl_find_symbol(strt + symp->st_name, &this,
247 			    SYM_SEARCH_ALL|SYM_NOWARNNOTFOUND|SYM_PLT,
248 			    symp, object, NULL);
249 			if (this)
250 				*gotp = this->st_value + ooff;
251 		} else if (ELF64_ST_TYPE(symp->st_info) == STT_FUNC &&
252 			symp->st_value != *gotp) {
253 			*gotp += loff;
254 		} else {	/* Resolve all others immediately */
255 			this = 0;
256 			ooff = _dl_find_symbol(strt + symp->st_name, &this,
257 			    SYM_SEARCH_ALL|SYM_NOWARNNOTFOUND|SYM_PLT,
258 			    symp, object, NULL);
259 			if (this)
260 				*gotp = this->st_value + ooff;
261 			else
262 				*gotp = symp->st_value + loff;
263 		}
264 		gotp++;
265 		symp++;
266 	}
267 	object->status |= STAT_GOT_DONE;
268 
269 	DL_DEB(("got: %x, %x\n", object->got_start, object->got_size));
270 	if (object->got_size != 0)
271 		_dl_mprotect((void*)object->got_start, object->got_size,
272 		    PROT_READ);
273 
274 	return (0);
275 }
276