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 #if defined(HAVE_ENDIAN_H)
34 # include <endian.h>
35 #elif defined(HAVE_SYS_ENDIAN_H)
36 # include <sys/endian.h>
37 #endif
38 #if defined(__BYTE_ORDER) && __BYTE_ORDER == __BIG_ENDIAN
39 # define WE_ARE_BIG_ENDIAN 1
40 # define WE_ARE_LITTLE_ENDIAN 0
41 #elif defined(__BYTE_ORDER) && __BYTE_ORDER == __LITTLE_ENDIAN
42 # define WE_ARE_BIG_ENDIAN 0
43 # define WE_ARE_LITTLE_ENDIAN 1
44 #elif defined(_BYTE_ORDER) && _BYTE_ORDER == _BIG_ENDIAN
45 # define WE_ARE_BIG_ENDIAN 1
46 # define WE_ARE_LITTLE_ENDIAN 0
47 #elif defined(_BYTE_ORDER) && _BYTE_ORDER == _LITTLE_ENDIAN
48 # define WE_ARE_BIG_ENDIAN 0
49 # define WE_ARE_LITTLE_ENDIAN 1
50 #elif defined(BYTE_ORDER) && BYTE_ORDER == BIG_ENDIAN
51 # define WE_ARE_BIG_ENDIAN 1
52 # define WE_ARE_LITTLE_ENDIAN 0
53 #elif defined(BYTE_ORDER) && BYTE_ORDER == LITTLE_ENDIAN
54 # define WE_ARE_BIG_ENDIAN 0
55 # define WE_ARE_LITTLE_ENDIAN 1
56 #elif defined(__386__)
57 # define WE_ARE_BIG_ENDIAN 0
58 # define WE_ARE_LITTLE_ENDIAN 1
59 #else
60 # error "Can't determine endianness"
61 #endif
62
63 #include <elf.h>
64 #include <sys/procfs.h> /* struct elf_prstatus */
65
66 #include "_UCD_lib.h"
67 #include "_UCD_internal.h"
68
69 #define NOTE_DATA(_hdr) STRUCT_MEMBER_P((_hdr), sizeof (Elf32_Nhdr) + UNW_ALIGN((_hdr)->n_namesz, 4))
70 #define NOTE_SIZE(_hdr) (sizeof (Elf32_Nhdr) + UNW_ALIGN((_hdr)->n_namesz, 4) + (_hdr)->n_descsz)
71 #define NOTE_NEXT(_hdr) STRUCT_MEMBER_P((_hdr), NOTE_SIZE(_hdr))
72 #define NOTE_FITS_IN(_hdr, _size) ((_size) >= sizeof (Elf32_Nhdr) && (_size) >= NOTE_SIZE (_hdr))
73 #define NOTE_FITS(_hdr, _end) NOTE_FITS_IN((_hdr), (unsigned long)((char *)(_end) - (char *)(_hdr)))
74
75 struct UCD_info *
_UCD_create(const char * filename)76 _UCD_create(const char *filename)
77 {
78 union
79 {
80 Elf32_Ehdr h32;
81 Elf64_Ehdr h64;
82 } elf_header;
83 #define elf_header32 elf_header.h32
84 #define elf_header64 elf_header.h64
85 bool _64bits;
86
87 struct UCD_info *ui = memset(malloc(sizeof(*ui)), 0, sizeof(*ui));
88 ui->edi.di_cache.format = -1;
89 ui->edi.di_debug.format = -1;
90 #if UNW_TARGET_IA64
91 ui->edi.ktab.format = -1;
92 #endif
93
94 int fd = ui->coredump_fd = open(filename, O_RDONLY);
95 if (fd < 0)
96 goto err;
97 ui->coredump_filename = strdup(filename);
98
99 /* No sane ELF32 file is going to be smaller then ELF64 _header_,
100 * so let's just read 64-bit sized one.
101 */
102 if (read(fd, &elf_header64, sizeof(elf_header64)) != sizeof(elf_header64))
103 {
104 Debug(0, "'%s' is not an ELF file\n", filename);
105 goto err;
106 }
107
108 if (memcmp(&elf_header32, ELFMAG, SELFMAG) != 0)
109 {
110 Debug(0, "'%s' is not an ELF file\n", filename);
111 goto err;
112 }
113
114 if (elf_header32.e_ident[EI_CLASS] != ELFCLASS32
115 && elf_header32.e_ident[EI_CLASS] != ELFCLASS64)
116 {
117 Debug(0, "'%s' is not a 32/64 bit ELF file\n", filename);
118 goto err;
119 }
120
121 if (WE_ARE_LITTLE_ENDIAN != (elf_header32.e_ident[EI_DATA] == ELFDATA2LSB))
122 {
123 Debug(0, "'%s' is endian-incompatible\n", filename);
124 goto err;
125 }
126
127 _64bits = (elf_header32.e_ident[EI_CLASS] == ELFCLASS64);
128 if (_64bits && sizeof(elf_header64.e_entry) > sizeof(off_t))
129 {
130 Debug(0, "Can't process '%s': 64-bit file "
131 "while only %ld bits are supported",
132 filename, 8L * sizeof(off_t));
133 goto err;
134 }
135
136 /* paranoia check */
137 if (_64bits
138 ? 0 /* todo: (elf_header64.e_ehsize != NN || elf_header64.e_phentsize != NN) */
139 : (elf_header32.e_ehsize != 52 || elf_header32.e_phentsize != 32)
140 )
141 {
142 Debug(0, "'%s' has wrong e_ehsize or e_phentsize\n", filename);
143 goto err;
144 }
145
146 off_t ofs = (_64bits ? elf_header64.e_phoff : elf_header32.e_phoff);
147 if (lseek(fd, ofs, SEEK_SET) != ofs)
148 {
149 Debug(0, "Can't read phdrs from '%s'\n", filename);
150 goto err;
151 }
152 unsigned size = ui->phdrs_count = (_64bits ? elf_header64.e_phnum : elf_header32.e_phnum);
153 coredump_phdr_t *phdrs = ui->phdrs = memset(malloc(size * sizeof(phdrs[0])), 0, size * sizeof(phdrs[0]));
154 if (_64bits)
155 {
156 coredump_phdr_t *cur = phdrs;
157 unsigned i = 0;
158 while (i < size)
159 {
160 Elf64_Phdr hdr64;
161 if (read(fd, &hdr64, sizeof(hdr64)) != sizeof(hdr64))
162 {
163 Debug(0, "Can't read phdrs from '%s'\n", filename);
164 goto err;
165 }
166 cur->p_type = hdr64.p_type ;
167 cur->p_flags = hdr64.p_flags ;
168 cur->p_offset = hdr64.p_offset;
169 cur->p_vaddr = hdr64.p_vaddr ;
170 /*cur->p_paddr = hdr32.p_paddr ; always 0 */
171 //TODO: check that and abort if it isn't?
172 cur->p_filesz = hdr64.p_filesz;
173 cur->p_memsz = hdr64.p_memsz ;
174 cur->p_align = hdr64.p_align ;
175 /* cur->backing_filename = NULL; - done by memset */
176 cur->backing_fd = -1;
177 cur->backing_filesize = hdr64.p_filesz;
178 i++;
179 cur++;
180 }
181 } else {
182 coredump_phdr_t *cur = phdrs;
183 unsigned i = 0;
184 while (i < size)
185 {
186 Elf32_Phdr hdr32;
187 if (read(fd, &hdr32, sizeof(hdr32)) != sizeof(hdr32))
188 {
189 Debug(0, "Can't read phdrs from '%s'\n", filename);
190 goto err;
191 }
192 cur->p_type = hdr32.p_type ;
193 cur->p_flags = hdr32.p_flags ;
194 cur->p_offset = hdr32.p_offset;
195 cur->p_vaddr = hdr32.p_vaddr ;
196 /*cur->p_paddr = hdr32.p_paddr ; always 0 */
197 cur->p_filesz = hdr32.p_filesz;
198 cur->p_memsz = hdr32.p_memsz ;
199 cur->p_align = hdr32.p_align ;
200 /* cur->backing_filename = NULL; - done by memset */
201 cur->backing_fd = -1;
202 cur->backing_filesize = hdr32.p_memsz;
203 i++;
204 cur++;
205 }
206 }
207
208 unsigned i = 0;
209 coredump_phdr_t *cur = phdrs;
210 while (i < size)
211 {
212 Debug(2, "phdr[%03d]: type:%d", i, cur->p_type);
213 if (cur->p_type == PT_NOTE)
214 {
215 Elf32_Nhdr *note_hdr, *note_end;
216 unsigned n_threads;
217
218 ui->note_phdr = malloc(cur->p_filesz);
219 if (lseek(fd, cur->p_offset, SEEK_SET) != (off_t)cur->p_offset
220 || (uoff_t)read(fd, ui->note_phdr, cur->p_filesz) != cur->p_filesz)
221 {
222 Debug(0, "Can't read PT_NOTE from '%s'\n", filename);
223 goto err;
224 }
225
226 note_end = STRUCT_MEMBER_P (ui->note_phdr, cur->p_filesz);
227
228 /* Count number of threads */
229 n_threads = 0;
230 note_hdr = (Elf32_Nhdr *)ui->note_phdr;
231 while (NOTE_FITS (note_hdr, note_end))
232 {
233 if (note_hdr->n_type == NT_PRSTATUS)
234 n_threads++;
235
236 note_hdr = NOTE_NEXT (note_hdr);
237 }
238
239 ui->n_threads = n_threads;
240 ui->threads = malloc(sizeof (void *) * n_threads);
241
242 n_threads = 0;
243 note_hdr = (Elf32_Nhdr *)ui->note_phdr;
244 while (NOTE_FITS (note_hdr, note_end))
245 {
246 if (note_hdr->n_type == NT_PRSTATUS)
247 ui->threads[n_threads++] = NOTE_DATA (note_hdr);
248
249 note_hdr = NOTE_NEXT (note_hdr);
250 }
251 }
252 if (cur->p_type == PT_LOAD)
253 {
254 Debug(2, " ofs:%08llx va:%08llx filesize:%08llx memsize:%08llx flg:%x",
255 (unsigned long long) cur->p_offset,
256 (unsigned long long) cur->p_vaddr,
257 (unsigned long long) cur->p_filesz,
258 (unsigned long long) cur->p_memsz,
259 cur->p_flags
260 );
261 if (cur->p_filesz < cur->p_memsz)
262 Debug(2, " partial");
263 if (cur->p_flags & PF_X)
264 Debug(2, " executable");
265 }
266 Debug(2, "\n");
267 i++;
268 cur++;
269 }
270
271 if (ui->n_threads == 0)
272 {
273 Debug(0, "No NT_PRSTATUS note found in '%s'\n", filename);
274 goto err;
275 }
276
277 ui->prstatus = ui->threads[0];
278
279 return ui;
280
281 err:
282 _UCD_destroy(ui);
283 return NULL;
284 }
285
_UCD_get_num_threads(struct UCD_info * ui)286 int _UCD_get_num_threads(struct UCD_info *ui)
287 {
288 return ui->n_threads;
289 }
290
_UCD_select_thread(struct UCD_info * ui,int n)291 void _UCD_select_thread(struct UCD_info *ui, int n)
292 {
293 if (n >= 0 && n < ui->n_threads)
294 ui->prstatus = ui->threads[n];
295 }
296
_UCD_get_pid(struct UCD_info * ui)297 pid_t _UCD_get_pid(struct UCD_info *ui)
298 {
299 return ui->prstatus->pr_pid;
300 }
301
_UCD_get_cursig(struct UCD_info * ui)302 int _UCD_get_cursig(struct UCD_info *ui)
303 {
304 return ui->prstatus->pr_cursig;
305 }
306
_UCD_add_backing_file_at_segment(struct UCD_info * ui,int phdr_no,const char * filename)307 int _UCD_add_backing_file_at_segment(struct UCD_info *ui, int phdr_no, const char *filename)
308 {
309 if ((unsigned)phdr_no >= ui->phdrs_count)
310 {
311 Debug(0, "There is no segment %d in this coredump\n", phdr_no);
312 return -1;
313 }
314
315 struct coredump_phdr *phdr = &ui->phdrs[phdr_no];
316 if (phdr->backing_filename)
317 {
318 Debug(0, "Backing file already added to segment %d\n", phdr_no);
319 return -1;
320 }
321
322 int fd = open(filename, O_RDONLY);
323 if (fd < 0)
324 {
325 Debug(0, "Can't open '%s'\n", filename);
326 return -1;
327 }
328
329 phdr->backing_fd = fd;
330 phdr->backing_filename = strdup(filename);
331
332 struct stat statbuf;
333 if (fstat(fd, &statbuf) != 0)
334 {
335 Debug(0, "Can't stat '%s'\n", filename);
336 goto err;
337 }
338 phdr->backing_filesize = (uoff_t)statbuf.st_size;
339
340 if (phdr->p_flags != (PF_X | PF_R))
341 Debug(1, "Note: phdr[%u] is not r-x: flags are 0x%x\n", phdr_no, phdr->p_flags);
342
343 if (phdr->backing_filesize > phdr->p_memsz)
344 {
345 /* This is expected */
346 Debug(2, "Note: phdr[%u] is %lld bytes, file is larger: %lld bytes\n",
347 phdr_no,
348 (unsigned long long)phdr->p_memsz,
349 (unsigned long long)phdr->backing_filesize
350 );
351 }
352 //TODO: else loudly complain? Maybe even fail?
353
354 if (phdr->p_filesz != 0)
355 {
356 //TODO: loop and compare in smaller blocks
357 char *core_buf = malloc(phdr->p_filesz);
358 char *file_buf = malloc(phdr->p_filesz);
359 if (lseek(ui->coredump_fd, phdr->p_offset, SEEK_SET) != (off_t)phdr->p_offset
360 || (uoff_t)read(ui->coredump_fd, core_buf, phdr->p_filesz) != phdr->p_filesz
361 )
362 {
363 Debug(0, "Error reading from coredump file\n");
364 err_read:
365 free(core_buf);
366 free(file_buf);
367 goto err;
368 }
369 if ((uoff_t)read(fd, file_buf, phdr->p_filesz) != phdr->p_filesz)
370 {
371 Debug(0, "Error reading from '%s'\n", filename);
372 goto err_read;
373 }
374 int r = memcmp(core_buf, file_buf, phdr->p_filesz);
375 free(core_buf);
376 free(file_buf);
377 if (r != 0)
378 {
379 Debug(1, "Note: phdr[%u] first %lld bytes in core dump and in file do not match\n",
380 phdr_no, (unsigned long long)phdr->p_filesz
381 );
382 } else {
383 Debug(1, "Note: phdr[%u] first %lld bytes in core dump and in file match\n",
384 phdr_no, (unsigned long long)phdr->p_filesz
385 );
386 }
387 }
388
389 /* Success */
390 return 0;
391
392 err:
393 if (phdr->backing_fd >= 0)
394 {
395 close(phdr->backing_fd);
396 phdr->backing_fd = -1;
397 }
398 free(phdr->backing_filename);
399 phdr->backing_filename = NULL;
400 return -1;
401 }
402
_UCD_add_backing_file_at_vaddr(struct UCD_info * ui,unsigned long vaddr,const char * filename)403 int _UCD_add_backing_file_at_vaddr(struct UCD_info *ui,
404 unsigned long vaddr,
405 const char *filename)
406 {
407 unsigned i;
408 for (i = 0; i < ui->phdrs_count; i++)
409 {
410 struct coredump_phdr *phdr = &ui->phdrs[i];
411 if (phdr->p_vaddr != vaddr)
412 continue;
413 /* It seems to match. Add it. */
414 return _UCD_add_backing_file_at_segment(ui, i, filename);
415 }
416 return -1;
417 }
418