1 /* Standard argp argument parsers for tools using libdwfl. 2 Copyright (C) 2005-2010, 2012, 2015 Red Hat, Inc. 3 This file is part of elfutils. 4 5 This file is free software; you can redistribute it and/or modify 6 it under the terms of either 7 8 * the GNU Lesser General Public License as published by the Free 9 Software Foundation; either version 3 of the License, or (at 10 your option) any later version 11 12 or 13 14 * the GNU General Public License as published by the Free 15 Software Foundation; either version 2 of the License, or (at 16 your option) any later version 17 18 or both in parallel, as here. 19 20 elfutils is distributed in the hope that it will be useful, but 21 WITHOUT ANY WARRANTY; without even the implied warranty of 22 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 23 General Public License for more details. 24 25 You should have received copies of the GNU General Public License and 26 the GNU Lesser General Public License along with this program. If 27 not, see <http://www.gnu.org/licenses/>. */ 28 29 #include "libdwflP.h" 30 #include <argp.h> 31 #include <stdlib.h> 32 #include <assert.h> 33 #include <libintl.h> 34 #include <fcntl.h> 35 #include <unistd.h> 36 37 /* gettext helper macros. */ 38 #define _(Str) dgettext ("elfutils", Str) 39 40 41 #define OPT_DEBUGINFO 0x100 42 #define OPT_COREFILE 0x101 43 44 static const struct argp_option options[] = 45 { 46 { NULL, 0, NULL, 0, N_("Input selection options:"), 0 }, 47 { "executable", 'e', "FILE", 0, N_("Find addresses in FILE"), 0 }, 48 { "core", OPT_COREFILE, "COREFILE", 0, 49 N_("Find addresses from signatures found in COREFILE"), 0 }, 50 { "pid", 'p', "PID", 0, 51 N_("Find addresses in files mapped into process PID"), 0 }, 52 { "linux-process-map", 'M', "FILE", 0, 53 N_("Find addresses in files mapped as read from FILE" 54 " in Linux /proc/PID/maps format"), 0 }, 55 { "kernel", 'k', NULL, 0, N_("Find addresses in the running kernel"), 0 }, 56 { "offline-kernel", 'K', "RELEASE", OPTION_ARG_OPTIONAL, 57 N_("Kernel with all modules"), 0 }, 58 { "debuginfo-path", OPT_DEBUGINFO, "PATH", 0, 59 N_("Search path for separate debuginfo files"), 0 }, 60 { NULL, 0, NULL, 0, NULL, 0 } 61 }; 62 63 static char *debuginfo_path; 64 65 static const Dwfl_Callbacks offline_callbacks = 66 { 67 .find_debuginfo = INTUSE(dwfl_standard_find_debuginfo), 68 .debuginfo_path = &debuginfo_path, 69 70 .section_address = INTUSE(dwfl_offline_section_address), 71 72 /* We use this table for core files too. */ 73 .find_elf = INTUSE(dwfl_build_id_find_elf), 74 }; 75 76 static const Dwfl_Callbacks proc_callbacks = 77 { 78 .find_debuginfo = INTUSE(dwfl_standard_find_debuginfo), 79 .debuginfo_path = &debuginfo_path, 80 81 .find_elf = INTUSE(dwfl_linux_proc_find_elf), 82 }; 83 84 static const Dwfl_Callbacks kernel_callbacks = 85 { 86 .find_debuginfo = INTUSE(dwfl_standard_find_debuginfo), 87 .debuginfo_path = &debuginfo_path, 88 89 .find_elf = INTUSE(dwfl_linux_kernel_find_elf), 90 .section_address = INTUSE(dwfl_linux_kernel_module_section_address), 91 }; 92 93 /* Structure held at state->HOOK. */ 94 struct parse_opt 95 { 96 Dwfl *dwfl; 97 /* The -e|--executable parameter. */ 98 const char *e; 99 /* The --core parameter. */ 100 const char *core; 101 }; 102 103 static error_t 104 parse_opt (int key, char *arg, struct argp_state *state) 105 { 106 inline void failure (Dwfl *dwfl, int errnum, const char *msg) 107 { 108 if (dwfl != NULL) 109 dwfl_end (dwfl); 110 if (errnum == -1) 111 argp_failure (state, EXIT_FAILURE, 0, "%s: %s", 112 msg, INTUSE(dwfl_errmsg) (-1)); 113 else 114 argp_failure (state, EXIT_FAILURE, errnum, "%s", msg); 115 } 116 inline error_t fail (Dwfl *dwfl, int errnum, const char *msg) 117 { 118 failure (dwfl, errnum, msg); 119 return errnum == -1 ? EIO : errnum; 120 } 121 122 switch (key) 123 { 124 case ARGP_KEY_INIT: 125 { 126 assert (state->hook == NULL); 127 struct parse_opt *opt = calloc (1, sizeof (*opt)); 128 if (opt == NULL) 129 failure (NULL, DWFL_E_ERRNO, "calloc"); 130 state->hook = opt; 131 } 132 break; 133 134 case OPT_DEBUGINFO: 135 debuginfo_path = arg; 136 break; 137 138 case 'e': 139 { 140 struct parse_opt *opt = state->hook; 141 Dwfl *dwfl = opt->dwfl; 142 if (dwfl == NULL) 143 { 144 dwfl = INTUSE(dwfl_begin) (&offline_callbacks); 145 if (dwfl == NULL) 146 return fail (dwfl, -1, arg); 147 opt->dwfl = dwfl; 148 149 /* Start at zero so if there is just one -e foo.so, 150 the DSO is shown without address bias. */ 151 dwfl->offline_next_address = 0; 152 } 153 if (dwfl->callbacks != &offline_callbacks) 154 { 155 toomany: 156 argp_error (state, "%s", 157 _("only one of -e, -p, -k, -K, or --core allowed")); 158 return EINVAL; 159 } 160 opt->e = arg; 161 } 162 break; 163 164 case 'p': 165 { 166 struct parse_opt *opt = state->hook; 167 if (opt->dwfl == NULL) 168 { 169 Dwfl *dwfl = INTUSE(dwfl_begin) (&proc_callbacks); 170 int result = INTUSE(dwfl_linux_proc_report) (dwfl, atoi (arg)); 171 if (result != 0) 172 return fail (dwfl, result, arg); 173 174 /* Non-fatal to not be able to attach to process, ignore error. */ 175 INTUSE(dwfl_linux_proc_attach) (dwfl, atoi (arg), false); 176 177 opt->dwfl = dwfl; 178 } 179 else 180 goto toomany; 181 } 182 break; 183 184 case 'M': 185 { 186 struct parse_opt *opt = state->hook; 187 if (opt->dwfl == NULL) 188 { 189 FILE *f = fopen (arg, "r"); 190 if (f == NULL) 191 { 192 int code = errno; 193 argp_failure (state, EXIT_FAILURE, code, 194 "cannot open '%s'", arg); 195 return code; 196 } 197 Dwfl *dwfl = INTUSE(dwfl_begin) (&proc_callbacks); 198 int result = INTUSE(dwfl_linux_proc_maps_report) (dwfl, f); 199 fclose (f); 200 if (result != 0) 201 return fail (dwfl, result, arg); 202 opt->dwfl = dwfl; 203 } 204 else 205 goto toomany; 206 } 207 break; 208 209 case OPT_COREFILE: 210 { 211 struct parse_opt *opt = state->hook; 212 Dwfl *dwfl = opt->dwfl; 213 if (dwfl == NULL) 214 opt->dwfl = dwfl = INTUSE(dwfl_begin) (&offline_callbacks); 215 /* Permit -e and --core together. */ 216 else if (dwfl->callbacks != &offline_callbacks) 217 goto toomany; 218 opt->core = arg; 219 } 220 break; 221 222 case 'k': 223 { 224 struct parse_opt *opt = state->hook; 225 if (opt->dwfl == NULL) 226 { 227 Dwfl *dwfl = INTUSE(dwfl_begin) (&kernel_callbacks); 228 int result = INTUSE(dwfl_linux_kernel_report_kernel) (dwfl); 229 if (result != 0) 230 return fail (dwfl, result, _("cannot load kernel symbols")); 231 result = INTUSE(dwfl_linux_kernel_report_modules) (dwfl); 232 if (result != 0) 233 /* Non-fatal to have no modules since we do have the kernel. */ 234 failure (dwfl, result, _("cannot find kernel modules")); 235 opt->dwfl = dwfl; 236 } 237 else 238 goto toomany; 239 } 240 break; 241 242 case 'K': 243 { 244 struct parse_opt *opt = state->hook; 245 if (opt->dwfl == NULL) 246 { 247 Dwfl *dwfl = INTUSE(dwfl_begin) (&offline_callbacks); 248 int result = INTUSE(dwfl_linux_kernel_report_offline) (dwfl, arg, 249 NULL); 250 if (result != 0) 251 return fail (dwfl, result, _("cannot find kernel or modules")); 252 opt->dwfl = dwfl; 253 } 254 else 255 goto toomany; 256 } 257 break; 258 259 case ARGP_KEY_SUCCESS: 260 { 261 struct parse_opt *opt = state->hook; 262 Dwfl *dwfl = opt->dwfl; 263 264 if (dwfl == NULL) 265 { 266 /* Default if no -e, -p, or -k, is "-e a.out". */ 267 arg = "a.out"; 268 dwfl = INTUSE(dwfl_begin) (&offline_callbacks); 269 if (INTUSE(dwfl_report_offline) (dwfl, "", arg, -1) == NULL) 270 return fail (dwfl, -1, arg); 271 opt->dwfl = dwfl; 272 } 273 274 if (opt->core) 275 { 276 int fd = open (opt->core, O_RDONLY); 277 if (fd < 0) 278 { 279 int code = errno; 280 argp_failure (state, EXIT_FAILURE, code, 281 "cannot open '%s'", opt->core); 282 return code; 283 } 284 285 Elf *core; 286 Dwfl_Error error = __libdw_open_file (&fd, &core, true, false); 287 if (error != DWFL_E_NOERROR) 288 { 289 argp_failure (state, EXIT_FAILURE, 0, 290 _("cannot read ELF core file: %s"), 291 INTUSE(dwfl_errmsg) (error)); 292 return error == DWFL_E_ERRNO ? errno : EIO; 293 } 294 295 int result = INTUSE(dwfl_core_file_report) (dwfl, core, opt->e); 296 if (result < 0) 297 { 298 elf_end (core); 299 close (fd); 300 return fail (dwfl, result, opt->core); 301 } 302 303 /* Non-fatal to not be able to attach to core, ignore error. */ 304 INTUSE(dwfl_core_file_attach) (dwfl, core); 305 306 /* Store core Elf and fd in Dwfl to expose with dwfl_end. */ 307 if (dwfl->user_core == NULL) 308 { 309 dwfl->user_core = calloc (1, sizeof (struct Dwfl_User_Core)); 310 if (dwfl->user_core == NULL) 311 { 312 argp_failure (state, EXIT_FAILURE, 0, 313 _("Not enough memory")); 314 return ENOMEM; 315 } 316 } 317 dwfl->user_core->core = core; 318 dwfl->user_core->fd = fd; 319 320 if (result == 0) 321 { 322 argp_failure (state, EXIT_FAILURE, 0, 323 _("No modules recognized in core file")); 324 return ENOENT; 325 } 326 } 327 else if (opt->e) 328 { 329 if (INTUSE(dwfl_report_offline) (dwfl, "", opt->e, -1) == NULL) 330 return fail (dwfl, -1, opt->e); 331 } 332 333 /* One of the three flavors has done dwfl_begin and some reporting 334 if we got here. Tie up the Dwfl and return it to the caller of 335 argp_parse. */ 336 337 int result = INTUSE(dwfl_report_end) (dwfl, NULL, NULL); 338 assert (result == 0); 339 340 /* Update the input all along, so a parent parser can see it. 341 As we free OPT the update below will be no longer active. */ 342 *(Dwfl **) state->input = dwfl; 343 free (opt); 344 state->hook = NULL; 345 } 346 break; 347 348 case ARGP_KEY_ERROR: 349 { 350 struct parse_opt *opt = state->hook; 351 dwfl_end (opt->dwfl); 352 free (opt); 353 state->hook = NULL; 354 } 355 break; 356 357 default: 358 return ARGP_ERR_UNKNOWN; 359 } 360 361 /* Update the input all along, so a parent parser can see it. */ 362 struct parse_opt *opt = state->hook; 363 if (opt) 364 *(Dwfl **) state->input = opt->dwfl; 365 366 return 0; 367 } 368 369 static const struct argp libdwfl_argp = 370 { .options = options, .parser = parse_opt }; 371 372 const struct argp * 373 dwfl_standard_argp (void) 374 { 375 return &libdwfl_argp; 376 } 377