• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Print the source files of a given ELF file.
2    Copyright (C) 2023 Red Hat, Inc.
3    This file is part of elfutils.
4    Written by Housam Alamour <alamourh@redhat.com>.
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
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 "printversion.h"
20 #include <dwarf.h>
21 #include <argp.h>
22 #include <cstring>
23 #include <set>
24 #include <string>
25 #include <cassert>
26 #include <config.h>
27 
28 #include <libdwfl.h>
29 #include <fcntl.h>
30 #include <iostream>
31 #include <libdw.h>
32 
33 using namespace std;
34 
35 /* Name and version of program.  */
36 ARGP_PROGRAM_VERSION_HOOK_DEF = print_version;
37 
38 /* Bug report address.  */
39 ARGP_PROGRAM_BUG_ADDRESS_DEF = PACKAGE_BUGREPORT;
40 
41 /* Definitions of arguments for argp functions.  */
42 static const struct argp_option options[] =
43 {
44   { NULL, 0, NULL, OPTION_DOC, N_("Output options:"), 1 },
45   { "null", '0', NULL, 0,
46     N_ ("Separate items by a null instead of a newline."), 0 },
47   { "verbose", 'v', NULL, 0,
48     N_ ("Increase verbosity of logging messages."), 0 },
49   { "cu-only", 'c', NULL, 0, N_ ("Only list the CU names."), 0 },
50   { NULL, 0, NULL, 0, NULL, 0 }
51 };
52 
53 /* Short description of program.  */
54 static const char doc[] = N_("Lists the source files of a DWARF/ELF file. The default input is the file 'a.out'.");
55 
56 /* Strings for arguments in help texts.  */
57 static const char args_doc[] = N_("INPUT");
58 
59 /* Prototype for option handler.  */
60 static error_t parse_opt (int key, char *arg, struct argp_state *state);
61 
62 static struct argp_child argp_children[2]; /* [0] is set in main.  */
63 
64 /* Data structure to communicate with argp functions.  */
65 static const struct argp argp =
66 {
67   options, parse_opt, args_doc, doc, argp_children, NULL, NULL
68 };
69 
70 /* Verbose message printing. */
71 static bool verbose;
72 /* Delimit the output with nulls. */
73 static bool null_arg;
74 /* Only print compilation unit names. */
75 static bool CU_only;
76 
77 /* Handle program arguments. */
78 static error_t
parse_opt(int key,char * arg,struct argp_state * state)79 parse_opt (int key, char *arg, struct argp_state *state)
80 {
81   /* Suppress "unused parameter" warning. */
82   (void)arg;
83   switch (key)
84     {
85     case ARGP_KEY_INIT:
86       state->child_inputs[0] = state->input;
87       break;
88 
89     case '0':
90       null_arg = true;
91       break;
92 
93     case 'v':
94       verbose = true;
95       break;
96 
97     case 'c':
98       CU_only = true;
99       break;
100 
101     default:
102       return ARGP_ERR_UNKNOWN;
103     }
104   return 0;
105 }
106 
107 
108 /* Global list of collected source files.  Normally, it'll contain
109    the sources of just one named binary, but the '-K' option can cause
110    multiple dwfl modules to be loaded, thus listed.   */
111    set<string> debug_sourcefiles;
112 
113 static int
collect_sourcefiles(Dwfl_Module * dwflmod,void ** userdata,const char * name,Dwarf_Addr base,void * arg)114 collect_sourcefiles (Dwfl_Module *dwflmod,
115                      void **userdata __attribute__ ((unused)),
116                      const char *name __attribute__ ((unused)),
117                      Dwarf_Addr base __attribute__ ((unused)),
118                      void *arg __attribute__ ((unused)))
119 {
120   Dwarf *dbg;
121   Dwarf_Addr bias; /* ignored - for addressing purposes only  */
122 
123   dbg = dwfl_module_getdwarf (dwflmod, &bias);
124 
125   Dwarf_Off offset = 0;
126   Dwarf_Off old_offset;
127   size_t hsize;
128 
129   /* Traverse all CUs of this module.  */
130   while (dwarf_nextcu (dbg, old_offset = offset, &offset, &hsize, NULL, NULL, NULL) == 0)
131     {
132       Dwarf_Die cudie_mem;
133       Dwarf_Die *cudie = dwarf_offdie (dbg, old_offset + hsize, &cudie_mem);
134 
135       if (cudie == NULL)
136         continue;
137 
138       const char *cuname = dwarf_diename (cudie) ?: "<unknown>";
139       Dwarf_Files *files;
140       size_t nfiles;
141       if (dwarf_getsrcfiles (cudie, &files, &nfiles) != 0)
142         continue;
143 
144       /* extract DW_AT_comp_dir to resolve relative file names  */
145       const char *comp_dir = "";
146       const char *const *dirs;
147       size_t ndirs;
148 
149       if (dwarf_getsrcdirs (files, &dirs, &ndirs) == 0 && dirs[0] != NULL)
150         comp_dir = dirs[0];
151       if (comp_dir == NULL)
152         comp_dir = "";
153 
154       if (verbose)
155         std::clog << "searching for sources for cu=" << cuname
156                   << " comp_dir=" << comp_dir << " #files=" << nfiles
157                   << " #dirs=" << ndirs << endl;
158 
159       for (size_t f = 1; f < nfiles; f++)
160         {
161           const char *hat;
162           if (CU_only)
163           {
164             if (strcmp(cuname, "<unknown>") == 0 || strcmp(cuname, "<artificial>") == 0 )
165               continue;
166             hat = cuname;
167           }
168           else
169             hat = dwarf_filesrc (files, f, NULL, NULL);
170 
171           if (hat == NULL)
172             continue;
173 
174           if (string(hat).find("<built-in>")
175               != std::string::npos) /* gcc intrinsics, don't bother record  */
176             continue;
177 
178           string waldo;
179           if (hat[0] == '/') /* absolute */
180             waldo = (string (hat));
181           else if (comp_dir[0] != '\0') /* comp_dir relative */
182             waldo = (string (comp_dir) + string ("/") + string (hat));
183           debug_sourcefiles.insert (waldo);
184         }
185     }
186 
187   return DWARF_CB_OK;
188 }
189 
190 
191 int
main(int argc,char * argv[])192 main (int argc, char *argv[])
193 {
194   int remaining;
195 
196   /* Parse and process arguments.  This includes opening the modules.  */
197   argp_children[0].argp = dwfl_standard_argp ();
198   argp_children[0].group = 1;
199 
200   Dwfl *dwfl = NULL;
201   (void) argp_parse (&argp, argc, argv, 0, &remaining, &dwfl);
202   assert (dwfl != NULL);
203   /* Process all loaded modules - probably just one, except if -K or -p is used. */
204   (void) dwfl_getmodules (dwfl, &collect_sourcefiles, NULL, 0);
205 
206   if (!debug_sourcefiles.empty ())
207     for (const string &element : debug_sourcefiles)
208       {
209         std::cout << element;
210         if (null_arg)
211           std::cout << '\0';
212         else
213           std::cout << '\n';
214       }
215 
216   dwfl_end (dwfl);
217   return 0;
218 }
219