static int analyse_elf(struct pkg *pkg, const char *fpath, int (action)(void *, struct pkg *, const char *, const char *, bool), void *actdata) { Elf *e = NULL; GElf_Ehdr elfhdr; Elf_Scn *scn = NULL; Elf_Scn *note = NULL; Elf_Scn *dynamic = NULL; GElf_Shdr shdr; Elf_Data *data; GElf_Dyn *dyn, dyn_mem; struct stat sb; int ret = EPKG_OK; size_t numdyn = 0; size_t sh_link = 0; size_t dynidx; const char *osname; const char *shlib; bool developer = false; bool is_shlib = false; pkg_config_bool(PKG_CONFIG_DEVELOPER_MODE, &developer); int fd; if (lstat(fpath, &sb) != 0) pkg_emit_errno("fstat() failed for", fpath); /* ignore empty files and non regular files */ if (sb.st_size == 0 || !S_ISREG(sb.st_mode)) return (EPKG_END); /* Empty file or sym-link: no results */ if ((fd = open(fpath, O_RDONLY, 0)) < 0) { return (EPKG_FATAL); } if ((e = elf_begin(fd, ELF_C_READ, NULL)) == NULL) { ret = EPKG_FATAL; pkg_emit_error("elf_begin() for %s failed: %s", fpath, elf_errmsg(-1)); goto cleanup; } if (elf_kind(e) != ELF_K_ELF) { /* Not an elf file: no results */ ret = EPKG_END; goto cleanup; } if (developer) pkg->flags |= PKG_CONTAINS_ELF_OBJECTS; if (gelf_getehdr(e, &elfhdr) == NULL) { ret = EPKG_FATAL; pkg_emit_error("getehdr() failed: %s.", elf_errmsg(-1)); goto cleanup; } while ((scn = elf_nextscn(e, scn)) != NULL) { if (gelf_getshdr(scn, &shdr) != &shdr) { ret = EPKG_FATAL; pkg_emit_error("getshdr() for %s failed: %s", fpath, elf_errmsg(-1)); goto cleanup; } switch (shdr.sh_type) { case SHT_NOTE: note = scn; break; case SHT_DYNAMIC: dynamic = scn; sh_link = shdr.sh_link; numdyn = shdr.sh_size / shdr.sh_entsize; break; } if (note != NULL && dynamic != NULL) break; } /* * note == NULL usually means a shared object for use with dlopen(3) * dynamic == NULL means not a dynamically linked elf */ if (dynamic == NULL) { ret = EPKG_END; goto cleanup; /* not a dynamically linked elf: no results */ } if (note != NULL) { if ((data = elf_getdata(note, NULL)) == NULL) { ret = EPKG_END; /* Some error occurred, ignore this file */ goto cleanup; } if (data->d_buf == NULL) { ret = EPKG_END; /* No osname available */ goto cleanup; } osname = (const char *) data->d_buf + sizeof(Elf_Note); if (strncasecmp(osname, "freebsd", sizeof("freebsd")) != 0 && strncasecmp(osname, "dragonfly", sizeof("dragonfly")) != 0) { ret = EPKG_END; /* Foreign (probably linux) ELF object */ goto cleanup; } } else { if (elfhdr.e_ident[EI_OSABI] != ELFOSABI_FREEBSD) { ret = EPKG_END; goto cleanup; } } if ((data = elf_getdata(dynamic, NULL)) == NULL) { ret = EPKG_END; /* Some error occurred, ignore this file */ goto cleanup; } /* First, scan through the data from the .dynamic section to find any RPATH or RUNPATH settings. These are colon separated paths to prepend to the ld.so search paths from the ELF hints file. These always seem to come right after the NEEDED shared library entries. NEEDED entries should resolve to a filename for installed executables, but need not resolve for installed shared libraries -- additional info from the apps that link against them would be required. Shared libraries are distinguished by a DT_SONAME tag */ rpath_list_init(); for (dynidx = 0; dynidx < numdyn; dynidx++) { if ((dyn = gelf_getdyn(data, dynidx, &dyn_mem)) == NULL) { ret = EPKG_FATAL; pkg_emit_error("getdyn() failed for %s: %s", fpath, elf_errmsg(-1)); goto cleanup; } if (dyn->d_tag == DT_SONAME) { is_shlib = true; /* The file being scanned is a shared library *provided* by the package. Record this if appropriate */ pkg_addshlib_provided(pkg, basename(fpath)); } if (dyn->d_tag != DT_RPATH && dyn->d_tag != DT_RUNPATH) continue; shlib_list_from_rpath(elf_strptr(e, sh_link, dyn->d_un.d_val), dirname(fpath)); break; } /* Now find all of the NEEDED shared libraries. */ for (dynidx = 0; dynidx < numdyn; dynidx++) { if ((dyn = gelf_getdyn(data, dynidx, &dyn_mem)) == NULL) { ret = EPKG_FATAL; pkg_emit_error("getdyn() failed for %s: %s", fpath, elf_errmsg(-1)); goto cleanup; } if (dyn->d_tag != DT_NEEDED) continue; shlib = elf_strptr(e, sh_link, dyn->d_un.d_val); action(actdata, pkg, fpath, shlib, is_shlib); } cleanup: rpath_list_free(); if (e != NULL) elf_end(e); close(fd); return (ret); }
static int analyse_elf(struct pkg *pkg, const char *fpath) { Elf *e = NULL; GElf_Ehdr elfhdr; Elf_Scn *scn = NULL; Elf_Scn *note = NULL; Elf_Scn *dynamic = NULL; GElf_Shdr shdr; Elf_Data *data; GElf_Dyn *dyn, dyn_mem; struct stat sb; int ret = EPKG_OK; size_t numdyn = 0; size_t sh_link = 0; size_t dynidx; const char *myarch; const char *shlib; char *rpath = NULL; bool is_shlib = false; myarch = pkg_object_string(pkg_config_get("ABI")); int fd; pkg_debug(1, "analysing elf %s", fpath); if (lstat(fpath, &sb) != 0) pkg_emit_errno("fstat() failed for", fpath); /* ignore empty files and non regular files */ if (sb.st_size == 0 || !S_ISREG(sb.st_mode)) return (EPKG_END); /* Empty file or sym-link: no results */ if ((fd = open(fpath, O_RDONLY, 0)) < 0) { return (EPKG_FATAL); } if ((e = elf_begin(fd, ELF_C_READ, NULL)) == NULL) { ret = EPKG_FATAL; pkg_emit_error("elf_begin() for %s failed: %s", fpath, elf_errmsg(-1)); goto cleanup; } if (elf_kind(e) != ELF_K_ELF) { /* Not an elf file: no results */ ret = EPKG_END; pkg_debug(1, "not an elf"); goto cleanup; } if (ctx.developer_mode) pkg->flags |= PKG_CONTAINS_ELF_OBJECTS; if (gelf_getehdr(e, &elfhdr) == NULL) { ret = EPKG_FATAL; pkg_emit_error("getehdr() failed: %s.", elf_errmsg(-1)); goto cleanup; } if (elfhdr.e_type != ET_DYN && elfhdr.e_type != ET_EXEC && elfhdr.e_type != ET_REL) { pkg_debug(1, "not an elf"); ret = EPKG_END; goto cleanup; } /* Elf file has sections header */ while ((scn = elf_nextscn(e, scn)) != NULL) { if (gelf_getshdr(scn, &shdr) != &shdr) { ret = EPKG_FATAL; pkg_emit_error("getshdr() for %s failed: %s", fpath, elf_errmsg(-1)); goto cleanup; } switch (shdr.sh_type) { case SHT_NOTE: if ((data = elf_getdata(scn, NULL)) == NULL) { ret = EPKG_END; /* Some error occurred, ignore this file */ goto cleanup; } else if (data->d_buf != NULL) { Elf_Note *en = (Elf_Note *)data->d_buf; if (en->n_type == NT_ABI_TAG) note = scn; } break; case SHT_DYNAMIC: dynamic = scn; sh_link = shdr.sh_link; if (shdr.sh_entsize == 0) { ret = EPKG_END; goto cleanup; } numdyn = shdr.sh_size / shdr.sh_entsize; break; } if (note != NULL && dynamic != NULL) break; } /* * note == NULL usually means a shared object for use with dlopen(3) * dynamic == NULL means not a dynamically linked elf */ if (dynamic == NULL) { ret = EPKG_END; goto cleanup; /* not a dynamically linked elf: no results */ } if (!shlib_valid_abi(fpath, &elfhdr, myarch)) { ret = EPKG_END; goto cleanup; /* Invalid ABI */ } #ifdef __FreeBSD__ if (elfhdr.e_ident[EI_OSABI] != ELFOSABI_FREEBSD && !is_old_freebsd_armheader(&elfhdr)) { ret = EPKG_END; goto cleanup; } #endif if ((data = elf_getdata(dynamic, NULL)) == NULL) { ret = EPKG_END; /* Some error occurred, ignore this file */ goto cleanup; } /* First, scan through the data from the .dynamic section to find any RPATH or RUNPATH settings. These are colon separated paths to prepend to the ld.so search paths from the ELF hints file. These always seem to come right after the NEEDED shared library entries. NEEDED entries should resolve to a filename for installed executables, but need not resolve for installed shared libraries -- additional info from the apps that link against them would be required. Shared libraries are distinguished by a DT_SONAME tag */ rpath_list_init(); for (dynidx = 0; dynidx < numdyn; dynidx++) { if ((dyn = gelf_getdyn(data, dynidx, &dyn_mem)) == NULL) { ret = EPKG_FATAL; pkg_emit_error("getdyn() failed for %s: %s", fpath, elf_errmsg(-1)); goto cleanup; } if (dyn->d_tag == DT_SONAME) { is_shlib = true; /* The file being scanned is a shared library *provided* by the package. Record this if appropriate */ shlib = elf_strptr(e, sh_link, dyn->d_un.d_val); if (shlib != NULL && *shlib != '\0') pkg_addshlib_provided(pkg, shlib); } if ((dyn->d_tag == DT_RPATH || dyn->d_tag == DT_RUNPATH) && rpath == NULL) rpath = elf_strptr(e, sh_link, dyn->d_un.d_val); } if (rpath != NULL) shlib_list_from_rpath(rpath, bsd_dirname(fpath)); /* Now find all of the NEEDED shared libraries. */ for (dynidx = 0; dynidx < numdyn; dynidx++) { if ((dyn = gelf_getdyn(data, dynidx, &dyn_mem)) == NULL) { ret = EPKG_FATAL; pkg_emit_error("getdyn() failed for %s: %s", fpath, elf_errmsg(-1)); goto cleanup; } if (dyn->d_tag != DT_NEEDED) continue; shlib = elf_strptr(e, sh_link, dyn->d_un.d_val); add_shlibs_to_pkg(pkg, fpath, shlib, is_shlib); } cleanup: rpath_list_free(); if (e != NULL) elf_end(e); close(fd); return (ret); }