Add macro %isu_package to generate ISU Package
[platform/upstream/rpm.git] / tools / elfdeps.c
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 int assume_exec = 0;
22
23 typedef struct elfInfo_s {
24     Elf *elf;
25
26     int isDSO;
27     int isExec;                 /* requires are only added to executables */
28     int gotDEBUG;
29     int gotHASH;
30     int gotGNUHASH;
31     char *soname;
32     char *interp;
33     const char *marker;         /* elf class marker or NULL */
34
35     ARGV_t requires;
36     ARGV_t provides;
37 } elfInfo;
38
39 static int skipPrivate(const char *s)
40
41     return (filter_private && rstreq(s, "GLIBC_PRIVATE"));
42 }
43
44 /*
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.
50  */
51 static int skipSoname(const char *soname)
52 {
53     int sane = 0;
54
55     /* Filter out empty and all-whitespace sonames */
56     for (const char *s = soname; *s != '\0'; s++) {
57         if (!risspace(*s)) {
58             sane = 1;
59             break;
60         }
61     }
62
63     if (!sane)
64         return 1;
65
66     if (filter_soname) {
67         if (!strstr(soname, ".so"))
68             return 1;
69
70         if (rstreqn(soname, "ld.", 3) || rstreqn(soname, "ld-", 3) ||
71             rstreqn(soname, "ld64.", 3) || rstreqn(soname, "ld64-", 3))
72             return 0;
73
74         if (rstreqn(soname, "lib", 3))
75             return 0;
76         else
77             return 1;
78     }
79
80     return 0;
81 }
82
83 static const char *mkmarker(GElf_Ehdr *ehdr)
84 {
85     const char *marker = NULL;
86
87     if (ehdr->e_ident[EI_CLASS] == ELFCLASS64) {
88         switch (ehdr->e_machine) {
89         case EM_ALPHA:
90         case EM_FAKE_ALPHA:
91             /* alpha doesn't traditionally have 64bit markers */
92             break;
93         default:
94             marker = "(64bit)";
95             break;
96         }
97     }
98     return marker;
99 }
100
101 static void addDep(ARGV_t *deps,
102                    const char *soname, const char *ver, const char *marker)
103 {
104     char *dep = NULL;
105
106     if (skipSoname(soname))
107         return;
108
109     if (ver || marker) {
110         rasprintf(&dep,
111                   "%s(%s)%s", soname, ver ? ver : "", marker ? marker : "");
112     }
113     argvAdd(deps, dep ? dep : soname);
114     free(dep);
115 }
116
117 static void processVerDef(Elf_Scn *scn, GElf_Shdr *shdr, elfInfo *ei)
118 {
119     Elf_Data *data = NULL;
120     unsigned int offset, auxoffset;
121     char *soname = NULL;
122
123     while ((data = elf_getdata(scn, data)) != NULL) {
124         offset = 0;
125
126         for (int i = shdr->sh_info; --i >= 0; ) {
127             GElf_Verdef def_mem, *def;
128             def = gelf_getverdef (data, offset, &def_mem);
129             if (def == NULL)
130                 break;
131             auxoffset = offset + def->vd_aux;
132             offset += def->vd_next;
133
134             for (int j = def->vd_cnt; --j >= 0; ) {
135                 GElf_Verdaux aux_mem, * aux;
136                 const char *s;
137                 aux = gelf_getverdaux (data, auxoffset, &aux_mem);
138                 if (aux == NULL)
139                     break;
140                 s = elf_strptr(ei->elf, shdr->sh_link, aux->vda_name);
141                 if (s == NULL)
142                     break;
143                 if (def->vd_flags & VER_FLG_BASE) {
144                     rfree(soname);
145                     soname = rstrdup(s);
146                     auxoffset += aux->vda_next;
147                     continue;
148                 } else if (soname && !soname_only && !skipPrivate(s)) {
149                     addDep(&ei->provides, soname, s, ei->marker);
150                 }
151             }
152                     
153         }
154     }
155     rfree(soname);
156 }
157
158 static void processVerNeed(Elf_Scn *scn, GElf_Shdr *shdr, elfInfo *ei)
159 {
160     Elf_Data *data = NULL;
161     char *soname = 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);
168             if (need == NULL)
169                 break;
170
171             s = elf_strptr(ei->elf, shdr->sh_link, need->vn_file);
172             if (s == NULL)
173                 break;
174             rfree(soname);
175             soname = rstrdup(s);
176             auxoffset = offset + need->vn_aux;
177
178             for (int j = need->vn_cnt; --j >= 0; ) {
179                 GElf_Vernaux aux_mem, * aux;
180                 aux = gelf_getvernaux (data, auxoffset, &aux_mem);
181                 if (aux == NULL)
182                     break;
183                 s = elf_strptr(ei->elf, shdr->sh_link, aux->vna_name);
184                 if (s == NULL)
185                     break;
186
187                 if (ei->isExec && soname && !soname_only && !skipPrivate(s)) {
188                     addDep(&ei->requires, soname, s, ei->marker);
189                 }
190                 auxoffset += aux->vna_next;
191             }
192             offset += need->vn_next;
193         }
194     }
195     rfree(soname);
196 }
197
198 static void processDynamic(Elf_Scn *scn, GElf_Shdr *shdr, elfInfo *ei)
199 {
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;
205
206             dyn = gelf_getdyn (data, i, &dyn_mem);
207             if (dyn == NULL)
208                 break;
209
210             switch (dyn->d_tag) {
211             case DT_HASH:
212                 ei->gotHASH = 1;
213                 break;
214             case DT_GNU_HASH:
215                 ei->gotGNUHASH = 1;
216                 break;
217             case DT_DEBUG:
218                 ei->gotDEBUG = 1;
219                 break;
220             case DT_SONAME:
221                 s = elf_strptr(ei->elf, shdr->sh_link, dyn->d_un.d_val);
222                 if (s)
223                     ei->soname = rstrdup(s);
224                 break;
225             case DT_NEEDED:
226                 if (ei->isExec) {
227                     s = elf_strptr(ei->elf, shdr->sh_link, dyn->d_un.d_val);
228                     if (s)
229                         addDep(&ei->requires, s, NULL, ei->marker);
230                 }
231                 break;
232             }
233         }
234     }
235 }
236
237 static void processSections(elfInfo *ei)
238 {
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);
243         if (shdr == NULL)
244             break;
245
246         switch (shdr->sh_type) {
247         case SHT_GNU_verdef:
248             processVerDef(scn, shdr, ei);
249             break;
250         case SHT_GNU_verneed:
251             processVerNeed(scn, shdr, ei);
252             break;
253         case SHT_DYNAMIC:
254             processDynamic(scn, shdr, ei);
255             break;
256         default:
257             break;
258         }
259     }
260 }
261
262 static void processProgHeaders(elfInfo *ei, GElf_Ehdr *ehdr)
263 {
264     for (size_t i = 0; i < ehdr->e_phnum; i++) {
265         GElf_Phdr mem;
266         GElf_Phdr *phdr = gelf_getphdr(ei->elf, i, &mem);
267
268         if (phdr && phdr->p_type == PT_INTERP) {
269             size_t maxsize;
270             char * filedata = elf_rawfile(ei->elf, &maxsize);
271
272             if (filedata && phdr->p_offset < maxsize) {
273                 ei->interp = rstrdup(filedata + phdr->p_offset);
274                 break;
275             }
276         }
277     }
278 }
279
280 static int processFile(const char *fn, int dtype)
281 {
282     int rc = 1;
283     int fdno;
284     struct stat st;
285     GElf_Ehdr *ehdr, ehdr_mem;
286     elfInfo *ei = rcalloc(1, sizeof(*ei));
287
288     fdno = open(fn, O_RDONLY);
289     if (fdno < 0 || fstat(fdno, &st) < 0)
290         goto exit;
291
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)
295         goto exit;
296
297     ehdr = gelf_getehdr(ei->elf, &ehdr_mem);
298     if (ehdr == NULL)
299         goto exit;
300
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));
305
306         processProgHeaders(ei, ehdr);
307         processSections(ei);
308     }
309
310     /*
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.
313      */
314     if (ei->isExec && ei->gotGNUHASH && !ei->gotHASH && !soname_only) {
315         argvAdd(&ei->requires, "rtld(GNU_HASH)");
316     }
317
318     /*
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.
322      */
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);
327         }
328         if (ei->soname)
329             addDep(&ei->provides, ei->soname, NULL, ei->marker);
330     }
331
332     /* If requested and present, add dep for interpreter (ie dynamic linker) */
333     if (ei->interp && require_interp)
334         argvAdd(&ei->requires, ei->interp);
335
336     rc = 0;
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);
340     }
341
342 exit:
343     if (fdno >= 0) close(fdno);
344     if (ei) {
345         argvFree(ei->provides);
346         argvFree(ei->requires);
347         free(ei->soname);
348         free(ei->interp);
349         if (ei->elf) elf_end(ei->elf);
350         rfree(ei);
351     }
352     return rc;
353 }
354
355 int main(int argc, char *argv[])
356 {
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         { "assume-exec", 0, POPT_ARG_VAL, &assume_exec, -1, NULL, NULL },
370         POPT_AUTOHELP 
371         POPT_TABLEEND
372     };
373
374     xsetprogname(argv[0]); /* Portability call -- see system.h */
375
376     optCon = poptGetContext(argv[0], argc, (const char **) argv, opts, 0);
377     if (argc < 2 || poptGetNextOpt(optCon) == 0) {
378         poptPrintUsage(optCon, stderr, 0);
379         exit(EXIT_FAILURE);
380     }
381
382     /* Normally our data comes from stdin, but permit args too */
383     if (poptPeekArg(optCon)) {
384         const char *fn;
385         while ((fn = poptGetArg(optCon)) != NULL) {
386             (void) processFile(fn, requires);
387         }
388     } else {
389         char fn[BUFSIZ];
390         while (fgets(fn, sizeof(fn), stdin) != NULL) {
391             fn[strlen(fn)-1] = '\0';
392             (void) processFile(fn, requires);
393         }
394     }
395
396     poptFreeContext(optCon);
397     return 0;
398 }