1 |
|
/* Write ELF core dump files for GNU Hurd. |
2 |
|
Copyright (C) 2002 Free Software Foundation, Inc. |
3 |
|
Written by Roland McGrath. |
4 |
|
|
5 |
|
This file is part of the GNU Hurd. |
6 |
|
|
7 |
|
The GNU Hurd is free software; you can redistribute it and/or modify |
8 |
|
it under the terms of the GNU General Public License as published by |
9 |
|
the Free Software Foundation; either version 2, or (at your option) |
10 |
|
any later version. |
11 |
|
|
12 |
|
The GNU Hurd is distributed in the hope that it will be useful, |
13 |
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 |
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
15 |
|
GNU General Public License for more details. |
16 |
|
|
17 |
|
You should have received a copy of the GNU General Public License |
18 |
|
along with the GNU Hurd; see the file COPYING. If not, write to |
19 |
|
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ |
20 |
|
|
21 |
#include <hurd.h> |
#include <hurd.h> |
22 |
#include <elf.h> |
#include <elf.h> |
23 |
#include <link.h> |
#include <link.h> |
28 |
#include <sys/procfs.h> |
#include <sys/procfs.h> |
29 |
#include <stddef.h> |
#include <stddef.h> |
30 |
|
|
|
|
|
|
#define ELF_MACHINE EM_386 /* XXX */ |
|
|
|
|
31 |
#define ELF_CLASS PASTE (ELFCLASS, __ELF_NATIVE_CLASS) |
#define ELF_CLASS PASTE (ELFCLASS, __ELF_NATIVE_CLASS) |
32 |
#define PASTE(a, b) PASTE_1 (a, b) |
#define PASTE(a, b) PASTE_1 (a, b) |
33 |
#define PASTE_1(a, b) a##b |
#define PASTE_1(a, b) a##b |
39 |
#define ELF_DATA ELFDATA2LSB |
#define ELF_DATA ELFDATA2LSB |
40 |
#endif |
#endif |
41 |
|
|
42 |
|
#include <mach/thread_status.h> |
43 |
|
#include <assert.h> |
44 |
|
|
45 |
|
#ifdef i386_THREAD_STATE |
46 |
|
# define ELF_MACHINE EM_386 |
47 |
|
|
48 |
|
/* There is a natural layout for this on x86, so it happens |
49 |
|
that the canonical gregset_t from <sys/ucontext.h> |
50 |
|
matches Mach's i386_thread_state exactly. */ |
51 |
|
static inline void |
52 |
|
fetch_thread_regset (thread_t thread, prgregset_t *gregs) |
53 |
|
{ |
54 |
|
mach_msg_type_number_t count = i386_THREAD_STATE_COUNT; |
55 |
|
assert (sizeof (struct i386_thread_state) == sizeof (prgregset_t)); |
56 |
|
assert (offsetof (struct i386_thread_state, gs) == REG_GS); |
57 |
|
assert (offsetof (struct i386_thread_state, eip) == REG_EIP * 4); |
58 |
|
assert (offsetof (struct i386_thread_state, ss) == REG_SS * 4); |
59 |
|
(void) thread_get_state (thread, i386_THREAD_STATE, |
60 |
|
(thread_state_t) gregs, &count); |
61 |
|
} |
62 |
|
|
63 |
|
static inline void |
64 |
|
fetch_thread_fpregset (thread_t thread, prfpregset_t *fpregs) |
65 |
|
{ |
66 |
|
struct i386_float_state st; |
67 |
|
mach_msg_type_number_t count = i386_FLOAT_STATE_COUNT; |
68 |
|
error_t err = thread_get_state (thread, i386_FLOAT_STATE, |
69 |
|
(thread_state_t) &st, &count); |
70 |
|
if (err == 0 && st.initialized) |
71 |
|
{ |
72 |
|
assert (sizeof *fpregs >= sizeof st.hw_state); |
73 |
|
memcpy (fpregs, st.hw_state, sizeof st.hw_state); |
74 |
|
} |
75 |
|
} |
76 |
|
|
77 |
|
#else |
78 |
|
# warning "do not understand this machine flavor, no registers in dumps" |
79 |
|
# define ELF_MACHINE EM_NONE |
80 |
|
#endif |
81 |
|
|
82 |
|
|
83 |
#define TIME_VALUE_TO_TIMESPEC(tv, ts) \ |
#define TIME_VALUE_TO_TIMESPEC(tv, ts) \ |
84 |
TIMEVAL_TO_TIMESPEC ((struct timeval *) (tv), (ts)) |
TIMEVAL_TO_TIMESPEC ((struct timeval *) (tv), (ts)) |
86 |
#define PAGES_TO_KB(x) ((x) * (vm_page_size / 1024)) |
#define PAGES_TO_KB(x) ((x) * (vm_page_size / 1024)) |
87 |
#define ENCODE_PCT(f) ((uint16_t) ((f) * 32768.0)) |
#define ENCODE_PCT(f) ((uint16_t) ((f) * 32768.0)) |
88 |
|
|
89 |
extern process_t procserver; |
extern process_t procserver; /* crash.c */ |
90 |
|
|
91 |
error_t |
error_t |
92 |
dump_core (task_t task, file_t file, off_t corelimit, |
dump_core (task_t task, file_t file, off_t corelimit, |
284 |
/* The psinfo_t note contains some process-global info we should get from |
/* The psinfo_t note contains some process-global info we should get from |
285 |
the proc server, but no thread-specific info like register state. */ |
the proc server, but no thread-specific info like register state. */ |
286 |
{ |
{ |
287 |
DEFINE_NOTE (psinfo_t) note; |
DEFINE_NOTE (psinfo_t) psinfo; |
288 |
|
DEFINE_NOTE (pstatus_t) pstatus; |
289 |
int flags = PI_FETCH_TASKINFO | PI_FETCH_THREADS | PI_FETCH_THREAD_BASIC; |
int flags = PI_FETCH_TASKINFO | PI_FETCH_THREADS | PI_FETCH_THREAD_BASIC; |
290 |
char *waits = 0; |
char *waits = 0; |
291 |
mach_msg_type_number_t num_waits = 0; |
mach_msg_type_number_t num_waits = 0; |
292 |
char pibuf[offsetof (struct procinfo, threadinfos[2])]; |
char pibuf[offsetof (struct procinfo, threadinfos[2])]; |
293 |
struct procinfo *pi = (void *) &pibuf; |
struct procinfo *pi = (void *) &pibuf; |
294 |
mach_msg_type_number_t pi_size = sizeof pibuf; |
mach_msg_type_number_t pi_size = sizeof pibuf; |
295 |
|
|
296 |
|
memset (&pstatus.data, 0, sizeof pstatus.data); |
297 |
|
memset (&psinfo.data, 0, sizeof psinfo.data); |
298 |
|
pstatus.data.pr_pid = psinfo.data.pr_pid = pid; |
299 |
|
|
300 |
err = proc_getprocinfo (procserver, pid, &flags, |
err = proc_getprocinfo (procserver, pid, &flags, |
301 |
(procinfo_t *) &pi, &pi_size, |
(procinfo_t *) &pi, &pi_size, |
302 |
&waits, &num_waits); |
&waits, &num_waits); |
304 |
{ |
{ |
305 |
if (num_waits != 0) |
if (num_waits != 0) |
306 |
munmap (waits, num_waits); |
munmap (waits, num_waits); |
307 |
memset (¬e.data, 0, sizeof note.data); |
|
308 |
note.data.pr_flag = pi->state; |
pstatus.data.pr_flags = psinfo.data.pr_flag = pi->state; |
309 |
note.data.pr_nlwp = pi->nthreads; |
pstatus.data.pr_nlwp = psinfo.data.pr_nlwp = pi->nthreads; |
310 |
note.data.pr_pid = pid; |
pstatus.data.pr_ppid = psinfo.data.pr_ppid = pi->ppid; |
311 |
note.data.pr_ppid = pi->ppid; |
pstatus.data.pr_pgid = psinfo.data.pr_pgid = pi->pgrp; |
312 |
note.data.pr_pgid = pi->pgrp; |
pstatus.data.pr_sid = psinfo.data.pr_sid = pi->session; |
313 |
note.data.pr_sid = pi->session; |
|
314 |
note.data.pr_euid = pi->owner; |
psinfo.data.pr_euid = pi->owner; |
315 |
/* XXX struct procinfo should have these */ |
/* XXX struct procinfo should have these */ |
316 |
note.data.pr_egid = note.data.pr_gid = note.data.pr_uid = -1; |
psinfo.data.pr_egid = psinfo.data.pr_gid = psinfo.data.pr_uid = -1; |
317 |
note.data.pr_size = PAGES_TO_KB (pi->taskinfo.virtual_size); |
|
318 |
note.data.pr_rssize = PAGES_TO_KB (pi->taskinfo.resident_size); |
psinfo.data.pr_size = PAGES_TO_KB (pi->taskinfo.virtual_size); |
319 |
|
psinfo.data.pr_rssize = PAGES_TO_KB (pi->taskinfo.resident_size); |
320 |
|
|
321 |
{ |
{ |
322 |
/* Sum all the threads' cpu_usage fields. */ |
/* Sum all the threads' cpu_usage fields. */ |
323 |
integer_t cpu_usage = 0; |
integer_t cpu_usage = 0; |
324 |
for (i = 0; i < pi->nthreads; ++i) |
for (i = 0; i < pi->nthreads; ++i) |
325 |
cpu_usage += pi->threadinfos[i].pis_bi.cpu_usage; |
cpu_usage += pi->threadinfos[i].pis_bi.cpu_usage; |
326 |
note.data.pr_pctcpu = ENCODE_PCT ((float) cpu_usage |
psinfo.data.pr_pctcpu = ENCODE_PCT ((float) cpu_usage |
327 |
/ (float) TH_USAGE_SCALE); |
/ (float) TH_USAGE_SCALE); |
328 |
} |
} |
329 |
if (host_memory_size > 0.0) |
if (host_memory_size > 0.0) |
330 |
note.data.pr_pctmem = ENCODE_PCT ((float) pi->taskinfo.resident_size |
psinfo.data.pr_pctmem |
331 |
/ host_memory_size); |
= ENCODE_PCT |
332 |
|
((float) pi->taskinfo.resident_size / host_memory_size); |
333 |
|
|
334 |
TIME_VALUE_TO_TIMESPEC (&pi->taskinfo.creation_time, |
TIME_VALUE_TO_TIMESPEC (&pi->taskinfo.creation_time, |
335 |
¬e.data.pr_start); |
&psinfo.data.pr_start); |
336 |
|
|
337 |
|
TIME_VALUE_TO_TIMESPEC (&pi->taskinfo.user_time, |
338 |
|
&pstatus.data.pr_utime); |
339 |
|
TIME_VALUE_TO_TIMESPEC (&pi->taskinfo.system_time, |
340 |
|
&pstatus.data.pr_stime); |
341 |
|
/* Sum the user and system time for pr_time. */ |
342 |
timeradd ((const struct timeval *) &pi->taskinfo.user_time, |
timeradd ((const struct timeval *) &pi->taskinfo.user_time, |
343 |
(const struct timeval *) &pi->taskinfo.system_time, |
(const struct timeval *) &pi->taskinfo.system_time, |
344 |
(struct timeval *) &pi->taskinfo.user_time); |
(struct timeval *) &pi->taskinfo.user_time); |
345 |
TIME_VALUE_TO_TIMESPEC (&pi->taskinfo.user_time, ¬e.data.pr_time); |
TIME_VALUE_TO_TIMESPEC (&pi->taskinfo.user_time, &psinfo.data.pr_time); |
346 |
/* XXX struct procinfo should have dead child info for pr_ctime */ |
/* XXX struct procinfo should have dead child info for pr_c[us]?time */ |
347 |
note.data.pr_wstat = pi->exitstatus; |
|
348 |
|
psinfo.data.pr_wstat = pi->exitstatus; |
349 |
|
|
350 |
if ((void *) pi != &pibuf) |
if ((void *) pi != &pibuf) |
351 |
munmap (pi, pi_size); |
munmap (pi, pi_size); |
352 |
|
} |
353 |
{ |
if (err == 0) |
354 |
/* We have to nab the process's own proc port to get the |
{ |
355 |
proc server to tell us its registered arg locations. */ |
/* We have to nab the process's own proc port to get the |
356 |
process_t proc; |
proc server to tell us its registered arg locations. */ |
357 |
err = proc_task2proc (procserver, task, &proc); |
process_t proc; |
358 |
if (err == 0) |
err = proc_task2proc (procserver, task, &proc); |
359 |
{ |
if (err == 0) |
360 |
err = proc_get_arg_locations (proc, |
{ |
361 |
¬e.data.pr_argv, |
err = proc_get_arg_locations (proc, |
362 |
¬e.data.pr_envp); |
&psinfo.data.pr_argv, |
363 |
mach_port_deallocate (mach_task_self (), proc); |
&psinfo.data.pr_envp); |
364 |
} |
mach_port_deallocate (mach_task_self (), proc); |
365 |
err = 0; |
} |
|
} |
|
366 |
{ |
{ |
367 |
/* Now fetch the arguments. We could do this directly from the |
/* Now fetch the arguments. We could do this directly from the |
368 |
task given the locations we now have. But we are lazy and have |
task given the locations we now have. But we are lazy and have |
369 |
the proc server do it for us. */ |
the proc server do it for us. */ |
370 |
char *data = note.data.pr_psargs; |
char *data = psinfo.data.pr_psargs; |
371 |
size_t datalen = sizeof note.data.pr_psargs; |
size_t datalen = sizeof psinfo.data.pr_psargs; |
372 |
err = proc_getprocargs (procserver, pid, &data, &datalen); |
err = proc_getprocargs (procserver, pid, &data, &datalen); |
373 |
if (err == 0) |
if (err == 0) |
374 |
{ |
{ |
375 |
note.data.pr_argc = argz_count (data, datalen); |
psinfo.data.pr_argc = argz_count (data, datalen); |
376 |
argz_stringify (data, datalen, ' '); |
argz_stringify (data, datalen, ' '); |
377 |
if (data != note.data.pr_psargs) |
if (data != psinfo.data.pr_psargs) |
378 |
{ |
{ |
379 |
memcpy (note.data.pr_psargs, data, |
memcpy (psinfo.data.pr_psargs, data, |
380 |
sizeof note.data.pr_psargs); |
sizeof psinfo.data.pr_psargs); |
381 |
munmap (data, datalen); |
munmap (data, datalen); |
382 |
} |
} |
383 |
} |
} |
384 |
} |
} |
385 |
err = WRITE_NOTE (NT_PSTATUS, note); |
err = WRITE_NOTE (NT_PSINFO, psinfo); |
386 |
} |
} |
387 |
|
|
388 |
#if 0 |
err = WRITE_NOTE (NT_PSTATUS, pstatus) ?: err; |
|
note.data.pr_info.si_signo = signo; |
|
|
note.data.pr_info.si_code = sigcode; |
|
|
note.data.pr_info.si_errno = sigerror; |
|
|
#endif |
|
389 |
} |
} |
390 |
if (err || (corelimit >= 0 && corelimit <= offset)) |
if (err || (corelimit >= 0 && corelimit <= offset)) |
391 |
return err; |
return err; |
397 |
return err; |
return err; |
398 |
for (i = 0; i < nthreads; ++i) |
for (i = 0; i < nthreads; ++i) |
399 |
{ |
{ |
400 |
{ |
DEFINE_NOTE (lwpstatus_t) note; |
401 |
/* lwpinfo_t a la Solaris gives thread's CPU time and such. */ |
memset (¬e.data, 0, sizeof note.data); |
402 |
DEFINE_NOTE (struct thread_basic_info) note; |
note.data.pr_lwpid = i; |
403 |
mach_msg_type_number_t count = THREAD_BASIC_INFO_COUNT; |
|
404 |
err = thread_info (threads[i], THREAD_BASIC_INFO, |
/* We have to write the death signal into every thread's record, even |
405 |
(thread_info_t)¬e.data, &count); |
though only one thread really took the signal. This is both because |
406 |
if (err == 0) |
we don't know which thread it was, and because GDB blindly uses the |
407 |
err = WRITE_NOTE (NT_LWPSINFO, note); |
value from each record to clobber the last (even if it's zero). */ |
408 |
else /* Just skip it if we can't get the info. */ |
note.data.pr_cursig = signo; |
409 |
err = 0; |
note.data.pr_info.si_signo = signo; |
410 |
} |
note.data.pr_info.si_code = sigcode; |
411 |
if (err || (corelimit >= 0 && corelimit <= offset)) |
note.data.pr_info.si_errno = sigerror; |
|
break; |
|
412 |
|
|
413 |
#ifdef WRITE_THREAD_NOTES |
fetch_thread_regset (threads[i], ¬e.data.pr_reg); |
414 |
/* XXX Here would go the note flavors for machine thread states. */ |
fetch_thread_fpregset (threads[i], ¬e.data.pr_fpreg); |
415 |
err = WRITE_THREAD_NOTES (i, threads[i]); |
|
416 |
#endif |
err = WRITE_NOTE (NT_LWPSTATUS, note); |
417 |
if (err || (corelimit >= 0 && corelimit <= offset)) |
if (err || (corelimit >= 0 && corelimit <= offset)) |
418 |
break; |
break; |
419 |
|
|
420 |
mach_port_deallocate (mach_task_self (), threads[i]); |
mach_port_deallocate (mach_task_self (), threads[i]); |
421 |
} |
} |
422 |
|
/* If we broke out of the loop early, deallocate remaining thread ports. */ |
423 |
while (i < nthreads) |
while (i < nthreads) |
424 |
mach_port_deallocate (mach_task_self (), threads[i++]); |
mach_port_deallocate (mach_task_self (), threads[i++]); |
425 |
munmap (threads, nthreads * sizeof *threads); |
munmap (threads, nthreads * sizeof *threads); |