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