]>
Commit | Line | Data |
---|---|---|
d66e34cd RM |
1 | /* Machine-dependent ELF dynamic relocation inline functions. i386 version. |
2 | Copyright (C) 1995 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 "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) \ | |
68 | ++(const Elf32_Rel *) (dynamic_info)[DT_REL]->d_un.d_ptr; | |
69 | ||
70 | /* Perform the relocation specified by RELOC and SYM (which is fully resolved). | |
421f82e5 | 71 | MAP is the object containing the reloc. */ |
d66e34cd RM |
72 | |
73 | static inline void | |
421f82e5 | 74 | elf_machine_rel (struct link_map *map, |
d66e34cd RM |
75 | const Elf32_Rel *reloc, |
76 | Elf32_Addr sym_loadaddr, const Elf32_Sym *sym) | |
77 | { | |
a1a9d215 | 78 | Elf32_Addr *const reloc_addr = (void *) (map->l_addr + reloc->r_offset); |
d66e34cd RM |
79 | const Elf32_Addr sym_value = sym_loadaddr + sym->st_value; |
80 | ||
81 | switch (ELF32_R_TYPE (reloc->r_info)) | |
82 | { | |
83 | case R_386_COPY: | |
84 | memcpy (reloc_addr, (void *) sym_value, sym->st_size); | |
85 | break; | |
86 | case R_386_GLOB_DAT: | |
87 | case R_386_JMP_SLOT: | |
88 | *reloc_addr = sym_value; | |
89 | break; | |
90 | case R_386_32: | |
91 | *reloc_addr += sym_value; | |
92 | break; | |
93 | case R_386_RELATIVE: | |
421f82e5 | 94 | *reloc_addr += map->l_addr; |
d66e34cd RM |
95 | break; |
96 | case R_386_PC32: | |
97 | *reloc_addr = sym_value - (Elf32_Addr) reloc_addr; | |
98 | break; | |
99 | default: | |
100 | assert (! "unexpected dynamic reloc type"); | |
101 | break; | |
102 | } | |
103 | } | |
104 | ||
a1a9d215 RM |
105 | static inline void |
106 | elf_machine_lazy_rel (struct link_map *map, const Elf32_Rel *reloc) | |
107 | { | |
108 | Elf32_Addr *const reloc_addr = (void *) (map->l_addr + reloc->r_offset); | |
109 | switch (ELF32_R_TYPE (reloc->r_info)) | |
110 | { | |
111 | case R_386_JMP_SLOT: | |
112 | *reloc_addr += map->l_addr; | |
113 | break; | |
114 | default: | |
115 | assert (! "unexpected PLT reloc type"); | |
116 | break; | |
117 | } | |
118 | } | |
d66e34cd RM |
119 | |
120 | /* The i386 never uses Elf32_Rela relocations. */ | |
421f82e5 | 121 | #define ELF_MACHINE_NO_RELA 1 |
d66e34cd RM |
122 | |
123 | ||
124 | /* Set up the loaded object described by L so its unrelocated PLT | |
125 | entries will jump to the on-demand fixup code in dl-runtime.c. */ | |
126 | ||
127 | static inline void | |
128 | elf_machine_runtime_setup (struct link_map *l) | |
129 | { | |
a1a9d215 | 130 | Elf32_Addr *got; |
d66e34cd | 131 | extern void _dl_runtime_resolve (Elf32_Word); |
a1a9d215 | 132 | |
d66e34cd RM |
133 | /* The GOT entries for functions in the PLT have not yet been filled |
134 | in. Their initial contents will arrange when called to push an | |
135 | offset into the .rel.plt section, push _GLOBAL_OFFSET_TABLE_[1], | |
136 | and then jump to _GLOBAL_OFFSET_TABLE[2]. */ | |
a1a9d215 | 137 | got = (Elf32_Addr *) (l->l_addr + l->l_info[DT_PLTGOT]->d_un.d_ptr); |
d66e34cd RM |
138 | got[1] = (Elf32_Addr) l; /* Identify this shared object. */ |
139 | /* This function will get called to fix up the GOT entry indicated by | |
140 | the offset on the stack, and then jump to the resolved address. */ | |
141 | got[2] = (Elf32_Addr) &_dl_runtime_resolve; | |
142 | } | |
143 | ||
144 | ||
145 | /* Initial entry point code for the dynamic linker. | |
146 | The C function `_dl_start' is the real entry point; | |
147 | its return value is the user program's entry point. */ | |
148 | ||
149 | #define RTLD_START asm ("\ | |
150 | .text\n\ | |
151 | .globl _start\n\ | |
421f82e5 RM |
152 | .globl _dl_start_user\n\ |
153 | _start:\n\ | |
154 | call _dl_start\n\ | |
155 | _dl_start_user:\n\ | |
156 | # Save the user entry point address in %edi.\n\ | |
157 | movl %eax, %edi\n\ | |
158 | # Point %ebx at the GOT. | |
a1a9d215 RM |
159 | call 0f\n\ |
160 | 0: popl %ebx\n\ | |
161 | addl $_GLOBAL_OFFSET_TABLE_+[.-0b], %ebx\n\ | |
162 | # See if we were run as a command with the executable file\n\ | |
163 | # name as an extra leading argument.\n\ | |
164 | movl rtld_command@GOT(%ebx), %eax\n\ | |
165 | movl (%eax),%eax\n\ | |
166 | testl %eax,%eax\n\ | |
167 | jz 0f\n\ | |
168 | # Pop the original argument count, decrement it, and replace\n\ | |
169 | # the original first argument pointer with the new count.\n\ | |
170 | popl %eax\n\ | |
171 | decl %eax\n\ | |
172 | movl %eax,(%esp)\n\ | |
d66e34cd RM |
173 | # Call _dl_init_next to return the address of an initializer\n\ |
174 | # function to run.\n\ | |
175 | 0: call _dl_init_next@PLT\n\ | |
176 | # Check for zero return, when out of initializers.\n\ | |
177 | testl %eax,%eax\n\ | |
178 | jz 1f\n\ | |
179 | # Call the shared object initializer function.\n\ | |
a1a9d215 | 180 | # NOTE: We depend only on the registers (%ebx and %edi)\n\ |
d66e34cd RM |
181 | # and the return address pushed by this call;\n\ |
182 | # the initializer is called with the stack just\n\ | |
183 | # as it appears on entry, and it is free to move\n\ | |
184 | # the stack around, as long as it winds up jumping to\n\ | |
185 | # the return address on the top of the stack.\n\ | |
186 | call *%eax\n\ | |
187 | # Loop to call _dl_init_next for the next initializer.\n\ | |
188 | jmp 0b\n\ | |
a1a9d215 RM |
189 | 1: # Pass our finalizer function to the user in %edx, as per ELF ABI.\n\ |
190 | movl _dl_fini@GOT(%ebx), %edx\n\ | |
421f82e5 RM |
191 | # Jump to the user's entry point.\n\ |
192 | jmp *%edi\n\ | |
d66e34cd | 193 | "); |