1/* libunwind - a platform-independent unwind library
2
3This file is part of libunwind.
4
5Permission is hereby granted, free of charge, to any person obtaining
6a copy of this software and associated documentation files (the
7"Software"), to deal in the Software without restriction, including
8without limitation the rights to use, copy, modify, merge, publish,
9distribute, sublicense, and/or sell copies of the Software, and to
10permit persons to whom the Software is furnished to do so, subject to
11the following conditions:
12
13The above copyright notice and this permission notice shall be
14included in all copies or substantial portions of the Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22WITH 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
75struct UCD_info *
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
286int _UCD_get_num_threads(struct UCD_info *ui)
287{
288  return ui->n_threads;
289}
290
291void _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
297pid_t _UCD_get_pid(struct UCD_info *ui)
298{
299  return ui->prstatus->pr_pid;
300}
301
302int _UCD_get_cursig(struct UCD_info *ui)
303{
304  return ui->prstatus->pr_cursig;
305}
306
307int _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
403int _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