1/* Common core note type descriptions for Linux.
2   Copyright (C) 2007-2010 Red Hat, Inc.
3   This file is part of elfutils.
4
5   This file is free software; you can redistribute it and/or modify
6   it under the terms of either
7
8     * the GNU Lesser General Public License as published by the Free
9       Software Foundation; either version 3 of the License, or (at
10       your option) any later version
11
12   or
13
14     * the GNU General Public License as published by the Free
15       Software Foundation; either version 2 of the License, or (at
16       your option) any later version
17
18   or both in parallel, as here.
19
20   elfutils is distributed in the hope that it will be useful, but
21   WITHOUT ANY WARRANTY; without even the implied warranty of
22   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
23   General Public License for more details.
24
25   You should have received copies of the GNU General Public License and
26   the GNU Lesser General Public License along with this program.  If
27   not, see <http://www.gnu.org/licenses/>.  */
28
29#include <string.h>
30
31/* The including CPU_corenote.c file provides prstatus_regs and
32   defines macros ULONG, [PUG]ID_T, and ALIGN_*, TYPE_*.
33
34   Here we describe the common layout used in <linux/elfcore.h>.  */
35
36#define	CHAR			int8_t
37#define	ALIGN_CHAR		1
38#define	TYPE_CHAR		ELF_T_BYTE
39#define	SHORT			uint16_t
40#define ALIGN_SHORT		2
41#define TYPE_SHORT		ELF_T_HALF
42#define	INT			int32_t
43#define ALIGN_INT		4
44#define TYPE_INT		ELF_T_SWORD
45#ifndef ALIGN_PR_REG
46# define ALIGN_PR_REG		ALIGN_ULONG
47#endif
48
49#define FIELD(type, name) type name __attribute__ ((aligned (ALIGN_##type)))
50
51struct EBLHOOK(siginfo)
52{
53  FIELD (INT, si_signo);
54  FIELD (INT, si_code);
55  FIELD (INT, si_errno);
56};
57
58struct EBLHOOK(timeval)
59{
60  FIELD (ULONG, tv_sec);
61  FIELD (ULONG, tv_usec);
62};
63
64/* On sparc64, tv_usec (suseconds_t) is actually 32 bits with 32 bits padding.
65   The 'T'|0x80 value for .format indicates this as a special kludge.  */
66#if SUSECONDS_HALF
67# define TIMEVAL_FIELD(name)	FIELD (time, ULONG, name, 'T'|0x80, .count = 2)
68#else
69# define TIMEVAL_FIELD(name)	FIELD (time, ULONG, name, 'T', .count = 2)
70#endif
71
72
73struct EBLHOOK(prstatus)
74{
75  struct EBLHOOK(siginfo) pr_info;
76  FIELD (SHORT, pr_cursig);
77  FIELD (ULONG, pr_sigpend);
78  FIELD (ULONG, pr_sighold);
79  FIELD (PID_T, pr_pid);
80  FIELD (PID_T, pr_ppid);
81  FIELD (PID_T, pr_pgrp);
82  FIELD (PID_T, pr_sid);
83  struct EBLHOOK(timeval) pr_utime;
84  struct EBLHOOK(timeval) pr_stime;
85  struct EBLHOOK(timeval) pr_cutime;
86  struct EBLHOOK(timeval) pr_cstime;
87  struct
88  {
89    FIELD (ULONG, pr_reg[PRSTATUS_REGS_SIZE / sizeof (ULONG)]);
90  }
91#ifdef ALIGN_PR_REG
92    __attribute__ ((aligned (ALIGN_PR_REG)))
93#endif
94    ;
95  FIELD (INT, pr_fpvalid);
96};
97
98#define	FNAMESZ	16
99#define	PRARGSZ	80
100
101struct EBLHOOK(prpsinfo)
102{
103  FIELD (CHAR, pr_state);
104  FIELD (CHAR, pr_sname);
105  FIELD (CHAR, pr_zomb);
106  FIELD (CHAR, pr_nice);
107  FIELD (ULONG, pr_flag);
108  FIELD (UID_T, pr_uid);
109  FIELD (GID_T, pr_gid);
110  FIELD (PID_T, pr_pid);
111  FIELD (PID_T, pr_ppid);
112  FIELD (PID_T, pr_pgrp);
113  FIELD (PID_T, pr_sid);
114  FIELD (CHAR, pr_fname[FNAMESZ]);
115  FIELD (CHAR, pr_psargs[PRARGSZ]);
116};
117
118#undef	FIELD
119
120#define FIELD(igroup, itype, item, fmt, ...)			\
121    {								\
122      .name = #item,						\
123      .group = #igroup,					\
124      .offset = offsetof (struct EBLHOOK(prstatus), pr_##item),	\
125      .type = TYPE_##itype,					\
126      .format = fmt,						\
127      __VA_ARGS__						\
128    }
129
130static const Ebl_Core_Item prstatus_items[] =
131  {
132    FIELD (signal, INT, info.si_signo, 'd'),
133    FIELD (signal, INT, info.si_code, 'd'),
134    FIELD (signal, INT, info.si_errno, 'd'),
135    FIELD (signal, SHORT, cursig, 'd'),
136
137    /* Use different group name for a newline delimiter.  */
138    FIELD (signal2, ULONG, sigpend, 'B'),
139    FIELD (signal3, ULONG, sighold, 'B'),
140    FIELD (identity, PID_T, pid, 'd', .thread_identifier = true),
141    FIELD (identity, PID_T, ppid, 'd'),
142    FIELD (identity, PID_T, pgrp, 'd'),
143    FIELD (identity, PID_T, sid, 'd'),
144    TIMEVAL_FIELD (utime),
145    TIMEVAL_FIELD (stime),
146    TIMEVAL_FIELD (cutime),
147    TIMEVAL_FIELD (cstime),
148#ifdef PRSTATUS_REGSET_ITEMS
149    PRSTATUS_REGSET_ITEMS,
150#endif
151    FIELD (register, INT, fpvalid, 'd'),
152  };
153
154#undef	FIELD
155
156#define FIELD(igroup, itype, item, fmt, ...)			\
157    {								\
158      .name = #item,						\
159      .group = #igroup,					\
160      .offset = offsetof (struct EBLHOOK(prpsinfo), pr_##item),	\
161      .type = TYPE_##itype,					\
162      .format = fmt,						\
163      __VA_ARGS__						\
164    }
165
166static const Ebl_Core_Item prpsinfo_items[] =
167  {
168    FIELD (state, CHAR, state, 'd'),
169    FIELD (state, CHAR, sname, 'c'),
170    FIELD (state, CHAR, zomb, 'd'),
171    FIELD (state, CHAR, nice, 'd'),
172    FIELD (state, ULONG, flag, 'x'),
173    FIELD (identity, UID_T, uid, 'd'),
174    FIELD (identity, GID_T, gid, 'd'),
175    FIELD (identity, PID_T, pid, 'd'),
176    FIELD (identity, PID_T, ppid, 'd'),
177    FIELD (identity, PID_T, pgrp, 'd'),
178    FIELD (identity, PID_T, sid, 'd'),
179    FIELD (command, CHAR, fname, 's', .count = FNAMESZ),
180    FIELD (command, CHAR, psargs, 's', .count = PRARGSZ),
181  };
182
183static const Ebl_Core_Item vmcoreinfo_items[] =
184  {
185    {
186      .type = ELF_T_BYTE, .format = '\n'
187    }
188  };
189
190#undef	FIELD
191
192int
193EBLHOOK(core_note) (nhdr, name, regs_offset, nregloc, reglocs, nitems, items)
194     const GElf_Nhdr *nhdr;
195     const char *name;
196     GElf_Word *regs_offset;
197     size_t *nregloc;
198     const Ebl_Register_Location **reglocs;
199     size_t *nitems;
200     const Ebl_Core_Item **items;
201{
202  switch (nhdr->n_namesz)
203    {
204    case sizeof "CORE" - 1:	/* Buggy old Linux kernels.  */
205      if (memcmp (name, "CORE", nhdr->n_namesz) == 0)
206	break;
207      return 0;
208
209    case sizeof "CORE":
210      if (memcmp (name, "CORE", nhdr->n_namesz) == 0)
211	break;
212      /* Buggy old Linux kernels didn't terminate "LINUX".
213         Fall through.  */
214
215    case sizeof "LINUX":
216      if (memcmp (name, "LINUX", nhdr->n_namesz) == 0)
217	break;
218      return 0;
219
220    case sizeof "VMCOREINFO":
221      if (nhdr->n_type != 0
222	  || memcmp (name, "VMCOREINFO", sizeof "VMCOREINFO") != 0)
223	return 0;
224      *regs_offset = 0;
225      *nregloc = 0;
226      *nitems = 1;
227      *items = vmcoreinfo_items;
228      return 1;
229
230    default:
231      return 0;
232    }
233
234  switch (nhdr->n_type)
235    {
236    case NT_PRSTATUS:
237      if (nhdr->n_descsz != sizeof (struct EBLHOOK(prstatus)))
238	return 0;
239      *regs_offset = offsetof (struct EBLHOOK(prstatus), pr_reg);
240      *nregloc = sizeof prstatus_regs / sizeof prstatus_regs[0];
241      *reglocs = prstatus_regs;
242      *nitems = sizeof prstatus_items / sizeof prstatus_items[0];
243      *items = prstatus_items;
244      return 1;
245
246    case NT_PRPSINFO:
247      if (nhdr->n_descsz != sizeof (struct EBLHOOK(prpsinfo)))
248	return 0;
249      *regs_offset = 0;
250      *nregloc = 0;
251      *reglocs = NULL;
252      *nitems = sizeof prpsinfo_items / sizeof prpsinfo_items[0];
253      *items = prpsinfo_items;
254      return 1;
255
256#define EXTRA_REGSET(type, size, table)					      \
257    case type:								      \
258      if (nhdr->n_descsz != size)					      \
259	return 0;							      \
260      *regs_offset = 0;							      \
261      *nregloc = sizeof table / sizeof table[0];			      \
262      *reglocs = table;							      \
263      *nitems = 0;							      \
264      *items = NULL;							      \
265      return 1;
266
267#define EXTRA_REGSET_ITEMS(type, size, table, extra_items)		      \
268    case type:								      \
269      if (nhdr->n_descsz != size)					      \
270	return 0;							      \
271      *regs_offset = 0;							      \
272      *nregloc = sizeof table / sizeof table[0];			      \
273      *reglocs = table;							      \
274      *nitems = sizeof extra_items / sizeof extra_items[0];		      \
275      *items = extra_items;						      \
276      return 1;
277
278#define EXTRA_ITEMS(type, size, extra_items)				      \
279    case type:								      \
280      if (nhdr->n_descsz != size)					      \
281	return 0;							      \
282      *regs_offset = 0;							      \
283      *nregloc = 0;							      \
284      *reglocs = NULL;							      \
285      *nitems = sizeof extra_items / sizeof extra_items[0];		      \
286      *items = extra_items;						      \
287      return 1;
288
289#ifdef FPREGSET_SIZE
290    EXTRA_REGSET (NT_FPREGSET, FPREGSET_SIZE, fpregset_regs)
291#endif
292
293#ifdef EXTRA_NOTES
294    EXTRA_NOTES
295#endif
296    }
297
298  return 0;
299}
300