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