1 | #include "dso.h" |
2 | #include "symbol.h" |
3 | #include "symsrc.h" |
4 | |
5 | #include <errno.h> |
6 | #include <unistd.h> |
7 | #include <stdio.h> |
8 | #include <fcntl.h> |
9 | #include <string.h> |
10 | #include <stdlib.h> |
11 | #include <byteswap.h> |
12 | #include <sys/stat.h> |
13 | #include <linux/zalloc.h> |
14 | #include <internal/lib.h> |
15 | |
16 | static bool check_need_swap(int file_endian) |
17 | { |
18 | const int data = 1; |
19 | u8 *check = (u8 *)&data; |
20 | int host_endian; |
21 | |
22 | if (check[0] == 1) |
23 | host_endian = ELFDATA2LSB; |
24 | else |
25 | host_endian = ELFDATA2MSB; |
26 | |
27 | return host_endian != file_endian; |
28 | } |
29 | |
30 | #define NOTE_ALIGN(sz) (((sz) + 3) & ~3) |
31 | |
32 | #define NT_GNU_BUILD_ID 3 |
33 | |
34 | static int read_build_id(void *note_data, size_t note_len, struct build_id *bid, |
35 | bool need_swap) |
36 | { |
37 | size_t size = sizeof(bid->data); |
38 | struct { |
39 | u32 n_namesz; |
40 | u32 n_descsz; |
41 | u32 n_type; |
42 | } *nhdr; |
43 | void *ptr; |
44 | |
45 | ptr = note_data; |
46 | while (ptr < (note_data + note_len)) { |
47 | const char *name; |
48 | size_t namesz, descsz; |
49 | |
50 | nhdr = ptr; |
51 | if (need_swap) { |
52 | nhdr->n_namesz = bswap_32(nhdr->n_namesz); |
53 | nhdr->n_descsz = bswap_32(nhdr->n_descsz); |
54 | nhdr->n_type = bswap_32(nhdr->n_type); |
55 | } |
56 | |
57 | namesz = NOTE_ALIGN(nhdr->n_namesz); |
58 | descsz = NOTE_ALIGN(nhdr->n_descsz); |
59 | |
60 | ptr += sizeof(*nhdr); |
61 | name = ptr; |
62 | ptr += namesz; |
63 | if (nhdr->n_type == NT_GNU_BUILD_ID && |
64 | nhdr->n_namesz == sizeof("GNU" )) { |
65 | if (memcmp(p: name, q: "GNU" , size: sizeof("GNU" )) == 0) { |
66 | size_t sz = min(size, descsz); |
67 | memcpy(bid->data, ptr, sz); |
68 | memset(bid->data + sz, 0, size - sz); |
69 | bid->size = sz; |
70 | return 0; |
71 | } |
72 | } |
73 | ptr += descsz; |
74 | } |
75 | |
76 | return -1; |
77 | } |
78 | |
79 | int filename__read_debuglink(const char *filename __maybe_unused, |
80 | char *debuglink __maybe_unused, |
81 | size_t size __maybe_unused) |
82 | { |
83 | return -1; |
84 | } |
85 | |
86 | /* |
87 | * Just try PT_NOTE header otherwise fails |
88 | */ |
89 | int filename__read_build_id(const char *filename, struct build_id *bid) |
90 | { |
91 | FILE *fp; |
92 | int ret = -1; |
93 | bool need_swap = false; |
94 | u8 e_ident[EI_NIDENT]; |
95 | size_t buf_size; |
96 | void *buf; |
97 | int i; |
98 | |
99 | fp = fopen(filename, "r" ); |
100 | if (fp == NULL) |
101 | return -1; |
102 | |
103 | if (fread(e_ident, sizeof(e_ident), 1, fp) != 1) |
104 | goto out; |
105 | |
106 | if (memcmp(e_ident, ELFMAG, SELFMAG) || |
107 | e_ident[EI_VERSION] != EV_CURRENT) |
108 | goto out; |
109 | |
110 | need_swap = check_need_swap(e_ident[EI_DATA]); |
111 | |
112 | /* for simplicity */ |
113 | fseek(fp, 0, SEEK_SET); |
114 | |
115 | if (e_ident[EI_CLASS] == ELFCLASS32) { |
116 | Elf32_Ehdr ehdr; |
117 | Elf32_Phdr *phdr; |
118 | |
119 | if (fread(&ehdr, sizeof(ehdr), 1, fp) != 1) |
120 | goto out; |
121 | |
122 | if (need_swap) { |
123 | ehdr.e_phoff = bswap_32(ehdr.e_phoff); |
124 | ehdr.e_phentsize = bswap_16(ehdr.e_phentsize); |
125 | ehdr.e_phnum = bswap_16(ehdr.e_phnum); |
126 | } |
127 | |
128 | buf_size = ehdr.e_phentsize * ehdr.e_phnum; |
129 | buf = malloc(buf_size); |
130 | if (buf == NULL) |
131 | goto out; |
132 | |
133 | fseek(fp, ehdr.e_phoff, SEEK_SET); |
134 | if (fread(buf, buf_size, 1, fp) != 1) |
135 | goto out_free; |
136 | |
137 | for (i = 0, phdr = buf; i < ehdr.e_phnum; i++, phdr++) { |
138 | void *tmp; |
139 | long offset; |
140 | |
141 | if (need_swap) { |
142 | phdr->p_type = bswap_32(phdr->p_type); |
143 | phdr->p_offset = bswap_32(phdr->p_offset); |
144 | phdr->p_filesz = bswap_32(phdr->p_filesz); |
145 | } |
146 | |
147 | if (phdr->p_type != PT_NOTE) |
148 | continue; |
149 | |
150 | buf_size = phdr->p_filesz; |
151 | offset = phdr->p_offset; |
152 | tmp = realloc(buf, buf_size); |
153 | if (tmp == NULL) |
154 | goto out_free; |
155 | |
156 | buf = tmp; |
157 | fseek(fp, offset, SEEK_SET); |
158 | if (fread(buf, buf_size, 1, fp) != 1) |
159 | goto out_free; |
160 | |
161 | ret = read_build_id(buf, buf_size, bid, need_swap); |
162 | if (ret == 0) { |
163 | ret = bid->size; |
164 | break; |
165 | } |
166 | } |
167 | } else { |
168 | Elf64_Ehdr ehdr; |
169 | Elf64_Phdr *phdr; |
170 | |
171 | if (fread(&ehdr, sizeof(ehdr), 1, fp) != 1) |
172 | goto out; |
173 | |
174 | if (need_swap) { |
175 | ehdr.e_phoff = bswap_64(ehdr.e_phoff); |
176 | ehdr.e_phentsize = bswap_16(ehdr.e_phentsize); |
177 | ehdr.e_phnum = bswap_16(ehdr.e_phnum); |
178 | } |
179 | |
180 | buf_size = ehdr.e_phentsize * ehdr.e_phnum; |
181 | buf = malloc(buf_size); |
182 | if (buf == NULL) |
183 | goto out; |
184 | |
185 | fseek(fp, ehdr.e_phoff, SEEK_SET); |
186 | if (fread(buf, buf_size, 1, fp) != 1) |
187 | goto out_free; |
188 | |
189 | for (i = 0, phdr = buf; i < ehdr.e_phnum; i++, phdr++) { |
190 | void *tmp; |
191 | long offset; |
192 | |
193 | if (need_swap) { |
194 | phdr->p_type = bswap_32(phdr->p_type); |
195 | phdr->p_offset = bswap_64(phdr->p_offset); |
196 | phdr->p_filesz = bswap_64(phdr->p_filesz); |
197 | } |
198 | |
199 | if (phdr->p_type != PT_NOTE) |
200 | continue; |
201 | |
202 | buf_size = phdr->p_filesz; |
203 | offset = phdr->p_offset; |
204 | tmp = realloc(buf, buf_size); |
205 | if (tmp == NULL) |
206 | goto out_free; |
207 | |
208 | buf = tmp; |
209 | fseek(fp, offset, SEEK_SET); |
210 | if (fread(buf, buf_size, 1, fp) != 1) |
211 | goto out_free; |
212 | |
213 | ret = read_build_id(buf, buf_size, bid, need_swap); |
214 | if (ret == 0) { |
215 | ret = bid->size; |
216 | break; |
217 | } |
218 | } |
219 | } |
220 | out_free: |
221 | free(buf); |
222 | out: |
223 | fclose(fp); |
224 | return ret; |
225 | } |
226 | |
227 | int sysfs__read_build_id(const char *filename, struct build_id *bid) |
228 | { |
229 | int fd; |
230 | int ret = -1; |
231 | struct stat stbuf; |
232 | size_t buf_size; |
233 | void *buf; |
234 | |
235 | fd = open(filename, O_RDONLY); |
236 | if (fd < 0) |
237 | return -1; |
238 | |
239 | if (fstat(fd, &stbuf) < 0) |
240 | goto out; |
241 | |
242 | buf_size = stbuf.st_size; |
243 | buf = malloc(buf_size); |
244 | if (buf == NULL) |
245 | goto out; |
246 | |
247 | if (read(fd, buf, buf_size) != (ssize_t) buf_size) |
248 | goto out_free; |
249 | |
250 | ret = read_build_id(note_data: buf, note_len: buf_size, bid, need_swap: false); |
251 | out_free: |
252 | free(buf); |
253 | out: |
254 | close(fd); |
255 | return ret; |
256 | } |
257 | |
258 | int symsrc__init(struct symsrc *ss, struct dso *dso, const char *name, |
259 | enum dso_binary_type type) |
260 | { |
261 | int fd = open(name, O_RDONLY); |
262 | if (fd < 0) |
263 | goto out_errno; |
264 | |
265 | ss->name = strdup(name); |
266 | if (!ss->name) |
267 | goto out_close; |
268 | |
269 | ss->fd = fd; |
270 | ss->type = type; |
271 | |
272 | return 0; |
273 | out_close: |
274 | close(fd); |
275 | out_errno: |
276 | dso->load_errno = errno; |
277 | return -1; |
278 | } |
279 | |
280 | bool symsrc__possibly_runtime(struct symsrc *ss __maybe_unused) |
281 | { |
282 | /* Assume all sym sources could be a runtime image. */ |
283 | return true; |
284 | } |
285 | |
286 | bool symsrc__has_symtab(struct symsrc *ss __maybe_unused) |
287 | { |
288 | return false; |
289 | } |
290 | |
291 | void symsrc__destroy(struct symsrc *ss) |
292 | { |
293 | zfree(&ss->name); |
294 | close(ss->fd); |
295 | } |
296 | |
297 | int dso__synthesize_plt_symbols(struct dso *dso __maybe_unused, |
298 | struct symsrc *ss __maybe_unused) |
299 | { |
300 | return 0; |
301 | } |
302 | |
303 | static int fd__is_64_bit(int fd) |
304 | { |
305 | u8 e_ident[EI_NIDENT]; |
306 | |
307 | if (lseek(fd, 0, SEEK_SET)) |
308 | return -1; |
309 | |
310 | if (readn(fd, e_ident, sizeof(e_ident)) != sizeof(e_ident)) |
311 | return -1; |
312 | |
313 | if (memcmp(e_ident, ELFMAG, SELFMAG) || |
314 | e_ident[EI_VERSION] != EV_CURRENT) |
315 | return -1; |
316 | |
317 | return e_ident[EI_CLASS] == ELFCLASS64; |
318 | } |
319 | |
320 | enum dso_type dso__type_fd(int fd) |
321 | { |
322 | Elf64_Ehdr ehdr; |
323 | int ret; |
324 | |
325 | ret = fd__is_64_bit(fd); |
326 | if (ret < 0) |
327 | return DSO__TYPE_UNKNOWN; |
328 | |
329 | if (ret) |
330 | return DSO__TYPE_64BIT; |
331 | |
332 | if (readn(fd, &ehdr, sizeof(ehdr)) != sizeof(ehdr)) |
333 | return DSO__TYPE_UNKNOWN; |
334 | |
335 | if (ehdr.e_machine == EM_X86_64) |
336 | return DSO__TYPE_X32BIT; |
337 | |
338 | return DSO__TYPE_32BIT; |
339 | } |
340 | |
341 | int dso__load_sym(struct dso *dso, struct map *map __maybe_unused, |
342 | struct symsrc *ss, |
343 | struct symsrc *runtime_ss __maybe_unused, |
344 | int kmodule __maybe_unused) |
345 | { |
346 | struct build_id bid; |
347 | int ret; |
348 | |
349 | ret = fd__is_64_bit(fd: ss->fd); |
350 | if (ret >= 0) |
351 | dso->is_64_bit = ret; |
352 | |
353 | if (filename__read_build_id(filename: ss->name, bid: &bid) > 0) |
354 | dso__set_build_id(dso, bid: &bid); |
355 | return 0; |
356 | } |
357 | |
358 | int file__read_maps(int fd __maybe_unused, bool exe __maybe_unused, |
359 | mapfn_t mapfn __maybe_unused, void *data __maybe_unused, |
360 | bool *is_64_bit __maybe_unused) |
361 | { |
362 | return -1; |
363 | } |
364 | |
365 | int (struct kcore_extract *kce __maybe_unused) |
366 | { |
367 | return -1; |
368 | } |
369 | |
370 | void (struct kcore_extract *kce __maybe_unused) |
371 | { |
372 | } |
373 | |
374 | int kcore_copy(const char *from_dir __maybe_unused, |
375 | const char *to_dir __maybe_unused) |
376 | { |
377 | return -1; |
378 | } |
379 | |
380 | void symbol__elf_init(void) |
381 | { |
382 | } |
383 | |
384 | char *dso__demangle_sym(struct dso *dso __maybe_unused, |
385 | int kmodule __maybe_unused, |
386 | const char *elf_name __maybe_unused) |
387 | { |
388 | return NULL; |
389 | } |
390 | |
391 | bool filename__has_section(const char *filename __maybe_unused, const char *sec __maybe_unused) |
392 | { |
393 | return false; |
394 | } |
395 | |