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