1/*	$OpenBSD: generate.c,v 1.7 2024/02/27 06:58:19 anton Exp $ */
2
3/*
4 * Copyright (c) 2017 Martin Pieuchot
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19#include <sys/types.h>
20#include <sys/queue.h>
21#include <sys/tree.h>
22#include <sys/ctf.h>
23
24#include <assert.h>
25#include <err.h>
26#include <fcntl.h>
27#include <string.h>
28#include <stdlib.h>
29#include <stddef.h>
30#include <stdint.h>
31#include <unistd.h>
32
33#ifdef ZLIB
34#include <zlib.h>
35#endif /* ZLIB */
36
37#include "itype.h"
38#include "xmalloc.h"
39#include "hash.h"
40
41#define ROUNDUP(x, y) ((((x) + (y) - 1) / (y)) * (y))
42
43/*
44 * Dynamic buffer, used for content & string table.
45 */
46struct dbuf {
47	char		*data;	/* start data buffer */
48	size_t		 size;	/* size of the buffer */
49
50	char		*cptr; /* position in [data, data + size] */
51	size_t		 coff; /* number of written bytes */
52};
53
54#define DBUF_CHUNKSZ	(64 * 1024)
55
56/* In-memory representation of a CTF section. */
57struct imcs {
58	struct dbuf	 body;
59	struct dbuf	 stab;	/* corresponding string table */
60	struct hash	*htab;	/* hash table of known strings */
61};
62
63struct strentry {
64	struct hash_entry se_key;	/* Must be first */
65#define se_str se_key.hkey
66	size_t		 se_off;
67};
68
69#ifdef ZLIB
70char		*data_compress(const char *, size_t, size_t, size_t *);
71#endif /* ZLIB */
72
73void
74dbuf_realloc(struct dbuf *dbuf, size_t len)
75{
76	assert(dbuf != NULL);
77	assert(len != 0);
78
79	dbuf->data = xrealloc(dbuf->data, dbuf->size + len);
80	dbuf->size += len;
81	dbuf->cptr = dbuf->data + dbuf->coff;
82}
83
84void
85dbuf_copy(struct dbuf *dbuf, void const *data, size_t len)
86{
87	size_t left;
88
89	assert(dbuf->cptr != NULL);
90	assert(dbuf->data != NULL);
91	assert(dbuf->size != 0);
92
93	if (len == 0)
94		return;
95
96	left = dbuf->size - dbuf->coff;
97	if (left < len)
98		dbuf_realloc(dbuf, ROUNDUP((len - left), DBUF_CHUNKSZ));
99
100	memcpy(dbuf->cptr, data, len);
101	dbuf->cptr += len;
102	dbuf->coff += len;
103}
104
105size_t
106dbuf_pad(struct dbuf *dbuf, int align)
107{
108	int i = (align - (dbuf->coff % align)) % align;
109
110	while (i-- > 0)
111		dbuf_copy(dbuf, "", 1);
112
113	return dbuf->coff;
114}
115
116size_t
117imcs_add_string(struct imcs *imcs, const char *str)
118{
119	struct strentry *se;
120	unsigned int slot;
121
122	if (str == NULL || *str == '\0')
123		return 0;
124
125	se = (struct strentry *)hash_find(imcs->htab, str, &slot);
126	if (se == NULL) {
127		se = xmalloc(sizeof(*se));
128		hash_insert(imcs->htab, slot, &se->se_key, str);
129		se->se_off = imcs->stab.coff;
130
131		dbuf_copy(&imcs->stab, str, strlen(str) + 1);
132	}
133
134	return se->se_off;
135}
136
137void
138imcs_add_func(struct imcs *imcs, struct itype *it)
139{
140	uint16_t		 func, arg;
141	struct imember		*im;
142	int			 kind, root, vlen;
143
144	vlen = it->it_nelems;
145	kind = it->it_type;
146	root = 0;
147
148	func = (kind << 11) | (root << 10) | (vlen & CTF_MAX_VLEN);
149	dbuf_copy(&imcs->body, &func, sizeof(func));
150
151	if (kind == CTF_K_UNKNOWN)
152		return;
153
154	func = it->it_refp->it_idx;
155	dbuf_copy(&imcs->body, &func, sizeof(func));
156
157	TAILQ_FOREACH(im, &it->it_members, im_next) {
158		arg = im->im_refp->it_idx;
159		dbuf_copy(&imcs->body, &arg, sizeof(arg));
160	}
161}
162
163void
164imcs_add_obj(struct imcs *imcs, struct itype *it)
165{
166	uint16_t		 type;
167
168	type = it->it_refp->it_idx;
169	dbuf_copy(&imcs->body, &type, sizeof(type));
170}
171
172void
173imcs_add_type(struct imcs *imcs, struct itype *it)
174{
175	struct imember		*im;
176	struct ctf_type		 ctt;
177	struct ctf_array	 cta;
178	unsigned int		 eob;
179	uint32_t		 size;
180	uint16_t		 arg;
181	size_t			 ctsz;
182	int			 kind, root, vlen;
183
184	assert(it->it_type != CTF_K_UNKNOWN && it->it_type != CTF_K_FORWARD);
185
186	vlen = it->it_nelems;
187	size = it->it_size;
188	kind = it->it_type;
189	root = 0;
190
191	ctt.ctt_name = imcs_add_string(imcs, it_name(it));
192	ctt.ctt_info = (kind << 11) | (root << 10) | (vlen & CTF_MAX_VLEN);
193
194	/* Base types don't have reference, typedef & pointer don't have size */
195	if (it->it_refp != NULL && kind != CTF_K_ARRAY) {
196		ctt.ctt_type = it->it_refp->it_idx;
197		ctsz = sizeof(struct ctf_stype);
198	} else if (size <= CTF_MAX_SIZE) {
199		if (kind == CTF_K_INTEGER || kind == CTF_K_FLOAT) {
200			assert(size <= 128);
201			if (size == 0)
202				ctt.ctt_size = 0;
203			else if (size <= 8)
204				ctt.ctt_size = 1;
205			else if (size <= 16)
206				ctt.ctt_size = 2;
207			else if (size <= 32)
208				ctt.ctt_size = 4;
209			else if (size <= 64)
210				ctt.ctt_size = 8;
211			else
212				ctt.ctt_size = 16;
213		} else
214			ctt.ctt_size = size;
215		ctsz = sizeof(struct ctf_stype);
216	} else {
217		ctt.ctt_lsizehi = CTF_SIZE_TO_LSIZE_HI(size);
218		ctt.ctt_lsizelo = CTF_SIZE_TO_LSIZE_LO(size);
219		ctt.ctt_size = CTF_LSIZE_SENT;
220		ctsz = sizeof(struct ctf_type);
221	}
222
223	dbuf_copy(&imcs->body, &ctt, ctsz);
224
225	switch (kind) {
226		assert(1 == 0);
227		break;
228	case CTF_K_INTEGER:
229	case CTF_K_FLOAT:
230		eob = CTF_INT_DATA(it->it_enc, 0, size);
231		dbuf_copy(&imcs->body, &eob, sizeof(eob));
232		break;
233	case CTF_K_ARRAY:
234		memset(&cta, 0, sizeof(cta));
235		cta.cta_contents = it->it_refp->it_idx;
236		cta.cta_index = long_tidx;
237		cta.cta_nelems = it->it_nelems;
238		dbuf_copy(&imcs->body, &cta, sizeof(cta));
239		break;
240	case CTF_K_STRUCT:
241	case CTF_K_UNION:
242		if (size < CTF_LSTRUCT_THRESH) {
243			struct ctf_member	 ctm;
244
245			memset(&ctm, 0, sizeof(ctm));
246			TAILQ_FOREACH(im, &it->it_members, im_next) {
247				ctm.ctm_name =
248				    imcs_add_string(imcs, im_name(im));
249				ctm.ctm_type = im->im_refp->it_idx;
250				ctm.ctm_offset = im->im_off;
251
252				dbuf_copy(&imcs->body, &ctm, sizeof(ctm));
253			}
254		} else {
255			struct ctf_lmember	 ctlm;
256
257			memset(&ctlm, 0, sizeof(ctlm));
258			TAILQ_FOREACH(im, &it->it_members, im_next) {
259				ctlm.ctlm_name =
260				    imcs_add_string(imcs, im_name(im));
261				ctlm.ctlm_type = im->im_refp->it_idx;
262				ctlm.ctlm_offsethi =
263				    CTF_OFFSET_TO_LMEMHI(im->im_off);
264				ctlm.ctlm_offsetlo =
265				    CTF_OFFSET_TO_LMEMLO(im->im_off);
266
267
268				dbuf_copy(&imcs->body, &ctlm, sizeof(ctlm));
269			}
270		}
271		break;
272	case CTF_K_FUNCTION:
273		TAILQ_FOREACH(im, &it->it_members, im_next) {
274			arg = im->im_refp->it_idx;
275			dbuf_copy(&imcs->body, &arg, sizeof(arg));
276		}
277		if (vlen & 1) {
278			arg = 0;
279			dbuf_copy(&imcs->body, &arg, sizeof(arg));
280		}
281		break;
282	case CTF_K_ENUM:
283		TAILQ_FOREACH(im, &it->it_members, im_next) {
284			struct ctf_enum	cte;
285
286			cte.cte_name = imcs_add_string(imcs, im_name(im));
287			cte.cte_value = im->im_ref;
288
289			dbuf_copy(&imcs->body, &cte, sizeof(cte));
290		}
291		break;
292	case CTF_K_POINTER:
293	case CTF_K_TYPEDEF:
294	case CTF_K_VOLATILE:
295	case CTF_K_CONST:
296	case CTF_K_RESTRICT:
297	default:
298		break;
299	}
300}
301
302void
303imcs_generate(struct imcs *imcs, struct ctf_header *cth, const char *label)
304{
305	struct itype		*it;
306	struct ctf_lblent	 lbl;
307
308	memset(imcs, 0, sizeof(*imcs));
309
310	dbuf_realloc(&imcs->body, DBUF_CHUNKSZ);
311	dbuf_realloc(&imcs->stab, DBUF_CHUNKSZ);
312
313	imcs->htab = hash_init(10);
314	if (imcs->htab == NULL)
315		err(1, "hash_init");
316
317	/* Add empty string */
318	dbuf_copy(&imcs->stab, "", 1);
319
320	/* We don't use parent label */
321	cth->cth_parlabel = 0;
322	cth->cth_parname = 0;
323
324	/* Insert a single label for all types. */
325	cth->cth_lbloff = 0;
326	lbl.ctl_label = imcs_add_string(imcs, label);
327	lbl.ctl_typeidx = tidx;
328	dbuf_copy(&imcs->body, &lbl, sizeof(lbl));
329
330	/* Insert objects */
331	cth->cth_objtoff = dbuf_pad(&imcs->body, 2);
332	TAILQ_FOREACH(it, &iobjq, it_symb)
333		imcs_add_obj(imcs, it);
334
335	/* Insert functions */
336	cth->cth_funcoff = dbuf_pad(&imcs->body, 2);
337	TAILQ_FOREACH(it, &ifuncq, it_symb)
338		imcs_add_func(imcs, it);
339
340	/* Insert types */
341	cth->cth_typeoff = dbuf_pad(&imcs->body, 4);
342	TAILQ_FOREACH(it, &itypeq, it_next) {
343		if (it->it_flags & (ITF_FUNC|ITF_OBJ))
344			continue;
345
346		imcs_add_type(imcs, it);
347	}
348
349	/* String table is written from its own buffer. */
350	cth->cth_stroff = imcs->body.coff;
351	cth->cth_strlen = imcs->stab.coff;
352}
353
354/*
355 * Generate a CTF buffer from the internal type representation.
356 */
357int
358generate(const char *path, const char *label, int compress)
359{
360	char			*p, *ctfdata = NULL;
361	ssize_t			 ctflen;
362	struct ctf_header	 cth;
363	struct imcs		 imcs;
364	int			 error = 0, fd;
365
366	memset(&cth, 0, sizeof(cth));
367
368	cth.cth_magic = CTF_MAGIC;
369	cth.cth_version = CTF_VERSION;
370
371#ifdef ZLIB
372	if (compress)
373		cth.cth_flags = CTF_F_COMPRESS;
374#endif /* ZLIB */
375
376	imcs_generate(&imcs, &cth, label);
377
378	ctflen = sizeof(cth) + imcs.body.coff + imcs.stab.coff;
379	p = ctfdata = xmalloc(ctflen);
380
381	memcpy(p, &cth, sizeof(cth));
382	p += sizeof(cth);
383
384	memcpy(p, imcs.body.data, imcs.body.coff);
385	p += imcs.body.coff;
386
387	memcpy(p, imcs.stab.data, imcs.stab.coff);
388	p += imcs.stab.coff;
389
390	assert((p - ctfdata) == ctflen);
391
392#ifdef ZLIB
393	if (compress) {
394		char *cdata;
395		size_t clen;
396
397		cdata = data_compress(ctfdata + sizeof(cth),
398		    ctflen - sizeof(cth), ctflen - sizeof(cth), &clen);
399		if (cdata == NULL) {
400			warnx("compressing CTF data");
401			free(ctfdata);
402			return -1;
403		}
404
405		memcpy(ctfdata + sizeof(cth), cdata, clen);
406		ctflen = clen + sizeof(cth);
407
408		free(cdata);
409	}
410#endif /* ZLIB */
411
412	fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
413	if (fd == -1) {
414		warn("open %s", path);
415		free(ctfdata);
416		return -1;
417	}
418
419	if (write(fd, ctfdata, ctflen) != ctflen) {
420		warn("unable to write %zd bytes for %s", ctflen, path);
421		error = -1;
422	}
423
424	close(fd);
425	free(ctfdata);
426	return error;
427}
428
429#ifdef ZLIB
430char *
431data_compress(const char *buf, size_t size, size_t len, size_t *pclen)
432{
433	z_stream		 stream;
434	char			*data;
435	int			 error;
436
437	data = malloc(len);
438	if (data == NULL) {
439		warn(NULL);
440		return NULL;
441	}
442
443	memset(&stream, 0, sizeof(stream));
444	stream.zalloc = Z_NULL;
445	stream.zfree = Z_NULL;
446	stream.opaque = Z_NULL;
447
448	if ((error = deflateInit(&stream, Z_BEST_COMPRESSION)) != Z_OK) {
449		warnx("zlib deflateInit failed: %s", zError(error));
450		goto exit;
451	}
452
453	stream.next_in = (void *)buf;
454	stream.avail_in = size;
455	stream.next_out = (unsigned char *)data;
456	stream.avail_out = len;
457
458	if ((error = deflate(&stream, Z_FINISH)) != Z_STREAM_END) {
459		warnx("zlib deflate failed: %s", zError(error));
460		deflateEnd(&stream);
461		goto exit;
462	}
463
464	if ((error = deflateEnd(&stream)) != Z_OK) {
465		warnx("zlib deflateEnd failed: %s", zError(error));
466		goto exit;
467	}
468
469	if (pclen != NULL)
470		*pclen = stream.total_out;
471
472	return data;
473
474exit:
475	free(data);
476	return NULL;
477}
478#endif /* ZLIB */
479