1 /* $NetBSD: rmreloc.c,v 1.3 2007/03/04 05:59:07 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
58 os_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
68 typedef 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
84 static inline Elf_Addr
load_ptr(void * where)85 load_ptr(void *where)
86 {
87 Elf_Addr res;
88
89 memcpy(&res, where, sizeof(res));
90
91 return (res);
92 }
93
94 static inline void
store_ptr(void * where,Elf_Addr val)95 store_ptr(void *where, Elf_Addr val)
96 {
97
98 memcpy(where, &val, sizeof(val));
99 }
100
101 static struct os_error bad_reloc = {
102 0, "Unhandled ELF redirection"
103 };
104
105 os_error *
relocate_self(Elf_Dyn * dynamic,void * oldbase,void * newbase)106 relocate_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