baddr statedump: hold ust lock around allocations
[lttng-ust.git] / liblttng-ust / lttng-ust-elf.c
CommitLineData
8e2aed3f
AB
1/*
2 * Copyright (C) 2015 Antoine Busque <abusque@efficios.com>
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18
19#include <helper.h>
20#include <string.h>
21#include <lttng/align.h>
22#include <lttng/ust-elf.h>
23
24/*
25 * Retrieve the nth (where n is the `index` argument) phdr (program
26 * header) from the given elf instance.
27 *
28 * A pointer to the phdr is returned on success, NULL on failure.
29 */
30static
31struct lttng_ust_elf_phdr *lttng_ust_elf_get_phdr(struct lttng_ust_elf *elf,
32 uint16_t index)
33{
34 struct lttng_ust_elf_phdr *phdr = NULL;
35 long offset;
36
37 if (!elf) {
38 goto error;
39 }
40
41 if (index >= elf->ehdr->e_phnum) {
42 goto error;
43 }
44
45 phdr = zmalloc(sizeof(struct lttng_ust_elf_phdr));
46 if (!phdr) {
47 goto error;
48 }
49
50 offset = elf->ehdr->e_phoff + index * elf->ehdr->e_phentsize;
51 if (fseek(elf->file, offset, SEEK_SET)) {
52 goto error;
53 }
54
55 if (is_elf_32_bit(elf)) {
56 Elf32_Phdr elf_phdr;
57
58 if (!fread(&elf_phdr, sizeof(elf_phdr), 1, elf->file)) {
59 goto error;
60 }
61 if (!is_elf_native_endian(elf)) {
62 bswap_phdr(elf_phdr);
63 }
64 copy_phdr(elf_phdr, *phdr);
65 } else {
66 Elf64_Phdr elf_phdr;
67
68 if (!fread(&elf_phdr, sizeof(elf_phdr), 1, elf->file)) {
69 goto error;
70 }
71 if (!is_elf_native_endian(elf)) {
72 bswap_phdr(elf_phdr);
73 }
74 copy_phdr(elf_phdr, *phdr);
75 }
76
77 return phdr;
78
79error:
80 free(phdr);
81 return NULL;
82}
83
84/*
85 * Retrieve the nth (where n is the `index` argument) shdr (section
86 * header) from the given elf instance.
87 *
88 * A pointer to the shdr is returned on success, NULL on failure.
89 */
90static
91struct lttng_ust_elf_shdr *lttng_ust_elf_get_shdr(struct lttng_ust_elf *elf,
92 uint16_t index)
93{
94 struct lttng_ust_elf_shdr *shdr = NULL;
95 long offset;
96
97 if (!elf) {
98 goto error;
99 }
100
101 if (index >= elf->ehdr->e_shnum) {
102 goto error;
103 }
104
105 shdr = zmalloc(sizeof(struct lttng_ust_elf_shdr));
106 if (!shdr) {
107 goto error;
108 }
109
110 offset = elf->ehdr->e_shoff + index * elf->ehdr->e_shentsize;
111 if (fseek(elf->file, offset, SEEK_SET)) {
112 goto error;
113 }
114
115 if (is_elf_32_bit(elf)) {
116 Elf32_Shdr elf_shdr;
117
118 if (!fread(&elf_shdr, sizeof(elf_shdr), 1, elf->file)) {
119 goto error;
120 }
121 if (!is_elf_native_endian(elf)) {
122 bswap_shdr(elf_shdr);
123 }
124 copy_shdr(elf_shdr, *shdr);
125 } else {
126 Elf64_Shdr elf_shdr;
127
128 if (!fread(&elf_shdr, sizeof(elf_shdr), 1, elf->file)) {
129 goto error;
130 }
131 if (!is_elf_native_endian(elf)) {
132 bswap_shdr(elf_shdr);
133 }
134 copy_shdr(elf_shdr, *shdr);
135 }
136
137 return shdr;
138
139error:
140 free(shdr);
141 return NULL;
142}
143
144/*
145 * Lookup a section's name from a given offset (usually from an shdr's
146 * sh_name value) in bytes relative to the beginning of the section
147 * names string table.
148 *
149 * If no name is found, NULL is returned.
150 */
151static
152char *lttng_ust_elf_get_section_name(struct lttng_ust_elf *elf, uint32_t offset)
153{
154 char *name = NULL;
155 size_t len;
156
157 if (!elf) {
158 goto error;
159 }
160
161 if (offset >= elf->section_names_size) {
162 goto error;
163 }
164
165 if (fseek(elf->file, elf->section_names_offset + offset, SEEK_SET)) {
166 goto error;
167 }
168 /* Note that len starts at 1, it is not an index. */
169 for (len = 1; offset + len <= elf->section_names_size; ++len) {
170 switch (fgetc(elf->file)) {
171 case EOF:
172 goto error;
173 case '\0':
174 goto end;
175 default:
176 break;
177 }
178 }
179
180 /* No name was found before the end of the table. */
181 goto error;
182
183end:
184 name = zmalloc(sizeof(char) * len);
185 if (!name) {
186 goto error;
187 }
188 if (fseek(elf->file, elf->section_names_offset + offset,
189 SEEK_SET)) {
190 goto error;
191 }
192 if (!fgets(name, len, elf->file)) {
193 goto error;
194 }
195
196 return name;
197
198error:
199 free(name);
200 return NULL;
201}
202
203/*
204 * Create an instance of lttng_ust_elf for the ELF file located at
205 * `path`.
206 *
207 * Return a pointer to the instance on success, NULL on failure.
208 */
209struct lttng_ust_elf *lttng_ust_elf_create(const char *path)
210{
211 uint8_t e_ident[EI_NIDENT];
212 struct lttng_ust_elf_shdr *section_names_shdr;
213 struct lttng_ust_elf *elf;
214
215 elf = zmalloc(sizeof(struct lttng_ust_elf));
216 if (!elf) {
217 goto error;
218 }
219
220 elf->path = strdup(path);
221 if (!elf->path) {
222 goto error;
223 }
224
225 elf->file = fopen(elf->path, "rb");
226 if (!elf->file) {
227 goto error;
228 }
229
230 if (!fread(e_ident, 1, EI_NIDENT, elf->file)) {
231 goto error;
232 }
233 elf->bitness = e_ident[EI_CLASS];
234 elf->endianness = e_ident[EI_DATA];
235 rewind(elf->file);
236
237 elf->ehdr = zmalloc(sizeof(struct lttng_ust_elf_ehdr));
238 if (!elf->ehdr) {
239 goto error;
240 }
241
242 if (is_elf_32_bit(elf)) {
243 Elf32_Ehdr elf_ehdr;
244
245 if (!fread(&elf_ehdr, sizeof(elf_ehdr), 1, elf->file)) {
246 goto error;
247 }
248 if (!is_elf_native_endian(elf)) {
249 bswap_ehdr(elf_ehdr);
250 }
251 copy_ehdr(elf_ehdr, *(elf->ehdr));
252 } else {
253 Elf64_Ehdr elf_ehdr;
254
255 if (!fread(&elf_ehdr, sizeof(elf_ehdr), 1, elf->file)) {
256 goto error;
257 }
258 if (!is_elf_native_endian(elf)) {
259 bswap_ehdr(elf_ehdr);
260 }
261 copy_ehdr(elf_ehdr, *(elf->ehdr));
262 }
263
264 section_names_shdr = lttng_ust_elf_get_shdr(elf, elf->ehdr->e_shstrndx);
265 if (!section_names_shdr) {
266 goto error;
267 }
268
269 elf->section_names_offset = section_names_shdr->sh_offset;
270 elf->section_names_size = section_names_shdr->sh_size;
271
272 free(section_names_shdr);
273
274 return elf;
275
276error:
277 if (elf) {
278 free(elf->ehdr);
279 fclose(elf->file);
280 free(elf->path);
281 }
282 free(elf);
283 return NULL;
284}
285
286/*
287 * Destroy the given lttng_ust_elf instance.
288 */
289void lttng_ust_elf_destroy(struct lttng_ust_elf *elf)
290{
291 if (!elf) {
292 return;
293 }
294
295 free(elf->ehdr);
296 fclose(elf->file);
297 free(elf->path);
298 free(elf);
299}
300
301/*
302 * Compute the total in-memory size of the ELF file, in bytes.
303 *
304 * Returns 0 if successful, -1 if not. On success, the memory size is
305 * returned through the out parameter `memsz`.
306 */
307int lttng_ust_elf_get_memsz(struct lttng_ust_elf *elf, uint64_t *memsz)
308{
309 uint16_t i;
310 uint64_t _memsz = 0;
311
312 if (!elf || !memsz) {
313 goto error;
314 }
315
316 for (i = 0; i < elf->ehdr->e_phnum; ++i) {
317 struct lttng_ust_elf_phdr *phdr;
318 uint64_t align;
319
320 phdr = lttng_ust_elf_get_phdr(elf, i);
321 if (!phdr) {
322 goto error;
323 }
324
325 /*
326 * Only PT_LOAD segments contribute to memsz. Skip
327 * other segments.
328 */
329 if (phdr->p_type != PT_LOAD) {
330 goto next_loop;
331 }
332
333 /*
334 * A p_align of 0 means no alignment, i.e. aligned to
335 * 1 byte.
336 */
337 align = phdr->p_align == 0 ? 1 : phdr->p_align;
338 /* Align the start of the segment. */
339 _memsz += offset_align(_memsz, align);
340 _memsz += phdr->p_memsz;
341 /*
342 * Add padding at the end of the segment, so it ends
343 * on a multiple of the align value (which usually
344 * means a page boundary). This makes the computation
345 * valid even in cases where p_align would change from
346 * one segment to the next.
347 */
348 _memsz += offset_align(_memsz, align);
349 next_loop:
350 free(phdr);
351 }
352
353 *memsz = _memsz;
354 return 0;
355error:
356 return -1;
357}
358
359/*
360 * Internal method used to try and get the build_id from a PT_NOTE
361 * segment ranging from `offset` to `segment_end`.
362 *
363 * If the function returns successfully, the out parameter `found`
364 * indicates whether the build id information was present in the
365 * segment or not. If `found` is not 0, the out parameters `build_id`
366 * and `length` will both have been set with the retrieved
367 * information.
368 *
369 * Returns 0 on success, -1 if an error occurred.
370 */
371static
372int lttng_ust_elf_get_build_id_from_segment(
373 struct lttng_ust_elf *elf, uint8_t **build_id, size_t *length,
374 uint64_t offset, uint64_t segment_end, int *found)
375{
376 uint8_t *_build_id;
377 size_t _length;
378 int _found = 0;
379
380 while (offset < segment_end) {
381 struct lttng_ust_elf_nhdr nhdr;
382
383 /* Align start of note entry */
384 offset += offset_align(offset, ELF_NOTE_ENTRY_ALIGN);
385 if (offset >= segment_end) {
386 break;
387 }
388 /*
389 * We seek manually because if the note isn't the
390 * build id the data following the header will not
391 * have been read.
392 */
393 if (fseek(elf->file, offset, SEEK_SET)) {
394 goto error;
395 }
396 if (!fread(&nhdr, sizeof(nhdr), 1, elf->file)) {
397 goto error;
398 }
399
400 if (!is_elf_native_endian(elf)) {
401 nhdr.n_namesz = bswap_32(nhdr.n_namesz);
402 nhdr.n_descsz = bswap_32(nhdr.n_descsz);
403 nhdr.n_type = bswap_32(nhdr.n_type);
404 }
405
406 offset += sizeof(nhdr) + nhdr.n_namesz;
407 /* Align start of desc entry */
408 offset += offset_align(offset, ELF_NOTE_DESC_ALIGN);
409
410 if (nhdr.n_type != NT_GNU_BUILD_ID) {
411 /*
412 * Ignore non build id notes but still
413 * increase the offset.
414 */
415 offset += nhdr.n_descsz;
416 continue;
417 }
418
419 _length = nhdr.n_descsz;
420 _build_id = zmalloc(sizeof(uint8_t) * _length);
421 if (!build_id) {
422 goto error;
423 }
424
425 if (fseek(elf->file, offset, SEEK_SET)) {
426 goto error;
427 }
428 if (!fread(_build_id, sizeof(*_build_id), _length, elf->file)) {
429 goto error;
430 }
431
432 _found = 1;
433 break;
434 }
435
436 if (_found) {
437 *build_id = _build_id;
438 *length = _length;
439 }
440
441 *found = _found;
442 return 0;
443error:
444 return -1;
445}
446
447/*
448 * Retrieve a build ID (an array of bytes) from the corresponding
449 * section in the ELF file. The length of the build ID can be either
450 * 16 or 20 bytes depending on the method used to generate it, hence
451 * the length out parameter.
452 *
453 * If the function returns successfully, the out parameter `found`
454 * indicates whether the build id information was present in the ELF
455 * file or not. If `found` is not 0, the out parameters `build_id` and
456 * `length` will both have been set with the retrieved information.
457 *
458 * Returns 0 on success, -1 if an error occurred.
459 */
460int lttng_ust_elf_get_build_id(struct lttng_ust_elf *elf, uint8_t **build_id,
461 size_t *length, int *found)
462{
463 uint16_t i;
464 uint8_t *_build_id;
465 size_t _length;
466 int _found = 0;
467
468 if (!elf || !build_id || !length || !found) {
469 goto error;
470 }
471
472 for (i = 0; i < elf->ehdr->e_phnum; ++i) {
473 uint64_t offset, segment_end;
474 struct lttng_ust_elf_phdr *phdr;
475 int ret;
476
477 phdr = lttng_ust_elf_get_phdr(elf, i);
478 if (!phdr) {
479 goto error;
480 }
481
482 /* Build ID will be contained in a PT_NOTE segment. */
483 if (phdr->p_type != PT_NOTE) {
484 goto next_loop;
485 }
486
487 offset = phdr->p_offset;
488 segment_end = offset + phdr->p_filesz;
489 ret = lttng_ust_elf_get_build_id_from_segment(
490 elf, &_build_id, &_length, offset, segment_end,
491 &_found);
492 next_loop:
493 free(phdr);
494 if (ret) {
495 goto error;
496 }
497 if (_found) {
498 break;
499 }
500 }
501
502 if (_found) {
503 *build_id = _build_id;
504 *length = _length;
505 }
506
507 *found = _found;
508 return 0;
509error:
510 return -1;
511}
512
513/*
514 * Try to retrieve filename and CRC from given ELF section `shdr`.
515 *
516 * If the function returns successfully, the out parameter `found`
517 * indicates whether the debug link information was present in the ELF
518 * section or not. If `found` is not 0, the out parameters `filename` and
519 * `crc` will both have been set with the retrieved information.
520 *
521 * Returns 0 on success, -1 if an error occurred.
522 */
523static
524int lttng_ust_elf_get_debug_link_from_section(struct lttng_ust_elf *elf,
525 char **filename, uint32_t *crc,
526 int *found,
527 struct lttng_ust_elf_shdr *shdr)
528{
529 int _found = 0;
530 char *_filename;
531 char *section_name = NULL;
532 uint32_t _crc;
533
534 if (!elf || !filename || !crc || !found || !shdr) {
535 goto error;
536 }
537
538 /*
539 * The .gnu_debuglink section is of type SHT_PROGBITS,
540 * skip the other sections.
541 */
542 if (shdr->sh_type != SHT_PROGBITS) {
543 goto end;
544 }
545
546 section_name = lttng_ust_elf_get_section_name(elf,
547 shdr->sh_name);
548 if (!section_name) {
549 goto end;
550 }
551 if (strcmp(section_name, ".gnu_debuglink")) {
552 goto end;
553 }
554
555 /*
556 * The length of the filename is the sh_size excluding the CRC
557 * which comes after it in the section.
558 */
559 _filename = zmalloc(sizeof(char) * (shdr->sh_size - ELF_CRC_SIZE));
560 if (!_filename) {
561 goto error;
562 }
563 if (fseek(elf->file, shdr->sh_offset, SEEK_SET)) {
564 goto error;
565 }
566 if (!fread(_filename, sizeof(*_filename), shdr->sh_size - ELF_CRC_SIZE,
567 elf->file)) {
568 goto error;
569 }
570 if (!fread(&_crc, sizeof(_crc), 1, elf->file)) {
571 goto error;
572 }
573 if (!is_elf_native_endian(elf)) {
574 _crc = bswap_32(_crc);
575 }
576
577 _found = 1;
578
579end:
580 free(section_name);
581 if (_found) {
582 *filename = _filename;
583 *crc = _crc;
584 }
585 *found = _found;
586
587 return 0;
588
589error:
590 if (section_name) {
591 free(section_name);
592 }
593
594 return -1;
595}
596
597/*
598 * Retrieve filename and CRC from ELF's .gnu_debuglink section, if any.
599 *
600 * If the function returns successfully, the out parameter `found`
601 * indicates whether the debug link information was present in the ELF
602 * file or not. If `found` is not 0, the out parameters `filename` and
603 * `crc` will both have been set with the retrieved information.
604 *
605 * Returns 0 on success, -1 if an error occurred.
606 */
607int lttng_ust_elf_get_debug_link(struct lttng_ust_elf *elf, char **filename,
608 uint32_t *crc, int *found)
609{
610 int ret;
611 uint16_t i;
612 int _found = 0;
613 char *_filename;
614 uint32_t _crc;
615
616 if (!elf || !filename || !crc || !found) {
617 goto error;
618 }
619
620 for (i = 0; i < elf->ehdr->e_shnum; ++i) {
621 struct lttng_ust_elf_shdr *shdr = NULL;
622
623 shdr = lttng_ust_elf_get_shdr(elf, i);
624 if (!shdr) {
625 goto error;
626 }
627
628 ret = lttng_ust_elf_get_debug_link_from_section(
629 elf, &_filename, &_crc, &_found, shdr);
630 free(shdr);
631
632 if (ret) {
633 goto error;
634 }
635 if (_found) {
636 break;
637 }
638 }
639
640 if (_found) {
641 *filename = _filename;
642 *crc = _crc;
643 }
644
645 *found = _found;
646 return 0;
647error:
648 return -1;
649}
This page took 0.048077 seconds and 4 git commands to generate.