13 #include <rpm/rpmstring.h>
16 int filter_private = 0;
19 int filter_soname = 1;
20 int require_interp = 0;
23 typedef struct elfInfo_s {
27 int isExec; /* requires are only added to executables */
33 const char *marker; /* elf class marker or NULL */
39 static int skipPrivate(const char *s)
41 return (filter_private && rstreq(s, "GLIBC_PRIVATE"));
45 * Rough soname sanity filtering: all sane soname's dependencies need to
46 * contain ".so", and normal linkable libraries start with "lib",
47 * everything else is an exception of some sort. The most notable
48 * and common exception is the dynamic linker itself, which we allow
49 * here, the rest can use --no-filter-soname.
51 static int skipSoname(const char *soname)
55 /* Filter out empty and all-whitespace sonames */
56 for (const char *s = soname; *s != '\0'; s++) {
67 if (!strstr(soname, ".so"))
70 if (rstreqn(soname, "ld.", 3) || rstreqn(soname, "ld-", 3) ||
71 rstreqn(soname, "ld64.", 3) || rstreqn(soname, "ld64-", 3))
74 if (rstreqn(soname, "lib", 3))
83 static const char *mkmarker(GElf_Ehdr *ehdr)
85 const char *marker = NULL;
87 if (ehdr->e_ident[EI_CLASS] == ELFCLASS64) {
88 switch (ehdr->e_machine) {
91 /* alpha doesn't traditionally have 64bit markers */
101 static void addDep(ARGV_t *deps,
102 const char *soname, const char *ver, const char *marker)
106 if (skipSoname(soname))
111 "%s(%s)%s", soname, ver ? ver : "", marker ? marker : "");
113 argvAdd(deps, dep ? dep : soname);
117 static void processVerDef(Elf_Scn *scn, GElf_Shdr *shdr, elfInfo *ei)
119 Elf_Data *data = NULL;
120 unsigned int offset, auxoffset;
123 while ((data = elf_getdata(scn, data)) != NULL) {
126 for (int i = shdr->sh_info; --i >= 0; ) {
127 GElf_Verdef def_mem, *def;
128 def = gelf_getverdef (data, offset, &def_mem);
131 auxoffset = offset + def->vd_aux;
132 offset += def->vd_next;
134 for (int j = def->vd_cnt; --j >= 0; ) {
135 GElf_Verdaux aux_mem, * aux;
137 aux = gelf_getverdaux (data, auxoffset, &aux_mem);
140 s = elf_strptr(ei->elf, shdr->sh_link, aux->vda_name);
143 if (def->vd_flags & VER_FLG_BASE) {
146 auxoffset += aux->vda_next;
148 } else if (soname && !soname_only && !skipPrivate(s)) {
149 addDep(&ei->provides, soname, s, ei->marker);
158 static void processVerNeed(Elf_Scn *scn, GElf_Shdr *shdr, elfInfo *ei)
160 Elf_Data *data = NULL;
162 while ((data = elf_getdata(scn, data)) != NULL) {
163 unsigned int offset = 0, auxoffset;
164 for (int i = shdr->sh_info; --i >= 0; ) {
165 const char *s = NULL;
166 GElf_Verneed need_mem, *need;
167 need = gelf_getverneed (data, offset, &need_mem);
171 s = elf_strptr(ei->elf, shdr->sh_link, need->vn_file);
176 auxoffset = offset + need->vn_aux;
178 for (int j = need->vn_cnt; --j >= 0; ) {
179 GElf_Vernaux aux_mem, * aux;
180 aux = gelf_getvernaux (data, auxoffset, &aux_mem);
183 s = elf_strptr(ei->elf, shdr->sh_link, aux->vna_name);
187 if (ei->isExec && soname && !soname_only && !skipPrivate(s)) {
188 addDep(&ei->requires, soname, s, ei->marker);
190 auxoffset += aux->vna_next;
192 offset += need->vn_next;
198 static void processDynamic(Elf_Scn *scn, GElf_Shdr *shdr, elfInfo *ei)
200 Elf_Data *data = NULL;
201 while ((data = elf_getdata(scn, data)) != NULL) {
202 for (int i = 0; i < (shdr->sh_size / shdr->sh_entsize); i++) {
203 const char *s = NULL;
204 GElf_Dyn dyn_mem, *dyn;
206 dyn = gelf_getdyn (data, i, &dyn_mem);
210 switch (dyn->d_tag) {
221 s = elf_strptr(ei->elf, shdr->sh_link, dyn->d_un.d_val);
223 ei->soname = rstrdup(s);
227 s = elf_strptr(ei->elf, shdr->sh_link, dyn->d_un.d_val);
229 addDep(&ei->requires, s, NULL, ei->marker);
237 static void processSections(elfInfo *ei)
239 Elf_Scn * scn = NULL;
240 while ((scn = elf_nextscn(ei->elf, scn)) != NULL) {
241 GElf_Shdr shdr_mem, *shdr;
242 shdr = gelf_getshdr(scn, &shdr_mem);
246 switch (shdr->sh_type) {
248 processVerDef(scn, shdr, ei);
250 case SHT_GNU_verneed:
251 processVerNeed(scn, shdr, ei);
254 processDynamic(scn, shdr, ei);
262 static void processProgHeaders(elfInfo *ei, GElf_Ehdr *ehdr)
264 for (size_t i = 0; i < ehdr->e_phnum; i++) {
266 GElf_Phdr *phdr = gelf_getphdr(ei->elf, i, &mem);
268 if (phdr && phdr->p_type == PT_INTERP) {
270 char * filedata = elf_rawfile(ei->elf, &maxsize);
272 if (filedata && phdr->p_offset < maxsize) {
273 ei->interp = rstrdup(filedata + phdr->p_offset);
280 static int processFile(const char *fn, int dtype)
285 GElf_Ehdr *ehdr, ehdr_mem;
286 elfInfo *ei = rcalloc(1, sizeof(*ei));
288 fdno = open(fn, O_RDONLY);
289 if (fdno < 0 || fstat(fdno, &st) < 0)
292 (void) elf_version(EV_CURRENT);
293 ei->elf = elf_begin(fdno, ELF_C_READ, NULL);
294 if (ei->elf == NULL || elf_kind(ei->elf) != ELF_K_ELF)
297 ehdr = gelf_getehdr(ei->elf, &ehdr_mem);
301 if (ehdr->e_type == ET_DYN || ehdr->e_type == ET_EXEC) {
302 ei->marker = mkmarker(ehdr);
303 ei->isDSO = (ehdr->e_type == ET_DYN);
304 ei->isExec = assume_exec || (st.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH));
306 processProgHeaders(ei, ehdr);
311 * For DSOs which use the .gnu_hash section and don't have a .hash
312 * section, we need to ensure that we have a new enough glibc.
314 if (ei->isExec && ei->gotGNUHASH && !ei->gotHASH && !soname_only) {
315 argvAdd(&ei->requires, "rtld(GNU_HASH)");
319 * For DSOs, add DT_SONAME as provide. If its missing, we can fake
320 * it from the basename if requested. The bizarre looking DT_DEBUG
321 * check is used to avoid adding basename provides for PIE executables.
323 if (ei->isDSO && !ei->gotDEBUG) {
324 if (!ei->soname && fake_soname) {
325 const char *bn = strrchr(fn, '/');
326 ei->soname = rstrdup(bn ? bn + 1 : fn);
329 addDep(&ei->provides, ei->soname, NULL, ei->marker);
332 /* If requested and present, add dep for interpreter (ie dynamic linker) */
333 if (ei->interp && require_interp)
334 argvAdd(&ei->requires, ei->interp);
337 /* dump the requested dependencies for this file */
338 for (ARGV_t dep = dtype ? ei->requires : ei->provides; dep && *dep; dep++) {
339 fprintf(stdout, "%s\n", *dep);
343 if (fdno >= 0) close(fdno);
345 argvFree(ei->provides);
346 argvFree(ei->requires);
349 if (ei->elf) elf_end(ei->elf);
355 int main(int argc, char *argv[])
361 struct poptOption opts[] = {
362 { "provides", 'P', POPT_ARG_VAL, &provides, -1, NULL, NULL },
363 { "requires", 'R', POPT_ARG_VAL, &requires, -1, NULL, NULL },
364 { "filter-private", 0, POPT_ARG_VAL, &filter_private, -1, NULL, NULL },
365 { "soname-only", 0, POPT_ARG_VAL, &soname_only, -1, NULL, NULL },
366 { "no-fake-soname", 0, POPT_ARG_VAL, &fake_soname, 0, NULL, NULL },
367 { "no-filter-soname", 0, POPT_ARG_VAL, &filter_soname, 0, NULL, NULL },
368 { "require-interp", 0, POPT_ARG_VAL, &require_interp, -1, NULL, NULL },
369 { "assume-exec", 0, POPT_ARG_VAL, &assume_exec, -1, NULL, NULL },
374 xsetprogname(argv[0]); /* Portability call -- see system.h */
376 optCon = poptGetContext(argv[0], argc, (const char **) argv, opts, 0);
377 if (argc < 2 || poptGetNextOpt(optCon) == 0) {
378 poptPrintUsage(optCon, stderr, 0);
382 /* Normally our data comes from stdin, but permit args too */
383 if (poptPeekArg(optCon)) {
385 while ((fn = poptGetArg(optCon)) != NULL) {
386 (void) processFile(fn, requires);
390 while (fgets(fn, sizeof(fn), stdin) != NULL) {
391 fn[strlen(fn)-1] = '\0';
392 (void) processFile(fn, requires);
396 poptFreeContext(optCon);