xref: /openbsd/usr.bin/ctfconv/generate.c (revision 69f10b8a)
1 /*	$OpenBSD: generate.c,v 1.8 2024/10/02 12:31:33 claudio 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
dbuf_realloc(struct dbuf * dbuf,size_t len)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
dbuf_copy(struct dbuf * dbuf,void const * data,size_t len)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
dbuf_pad(struct dbuf * dbuf,int align)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
imcs_add_string(struct imcs * imcs,const char * str)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
imcs_add_func(struct imcs * imcs,struct itype * it)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
imcs_add_obj(struct imcs * imcs,struct itype * it)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
imcs_add_type(struct imcs * imcs,struct itype * it)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 if (size <= 96)
212 				ctt.ctt_size = 12;
213 			else
214 				ctt.ctt_size = 16;
215 		} else
216 			ctt.ctt_size = size;
217 		ctsz = sizeof(struct ctf_stype);
218 	} else {
219 		ctt.ctt_lsizehi = CTF_SIZE_TO_LSIZE_HI(size);
220 		ctt.ctt_lsizelo = CTF_SIZE_TO_LSIZE_LO(size);
221 		ctt.ctt_size = CTF_LSIZE_SENT;
222 		ctsz = sizeof(struct ctf_type);
223 	}
224 
225 	dbuf_copy(&imcs->body, &ctt, ctsz);
226 
227 	switch (kind) {
228 		assert(1 == 0);
229 		break;
230 	case CTF_K_INTEGER:
231 	case CTF_K_FLOAT:
232 		eob = CTF_INT_DATA(it->it_enc, 0, size);
233 		dbuf_copy(&imcs->body, &eob, sizeof(eob));
234 		break;
235 	case CTF_K_ARRAY:
236 		memset(&cta, 0, sizeof(cta));
237 		cta.cta_contents = it->it_refp->it_idx;
238 		cta.cta_index = long_tidx;
239 		cta.cta_nelems = it->it_nelems;
240 		dbuf_copy(&imcs->body, &cta, sizeof(cta));
241 		break;
242 	case CTF_K_STRUCT:
243 	case CTF_K_UNION:
244 		if (size < CTF_LSTRUCT_THRESH) {
245 			struct ctf_member	 ctm;
246 
247 			memset(&ctm, 0, sizeof(ctm));
248 			TAILQ_FOREACH(im, &it->it_members, im_next) {
249 				ctm.ctm_name =
250 				    imcs_add_string(imcs, im_name(im));
251 				ctm.ctm_type = im->im_refp->it_idx;
252 				ctm.ctm_offset = im->im_off;
253 
254 				dbuf_copy(&imcs->body, &ctm, sizeof(ctm));
255 			}
256 		} else {
257 			struct ctf_lmember	 ctlm;
258 
259 			memset(&ctlm, 0, sizeof(ctlm));
260 			TAILQ_FOREACH(im, &it->it_members, im_next) {
261 				ctlm.ctlm_name =
262 				    imcs_add_string(imcs, im_name(im));
263 				ctlm.ctlm_type = im->im_refp->it_idx;
264 				ctlm.ctlm_offsethi =
265 				    CTF_OFFSET_TO_LMEMHI(im->im_off);
266 				ctlm.ctlm_offsetlo =
267 				    CTF_OFFSET_TO_LMEMLO(im->im_off);
268 
269 
270 				dbuf_copy(&imcs->body, &ctlm, sizeof(ctlm));
271 			}
272 		}
273 		break;
274 	case CTF_K_FUNCTION:
275 		TAILQ_FOREACH(im, &it->it_members, im_next) {
276 			arg = im->im_refp->it_idx;
277 			dbuf_copy(&imcs->body, &arg, sizeof(arg));
278 		}
279 		if (vlen & 1) {
280 			arg = 0;
281 			dbuf_copy(&imcs->body, &arg, sizeof(arg));
282 		}
283 		break;
284 	case CTF_K_ENUM:
285 		TAILQ_FOREACH(im, &it->it_members, im_next) {
286 			struct ctf_enum	cte;
287 
288 			cte.cte_name = imcs_add_string(imcs, im_name(im));
289 			cte.cte_value = im->im_ref;
290 
291 			dbuf_copy(&imcs->body, &cte, sizeof(cte));
292 		}
293 		break;
294 	case CTF_K_POINTER:
295 	case CTF_K_TYPEDEF:
296 	case CTF_K_VOLATILE:
297 	case CTF_K_CONST:
298 	case CTF_K_RESTRICT:
299 	default:
300 		break;
301 	}
302 }
303 
304 void
imcs_generate(struct imcs * imcs,struct ctf_header * cth,const char * label)305 imcs_generate(struct imcs *imcs, struct ctf_header *cth, const char *label)
306 {
307 	struct itype		*it;
308 	struct ctf_lblent	 lbl;
309 
310 	memset(imcs, 0, sizeof(*imcs));
311 
312 	dbuf_realloc(&imcs->body, DBUF_CHUNKSZ);
313 	dbuf_realloc(&imcs->stab, DBUF_CHUNKSZ);
314 
315 	imcs->htab = hash_init(10);
316 	if (imcs->htab == NULL)
317 		err(1, "hash_init");
318 
319 	/* Add empty string */
320 	dbuf_copy(&imcs->stab, "", 1);
321 
322 	/* We don't use parent label */
323 	cth->cth_parlabel = 0;
324 	cth->cth_parname = 0;
325 
326 	/* Insert a single label for all types. */
327 	cth->cth_lbloff = 0;
328 	lbl.ctl_label = imcs_add_string(imcs, label);
329 	lbl.ctl_typeidx = tidx;
330 	dbuf_copy(&imcs->body, &lbl, sizeof(lbl));
331 
332 	/* Insert objects */
333 	cth->cth_objtoff = dbuf_pad(&imcs->body, 2);
334 	TAILQ_FOREACH(it, &iobjq, it_symb)
335 		imcs_add_obj(imcs, it);
336 
337 	/* Insert functions */
338 	cth->cth_funcoff = dbuf_pad(&imcs->body, 2);
339 	TAILQ_FOREACH(it, &ifuncq, it_symb)
340 		imcs_add_func(imcs, it);
341 
342 	/* Insert types */
343 	cth->cth_typeoff = dbuf_pad(&imcs->body, 4);
344 	TAILQ_FOREACH(it, &itypeq, it_next) {
345 		if (it->it_flags & (ITF_FUNC|ITF_OBJ))
346 			continue;
347 
348 		imcs_add_type(imcs, it);
349 	}
350 
351 	/* String table is written from its own buffer. */
352 	cth->cth_stroff = imcs->body.coff;
353 	cth->cth_strlen = imcs->stab.coff;
354 }
355 
356 /*
357  * Generate a CTF buffer from the internal type representation.
358  */
359 int
generate(const char * path,const char * label,int compress)360 generate(const char *path, const char *label, int compress)
361 {
362 	char			*p, *ctfdata = NULL;
363 	ssize_t			 ctflen;
364 	struct ctf_header	 cth;
365 	struct imcs		 imcs;
366 	int			 error = 0, fd;
367 
368 	memset(&cth, 0, sizeof(cth));
369 
370 	cth.cth_magic = CTF_MAGIC;
371 	cth.cth_version = CTF_VERSION;
372 
373 #ifdef ZLIB
374 	if (compress)
375 		cth.cth_flags = CTF_F_COMPRESS;
376 #endif /* ZLIB */
377 
378 	imcs_generate(&imcs, &cth, label);
379 
380 	ctflen = sizeof(cth) + imcs.body.coff + imcs.stab.coff;
381 	p = ctfdata = xmalloc(ctflen);
382 
383 	memcpy(p, &cth, sizeof(cth));
384 	p += sizeof(cth);
385 
386 	memcpy(p, imcs.body.data, imcs.body.coff);
387 	p += imcs.body.coff;
388 
389 	memcpy(p, imcs.stab.data, imcs.stab.coff);
390 	p += imcs.stab.coff;
391 
392 	assert((p - ctfdata) == ctflen);
393 
394 #ifdef ZLIB
395 	if (compress) {
396 		char *cdata;
397 		size_t clen;
398 
399 		cdata = data_compress(ctfdata + sizeof(cth),
400 		    ctflen - sizeof(cth), ctflen - sizeof(cth), &clen);
401 		if (cdata == NULL) {
402 			warnx("compressing CTF data");
403 			free(ctfdata);
404 			return -1;
405 		}
406 
407 		memcpy(ctfdata + sizeof(cth), cdata, clen);
408 		ctflen = clen + sizeof(cth);
409 
410 		free(cdata);
411 	}
412 #endif /* ZLIB */
413 
414 	fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
415 	if (fd == -1) {
416 		warn("open %s", path);
417 		free(ctfdata);
418 		return -1;
419 	}
420 
421 	if (write(fd, ctfdata, ctflen) != ctflen) {
422 		warn("unable to write %zd bytes for %s", ctflen, path);
423 		error = -1;
424 	}
425 
426 	close(fd);
427 	free(ctfdata);
428 	return error;
429 }
430 
431 #ifdef ZLIB
432 char *
data_compress(const char * buf,size_t size,size_t len,size_t * pclen)433 data_compress(const char *buf, size_t size, size_t len, size_t *pclen)
434 {
435 	z_stream		 stream;
436 	char			*data;
437 	int			 error;
438 
439 	data = malloc(len);
440 	if (data == NULL) {
441 		warn(NULL);
442 		return NULL;
443 	}
444 
445 	memset(&stream, 0, sizeof(stream));
446 	stream.zalloc = Z_NULL;
447 	stream.zfree = Z_NULL;
448 	stream.opaque = Z_NULL;
449 
450 	if ((error = deflateInit(&stream, Z_BEST_COMPRESSION)) != Z_OK) {
451 		warnx("zlib deflateInit failed: %s", zError(error));
452 		goto exit;
453 	}
454 
455 	stream.next_in = (void *)buf;
456 	stream.avail_in = size;
457 	stream.next_out = (unsigned char *)data;
458 	stream.avail_out = len;
459 
460 	if ((error = deflate(&stream, Z_FINISH)) != Z_STREAM_END) {
461 		warnx("zlib deflate failed: %s", zError(error));
462 		deflateEnd(&stream);
463 		goto exit;
464 	}
465 
466 	if ((error = deflateEnd(&stream)) != Z_OK) {
467 		warnx("zlib deflateEnd failed: %s", zError(error));
468 		goto exit;
469 	}
470 
471 	if (pclen != NULL)
472 		*pclen = stream.total_out;
473 
474 	return data;
475 
476 exit:
477 	free(data);
478 	return NULL;
479 }
480 #endif /* ZLIB */
481