]>
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 | } | |
710f7bab RM |
114 | loadbase = (*resolve) (&sym, (Elf32_Addr) reloc_addr, 0); |
115 | *reloc_addr += sym ? (loadbase + sym->st_value) : 0; | |
d66e34cd RM |
116 | break; |
117 | case R_386_RELATIVE: | |
5bf62f2d RM |
118 | if (map->l_type != lt_interpreter) /* Already done in dynamic linker. */ |
119 | *reloc_addr += map->l_addr; | |
d66e34cd RM |
120 | break; |
121 | case R_386_PC32: | |
710f7bab RM |
122 | loadbase = (*resolve) (&sym, (Elf32_Addr) reloc_addr, 0); |
123 | *reloc_addr += ((sym ? (loadbase + sym->st_value) : 0) - | |
124 | (Elf32_Addr) reloc_addr); | |
d66e34cd | 125 | break; |
86fe2915 RM |
126 | case R_386_NONE: /* Alright, Wilbur. */ |
127 | break; | |
d66e34cd RM |
128 | default: |
129 | assert (! "unexpected dynamic reloc type"); | |
130 | break; | |
131 | } | |
132 | } | |
133 | ||
a1a9d215 RM |
134 | static inline void |
135 | elf_machine_lazy_rel (struct link_map *map, const Elf32_Rel *reloc) | |
136 | { | |
137 | Elf32_Addr *const reloc_addr = (void *) (map->l_addr + reloc->r_offset); | |
138 | switch (ELF32_R_TYPE (reloc->r_info)) | |
139 | { | |
140 | case R_386_JMP_SLOT: | |
141 | *reloc_addr += map->l_addr; | |
142 | break; | |
143 | default: | |
144 | assert (! "unexpected PLT reloc type"); | |
145 | break; | |
146 | } | |
147 | } | |
d66e34cd | 148 | |
6c03c2cf RM |
149 | /* Nonzero iff TYPE describes relocation of a PLT entry, so |
150 | PLT entries should not be allowed to define the value. */ | |
151 | #define elf_machine_pltrel_p(type) ((type) == R_386_JMP_SLOT) | |
152 | ||
d66e34cd | 153 | /* The i386 never uses Elf32_Rela relocations. */ |
421f82e5 | 154 | #define ELF_MACHINE_NO_RELA 1 |
d66e34cd RM |
155 | |
156 | ||
157 | /* Set up the loaded object described by L so its unrelocated PLT | |
158 | entries will jump to the on-demand fixup code in dl-runtime.c. */ | |
159 | ||
160 | static inline void | |
a2e1b046 | 161 | elf_machine_runtime_setup (struct link_map *l, int lazy) |
d66e34cd | 162 | { |
a1a9d215 | 163 | Elf32_Addr *got; |
d66e34cd | 164 | extern void _dl_runtime_resolve (Elf32_Word); |
a1a9d215 | 165 | |
a2e1b046 RM |
166 | if (l->l_info[DT_JMPREL] && lazy) |
167 | { | |
168 | /* The GOT entries for functions in the PLT have not yet been filled | |
169 | in. Their initial contents will arrange when called to push an | |
170 | offset into the .rel.plt section, push _GLOBAL_OFFSET_TABLE_[1], | |
171 | and then jump to _GLOBAL_OFFSET_TABLE[2]. */ | |
172 | got = (Elf32_Addr *) (l->l_addr + l->l_info[DT_PLTGOT]->d_un.d_ptr); | |
173 | got[1] = (Elf32_Addr) l; /* Identify this shared object. */ | |
174 | /* This function will get called to fix up the GOT entry indicated by | |
175 | the offset on the stack, and then jump to the resolved address. */ | |
176 | got[2] = (Elf32_Addr) &_dl_runtime_resolve; | |
177 | } | |
d66e34cd | 178 | |
38334018 RM |
179 | /* This code is used in dl-runtime.c to call the `fixup' function |
180 | and then redirect to the address it returns. */ | |
181 | #define ELF_MACHINE_RUNTIME_TRAMPOLINE asm ("\ | |
182 | .globl _dl_runtime_resolve | |
183 | .type _dl_runtime_resolve, @function | |
184 | _dl_runtime_resolve: | |
185 | call fixup # Args pushed by PLT. | |
186 | addl $8, %esp # Pop args. | |
187 | jmp *%eax # Jump to function address. | |
188 | "); | |
189 | /* The PLT uses Elf32_Rel relocs. */ | |
190 | #define elf_machine_relplt elf_machine_rel | |
191 | } | |
d66e34cd | 192 | |
5bf62f2d RM |
193 | /* Mask identifying addresses reserved for the user program, |
194 | where the dynamic linker should not map anything. */ | |
195 | #define ELF_MACHINE_USER_ADDRESS_MASK 0xf8000000UL | |
196 | ||
197 | ||
198 | ||
d66e34cd RM |
199 | /* Initial entry point code for the dynamic linker. |
200 | The C function `_dl_start' is the real entry point; | |
201 | its return value is the user program's entry point. */ | |
202 | ||
203 | #define RTLD_START asm ("\ | |
204 | .text\n\ | |
205 | .globl _start\n\ | |
421f82e5 RM |
206 | .globl _dl_start_user\n\ |
207 | _start:\n\ | |
208 | call _dl_start\n\ | |
209 | _dl_start_user:\n\ | |
210 | # Save the user entry point address in %edi.\n\ | |
211 | movl %eax, %edi\n\ | |
212 | # Point %ebx at the GOT. | |
a1a9d215 RM |
213 | call 0f\n\ |
214 | 0: popl %ebx\n\ | |
215 | addl $_GLOBAL_OFFSET_TABLE_+[.-0b], %ebx\n\ | |
216 | # See if we were run as a command with the executable file\n\ | |
217 | # name as an extra leading argument.\n\ | |
24906b43 | 218 | movl _dl_skip_args@GOT(%ebx), %eax\n\ |
a1a9d215 | 219 | movl (%eax),%eax\n\ |
24906b43 RM |
220 | # Pop the original argument count.\n\ |
221 | popl %ecx\n\ | |
222 | # Subtract _dl_skip_args from it.\n\ | |
223 | subl %eax, %ecx\n\ | |
224 | # Adjust the stack pointer to skip _dl_skip_args words.\n\ | |
225 | leal (%esp,%eax,4), %esp\n\ | |
226 | # Push back the modified argument count.\n\ | |
227 | pushl %ecx\n\ | |
d66e34cd RM |
228 | # Call _dl_init_next to return the address of an initializer\n\ |
229 | # function to run.\n\ | |
230 | 0: call _dl_init_next@PLT\n\ | |
231 | # Check for zero return, when out of initializers.\n\ | |
232 | testl %eax,%eax\n\ | |
233 | jz 1f\n\ | |
234 | # Call the shared object initializer function.\n\ | |
a1a9d215 | 235 | # NOTE: We depend only on the registers (%ebx and %edi)\n\ |
d66e34cd RM |
236 | # and the return address pushed by this call;\n\ |
237 | # the initializer is called with the stack just\n\ | |
238 | # as it appears on entry, and it is free to move\n\ | |
239 | # the stack around, as long as it winds up jumping to\n\ | |
240 | # the return address on the top of the stack.\n\ | |
241 | call *%eax\n\ | |
242 | # Loop to call _dl_init_next for the next initializer.\n\ | |
243 | jmp 0b\n\ | |
a1a9d215 RM |
244 | 1: # Pass our finalizer function to the user in %edx, as per ELF ABI.\n\ |
245 | movl _dl_fini@GOT(%ebx), %edx\n\ | |
421f82e5 RM |
246 | # Jump to the user's entry point.\n\ |
247 | jmp *%edi\n\ | |
d66e34cd | 248 | "); |