core.c revision 249672
1/*-
2 * Copyright (c) 2013 Mikolaj Golub <trociny@FreeBSD.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 * $FreeBSD: head/lib/libprocstat/core.c 249672 2013-04-20 07:57:08Z trociny $
27 */
28
29#include <sys/param.h>
30#include <sys/elf.h>
31#include <sys/user.h>
32
33#include <assert.h>
34#include <err.h>
35#include <fcntl.h>
36#include <gelf.h>
37#include <libelf.h>
38#include <stdbool.h>
39#include <stdint.h>
40#include <stdio.h>
41#include <stdlib.h>
42#include <string.h>
43#include <unistd.h>
44
45#include "core.h"
46
47#define PROCSTAT_CORE_MAGIC	0x012DADB8
48struct procstat_core
49{
50	int		pc_magic;
51	int		pc_fd;
52	Elf		*pc_elf;
53	GElf_Ehdr	pc_ehdr;
54	GElf_Phdr	pc_phdr;
55};
56
57static bool	core_offset(struct procstat_core *core, off_t offset);
58static bool	core_read(struct procstat_core *core, void *buf, size_t len);
59
60struct procstat_core *
61procstat_core_open(const char *filename)
62{
63	struct procstat_core *core;
64	Elf *e;
65	GElf_Ehdr ehdr;
66	GElf_Phdr phdr;
67	size_t nph;
68	int fd, i;
69
70	if (elf_version(EV_CURRENT) == EV_NONE) {
71		warnx("ELF library too old");
72		return (NULL);
73	}
74	fd = open(filename, O_RDONLY, 0);
75	if (fd == -1) {
76		warn("open(%s)", filename);
77		return (NULL);
78	}
79	e = elf_begin(fd, ELF_C_READ, NULL);
80	if (e == NULL) {
81		warnx("elf_begin: %s", elf_errmsg(-1));
82		goto fail;
83	}
84	if (elf_kind(e) != ELF_K_ELF) {
85		warnx("%s is not an ELF object", filename);
86		goto fail;
87	}
88	if (gelf_getehdr(e, &ehdr) == NULL) {
89		warnx("gelf_getehdr: %s", elf_errmsg(-1));
90		goto fail;
91	}
92	if (ehdr.e_type != ET_CORE) {
93		warnx("%s is not a CORE file", filename);
94		goto fail;
95	}
96	if (elf_getphnum(e, &nph) == 0) {
97		warnx("program headers not found");
98		goto fail;
99	}
100	for (i = 0; i < ehdr.e_phnum; i++) {
101		if (gelf_getphdr(e, i, &phdr) != &phdr) {
102			warnx("gelf_getphdr: %s", elf_errmsg(-1));
103			goto fail;
104		}
105		if (phdr.p_type == PT_NOTE)
106			break;
107	}
108	if (i == ehdr.e_phnum) {
109		warnx("NOTE program header not found");
110		goto fail;
111	}
112	core = malloc(sizeof(struct procstat_core));
113	if (core == NULL) {
114		warn("malloc(%zu)", sizeof(struct procstat_core));
115		goto fail;
116	}
117	core->pc_magic = PROCSTAT_CORE_MAGIC;
118	core->pc_fd = fd;
119	core->pc_elf = e;
120	core->pc_ehdr = ehdr;
121	core->pc_phdr = phdr;
122
123	return (core);
124fail:
125	if (e != NULL)
126		elf_end(e);
127	close(fd);
128
129	return (NULL);
130}
131
132void
133procstat_core_close(struct procstat_core *core)
134{
135
136	assert(core->pc_magic == PROCSTAT_CORE_MAGIC);
137
138	elf_end(core->pc_elf);
139	close(core->pc_fd);
140	free(core);
141}
142
143void *
144procstat_core_get(struct procstat_core *core, enum psc_type type, void *buf,
145    size_t *lenp)
146{
147	Elf_Note nhdr;
148	off_t offset, eoffset;
149	void *freebuf;
150	size_t len;
151	u_int32_t n_type;
152	int cstructsize, structsize;
153	char nbuf[8];
154
155	assert(core->pc_magic == PROCSTAT_CORE_MAGIC);
156
157	switch(type) {
158	case PSC_TYPE_PROC:
159		n_type = NT_PROCSTAT_PROC;
160		structsize = sizeof(struct kinfo_proc);
161		break;
162	case PSC_TYPE_FILES:
163		n_type = NT_PROCSTAT_FILES;
164		structsize = sizeof(struct kinfo_file);
165		break;
166	case PSC_TYPE_VMMAP:
167		n_type = NT_PROCSTAT_VMMAP;
168		structsize = sizeof(struct kinfo_vmentry);
169		break;
170	case PSC_TYPE_GROUPS:
171		n_type = NT_PROCSTAT_GROUPS;
172		structsize = sizeof(gid_t);
173		break;
174	case PSC_TYPE_UMASK:
175		n_type = NT_PROCSTAT_UMASK;
176		structsize = sizeof(u_short);
177		break;
178	default:
179		warnx("unknown core stat type: %d", type);
180		return (NULL);
181	}
182
183	offset = core->pc_phdr.p_offset;
184	eoffset = offset + core->pc_phdr.p_filesz;
185
186	while (offset < eoffset) {
187		if (!core_offset(core, offset))
188			return (NULL);
189		if (!core_read(core, &nhdr, sizeof(nhdr)))
190			return (NULL);
191
192		offset += sizeof(nhdr) +
193		    roundup2(nhdr.n_namesz, sizeof(Elf32_Size)) +
194		    roundup2(nhdr.n_descsz, sizeof(Elf32_Size));
195
196		if (nhdr.n_namesz == 0 && nhdr.n_descsz == 0)
197			break;
198		if (nhdr.n_type != n_type)
199			continue;
200		if (nhdr.n_namesz != 8)
201			continue;
202		if (!core_read(core, nbuf, sizeof(nbuf)))
203			return (NULL);
204		if (strcmp(nbuf, "FreeBSD") != 0)
205			continue;
206		if (nhdr.n_descsz < sizeof(cstructsize)) {
207			warnx("corrupted core file");
208			return (NULL);
209		}
210		if (!core_read(core, &cstructsize, sizeof(cstructsize)))
211			return (NULL);
212		if (cstructsize != structsize) {
213			warnx("version mismatch");
214			return (NULL);
215		}
216		len = nhdr.n_descsz - sizeof(cstructsize);
217		if (len == 0)
218			return (NULL);
219		if (buf != NULL) {
220			len = MIN(len, *lenp);
221			freebuf = NULL;
222		} else {
223			freebuf = buf = malloc(len);
224			if (buf == NULL) {
225				warn("malloc(%zu)", len);
226				return (NULL);
227			}
228		}
229		if (!core_read(core, buf, len)) {
230			free(freebuf);
231			return (NULL);
232		}
233		*lenp = len;
234		return (buf);
235        }
236
237	return (NULL);
238}
239
240static bool
241core_offset(struct procstat_core *core, off_t offset)
242{
243
244	assert(core->pc_magic == PROCSTAT_CORE_MAGIC);
245
246	if (lseek(core->pc_fd, offset, SEEK_SET) == -1) {
247		warn("core: lseek(%jd)", (intmax_t)offset);
248		return (false);
249	}
250	return (true);
251}
252
253static bool
254core_read(struct procstat_core *core, void *buf, size_t len)
255{
256	ssize_t n;
257
258	assert(core->pc_magic == PROCSTAT_CORE_MAGIC);
259
260	n = read(core->pc_fd, buf, len);
261	if (n == -1) {
262		warn("core: read");
263		return (false);
264	}
265	if (n < (ssize_t)len) {
266		warnx("core: short read");
267		return (false);
268	}
269	return (true);
270}
271