]>
Commit | Line | Data |
---|---|---|
d66e34cd RM |
1 | /* _dl_map_object -- Map in a shared object's segments from the file. |
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 | #include <link.h> | |
21 | #include <sys/types.h> | |
22 | #include <sys/mman.h> | |
23 | #include <string.h> | |
24 | #include <fcntl.h> | |
25 | #include <unistd.h> | |
26 | #include <stdlib.h> | |
27 | #include <errno.h> | |
28 | #include "dynamic-link.h" | |
29 | ||
30 | ||
31 | #include <endian.h> | |
32 | #if BYTE_ORDER == BIG_ENDIAN | |
33 | #define byteorder ELFDATA2MSB | |
34 | #define byteorder_name "big-endian" | |
35 | #elif BYTE_ORDER == LITTLE_ENDIAN | |
36 | #define byteorder ELFDATA2LSB | |
37 | #define byteorder_name "little-endian" | |
38 | #else | |
39 | #error "Unknown BYTE_ORDER " BYTE_ORDER | |
40 | #define byteorder ELFDATANONE | |
41 | #endif | |
42 | ||
43 | #define STRING(x) #x | |
44 | ||
45 | int _dl_zerofd = -1; | |
46 | ||
47 | ||
48 | /* Try to open NAME in one of the directories in DIRPATH. | |
49 | Return the fd, or -1. If successful, fill in *REALNAME | |
50 | with the malloc'd full directory name. */ | |
51 | ||
52 | static int | |
53 | open_path (const char *name, size_t namelen, | |
54 | const char *dirpath, | |
55 | char **realname) | |
56 | { | |
57 | char buf[strlen (dirpath) + 1 + namelen]; | |
58 | const char *p; | |
59 | int fd; | |
60 | ||
61 | p = dirpath; | |
62 | if (p == NULL || *p == '\0') | |
63 | { | |
64 | errno = ENOENT; | |
65 | return -1; | |
66 | } | |
67 | ||
68 | do | |
69 | { | |
70 | dirpath = p; | |
71 | p = strpbrk (dirpath, ":;"); | |
72 | if (p == NULL) | |
73 | p = strchr (dirpath, '\0'); | |
74 | ||
75 | if (p == dirpath) | |
76 | /* Two adjacent colons, or a colon at the beginning or the end of | |
77 | the path means to search the current directory. */ | |
78 | (void) memcpy (buf, name, namelen); | |
79 | else | |
80 | { | |
81 | /* Construct the pathname to try. */ | |
82 | (void) memcpy (buf, dirpath, p - dirpath); | |
83 | buf[p - dirpath] = '/'; | |
84 | (void) memcpy (&buf[(p - dirpath) + 1], name, namelen); | |
85 | } | |
86 | ||
87 | fd = open (buf, O_RDONLY); | |
88 | if (fd != -1) | |
89 | { | |
90 | *realname = strdup (buf); | |
91 | return fd; | |
92 | } | |
93 | if (errno != ENOENT && errno != EACCES) | |
94 | /* The file exists and is readable, but something went wrong. */ | |
95 | return -1; | |
96 | } | |
97 | while (*p++ != '\0'); | |
98 | ||
99 | return -1; | |
100 | } | |
101 | ||
102 | ||
103 | /* Map in the shared object file NAME. */ | |
104 | ||
105 | struct link_map * | |
106 | _dl_map_object (struct link_map *loader, const char *name, | |
107 | Elf32_Addr *entry_point) | |
108 | { | |
109 | int fd; | |
421f82e5 | 110 | struct link_map *l = NULL; |
d66e34cd RM |
111 | char *realname; |
112 | const size_t pagesize = getpagesize (); | |
113 | void *file_mapping = NULL; | |
114 | size_t mapping_size = 0; | |
421f82e5 RM |
115 | |
116 | void lose (int code, const char *msg) | |
117 | { | |
118 | (void) close (fd); | |
119 | if (file_mapping) | |
120 | munmap (file_mapping, mapping_size); | |
121 | _dl_signal_error (code, l ? l->l_name : name, msg); | |
122 | } | |
123 | ||
d66e34cd RM |
124 | /* Make sure LOCATION is mapped in. */ |
125 | void *map (off_t location, size_t size) | |
126 | { | |
127 | if ((off_t) mapping_size <= location + (off_t) size) | |
128 | { | |
129 | void *result; | |
130 | if (file_mapping) | |
131 | munmap (file_mapping, mapping_size); | |
132 | mapping_size = (location + size + 1 + pagesize - 1); | |
133 | mapping_size &= ~(pagesize - 1); | |
134 | result = mmap (file_mapping, mapping_size, PROT_READ, | |
135 | MAP_COPY|MAP_FILE, fd, 0); | |
136 | if (result == (void *) -1) | |
421f82e5 | 137 | lose (errno, "cannot map file data"); |
d66e34cd RM |
138 | file_mapping = result; |
139 | } | |
140 | return file_mapping + location; | |
141 | } | |
142 | ||
143 | const Elf32_Ehdr *header; | |
d66e34cd RM |
144 | |
145 | /* Look for this name among those already loaded. */ | |
146 | for (l = _dl_loaded; l; l = l->l_next) | |
147 | if (! strcmp (name, l->l_libname)) | |
148 | { | |
149 | /* The object is already loaded. | |
150 | Just bump its reference count and return it. */ | |
151 | ++l->l_opencount; | |
152 | return l; | |
153 | } | |
154 | ||
155 | if (strchr (name, '/') == NULL) | |
156 | { | |
157 | /* Search for NAME in several places. */ | |
158 | ||
159 | size_t namelen = strlen (name) + 1; | |
160 | ||
a1a9d215 | 161 | inline void trypath (const char *dirpath) |
d66e34cd RM |
162 | { |
163 | fd = open_path (name, namelen, dirpath, &realname); | |
164 | } | |
165 | ||
a1a9d215 | 166 | fd = -1; |
d66e34cd RM |
167 | if (loader && loader->l_info[DT_RPATH]) |
168 | trypath ((const char *) (loader->l_addr + | |
169 | loader->l_info[DT_RPATH]->d_un.d_ptr)); | |
170 | if (fd == -1 && ! _dl_secure) | |
171 | trypath (getenv ("LD_LIBRARY_PATH")); | |
172 | if (fd == -1) | |
173 | trypath ("/lib:/usr/lib"); | |
174 | } | |
175 | else | |
176 | { | |
177 | fd = open (name, O_RDONLY); | |
178 | if (fd != -1) | |
179 | realname = strdup (name); | |
180 | } | |
181 | ||
182 | if (fd == -1) | |
421f82e5 | 183 | lose (errno, "cannot open shared object file"); |
d66e34cd RM |
184 | |
185 | /* Look again to see if the real name matched another already loaded. */ | |
186 | for (l = _dl_loaded; l; l = l->l_next) | |
187 | if (! strcmp (realname, l->l_name)) | |
188 | { | |
189 | /* The object is already loaded. | |
190 | Just bump its reference count and return it. */ | |
191 | close (fd); | |
192 | ++l->l_opencount; | |
193 | return l; | |
194 | } | |
195 | ||
196 | ||
197 | /* Map in the first page to read the header. */ | |
198 | header = map (0, sizeof *header); | |
d66e34cd RM |
199 | |
200 | #undef LOSE | |
421f82e5 | 201 | #define LOSE(s) lose (0, (s)) |
d66e34cd RM |
202 | /* Check the header for basic validity. */ |
203 | if (*(Elf32_Word *) &header->e_ident != ((ELFMAG0 << (EI_MAG0 * 8)) | | |
204 | (ELFMAG1 << (EI_MAG1 * 8)) | | |
205 | (ELFMAG2 << (EI_MAG2 * 8)) | | |
206 | (ELFMAG3 << (EI_MAG3 * 8)))) | |
207 | LOSE ("invalid ELF header"); | |
208 | if (header->e_ident[EI_CLASS] != ELFCLASS32) | |
209 | LOSE ("ELF file class not 32-bit"); | |
210 | if (header->e_ident[EI_DATA] != byteorder) | |
211 | LOSE ("ELF file data encoding not " byteorder_name); | |
212 | if (header->e_ident[EI_VERSION] != EV_CURRENT) | |
213 | LOSE ("ELF file version ident not " STRING(EV_CURRENT)); | |
214 | if (header->e_version != EV_CURRENT) | |
215 | LOSE ("ELF file version not " STRING(EV_CURRENT)); | |
216 | if (! elf_machine_matches_host (header->e_machine)) | |
217 | LOSE ("ELF file machine architecture not " ELF_MACHINE_NAME); | |
218 | if (header->e_phentsize != sizeof (Elf32_Phdr)) | |
219 | LOSE ("ELF file's phentsize not the expected size"); | |
220 | ||
221 | /* Enter the new object in the list of loaded objects. */ | |
222 | l = _dl_new_object (realname, name, lt_loaded); | |
223 | l->l_opencount = 1; | |
224 | ||
225 | if (_dl_zerofd == -1) | |
226 | { | |
227 | _dl_zerofd = _dl_sysdep_open_zero_fill (); | |
228 | if (_dl_zerofd == -1) | |
421f82e5 | 229 | _dl_signal_error (errno, NULL, "cannot open zero fill device"); |
d66e34cd RM |
230 | } |
231 | ||
232 | { | |
233 | /* Copy the program header table into stack space so we can then unmap | |
234 | the headers. */ | |
235 | Elf32_Phdr phdr[header->e_phnum]; | |
236 | const Elf32_Phdr *ph; | |
237 | int anywhere; | |
238 | ||
239 | ph = map (header->e_phoff, header->e_phnum * sizeof (Elf32_Phdr)); | |
d66e34cd RM |
240 | memcpy (phdr, ph, sizeof phdr); |
241 | l->l_phnum = header->e_phnum; | |
242 | ||
243 | anywhere = header->e_type == ET_DYN || header->e_type == ET_REL; | |
244 | ||
245 | if (entry_point) | |
246 | *entry_point = header->e_entry; | |
247 | ||
248 | /* We are done reading the file's headers now. Unmap them. */ | |
249 | munmap (file_mapping, mapping_size); | |
250 | ||
251 | /* Scan the program header table, processing its load commands. */ | |
252 | l->l_addr = 0; | |
253 | l->l_ld = 0; | |
254 | for (ph = phdr; ph < &phdr[l->l_phnum]; ++ph) | |
255 | switch (ph->p_type) | |
256 | { | |
257 | /* These entries tell us where to find things once the file's | |
258 | segments are mapped in. We record the addresses it says | |
259 | verbatim, and later correct for the run-time load address. */ | |
260 | case PT_DYNAMIC: | |
261 | l->l_ld = (void *) ph->p_vaddr; | |
262 | break; | |
263 | case PT_PHDR: | |
264 | l->l_phdr = (void *) ph->p_vaddr; | |
265 | break; | |
266 | ||
267 | case PT_LOAD: | |
268 | /* A load command tells us to map in part of the file. */ | |
269 | if (ph->p_align % pagesize != 0) | |
270 | LOSE ("ELF load command alignment not page-aligned"); | |
271 | if ((ph->p_vaddr - ph->p_offset) % ph->p_align) | |
272 | LOSE ("ELF load command address/offset not properly aligned"); | |
273 | { | |
274 | Elf32_Addr mapstart = ph->p_vaddr & ~(ph->p_align - 1); | |
275 | Elf32_Addr mapend = ((ph->p_vaddr + ph->p_filesz + ph->p_align - 1) | |
276 | & ~(ph->p_align - 1)); | |
277 | off_t mapoff = ph->p_offset & ~(ph->p_align - 1); | |
278 | caddr_t mapat; | |
279 | int prot = 0; | |
280 | if (ph->p_flags & PF_R) | |
281 | prot |= PROT_READ; | |
282 | if (ph->p_flags & PF_W) | |
283 | prot |= PROT_WRITE; | |
284 | if (ph->p_flags & PF_X) | |
285 | prot |= PROT_EXEC; | |
286 | ||
287 | if (anywhere) | |
288 | { | |
289 | /* XXX this loses if the first segment mmap call puts | |
290 | it someplace where the later segments cannot fit. */ | |
421f82e5 RM |
291 | mapat = mmap ((caddr_t) (l->l_addr + mapstart), |
292 | mapend - mapstart, | |
d66e34cd RM |
293 | prot, MAP_COPY|MAP_FILE|MAP_INHERIT | |
294 | /* Let the system choose any convenient | |
295 | location if this is the first segment. | |
296 | Following segments must be contiguous in | |
297 | virtual space with the first. */ | |
298 | (l->l_addr == 0 ? 0 : MAP_FIXED), | |
299 | fd, mapoff); | |
300 | if (l->l_addr == 0) | |
301 | /* This was the first segment mapped, so MAPAT is | |
302 | the address the system chose for us. Record it. */ | |
303 | l->l_addr = (Elf32_Addr) mapat - mapstart; | |
304 | } | |
305 | else | |
306 | { | |
307 | mapat = mmap ((caddr_t) mapstart, mapend - mapstart, | |
308 | prot, MAP_COPY|MAP_FILE|MAP_INHERIT|MAP_FIXED, | |
309 | fd, mapoff); | |
310 | /* This file refers to absolute addresses. So consider its | |
311 | "load base" to be zero, since that is what we add to the | |
312 | file's addresses to find them in our memory. */ | |
313 | l->l_addr = 0; | |
314 | } | |
315 | if (mapat == (caddr_t) -1) | |
421f82e5 | 316 | lose (errno, "failed to map segment from shared object"); |
d66e34cd RM |
317 | |
318 | if (ph->p_memsz > ph->p_filesz) | |
319 | { | |
320 | /* Extra zero pages should appear at the end of this segment, | |
a1a9d215 RM |
321 | after the data mapped from the file. */ |
322 | caddr_t zero, zeroend, zeropage; | |
323 | ||
324 | mapat += ph->p_vaddr - mapstart; | |
325 | zero = mapat + ph->p_filesz; | |
326 | zeroend = mapat + ph->p_memsz; | |
327 | zeropage = (caddr_t) ((Elf32_Addr) (zero + pagesize - 1) | |
328 | & ~(pagesize - 1)); | |
d66e34cd RM |
329 | |
330 | if (zeroend < zeropage) | |
331 | /* All the extra data is in the last page of the segment. | |
332 | We can just zero it. */ | |
333 | zeropage = zeroend; | |
334 | if (zeropage > zero) | |
335 | { | |
336 | /* Zero the final part of the last page of the segment. */ | |
337 | if ((prot & PROT_WRITE) == 0) | |
338 | { | |
339 | /* Dag nab it. */ | |
340 | if (mprotect ((caddr_t) ((Elf32_Addr) zero | |
341 | & ~(pagesize - 1)), | |
342 | pagesize, | |
343 | prot|PROT_WRITE) < 0) | |
421f82e5 | 344 | lose (errno, "cannot change memory protections"); |
d66e34cd | 345 | } |
a1a9d215 | 346 | memset (zero, 0, zeropage - zero); |
d66e34cd RM |
347 | if ((prot & PROT_WRITE) == 0) |
348 | mprotect ((caddr_t) ((Elf32_Addr) zero | |
349 | & ~(pagesize - 1)), | |
350 | pagesize, prot); | |
351 | } | |
352 | ||
353 | if (zeroend > zeropage) | |
354 | /* Map the remaining zero pages in from the zero fill FD. */ | |
355 | mapat = mmap (zeropage, zeroend - zeropage, | |
356 | prot, MAP_ANON|MAP_PRIVATE|MAP_FIXED, | |
357 | _dl_zerofd, 0); | |
358 | } | |
359 | } | |
360 | } | |
361 | ||
362 | if (l->l_ld == 0) | |
363 | LOSE ("object file has no dynamic section"); | |
364 | (Elf32_Addr) l->l_ld += l->l_addr; | |
365 | ||
366 | if (l->l_phdr == 0) | |
367 | l->l_phdr = (void *) ((const Elf32_Ehdr *) l->l_addr)->e_phoff; | |
368 | (Elf32_Addr) l->l_phdr += l->l_addr; | |
369 | } | |
370 | ||
371 | elf_get_dynamic_info (l->l_ld, l->l_info); | |
372 | if (l->l_info[DT_HASH]) | |
373 | _dl_setup_hash (l); | |
374 | ||
375 | return l; | |
376 | } |