1/* $NetBSD: rmreloc.c,v 1.2 2006/06/25 21:32:41 christos Exp $ */
2
3/*
4 * Copyright 1996 John D. Polstra.
5 * Copyright 1996 Matt Thomas <matt@3am-software.com>
6 * Copyright 2002 Charles M. Hannum <root@ihack.net>
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 * 3. All advertising materials mentioning features or use of this software
18 *    must display the following acknowledgement:
19 *      This product includes software developed by John Polstra.
20 * 4. The name of the author may not be used to endorse or promote products
21 *    derived from this software without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
24 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
25 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
26 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
27 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
28 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
32 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 */
34/*
35 * rmreloc.c - relocate an ELFish RISC OS relocatable module.
36 */
37/*
38 * This code is a heavily hacked version of parts of:
39 * lib/libexec/ld.elf_so/headers.c
40 * lib/libexec/ld.elf_so/arch/arm/mdreloc.c
41 *
42 * At present it only deals with DT_REL tables containing R_ARM_NONE
43 * and R_ARM_RELATIVE relocations, because those are all that my
44 * linker emits.  More can be added as needed.  Note that this has to
45 * handle relocating already-relocated code, e.g. after *RMTidy, so
46 * most relocations have to reference oldbase, which ld.elf_so just
47 * assumes is zero.  There may be a cleverer way to do this.
48 */
49
50#include <sys/types.h>
51#include <sys/stdint.h>
52#include <lib/libsa/stand.h>
53#define ELFSIZE 32
54#include <sys/exec_elf.h>
55
56#include <riscoscalls.h>
57
58os_error *relocate_self(Elf_Dyn *, void *, void *);
59
60#define assert(x) /* nothing */
61
62/*
63 * While relocating ourselves, we must not refer to any global variables.
64 * This includes _DYNAMIC -- the startup code finds it for us and passes
65 * it to us along with the base address of the module.
66 */
67
68typedef struct {
69	void *        relocbase;	/* Reloc const = mapbase - *vaddrbase */
70	Elf_Dyn        *dynamic;	/* Dynamic section */
71	const Elf_Rel  *rel;		/* Relocation entries */
72	const Elf_Rel  *rellim;		/* Limit of Relocation entries */
73} Obj_Entry;
74
75#define rdbg(x) /* nothing */
76
77/*
78 * It is possible for the compiler to emit relocations for unaligned data.
79 * We handle this situation with these inlines.
80 */
81#define	RELOC_ALIGNED_P(x) \
82	(((uintptr_t)(x) & (sizeof(void *) - 1)) == 0)
83
84static inline Elf_Addr
85load_ptr(void *where)
86{
87	Elf_Addr res;
88
89	memcpy(&res, where, sizeof(res));
90
91	return (res);
92}
93
94static inline void
95store_ptr(void *where, Elf_Addr val)
96{
97
98	memcpy(where, &val, sizeof(val));
99}
100
101static struct os_error bad_reloc = {
102	0, "Unhandled ELF redirection"
103};
104
105os_error *
106relocate_self(Elf_Dyn *dynamic, void *oldbase, void *newbase)
107{
108	Elf_Dyn *dynp;
109	Obj_Entry o = { 0 };
110	Obj_Entry *obj;
111	const Elf_Rel *rel;
112	Elf_Addr        relsz = 0;
113
114	obj = &o; obj->dynamic = dynamic; obj->relocbase = newbase;
115
116	for (dynp = obj->dynamic; dynp->d_tag != DT_NULL; ++dynp) {
117		switch (dynp->d_tag) {
118		case DT_REL:
119			obj->rel = (const Elf_Rel *)
120			    (obj->relocbase + dynp->d_un.d_ptr);
121			break;
122		case DT_RELSZ:
123			relsz = dynp->d_un.d_val;
124			break;
125		case DT_RELENT:
126			assert(dynp->d_un.d_val == sizeof(Elf_Rel));
127			break;
128		}
129	}
130
131	obj->rellim = (const Elf_Rel *)((void *)obj->rel + relsz);
132
133	for (rel = obj->rel; rel < obj->rellim; rel++) {
134		Elf_Addr        *where;
135		Elf_Addr         tmp;
136
137		where = (Elf_Addr *)(obj->relocbase + rel->r_offset);
138
139		switch (ELF_R_TYPE(rel->r_info)) {
140		case R_TYPE(NONE):
141			break;
142
143		case R_TYPE(RELATIVE):	/* word32 B + A */
144			if (__predict_true(RELOC_ALIGNED_P(where))) {
145				tmp = *where + (Elf_Addr)obj->relocbase -
146				    (Elf_Addr)oldbase;
147				*where = tmp;
148			} else {
149				tmp = load_ptr(where) +
150				    (Elf_Addr)obj->relocbase -
151				    (Elf_Addr)oldbase;
152				store_ptr(where, tmp);
153			}
154			rdbg(("RELATIVE in %s --> %p", obj->path,
155			    (void *)tmp));
156			break;
157
158		default:
159			return &bad_reloc;
160		}
161	}
162	return NULL;
163}
164