xref: /openbsd-src/usr.bin/ctfconv/generate.c (revision 69f10b8ad3e2cd417c7a567d64217c86e2699f4d)
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