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