diff --git a/lib/libproc/_libproc.h b/lib/libproc/_libproc.h index 8026128544b..b1ed8e3e88f 100644 --- a/lib/libproc/_libproc.h +++ b/lib/libproc/_libproc.h @@ -32,12 +32,24 @@ #include #include +#include #include #include "libproc.h" struct procstat; +struct file_info { + Elf *elf; + int fd; + u_int refs; +}; + +struct map_info { + prmap_t map; + struct file_info *file; +}; + struct proc_handle { struct proc_handle_public public; /* Public fields. */ int flags; /* Process flags. */ @@ -45,13 +57,13 @@ struct proc_handle { int wstat; /* Process wait status. */ int model; /* Process data model. */ rd_agent_t *rdap; /* librtld_db agent */ - rd_loadobj_t *rdobjs; /* Array of loaded objects. */ - size_t rdobjsz; /* Array size. */ - size_t nobjs; /* Num. objects currently loaded. */ - rd_loadobj_t *rdexec; /* rdobj for program executable. */ - struct lwpstatus lwps; /* Process status. */ + struct map_info *mappings; /* File mappings for proc. */ + size_t maparrsz; /* Map array size. */ + size_t nmappings; /* Number of mappings. */ + prmap_t *exec_map; /* Executable text mapping. */ + lwpstatus_t lwps; /* Process status. */ struct procstat *procstat; /* libprocstat handle. */ - char execpath[MAXPATHLEN]; /* Path to program executable. */ + char execpath[PATH_MAX]; /* Path to program executable. */ }; #ifdef DEBUG diff --git a/lib/libproc/libproc.h b/lib/libproc/libproc.h index a41606f3ac2..5798a3b4b58 100644 --- a/lib/libproc/libproc.h +++ b/lib/libproc/libproc.h @@ -131,7 +131,7 @@ char *proc_objname(struct proc_handle *, uintptr_t, char *, size_t); prmap_t *proc_obj2map(struct proc_handle *, const char *); int proc_iter_objs(struct proc_handle *, proc_map_f *, void *); int proc_iter_symbyaddr(struct proc_handle *, const char *, int, - int, proc_sym_f *, void *); + int, proc_sym_f *, void *); int proc_addr2sym(struct proc_handle *, uintptr_t, char *, size_t, GElf_Sym *); int proc_attach(pid_t pid, int flags, struct proc_handle **pphdl); int proc_continue(struct proc_handle *); diff --git a/lib/libproc/proc_create.c b/lib/libproc/proc_create.c index ce8ecd2d932..a4ac3cc8a15 100644 --- a/lib/libproc/proc_create.c +++ b/lib/libproc/proc_create.c @@ -230,8 +230,24 @@ bad: void proc_free(struct proc_handle *phdl) { + struct file_info *file; + size_t i; + for (i = 0; i < phdl->nmappings; i++) { + file = phdl->mappings[i].file; + if (file != NULL && --file->refs == 0) { + if (file->elf != NULL) { + (void)elf_end(file->elf); + (void)close(file->fd); + } + free(file); + } + } + if (phdl->maparrsz > 0) + free(phdl->mappings); if (phdl->procstat != NULL) procstat_close(phdl->procstat); + if (phdl->rdap != NULL) + rd_delete(phdl->rdap); free(phdl); } diff --git a/lib/libproc/proc_rtld.c b/lib/libproc/proc_rtld.c index 22aa8d1072b..7774a3e4842 100644 --- a/lib/libproc/proc_rtld.c +++ b/lib/libproc/proc_rtld.c @@ -31,46 +31,99 @@ __FBSDID("$FreeBSD$"); #include -#include #include +#include + #include #include "_libproc.h" +static void rdl2prmap(const rd_loadobj_t *, prmap_t *); + static int map_iter(const rd_loadobj_t *lop, void *arg) { - struct proc_handle *phdl = arg; + struct file_info *file; + struct map_info *mapping, *tmp; + struct proc_handle *phdl; + size_t i; - if (phdl->nobjs >= phdl->rdobjsz) { - phdl->rdobjsz *= 2; - phdl->rdobjs = reallocf(phdl->rdobjs, sizeof(*phdl->rdobjs) * - phdl->rdobjsz); - if (phdl->rdobjs == NULL) + phdl = arg; + if (phdl->nmappings >= phdl->maparrsz) { + phdl->maparrsz *= 2; + tmp = reallocarray(phdl->mappings, phdl->maparrsz, + sizeof(*phdl->mappings)); + if (tmp == NULL) return (-1); + phdl->mappings = tmp; } + + mapping = &phdl->mappings[phdl->nmappings]; + rdl2prmap(lop, &mapping->map); if (strcmp(lop->rdl_path, phdl->execpath) == 0 && (lop->rdl_prot & RD_RDL_X) != 0) - phdl->rdexec = &phdl->rdobjs[phdl->nobjs]; - memcpy(&phdl->rdobjs[phdl->nobjs++], lop, sizeof(*lop)); + phdl->exec_map = &mapping->map; + file = NULL; + if (lop->rdl_path[0] != '\0') { + /* Look for an existing mapping of the same file. */ + for (i = 0; i < phdl->nmappings; i++) + if (strcmp(mapping->map.pr_mapname, + phdl->mappings[i].map.pr_mapname) == 0) { + file = phdl->mappings[i].file; + break; + } + + if (file == NULL) { + file = malloc(sizeof(*file)); + if (file == NULL) + return (-1); + file->elf = NULL; + file->fd = -1; + file->refs = 1; + } else + file->refs++; + } + mapping->file = file; + phdl->nmappings++; return (0); } +static void +rdl2prmap(const rd_loadobj_t *rdl, prmap_t *map) +{ + + map->pr_vaddr = rdl->rdl_saddr; + map->pr_size = rdl->rdl_eaddr - rdl->rdl_saddr; + map->pr_offset = rdl->rdl_offset; + map->pr_mflags = 0; + if (rdl->rdl_prot & RD_RDL_R) + map->pr_mflags |= MA_READ; + if (rdl->rdl_prot & RD_RDL_W) + map->pr_mflags |= MA_WRITE; + if (rdl->rdl_prot & RD_RDL_X) + map->pr_mflags |= MA_EXEC; + (void)strlcpy(map->pr_mapname, rdl->rdl_path, + sizeof(map->pr_mapname)); +} + rd_agent_t * proc_rdagent(struct proc_handle *phdl) { + if (phdl->rdap == NULL && phdl->status != PS_UNDEAD && phdl->status != PS_IDLE) { - if ((phdl->rdap = rd_new(phdl)) != NULL) { - phdl->rdobjs = malloc(sizeof(*phdl->rdobjs) * 64); - phdl->rdobjsz = 64; - if (phdl->rdobjs == NULL) - return (phdl->rdap); - rd_loadobj_iter(phdl->rdap, map_iter, phdl); - } - } + if ((phdl->rdap = rd_new(phdl)) == NULL) + return (NULL); + phdl->maparrsz = 64; + phdl->mappings = calloc(phdl->maparrsz, + sizeof(*phdl->mappings)); + if (phdl->mappings == NULL) + return (phdl->rdap); + if (rd_loadobj_iter(phdl->rdap, map_iter, phdl) != RD_OK) + return (NULL); + } return (phdl->rdap); } @@ -78,7 +131,6 @@ void proc_updatesyms(struct proc_handle *phdl) { - memset(phdl->rdobjs, 0, sizeof(*phdl->rdobjs) * phdl->rdobjsz); - phdl->nobjs = 0; + memset(phdl->mappings, 0, sizeof(*phdl->mappings) * phdl->maparrsz); rd_loadobj_iter(phdl->rdap, map_iter, phdl); } diff --git a/lib/libproc/proc_sym.c b/lib/libproc/proc_sym.c index dccf397b2ed..cb950f2cb3d 100644 --- a/lib/libproc/proc_sym.c +++ b/lib/libproc/proc_sym.c @@ -1,4 +1,5 @@ /*- + * Copyright (c) 2016 Mark Johnston * Copyright (c) 2010 The FreeBSD Foundation * Copyright (c) 2008 John Birrell (jb@freebsd.org) * All rights reserved. @@ -64,8 +65,6 @@ typedef struct ctf_file ctf_file_t; extern char *__cxa_demangle(const char *, char *, size_t *, int *); #endif /* NO_CXA_DEMANGLE */ -static void proc_rdl2prmap(rd_loadobj_t *, prmap_t *); - static int crc32_file(int fd, uint32_t *crc) { @@ -131,18 +130,27 @@ out: * returned. */ static int -open_object(prmap_t *map, Elf **elfp, int *fdp) +open_object(struct map_info *mapping) { char path[PATH_MAX]; GElf_Shdr shdr; Elf *e, *e2; Elf_Data *data; Elf_Scn *scn; + struct file_info *file; + prmap_t *map; const char *debugfile, *scnname; size_t ndx; uint32_t crc; int fd, fd2; + if (mapping->map.pr_mapname[0] == '\0') + return (-1); /* anonymous object */ + if (mapping->file->elf != NULL) + return (0); /* already loaded */ + + file = mapping->file; + map = &mapping->map; if ((fd = open(map->pr_mapname, O_RDONLY | O_CLOEXEC)) < 0) { DPRINTF("ERROR: open %s failed", map->pr_mapname); return (-1); @@ -225,8 +233,8 @@ open_object(prmap_t *map, Elf **elfp, int *fdp) internal: /* We didn't find a debug file, just return the object's descriptor. */ - *elfp = e; - *fdp = fd; + file->elf = e; + file->fd = fd; return (0); external: @@ -237,8 +245,8 @@ external: } (void)elf_end(e); (void)close(fd); - *elfp = e2; - *fdp = fd2; + file->elf = e2; + file->fd = fd2; return (0); err: @@ -248,164 +256,105 @@ err: return (-1); } -static void -proc_rdl2prmap(rd_loadobj_t *rdl, prmap_t *map) -{ - map->pr_vaddr = rdl->rdl_saddr; - map->pr_size = rdl->rdl_eaddr - rdl->rdl_saddr; - map->pr_offset = rdl->rdl_offset; - map->pr_mflags = 0; - if (rdl->rdl_prot & RD_RDL_R) - map->pr_mflags |= MA_READ; - if (rdl->rdl_prot & RD_RDL_W) - map->pr_mflags |= MA_WRITE; - if (rdl->rdl_prot & RD_RDL_X) - map->pr_mflags |= MA_EXEC; - strlcpy(map->pr_mapname, rdl->rdl_path, - sizeof(map->pr_mapname)); -} - char * proc_objname(struct proc_handle *p, uintptr_t addr, char *objname, size_t objnamesz) { + prmap_t *map; size_t i; - rd_loadobj_t *rdl; - for (i = 0; i < p->nobjs; i++) { - rdl = &p->rdobjs[i]; - if (addr >= rdl->rdl_saddr && addr < rdl->rdl_eaddr) { - strlcpy(objname, rdl->rdl_path, objnamesz); + for (i = 0; i < p->nmappings; i++) { + map = &p->mappings[i].map; + if (addr >= map->pr_vaddr && + addr < map->pr_vaddr + map->pr_size) { + strlcpy(objname, map->pr_mapname, objnamesz); return (objname); } } return (NULL); } +/* + * Currently just returns the first mapping of the named object, effectively + * what Plmid_to_map(p, PR_LMID_EVERY, objname) does in illumos libproc. + */ prmap_t * proc_obj2map(struct proc_handle *p, const char *objname) { - size_t i; + char path[PATH_MAX], *base; prmap_t *map; - rd_loadobj_t *rdl; - char path[MAXPATHLEN]; + size_t i; - rdl = NULL; - for (i = 0; i < p->nobjs; i++) { - basename_r(p->rdobjs[i].rdl_path, path); - if (strcmp(path, objname) == 0) { - rdl = &p->rdobjs[i]; + map = NULL; + for (i = 0; i < p->nmappings; i++) { + strlcpy(path, p->mappings[i].map.pr_mapname, sizeof(path)); + base = basename(path); + if (strcmp(base, objname) == 0) { + map = &p->mappings[i].map; break; } } - if (rdl == NULL) { - if (strcmp(objname, "a.out") == 0 && p->rdexec != NULL) - rdl = p->rdexec; - else - return (NULL); - } - - if ((map = malloc(sizeof(*map))) == NULL) - return (NULL); - proc_rdl2prmap(rdl, map); + if (map == NULL && strcmp(objname, "a.out") == 0 && p->exec_map != NULL) + map = p->exec_map; return (map); } int proc_iter_objs(struct proc_handle *p, proc_map_f *func, void *cd) { + char last[MAXPATHLEN], path[MAXPATHLEN], *base; + prmap_t *map; size_t i; - rd_loadobj_t *rdl; - prmap_t map; - char path[MAXPATHLEN]; - char last[MAXPATHLEN]; int error; - if (p->nobjs == 0) - return (-1); + if (p->nmappings == 0) + if (proc_rdagent(p) == NULL) + return (-1); error = 0; memset(last, 0, sizeof(last)); - for (i = 0; i < p->nobjs; i++) { - rdl = &p->rdobjs[i]; - proc_rdl2prmap(rdl, &map); - basename_r(rdl->rdl_path, path); + for (i = 0; i < p->nmappings; i++) { + map = &p->mappings[i].map; + strlcpy(path, map->pr_mapname, sizeof(path)); + base = basename(path); /* * We shouldn't call the callback twice with the same object. * To do that we are assuming the fact that if there are * repeated object names (i.e. different mappings for the * same object) they occur next to each other. */ - if (strcmp(path, last) == 0) + if (strcmp(base, last) == 0) continue; - if ((error = (*func)(cd, &map, path)) != 0) + if ((error = (*func)(cd, map, base)) != 0) break; strlcpy(last, path, sizeof(last)); } return (error); } +static struct map_info * +_proc_addr2map(struct proc_handle *p, uintptr_t addr) +{ + struct map_info *mapping; + size_t i; + + if (p->nmappings == 0) + if (proc_rdagent(p) == NULL) + return (NULL); + for (i = 0; i < p->nmappings; i++) { + mapping = &p->mappings[i]; + if (addr >= mapping->map.pr_vaddr && + addr < mapping->map.pr_vaddr + mapping->map.pr_size) + return (mapping); + } + return (NULL); +} + prmap_t * proc_addr2map(struct proc_handle *p, uintptr_t addr) { - size_t i; - int cnt, lastvn = 0; - prmap_t *map; - rd_loadobj_t *rdl; - struct kinfo_vmentry *kves, *kve; - /* - * If we don't have a cache of listed objects, we need to query - * it ourselves. - */ - if (p->nobjs == 0) { - if ((kves = kinfo_getvmmap(proc_getpid(p), &cnt)) == NULL) - return (NULL); - for (i = 0; i < (size_t)cnt; i++) { - kve = kves + i; - if (kve->kve_type == KVME_TYPE_VNODE) - lastvn = i; - if (addr >= kve->kve_start && addr < kve->kve_end) { - if ((map = malloc(sizeof(*map))) == NULL) { - free(kves); - return (NULL); - } - map->pr_vaddr = kve->kve_start; - map->pr_size = kve->kve_end - kve->kve_start; - map->pr_offset = kve->kve_offset; - map->pr_mflags = 0; - if (kve->kve_protection & KVME_PROT_READ) - map->pr_mflags |= MA_READ; - if (kve->kve_protection & KVME_PROT_WRITE) - map->pr_mflags |= MA_WRITE; - if (kve->kve_protection & KVME_PROT_EXEC) - map->pr_mflags |= MA_EXEC; - if (kve->kve_flags & KVME_FLAG_COW) - map->pr_mflags |= MA_COW; - if (kve->kve_flags & KVME_FLAG_NEEDS_COPY) - map->pr_mflags |= MA_NEEDS_COPY; - if (kve->kve_flags & KVME_FLAG_NOCOREDUMP) - map->pr_mflags |= MA_NOCOREDUMP; - strlcpy(map->pr_mapname, kves[lastvn].kve_path, - sizeof(map->pr_mapname)); - free(kves); - return (map); - } - } - free(kves); - return (NULL); - } - - for (i = 0; i < p->nobjs; i++) { - rdl = &p->rdobjs[i]; - if (addr >= rdl->rdl_saddr && addr < rdl->rdl_eaddr) { - if ((map = malloc(sizeof(*map))) == NULL) - return (NULL); - proc_rdl2prmap(rdl, map); - return (map); - } - } - return (NULL); + return (&_proc_addr2map(p, addr)->map); } /* @@ -452,21 +401,23 @@ proc_addr2sym(struct proc_handle *p, uintptr_t addr, char *name, GElf_Ehdr ehdr; GElf_Shdr shdr; Elf *e; - Elf_Scn *scn, *dynsymscn = NULL, *symtabscn = NULL; - prmap_t *map; + Elf_Scn *scn, *dynsymscn, *symtabscn; + struct map_info *mapping; const char *s; uintptr_t off; - u_long symtabstridx = 0, dynsymstridx = 0; - int fd, error = -1; + u_long symtabstridx, dynsymstridx; + int error = -1; - if ((map = proc_addr2map(p, addr)) == NULL) { + if ((mapping = _proc_addr2map(p, addr)) == NULL) { DPRINTFX("ERROR: proc_addr2map failed to resolve 0x%jx", addr); return (-1); } - if (open_object(map, &e, &fd) != 0) { - DPRINTFX("ERROR: failed to open object %s", map->pr_mapname); + if (open_object(mapping) != 0) { + DPRINTFX("ERROR: failed to open object %s", + mapping->map.pr_mapname); return (-1); } + e = mapping->file->elf; if (gelf_getehdr(e, &ehdr) == NULL) { DPRINTFX("ERROR: gelf_getehdr() failed: %s", elf_errmsg(-1)); goto err; @@ -476,7 +427,8 @@ proc_addr2sym(struct proc_handle *p, uintptr_t addr, char *name, * Find the index of the STRTAB and SYMTAB sections to locate * symbol names. */ - scn = NULL; + symtabstridx = dynsymstridx = 0; + scn = dynsymscn = symtabscn = NULL; while ((scn = elf_nextscn(e, scn)) != NULL) { gelf_getshdr(scn, &shdr); switch (shdr.sh_type) { @@ -491,7 +443,7 @@ proc_addr2sym(struct proc_handle *p, uintptr_t addr, char *name, } } - off = ehdr.e_type == ET_EXEC ? 0 : map->pr_vaddr; + off = ehdr.e_type == ET_EXEC ? 0 : mapping->map.pr_vaddr; /* * First look up the symbol in the dynsymtab, and fall back to the @@ -508,56 +460,39 @@ proc_addr2sym(struct proc_handle *p, uintptr_t addr, char *name, out: demangle(s, name, namesz); err: - (void)elf_end(e); - (void)close(fd); - free(map); return (error); } +static struct map_info * +_proc_name2map(struct proc_handle *p, const char *name) +{ + char path[MAXPATHLEN], *base; + struct map_info *mapping; + size_t i; + + mapping = NULL; + if (p->nmappings == 0) + if (proc_rdagent(p) == NULL) + return (NULL); + for (i = 0; i < p->nmappings; i++) { + mapping = &p->mappings[i]; + (void)strlcpy(path, mapping->map.pr_mapname, sizeof(path)); + base = basename(path); + if (strcmp(base, name) == 0) + break; + } + if (i == p->nmappings) + mapping = NULL; + if (mapping == NULL && strcmp(name, "a.out") == 0) + mapping = _proc_addr2map(p, p->exec_map->pr_vaddr); + return (mapping); +} + prmap_t * proc_name2map(struct proc_handle *p, const char *name) { - size_t i; - int cnt; - prmap_t *map = NULL; - char tmppath[MAXPATHLEN]; - struct kinfo_vmentry *kves, *kve; - rd_loadobj_t *rdl; - /* - * If we haven't iterated over the list of loaded objects, - * librtld_db isn't yet initialized and it's very likely - * that librtld_db called us. We need to do the heavy - * lifting here to find the symbol librtld_db is looking for. - */ - if (p->nobjs == 0) { - if ((kves = kinfo_getvmmap(proc_getpid(p), &cnt)) == NULL) - return (NULL); - for (i = 0; i < (size_t)cnt; i++) { - kve = kves + i; - basename_r(kve->kve_path, tmppath); - if (strcmp(tmppath, name) == 0) { - map = proc_addr2map(p, kve->kve_start); - break; - } - } - free(kves); - } else - for (i = 0; i < p->nobjs; i++) { - rdl = &p->rdobjs[i]; - basename_r(rdl->rdl_path, tmppath); - if (strcmp(tmppath, name) == 0) { - if ((map = malloc(sizeof(*map))) == NULL) - return (NULL); - proc_rdl2prmap(rdl, map); - break; - } - } - - if (map == NULL && strcmp(name, "a.out") == 0 && p->rdexec != NULL) - map = proc_addr2map(p, p->rdexec->rdl_saddr); - - return (map); + return (&_proc_name2map(p, name)->map); } /* @@ -592,23 +527,25 @@ int proc_name2sym(struct proc_handle *p, const char *object, const char *symbol, GElf_Sym *symcopy, prsyminfo_t *si) { - Elf *e; - Elf_Scn *scn, *dynsymscn = NULL, *symtabscn = NULL; - GElf_Shdr shdr; GElf_Ehdr ehdr; - prmap_t *map; + GElf_Shdr shdr; + Elf *e; + Elf_Scn *scn, *dynsymscn, *symtabscn; + struct map_info *mapping; uintptr_t off; - u_long symtabstridx = 0, dynsymstridx = 0; - int fd, error = -1; + u_long symtabstridx, dynsymstridx; + int error = -1; - if ((map = proc_name2map(p, object)) == NULL) { + if ((mapping = _proc_name2map(p, object)) == NULL) { DPRINTFX("ERROR: proc_name2map failed to resolve %s", object); return (-1); } - if (open_object(map, &e, &fd) != 0) { - DPRINTFX("ERROR: failed to open object %s", map->pr_mapname); + if (open_object(mapping) != 0) { + DPRINTFX("ERROR: failed to open object %s", + mapping->map.pr_mapname); return (-1); } + e = mapping->file->elf; if (gelf_getehdr(e, &ehdr) == NULL) { DPRINTFX("ERROR: gelf_getehdr() failed: %s", elf_errmsg(-1)); goto err; @@ -618,7 +555,8 @@ proc_name2sym(struct proc_handle *p, const char *object, const char *symbol, * Find the index of the STRTAB and SYMTAB sections to locate * symbol names. */ - scn = NULL; + symtabstridx = dynsymstridx = 0; + scn = dynsymscn = symtabscn = NULL; while ((scn = elf_nextscn(e, scn)) != NULL) { gelf_getshdr(scn, &shdr); switch (shdr.sh_type) { @@ -646,13 +584,10 @@ proc_name2sym(struct proc_handle *p, const char *object, const char *symbol, goto out; out: - off = ehdr.e_type == ET_EXEC ? 0 : map->pr_vaddr; + off = ehdr.e_type == ET_EXEC ? 0 : mapping->map.pr_vaddr; symcopy->st_value += off; err: - (void)elf_end(e); - (void)close(fd); - free(map); return (error); } @@ -668,7 +603,6 @@ proc_name2ctf(struct proc_handle *p, const char *name) return (NULL); ctf = ctf_open(map->pr_mapname, &error); - free(map); return (ctf); #else (void)p; @@ -681,26 +615,27 @@ int proc_iter_symbyaddr(struct proc_handle *p, const char *object, int which, int mask, proc_sym_f *func, void *cd) { - Elf *e; - int i, fd; - prmap_t *map; - Elf_Scn *scn, *foundscn; - Elf_Data *data; GElf_Ehdr ehdr; GElf_Shdr shdr; GElf_Sym sym; - unsigned long stridx = -1; + Elf *e; + Elf_Scn *scn, *foundscn; + Elf_Data *data; + struct map_info *mapping; char *s; - int error = -1; + unsigned long stridx = -1; + int error = -1, i; - if ((map = proc_name2map(p, object)) == NULL) { + if ((mapping = _proc_name2map(p, object)) == NULL) { DPRINTFX("ERROR: proc_name2map failed to resolve %s", object); return (-1); } - if (open_object(map, &e, &fd) != 0) { - DPRINTFX("ERROR: failed to open object %s", map->pr_mapname); + if (open_object(mapping) != 0) { + DPRINTFX("ERROR: failed to open object %s", + mapping->map.pr_mapname); return (-1); } + e = mapping->file->elf; if (gelf_getehdr(e, &ehdr) == NULL) { DPRINTFX("ERROR: gelf_getehdr() failed: %s", elf_errmsg(-1)); goto err; @@ -755,14 +690,11 @@ proc_iter_symbyaddr(struct proc_handle *p, const char *object, int which, continue; s = elf_strptr(e, stridx, sym.st_name); if (ehdr.e_type != ET_EXEC) - sym.st_value += map->pr_vaddr; + sym.st_value += mapping->map.pr_vaddr; if ((error = (*func)(cd, &sym, s)) != 0) goto err; } error = 0; err: - elf_end(e); - close(fd); - free(map); return (error); }