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