coredump-elf.c revision e808930793aeddc4cfd3e7a94b665913bec2566c
1 2/*--------------------------------------------------------------------*/ 3/*--- Dumping core. coredump-elf.c ---*/ 4/*--------------------------------------------------------------------*/ 5 6/* 7 This file is part of Valgrind, a dynamic binary instrumentation 8 framework. 9 10 Copyright (C) 2000-2006 Julian Seward 11 jseward@acm.org 12 13 This program is free software; you can redistribute it and/or 14 modify it under the terms of the GNU General Public License as 15 published by the Free Software Foundation; either version 2 of the 16 License, or (at your option) any later version. 17 18 This program is distributed in the hope that it will be useful, but 19 WITHOUT ANY WARRANTY; without even the implied warranty of 20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 21 General Public License for more details. 22 23 You should have received a copy of the GNU General Public License 24 along with this program; if not, write to the Free Software 25 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 26 02111-1307, USA. 27 28 The GNU General Public License is contained in the file COPYING. 29*/ 30 31#include "pub_core_basics.h" 32#include "pub_core_vki.h" 33#include "pub_core_aspacemgr.h" 34#include "pub_core_libcbase.h" 35#include "pub_core_machine.h" 36#include "pub_core_coredump.h" 37#include "pub_core_libcprint.h" 38#include "pub_core_libcfile.h" // VG_(close) et al 39#include "pub_core_libcproc.h" // VG_(geteuid), VG_(getegid) 40#include "pub_core_libcassert.h" // VG_(exit), vg_assert 41#include "pub_core_mallocfree.h" // VG_(malloc), VG_(free) 42#include "pub_core_threadstate.h" 43#include "pub_core_clientstate.h" 44#include "pub_core_options.h" 45 46#include "priv_elf.h" 47 48/* 49 Dump core 50 51 Generate a standard ELF core file corresponding to the client state 52 at the time of a crash. 53 */ 54#include <elf.h> 55#ifndef NT_PRXFPREG 56#define NT_PRXFPREG 0x46e62b7f /* copied from gdb5.1/include/elf/common.h */ 57#endif /* NT_PRXFPREG */ 58 59#if VG_WORDSIZE == 8 60#define ESZ(x) Elf64_##x 61#elif VG_WORDSIZE == 4 62#define ESZ(x) Elf32_##x 63#else 64#error VG_WORDSIZE needs to ==4 or ==8 65#endif 66 67/* TODO: GIVE THIS A PROPER HOME 68 TODO: MERGE THIS WITH DUPLICATES IN m_main.c and mc_leakcheck.c 69 Extract from aspacem a vector of the current segment start 70 addresses. The vector is dynamically allocated and should be freed 71 by the caller when done. REQUIRES m_mallocfree to be running. 72 Writes the number of addresses required into *n_acquired. */ 73 74static Addr* get_seg_starts ( /*OUT*/Int* n_acquired ) 75{ 76 Addr* starts; 77 Int n_starts, r = 0; 78 79 n_starts = 1; 80 while (True) { 81 starts = VG_(malloc)( n_starts * sizeof(Addr) ); 82 if (starts == NULL) 83 break; 84 r = VG_(am_get_segment_starts)( starts, n_starts ); 85 if (r >= 0) 86 break; 87 VG_(free)(starts); 88 n_starts *= 2; 89 } 90 91 if (starts == NULL) { 92 *n_acquired = 0; 93 return NULL; 94 } 95 96 *n_acquired = r; 97 return starts; 98} 99 100/* If true, then this Segment may be mentioned in the core */ 101static Bool may_dump(const NSegment *seg) 102{ 103 if (seg->kind == SkAnonC || 104 seg->kind == SkShmC || 105 (seg->kind == SkFileC && 106 !VKI_S_ISCHR(seg->mode) && !VKI_S_ISBLK(seg->mode))) 107 return True; 108 109 return False; 110} 111 112/* If true, then this Segment's contents will be in the core */ 113static Bool should_dump(const NSegment *seg) 114{ 115 return may_dump(seg); // && seg->hasW; 116} 117 118static void fill_ehdr(ESZ(Ehdr) *ehdr, Int num_phdrs) 119{ 120 VG_(memset)(ehdr, 0, sizeof(*ehdr)); 121 122 VG_(memcpy)(ehdr->e_ident, ELFMAG, SELFMAG); 123 ehdr->e_ident[EI_CLASS] = VG_ELF_CLASS; 124 ehdr->e_ident[EI_DATA] = VG_ELF_DATA2XXX; 125 ehdr->e_ident[EI_VERSION] = EV_CURRENT; 126 127 ehdr->e_type = ET_CORE; 128 ehdr->e_machine = VG_ELF_MACHINE; 129 ehdr->e_version = EV_CURRENT; 130 ehdr->e_entry = 0; 131 ehdr->e_phoff = sizeof(ESZ(Ehdr)); 132 ehdr->e_shoff = 0; 133 ehdr->e_flags = 0; 134 ehdr->e_ehsize = sizeof(ESZ(Ehdr)); 135 ehdr->e_phentsize = sizeof(ESZ(Phdr)); 136 ehdr->e_phnum = num_phdrs; 137 ehdr->e_shentsize = 0; 138 ehdr->e_shnum = 0; 139 ehdr->e_shstrndx = 0; 140 141} 142 143static void fill_phdr(ESZ(Phdr) *phdr, const NSegment *seg, UInt off, Bool write) 144{ 145 SizeT len = seg->end - seg->start; 146 147 write = write && should_dump(seg); 148 149 VG_(memset)(phdr, 0, sizeof(*phdr)); 150 151 phdr->p_type = PT_LOAD; 152 phdr->p_offset = off; 153 phdr->p_vaddr = seg->start; 154 phdr->p_paddr = 0; 155 phdr->p_filesz = write ? len : 0; 156 phdr->p_memsz = len; 157 phdr->p_flags = 0; 158 159 if (seg->hasR) 160 phdr->p_flags |= PF_R; 161 if (seg->hasW) 162 phdr->p_flags |= PF_W; 163 if (seg->hasX) 164 phdr->p_flags |= PF_X; 165 166 phdr->p_align = VKI_PAGE_SIZE; 167} 168 169struct note { 170 struct note *next; 171 ESZ(Nhdr) note; 172 Char name[0]; 173}; 174 175static UInt note_size(const struct note *n) 176{ 177 return sizeof(ESZ(Nhdr)) + VG_ROUNDUP(VG_(strlen)(n->name)+1, 4) + VG_ROUNDUP(n->note.n_descsz, 4); 178} 179 180static void add_note(struct note **list, const Char *name, UInt type, const void *data, UInt datasz) 181{ 182 Int namelen = VG_(strlen)(name)+1; 183 Int notelen = sizeof(struct note) + 184 VG_ROUNDUP(namelen, 4) + 185 VG_ROUNDUP(datasz, 4); 186 struct note *n = VG_(arena_malloc)(VG_AR_CORE, notelen); 187 188 VG_(memset)(n, 0, notelen); 189 190 n->next = *list; 191 *list = n; 192 193 n->note.n_type = type; 194 n->note.n_namesz = namelen; 195 n->note.n_descsz = datasz; 196 197 VG_(memcpy)(n->name, name, namelen); 198 VG_(memcpy)(n->name+VG_ROUNDUP(namelen,4), data, datasz); 199} 200 201static void write_note(Int fd, const struct note *n) 202{ 203 VG_(write)(fd, &n->note, note_size(n)); 204} 205 206static void fill_prpsinfo(const ThreadState *tst, struct vki_elf_prpsinfo *prpsinfo) 207{ 208 static Char name[VKI_PATH_MAX]; 209 210 VG_(memset)(prpsinfo, 0, sizeof(*prpsinfo)); 211 212 switch(tst->status) { 213 case VgTs_Runnable: 214 case VgTs_Yielding: 215 prpsinfo->pr_sname = 'R'; 216 break; 217 218 case VgTs_WaitSys: 219 prpsinfo->pr_sname = 'S'; 220 break; 221 222 case VgTs_Zombie: 223 prpsinfo->pr_sname = 'Z'; 224 break; 225 226 case VgTs_Empty: 227 case VgTs_Init: 228 prpsinfo->pr_sname = '?'; 229 break; 230 } 231 232 prpsinfo->pr_uid = 0; 233 prpsinfo->pr_gid = 0; 234 235 if (VG_(resolve_filename)(VG_(cl_exec_fd), name, VKI_PATH_MAX)) { 236 Char *n = name+VG_(strlen)(name)-1; 237 238 while (n > name && *n != '/') 239 n--; 240 if (n != name) 241 n++; 242 243 VG_(strncpy)(prpsinfo->pr_fname, n, sizeof(prpsinfo->pr_fname)); 244 } 245} 246 247static void fill_prstatus(const ThreadState *tst, 248 struct vki_elf_prstatus *prs, 249 const vki_siginfo_t *si) 250{ 251 struct vki_user_regs_struct *regs; 252 253 VG_(memset)(prs, 0, sizeof(*prs)); 254 255 prs->pr_info.si_signo = si->si_signo; 256 prs->pr_info.si_code = si->si_code; 257 prs->pr_info.si_errno = 0; 258 259 prs->pr_cursig = si->si_signo; 260 261 prs->pr_pid = tst->os_state.lwpid; 262 prs->pr_ppid = 0; 263 prs->pr_pgrp = VG_(getpgrp)(); 264 prs->pr_sid = VG_(getpgrp)(); 265 266 regs = (struct vki_user_regs_struct *)prs->pr_reg; 267 268 vg_assert(sizeof(*regs) == sizeof(prs->pr_reg)); 269 270 ML_(fill_elfregs_from_tst)(regs, &tst->arch); 271} 272 273static void fill_fpu(const ThreadState *tst, vki_elf_fpregset_t *fpu) 274{ 275 ML_(fill_elffpregs_from_tst)(fpu, &tst->arch); 276} 277 278#if defined(VGP_x86_linux) 279static void fill_xfpu(const ThreadState *tst, vki_elf_fpxregset_t *xfpu) 280{ 281 ML_(fill_elffpxregs_from_tst)(xfpu, &tst->arch); 282} 283#endif 284 285static 286void make_elf_coredump(ThreadId tid, const vki_siginfo_t *si, UInt max_size) 287{ 288 Char buf[1000]; 289 Char *basename = "vgcore"; 290 Char *coreext = ""; 291 Int seq = 0; 292 Int core_fd; 293 NSegment const * seg; 294 ESZ(Ehdr) ehdr; 295 ESZ(Phdr) *phdrs; 296 Int num_phdrs; 297 Int i, idx; 298 UInt off; 299 struct note *notelist, *note; 300 UInt notesz; 301 struct vki_elf_prpsinfo prpsinfo; 302 struct vki_elf_prstatus prstatus; 303 Addr *seg_starts; 304 Int n_seg_starts; 305 306 if (VG_(clo_log_name) != NULL) { 307 coreext = ".core"; 308 basename = VG_(clo_log_name); 309 } 310 311 for(;;) { 312 SysRes sres; 313 314 if (seq == 0) 315 VG_(sprintf)(buf, "%s%s.%d", 316 basename, coreext, VG_(getpid)()); 317 else 318 VG_(sprintf)(buf, "%s%s.%d.%d", 319 basename, coreext, VG_(getpid)(), seq); 320 seq++; 321 322 sres = VG_(open)(buf, 323 VKI_O_CREAT|VKI_O_WRONLY|VKI_O_EXCL|VKI_O_TRUNC, 324 VKI_S_IRUSR|VKI_S_IWUSR); 325 if (!sres.isError) { 326 core_fd = sres.res; 327 break; 328 } 329 330 if (sres.isError && sres.err != VKI_EEXIST) 331 return; /* can't create file */ 332 } 333 334 /* Get the segments */ 335 seg_starts = get_seg_starts(&n_seg_starts); 336 337 /* First, count how many memory segments to dump */ 338 num_phdrs = 1; /* start with notes */ 339 for(i = 0; i < n_seg_starts; i++) { 340 if (!may_dump(VG_(am_find_nsegment(seg_starts[i])))) 341 continue; 342 343 num_phdrs++; 344 } 345 346 fill_ehdr(&ehdr, num_phdrs); 347 348 notelist = NULL; 349 350 /* Second, work out their layout */ 351 phdrs = VG_(arena_malloc)(VG_AR_CORE, sizeof(*phdrs) * num_phdrs); 352 353 for(i = 1; i < VG_N_THREADS; i++) { 354 vki_elf_fpregset_t fpu; 355#if defined(VGP_x86_linux) 356 vki_elf_fpxregset_t xfpu; 357#endif 358 359 if (VG_(threads)[i].status == VgTs_Empty) 360 continue; 361 362#if defined(VGP_x86_linux) 363 fill_xfpu(&VG_(threads)[i], &xfpu); 364 add_note(¬elist, "LINUX", NT_PRXFPREG, &xfpu, sizeof(xfpu)); 365#endif 366 367 fill_fpu(&VG_(threads)[i], &fpu); 368 add_note(¬elist, "CORE", NT_FPREGSET, &fpu, sizeof(fpu)); 369 370 fill_prstatus(&VG_(threads)[i], &prstatus, si); 371 add_note(¬elist, "CORE", NT_PRSTATUS, &prstatus, sizeof(prstatus)); 372 } 373 374 fill_prpsinfo(&VG_(threads)[tid], &prpsinfo); 375 add_note(¬elist, "CORE", NT_PRPSINFO, &prpsinfo, sizeof(prpsinfo)); 376 377 for(note = notelist, notesz = 0; note != NULL; note = note->next) 378 notesz += note_size(note); 379 380 off = sizeof(ehdr) + sizeof(*phdrs) * num_phdrs; 381 382 phdrs[0].p_type = PT_NOTE; 383 phdrs[0].p_offset = off; 384 phdrs[0].p_vaddr = 0; 385 phdrs[0].p_paddr = 0; 386 phdrs[0].p_filesz = notesz; 387 phdrs[0].p_memsz = 0; 388 phdrs[0].p_flags = 0; 389 phdrs[0].p_align = 0; 390 391 off += notesz; 392 393 off = VG_PGROUNDUP(off); 394 395 for(i = 0, idx = 1; i < n_seg_starts; i++) { 396 seg = VG_(am_find_nsegment(seg_starts[i])); 397 398 if (!may_dump(seg)) 399 continue; 400 401 fill_phdr(&phdrs[idx], seg, off, (seg->end - seg->start + off) < max_size); 402 403 off += phdrs[idx].p_filesz; 404 405 idx++; 406 } 407 408 /* write everything out */ 409 VG_(write)(core_fd, &ehdr, sizeof(ehdr)); 410 VG_(write)(core_fd, phdrs, sizeof(*phdrs) * num_phdrs); 411 412 for(note = notelist; note != NULL; note = note->next) 413 write_note(core_fd, note); 414 415 VG_(lseek)(core_fd, phdrs[1].p_offset, VKI_SEEK_SET); 416 417 for(i = 0, idx = 1; i < n_seg_starts; i++) { 418 seg = VG_(am_find_nsegment(seg_starts[i])); 419 420 if (!should_dump(seg)) 421 continue; 422 423 if (phdrs[idx].p_filesz > 0) { 424 Int ret; 425 426 vg_assert(VG_(lseek)(core_fd, phdrs[idx].p_offset, VKI_SEEK_SET) == phdrs[idx].p_offset); 427 vg_assert(seg->end - seg->start >= phdrs[idx].p_filesz); 428 429 ret = VG_(write)(core_fd, (void *)seg->start, phdrs[idx].p_filesz); 430 } 431 idx++; 432 } 433 434 VG_(free)(seg_starts); 435 436 VG_(close)(core_fd); 437} 438 439void VG_(make_coredump)(ThreadId tid, const vki_siginfo_t *si, UInt max_size) 440{ 441 make_elf_coredump(tid, si, max_size); 442} 443 444/*--------------------------------------------------------------------*/ 445/*--- end ---*/ 446/*--------------------------------------------------------------------*/ 447