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 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 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 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 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 * 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