File: [local] / src / libexec / ld.so / resolve.c (download)
Revision 1.102, Mon Jan 22 02:08:31 2024 UTC (4 months, 2 weeks ago) by deraadt
Branch: MAIN
CVS Tags: OPENBSD_7_5_BASE, OPENBSD_7_5, HEAD Changes since 1.101: +4 -4 lines
ugly whitespaces
|
/* $OpenBSD: resolve.c,v 1.102 2024/01/22 02:08:31 deraadt Exp $ */
/*
* Copyright (c) 1998 Per Fogelstrom, Opsycon AB
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#define _DYN_LOADER
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <limits.h>
#include <link.h>
#include "util.h"
#include "path.h"
#include "resolve.h"
#include "syscall.h"
/* substitution types */
typedef enum {
SUBST_UNKNOWN, SUBST_ORIGIN, SUBST_OSNAME, SUBST_OSREL, SUBST_PLATFORM
} SUBST_TYPES;
struct symlookup {
const char *sl_name;
struct sym_res sl_out;
struct sym_res sl_weak_out;
unsigned long sl_elf_hash;
uint32_t sl_gnu_hash;
int sl_flags;
};
elf_object_t *_dl_objects;
int object_count;
static elf_object_t *_dl_last_object;
elf_object_t *_dl_loading_object;
void
_dl_handle_nodelete(elf_object_t *object)
{
/*
* If a .so is marked nodelete, then the entire load group that it's
* in needs to be kept around forever, so add a reference there.
* XXX It would be better if we tracked inter-object dependencies
* from relocations and didn't leave dangling pointers when a load
* group was partially unloaded. That would render this unnecessary.
*/
if (object->obj_flags & DF_1_NODELETE &&
(object->load_object->status & STAT_NODELETE) == 0) {
DL_DEB(("objname %s is nodelete\n", object->load_name));
object->load_object->opencount++;
object->load_object->status |= STAT_NODELETE;
}
}
/*
* Add a new dynamic object to the object list.
*/
void
_dl_add_object(elf_object_t *object)
{
_dl_handle_nodelete(object);
/*
* if this is a new object, prev will be NULL
* != NULL if an object already in the list
* prev == NULL for the first item in the list, but that will
* be the executable.
*/
if (object->prev != NULL)
return;
if (_dl_objects == NULL) { /* First object ? */
_dl_last_object = _dl_objects = object;
object_count = 2; /* count ld.so early */
} else {
_dl_last_object->next = object;
object->prev = _dl_last_object;
_dl_last_object = object;
if (object->obj_type != OBJTYPE_LDR) /* see above */
object_count++;
}
}
/*
* Identify substitution sequence name.
*/
static int
_dl_subst_name(const char *name, size_t siz) {
switch (siz) {
case 5:
if (_dl_strncmp(name, "OSREL", 5) == 0)
return SUBST_OSREL;
break;
case 6:
if (_dl_strncmp(name, "ORIGIN", 6) == 0)
return SUBST_ORIGIN;
if (_dl_strncmp(name, "OSNAME", 6) == 0)
return SUBST_OSNAME;
break;
case 8:
if (_dl_strncmp(name, "PLATFORM", 8) == 0)
return SUBST_PLATFORM;
break;
}
return (SUBST_UNKNOWN);
}
/*
* Perform $ORIGIN substitutions on path
*/
static void
_dl_origin_subst_path(elf_object_t *object, const char *origin_path,
char **path)
{
char tmp_path[PATH_MAX];
char *new_path, *tp;
const char *pp, *name, *value;
static struct utsname uts;
size_t value_len;
int skip_brace;
if (uts.sysname[0] == '\0') {
if (_dl_uname(&uts) != 0)
return;
}
tp = tmp_path;
pp = *path;
while (*pp != '\0' && (tp - tmp_path) < sizeof(tmp_path)) {
/* copy over chars up to but not including $ */
while (*pp != '\0' && *pp != '$' &&
(tp - tmp_path) < sizeof(tmp_path))
*tp++ = *pp++;
/* substitution sequence detected */
if (*pp == '$' && (tp - tmp_path) < sizeof(tmp_path)) {
pp++;
if ((skip_brace = (*pp == '{')))
pp++;
/* skip over name */
name = pp;
while (_dl_isalnum((unsigned char)*pp) || *pp == '_')
pp++;
switch (_dl_subst_name(name, pp - name)) {
case SUBST_ORIGIN:
value = origin_path;
break;
case SUBST_OSNAME:
value = uts.sysname;
break;
case SUBST_OSREL:
value = uts.release;
break;
case SUBST_PLATFORM:
value = uts.machine;
break;
default:
value = "";
}
value_len = _dl_strlen(value);
if (value_len >= sizeof(tmp_path) - (tp - tmp_path))
return;
_dl_bcopy(value, tp, value_len);
tp += value_len;
if (skip_brace && *pp == '}')
pp++;
}
}
/* no substitution made if result exceeds sizeof(tmp_path) */
if (tp - tmp_path >= sizeof(tmp_path))
return;
/* NULL terminate tmp_path */
*tp = '\0';
if (_dl_strcmp(tmp_path, *path) == 0)
return;
new_path = _dl_strdup(tmp_path);
if (new_path == NULL)
return;
DL_DEB(("orig_path %s\n", *path));
DL_DEB(("new_path %s\n", new_path));
_dl_free(*path);
*path = new_path;
}
/*
* Determine origin_path from object load_name. The origin_path argument
* must refer to a buffer capable of storing at least PATH_MAX characters.
* Returns 0 on success.
*/
static int
_dl_origin_path(elf_object_t *object, char *origin_path)
{
const char *dirname_path;
/* syscall in ld.so returns 0/-errno, where libc returns char* */
if (_dl___realpath(object->load_name, origin_path) < 0)
return -1;
dirname_path = _dl_dirname(origin_path);
if (dirname_path == NULL)
return -1;
_dl_strlcpy(origin_path, dirname_path, PATH_MAX);
return 0;
}
/*
* Perform $ORIGIN substitutions on runpath and rpath
*/
static void
_dl_origin_subst(elf_object_t *object)
{
char origin_path[PATH_MAX];
char **pp;
if (_dl_origin_path(object, origin_path) != 0)
return;
/* perform path substitutions on each segment of runpath and rpath */
if (object->runpath != NULL) {
for (pp = object->runpath; *pp != NULL; pp++)
_dl_origin_subst_path(object, origin_path, pp);
}
if (object->rpath != NULL) {
for (pp = object->rpath; *pp != NULL; pp++)
_dl_origin_subst_path(object, origin_path, pp);
}
}
/*
* Initialize a new dynamic object.
*/
elf_object_t *
_dl_finalize_object(const char *objname, Elf_Dyn *dynp, Elf_Phdr *phdrp,
int phdrc, const int objtype, const long lbase, const long obase)
{
elf_object_t *object;
Elf_Addr gnu_hash = 0;
DL_DEB(("objname [%s], dynp %p, objtype %x lbase %lx, obase %lx\n",
objname, dynp, objtype, lbase, obase));
object = _dl_calloc(1, sizeof(elf_object_t));
if (object == NULL)
_dl_oom();
object->prev = object->next = NULL;
object->load_dyn = dynp;
while (dynp->d_tag != DT_NULL) {
if (dynp->d_tag < DT_NUM)
object->Dyn.info[dynp->d_tag] = dynp->d_un.d_val;
else if (dynp->d_tag >= DT_LOPROC &&
dynp->d_tag < DT_LOPROC + DT_PROCNUM)
object->Dyn.info[dynp->d_tag + DT_NUM - DT_LOPROC] =
dynp->d_un.d_val;
if (dynp->d_tag == DT_TEXTREL)
object->dyn.textrel = 1;
if (dynp->d_tag == DT_SYMBOLIC)
object->dyn.symbolic = 1;
if (dynp->d_tag == DT_BIND_NOW)
object->obj_flags |= DF_1_NOW;
if (dynp->d_tag == DT_FLAGS_1)
object->obj_flags |= dynp->d_un.d_val;
if (dynp->d_tag == DT_FLAGS) {
object->dyn.flags |= dynp->d_un.d_val;
if (dynp->d_un.d_val & DF_SYMBOLIC)
object->dyn.symbolic = 1;
if (dynp->d_un.d_val & DF_TEXTREL)
object->dyn.textrel = 1;
if (dynp->d_un.d_val & DF_ORIGIN)
object->obj_flags |= DF_1_ORIGIN;
if (dynp->d_un.d_val & DF_BIND_NOW)
object->obj_flags |= DF_1_NOW;
}
if (dynp->d_tag == DT_RELACOUNT)
object->relacount = dynp->d_un.d_val;
if (dynp->d_tag == DT_RELCOUNT)
object->relcount = dynp->d_un.d_val;
if (dynp->d_tag == DT_GNU_HASH)
gnu_hash = dynp->d_un.d_val;
dynp++;
}
DL_DEB((" flags %s = 0x%x\n", objname, object->obj_flags));
object->obj_type = objtype;
if (_dl_loading_object == NULL) {
/*
* no loading object, object is the loading object,
* as it is either executable, or dlopened()
*/
_dl_loading_object = object;
}
if ((object->obj_flags & DF_1_NOOPEN) != 0 &&
_dl_loading_object->obj_type == OBJTYPE_DLO &&
!_dl_traceld) {
_dl_free(object);
_dl_errno = DL_CANT_LOAD_OBJ;
return(NULL);
}
/*
* Now relocate all pointer to dynamic info, but only
* the ones which have pointer values.
*/
if (object->Dyn.info[DT_PLTGOT])
object->Dyn.info[DT_PLTGOT] += obase;
if (object->Dyn.info[DT_STRTAB])
object->Dyn.info[DT_STRTAB] += obase;
if (object->Dyn.info[DT_SYMTAB])
object->Dyn.info[DT_SYMTAB] += obase;
if (object->Dyn.info[DT_RELA])
object->Dyn.info[DT_RELA] += obase;
if (object->Dyn.info[DT_SONAME])
object->Dyn.info[DT_SONAME] += object->Dyn.info[DT_STRTAB];
if (object->Dyn.info[DT_RPATH])
object->Dyn.info[DT_RPATH] += object->Dyn.info[DT_STRTAB];
if (object->Dyn.info[DT_RUNPATH])
object->Dyn.info[DT_RUNPATH] += object->Dyn.info[DT_STRTAB];
if (object->Dyn.info[DT_REL])
object->Dyn.info[DT_REL] += obase;
if (object->Dyn.info[DT_INIT])
object->Dyn.info[DT_INIT] += obase;
if (object->Dyn.info[DT_FINI])
object->Dyn.info[DT_FINI] += obase;
if (object->Dyn.info[DT_JMPREL])
object->Dyn.info[DT_JMPREL] += obase;
if (object->Dyn.info[DT_INIT_ARRAY])
object->Dyn.info[DT_INIT_ARRAY] += obase;
if (object->Dyn.info[DT_FINI_ARRAY])
object->Dyn.info[DT_FINI_ARRAY] += obase;
if (object->Dyn.info[DT_PREINIT_ARRAY])
object->Dyn.info[DT_PREINIT_ARRAY] += obase;
if (object->Dyn.info[DT_RELR])
object->Dyn.info[DT_RELR] += obase;
if (gnu_hash) {
Elf_Word *hashtab = (Elf_Word *)(gnu_hash + obase);
Elf_Word nbuckets = hashtab[0];
Elf_Word nmaskwords = hashtab[2];
/* validity check */
if (nbuckets > 0 && (nmaskwords & (nmaskwords - 1)) == 0) {
Elf_Word symndx = hashtab[1];
int bloom_size32 = (ELFSIZE / 32) * nmaskwords;
object->nbuckets = nbuckets;
object->symndx_gnu = symndx;
object->mask_bm_gnu = nmaskwords - 1;
object->shift2_gnu = hashtab[3];
object->bloom_gnu = (Elf_Addr *)(hashtab + 4);
object->buckets_gnu = hashtab + 4 + bloom_size32;
object->chains_gnu = object->buckets_gnu + nbuckets
- symndx;
/*
* If the ELF hash is present, get the total symbol
* count ("nchains") from there. Otherwise, count
* the entries in the GNU hash chain.
*/
if (object->Dyn.info[DT_HASH] == 0) {
Elf_Word n;
for (n = 0; n < nbuckets; n++) {
Elf_Word bkt = object->buckets_gnu[n];
const Elf_Word *hashval;
if (bkt == 0)
continue;
hashval = &object->chains_gnu[bkt];
do {
symndx++;
} while ((*hashval++ & 1U) == 0);
}
object->nchains = symndx;
}
object->status |= STAT_GNU_HASH;
}
}
if (object->Dyn.info[DT_HASH] != 0) {
Elf_Hash_Word *hashtab =
(Elf_Hash_Word *)(object->Dyn.info[DT_HASH] + obase);
object->nchains = hashtab[1];
if (object->nbuckets == 0) {
object->nbuckets = hashtab[0];
object->buckets_elf = hashtab + 2;
object->chains_elf = object->buckets_elf +
object->nbuckets;
}
}
object->phdrp = phdrp;
object->phdrc = phdrc;
object->load_base = lbase;
object->obj_base = obase;
object->load_name = _dl_strdup(objname);
if (object->load_name == NULL)
_dl_oom();
object->load_object = _dl_loading_object;
if (object->load_object == object)
DL_DEB(("head %s\n", object->load_name));
DL_DEB(("obj %s has %s as head\n", object->load_name,
_dl_loading_object->load_name));
object->refcount = 0;
object->opencount = 0; /* # dlopen() & exe */
object->grprefcount = 0;
/* default dev, inode for dlopen-able objects. */
object->dev = 0;
object->inode = 0;
object->grpsym_gen = 0;
TAILQ_INIT(&object->grpref_list);
if (object->dyn.runpath)
object->runpath = _dl_split_path(object->dyn.runpath);
/*
* DT_RPATH is ignored if DT_RUNPATH is present...except in
* the exe, whose DT_RPATH is a fallback for libs that don't
* use DT_RUNPATH
*/
if (object->dyn.rpath && (object->runpath == NULL ||
objtype == OBJTYPE_EXE))
object->rpath = _dl_split_path(object->dyn.rpath);
if ((object->obj_flags & DF_1_ORIGIN) && _dl_trust)
_dl_origin_subst(object);
_dl_trace_object_setup(object);
return (object);
}
static void
_dl_tailq_free(struct dep_node *n)
{
struct dep_node *next;
while (n != NULL) {
next = TAILQ_NEXT(n, next_sib);
_dl_free(n);
n = next;
}
}
static elf_object_t *free_objects;
void
_dl_cleanup_objects()
{
elf_object_t *nobj, *head;
struct dep_node *n, *next;
n = TAILQ_FIRST(&_dlopened_child_list);
while (n != NULL) {
next = TAILQ_NEXT(n, next_sib);
if (OBJECT_DLREF_CNT(n->data) == 0) {
TAILQ_REMOVE(&_dlopened_child_list, n, next_sib);
_dl_free(n);
}
n = next;
}
head = free_objects;
free_objects = NULL;
while (head != NULL) {
_dl_free(head->load_name);
_dl_free((char *)head->sod.sod_name);
_dl_free_path(head->runpath);
_dl_free_path(head->rpath);
_dl_free(head->grpsym_vec.vec);
_dl_free(head->child_vec.vec);
_dl_tailq_free(TAILQ_FIRST(&head->grpref_list));
nobj = head->next;
_dl_free(head);
head = nobj;
}
}
void
_dl_remove_object(elf_object_t *object)
{
object->prev->next = object->next;
if (object->next)
object->next->prev = object->prev;
if (_dl_last_object == object)
_dl_last_object = object->prev;
object_count--;
object->next = free_objects;
free_objects = object;
}
static int
matched_symbol(elf_object_t *obj, const Elf_Sym *sym, struct symlookup *sl)
{
switch (ELF_ST_TYPE(sym->st_info)) {
case STT_FUNC:
/*
* Allow this symbol if we are referring to a function which
* has a value, even if section is UNDEF. This allows &func
* to refer to PLT as per the ELF spec. If flags has SYM_PLT
* set, we must have actual symbol, so this symbol is skipped.
*/
if ((sl->sl_flags & SYM_PLT) && sym->st_shndx == SHN_UNDEF)
return 0;
if (sym->st_value == 0)
return 0;
break;
case STT_NOTYPE:
case STT_OBJECT:
if (sym->st_value == 0)
return 0;
#if 0
/* FALLTHROUGH */
case STT_TLS:
#endif
if (sym->st_shndx == SHN_UNDEF)
return 0;
break;
default:
return 0;
}
if (sym != sl->sl_out.sym &&
_dl_strcmp(sl->sl_name, obj->dyn.strtab + sym->st_name))
return 0;
if (ELF_ST_BIND(sym->st_info) == STB_GLOBAL) {
sl->sl_out.sym = sym;
sl->sl_out.obj = obj;
return 1;
} else if (ELF_ST_BIND(sym->st_info) == STB_WEAK) {
if (sl->sl_weak_out.sym == NULL) {
sl->sl_weak_out.sym = sym;
sl->sl_weak_out.obj = obj;
}
/* done with this object, but need to check other objects */
return -1;
}
return 0;
}
static int
_dl_find_symbol_obj(elf_object_t *obj, struct symlookup *sl)
{
const Elf_Sym *symt = obj->dyn.symtab;
if (obj->status & STAT_GNU_HASH) {
uint32_t hash = sl->sl_gnu_hash;
Elf_Addr bloom_word;
unsigned int h1;
unsigned int h2;
Elf_Word bucket;
const Elf_Word *hashval;
/* pick right bitmask word from Bloom filter array */
bloom_word = obj->bloom_gnu[(hash / ELFSIZE) &
obj->mask_bm_gnu];
/* calculate modulus ELFSIZE of gnu hash and its derivative */
h1 = hash & (ELFSIZE - 1);
h2 = (hash >> obj->shift2_gnu) & (ELFSIZE - 1);
/* Filter out the "definitely not in set" queries */
if (((bloom_word >> h1) & (bloom_word >> h2) & 1) == 0)
return 0;
/* Locate hash chain and corresponding value element */
bucket = obj->buckets_gnu[hash % obj->nbuckets];
if (bucket == 0)
return 0;
hashval = &obj->chains_gnu[bucket];
do {
if (((*hashval ^ hash) >> 1) == 0) {
const Elf_Sym *sym = symt +
(hashval - obj->chains_gnu);
int r = matched_symbol(obj, sym, sl);
if (r)
return r > 0;
}
} while ((*hashval++ & 1U) == 0);
} else {
Elf_Word si;
for (si = obj->buckets_elf[sl->sl_elf_hash % obj->nbuckets];
si != STN_UNDEF; si = obj->chains_elf[si]) {
const Elf_Sym *sym = symt + si;
int r = matched_symbol(obj, sym, sl);
if (r)
return r > 0;
}
}
return 0;
}
struct sym_res
_dl_find_symbol(const char *name, int flags, const Elf_Sym *ref_sym,
elf_object_t *req_obj)
{
const unsigned char *p;
unsigned char c;
struct symlookup sl = {
.sl_name = name,
.sl_out = { .sym = NULL },
.sl_weak_out = { .sym = NULL },
.sl_elf_hash = 0,
.sl_gnu_hash = 5381,
.sl_flags = flags,
};
/* Calculate both hashes in one pass */
for (p = (const unsigned char *)name; (c = *p) != '\0'; p++) {
sl.sl_elf_hash = (sl.sl_elf_hash << 4) + c;
sl.sl_elf_hash ^= (sl.sl_elf_hash >> 24) & 0xf0;
sl.sl_gnu_hash = sl.sl_gnu_hash * 33 + c;
}
sl.sl_elf_hash &= 0x0fffffff;
if (req_obj->dyn.symbolic)
if (_dl_find_symbol_obj(req_obj, &sl))
goto found;
if (flags & SYM_DLSYM) {
struct object_vector vec;
int i;
if (_dl_find_symbol_obj(req_obj, &sl))
goto found;
/* weak definition in the specified object is good enough */
if (sl.sl_weak_out.sym != NULL)
goto found;
/* search dlopened obj and all children */
vec = req_obj->load_object->grpsym_vec;
for (i = 0; i < vec.len; i++) {
if (vec.vec[i] == req_obj)
continue; /* already searched */
if (_dl_find_symbol_obj(vec.vec[i], &sl))
goto found;
}
} else {
struct dep_node *n;
struct object_vector vec;
int i, skip = 0;
if ((flags & SYM_SEARCH_SELF) || (flags & SYM_SEARCH_NEXT))
skip = 1;
/*
* search dlopened objects: global or req_obj == dlopened_obj
* and its children
*/
TAILQ_FOREACH(n, &_dlopened_child_list, next_sib) {
if (((n->data->obj_flags & DF_1_GLOBAL) == 0) &&
(n->data != req_obj->load_object))
continue;
vec = n->data->grpsym_vec;
for (i = 0; i < vec.len; i++) {
if (skip == 1) {
if (vec.vec[i] == req_obj) {
skip = 0;
if (flags & SYM_SEARCH_NEXT)
continue;
} else
continue;
}
if ((flags & SYM_SEARCH_OTHER) &&
(vec.vec[i] == req_obj))
continue;
if (_dl_find_symbol_obj(vec.vec[i], &sl))
goto found;
}
}
}
found:
if (sl.sl_out.sym == NULL) {
if (sl.sl_weak_out.sym != NULL)
sl.sl_out = sl.sl_weak_out;
else {
if ((ref_sym == NULL ||
(ELF_ST_BIND(ref_sym->st_info) != STB_WEAK)) &&
(flags & SYM_WARNNOTFOUND))
_dl_printf("%s:%s: undefined symbol '%s'\n",
__progname, req_obj->load_name, name);
return (struct sym_res){ NULL, NULL };
}
}
if (ref_sym != NULL && ref_sym->st_size != 0 &&
(ref_sym->st_size != sl.sl_out.sym->st_size) &&
(ELF_ST_TYPE(sl.sl_out.sym->st_info) != STT_FUNC)) {
_dl_printf("%s:%s: %s : WARNING: "
"symbol(%s) size mismatch, relink your program\n",
__progname, req_obj->load_name, sl.sl_out.obj->load_name,
name);
}
return sl.sl_out;
}
void
_dl_debug_state(void)
{
/* Debugger stub */
}
/*
* Search for DT_SONAME, and check if this is libc
*/
int
_dl_islibc(Elf_Dyn *_dynp, Elf_Addr loff)
{
Elf_Dyn *d, *dynp = (Elf_Dyn *)((unsigned long)_dynp + loff);
long base = 0;
for (d = dynp; d->d_tag != DT_NULL; d++)
if (d->d_tag == DT_STRTAB) {
base = d->d_un.d_ptr + loff;
break;
}
if (base == 0)
return 0;
for (d = dynp; d->d_tag != DT_NULL; d++)
if (d->d_tag == DT_SONAME) {
if (_dl_strncmp((char *)(base + d->d_un.d_ptr),
"libc.so.", 8) == 0)
return 1;
break;
}
return 0;
}
void
_dl_pin(int file, Elf_Phdr *phdp, void *base, size_t len,
void *exec_base, size_t exec_size)
{
struct pinsyscalls {
u_int offset;
u_int sysno;
} *syscalls;
int npins = 0, nsyscalls, i;
u_int *pins = NULL;
vaddr_t offset;
if (phdp->p_filesz > SYS_MAXSYSCALL * 2 * sizeof(*syscalls) ||
phdp->p_filesz % sizeof(*syscalls) != 0 ||
phdp->p_offset & 0x3)
return;
syscalls = _dl_mmap(NULL, phdp->p_filesz, PROT_READ,
MAP_PRIVATE|MAP_FILE, file, phdp->p_offset);
if (syscalls == MAP_FAILED)
return;
/* Validate, and calculate pintable size */
nsyscalls = phdp->p_filesz / sizeof(*syscalls);
for (i = 0; i < nsyscalls; i++) {
if (syscalls[i].sysno < 0 ||
syscalls[i].sysno >= SYS_MAXSYSCALL ||
syscalls[i].offset >= len)
goto bad;
npins = MAXIMUM(npins, syscalls[i].sysno);
}
npins++;
/*
* Fill pintable: 0 = invalid, -1 = accept, else offset
* from base, rebase to text_start while at it
*/
pins = _dl_calloc(npins, sizeof(u_int));
offset = exec_base - base;
for (i = 0; i < nsyscalls; i++) {
if (pins[syscalls[i].sysno])
pins[syscalls[i].sysno] = (u_int)-1; /* duplicated */
else
pins[syscalls[i].sysno] = syscalls[i].offset - offset;
}
base += offset;
len = len - offset;
bad:
_dl_munmap(syscalls, phdp->p_filesz);
if (pins)
_dl_pinsyscalls(base, len, pins, npins);
_dl_free(pins);
}