]>
Commit | Line | Data |
---|---|---|
01f3e03b RM |
1 | /* Machine-dependent ELF dynamic relocation inline functions. m68k version. |
2 | Copyright (C) 1996 Free Software Foundation, Inc. | |
3 | This file is part of the GNU C Library. | |
4 | ||
5 | The GNU C Library is free software; you can redistribute it and/or | |
6 | modify it under the terms of the GNU Library General Public License as | |
7 | published by the Free Software Foundation; either version 2 of the | |
8 | License, or (at your option) any later version. | |
9 | ||
10 | The GNU C Library is distributed in the hope that it will be useful, | |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
13 | Library General Public License for more details. | |
14 | ||
15 | You should have received a copy of the GNU Library General Public | |
16 | License along with the GNU C Library; see the file COPYING.LIB. If | |
17 | not, write to the Free Software Foundation, Inc., 675 Mass Ave, | |
18 | Cambridge, MA 02139, USA. */ | |
19 | ||
20 | #define ELF_MACHINE_NAME "m68k" | |
21 | ||
22 | #include <assert.h> | |
23 | #include <string.h> | |
24 | #include <link.h> | |
25 | ||
26 | ||
27 | /* Return nonzero iff E_MACHINE is compatible with the running host. */ | |
28 | static inline int | |
29 | elf_machine_matches_host (Elf32_Half e_machine) | |
30 | { | |
31 | switch (e_machine) | |
32 | { | |
33 | case EM_68K: | |
34 | return 1; | |
35 | default: | |
36 | return 0; | |
37 | } | |
38 | } | |
39 | ||
40 | ||
41 | /* Return the run-time address of the _GLOBAL_OFFSET_TABLE_. | |
42 | Must be inlined in a function which uses global data. */ | |
43 | static inline Elf32_Addr * | |
44 | elf_machine_got (void) | |
45 | { | |
46 | register Elf32_Addr *got asm ("%a5"); | |
47 | return got; | |
48 | } | |
49 | ||
50 | ||
51 | /* Return the run-time load address of the shared object. */ | |
52 | static inline Elf32_Addr | |
53 | elf_machine_load_address (void) | |
54 | { | |
be10a868 RM |
55 | Elf32_Addr addr; |
56 | asm ("here: lea here(%%pc), %0\n" | |
57 | " sub.l %#here, %0" | |
58 | : "=a" (addr)); | |
59 | return addr; | |
01f3e03b RM |
60 | } |
61 | ||
be10a868 RM |
62 | /* The `subl' insn above will contain an R_68K_RELATIVE relocation |
63 | entry intended to insert the run-time address of the label `here'. | |
64 | This will be the first relocation in the text of the dynamic | |
65 | linker; we skip it to avoid trying to modify read-only text in this | |
66 | early stage. */ | |
67 | #define ELF_MACHINE_BEFORE_RTLD_RELOC(dynamic_info) \ | |
68 | ((dynamic_info)[DT_RELA]->d_un.d_ptr += sizeof (Elf32_Rela), \ | |
69 | (dynamic_info)[DT_RELASZ]->d_un.d_val -= sizeof (Elf32_Rela)) | |
70 | ||
01f3e03b RM |
71 | /* Perform the relocation specified by RELOC and SYM (which is fully resolved). |
72 | MAP is the object containing the reloc. */ | |
73 | ||
74 | static inline void | |
75 | elf_machine_rela (struct link_map *map, | |
1c2f546b | 76 | const Elf32_Rela *reloc, const Elf32_Sym *sym, |
710f7bab RM |
77 | Elf32_Addr (*resolve) (const Elf32_Sym **ref, |
78 | Elf32_Addr reloc_addr, | |
79 | int noplt)) | |
01f3e03b RM |
80 | { |
81 | Elf32_Addr *const reloc_addr = (void *) (map->l_addr + reloc->r_offset); | |
710f7bab | 82 | Elf32_Addr loadbase; |
01f3e03b RM |
83 | |
84 | switch (ELF32_R_TYPE (reloc->r_info)) | |
85 | { | |
86 | case R_68K_COPY: | |
710f7bab RM |
87 | loadbase = (*resolve) (&sym, (Elf32_Addr) reloc_addr, 0); |
88 | memcpy (reloc_addr, (void *) (loadbase + sym->st_value), sym->st_size); | |
01f3e03b RM |
89 | break; |
90 | case R_68K_GLOB_DAT: | |
710f7bab RM |
91 | loadbase = (resolve ? (*resolve) (&sym, (Elf32_Addr) reloc_addr, 0) : |
92 | /* RESOLVE is null during bootstrap relocation. */ | |
93 | map->l_addr); | |
94 | *reloc_addr = sym ? (loadbase + sym->st_value) : 0; | |
95 | break; | |
01f3e03b | 96 | case R_68K_JMP_SLOT: |
710f7bab RM |
97 | loadbase = (resolve ? (*resolve) (&sym, (Elf32_Addr) reloc_addr, 1) : |
98 | /* RESOLVE is null during bootstrap relocation. */ | |
99 | map->l_addr); | |
100 | *reloc_addr = sym ? (loadbase + sym->st_value) : 0; | |
01f3e03b RM |
101 | break; |
102 | case R_68K_8: | |
710f7bab RM |
103 | loadbase = (*resolve) (&sym, (Elf32_Addr) reloc_addr, 0); |
104 | *(char *) reloc_addr = ((sym ? (loadbase + sym->st_value) : 0) | |
105 | + reloc->r_addend); | |
01f3e03b RM |
106 | break; |
107 | case R_68K_16: | |
710f7bab RM |
108 | loadbase = (*resolve) (&sym, (Elf32_Addr) reloc_addr, 0); |
109 | *(short *) reloc_addr = ((sym ? (loadbase + sym->st_value) : 0) | |
110 | + reloc->r_addend); | |
01f3e03b RM |
111 | break; |
112 | case R_68K_32: | |
1c2f546b RM |
113 | loadbase = (resolve ? (*resolve) (&sym, (Elf32_Addr) reloc_addr, 0) : |
114 | /* RESOLVE is null during bootstrap relocation. */ | |
115 | map->l_addr); | |
710f7bab RM |
116 | *reloc_addr = ((sym ? (loadbase + sym->st_value) : 0) |
117 | + reloc->r_addend); | |
01f3e03b RM |
118 | break; |
119 | case R_68K_RELATIVE: | |
120 | *reloc_addr = map->l_addr + reloc->r_addend; | |
121 | break; | |
122 | case R_68K_PC8: | |
710f7bab RM |
123 | loadbase = (*resolve) (&sym, (Elf32_Addr) reloc_addr, 0); |
124 | *(char *) reloc_addr = ((sym ? (loadbase + sym->st_value) : 0) | |
125 | + reloc->r_addend | |
01f3e03b RM |
126 | - (Elf32_Addr) reloc_addr); |
127 | break; | |
128 | case R_68K_PC16: | |
710f7bab RM |
129 | loadbase = (*resolve) (&sym, (Elf32_Addr) reloc_addr, 0); |
130 | *(short *) reloc_addr = ((sym ? (loadbase + sym->st_value) : 0) | |
131 | + reloc->r_addend | |
01f3e03b RM |
132 | - (Elf32_Addr) reloc_addr); |
133 | break; | |
134 | case R_68K_PC32: | |
710f7bab RM |
135 | loadbase = (*resolve) (&sym, (Elf32_Addr) reloc_addr, 0); |
136 | *reloc_addr = ((sym ? (loadbase + sym->st_value) : 0) | |
137 | + reloc->r_addend | |
138 | - (Elf32_Addr) reloc_addr); | |
01f3e03b RM |
139 | break; |
140 | case R_68K_NONE: /* Alright, Wilbur. */ | |
141 | break; | |
142 | default: | |
143 | assert (! "unexpected dynamic reloc type"); | |
144 | break; | |
145 | } | |
146 | } | |
147 | ||
148 | static inline void | |
149 | elf_machine_lazy_rel (struct link_map *map, const Elf32_Rela *reloc) | |
150 | { | |
151 | Elf32_Addr *const reloc_addr = (void *) (map->l_addr + reloc->r_offset); | |
152 | switch (ELF32_R_TYPE (reloc->r_info)) | |
153 | { | |
154 | case R_68K_NONE: | |
155 | break; | |
156 | case R_68K_JMP_SLOT: | |
157 | *reloc_addr += map->l_addr; | |
158 | break; | |
159 | default: | |
160 | assert (! "unexpected PLT reloc type"); | |
161 | break; | |
162 | } | |
163 | } | |
164 | ||
6c03c2cf RM |
165 | /* Nonzero iff TYPE describes relocation of a PLT entry, so |
166 | PLT entries should not be allowed to define the value. */ | |
167 | #define elf_machine_pltrel_p(type) ((type) == R_68K_JMP_SLOT) | |
168 | ||
01f3e03b RM |
169 | /* The m68k never uses Elf32_Rel relocations. */ |
170 | #define ELF_MACHINE_NO_REL 1 | |
171 | ||
172 | ||
173 | /* Set up the loaded object described by L so its unrelocated PLT | |
174 | entries will jump to the on-demand fixup code in dl-runtime.c. */ | |
175 | ||
176 | static inline void | |
177 | elf_machine_runtime_setup (struct link_map *l, int lazy) | |
178 | { | |
179 | Elf32_Addr *got; | |
180 | extern void _dl_runtime_resolve (Elf32_Word); | |
181 | ||
182 | if (l->l_info[DT_JMPREL] && lazy) | |
183 | { | |
184 | /* The GOT entries for functions in the PLT have not yet been | |
185 | filled in. Their initial contents will arrange when called | |
186 | to push an offset into the .rela.plt section, push | |
187 | _GLOBAL_OFFSET_TABLE_[1], and then jump to | |
188 | _GLOBAL_OFFSET_TABLE_[2]. */ | |
189 | got = (Elf32_Addr *) (l->l_addr + l->l_info[DT_PLTGOT]->d_un.d_ptr); | |
190 | got[1] = (Elf32_Addr) l; /* Identify this shared object. */ | |
191 | /* This function will get called to fix up the GOT entry | |
192 | indicated by the offset on the stack, and then jump to the | |
193 | resolved address. */ | |
194 | got[2] = (Elf32_Addr) &_dl_runtime_resolve; | |
195 | } | |
196 | ||
197 | /* This code is used in dl-runtime.c to call the `fixup' function | |
198 | and then redirect to the address it returns. */ | |
199 | #define ELF_MACHINE_RUNTIME_TRAMPOLINE asm ("\ | |
200 | | Trampoline for _dl_runtime_resolver | |
201 | .globl _dl_runtime_resolve | |
202 | .type _dl_runtime_resolve, @function | |
203 | _dl_runtime_resolve: | |
be10a868 | 204 | | Save %a0 (struct return address) and %a1. |
01f3e03b | 205 | move.l %a0, -(%sp) |
be10a868 | 206 | move.l %a1, -(%sp) |
01f3e03b | 207 | | Call the real address resolver. |
be10a868 RM |
208 | jbsr fixup |
209 | | Restore register %a0 and %a1. | |
210 | move.l (%sp)+, %a1 | |
01f3e03b RM |
211 | move.l (%sp)+, %a0 |
212 | | Pop parameters | |
213 | addq.l #8, %sp | |
214 | | Call real function. | |
215 | jmp (%d0) | |
216 | .size _dl_runtime_resolve, . - _dl_runtime_resolve | |
217 | "); | |
be10a868 RM |
218 | #define ELF_MACHINE_RUNTIME_FIXUP_ARGS long int save_a0, long int save_a1 |
219 | /* The PLT uses Elf32_Rela relocs. */ | |
220 | #define elf_machine_relplt elf_machine_rela | |
01f3e03b RM |
221 | } |
222 | ||
223 | ||
224 | /* Mask identifying addresses reserved for the user program, | |
225 | where the dynamic linker should not map anything. */ | |
226 | #define ELF_MACHINE_USER_ADDRESS_MASK 0x80000000UL | |
227 | ||
228 | /* Initial entry point code for the dynamic linker. | |
229 | The C function `_dl_start' is the real entry point; | |
230 | its return value is the user program's entry point. */ | |
231 | ||
232 | #define RTLD_START asm ("\ | |
233 | .text | |
234 | .globl _start | |
235 | .globl _dl_start_user | |
236 | _start: | |
8d6468d0 | 237 | move.l %sp, -(%sp) |
01f3e03b | 238 | jbsr _dl_start |
8d6468d0 | 239 | addq.l #4, %sp |
01f3e03b RM |
240 | _dl_start_user: |
241 | | Save the user entry point address in %a4. | |
242 | move.l %d0, %a4 | |
243 | | Point %a5 at the GOT. | |
244 | lea _GLOBAL_OFFSET_TABLE_@GOTPC(%pc), %a5 | |
245 | | See if we were run as a command with the executable file | |
246 | | name as an extra leading argument. | |
247 | move.l ([_dl_skip_args@GOT, %a5]), %d0 | |
248 | jeq 0f | |
249 | | Pop the original argument count | |
250 | move.l (%sp)+, %d1 | |
251 | | Subtract _dl_skip_args from it. | |
252 | sub.l %d0, %d1 | |
253 | | Adjust the stack pointer to skip _dl_skip_args words. | |
254 | lea (%sp, %d0*4), %sp | |
255 | | Push back the modified argument count. | |
256 | move.l %d1, -(%sp) | |
ba79d61b RM |
257 | 0: | Push _dl_default_scope[2] as argument in _dl_init_next call below. |
258 | move.l ([_dl_default_scope@GOT, %a5]), %d2 | |
259 | 0: move.l (%d2, 8), -(%sp) | |
01f3e03b RM |
260 | | Call _dl_init_next to return the address of an initializer |
261 | | function to run. | |
f68b86cc RM |
262 | bsr.l _dl_init_next@PLTPC |
263 | add.l #4, %sp | Pop argument. | |
01f3e03b RM |
264 | | Check for zero return, when out of initializers. |
265 | tst.l %d0 | |
266 | jeq 1f | |
267 | | Call the shared object initializer function. | |
f68b86cc | 268 | | NOTE: We depend only on the registers (%d2, %a4 and %a5) |
01f3e03b RM |
269 | | and the return address pushed by this call; |
270 | | the initializer is called with the stack just | |
271 | | as it appears on entry, and it is free to move | |
272 | | the stack around, as long as it winds up jumping to | |
273 | | the return address on the top of the stack. | |
274 | move.l %d0, %a0 | |
275 | jsr (%a0) | |
276 | | Loop to call _dl_init_next for the next initializer. | |
277 | jra 0b | |
278 | 1: | Pass our finalizer function to the user in %a1. | |
279 | move.l _dl_fini@GOT(%a5), %a1 | |
280 | | Initialize %fp with the stack pointer. | |
281 | move.l %sp, %fp | |
282 | | Jump to the user's entry point. | |
283 | jmp (%a4)"); |