xref: /openbsd-src/usr.bin/ctfconv/generate.c (revision 7c0ec4b8992567abb1e1536622dc789a9a39d9f1)
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  */
46 struct 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. */
57 struct imcs {
58 	struct dbuf	 body;
59 	struct dbuf	 stab;	/* corresponding string table */
60 	struct hash	*htab;	/* hash table of known strings */
61 };
62 
63 struct 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
70 char		*data_compress(const char *, size_t, size_t, size_t *);
71 #endif /* ZLIB */
72 
73 void
74 dbuf_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 
84 void
85 dbuf_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 
105 size_t
106 dbuf_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 
116 size_t
117 imcs_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 
137 void
138 imcs_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 
163 void
164 imcs_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 
172 void
173 imcs_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 
302 void
303 imcs_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  */
357 int
358 generate(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
430 char *
431 data_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 
474 exit:
475 	free(data);
476 	return NULL;
477 }
478 #endif /* ZLIB */
479