Annotation of src/libexec/ld.elf_so/map_object.c, Revision 1.49
1.49 ! christos 1: /* $NetBSD: map_object.c,v 1.48 2013/05/06 08:02:20 skrll Exp $ */
1.1 cgd 2:
3: /*
4: * Copyright 1996 John D. Polstra.
5: * Copyright 1996 Matt Thomas <matt@3am-software.com>
1.24 mycroft 6: * Copyright 2002 Charles M. Hannum <root@ihack.net>
1.1 cgd 7: * All rights reserved.
8: *
9: * Redistribution and use in source and binary forms, with or without
10: * modification, are permitted provided that the following conditions
11: * are met:
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.
22: *
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.
33: */
34:
1.31 skrll 35: #include <sys/cdefs.h>
36: #ifndef lint
1.49 ! christos 37: __RCSID("$NetBSD: map_object.c,v 1.48 2013/05/06 08:02:20 skrll Exp $");
1.31 skrll 38: #endif /* not lint */
39:
1.1 cgd 40: #include <errno.h>
41: #include <stddef.h>
1.10 mycroft 42: #include <stdlib.h>
1.1 cgd 43: #include <string.h>
44: #include <unistd.h>
1.10 mycroft 45: #include <sys/stat.h>
1.1 cgd 46: #include <sys/types.h>
47: #include <sys/mman.h>
48:
1.41 skrll 49: #include "debug.h"
1.1 cgd 50: #include "rtld.h"
1.7 hannken 51:
1.30 skrll 52: static int protflags(int); /* Elf flags -> mmap protection */
1.1 cgd 53:
1.41 skrll 54: #define EA_UNDEF (~(Elf_Addr)0)
55:
1.1 cgd 56: /*
57: * Map a shared object into memory. The argument is a file descriptor,
58: * which must be open on the object and positioned at its beginning.
59: *
60: * The return value is a pointer to a newly-allocated Obj_Entry structure
61: * for the shared object. Returns NULL on failure.
62: */
63: Obj_Entry *
1.34 christos 64: _rtld_map_object(const char *path, int fd, const struct stat *sb)
1.1 cgd 65: {
1.18 junyoung 66: Obj_Entry *obj;
67: Elf_Ehdr *ehdr;
68: Elf_Phdr *phdr;
1.42 joerg 69: #if defined(__HAVE_TLS_VARIANT_I) || defined(__HAVE_TLS_VARIANT_II)
70: Elf_Phdr *phtls;
71: #endif
1.41 skrll 72: size_t phsize;
1.18 junyoung 73: Elf_Phdr *phlimit;
74: Elf_Phdr *segs[2];
75: int nsegs;
1.22 mycroft 76: caddr_t mapbase = MAP_FAILED;
1.32 lukem 77: size_t mapsize = 0;
1.27 matt 78: int mapflags;
1.18 junyoung 79: Elf_Off base_offset;
1.28 taca 80: #ifdef MAP_ALIGNED
1.27 matt 81: Elf_Addr base_alignment;
1.28 taca 82: #endif
1.18 junyoung 83: Elf_Addr base_vaddr;
84: Elf_Addr base_vlimit;
85: Elf_Addr text_vlimit;
1.22 mycroft 86: int text_flags;
1.18 junyoung 87: caddr_t base_addr;
88: Elf_Off data_offset;
89: Elf_Addr data_vaddr;
90: Elf_Addr data_vlimit;
1.22 mycroft 91: int data_flags;
1.18 junyoung 92: caddr_t data_addr;
1.42 joerg 93: #if defined(__HAVE_TLS_VARIANT_I) || defined(__HAVE_TLS_VARIANT_II)
94: Elf_Addr tls_vaddr = 0; /* Noise GCC */
95: #endif
1.41 skrll 96: Elf_Addr phdr_vaddr;
97: size_t phdr_memsz;
1.18 junyoung 98: caddr_t gap_addr;
99: size_t gap_size;
1.41 skrll 100: int i;
1.1 cgd 101: #ifdef RTLD_LOADER
1.18 junyoung 102: Elf_Addr clear_vaddr;
103: caddr_t clear_addr;
104: size_t nclear;
1.1 cgd 105: #endif
1.26 fvdl 106:
1.38 christos 107: if (sb != NULL && sb->st_size < (off_t)sizeof (Elf_Ehdr)) {
1.45 dholland 108: _rtld_error("%s: not ELF file (too short)", path);
1.26 fvdl 109: return NULL;
110: }
1.1 cgd 111:
1.22 mycroft 112: obj = _rtld_obj_new();
1.34 christos 113: obj->path = xstrdup(path);
1.25 junyoung 114: obj->pathlen = strlen(path);
1.22 mycroft 115: if (sb != NULL) {
116: obj->dev = sb->st_dev;
117: obj->ino = sb->st_ino;
118: }
119:
1.20 mycroft 120: ehdr = mmap(NULL, _rtld_pagesz, PROT_READ, MAP_FILE | MAP_SHARED, fd,
1.16 mycroft 121: (off_t)0);
1.36 ad 122: obj->ehdr = ehdr;
1.20 mycroft 123: if (ehdr == MAP_FAILED) {
1.4 christos 124: _rtld_error("%s: read error: %s", path, xstrerror(errno));
1.22 mycroft 125: goto bad;
1.4 christos 126: }
127: /* Make sure the file is valid */
1.45 dholland 128: if (memcmp(ELFMAG, ehdr->e_ident, SELFMAG) != 0) {
129: _rtld_error("%s: not ELF file (magic number bad)", path);
130: goto bad;
131: }
132: if (ehdr->e_ident[EI_CLASS] != ELFCLASS) {
133: _rtld_error("%s: invalid ELF class %x; expected %x", path,
1.40 skrll 134: ehdr->e_ident[EI_CLASS], ELFCLASS);
1.16 mycroft 135: goto bad;
1.4 christos 136: }
137: /* Elf_e_ident includes class */
1.17 junyoung 138: if (ehdr->e_ident[EI_VERSION] != EV_CURRENT ||
139: ehdr->e_version != EV_CURRENT ||
140: ehdr->e_ident[EI_DATA] != ELFDEFNNAME(MACHDEP_ENDIANNESS)) {
1.16 mycroft 141: _rtld_error("%s: unsupported file version", path);
142: goto bad;
143: }
1.17 junyoung 144: if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN) {
1.16 mycroft 145: _rtld_error("%s: unsupported file type", path);
146: goto bad;
1.4 christos 147: }
1.17 junyoung 148: switch (ehdr->e_machine) {
1.4 christos 149: ELFDEFNNAME(MACHDEP_ID_CASES)
150: default:
1.16 mycroft 151: _rtld_error("%s: unsupported machine", path);
152: goto bad;
1.4 christos 153: }
154:
155: /*
156: * We rely on the program header being in the first page. This is
157: * not strictly required by the ABI specification, but it seems to
158: * always true in practice. And, it simplifies things considerably.
159: */
1.17 junyoung 160: assert(ehdr->e_phentsize == sizeof(Elf_Phdr));
1.19 junyoung 161: assert(ehdr->e_phoff + ehdr->e_phnum * sizeof(Elf_Phdr) <=
162: _rtld_pagesz);
1.4 christos 163:
164: /*
165: * Scan the program header entries, and save key information.
166: *
167: * We rely on there being exactly two load segments, text and data,
168: * in that order.
169: */
1.17 junyoung 170: phdr = (Elf_Phdr *) ((caddr_t)ehdr + ehdr->e_phoff);
1.42 joerg 171: #if defined(__HAVE_TLS_VARIANT_I) || defined(__HAVE_TLS_VARIANT_II)
172: phtls = NULL;
173: #endif
1.41 skrll 174: phsize = ehdr->e_phnum * sizeof(phdr[0]);
175: obj->phdr = NULL;
176: phdr_vaddr = EA_UNDEF;
177: phdr_memsz = 0;
1.17 junyoung 178: phlimit = phdr + ehdr->e_phnum;
1.4 christos 179: nsegs = 0;
180: while (phdr < phlimit) {
181: switch (phdr->p_type) {
1.10 mycroft 182: case PT_INTERP:
1.37 mrg 183: obj->interp = (void *)(uintptr_t)phdr->p_vaddr;
1.41 skrll 184: dbg(("%s: PT_INTERP %p", obj->path, obj->interp));
1.10 mycroft 185: break;
1.1 cgd 186:
1.8 kleink 187: case PT_LOAD:
1.12 mycroft 188: if (nsegs < 2)
189: segs[nsegs] = phdr;
1.4 christos 190: ++nsegs;
1.44 martin 191:
192: #if ELFSIZE == 64
193: #define PRImemsz PRIu64
194: #else
195: #define PRImemsz PRIu32
196: #endif
197: dbg(("%s: %s %p phsize %" PRImemsz, obj->path, "PT_LOAD",
1.43 christos 198: (void *)(uintptr_t)phdr->p_vaddr, phdr->p_memsz));
1.4 christos 199: break;
200:
1.41 skrll 201: case PT_PHDR:
202: phdr_vaddr = phdr->p_vaddr;
203: phdr_memsz = phdr->p_memsz;
1.44 martin 204: dbg(("%s: %s %p phsize %" PRImemsz, obj->path, "PT_PHDR",
1.43 christos 205: (void *)(uintptr_t)phdr->p_vaddr, phdr->p_memsz));
1.41 skrll 206: break;
1.48 skrll 207:
1.8 kleink 208: case PT_DYNAMIC:
1.37 mrg 209: obj->dynamic = (void *)(uintptr_t)phdr->p_vaddr;
1.44 martin 210: dbg(("%s: %s %p phsize %" PRImemsz, obj->path, "PT_DYNAMIC",
1.43 christos 211: (void *)(uintptr_t)phdr->p_vaddr, phdr->p_memsz));
1.4 christos 212: break;
1.42 joerg 213:
214: #if defined(__HAVE_TLS_VARIANT_I) || defined(__HAVE_TLS_VARIANT_II)
215: case PT_TLS:
216: phtls = phdr;
1.44 martin 217: dbg(("%s: %s %p phsize %" PRImemsz, obj->path, "PT_TLS",
1.43 christos 218: (void *)(uintptr_t)phdr->p_vaddr, phdr->p_memsz));
1.42 joerg 219: break;
220: #endif
1.46 skrll 221: #ifdef __ARM_EABI__
222: case PT_ARM_EXIDX:
223: obj->exidx_start = (void *)(uintptr_t)phdr->p_vaddr;
224: obj->exidx_sz = phdr->p_memsz;
225: break;
226: #endif
1.4 christos 227: }
1.1 cgd 228:
1.4 christos 229: ++phdr;
230: }
1.41 skrll 231: phdr = (Elf_Phdr *) ((caddr_t)ehdr + ehdr->e_phoff);
1.37 mrg 232: obj->entry = (void *)(uintptr_t)ehdr->e_entry;
1.22 mycroft 233: if (!obj->dynamic) {
1.12 mycroft 234: _rtld_error("%s: not dynamically linked", path);
1.16 mycroft 235: goto bad;
1.12 mycroft 236: }
237: if (nsegs != 2) {
238: _rtld_error("%s: wrong number of segments (%d != 2)", path,
239: nsegs);
1.16 mycroft 240: goto bad;
1.4 christos 241: }
1.1 cgd 242:
1.4 christos 243: /*
1.11 chs 244: * Map the entire address space of the object as a file
1.5 thorpej 245: * region to stake out our contiguous region and establish a
1.11 chs 246: * base for relocation. We use a file mapping so that
247: * the kernel will give us whatever alignment is appropriate
248: * for the platform we're running on.
1.5 thorpej 249: *
1.11 chs 250: * We map it using the text protection, map the data segment
251: * into the right place, then map an anon segment for the bss
252: * and unmap the gaps left by padding to alignment.
1.5 thorpej 253: */
1.11 chs 254:
1.27 matt 255: #ifdef MAP_ALIGNED
256: base_alignment = segs[0]->p_align;
257: #endif
1.4 christos 258: base_offset = round_down(segs[0]->p_offset);
259: base_vaddr = round_down(segs[0]->p_vaddr);
260: base_vlimit = round_up(segs[1]->p_vaddr + segs[1]->p_memsz);
1.11 chs 261: text_vlimit = round_up(segs[0]->p_vaddr + segs[0]->p_memsz);
1.22 mycroft 262: text_flags = protflags(segs[0]->p_flags);
263: data_offset = round_down(segs[1]->p_offset);
264: data_vaddr = round_down(segs[1]->p_vaddr);
265: data_vlimit = round_up(segs[1]->p_vaddr + segs[1]->p_filesz);
266: data_flags = protflags(segs[1]->p_flags);
1.23 mycroft 267: #ifdef RTLD_LOADER
1.22 mycroft 268: clear_vaddr = segs[1]->p_vaddr + segs[1]->p_filesz;
1.23 mycroft 269: #endif
1.22 mycroft 270:
271: obj->textsize = text_vlimit - base_vaddr;
272: obj->vaddrbase = base_vaddr;
273: obj->isdynamic = ehdr->e_type == ET_DYN;
274:
1.42 joerg 275: #if defined(__HAVE_TLS_VARIANT_I) || defined(__HAVE_TLS_VARIANT_II)
276: if (phtls != NULL) {
277: ++_rtld_tls_dtv_generation;
278: obj->tlsindex = ++_rtld_tls_max_index;
279: obj->tlssize = phtls->p_memsz;
280: obj->tlsalign = phtls->p_align;
281: obj->tlsinitsize = phtls->p_filesz;
282: tls_vaddr = phtls->p_vaddr;
283: }
284: #endif
285:
1.41 skrll 286: obj->phdr_loaded = false;
287: for (i = 0; i < nsegs; i++) {
288: if (phdr_vaddr != EA_UNDEF &&
289: segs[i]->p_vaddr <= phdr_vaddr &&
290: segs[i]->p_memsz >= phdr_memsz) {
291: obj->phdr_loaded = true;
292: break;
293: }
294: if (segs[i]->p_offset <= ehdr->e_phoff &&
295: segs[i]->p_memsz >= phsize) {
296: phdr_vaddr = segs[i]->p_vaddr + ehdr->e_phoff;
297: phdr_memsz = phsize;
298: obj->phdr_loaded = true;
299: break;
300: }
301: }
302: if (obj->phdr_loaded) {
303: obj->phdr = (void *)(uintptr_t)phdr_vaddr;
304: obj->phsize = phdr_memsz;
305: } else {
306: Elf_Phdr *buf;
307: buf = xmalloc(phsize);
308: if (buf == NULL) {
309: _rtld_error("%s: cannot allocate program header", path);
310: goto bad;
311: }
312: memcpy(buf, phdr, phsize);
313: obj->phdr = buf;
314: obj->phsize = phsize;
315: }
316: dbg(("%s: phdr %p phsize %zu (%s)", obj->path, obj->phdr, obj->phsize,
317: obj->phdr_loaded ? "loaded" : "allocated"));
318:
1.36 ad 319: /* Unmap header if it overlaps the first load section. */
320: if (base_offset < _rtld_pagesz) {
321: munmap(ehdr, _rtld_pagesz);
322: obj->ehdr = MAP_FAILED;
323: }
1.11 chs 324:
1.27 matt 325: /*
326: * Calculate log2 of the base section alignment.
327: */
328: mapflags = 0;
329: #ifdef MAP_ALIGNED
330: if (base_alignment > _rtld_pagesz) {
331: unsigned int log2 = 0;
332: for (; base_alignment > 1; base_alignment >>= 1)
333: log2++;
334: mapflags = MAP_ALIGNED(log2);
335: }
336: #endif
337:
1.1 cgd 338: #ifdef RTLD_LOADER
1.22 mycroft 339: base_addr = obj->isdynamic ? NULL : (caddr_t)base_vaddr;
1.1 cgd 340: #else
1.4 christos 341: base_addr = NULL;
1.1 cgd 342: #endif
1.22 mycroft 343: mapsize = base_vlimit - base_vaddr;
1.27 matt 344: mapbase = mmap(base_addr, mapsize, text_flags,
345: mapflags | MAP_FILE | MAP_PRIVATE, fd, base_offset);
1.5 thorpej 346: if (mapbase == MAP_FAILED) {
1.4 christos 347: _rtld_error("mmap of entire address space failed: %s",
348: xstrerror(errno));
1.16 mycroft 349: goto bad;
1.4 christos 350: }
1.11 chs 351:
1.4 christos 352: /* Overlay the data segment onto the proper region. */
353: data_addr = mapbase + (data_vaddr - base_vaddr);
1.22 mycroft 354: if (mmap(data_addr, data_vlimit - data_vaddr, data_flags,
355: MAP_FILE | MAP_PRIVATE | MAP_FIXED, fd, data_offset) ==
356: MAP_FAILED) {
1.4 christos 357: _rtld_error("mmap of data failed: %s", xstrerror(errno));
1.22 mycroft 358: goto bad;
1.11 chs 359: }
360:
361: /* Overlay the bss segment onto the proper region. */
362: if (mmap(mapbase + data_vlimit - base_vaddr, base_vlimit - data_vlimit,
1.22 mycroft 363: data_flags, MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0) ==
364: MAP_FAILED) {
1.11 chs 365: _rtld_error("mmap of bss failed: %s", xstrerror(errno));
1.22 mycroft 366: goto bad;
1.4 christos 367: }
1.5 thorpej 368:
369: /* Unmap the gap between the text and data. */
1.22 mycroft 370: gap_addr = mapbase + round_up(text_vlimit - base_vaddr);
1.5 thorpej 371: gap_size = data_addr - gap_addr;
1.21 mycroft 372: if (gap_size != 0 && mprotect(gap_addr, gap_size, PROT_NONE) == -1) {
373: _rtld_error("mprotect of text -> data gap failed: %s",
1.5 thorpej 374: xstrerror(errno));
1.22 mycroft 375: goto bad;
1.5 thorpej 376: }
377:
1.1 cgd 378: #ifdef RTLD_LOADER
1.4 christos 379: /* Clear any BSS in the last page of the data segment. */
380: clear_addr = mapbase + (clear_vaddr - base_vaddr);
381: if ((nclear = data_vlimit - clear_vaddr) > 0)
382: memset(clear_addr, 0, nclear);
383:
1.5 thorpej 384: /* Non-file portion of BSS mapped above. */
1.1 cgd 385: #endif
386:
1.42 joerg 387: #if defined(__HAVE_TLS_VARIANT_I) || defined(__HAVE_TLS_VARIANT_II)
388: if (phtls != NULL)
389: obj->tlsinit = mapbase + tls_vaddr;
390: #endif
391:
1.4 christos 392: obj->mapbase = mapbase;
393: obj->mapsize = mapsize;
394: obj->relocbase = mapbase - base_vaddr;
1.10 mycroft 395:
1.22 mycroft 396: if (obj->dynamic)
1.37 mrg 397: obj->dynamic = (void *)(obj->relocbase + (Elf_Addr)(uintptr_t)obj->dynamic);
1.22 mycroft 398: if (obj->entry)
1.37 mrg 399: obj->entry = (void *)(obj->relocbase + (Elf_Addr)(uintptr_t)obj->entry);
1.22 mycroft 400: if (obj->interp)
1.37 mrg 401: obj->interp = (void *)(obj->relocbase + (Elf_Addr)(uintptr_t)obj->interp);
1.41 skrll 402: if (obj->phdr_loaded)
403: obj->phdr = (void *)(obj->relocbase + (Elf_Addr)(uintptr_t)obj->phdr);
1.47 skrll 404: #ifdef __ARM_EABI__
405: if (obj->exidx_start)
406: obj->exidx_start = (void *)(obj->relocbase + (Elf_Addr)(uintptr_t)obj->exidx_start);
407: #endif
1.22 mycroft 408:
1.10 mycroft 409: return obj;
1.16 mycroft 410:
411: bad:
1.36 ad 412: if (obj->ehdr != MAP_FAILED)
413: munmap(obj->ehdr, _rtld_pagesz);
1.22 mycroft 414: if (mapbase != MAP_FAILED)
415: munmap(mapbase, mapsize);
416: _rtld_obj_free(obj);
1.16 mycroft 417: return NULL;
1.10 mycroft 418: }
419:
420: void
1.30 skrll 421: _rtld_obj_free(Obj_Entry *obj)
1.10 mycroft 422: {
423: Objlist_Entry *elm;
424:
1.42 joerg 425: #if defined(__HAVE_TLS_VARIANT_I) || defined(__HAVE_TLS_VARIANT_II)
426: if (obj->tls_done)
427: _rtld_tls_offset_free(obj);
428: #endif
1.35 ad 429: xfree(obj->path);
1.10 mycroft 430: while (obj->needed != NULL) {
431: Needed_Entry *needed = obj->needed;
432: obj->needed = needed->next;
1.35 ad 433: xfree(needed);
1.10 mycroft 434: }
1.49 ! christos 435: while (!STAILQ_EMPTY(&obj->names)) {
! 436: Name_Entry *entry = STAILQ_FIRST(&obj->names);
! 437: STAILQ_REMOVE_HEAD(&obj->names, link);
! 438: free(entry);
! 439: }
1.13 lukem 440: while ((elm = SIMPLEQ_FIRST(&obj->dldags)) != NULL) {
441: SIMPLEQ_REMOVE_HEAD(&obj->dldags, link);
1.35 ad 442: xfree(elm);
1.10 mycroft 443: }
1.13 lukem 444: while ((elm = SIMPLEQ_FIRST(&obj->dagmembers)) != NULL) {
445: SIMPLEQ_REMOVE_HEAD(&obj->dagmembers, link);
1.35 ad 446: xfree(elm);
1.10 mycroft 447: }
1.41 skrll 448: if (!obj->phdr_loaded)
449: xfree((void *)(uintptr_t)obj->phdr);
1.35 ad 450: xfree(obj);
1.39 skrll 451: #ifdef COMBRELOC
452: _rtld_combreloc_reset(obj);
453: #endif
1.10 mycroft 454: }
455:
456: Obj_Entry *
457: _rtld_obj_new(void)
458: {
459: Obj_Entry *obj;
460:
461: obj = CNEW(Obj_Entry);
1.49 ! christos 462: STAILQ_INIT(&obj->names);
1.10 mycroft 463: SIMPLEQ_INIT(&obj->dldags);
464: SIMPLEQ_INIT(&obj->dagmembers);
1.4 christos 465: return obj;
1.1 cgd 466: }
467:
468: /*
469: * Given a set of ELF protection flags, return the corresponding protection
470: * flags for MMAP.
471: */
472: static int
1.30 skrll 473: protflags(int elfflags)
1.1 cgd 474: {
1.4 christos 475: int prot = 0;
1.29 simonb 476:
1.8 kleink 477: if (elfflags & PF_R)
1.4 christos 478: prot |= PROT_READ;
1.1 cgd 479: #ifdef RTLD_LOADER
1.8 kleink 480: if (elfflags & PF_W)
1.4 christos 481: prot |= PROT_WRITE;
1.1 cgd 482: #endif
1.8 kleink 483: if (elfflags & PF_X)
1.4 christos 484: prot |= PROT_EXEC;
485: return prot;
1.1 cgd 486: }
CVSweb <webmaster@jp.NetBSD.org>