• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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