1 /**
2 * Support functions for ELF corefiles
3 */
4 /*
5 This file is part of libunwind.
6
7 Permission is hereby granted, free of charge, to any person obtaining a copy of
8 this software and associated documentation files (the "Software"), to deal in
9 the Software without restriction, including without limitation the rights to
10 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
11 of the Software, and to permit persons to whom the Software is furnished to do
12 so, subject to the following conditions:
13
14 The above copyright notice and this permission notice shall be included in all
15 copies or substantial portions of the Software.
16
17 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 SOFTWARE.
24 */
25 #include "_UCD_internal.h"
26
27 #include <errno.h>
28 #include <string.h>
29 #include <sys/types.h>
30 #include <unistd.h>
31
32
33 /**
34 * Read an ELF segment into an allocated memory buffer.
35 * @param[in] ui the unwind-coredump context
36 * @param[in] phdr pointer to the PHDR of the segment to load
37 * @param[out] segment pointer to the segment loaded
38 * @param[out] segment_size size of the @segment in bytes
39 *
40 * Allocates an appropriately-sized buffer to contain the segment of the
41 * coredump described by @phdr and reads it in from the core file.
42 *
43 * The caller is responsible for freeing the allocated segment memory.
44 *
45 * @returns UNW_SUCCESS on success, something else otherwise.
46 */
47 HIDDEN int
_UCD_elf_read_segment(struct UCD_info * ui,coredump_phdr_t * phdr,uint8_t ** segment,size_t * segment_size)48 _UCD_elf_read_segment(struct UCD_info *ui, coredump_phdr_t *phdr, uint8_t **segment, size_t *segment_size)
49 {
50 int ret = -UNW_EUNSPEC;
51 if (lseek(ui->coredump_fd, phdr->p_offset, SEEK_SET) != (off_t)phdr->p_offset)
52 {
53 Debug(0, "errno %d setting offset to %lu in '%s': %s\n",
54 errno, phdr->p_offset, ui->coredump_filename, strerror(errno));
55 return ret;
56 }
57
58 *segment_size = phdr->p_filesz;
59 *segment = malloc(*segment_size);
60 if (*segment == NULL)
61 {
62 Debug(0, "error %zu bytes of memory for segment\n", *segment_size);
63 return ret;
64 }
65
66 if (read(ui->coredump_fd, *segment, *segment_size) != (ssize_t)*segment_size)
67 {
68 Debug(0, "errno %d reading %zu bytes from '%s': %s\n",
69 errno, *segment_size, ui->coredump_filename, strerror(errno));
70 return ret;
71 }
72
73 ret = UNW_ESUCCESS;
74 return ret;
75 }
76
77
78 /**
79 * Parse a PT_NOTE segment into zero or more notes and visit each one
80 * @param[in] segment pointer to the PT_NOTE segment
81 * @param[in] segment_size size of @p segment in bytes
82 * @param[in] visit callback to process to the notes
83 * @param[in] arg context to forward to the callback
84 *
85 * One PT_NOTE segment might contain many variable-length notes. Parsing them
86 * out is just a matter of calculating the size of each note from the size
87 * fields contained in the (fixed-size) note header and adjusting for 4-byte
88 * alignment.
89 *
90 * For each note found the @p visit callback will be invoked. If the callback
91 * returns anything but UNW_ESUCCESS, traversal of the notes will be terminated
92 * and processing will return immediately, passing the return code through.
93 *
94 * @returns UNW_SUCCESS on success or the return value from @p visit otherwise.
95 */
96 HIDDEN int
_UCD_elf_visit_notes(uint8_t * segment,size_t segment_size,note_visitor_t visit,void * arg)97 _UCD_elf_visit_notes(uint8_t *segment, size_t segment_size, note_visitor_t visit, void *arg)
98 {
99 int ret = UNW_ESUCCESS;
100 size_t parsed_size = 0;
101 while (parsed_size < segment_size)
102 {
103 /*
104 * Note that Elf32_Nhdr and Elf64_Nhdr are identical, so it doesn't matter which
105 * structure is chosen here. I chose the one with the larger number because
106 * bigger is better.
107 */
108 Elf64_Nhdr *note = (Elf64_Nhdr *)(segment + parsed_size);
109 unsigned header_size = sizeof(Elf64_Nhdr);
110 unsigned name_size = UNW_ALIGN(note->n_namesz, 4);
111 unsigned desc_size = UNW_ALIGN(note->n_descsz, 4);
112 unsigned note_size = header_size + name_size + desc_size;
113 char *name = (char *)(note) + header_size;
114 uint8_t *desc = (uint8_t *)(note) + header_size + name_size;
115
116 ret = visit(note->n_namesz, note->n_descsz, note->n_type, name, desc, arg);
117 if (ret != UNW_ESUCCESS)
118 {
119 break;
120 }
121
122 parsed_size += note_size;
123 }
124 return ret;
125 }
126
127