1*69f10b8aSclaudio /* $OpenBSD: generate.c,v 1.8 2024/10/02 12:31:33 claudio Exp $ */ 20687c322Sjasper 3192095f7Smpi /* 4192095f7Smpi * Copyright (c) 2017 Martin Pieuchot 5192095f7Smpi * 6192095f7Smpi * Permission to use, copy, modify, and distribute this software for any 7192095f7Smpi * purpose with or without fee is hereby granted, provided that the above 8192095f7Smpi * copyright notice and this permission notice appear in all copies. 9192095f7Smpi * 10192095f7Smpi * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11192095f7Smpi * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12192095f7Smpi * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13192095f7Smpi * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14192095f7Smpi * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15192095f7Smpi * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16192095f7Smpi * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17192095f7Smpi */ 18192095f7Smpi 19192095f7Smpi #include <sys/types.h> 20192095f7Smpi #include <sys/queue.h> 21192095f7Smpi #include <sys/tree.h> 22192095f7Smpi #include <sys/ctf.h> 23192095f7Smpi 24192095f7Smpi #include <assert.h> 25192095f7Smpi #include <err.h> 26192095f7Smpi #include <fcntl.h> 27192095f7Smpi #include <string.h> 28192095f7Smpi #include <stdlib.h> 29192095f7Smpi #include <stddef.h> 30192095f7Smpi #include <stdint.h> 31192095f7Smpi #include <unistd.h> 32192095f7Smpi 33192095f7Smpi #ifdef ZLIB 34192095f7Smpi #include <zlib.h> 35192095f7Smpi #endif /* ZLIB */ 36192095f7Smpi 37192095f7Smpi #include "itype.h" 38192095f7Smpi #include "xmalloc.h" 39192095f7Smpi #include "hash.h" 40192095f7Smpi 41192095f7Smpi #define ROUNDUP(x, y) ((((x) + (y) - 1) / (y)) * (y)) 42192095f7Smpi 43192095f7Smpi /* 44192095f7Smpi * Dynamic buffer, used for content & string table. 45192095f7Smpi */ 46192095f7Smpi struct dbuf { 47192095f7Smpi char *data; /* start data buffer */ 48192095f7Smpi size_t size; /* size of the buffer */ 49192095f7Smpi 50192095f7Smpi char *cptr; /* position in [data, data + size] */ 51192095f7Smpi size_t coff; /* number of written bytes */ 52192095f7Smpi }; 53192095f7Smpi 54192095f7Smpi #define DBUF_CHUNKSZ (64 * 1024) 55192095f7Smpi 56192095f7Smpi /* In-memory representation of a CTF section. */ 57192095f7Smpi struct imcs { 58192095f7Smpi struct dbuf body; 59192095f7Smpi struct dbuf stab; /* corresponding string table */ 60192095f7Smpi struct hash *htab; /* hash table of known strings */ 61192095f7Smpi }; 62192095f7Smpi 63192095f7Smpi struct strentry { 64192095f7Smpi struct hash_entry se_key; /* Must be first */ 65192095f7Smpi #define se_str se_key.hkey 66192095f7Smpi size_t se_off; 67192095f7Smpi }; 68192095f7Smpi 69192095f7Smpi #ifdef ZLIB 708d2c6414Smillert char *data_compress(const char *, size_t, size_t, size_t *); 71192095f7Smpi #endif /* ZLIB */ 72192095f7Smpi 73192095f7Smpi void 74192095f7Smpi dbuf_realloc(struct dbuf *dbuf, size_t len) 75192095f7Smpi { 76192095f7Smpi assert(dbuf != NULL); 77192095f7Smpi assert(len != 0); 78192095f7Smpi 79192095f7Smpi dbuf->data = xrealloc(dbuf->data, dbuf->size + len); 80192095f7Smpi dbuf->size += len; 81192095f7Smpi dbuf->cptr = dbuf->data + dbuf->coff; 82192095f7Smpi } 83192095f7Smpi 84192095f7Smpi void 85192095f7Smpi dbuf_copy(struct dbuf *dbuf, void const *data, size_t len) 86192095f7Smpi { 878d2c6414Smillert size_t left; 88192095f7Smpi 89192095f7Smpi assert(dbuf->cptr != NULL); 90192095f7Smpi assert(dbuf->data != NULL); 91192095f7Smpi assert(dbuf->size != 0); 92192095f7Smpi 93192095f7Smpi if (len == 0) 94192095f7Smpi return; 95192095f7Smpi 96192095f7Smpi left = dbuf->size - dbuf->coff; 978d2c6414Smillert if (left < len) 98192095f7Smpi dbuf_realloc(dbuf, ROUNDUP((len - left), DBUF_CHUNKSZ)); 99192095f7Smpi 100192095f7Smpi memcpy(dbuf->cptr, data, len); 101192095f7Smpi dbuf->cptr += len; 102192095f7Smpi dbuf->coff += len; 103192095f7Smpi } 104192095f7Smpi 105192095f7Smpi size_t 106192095f7Smpi dbuf_pad(struct dbuf *dbuf, int align) 107192095f7Smpi { 108192095f7Smpi int i = (align - (dbuf->coff % align)) % align; 109192095f7Smpi 110192095f7Smpi while (i-- > 0) 111192095f7Smpi dbuf_copy(dbuf, "", 1); 112192095f7Smpi 113192095f7Smpi return dbuf->coff; 114192095f7Smpi } 115192095f7Smpi 116192095f7Smpi size_t 117192095f7Smpi imcs_add_string(struct imcs *imcs, const char *str) 118192095f7Smpi { 119192095f7Smpi struct strentry *se; 120192095f7Smpi unsigned int slot; 121192095f7Smpi 122192095f7Smpi if (str == NULL || *str == '\0') 123192095f7Smpi return 0; 124192095f7Smpi 125192095f7Smpi se = (struct strentry *)hash_find(imcs->htab, str, &slot); 126192095f7Smpi if (se == NULL) { 127192095f7Smpi se = xmalloc(sizeof(*se)); 128192095f7Smpi hash_insert(imcs->htab, slot, &se->se_key, str); 129192095f7Smpi se->se_off = imcs->stab.coff; 130192095f7Smpi 131192095f7Smpi dbuf_copy(&imcs->stab, str, strlen(str) + 1); 132192095f7Smpi } 133192095f7Smpi 134192095f7Smpi return se->se_off; 135192095f7Smpi } 136192095f7Smpi 137192095f7Smpi void 138192095f7Smpi imcs_add_func(struct imcs *imcs, struct itype *it) 139192095f7Smpi { 140192095f7Smpi uint16_t func, arg; 141192095f7Smpi struct imember *im; 142192095f7Smpi int kind, root, vlen; 143192095f7Smpi 144192095f7Smpi vlen = it->it_nelems; 145192095f7Smpi kind = it->it_type; 146192095f7Smpi root = 0; 147192095f7Smpi 148192095f7Smpi func = (kind << 11) | (root << 10) | (vlen & CTF_MAX_VLEN); 149192095f7Smpi dbuf_copy(&imcs->body, &func, sizeof(func)); 150192095f7Smpi 151192095f7Smpi if (kind == CTF_K_UNKNOWN) 152192095f7Smpi return; 153192095f7Smpi 154192095f7Smpi func = it->it_refp->it_idx; 155192095f7Smpi dbuf_copy(&imcs->body, &func, sizeof(func)); 156192095f7Smpi 157192095f7Smpi TAILQ_FOREACH(im, &it->it_members, im_next) { 158192095f7Smpi arg = im->im_refp->it_idx; 159192095f7Smpi dbuf_copy(&imcs->body, &arg, sizeof(arg)); 160192095f7Smpi } 161192095f7Smpi } 162192095f7Smpi 163192095f7Smpi void 164192095f7Smpi imcs_add_obj(struct imcs *imcs, struct itype *it) 165192095f7Smpi { 166192095f7Smpi uint16_t type; 167192095f7Smpi 168192095f7Smpi type = it->it_refp->it_idx; 169192095f7Smpi dbuf_copy(&imcs->body, &type, sizeof(type)); 170192095f7Smpi } 171192095f7Smpi 172192095f7Smpi void 173192095f7Smpi imcs_add_type(struct imcs *imcs, struct itype *it) 174192095f7Smpi { 175192095f7Smpi struct imember *im; 176192095f7Smpi struct ctf_type ctt; 177192095f7Smpi struct ctf_array cta; 178192095f7Smpi unsigned int eob; 179192095f7Smpi uint32_t size; 180192095f7Smpi uint16_t arg; 181192095f7Smpi size_t ctsz; 182192095f7Smpi int kind, root, vlen; 183192095f7Smpi 184192095f7Smpi assert(it->it_type != CTF_K_UNKNOWN && it->it_type != CTF_K_FORWARD); 185192095f7Smpi 186192095f7Smpi vlen = it->it_nelems; 187192095f7Smpi size = it->it_size; 188192095f7Smpi kind = it->it_type; 189192095f7Smpi root = 0; 190192095f7Smpi 191192095f7Smpi ctt.ctt_name = imcs_add_string(imcs, it_name(it)); 192192095f7Smpi ctt.ctt_info = (kind << 11) | (root << 10) | (vlen & CTF_MAX_VLEN); 193192095f7Smpi 194192095f7Smpi /* Base types don't have reference, typedef & pointer don't have size */ 195192095f7Smpi if (it->it_refp != NULL && kind != CTF_K_ARRAY) { 196192095f7Smpi ctt.ctt_type = it->it_refp->it_idx; 197192095f7Smpi ctsz = sizeof(struct ctf_stype); 198192095f7Smpi } else if (size <= CTF_MAX_SIZE) { 1996bdbea77Sclaudio if (kind == CTF_K_INTEGER || kind == CTF_K_FLOAT) { 2005b0c2bfcSanton assert(size <= 128); 2016bdbea77Sclaudio if (size == 0) 2026bdbea77Sclaudio ctt.ctt_size = 0; 2036bdbea77Sclaudio else if (size <= 8) 2046bdbea77Sclaudio ctt.ctt_size = 1; 2056bdbea77Sclaudio else if (size <= 16) 2066bdbea77Sclaudio ctt.ctt_size = 2; 2076bdbea77Sclaudio else if (size <= 32) 2086bdbea77Sclaudio ctt.ctt_size = 4; 2095b0c2bfcSanton else if (size <= 64) 2106bdbea77Sclaudio ctt.ctt_size = 8; 211*69f10b8aSclaudio else if (size <= 96) 212*69f10b8aSclaudio ctt.ctt_size = 12; 2135b0c2bfcSanton else 2145b0c2bfcSanton ctt.ctt_size = 16; 2156bdbea77Sclaudio } else 216192095f7Smpi ctt.ctt_size = size; 217192095f7Smpi ctsz = sizeof(struct ctf_stype); 218192095f7Smpi } else { 219192095f7Smpi ctt.ctt_lsizehi = CTF_SIZE_TO_LSIZE_HI(size); 220192095f7Smpi ctt.ctt_lsizelo = CTF_SIZE_TO_LSIZE_LO(size); 221192095f7Smpi ctt.ctt_size = CTF_LSIZE_SENT; 222192095f7Smpi ctsz = sizeof(struct ctf_type); 223192095f7Smpi } 224192095f7Smpi 225192095f7Smpi dbuf_copy(&imcs->body, &ctt, ctsz); 226192095f7Smpi 227192095f7Smpi switch (kind) { 228192095f7Smpi assert(1 == 0); 229192095f7Smpi break; 230192095f7Smpi case CTF_K_INTEGER: 231192095f7Smpi case CTF_K_FLOAT: 232192095f7Smpi eob = CTF_INT_DATA(it->it_enc, 0, size); 233192095f7Smpi dbuf_copy(&imcs->body, &eob, sizeof(eob)); 234192095f7Smpi break; 235192095f7Smpi case CTF_K_ARRAY: 236192095f7Smpi memset(&cta, 0, sizeof(cta)); 237192095f7Smpi cta.cta_contents = it->it_refp->it_idx; 238192095f7Smpi cta.cta_index = long_tidx; 239192095f7Smpi cta.cta_nelems = it->it_nelems; 240192095f7Smpi dbuf_copy(&imcs->body, &cta, sizeof(cta)); 241192095f7Smpi break; 242192095f7Smpi case CTF_K_STRUCT: 243192095f7Smpi case CTF_K_UNION: 244192095f7Smpi if (size < CTF_LSTRUCT_THRESH) { 245192095f7Smpi struct ctf_member ctm; 246192095f7Smpi 247192095f7Smpi memset(&ctm, 0, sizeof(ctm)); 248192095f7Smpi TAILQ_FOREACH(im, &it->it_members, im_next) { 249192095f7Smpi ctm.ctm_name = 25072c906afSmpi imcs_add_string(imcs, im_name(im)); 251192095f7Smpi ctm.ctm_type = im->im_refp->it_idx; 252192095f7Smpi ctm.ctm_offset = im->im_off; 253192095f7Smpi 254192095f7Smpi dbuf_copy(&imcs->body, &ctm, sizeof(ctm)); 255192095f7Smpi } 256192095f7Smpi } else { 257192095f7Smpi struct ctf_lmember ctlm; 258192095f7Smpi 259192095f7Smpi memset(&ctlm, 0, sizeof(ctlm)); 260192095f7Smpi TAILQ_FOREACH(im, &it->it_members, im_next) { 261192095f7Smpi ctlm.ctlm_name = 26272c906afSmpi imcs_add_string(imcs, im_name(im)); 263192095f7Smpi ctlm.ctlm_type = im->im_refp->it_idx; 264192095f7Smpi ctlm.ctlm_offsethi = 265192095f7Smpi CTF_OFFSET_TO_LMEMHI(im->im_off); 266192095f7Smpi ctlm.ctlm_offsetlo = 267192095f7Smpi CTF_OFFSET_TO_LMEMLO(im->im_off); 268192095f7Smpi 269192095f7Smpi 270192095f7Smpi dbuf_copy(&imcs->body, &ctlm, sizeof(ctlm)); 271192095f7Smpi } 272192095f7Smpi } 273192095f7Smpi break; 274192095f7Smpi case CTF_K_FUNCTION: 275192095f7Smpi TAILQ_FOREACH(im, &it->it_members, im_next) { 276192095f7Smpi arg = im->im_refp->it_idx; 277192095f7Smpi dbuf_copy(&imcs->body, &arg, sizeof(arg)); 278192095f7Smpi } 279192095f7Smpi if (vlen & 1) { 280192095f7Smpi arg = 0; 281192095f7Smpi dbuf_copy(&imcs->body, &arg, sizeof(arg)); 282192095f7Smpi } 283192095f7Smpi break; 284192095f7Smpi case CTF_K_ENUM: 285192095f7Smpi TAILQ_FOREACH(im, &it->it_members, im_next) { 286192095f7Smpi struct ctf_enum cte; 287192095f7Smpi 28872c906afSmpi cte.cte_name = imcs_add_string(imcs, im_name(im)); 289192095f7Smpi cte.cte_value = im->im_ref; 290192095f7Smpi 291192095f7Smpi dbuf_copy(&imcs->body, &cte, sizeof(cte)); 292192095f7Smpi } 293192095f7Smpi break; 294192095f7Smpi case CTF_K_POINTER: 295192095f7Smpi case CTF_K_TYPEDEF: 296192095f7Smpi case CTF_K_VOLATILE: 297192095f7Smpi case CTF_K_CONST: 298192095f7Smpi case CTF_K_RESTRICT: 299192095f7Smpi default: 300192095f7Smpi break; 301192095f7Smpi } 302192095f7Smpi } 303192095f7Smpi 304192095f7Smpi void 305192095f7Smpi imcs_generate(struct imcs *imcs, struct ctf_header *cth, const char *label) 306192095f7Smpi { 307192095f7Smpi struct itype *it; 308192095f7Smpi struct ctf_lblent lbl; 309192095f7Smpi 310192095f7Smpi memset(imcs, 0, sizeof(*imcs)); 311192095f7Smpi 312192095f7Smpi dbuf_realloc(&imcs->body, DBUF_CHUNKSZ); 313192095f7Smpi dbuf_realloc(&imcs->stab, DBUF_CHUNKSZ); 314192095f7Smpi 315192095f7Smpi imcs->htab = hash_init(10); 316192095f7Smpi if (imcs->htab == NULL) 317192095f7Smpi err(1, "hash_init"); 318192095f7Smpi 319192095f7Smpi /* Add empty string */ 320192095f7Smpi dbuf_copy(&imcs->stab, "", 1); 321192095f7Smpi 322192095f7Smpi /* We don't use parent label */ 323192095f7Smpi cth->cth_parlabel = 0; 324192095f7Smpi cth->cth_parname = 0; 325192095f7Smpi 326192095f7Smpi /* Insert a single label for all types. */ 327192095f7Smpi cth->cth_lbloff = 0; 328192095f7Smpi lbl.ctl_label = imcs_add_string(imcs, label); 329192095f7Smpi lbl.ctl_typeidx = tidx; 330192095f7Smpi dbuf_copy(&imcs->body, &lbl, sizeof(lbl)); 331192095f7Smpi 332192095f7Smpi /* Insert objects */ 333192095f7Smpi cth->cth_objtoff = dbuf_pad(&imcs->body, 2); 334192095f7Smpi TAILQ_FOREACH(it, &iobjq, it_symb) 335192095f7Smpi imcs_add_obj(imcs, it); 336192095f7Smpi 337192095f7Smpi /* Insert functions */ 338192095f7Smpi cth->cth_funcoff = dbuf_pad(&imcs->body, 2); 339192095f7Smpi TAILQ_FOREACH(it, &ifuncq, it_symb) 340192095f7Smpi imcs_add_func(imcs, it); 341192095f7Smpi 342192095f7Smpi /* Insert types */ 343192095f7Smpi cth->cth_typeoff = dbuf_pad(&imcs->body, 4); 344192095f7Smpi TAILQ_FOREACH(it, &itypeq, it_next) { 345192095f7Smpi if (it->it_flags & (ITF_FUNC|ITF_OBJ)) 346192095f7Smpi continue; 347192095f7Smpi 348192095f7Smpi imcs_add_type(imcs, it); 349192095f7Smpi } 350192095f7Smpi 351192095f7Smpi /* String table is written from its own buffer. */ 352192095f7Smpi cth->cth_stroff = imcs->body.coff; 353192095f7Smpi cth->cth_strlen = imcs->stab.coff; 354192095f7Smpi } 355192095f7Smpi 356192095f7Smpi /* 357192095f7Smpi * Generate a CTF buffer from the internal type representation. 358192095f7Smpi */ 359192095f7Smpi int 360192095f7Smpi generate(const char *path, const char *label, int compress) 361192095f7Smpi { 362192095f7Smpi char *p, *ctfdata = NULL; 363192095f7Smpi ssize_t ctflen; 364192095f7Smpi struct ctf_header cth; 365192095f7Smpi struct imcs imcs; 366d998a8d9Smpi int error = 0, fd; 367192095f7Smpi 368192095f7Smpi memset(&cth, 0, sizeof(cth)); 369192095f7Smpi 370192095f7Smpi cth.cth_magic = CTF_MAGIC; 371192095f7Smpi cth.cth_version = CTF_VERSION; 372192095f7Smpi 373192095f7Smpi #ifdef ZLIB 374192095f7Smpi if (compress) 375192095f7Smpi cth.cth_flags = CTF_F_COMPRESS; 376192095f7Smpi #endif /* ZLIB */ 377192095f7Smpi 378192095f7Smpi imcs_generate(&imcs, &cth, label); 379192095f7Smpi 380192095f7Smpi ctflen = sizeof(cth) + imcs.body.coff + imcs.stab.coff; 381192095f7Smpi p = ctfdata = xmalloc(ctflen); 382192095f7Smpi 383192095f7Smpi memcpy(p, &cth, sizeof(cth)); 384192095f7Smpi p += sizeof(cth); 385192095f7Smpi 386192095f7Smpi memcpy(p, imcs.body.data, imcs.body.coff); 387192095f7Smpi p += imcs.body.coff; 388192095f7Smpi 389192095f7Smpi memcpy(p, imcs.stab.data, imcs.stab.coff); 390192095f7Smpi p += imcs.stab.coff; 391192095f7Smpi 392192095f7Smpi assert((p - ctfdata) == ctflen); 393192095f7Smpi 394192095f7Smpi #ifdef ZLIB 395192095f7Smpi if (compress) { 396192095f7Smpi char *cdata; 397192095f7Smpi size_t clen; 398192095f7Smpi 399192095f7Smpi cdata = data_compress(ctfdata + sizeof(cth), 400192095f7Smpi ctflen - sizeof(cth), ctflen - sizeof(cth), &clen); 401192095f7Smpi if (cdata == NULL) { 402192095f7Smpi warnx("compressing CTF data"); 403192095f7Smpi free(ctfdata); 404192095f7Smpi return -1; 405192095f7Smpi } 406192095f7Smpi 407192095f7Smpi memcpy(ctfdata + sizeof(cth), cdata, clen); 408192095f7Smpi ctflen = clen + sizeof(cth); 409192095f7Smpi 410192095f7Smpi free(cdata); 411192095f7Smpi } 412192095f7Smpi #endif /* ZLIB */ 413192095f7Smpi 414192095f7Smpi fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644); 415192095f7Smpi if (fd == -1) { 416192095f7Smpi warn("open %s", path); 417192095f7Smpi free(ctfdata); 418192095f7Smpi return -1; 419192095f7Smpi } 420192095f7Smpi 421192095f7Smpi if (write(fd, ctfdata, ctflen) != ctflen) { 422192095f7Smpi warn("unable to write %zd bytes for %s", ctflen, path); 423192095f7Smpi error = -1; 424192095f7Smpi } 425192095f7Smpi 426192095f7Smpi close(fd); 427192095f7Smpi free(ctfdata); 428d998a8d9Smpi return error; 429192095f7Smpi } 430192095f7Smpi 431192095f7Smpi #ifdef ZLIB 432192095f7Smpi char * 4338d2c6414Smillert data_compress(const char *buf, size_t size, size_t len, size_t *pclen) 434192095f7Smpi { 435192095f7Smpi z_stream stream; 436192095f7Smpi char *data; 437192095f7Smpi int error; 438192095f7Smpi 439192095f7Smpi data = malloc(len); 440192095f7Smpi if (data == NULL) { 441192095f7Smpi warn(NULL); 442192095f7Smpi return NULL; 443192095f7Smpi } 444192095f7Smpi 445192095f7Smpi memset(&stream, 0, sizeof(stream)); 446192095f7Smpi stream.zalloc = Z_NULL; 447192095f7Smpi stream.zfree = Z_NULL; 448192095f7Smpi stream.opaque = Z_NULL; 449192095f7Smpi 450192095f7Smpi if ((error = deflateInit(&stream, Z_BEST_COMPRESSION)) != Z_OK) { 451192095f7Smpi warnx("zlib deflateInit failed: %s", zError(error)); 452192095f7Smpi goto exit; 453192095f7Smpi } 454192095f7Smpi 455192095f7Smpi stream.next_in = (void *)buf; 456192095f7Smpi stream.avail_in = size; 457192095f7Smpi stream.next_out = (unsigned char *)data; 458192095f7Smpi stream.avail_out = len; 459192095f7Smpi 460192095f7Smpi if ((error = deflate(&stream, Z_FINISH)) != Z_STREAM_END) { 461192095f7Smpi warnx("zlib deflate failed: %s", zError(error)); 462192095f7Smpi deflateEnd(&stream); 463192095f7Smpi goto exit; 464192095f7Smpi } 465192095f7Smpi 466192095f7Smpi if ((error = deflateEnd(&stream)) != Z_OK) { 467192095f7Smpi warnx("zlib deflateEnd failed: %s", zError(error)); 468192095f7Smpi goto exit; 469192095f7Smpi } 470192095f7Smpi 471192095f7Smpi if (pclen != NULL) 472192095f7Smpi *pclen = stream.total_out; 473192095f7Smpi 474192095f7Smpi return data; 475192095f7Smpi 476192095f7Smpi exit: 477192095f7Smpi free(data); 478192095f7Smpi return NULL; 479192095f7Smpi } 480192095f7Smpi #endif /* ZLIB */ 481