1 /* libunwind - a platform-independent unwind library
2
3 This file is part of libunwind.
4
5 Permission is hereby granted, free of charge, to any person obtaining
6 a copy of this software and associated documentation files (the
7 "Software"), to deal in the Software without restriction, including
8 without limitation the rights to use, copy, modify, merge, publish,
9 distribute, sublicense, and/or sell copies of the Software, and to
10 permit persons to whom the Software is furnished to do so, subject to
11 the following conditions:
12
13 The above copyright notice and this permission notice shall be
14 included in all copies or substantial portions of the Software.
15
16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
23
24 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #endif
27
28 /* Endian detection */
29 #include <limits.h>
30 #if defined(HAVE_BYTESWAP_H)
31 #include <byteswap.h>
32 #endif
33
34 #if defined(HAVE_ELF_H)
35 # include <elf.h>
36 #elif defined(HAVE_SYS_ELF_H)
37 # include <sys/elf.h>
38 #endif
39 #include <sys/procfs.h> /* struct elf_prstatus */
40
41 #include "_UCD_lib.h"
42 #include "_UCD_internal.h"
43
44
45 struct UCD_info *
_UCD_create(const char * filename)46 _UCD_create(const char *filename)
47 {
48 union
49 {
50 Elf32_Ehdr h32;
51 Elf64_Ehdr h64;
52 } elf_header;
53 #define elf_header32 elf_header.h32
54 #define elf_header64 elf_header.h64
55 bool _64bits;
56
57 struct UCD_info *ui = memset(malloc(sizeof(*ui)), 0, sizeof(*ui));
58 ui->edi.di_cache.format = -1;
59 ui->edi.di_debug.format = -1;
60 #if UNW_TARGET_IA64
61 ui->edi.ktab.format = -1;
62 #endif
63
64 int fd = ui->coredump_fd = open(filename, O_RDONLY);
65 if (fd < 0)
66 goto err;
67 ui->coredump_filename = strdup(filename);
68
69 /* No sane ELF32 file is going to be smaller then ELF64 _header_,
70 * so let's just read 64-bit sized one.
71 */
72 if (read(fd, &elf_header64, sizeof(elf_header64)) != sizeof(elf_header64))
73 {
74 Debug(0, "'%s' is not an ELF file\n", filename);
75 goto err;
76 }
77
78 if (memcmp(&elf_header32, ELFMAG, SELFMAG) != 0)
79 {
80 Debug(0, "'%s' is not an ELF file\n", filename);
81 goto err;
82 }
83
84 if (elf_header32.e_ident[EI_CLASS] != ELFCLASS32
85 && elf_header32.e_ident[EI_CLASS] != ELFCLASS64)
86 {
87 Debug(0, "'%s' is not a 32/64 bit ELF file\n", filename);
88 goto err;
89 }
90
91 if (target_is_big_endian() && (elf_header32.e_ident[EI_DATA] == ELFDATA2LSB))
92 {
93 Debug(0, "'%s' is endian-incompatible\n", filename);
94 goto err;
95 }
96
97 _64bits = (elf_header32.e_ident[EI_CLASS] == ELFCLASS64);
98 if (_64bits && sizeof(elf_header64.e_entry) > sizeof(off_t))
99 {
100 Debug(0, "Can't process '%s': 64-bit file "
101 "while only %ld bits are supported",
102 filename, 8L * sizeof(off_t));
103 goto err;
104 }
105
106 /* paranoia check */
107 if (_64bits
108 ? 0 /* todo: (elf_header64.e_ehsize != NN || elf_header64.e_phentsize != NN) */
109 : (elf_header32.e_ehsize != 52 || elf_header32.e_phentsize != 32)
110 )
111 {
112 Debug(0, "'%s' has wrong e_ehsize or e_phentsize\n", filename);
113 goto err;
114 }
115
116 off_t ofs = (_64bits ? elf_header64.e_phoff : elf_header32.e_phoff);
117 if (lseek(fd, ofs, SEEK_SET) != ofs)
118 {
119 Debug(0, "Can't read phdrs from '%s'\n", filename);
120 goto err;
121 }
122 unsigned size = ui->phdrs_count = (_64bits ? elf_header64.e_phnum : elf_header32.e_phnum);
123 coredump_phdr_t *phdrs = ui->phdrs = memset(malloc(size * sizeof(phdrs[0])), 0, size * sizeof(phdrs[0]));
124 if (_64bits)
125 {
126 coredump_phdr_t *cur = phdrs;
127 unsigned i = 0;
128 while (i < size)
129 {
130 Elf64_Phdr hdr64;
131 if (read(fd, &hdr64, sizeof(hdr64)) != sizeof(hdr64))
132 {
133 Debug(0, "Can't read phdrs from '%s'\n", filename);
134 goto err;
135 }
136 cur->p_type = hdr64.p_type ;
137 cur->p_flags = hdr64.p_flags ;
138 cur->p_offset = hdr64.p_offset;
139 cur->p_vaddr = hdr64.p_vaddr ;
140 /*cur->p_paddr = hdr32.p_paddr ; always 0 */
141 //TODO: check that and abort if it isn't?
142 cur->p_filesz = hdr64.p_filesz;
143 cur->p_memsz = hdr64.p_memsz ;
144 cur->p_align = hdr64.p_align ;
145 /* cur->backing_filename = NULL; - done by memset */
146 cur->backing_fd = -1;
147 cur->backing_filesize = hdr64.p_filesz;
148 i++;
149 cur++;
150 }
151 } else {
152 coredump_phdr_t *cur = phdrs;
153 unsigned i = 0;
154 while (i < size)
155 {
156 Elf32_Phdr hdr32;
157 if (read(fd, &hdr32, sizeof(hdr32)) != sizeof(hdr32))
158 {
159 Debug(0, "Can't read phdrs from '%s'\n", filename);
160 goto err;
161 }
162 cur->p_type = hdr32.p_type ;
163 cur->p_flags = hdr32.p_flags ;
164 cur->p_offset = hdr32.p_offset;
165 cur->p_vaddr = hdr32.p_vaddr ;
166 /*cur->p_paddr = hdr32.p_paddr ; always 0 */
167 cur->p_filesz = hdr32.p_filesz;
168 cur->p_memsz = hdr32.p_memsz ;
169 cur->p_align = hdr32.p_align ;
170 /* cur->backing_filename = NULL; - done by memset */
171 cur->backing_fd = -1;
172 cur->backing_filesize = hdr32.p_memsz;
173 i++;
174 cur++;
175 }
176 }
177
178 int ret = _UCD_get_threadinfo(ui, phdrs, size);
179 if (ret != UNW_ESUCCESS) {
180 Debug(0, "failure retrieving thread info from core file\n");
181 goto err;
182 }
183
184 ret = _UCD_get_mapinfo(ui, phdrs, size);
185 if (ret != UNW_ESUCCESS) {
186 Debug(0, "failure retrieving file mapping from core file\n");
187 goto err;
188 }
189
190 coredump_phdr_t *cur = phdrs;
191 for (unsigned i = 0; i < size; ++i)
192 {
193 if (cur->p_type == PT_LOAD)
194 {
195 Debug(2, " ofs:%08llx va:%08llx filesize:%08llx memsize:%08llx flg:%x",
196 (unsigned long long) cur->p_offset,
197 (unsigned long long) cur->p_vaddr,
198 (unsigned long long) cur->p_filesz,
199 (unsigned long long) cur->p_memsz,
200 cur->p_flags
201 );
202 if (cur->p_filesz < cur->p_memsz)
203 {
204 Debug(2, " partial");
205 }
206 if (cur->p_flags & PF_X)
207 {
208 Debug(2, " executable");
209 }
210 }
211 Debug(2, "\n");
212 cur++;
213 }
214
215 if (ui->n_threads == 0)
216 {
217 Debug(0, "No NT_PRSTATUS note found in '%s'\n", filename);
218 goto err;
219 }
220
221 ui->prstatus = &ui->threads[0];
222
223 return ui;
224
225 err:
226 _UCD_destroy(ui);
227 return NULL;
228 }
229
_UCD_get_num_threads(struct UCD_info * ui)230 int _UCD_get_num_threads(struct UCD_info *ui)
231 {
232 return ui->n_threads;
233 }
234
_UCD_select_thread(struct UCD_info * ui,int n)235 void _UCD_select_thread(struct UCD_info *ui, int n)
236 {
237 if (n >= 0 && n < ui->n_threads)
238 ui->prstatus = &ui->threads[n];
239 }
240
_UCD_get_pid(struct UCD_info * ui)241 pid_t _UCD_get_pid(struct UCD_info *ui)
242 {
243 return ui->prstatus->pr_pid;
244 }
245
_UCD_get_cursig(struct UCD_info * ui)246 int _UCD_get_cursig(struct UCD_info *ui)
247 {
248 return ui->prstatus->pr_cursig;
249 }
250
_UCD_add_backing_file_at_segment(struct UCD_info * ui,int phdr_no,const char * filename)251 int _UCD_add_backing_file_at_segment(struct UCD_info *ui, int phdr_no, const char *filename)
252 {
253 if ((unsigned)phdr_no >= ui->phdrs_count)
254 {
255 Debug(0, "There is no segment %d in this coredump\n", phdr_no);
256 return -1;
257 }
258
259 struct coredump_phdr *phdr = &ui->phdrs[phdr_no];
260 if (phdr->backing_filename)
261 {
262 Debug(0, "Backing file already added to segment %d\n", phdr_no);
263 return -1;
264 }
265
266 int fd = open(filename, O_RDONLY);
267 if (fd < 0)
268 {
269 Debug(0, "Can't open '%s'\n", filename);
270 return -1;
271 }
272
273 phdr->backing_fd = fd;
274 phdr->backing_filename = strdup(filename);
275
276 struct stat statbuf;
277 if (fstat(fd, &statbuf) != 0)
278 {
279 Debug(0, "Can't stat '%s'\n", filename);
280 goto err;
281 }
282 phdr->backing_filesize = (uoff_t)statbuf.st_size;
283
284 if (phdr->p_flags != (PF_X | PF_R))
285 {
286 Debug(1, "Note: phdr[%u] is not r-x: flags are 0x%x\n",
287 phdr_no, phdr->p_flags);
288 }
289
290 if (phdr->backing_filesize > phdr->p_memsz)
291 {
292 /* This is expected */
293 Debug(2, "Note: phdr[%u] is %lld bytes, file is larger: %lld bytes\n",
294 phdr_no,
295 (unsigned long long)phdr->p_memsz,
296 (unsigned long long)phdr->backing_filesize
297 );
298 }
299 //TODO: else loudly complain? Maybe even fail?
300
301 /* Success */
302 return 0;
303
304 err:
305 if (phdr->backing_fd >= 0)
306 {
307 close(phdr->backing_fd);
308 phdr->backing_fd = -1;
309 }
310 free(phdr->backing_filename);
311 phdr->backing_filename = NULL;
312 return -1;
313 }
314
_UCD_add_backing_file_at_vaddr(struct UCD_info * ui,unsigned long vaddr,const char * filename)315 int _UCD_add_backing_file_at_vaddr(struct UCD_info *ui,
316 unsigned long vaddr,
317 const char *filename)
318 {
319 unsigned i;
320 for (i = 0; i < ui->phdrs_count; i++)
321 {
322 struct coredump_phdr *phdr = &ui->phdrs[i];
323 if (phdr->p_vaddr != vaddr)
324 continue;
325 /* It seems to match. Add it. */
326 return _UCD_add_backing_file_at_segment(ui, i, filename);
327 }
328 return -1;
329 }
330