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