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