Commit | Line | Data |
---|---|---|
6f1b6291 NK |
1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* | |
3 | * DWARF debug information handling code. Copied from probe-finder.c. | |
4 | * | |
5 | * Written by Masami Hiramatsu <mhiramat@redhat.com> | |
6 | */ | |
7 | ||
8 | #include <errno.h> | |
9 | #include <fcntl.h> | |
10 | #include <stdio.h> | |
11 | #include <stdlib.h> | |
12 | #include <string.h> | |
13 | #include <unistd.h> | |
14 | #include <linux/zalloc.h> | |
15 | ||
16 | #include "build-id.h" | |
17 | #include "dso.h" | |
18 | #include "debug.h" | |
19 | #include "debuginfo.h" | |
20 | #include "symbol.h" | |
21 | ||
22 | #ifdef HAVE_DEBUGINFOD_SUPPORT | |
23 | #include <elfutils/debuginfod.h> | |
24 | #endif | |
25 | ||
26 | /* Dwarf FL wrappers */ | |
27 | static char *debuginfo_path; /* Currently dummy */ | |
28 | ||
29 | static const Dwfl_Callbacks offline_callbacks = { | |
30 | .find_debuginfo = dwfl_standard_find_debuginfo, | |
31 | .debuginfo_path = &debuginfo_path, | |
32 | ||
33 | .section_address = dwfl_offline_section_address, | |
34 | ||
35 | /* We use this table for core files too. */ | |
36 | .find_elf = dwfl_build_id_find_elf, | |
37 | }; | |
38 | ||
39 | /* Get a Dwarf from offline image */ | |
40 | static int debuginfo__init_offline_dwarf(struct debuginfo *dbg, | |
41 | const char *path) | |
42 | { | |
43 | GElf_Addr dummy; | |
44 | int fd; | |
45 | ||
46 | fd = open(path, O_RDONLY); | |
47 | if (fd < 0) | |
48 | return fd; | |
49 | ||
50 | dbg->dwfl = dwfl_begin(&offline_callbacks); | |
51 | if (!dbg->dwfl) | |
52 | goto error; | |
53 | ||
54 | dwfl_report_begin(dbg->dwfl); | |
55 | dbg->mod = dwfl_report_offline(dbg->dwfl, "", "", fd); | |
56 | if (!dbg->mod) | |
57 | goto error; | |
58 | ||
59 | dbg->dbg = dwfl_module_getdwarf(dbg->mod, &dbg->bias); | |
60 | if (!dbg->dbg) | |
61 | goto error; | |
62 | ||
63 | dwfl_module_build_id(dbg->mod, &dbg->build_id, &dummy); | |
64 | ||
65 | dwfl_report_end(dbg->dwfl, NULL, NULL); | |
66 | ||
67 | return 0; | |
68 | error: | |
69 | if (dbg->dwfl) | |
70 | dwfl_end(dbg->dwfl); | |
71 | else | |
72 | close(fd); | |
73 | memset(dbg, 0, sizeof(*dbg)); | |
74 | ||
75 | return -ENOENT; | |
76 | } | |
77 | ||
78 | static struct debuginfo *__debuginfo__new(const char *path) | |
79 | { | |
80 | struct debuginfo *dbg = zalloc(sizeof(*dbg)); | |
81 | if (!dbg) | |
82 | return NULL; | |
83 | ||
84 | if (debuginfo__init_offline_dwarf(dbg, path) < 0) | |
85 | zfree(&dbg); | |
86 | if (dbg) | |
87 | pr_debug("Open Debuginfo file: %s\n", path); | |
88 | return dbg; | |
89 | } | |
90 | ||
91 | enum dso_binary_type distro_dwarf_types[] = { | |
92 | DSO_BINARY_TYPE__FEDORA_DEBUGINFO, | |
93 | DSO_BINARY_TYPE__UBUNTU_DEBUGINFO, | |
94 | DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO, | |
95 | DSO_BINARY_TYPE__BUILDID_DEBUGINFO, | |
96 | DSO_BINARY_TYPE__MIXEDUP_UBUNTU_DEBUGINFO, | |
97 | DSO_BINARY_TYPE__NOT_FOUND, | |
98 | }; | |
99 | ||
100 | struct debuginfo *debuginfo__new(const char *path) | |
101 | { | |
102 | enum dso_binary_type *type; | |
103 | char buf[PATH_MAX], nil = '\0'; | |
104 | struct dso *dso; | |
105 | struct debuginfo *dinfo = NULL; | |
106 | struct build_id bid; | |
107 | ||
108 | /* Try to open distro debuginfo files */ | |
109 | dso = dso__new(path); | |
110 | if (!dso) | |
111 | goto out; | |
112 | ||
113 | /* Set the build id for DSO_BINARY_TYPE__BUILDID_DEBUGINFO */ | |
114 | if (is_regular_file(path) && filename__read_build_id(path, &bid) > 0) | |
115 | dso__set_build_id(dso, &bid); | |
116 | ||
117 | for (type = distro_dwarf_types; | |
118 | !dinfo && *type != DSO_BINARY_TYPE__NOT_FOUND; | |
119 | type++) { | |
120 | if (dso__read_binary_type_filename(dso, *type, &nil, | |
121 | buf, PATH_MAX) < 0) | |
122 | continue; | |
123 | dinfo = __debuginfo__new(buf); | |
124 | } | |
125 | dso__put(dso); | |
126 | ||
127 | out: | |
128 | /* if failed to open all distro debuginfo, open given binary */ | |
129 | return dinfo ? : __debuginfo__new(path); | |
130 | } | |
131 | ||
132 | void debuginfo__delete(struct debuginfo *dbg) | |
133 | { | |
134 | if (dbg) { | |
135 | if (dbg->dwfl) | |
136 | dwfl_end(dbg->dwfl); | |
137 | free(dbg); | |
138 | } | |
139 | } | |
140 | ||
141 | /* For the kernel module, we need a special code to get a DIE */ | |
142 | int debuginfo__get_text_offset(struct debuginfo *dbg, Dwarf_Addr *offs, | |
143 | bool adjust_offset) | |
144 | { | |
145 | int n, i; | |
146 | Elf32_Word shndx; | |
147 | Elf_Scn *scn; | |
148 | Elf *elf; | |
149 | GElf_Shdr mem, *shdr; | |
150 | const char *p; | |
151 | ||
152 | elf = dwfl_module_getelf(dbg->mod, &dbg->bias); | |
153 | if (!elf) | |
154 | return -EINVAL; | |
155 | ||
156 | /* Get the number of relocations */ | |
157 | n = dwfl_module_relocations(dbg->mod); | |
158 | if (n < 0) | |
159 | return -ENOENT; | |
160 | /* Search the relocation related .text section */ | |
161 | for (i = 0; i < n; i++) { | |
162 | p = dwfl_module_relocation_info(dbg->mod, i, &shndx); | |
163 | if (strcmp(p, ".text") == 0) { | |
164 | /* OK, get the section header */ | |
165 | scn = elf_getscn(elf, shndx); | |
166 | if (!scn) | |
167 | return -ENOENT; | |
168 | shdr = gelf_getshdr(scn, &mem); | |
169 | if (!shdr) | |
170 | return -ENOENT; | |
171 | *offs = shdr->sh_addr; | |
172 | if (adjust_offset) | |
173 | *offs -= shdr->sh_offset; | |
174 | } | |
175 | } | |
176 | return 0; | |
177 | } | |
178 | ||
179 | #ifdef HAVE_DEBUGINFOD_SUPPORT | |
180 | int get_source_from_debuginfod(const char *raw_path, | |
181 | const char *sbuild_id, char **new_path) | |
182 | { | |
183 | debuginfod_client *c = debuginfod_begin(); | |
184 | const char *p = raw_path; | |
185 | int fd; | |
186 | ||
187 | if (!c) | |
188 | return -ENOMEM; | |
189 | ||
190 | fd = debuginfod_find_source(c, (const unsigned char *)sbuild_id, | |
191 | 0, p, new_path); | |
192 | pr_debug("Search %s from debuginfod -> %d\n", p, fd); | |
193 | if (fd >= 0) | |
194 | close(fd); | |
195 | debuginfod_end(c); | |
196 | if (fd < 0) { | |
197 | pr_debug("Failed to find %s in debuginfod (%s)\n", | |
198 | raw_path, sbuild_id); | |
199 | return -ENOENT; | |
200 | } | |
201 | pr_debug("Got a source %s\n", *new_path); | |
202 | ||
203 | return 0; | |
204 | } | |
205 | #endif /* HAVE_DEBUGINFOD_SUPPORT */ |