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