]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blob - gdb/debuginfod-support.c
gdb/debuginfod: Prevent out_of_range exception
[thirdparty/binutils-gdb.git] / gdb / debuginfod-support.c
1 /* debuginfod utilities for GDB.
2 Copyright (C) 2020-2022 Free Software Foundation, Inc.
3
4 This file is part of GDB.
5
6 This program 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 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>. */
18
19 #include "defs.h"
20 #include <errno.h>
21 #include "gdbsupport/scoped_fd.h"
22 #include "debuginfod-support.h"
23 #include "gdbsupport/gdb_optional.h"
24 #include "cli/cli-cmds.h"
25 #include "cli/cli-style.h"
26 #include "target.h"
27
28 /* Set/show debuginfod commands. */
29 static cmd_list_element *set_debuginfod_prefix_list;
30 static cmd_list_element *show_debuginfod_prefix_list;
31
32 static const char debuginfod_on[] = "on";
33 static const char debuginfod_off[] = "off";
34 static const char debuginfod_ask[] = "ask";
35
36 static const char *debuginfod_enabled_enum[] =
37 {
38 debuginfod_on,
39 debuginfod_off,
40 debuginfod_ask,
41 nullptr
42 };
43
44 static const char *debuginfod_enabled =
45 #if defined(HAVE_LIBDEBUGINFOD)
46 debuginfod_ask;
47 #else
48 debuginfod_off;
49 #endif
50
51 static unsigned int debuginfod_verbose = 1;
52
53 #ifndef HAVE_LIBDEBUGINFOD
54 scoped_fd
55 debuginfod_source_query (const unsigned char *build_id,
56 int build_id_len,
57 const char *srcpath,
58 gdb::unique_xmalloc_ptr<char> *destname)
59 {
60 return scoped_fd (-ENOSYS);
61 }
62
63 scoped_fd
64 debuginfod_debuginfo_query (const unsigned char *build_id,
65 int build_id_len,
66 const char *filename,
67 gdb::unique_xmalloc_ptr<char> *destname)
68 {
69 return scoped_fd (-ENOSYS);
70 }
71
72 scoped_fd
73 debuginfod_exec_query (const unsigned char *build_id,
74 int build_id_len,
75 const char *filename,
76 gdb::unique_xmalloc_ptr<char> *destname)
77 {
78 return scoped_fd (-ENOSYS);
79 }
80
81 #define NO_IMPL _("Support for debuginfod is not compiled into GDB.")
82
83 #else
84 #include <elfutils/debuginfod.h>
85
86 struct user_data
87 {
88 user_data (const char *desc, const char *fname)
89 : desc (desc), fname (fname), has_printed (false)
90 { }
91
92 const char * const desc;
93 const char * const fname;
94 bool has_printed;
95 };
96
97 /* Deleter for a debuginfod_client. */
98
99 struct debuginfod_client_deleter
100 {
101 void operator() (debuginfod_client *c)
102 {
103 debuginfod_end (c);
104 }
105 };
106
107 using debuginfod_client_up
108 = std::unique_ptr<debuginfod_client, debuginfod_client_deleter>;
109
110 static int
111 progressfn (debuginfod_client *c, long cur, long total)
112 {
113 user_data *data = static_cast<user_data *> (debuginfod_get_user_data (c));
114 gdb_assert (data != nullptr);
115
116 if (check_quit_flag ())
117 {
118 gdb_printf ("Cancelling download of %s %ps...\n",
119 data->desc,
120 styled_string (file_name_style.style (), data->fname));
121 return 1;
122 }
123
124 if (!data->has_printed)
125 {
126 /* Include the transfer size, if available. */
127 if (total > 0)
128 {
129 float size = 1.0f * total / 1024;
130 const char *unit = "KB";
131
132 /* If size is greater than 0.01 MB, set unit to MB. */
133 if (size > 10.24)
134 {
135 size /= 1024;
136 unit = "MB";
137 }
138
139 gdb_printf ("Downloading %.2f %s %s %ps...\n",
140 size, unit, data->desc,
141 styled_string (file_name_style.style (),
142 data->fname));
143 }
144 else
145 gdb_printf ("Downloading %s %ps...\n", data->desc,
146 styled_string (file_name_style.style (), data->fname));
147
148 data->has_printed = true;
149 }
150
151 return 0;
152 }
153
154 static debuginfod_client *
155 get_debuginfod_client ()
156 {
157 static debuginfod_client_up global_client;
158
159 if (global_client == nullptr)
160 {
161 global_client.reset (debuginfod_begin ());
162
163 if (global_client != nullptr)
164 debuginfod_set_progressfn (global_client.get (), progressfn);
165 }
166
167 return global_client.get ();
168 }
169
170 /* Check if debuginfod is enabled. If configured to do so, ask the user
171 whether to enable debuginfod. */
172
173 static bool
174 debuginfod_is_enabled ()
175 {
176 const char *urls = getenv (DEBUGINFOD_URLS_ENV_VAR);
177
178 if (urls == nullptr || urls[0] == '\0'
179 || debuginfod_enabled == debuginfod_off)
180 return false;
181
182 if (debuginfod_enabled == debuginfod_ask)
183 {
184 gdb_printf (_("\nThis GDB supports auto-downloading debuginfo " \
185 "from the following URLs:\n"));
186
187 gdb::string_view url_view (urls);
188 while (true)
189 {
190 size_t off = url_view.find_first_not_of (' ');
191 if (off == gdb::string_view::npos)
192 break;
193 url_view = url_view.substr (off);
194 off = url_view.find_first_of (' ');
195 gdb_printf
196 (_(" <%ps>\n"),
197 styled_string (file_name_style.style (),
198 gdb::to_string (url_view.substr (0,
199 off)).c_str ()));
200 if (off == gdb::string_view::npos)
201 break;
202 url_view = url_view.substr (off);
203 }
204
205 int resp = nquery (_("Enable debuginfod for this session? "));
206 if (!resp)
207 {
208 gdb_printf (_("Debuginfod has been disabled.\nTo make this " \
209 "setting permanent, add \'set debuginfod " \
210 "enabled off\' to .gdbinit.\n"));
211 debuginfod_enabled = debuginfod_off;
212 return false;
213 }
214
215 gdb_printf (_("Debuginfod has been enabled.\nTo make this " \
216 "setting permanent, add \'set debuginfod enabled " \
217 "on\' to .gdbinit.\n"));
218 debuginfod_enabled = debuginfod_on;
219 }
220
221 return true;
222 }
223
224 /* See debuginfod-support.h */
225
226 scoped_fd
227 debuginfod_source_query (const unsigned char *build_id,
228 int build_id_len,
229 const char *srcpath,
230 gdb::unique_xmalloc_ptr<char> *destname)
231 {
232 if (!debuginfod_is_enabled ())
233 return scoped_fd (-ENOSYS);
234
235 debuginfod_client *c = get_debuginfod_client ();
236
237 if (c == nullptr)
238 return scoped_fd (-ENOMEM);
239
240 char *dname = nullptr;
241 user_data data ("source file", srcpath);
242
243 debuginfod_set_user_data (c, &data);
244 gdb::optional<target_terminal::scoped_restore_terminal_state> term_state;
245 if (target_supports_terminal_ours ())
246 {
247 term_state.emplace ();
248 target_terminal::ours ();
249 }
250
251 scoped_fd fd (debuginfod_find_source (c,
252 build_id,
253 build_id_len,
254 srcpath,
255 &dname));
256 debuginfod_set_user_data (c, nullptr);
257
258 if (fd.get () < 0 && fd.get () != -ENOENT)
259 gdb_printf (_("Download failed: %s. Continuing without source file %ps.\n"),
260 safe_strerror (-fd.get ()),
261 styled_string (file_name_style.style (), srcpath));
262
263 if (fd.get () >= 0)
264 destname->reset (dname);
265
266 return fd;
267 }
268
269 /* See debuginfod-support.h */
270
271 scoped_fd
272 debuginfod_debuginfo_query (const unsigned char *build_id,
273 int build_id_len,
274 const char *filename,
275 gdb::unique_xmalloc_ptr<char> *destname)
276 {
277 if (!debuginfod_is_enabled ())
278 return scoped_fd (-ENOSYS);
279
280 debuginfod_client *c = get_debuginfod_client ();
281
282 if (c == nullptr)
283 return scoped_fd (-ENOMEM);
284
285 char *dname = nullptr;
286 user_data data ("separate debug info for", filename);
287
288 debuginfod_set_user_data (c, &data);
289 gdb::optional<target_terminal::scoped_restore_terminal_state> term_state;
290 if (target_supports_terminal_ours ())
291 {
292 term_state.emplace ();
293 target_terminal::ours ();
294 }
295
296 scoped_fd fd (debuginfod_find_debuginfo (c, build_id, build_id_len,
297 &dname));
298 debuginfod_set_user_data (c, nullptr);
299
300 if (fd.get () < 0 && fd.get () != -ENOENT)
301 gdb_printf (_("Download failed: %s. Continuing without debug info for %ps.\n"),
302 safe_strerror (-fd.get ()),
303 styled_string (file_name_style.style (), filename));
304
305 if (fd.get () >= 0)
306 destname->reset (dname);
307
308 return fd;
309 }
310
311 /* See debuginfod-support.h */
312
313 scoped_fd
314 debuginfod_exec_query (const unsigned char *build_id,
315 int build_id_len,
316 const char *filename,
317 gdb::unique_xmalloc_ptr<char> *destname)
318 {
319 if (!debuginfod_is_enabled ())
320 return scoped_fd (-ENOSYS);
321
322 debuginfod_client *c = get_debuginfod_client ();
323
324 if (c == nullptr)
325 return scoped_fd (-ENOMEM);
326
327 char *dname = nullptr;
328 user_data data ("executable for", filename);
329
330 debuginfod_set_user_data (c, &data);
331 gdb::optional<target_terminal::scoped_restore_terminal_state> term_state;
332 if (target_supports_terminal_ours ())
333 {
334 term_state.emplace ();
335 target_terminal::ours ();
336 }
337
338 scoped_fd fd (debuginfod_find_executable (c, build_id, build_id_len, &dname));
339 debuginfod_set_user_data (c, nullptr);
340
341 if (fd.get () < 0 && fd.get () != -ENOENT)
342 gdb_printf (_("Download failed: %s. " \
343 "Continuing without executable for %ps.\n"),
344 safe_strerror (-fd.get ()),
345 styled_string (file_name_style.style (), filename));
346
347 if (fd.get () >= 0)
348 destname->reset (dname);
349
350 return fd;
351 }
352 #endif
353
354 /* Set callback for "set debuginfod enabled". */
355
356 static void
357 set_debuginfod_enabled (const char *value)
358 {
359 #if defined(HAVE_LIBDEBUGINFOD)
360 debuginfod_enabled = value;
361 #else
362 error (NO_IMPL);
363 #endif
364 }
365
366 /* Get callback for "set debuginfod enabled". */
367
368 static const char *
369 get_debuginfod_enabled ()
370 {
371 return debuginfod_enabled;
372 }
373
374 /* Show callback for "set debuginfod enabled". */
375
376 static void
377 show_debuginfod_enabled (ui_file *file, int from_tty, cmd_list_element *cmd,
378 const char *value)
379 {
380 gdb_printf (file,
381 _("Debuginfod functionality is currently set to "
382 "\"%s\".\n"), debuginfod_enabled);
383 }
384
385 /* Set callback for "set debuginfod urls". */
386
387 static void
388 set_debuginfod_urls (const std::string &urls)
389 {
390 #if defined(HAVE_LIBDEBUGINFOD)
391 if (setenv (DEBUGINFOD_URLS_ENV_VAR, urls.c_str (), 1) != 0)
392 warning (_("Unable to set debuginfod URLs: %s"), safe_strerror (errno));
393 #else
394 error (NO_IMPL);
395 #endif
396 }
397
398 /* Get callback for "set debuginfod urls". */
399
400 static const std::string&
401 get_debuginfod_urls ()
402 {
403 static std::string urls;
404 #if defined(HAVE_LIBDEBUGINFOD)
405 const char *envvar = getenv (DEBUGINFOD_URLS_ENV_VAR);
406
407 if (envvar != nullptr)
408 urls = envvar;
409 else
410 urls.clear ();
411 #endif
412
413 return urls;
414 }
415
416 /* Show callback for "set debuginfod urls". */
417
418 static void
419 show_debuginfod_urls (ui_file *file, int from_tty, cmd_list_element *cmd,
420 const char *value)
421 {
422 if (value[0] == '\0')
423 gdb_printf (file, _("Debuginfod URLs have not been set.\n"));
424 else
425 gdb_printf (file, _("Debuginfod URLs are currently set to:\n%s\n"),
426 value);
427 }
428
429 /* Show callback for "set debuginfod verbose". */
430
431 static void
432 show_debuginfod_verbose_command (ui_file *file, int from_tty,
433 cmd_list_element *cmd, const char *value)
434 {
435 gdb_printf (file, _("Debuginfod verbose output is set to %s.\n"),
436 value);
437 }
438
439 /* Register debuginfod commands. */
440
441 void _initialize_debuginfod ();
442 void
443 _initialize_debuginfod ()
444 {
445 /* set/show debuginfod */
446 add_setshow_prefix_cmd ("debuginfod", class_run,
447 _("Set debuginfod options."),
448 _("Show debuginfod options."),
449 &set_debuginfod_prefix_list,
450 &show_debuginfod_prefix_list,
451 &setlist, &showlist);
452
453 add_setshow_enum_cmd ("enabled", class_run, debuginfod_enabled_enum,
454 _("Set whether to use debuginfod."),
455 _("Show whether to use debuginfod."),
456 _("\
457 When on, enable the use of debuginfod to download missing debug info and\n\
458 source files."),
459 set_debuginfod_enabled,
460 get_debuginfod_enabled,
461 show_debuginfod_enabled,
462 &set_debuginfod_prefix_list,
463 &show_debuginfod_prefix_list);
464
465 /* set/show debuginfod urls */
466 add_setshow_string_noescape_cmd ("urls", class_run, _("\
467 Set the list of debuginfod server URLs."), _("\
468 Show the list of debuginfod server URLs."), _("\
469 Manage the space-separated list of debuginfod server URLs that GDB will query \
470 when missing debuginfo, executables or source files.\nThe default value is \
471 copied from the DEBUGINFOD_URLS environment variable."),
472 set_debuginfod_urls,
473 get_debuginfod_urls,
474 show_debuginfod_urls,
475 &set_debuginfod_prefix_list,
476 &show_debuginfod_prefix_list);
477
478 /* set/show debuginfod verbose */
479 add_setshow_zuinteger_cmd ("verbose", class_support,
480 &debuginfod_verbose, _("\
481 Set verbosity of debuginfod output."), _("\
482 Show debuginfod debugging."), _("\
483 When set to a non-zero value, display verbose output for each debuginfod \
484 query.\nTo disable, set to zero. Verbose output is displayed by default."),
485 nullptr,
486 show_debuginfod_verbose_command,
487 &set_debuginfod_prefix_list,
488 &show_debuginfod_prefix_list);
489 }