1 #include "system.h"
2 
3 #include <sys/types.h>
4 #include <sys/stat.h>
5 #include <unistd.h>
6 #include <stdlib.h>
7 #include <fcntl.h>
8 #include <error.h>
9 #include <errno.h>
10 #include <popt.h>
11 #include <gelf.h>
12 
13 #include <rpm/rpmstring.h>
14 #include <rpm/argv.h>
15 
16 int filter_private = 0;
17 int soname_only = 0;
18 int fake_soname = 1;
19 int filter_soname = 1;
20 int require_interp = 0;
21 
22 typedef struct elfInfo_s {
23     Elf *elf;
24 
25     int isDSO;
26     int isExec;			/* requires are only added to executables */
27     int gotDEBUG;
28     int gotHASH;
29     int gotGNUHASH;
30     char *soname;
31     char *interp;
32     const char *marker;		/* elf class marker or NULL */
33 
34     ARGV_t requires;
35     ARGV_t provides;
36 } elfInfo;
37 
skipPrivate(const char * s)38 static int skipPrivate(const char *s)
39 {
40     return (filter_private && rstreq(s, "GLIBC_PRIVATE"));
41 }
42 
43 /*
44  * Rough soname sanity filtering: all sane soname's dependencies need to
45  * contain ".so", and normal linkable libraries start with "lib",
46  * everything else is an exception of some sort. The most notable
47  * and common exception is the dynamic linker itself, which we allow
48  * here, the rest can use --no-filter-soname.
49  */
skipSoname(const char * soname)50 static int skipSoname(const char *soname)
51 {
52     int sane = 0;
53 
54     /* Filter out empty and all-whitespace sonames */
55     for (const char *s = soname; *s != '\0'; s++) {
56 	if (!risspace(*s)) {
57 	    sane = 1;
58 	    break;
59 	}
60     }
61 
62     if (!sane)
63 	return 1;
64 
65     if (filter_soname) {
66 	if (!strstr(soname, ".so"))
67 	    return 1;
68 
69 	if (rstreqn(soname, "ld.", 3) || rstreqn(soname, "ld-", 3) ||
70 	    rstreqn(soname, "ld64.", 3) || rstreqn(soname, "ld64-", 3))
71 	    return 0;
72 
73 	if (rstreqn(soname, "lib", 3))
74 	    return 0;
75 	else
76 	    return 1;
77     }
78 
79     return 0;
80 }
81 
mkmarker(GElf_Ehdr * ehdr)82 static const char *mkmarker(GElf_Ehdr *ehdr)
83 {
84     const char *marker = NULL;
85 
86     if (ehdr->e_ident[EI_CLASS] == ELFCLASS64) {
87 	switch (ehdr->e_machine) {
88 	case EM_ALPHA:
89 	case EM_FAKE_ALPHA:
90 	    /* alpha doesn't traditionally have 64bit markers */
91 	    break;
92 	default:
93 	    marker = "(64bit)";
94 	    break;
95 	}
96     }
97     return marker;
98 }
99 
addDep(ARGV_t * deps,const char * soname,const char * ver,const char * marker)100 static void addDep(ARGV_t *deps,
101 		   const char *soname, const char *ver, const char *marker)
102 {
103     char *dep = NULL;
104 
105     if (skipSoname(soname))
106 	return;
107 
108     if (ver || marker) {
109 	rasprintf(&dep,
110 		  "%s(%s)%s", soname, ver ? ver : "", marker ? marker : "");
111     }
112     argvAdd(deps, dep ? dep : soname);
113     free(dep);
114 }
115 
processVerDef(Elf_Scn * scn,GElf_Shdr * shdr,elfInfo * ei)116 static void processVerDef(Elf_Scn *scn, GElf_Shdr *shdr, elfInfo *ei)
117 {
118     Elf_Data *data = NULL;
119     unsigned int offset, auxoffset;
120     char *soname = NULL;
121 
122     while ((data = elf_getdata(scn, data)) != NULL) {
123 	offset = 0;
124 
125 	for (int i = shdr->sh_info; --i >= 0; ) {
126 	    GElf_Verdef def_mem, *def;
127 	    def = gelf_getverdef (data, offset, &def_mem);
128 	    if (def == NULL)
129 		break;
130 	    auxoffset = offset + def->vd_aux;
131 	    offset += def->vd_next;
132 
133 	    for (int j = def->vd_cnt; --j >= 0; ) {
134 		GElf_Verdaux aux_mem, * aux;
135 		const char *s;
136 		aux = gelf_getverdaux (data, auxoffset, &aux_mem);
137 		if (aux == NULL)
138 		    break;
139 		s = elf_strptr(ei->elf, shdr->sh_link, aux->vda_name);
140 		if (s == NULL)
141 		    break;
142 		if (def->vd_flags & VER_FLG_BASE) {
143 		    rfree(soname);
144 		    soname = rstrdup(s);
145 		    auxoffset += aux->vda_next;
146 		    continue;
147 		} else if (soname && !soname_only && !skipPrivate(s)) {
148 		    addDep(&ei->provides, soname, s, ei->marker);
149 		}
150 	    }
151 
152 	}
153     }
154     rfree(soname);
155 }
156 
processVerNeed(Elf_Scn * scn,GElf_Shdr * shdr,elfInfo * ei)157 static void processVerNeed(Elf_Scn *scn, GElf_Shdr *shdr, elfInfo *ei)
158 {
159     Elf_Data *data = NULL;
160     char *soname = NULL;
161     while ((data = elf_getdata(scn, data)) != NULL) {
162 	unsigned int offset = 0, auxoffset;
163 	for (int i = shdr->sh_info; --i >= 0; ) {
164 	    const char *s = NULL;
165 	    GElf_Verneed need_mem, *need;
166 	    need = gelf_getverneed (data, offset, &need_mem);
167 	    if (need == NULL)
168 		break;
169 
170 	    s = elf_strptr(ei->elf, shdr->sh_link, need->vn_file);
171 	    if (s == NULL)
172 		break;
173 	    rfree(soname);
174 	    soname = rstrdup(s);
175 	    auxoffset = offset + need->vn_aux;
176 
177 	    for (int j = need->vn_cnt; --j >= 0; ) {
178 		GElf_Vernaux aux_mem, * aux;
179 		aux = gelf_getvernaux (data, auxoffset, &aux_mem);
180 		if (aux == NULL)
181 		    break;
182 		s = elf_strptr(ei->elf, shdr->sh_link, aux->vna_name);
183 		if (s == NULL)
184 		    break;
185 
186 		if (ei->isExec && soname && !soname_only && !skipPrivate(s)) {
187 		    addDep(&ei->requires, soname, s, ei->marker);
188 		}
189 		auxoffset += aux->vna_next;
190 	    }
191 	    offset += need->vn_next;
192 	}
193     }
194     rfree(soname);
195 }
196 
processDynamic(Elf_Scn * scn,GElf_Shdr * shdr,elfInfo * ei)197 static void processDynamic(Elf_Scn *scn, GElf_Shdr *shdr, elfInfo *ei)
198 {
199     Elf_Data *data = NULL;
200     while ((data = elf_getdata(scn, data)) != NULL) {
201 	for (int i = 0; i < (shdr->sh_size / shdr->sh_entsize); i++) {
202 	    const char *s = NULL;
203 	    GElf_Dyn dyn_mem, *dyn;
204 
205 	    dyn = gelf_getdyn (data, i, &dyn_mem);
206 	    if (dyn == NULL)
207 		break;
208 
209 	    switch (dyn->d_tag) {
210 	    case DT_HASH:
211 		ei->gotHASH = 1;
212 		break;
213 	    case DT_GNU_HASH:
214 		ei->gotGNUHASH = 1;
215 		break;
216 	    case DT_DEBUG:
217 		ei->gotDEBUG = 1;
218 		break;
219 	    case DT_SONAME:
220 		s = elf_strptr(ei->elf, shdr->sh_link, dyn->d_un.d_val);
221 		if (s)
222 		    ei->soname = rstrdup(s);
223 		break;
224 	    case DT_NEEDED:
225 		if (ei->isExec) {
226 		    s = elf_strptr(ei->elf, shdr->sh_link, dyn->d_un.d_val);
227 		    if (s)
228 			addDep(&ei->requires, s, NULL, ei->marker);
229 		}
230 		break;
231 	    }
232 	}
233     }
234 }
235 
processSections(elfInfo * ei)236 static void processSections(elfInfo *ei)
237 {
238     Elf_Scn * scn = NULL;
239     while ((scn = elf_nextscn(ei->elf, scn)) != NULL) {
240 	GElf_Shdr shdr_mem, *shdr;
241 	shdr = gelf_getshdr(scn, &shdr_mem);
242 	if (shdr == NULL)
243 	    break;
244 
245 	switch (shdr->sh_type) {
246 	case SHT_GNU_verdef:
247 	    processVerDef(scn, shdr, ei);
248 	    break;
249 	case SHT_GNU_verneed:
250 	    processVerNeed(scn, shdr, ei);
251 	    break;
252 	case SHT_DYNAMIC:
253 	    processDynamic(scn, shdr, ei);
254 	    break;
255 	default:
256 	    break;
257 	}
258     }
259 }
260 
processProgHeaders(elfInfo * ei,GElf_Ehdr * ehdr)261 static void processProgHeaders(elfInfo *ei, GElf_Ehdr *ehdr)
262 {
263     for (size_t i = 0; i < ehdr->e_phnum; i++) {
264 	GElf_Phdr mem;
265 	GElf_Phdr *phdr = gelf_getphdr(ei->elf, i, &mem);
266 
267 	if (phdr && phdr->p_type == PT_INTERP) {
268 	    size_t maxsize;
269 	    char * filedata = elf_rawfile(ei->elf, &maxsize);
270 
271 	    if (filedata && phdr->p_offset < maxsize) {
272 		ei->interp = rstrdup(filedata + phdr->p_offset);
273 		break;
274 	    }
275 	}
276     }
277 }
278 
processFile(const char * fn,int dtype)279 static int processFile(const char *fn, int dtype)
280 {
281     int rc = 1;
282     int fdno;
283     struct stat st;
284     GElf_Ehdr *ehdr, ehdr_mem;
285     elfInfo *ei = rcalloc(1, sizeof(*ei));
286 
287     fdno = open(fn, O_RDONLY);
288     if (fdno < 0 || fstat(fdno, &st) < 0)
289 	goto exit;
290 
291     (void) elf_version(EV_CURRENT);
292     ei->elf = elf_begin(fdno, ELF_C_READ, NULL);
293     if (ei->elf == NULL || elf_kind(ei->elf) != ELF_K_ELF)
294 	goto exit;
295 
296     ehdr = gelf_getehdr(ei->elf, &ehdr_mem);
297     if (ehdr == NULL)
298 	goto exit;
299 
300     if (ehdr->e_type == ET_DYN || ehdr->e_type == ET_EXEC) {
301 	ei->marker = mkmarker(ehdr);
302     	ei->isDSO = (ehdr->e_type == ET_DYN);
303 	ei->isExec = (st.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH));
304 
305 	processProgHeaders(ei, ehdr);
306 	processSections(ei);
307     }
308 
309     /*
310      * For DSOs which use the .gnu_hash section and don't have a .hash
311      * section, we need to ensure that we have a new enough glibc.
312      */
313     if (ei->isExec && ei->gotGNUHASH && !ei->gotHASH && !soname_only) {
314 	argvAdd(&ei->requires, "rtld(GNU_HASH)");
315     }
316 
317     /*
318      * For DSOs, add DT_SONAME as provide. If its missing, we can fake
319      * it from the basename if requested. The bizarre looking DT_DEBUG
320      * check is used to avoid adding basename provides for PIE executables.
321      */
322     if (ei->isDSO && !ei->gotDEBUG) {
323 	if (!ei->soname && fake_soname) {
324 	    const char *bn = strrchr(fn, '/');
325 	    ei->soname = rstrdup(bn ? bn + 1 : fn);
326 	}
327 	if (ei->soname)
328 	    addDep(&ei->provides, ei->soname, NULL, ei->marker);
329     }
330 
331     /* If requested and present, add dep for interpreter (ie dynamic linker) */
332     if (ei->interp && require_interp)
333 	argvAdd(&ei->requires, ei->interp);
334 
335     rc = 0;
336     /* dump the requested dependencies for this file */
337     for (ARGV_t dep = dtype ? ei->requires : ei->provides; dep && *dep; dep++) {
338 	fprintf(stdout, "%s\n", *dep);
339     }
340 
341 exit:
342     if (fdno >= 0) close(fdno);
343     if (ei) {
344 	argvFree(ei->provides);
345 	argvFree(ei->requires);
346 	free(ei->soname);
347 	free(ei->interp);
348     	if (ei->elf) elf_end(ei->elf);
349 	rfree(ei);
350     }
351     return rc;
352 }
353 
main(int argc,char * argv[])354 int main(int argc, char *argv[])
355 {
356     int rc = 0;
357     int provides = 0;
358     int requires = 0;
359     poptContext optCon;
360 
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 	POPT_AUTOHELP
370 	POPT_TABLEEND
371     };
372 
373     xsetprogname(argv[0]); /* Portability call -- see system.h */
374 
375     optCon = poptGetContext(argv[0], argc, (const char **) argv, opts, 0);
376     if (argc < 2 || poptGetNextOpt(optCon) == 0) {
377 	poptPrintUsage(optCon, stderr, 0);
378 	exit(EXIT_FAILURE);
379     }
380 
381     /* Normally our data comes from stdin, but permit args too */
382     if (poptPeekArg(optCon)) {
383 	const char *fn;
384 	while ((fn = poptGetArg(optCon)) != NULL) {
385 	    if (processFile(fn, requires))
386 		rc = EXIT_FAILURE;
387 	}
388     } else {
389 	char fn[BUFSIZ];
390 	while (fgets(fn, sizeof(fn), stdin) != NULL) {
391 	    fn[strlen(fn)-1] = '\0';
392 	    if (processFile(fn, requires))
393 		rc = EXIT_FAILURE;
394 	}
395     }
396 
397     poptFreeContext(optCon);
398     return rc;
399 }
400