1 /* $NetBSD: map_object.c,v 1.37 2009/01/06 04:01:46 mrg Exp $ */
4 * Copyright 1996 John D. Polstra.
5 * Copyright 1996 Matt Thomas <matt@3am-software.com>
6 * Copyright 2002 Charles M. Hannum <root@ihack.net>
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. All advertising materials mentioning features or use of this software
18 * must display the following acknowledgement:
19 * This product includes software developed by John Polstra.
20 * 4. The name of the author may not be used to endorse or promote products
21 * derived from this software without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
24 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
25 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
26 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
27 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
28 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
32 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35 #include <sys/cdefs.h>
37 __RCSID("$NetBSD: map_object.c,v 1.37 2009/01/06 04:01:46 mrg Exp $");
46 #include <sys/types.h>
51 static int protflags(int); /* Elf flags -> mmap protection */
54 * Map a shared object into memory. The argument is a file descriptor,
55 * which must be open on the object and positioned at its beginning.
57 * The return value is a pointer to a newly-allocated Obj_Entry structure
58 * for the shared object. Returns NULL on failure.
61 _rtld_map_object(const char *path
, int fd
, const struct stat
*sb
)
69 caddr_t mapbase
= MAP_FAILED
;
74 Elf_Addr base_alignment
;
94 if (sb
!= NULL
&& sb
->st_size
< (off_t
)sizeof (Elf_Ehdr
)) {
95 _rtld_error("%s: unrecognized file format1", path
);
99 obj
= _rtld_obj_new();
100 obj
->path
= xstrdup(path
);
101 obj
->pathlen
= strlen(path
);
103 obj
->dev
= sb
->st_dev
;
104 obj
->ino
= sb
->st_ino
;
107 ehdr
= mmap(NULL
, _rtld_pagesz
, PROT_READ
, MAP_FILE
| MAP_SHARED
, fd
,
110 if (ehdr
== MAP_FAILED
) {
111 _rtld_error("%s: read error: %s", path
, xstrerror(errno
));
114 /* Make sure the file is valid */
115 if (memcmp(ELFMAG
, ehdr
->e_ident
, SELFMAG
) != 0 ||
116 ehdr
->e_ident
[EI_CLASS
] != ELFCLASS
) {
117 _rtld_error("%s: unrecognized file format2 [%x != %x]", path
, ehdr
->e_ident
[EI_CLASS
], ELFCLASS
);
120 /* Elf_e_ident includes class */
121 if (ehdr
->e_ident
[EI_VERSION
] != EV_CURRENT
||
122 ehdr
->e_version
!= EV_CURRENT
||
123 ehdr
->e_ident
[EI_DATA
] != ELFDEFNNAME(MACHDEP_ENDIANNESS
)) {
124 _rtld_error("%s: unsupported file version", path
);
127 if (ehdr
->e_type
!= ET_EXEC
&& ehdr
->e_type
!= ET_DYN
) {
128 _rtld_error("%s: unsupported file type", path
);
131 switch (ehdr
->e_machine
) {
132 ELFDEFNNAME(MACHDEP_ID_CASES
)
134 _rtld_error("%s: unsupported machine", path
);
139 * We rely on the program header being in the first page. This is
140 * not strictly required by the ABI specification, but it seems to
141 * always true in practice. And, it simplifies things considerably.
143 assert(ehdr
->e_phentsize
== sizeof(Elf_Phdr
));
144 assert(ehdr
->e_phoff
+ ehdr
->e_phnum
* sizeof(Elf_Phdr
) <=
148 * Scan the program header entries, and save key information.
150 * We rely on there being exactly two load segments, text and data,
153 phdr
= (Elf_Phdr
*) ((caddr_t
)ehdr
+ ehdr
->e_phoff
);
154 phlimit
= phdr
+ ehdr
->e_phnum
;
156 while (phdr
< phlimit
) {
157 switch (phdr
->p_type
) {
159 obj
->interp
= (void *)(uintptr_t)phdr
->p_vaddr
;
169 obj
->dynamic
= (void *)(uintptr_t)phdr
->p_vaddr
;
175 obj
->entry
= (void *)(uintptr_t)ehdr
->e_entry
;
177 _rtld_error("%s: not dynamically linked", path
);
181 _rtld_error("%s: wrong number of segments (%d != 2)", path
,
187 * Map the entire address space of the object as a file
188 * region to stake out our contiguous region and establish a
189 * base for relocation. We use a file mapping so that
190 * the kernel will give us whatever alignment is appropriate
191 * for the platform we're running on.
193 * We map it using the text protection, map the data segment
194 * into the right place, then map an anon segment for the bss
195 * and unmap the gaps left by padding to alignment.
199 base_alignment
= segs
[0]->p_align
;
201 base_offset
= round_down(segs
[0]->p_offset
);
202 base_vaddr
= round_down(segs
[0]->p_vaddr
);
203 base_vlimit
= round_up(segs
[1]->p_vaddr
+ segs
[1]->p_memsz
);
204 text_vlimit
= round_up(segs
[0]->p_vaddr
+ segs
[0]->p_memsz
);
205 text_flags
= protflags(segs
[0]->p_flags
);
206 data_offset
= round_down(segs
[1]->p_offset
);
207 data_vaddr
= round_down(segs
[1]->p_vaddr
);
208 data_vlimit
= round_up(segs
[1]->p_vaddr
+ segs
[1]->p_filesz
);
209 data_flags
= protflags(segs
[1]->p_flags
);
211 clear_vaddr
= segs
[1]->p_vaddr
+ segs
[1]->p_filesz
;
214 obj
->textsize
= text_vlimit
- base_vaddr
;
215 obj
->vaddrbase
= base_vaddr
;
216 obj
->isdynamic
= ehdr
->e_type
== ET_DYN
;
218 /* Unmap header if it overlaps the first load section. */
219 if (base_offset
< _rtld_pagesz
) {
220 munmap(ehdr
, _rtld_pagesz
);
221 obj
->ehdr
= MAP_FAILED
;
225 * Calculate log2 of the base section alignment.
229 if (base_alignment
> _rtld_pagesz
) {
230 unsigned int log2
= 0;
231 for (; base_alignment
> 1; base_alignment
>>= 1)
233 mapflags
= MAP_ALIGNED(log2
);
238 base_addr
= obj
->isdynamic
? NULL
: (caddr_t
)base_vaddr
;
242 mapsize
= base_vlimit
- base_vaddr
;
243 mapbase
= mmap(base_addr
, mapsize
, text_flags
,
244 mapflags
| MAP_FILE
| MAP_PRIVATE
, fd
, base_offset
);
245 if (mapbase
== MAP_FAILED
) {
246 _rtld_error("mmap of entire address space failed: %s",
251 /* Overlay the data segment onto the proper region. */
252 data_addr
= mapbase
+ (data_vaddr
- base_vaddr
);
253 if (mmap(data_addr
, data_vlimit
- data_vaddr
, data_flags
,
254 MAP_FILE
| MAP_PRIVATE
| MAP_FIXED
, fd
, data_offset
) ==
256 _rtld_error("mmap of data failed: %s", xstrerror(errno
));
260 /* Overlay the bss segment onto the proper region. */
261 if (mmap(mapbase
+ data_vlimit
- base_vaddr
, base_vlimit
- data_vlimit
,
262 data_flags
, MAP_ANON
| MAP_PRIVATE
| MAP_FIXED
, -1, 0) ==
264 _rtld_error("mmap of bss failed: %s", xstrerror(errno
));
268 /* Unmap the gap between the text and data. */
269 gap_addr
= mapbase
+ round_up(text_vlimit
- base_vaddr
);
270 gap_size
= data_addr
- gap_addr
;
271 if (gap_size
!= 0 && mprotect(gap_addr
, gap_size
, PROT_NONE
) == -1) {
272 _rtld_error("mprotect of text -> data gap failed: %s",
278 /* Clear any BSS in the last page of the data segment. */
279 clear_addr
= mapbase
+ (clear_vaddr
- base_vaddr
);
280 if ((nclear
= data_vlimit
- clear_vaddr
) > 0)
281 memset(clear_addr
, 0, nclear
);
283 /* Non-file portion of BSS mapped above. */
286 obj
->mapbase
= mapbase
;
287 obj
->mapsize
= mapsize
;
288 obj
->relocbase
= mapbase
- base_vaddr
;
291 obj
->dynamic
= (void *)(obj
->relocbase
+ (Elf_Addr
)(uintptr_t)obj
->dynamic
);
293 obj
->entry
= (void *)(obj
->relocbase
+ (Elf_Addr
)(uintptr_t)obj
->entry
);
295 obj
->interp
= (void *)(obj
->relocbase
+ (Elf_Addr
)(uintptr_t)obj
->interp
);
300 if (obj
->ehdr
!= MAP_FAILED
)
301 munmap(obj
->ehdr
, _rtld_pagesz
);
302 if (mapbase
!= MAP_FAILED
)
303 munmap(mapbase
, mapsize
);
309 _rtld_obj_free(Obj_Entry
*obj
)
314 while (obj
->needed
!= NULL
) {
315 Needed_Entry
*needed
= obj
->needed
;
316 obj
->needed
= needed
->next
;
319 while ((elm
= SIMPLEQ_FIRST(&obj
->dldags
)) != NULL
) {
320 SIMPLEQ_REMOVE_HEAD(&obj
->dldags
, link
);
323 while ((elm
= SIMPLEQ_FIRST(&obj
->dagmembers
)) != NULL
) {
324 SIMPLEQ_REMOVE_HEAD(&obj
->dagmembers
, link
);
335 obj
= CNEW(Obj_Entry
);
336 SIMPLEQ_INIT(&obj
->dldags
);
337 SIMPLEQ_INIT(&obj
->dagmembers
);
342 * Given a set of ELF protection flags, return the corresponding protection
346 protflags(int elfflags
)