1 /* Command-line frontend for retrieving ELF / DWARF / source files
2    from the debuginfod.
3    Copyright (C) 2019-2020 Red Hat, Inc.
4    This file is part of elfutils.
5 
6    This file is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10 
11    elfutils is distributed in the hope that it will be useful, but
12    WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    General Public License for more details.
15 
16    You should have received copies of the GNU General Public License and
17    the GNU Lesser General Public License along with this program.  If
18    not, see <http://www.gnu.org/licenses/>.  */
19 
20 #include "config.h"
21 #include "printversion.h"
22 #include "debuginfod.h"
23 #include <errno.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <argp.h>
28 #include <unistd.h>
29 #include <fcntl.h>
30 #include <gelf.h>
31 #include <libdwelf.h>
32 
33 
34 /* Name and version of program.  */
35 ARGP_PROGRAM_VERSION_HOOK_DEF = print_version;
36 
37 /* Bug report address.  */
38 ARGP_PROGRAM_BUG_ADDRESS_DEF = PACKAGE_BUGREPORT;
39 
40 /* Short description of program.  */
41 static const char doc[] = N_("Request debuginfo-related content "
42                              "from debuginfods listed in $" DEBUGINFOD_URLS_ENV_VAR ".");
43 
44 /* Strings for arguments in help texts.  */
45 static const char args_doc[] = N_("debuginfo BUILDID\n"
46                                   "debuginfo PATH\n"
47                                   "executable BUILDID\n"
48                                   "executable PATH\n"
49                                   "source BUILDID /FILENAME\n"
50                                   "source PATH /FILENAME\n");
51 
52 
53 /* Definitions of arguments for argp functions.  */
54 static const struct argp_option options[] =
55   {
56    { "verbose", 'v', NULL, 0, "Increase verbosity.", 0 },
57    { NULL, 0, NULL, 0, NULL, 0 }
58   };
59 
60 /* debuginfod connection handle.  */
61 static debuginfod_client *client;
62 static int verbose;
63 
progressfn(debuginfod_client * c,long a,long b)64 int progressfn(debuginfod_client *c __attribute__((__unused__)),
65 	       long a, long b)
66 {
67   fprintf (stderr, "Progress %ld / %ld\n", a, b);
68   return 0;
69 }
70 
71 
parse_opt(int key,char * arg,struct argp_state * state)72 static error_t parse_opt (int key, char *arg, struct argp_state *state)
73 {
74   (void) arg;
75   (void) state;
76   switch (key)
77     {
78     case 'v': verbose++;
79       debuginfod_set_progressfn (client, & progressfn); break;
80     default: return ARGP_ERR_UNKNOWN;
81     }
82   return 0;
83 }
84 
85 
86 /* Data structure to communicate with argp functions.  */
87 static struct argp argp =
88   {
89    options, parse_opt, args_doc, doc, NULL, NULL, NULL
90   };
91 
92 
93 
94 int
main(int argc,char ** argv)95 main(int argc, char** argv)
96 {
97   elf_version (EV_CURRENT);
98 
99   client = debuginfod_begin ();
100   if (client == NULL)
101     {
102       fprintf(stderr, "Couldn't create debuginfod client context\n");
103       return 1;
104     }
105 
106   /* Exercise user data pointer, to support testing only. */
107   debuginfod_set_user_data (client, (void *)"Progress");
108 
109   int remaining;
110   (void) argp_parse (&argp, argc, argv, ARGP_IN_ORDER|ARGP_NO_ARGS, &remaining, NULL);
111 
112   if (argc < 2 || remaining+1 == argc) /* no arguments or at least two non-option words */
113     {
114       argp_help (&argp, stderr, ARGP_HELP_USAGE, argv[0]);
115       return 1;
116     }
117 
118   /* If we were passed an ELF file name in the BUILDID slot, look in there. */
119   unsigned char* build_id = (unsigned char*) argv[remaining+1];
120   int build_id_len = 0; /* assume text */
121 
122   int any_non_hex = 0;
123   int i;
124   for (i = 0; build_id[i] != '\0'; i++)
125     if ((build_id[i] >= '0' && build_id[i] <= '9') ||
126         (build_id[i] >= 'a' && build_id[i] <= 'f'))
127       ;
128     else
129       any_non_hex = 1;
130 
131   int fd = -1;
132   Elf* elf = NULL;
133   if (any_non_hex) /* raw build-id */
134     {
135       fd = open ((char*) build_id, O_RDONLY);
136       if (fd < 0)
137         fprintf (stderr, "Cannot open %s: %s\n", build_id, strerror(errno));
138     }
139   if (fd >= 0)
140     {
141       elf = elf_begin (fd, ELF_C_READ_MMAP_PRIVATE, NULL);
142       if (elf == NULL)
143         fprintf (stderr, "Cannot elf_begin %s: %s\n", build_id, elf_errmsg(-1));
144     }
145   if (elf != NULL)
146     {
147       const void *extracted_build_id;
148       ssize_t s = dwelf_elf_gnu_build_id(elf, &extracted_build_id);
149       if (s > 0)
150         {
151           /* Success: replace the build_id pointer/len with the binary blob
152              that elfutils is keeping for us.  It'll remain valid until elf_end(). */
153           build_id = (unsigned char*) extracted_build_id;
154           build_id_len = s;
155         }
156       else
157         fprintf (stderr, "Cannot extract build-id from %s: %s\n", build_id, elf_errmsg(-1));
158     }
159 
160   char *cache_name;
161   int rc = 0;
162 
163   /* Check whether FILETYPE is valid and call the appropriate
164      debuginfod_find_* function. If FILETYPE is "source"
165      then ensure a FILENAME was also supplied as an argument.  */
166   if (strcmp(argv[remaining], "debuginfo") == 0)
167     rc = debuginfod_find_debuginfo(client,
168 				   build_id, build_id_len,
169 				   &cache_name);
170   else if (strcmp(argv[remaining], "executable") == 0)
171     rc = debuginfod_find_executable(client,
172                                     build_id, build_id_len,
173 				    &cache_name);
174   else if (strcmp(argv[remaining], "source") == 0)
175     {
176       if (remaining+2 == argc || argv[remaining+2][0] != '/')
177         {
178           fprintf(stderr, "If FILETYPE is \"source\" then absolute /FILENAME must be given\n");
179           return 1;
180         }
181       rc = debuginfod_find_source(client,
182                                   build_id, build_id_len,
183 				  argv[remaining+2], &cache_name);
184     }
185   else
186     {
187       argp_help (&argp, stderr, ARGP_HELP_USAGE, argv[0]);
188       return 1;
189     }
190 
191   if (verbose)
192     {
193       const char* url = debuginfod_get_url (client);
194       if (url != NULL)
195         fprintf(stderr, "Downloaded from %s\n", url);
196     }
197 
198   debuginfod_end (client);
199   if (elf)
200     elf_end(elf);
201   if (fd >= 0)
202     close (fd);
203 
204   if (rc < 0)
205     {
206       fprintf(stderr, "Server query failed: %s\n", strerror(-rc));
207       return 1;
208     }
209 
210   printf("%s\n", cache_name);
211   free (cache_name);
212 
213   return 0;
214 }
215