]>
Commit | Line | Data |
---|---|---|
d66e34cd | 1 | /* Machine-dependent ELF dynamic relocation inline functions. i386 version. |
a2e1b046 | 2 | Copyright (C) 1995, 1996 Free Software Foundation, Inc. |
d66e34cd RM |
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 "i386" | |
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_386: | |
34 | case EM_486: | |
35 | return 1; | |
36 | default: | |
37 | return 0; | |
38 | } | |
39 | } | |
40 | ||
41 | ||
42 | /* Return the run-time address of the _GLOBAL_OFFSET_TABLE_. | |
43 | Must be inlined in a function which uses global data. */ | |
44 | static inline Elf32_Addr * | |
45 | elf_machine_got (void) | |
46 | { | |
47 | register Elf32_Addr *got asm ("%ebx"); | |
48 | return got; | |
49 | } | |
50 | ||
51 | ||
52 | /* Return the run-time load address of the shared object. */ | |
53 | static inline Elf32_Addr | |
54 | elf_machine_load_address (void) | |
55 | { | |
56 | Elf32_Addr addr; | |
57 | asm (" call here\n" | |
58 | "here: popl %0\n" | |
59 | " subl $here, %0" | |
60 | : "=r" (addr)); | |
61 | return addr; | |
62 | } | |
63 | /* The `subl' insn above will contain an R_386_32 relocation entry | |
64 | intended to insert the run-time address of the label `here'. | |
65 | This will be the first relocation in the text of the dynamic linker; | |
66 | we skip it to avoid trying to modify read-only text in this early stage. */ | |
67 | #define ELF_MACHINE_BEFORE_RTLD_RELOC(dynamic_info) \ | |
44c8d1a2 | 68 | ++(const Elf32_Rel *) (dynamic_info)[DT_REL]->d_un.d_ptr; \ |
a993273c | 69 | (dynamic_info)[DT_RELSZ]->d_un.d_val -= sizeof (Elf32_Rel); |
d66e34cd RM |
70 | |
71 | /* Perform the relocation specified by RELOC and SYM (which is fully resolved). | |
421f82e5 | 72 | MAP is the object containing the reloc. */ |
d66e34cd RM |
73 | |
74 | static inline void | |
421f82e5 | 75 | elf_machine_rel (struct link_map *map, |
710f7bab RM |
76 | const Elf32_Rel *reloc, const Elf32_Sym *sym, |
77 | Elf32_Addr (*resolve) (const Elf32_Sym **ref, | |
78 | Elf32_Addr reloc_addr, | |
79 | int noplt)) | |
d66e34cd | 80 | { |
a1a9d215 | 81 | Elf32_Addr *const reloc_addr = (void *) (map->l_addr + reloc->r_offset); |
710f7bab | 82 | Elf32_Addr loadbase; |
d66e34cd RM |
83 | |
84 | switch (ELF32_R_TYPE (reloc->r_info)) | |
85 | { | |
86 | case R_386_COPY: | |
710f7bab RM |
87 | loadbase = (*resolve) (&sym, (Elf32_Addr) reloc_addr, 0); |
88 | memcpy (reloc_addr, (void *) (loadbase + sym->st_value), sym->st_size); | |
d66e34cd RM |
89 | break; |
90 | case R_386_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; | |
d66e34cd | 96 | case R_386_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; | |
d66e34cd RM |
101 | break; |
102 | case R_386_32: | |
5bf62f2d RM |
103 | if (map->l_type == lt_interpreter) |
104 | { | |
105 | /* Undo the relocation done here during bootstrapping. Now we will | |
106 | relocate it anew, possibly using a binding found in the user | |
107 | program or a loaded library rather than the dynamic linker's | |
108 | built-in definitions used while loading those libraries. */ | |
109 | const Elf32_Sym *const dlsymtab | |
110 | = (void *) (map->l_addr + map->l_info[DT_SYMTAB]->d_un.d_ptr); | |
111 | *reloc_addr -= (map->l_addr + | |
112 | dlsymtab[ELF32_R_SYM (reloc->r_info)].st_value); | |
113 | } | |
aa592a63 RM |
114 | loadbase = (resolve ? (*resolve) (&sym, (Elf32_Addr) reloc_addr, 0) : |
115 | /* RESOLVE is null during bootstrap relocation. */ | |
116 | map->l_addr); | |
710f7bab | 117 | *reloc_addr += sym ? (loadbase + sym->st_value) : 0; |
d66e34cd RM |
118 | break; |
119 | case R_386_RELATIVE: | |
5bf62f2d RM |
120 | if (map->l_type != lt_interpreter) /* Already done in dynamic linker. */ |
121 | *reloc_addr += map->l_addr; | |
d66e34cd RM |
122 | break; |
123 | case R_386_PC32: | |
aa592a63 RM |
124 | loadbase = (resolve ? (*resolve) (&sym, (Elf32_Addr) reloc_addr, 0) : |
125 | /* RESOLVE is null during bootstrap relocation. */ | |
126 | map->l_addr); | |
710f7bab RM |
127 | *reloc_addr += ((sym ? (loadbase + sym->st_value) : 0) - |
128 | (Elf32_Addr) reloc_addr); | |
d66e34cd | 129 | break; |
86fe2915 RM |
130 | case R_386_NONE: /* Alright, Wilbur. */ |
131 | break; | |
d66e34cd RM |
132 | default: |
133 | assert (! "unexpected dynamic reloc type"); | |
134 | break; | |
135 | } | |
136 | } | |
137 | ||
a1a9d215 RM |
138 | static inline void |
139 | elf_machine_lazy_rel (struct link_map *map, const Elf32_Rel *reloc) | |
140 | { | |
141 | Elf32_Addr *const reloc_addr = (void *) (map->l_addr + reloc->r_offset); | |
142 | switch (ELF32_R_TYPE (reloc->r_info)) | |
143 | { | |
144 | case R_386_JMP_SLOT: | |
145 | *reloc_addr += map->l_addr; | |
146 | break; | |
147 | default: | |
148 | assert (! "unexpected PLT reloc type"); | |
149 | break; | |
150 | } | |
151 | } | |
d66e34cd | 152 | |
6c03c2cf RM |
153 | /* Nonzero iff TYPE describes relocation of a PLT entry, so |
154 | PLT entries should not be allowed to define the value. */ | |
155 | #define elf_machine_pltrel_p(type) ((type) == R_386_JMP_SLOT) | |
156 | ||
d66e34cd | 157 | /* The i386 never uses Elf32_Rela relocations. */ |
421f82e5 | 158 | #define ELF_MACHINE_NO_RELA 1 |
d66e34cd RM |
159 | |
160 | ||
161 | /* Set up the loaded object described by L so its unrelocated PLT | |
162 | entries will jump to the on-demand fixup code in dl-runtime.c. */ | |
163 | ||
164 | static inline void | |
a2e1b046 | 165 | elf_machine_runtime_setup (struct link_map *l, int lazy) |
d66e34cd | 166 | { |
a1a9d215 | 167 | Elf32_Addr *got; |
d66e34cd | 168 | extern void _dl_runtime_resolve (Elf32_Word); |
a1a9d215 | 169 | |
a2e1b046 RM |
170 | if (l->l_info[DT_JMPREL] && lazy) |
171 | { | |
172 | /* The GOT entries for functions in the PLT have not yet been filled | |
173 | in. Their initial contents will arrange when called to push an | |
174 | offset into the .rel.plt section, push _GLOBAL_OFFSET_TABLE_[1], | |
175 | and then jump to _GLOBAL_OFFSET_TABLE[2]. */ | |
176 | got = (Elf32_Addr *) (l->l_addr + l->l_info[DT_PLTGOT]->d_un.d_ptr); | |
177 | got[1] = (Elf32_Addr) l; /* Identify this shared object. */ | |
178 | /* This function will get called to fix up the GOT entry indicated by | |
179 | the offset on the stack, and then jump to the resolved address. */ | |
180 | got[2] = (Elf32_Addr) &_dl_runtime_resolve; | |
181 | } | |
d66e34cd | 182 | |
38334018 RM |
183 | /* This code is used in dl-runtime.c to call the `fixup' function |
184 | and then redirect to the address it returns. */ | |
185 | #define ELF_MACHINE_RUNTIME_TRAMPOLINE asm ("\ | |
186 | .globl _dl_runtime_resolve | |
187 | .type _dl_runtime_resolve, @function | |
188 | _dl_runtime_resolve: | |
189 | call fixup # Args pushed by PLT. | |
190 | addl $8, %esp # Pop args. | |
191 | jmp *%eax # Jump to function address. | |
192 | "); | |
193 | /* The PLT uses Elf32_Rel relocs. */ | |
194 | #define elf_machine_relplt elf_machine_rel | |
195 | } | |
d66e34cd | 196 | |
5bf62f2d RM |
197 | /* Mask identifying addresses reserved for the user program, |
198 | where the dynamic linker should not map anything. */ | |
199 | #define ELF_MACHINE_USER_ADDRESS_MASK 0xf8000000UL | |
200 | ||
201 | ||
202 | ||
d66e34cd RM |
203 | /* Initial entry point code for the dynamic linker. |
204 | The C function `_dl_start' is the real entry point; | |
205 | its return value is the user program's entry point. */ | |
206 | ||
207 | #define RTLD_START asm ("\ | |
208 | .text\n\ | |
209 | .globl _start\n\ | |
421f82e5 RM |
210 | .globl _dl_start_user\n\ |
211 | _start:\n\ | |
212 | call _dl_start\n\ | |
213 | _dl_start_user:\n\ | |
214 | # Save the user entry point address in %edi.\n\ | |
215 | movl %eax, %edi\n\ | |
216 | # Point %ebx at the GOT. | |
a1a9d215 RM |
217 | call 0f\n\ |
218 | 0: popl %ebx\n\ | |
219 | addl $_GLOBAL_OFFSET_TABLE_+[.-0b], %ebx\n\ | |
220 | # See if we were run as a command with the executable file\n\ | |
221 | # name as an extra leading argument.\n\ | |
24906b43 | 222 | movl _dl_skip_args@GOT(%ebx), %eax\n\ |
a1a9d215 | 223 | movl (%eax),%eax\n\ |
24906b43 RM |
224 | # Pop the original argument count.\n\ |
225 | popl %ecx\n\ | |
226 | # Subtract _dl_skip_args from it.\n\ | |
227 | subl %eax, %ecx\n\ | |
228 | # Adjust the stack pointer to skip _dl_skip_args words.\n\ | |
229 | leal (%esp,%eax,4), %esp\n\ | |
230 | # Push back the modified argument count.\n\ | |
231 | pushl %ecx\n\ | |
f68b86cc RM |
232 | # Push _dl_loaded as argument in _dl_init_next call below.\n\ |
233 | movl _dl_loaded@GOT(%ebx), %eax\n\ | |
234 | movl (%eax), %esi\n\ | |
235 | 0: pushl %esi\n\ | |
d66e34cd RM |
236 | # Call _dl_init_next to return the address of an initializer\n\ |
237 | # function to run.\n\ | |
f68b86cc RM |
238 | call _dl_init_next@PLT\n\ |
239 | addl $4, %esp # Pop argument.\n\ | |
d66e34cd RM |
240 | # Check for zero return, when out of initializers.\n\ |
241 | testl %eax,%eax\n\ | |
242 | jz 1f\n\ | |
243 | # Call the shared object initializer function.\n\ | |
f68b86cc | 244 | # NOTE: We depend only on the registers (%ebx, %esi and %edi)\n\ |
d66e34cd RM |
245 | # and the return address pushed by this call;\n\ |
246 | # the initializer is called with the stack just\n\ | |
247 | # as it appears on entry, and it is free to move\n\ | |
248 | # the stack around, as long as it winds up jumping to\n\ | |
249 | # the return address on the top of the stack.\n\ | |
250 | call *%eax\n\ | |
251 | # Loop to call _dl_init_next for the next initializer.\n\ | |
252 | jmp 0b\n\ | |
a1a9d215 RM |
253 | 1: # Pass our finalizer function to the user in %edx, as per ELF ABI.\n\ |
254 | movl _dl_fini@GOT(%ebx), %edx\n\ | |
421f82e5 RM |
255 | # Jump to the user's entry point.\n\ |
256 | jmp *%edi\n\ | |
d66e34cd | 257 | "); |