coredump-elf.c revision cda2f0fbda4c4b2644babc830244be8aed95de1d
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-2009 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_aspacehl.h" 34#include "pub_core_aspacemgr.h" 35#include "pub_core_libcbase.h" 36#include "pub_core_machine.h" 37#include "pub_core_coredump.h" 38#include "pub_core_libcprint.h" 39#include "pub_core_libcfile.h" // VG_(close) et al 40#include "pub_core_libcproc.h" // VG_(geteuid), VG_(getegid) 41#include "pub_core_libcassert.h" // VG_(exit), vg_assert 42#include "pub_core_mallocfree.h" // VG_(malloc), VG_(free) 43#include "pub_core_threadstate.h" 44#include "pub_core_xarray.h" 45#include "pub_core_clientstate.h" 46#include "pub_core_options.h" 47 48#include "priv_elf.h" 49 50/* 51 Dump core 52 53 Generate a standard ELF core file corresponding to the client state 54 at the time of a crash. 55 */ 56#include <elf.h> 57#ifndef NT_PRXFPREG 58#define NT_PRXFPREG 0x46e62b7f /* copied from gdb5.1/include/elf/common.h */ 59#endif /* NT_PRXFPREG */ 60 61#if VG_WORDSIZE == 8 62#define ESZ(x) Elf64_##x 63#elif VG_WORDSIZE == 4 64#define ESZ(x) Elf32_##x 65#else 66#error VG_WORDSIZE needs to ==4 or ==8 67#endif 68 69/* If true, then this Segment may be mentioned in the core */ 70static Bool may_dump(const NSegment *seg) 71{ 72 if (seg->kind == SkAnonC || 73 seg->kind == SkShmC || 74 (seg->kind == SkFileC && 75 !VKI_S_ISCHR(seg->mode) && !VKI_S_ISBLK(seg->mode))) 76 return True; 77 78 return False; 79} 80 81/* If true, then this Segment's contents will be in the core */ 82static Bool should_dump(const NSegment *seg) 83{ 84 return may_dump(seg); // && seg->hasW; 85} 86 87static void fill_ehdr(ESZ(Ehdr) *ehdr, Int num_phdrs) 88{ 89 VG_(memset)(ehdr, 0, sizeof(*ehdr)); 90 91 VG_(memcpy)(ehdr->e_ident, ELFMAG, SELFMAG); 92 ehdr->e_ident[EI_CLASS] = VG_ELF_CLASS; 93 ehdr->e_ident[EI_DATA] = VG_ELF_DATA2XXX; 94 ehdr->e_ident[EI_VERSION] = EV_CURRENT; 95 96 ehdr->e_type = ET_CORE; 97 ehdr->e_machine = VG_ELF_MACHINE; 98 ehdr->e_version = EV_CURRENT; 99 ehdr->e_entry = 0; 100 ehdr->e_phoff = sizeof(ESZ(Ehdr)); 101 ehdr->e_shoff = 0; 102 ehdr->e_flags = 0; 103 ehdr->e_ehsize = sizeof(ESZ(Ehdr)); 104 ehdr->e_phentsize = sizeof(ESZ(Phdr)); 105 ehdr->e_phnum = num_phdrs; 106 ehdr->e_shentsize = 0; 107 ehdr->e_shnum = 0; 108 ehdr->e_shstrndx = 0; 109 110} 111 112static void fill_phdr(ESZ(Phdr) *phdr, const NSegment *seg, UInt off, Bool write) 113{ 114 SizeT len = seg->end - seg->start; 115 116 write = write && should_dump(seg); 117 118 VG_(memset)(phdr, 0, sizeof(*phdr)); 119 120 phdr->p_type = PT_LOAD; 121 phdr->p_offset = off; 122 phdr->p_vaddr = seg->start; 123 phdr->p_paddr = 0; 124 phdr->p_filesz = write ? len : 0; 125 phdr->p_memsz = len; 126 phdr->p_flags = 0; 127 128 if (seg->hasR) 129 phdr->p_flags |= PF_R; 130 if (seg->hasW) 131 phdr->p_flags |= PF_W; 132 if (seg->hasX) 133 phdr->p_flags |= PF_X; 134 135 phdr->p_align = VKI_PAGE_SIZE; 136} 137 138struct note { 139 struct note *next; 140 ESZ(Nhdr) note; 141 Char name[0]; 142}; 143 144static UInt note_size(const struct note *n) 145{ 146 return sizeof(ESZ(Nhdr)) + VG_ROUNDUP(VG_(strlen)(n->name)+1, 4) + VG_ROUNDUP(n->note.n_descsz, 4); 147} 148 149static void add_note(struct note **list, const Char *name, UInt type, const void *data, UInt datasz) 150{ 151 Int namelen = VG_(strlen)(name)+1; 152 Int notelen = sizeof(struct note) + 153 VG_ROUNDUP(namelen, 4) + 154 VG_ROUNDUP(datasz, 4); 155 struct note *n = VG_(arena_malloc)(VG_AR_CORE, "coredump-elf.an.1", notelen); 156 157 VG_(memset)(n, 0, notelen); 158 159 n->next = *list; 160 *list = n; 161 162 n->note.n_type = type; 163 n->note.n_namesz = namelen; 164 n->note.n_descsz = datasz; 165 166 VG_(memcpy)(n->name, name, namelen); 167 VG_(memcpy)(n->name+VG_ROUNDUP(namelen,4), data, datasz); 168} 169 170static void write_note(Int fd, const struct note *n) 171{ 172 VG_(write)(fd, &n->note, note_size(n)); 173} 174 175static void fill_prpsinfo(const ThreadState *tst, struct vki_elf_prpsinfo *prpsinfo) 176{ 177 static Char name[VKI_PATH_MAX]; 178 179 VG_(memset)(prpsinfo, 0, sizeof(*prpsinfo)); 180 181 switch(tst->status) { 182 case VgTs_Runnable: 183 case VgTs_Yielding: 184 prpsinfo->pr_sname = 'R'; 185 break; 186 187 case VgTs_WaitSys: 188 prpsinfo->pr_sname = 'S'; 189 break; 190 191 case VgTs_Zombie: 192 prpsinfo->pr_sname = 'Z'; 193 break; 194 195 case VgTs_Empty: 196 case VgTs_Init: 197 prpsinfo->pr_sname = '?'; 198 break; 199 } 200 201 prpsinfo->pr_uid = 0; 202 prpsinfo->pr_gid = 0; 203 204 if (VG_(resolve_filename)(VG_(cl_exec_fd), name, VKI_PATH_MAX)) { 205 Char *n = name+VG_(strlen)(name)-1; 206 207 while (n > name && *n != '/') 208 n--; 209 if (n != name) 210 n++; 211 212 VG_(strncpy)(prpsinfo->pr_fname, n, sizeof(prpsinfo->pr_fname)); 213 } 214} 215 216static void fill_prstatus(const ThreadState *tst, 217 struct vki_elf_prstatus *prs, 218 const vki_siginfo_t *si) 219{ 220 struct vki_user_regs_struct *regs; 221 222 VG_(memset)(prs, 0, sizeof(*prs)); 223 224 prs->pr_info.si_signo = si->si_signo; 225 prs->pr_info.si_code = si->si_code; 226 prs->pr_info.si_errno = 0; 227 228 prs->pr_cursig = si->si_signo; 229 230 prs->pr_pid = tst->os_state.lwpid; 231 prs->pr_ppid = 0; 232 prs->pr_pgrp = VG_(getpgrp)(); 233 prs->pr_sid = VG_(getpgrp)(); 234 235 regs = (struct vki_user_regs_struct *)prs->pr_reg; 236 237 vg_assert(sizeof(*regs) == sizeof(prs->pr_reg)); 238 239 ML_(fill_elfregs_from_tst)(regs, &tst->arch); 240} 241 242static void fill_fpu(const ThreadState *tst, vki_elf_fpregset_t *fpu) 243{ 244 ML_(fill_elffpregs_from_tst)(fpu, &tst->arch); 245} 246 247#if defined(VGP_x86_linux) 248static void fill_xfpu(const ThreadState *tst, vki_elf_fpxregset_t *xfpu) 249{ 250 ML_(fill_elffpxregs_from_tst)(xfpu, &tst->arch); 251} 252#endif 253 254static 255void make_elf_coredump(ThreadId tid, const vki_siginfo_t *si, UInt max_size) 256{ 257 Char* buf = NULL; 258 Char *basename = "vgcore"; 259 Char *coreext = ""; 260 Int seq = 0; 261 Int core_fd; 262 NSegment const * seg; 263 ESZ(Ehdr) ehdr; 264 ESZ(Phdr) *phdrs; 265 Int num_phdrs; 266 Int i, idx; 267 UInt off; 268 struct note *notelist, *note; 269 UInt notesz; 270 struct vki_elf_prpsinfo prpsinfo; 271 struct vki_elf_prstatus prstatus; 272 Addr *seg_starts; 273 Int n_seg_starts; 274 275 if (VG_(clo_log_name) != NULL) { 276 coreext = ".core"; 277 basename = VG_(expand_file_name)( 278 "--log-file (while creating core filename)", 279 VG_(clo_log_name)); 280 } 281 282 vg_assert(coreext); 283 vg_assert(basename); 284 buf = VG_(malloc)( "coredump-elf.mec.1", 285 VG_(strlen)(coreext) + VG_(strlen)(basename) 286 + 100/*for the two %ds. */ ); 287 vg_assert(buf); 288 289 for(;;) { 290 SysRes sres; 291 292 if (seq == 0) 293 VG_(sprintf)(buf, "%s%s.%d", 294 basename, coreext, VG_(getpid)()); 295 else 296 VG_(sprintf)(buf, "%s%s.%d.%d", 297 basename, coreext, VG_(getpid)(), seq); 298 seq++; 299 300 sres = VG_(open)(buf, 301 VKI_O_CREAT|VKI_O_WRONLY|VKI_O_EXCL|VKI_O_TRUNC, 302 VKI_S_IRUSR|VKI_S_IWUSR); 303 if (!sr_isError(sres)) { 304 core_fd = sr_Res(sres); 305 break; 306 } 307 308 if (sr_isError(sres) && sr_Err(sres) != VKI_EEXIST) 309 return; /* can't create file */ 310 } 311 312 /* Get the segments */ 313 seg_starts = VG_(get_segment_starts)(&n_seg_starts); 314 315 /* First, count how many memory segments to dump */ 316 num_phdrs = 1; /* start with notes */ 317 for(i = 0; i < n_seg_starts; i++) { 318 if (!may_dump(VG_(am_find_nsegment(seg_starts[i])))) 319 continue; 320 321 num_phdrs++; 322 } 323 324 fill_ehdr(&ehdr, num_phdrs); 325 326 notelist = NULL; 327 328 /* Second, work out their layout */ 329 phdrs = VG_(arena_malloc)(VG_AR_CORE, "coredump-elf.mec.1", 330 sizeof(*phdrs) * num_phdrs); 331 332 for(i = 1; i < VG_N_THREADS; i++) { 333 vki_elf_fpregset_t fpu; 334#if defined(VGP_x86_linux) 335 vki_elf_fpxregset_t xfpu; 336#endif 337 338 if (VG_(threads)[i].status == VgTs_Empty) 339 continue; 340 341#if defined(VGP_x86_linux) 342 fill_xfpu(&VG_(threads)[i], &xfpu); 343 add_note(¬elist, "LINUX", NT_PRXFPREG, &xfpu, sizeof(xfpu)); 344#endif 345 346 fill_fpu(&VG_(threads)[i], &fpu); 347 add_note(¬elist, "CORE", NT_FPREGSET, &fpu, sizeof(fpu)); 348 349 fill_prstatus(&VG_(threads)[i], &prstatus, si); 350 add_note(¬elist, "CORE", NT_PRSTATUS, &prstatus, sizeof(prstatus)); 351 } 352 353 fill_prpsinfo(&VG_(threads)[tid], &prpsinfo); 354 add_note(¬elist, "CORE", NT_PRPSINFO, &prpsinfo, sizeof(prpsinfo)); 355 356 for(note = notelist, notesz = 0; note != NULL; note = note->next) 357 notesz += note_size(note); 358 359 off = sizeof(ehdr) + sizeof(*phdrs) * num_phdrs; 360 361 phdrs[0].p_type = PT_NOTE; 362 phdrs[0].p_offset = off; 363 phdrs[0].p_vaddr = 0; 364 phdrs[0].p_paddr = 0; 365 phdrs[0].p_filesz = notesz; 366 phdrs[0].p_memsz = 0; 367 phdrs[0].p_flags = 0; 368 phdrs[0].p_align = 0; 369 370 off += notesz; 371 372 off = VG_PGROUNDUP(off); 373 374 for(i = 0, idx = 1; i < n_seg_starts; i++) { 375 seg = VG_(am_find_nsegment(seg_starts[i])); 376 377 if (!may_dump(seg)) 378 continue; 379 380 fill_phdr(&phdrs[idx], seg, off, (seg->end - seg->start + off) < max_size); 381 382 off += phdrs[idx].p_filesz; 383 384 idx++; 385 } 386 387 /* write everything out */ 388 VG_(write)(core_fd, &ehdr, sizeof(ehdr)); 389 VG_(write)(core_fd, phdrs, sizeof(*phdrs) * num_phdrs); 390 391 for(note = notelist; note != NULL; note = note->next) 392 write_note(core_fd, note); 393 394 VG_(lseek)(core_fd, phdrs[1].p_offset, VKI_SEEK_SET); 395 396 for(i = 0, idx = 1; i < n_seg_starts; i++) { 397 seg = VG_(am_find_nsegment(seg_starts[i])); 398 399 if (!should_dump(seg)) 400 continue; 401 402 if (phdrs[idx].p_filesz > 0) { 403 vg_assert(VG_(lseek)(core_fd, phdrs[idx].p_offset, VKI_SEEK_SET) == phdrs[idx].p_offset); 404 vg_assert(seg->end - seg->start >= phdrs[idx].p_filesz); 405 406 (void)VG_(write)(core_fd, (void *)seg->start, phdrs[idx].p_filesz); 407 } 408 idx++; 409 } 410 411 VG_(free)(seg_starts); 412 413 VG_(close)(core_fd); 414} 415 416void VG_(make_coredump)(ThreadId tid, const vki_siginfo_t *si, UInt max_size) 417{ 418 make_elf_coredump(tid, si, max_size); 419} 420 421/*--------------------------------------------------------------------*/ 422/*--- end ---*/ 423/*--------------------------------------------------------------------*/ 424