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