Edit

IABSD.fr/src/usr.bin/ctfconv/generate.c

Branch :

  • Show log

    Commit

  • Author : claudio
    Date : 2024-10-02 12:31:33
    Hash : 69f10b8a
    Message : On i386 long double is 80bit expanded to 96bits or 12 bytes. This is the size that the ctftools ctfconvert uses and I think we should do as well. Fixes regress/usr.bin/ctfdump on i386. OK miod@

  • usr.bin/ctfconv/generate.c
  • /*	$OpenBSD: generate.c,v 1.8 2024/10/02 12:31:33 claudio Exp $ */
    
    /*
     * Copyright (c) 2017 Martin Pieuchot
     *
     * Permission to use, copy, modify, and distribute this software for any
     * purpose with or without fee is hereby granted, provided that the above
     * copyright notice and this permission notice appear in all copies.
     *
     * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     */
    
    #include <sys/types.h>
    #include <sys/queue.h>
    #include <sys/tree.h>
    #include <sys/ctf.h>
    
    #include <assert.h>
    #include <err.h>
    #include <fcntl.h>
    #include <string.h>
    #include <stdlib.h>
    #include <stddef.h>
    #include <stdint.h>
    #include <unistd.h>
    
    #ifdef ZLIB
    #include <zlib.h>
    #endif /* ZLIB */
    
    #include "itype.h"
    #include "xmalloc.h"
    #include "hash.h"
    
    #define ROUNDUP(x, y) ((((x) + (y) - 1) / (y)) * (y))
    
    /*
     * Dynamic buffer, used for content & string table.
     */
    struct dbuf {
    	char		*data;	/* start data buffer */
    	size_t		 size;	/* size of the buffer */
    
    	char		*cptr; /* position in [data, data + size] */
    	size_t		 coff; /* number of written bytes */
    };
    
    #define DBUF_CHUNKSZ	(64 * 1024)
    
    /* In-memory representation of a CTF section. */
    struct imcs {
    	struct dbuf	 body;
    	struct dbuf	 stab;	/* corresponding string table */
    	struct hash	*htab;	/* hash table of known strings */
    };
    
    struct strentry {
    	struct hash_entry se_key;	/* Must be first */
    #define se_str se_key.hkey
    	size_t		 se_off;
    };
    
    #ifdef ZLIB
    char		*data_compress(const char *, size_t, size_t, size_t *);
    #endif /* ZLIB */
    
    void
    dbuf_realloc(struct dbuf *dbuf, size_t len)
    {
    	assert(dbuf != NULL);
    	assert(len != 0);
    
    	dbuf->data = xrealloc(dbuf->data, dbuf->size + len);
    	dbuf->size += len;
    	dbuf->cptr = dbuf->data + dbuf->coff;
    }
    
    void
    dbuf_copy(struct dbuf *dbuf, void const *data, size_t len)
    {
    	size_t left;
    
    	assert(dbuf->cptr != NULL);
    	assert(dbuf->data != NULL);
    	assert(dbuf->size != 0);
    
    	if (len == 0)
    		return;
    
    	left = dbuf->size - dbuf->coff;
    	if (left < len)
    		dbuf_realloc(dbuf, ROUNDUP((len - left), DBUF_CHUNKSZ));
    
    	memcpy(dbuf->cptr, data, len);
    	dbuf->cptr += len;
    	dbuf->coff += len;
    }
    
    size_t
    dbuf_pad(struct dbuf *dbuf, int align)
    {
    	int i = (align - (dbuf->coff % align)) % align;
    
    	while (i-- > 0)
    		dbuf_copy(dbuf, "", 1);
    
    	return dbuf->coff;
    }
    
    size_t
    imcs_add_string(struct imcs *imcs, const char *str)
    {
    	struct strentry *se;
    	unsigned int slot;
    
    	if (str == NULL || *str == '\0')
    		return 0;
    
    	se = (struct strentry *)hash_find(imcs->htab, str, &slot);
    	if (se == NULL) {
    		se = xmalloc(sizeof(*se));
    		hash_insert(imcs->htab, slot, &se->se_key, str);
    		se->se_off = imcs->stab.coff;
    
    		dbuf_copy(&imcs->stab, str, strlen(str) + 1);
    	}
    
    	return se->se_off;
    }
    
    void
    imcs_add_func(struct imcs *imcs, struct itype *it)
    {
    	uint16_t		 func, arg;
    	struct imember		*im;
    	int			 kind, root, vlen;
    
    	vlen = it->it_nelems;
    	kind = it->it_type;
    	root = 0;
    
    	func = (kind << 11) | (root << 10) | (vlen & CTF_MAX_VLEN);
    	dbuf_copy(&imcs->body, &func, sizeof(func));
    
    	if (kind == CTF_K_UNKNOWN)
    		return;
    
    	func = it->it_refp->it_idx;
    	dbuf_copy(&imcs->body, &func, sizeof(func));
    
    	TAILQ_FOREACH(im, &it->it_members, im_next) {
    		arg = im->im_refp->it_idx;
    		dbuf_copy(&imcs->body, &arg, sizeof(arg));
    	}
    }
    
    void
    imcs_add_obj(struct imcs *imcs, struct itype *it)
    {
    	uint16_t		 type;
    
    	type = it->it_refp->it_idx;
    	dbuf_copy(&imcs->body, &type, sizeof(type));
    }
    
    void
    imcs_add_type(struct imcs *imcs, struct itype *it)
    {
    	struct imember		*im;
    	struct ctf_type		 ctt;
    	struct ctf_array	 cta;
    	unsigned int		 eob;
    	uint32_t		 size;
    	uint16_t		 arg;
    	size_t			 ctsz;
    	int			 kind, root, vlen;
    
    	assert(it->it_type != CTF_K_UNKNOWN && it->it_type != CTF_K_FORWARD);
    
    	vlen = it->it_nelems;
    	size = it->it_size;
    	kind = it->it_type;
    	root = 0;
    
    	ctt.ctt_name = imcs_add_string(imcs, it_name(it));
    	ctt.ctt_info = (kind << 11) | (root << 10) | (vlen & CTF_MAX_VLEN);
    
    	/* Base types don't have reference, typedef & pointer don't have size */
    	if (it->it_refp != NULL && kind != CTF_K_ARRAY) {
    		ctt.ctt_type = it->it_refp->it_idx;
    		ctsz = sizeof(struct ctf_stype);
    	} else if (size <= CTF_MAX_SIZE) {
    		if (kind == CTF_K_INTEGER || kind == CTF_K_FLOAT) {
    			assert(size <= 128);
    			if (size == 0)
    				ctt.ctt_size = 0;
    			else if (size <= 8)
    				ctt.ctt_size = 1;
    			else if (size <= 16)
    				ctt.ctt_size = 2;
    			else if (size <= 32)
    				ctt.ctt_size = 4;
    			else if (size <= 64)
    				ctt.ctt_size = 8;
    			else if (size <= 96)
    				ctt.ctt_size = 12;
    			else
    				ctt.ctt_size = 16;
    		} else
    			ctt.ctt_size = size;
    		ctsz = sizeof(struct ctf_stype);
    	} else {
    		ctt.ctt_lsizehi = CTF_SIZE_TO_LSIZE_HI(size);
    		ctt.ctt_lsizelo = CTF_SIZE_TO_LSIZE_LO(size);
    		ctt.ctt_size = CTF_LSIZE_SENT;
    		ctsz = sizeof(struct ctf_type);
    	}
    
    	dbuf_copy(&imcs->body, &ctt, ctsz);
    
    	switch (kind) {
    		assert(1 == 0);
    		break;
    	case CTF_K_INTEGER:
    	case CTF_K_FLOAT:
    		eob = CTF_INT_DATA(it->it_enc, 0, size);
    		dbuf_copy(&imcs->body, &eob, sizeof(eob));
    		break;
    	case CTF_K_ARRAY:
    		memset(&cta, 0, sizeof(cta));
    		cta.cta_contents = it->it_refp->it_idx;
    		cta.cta_index = long_tidx;
    		cta.cta_nelems = it->it_nelems;
    		dbuf_copy(&imcs->body, &cta, sizeof(cta));
    		break;
    	case CTF_K_STRUCT:
    	case CTF_K_UNION:
    		if (size < CTF_LSTRUCT_THRESH) {
    			struct ctf_member	 ctm;
    
    			memset(&ctm, 0, sizeof(ctm));
    			TAILQ_FOREACH(im, &it->it_members, im_next) {
    				ctm.ctm_name =
    				    imcs_add_string(imcs, im_name(im));
    				ctm.ctm_type = im->im_refp->it_idx;
    				ctm.ctm_offset = im->im_off;
    
    				dbuf_copy(&imcs->body, &ctm, sizeof(ctm));
    			}
    		} else {
    			struct ctf_lmember	 ctlm;
    
    			memset(&ctlm, 0, sizeof(ctlm));
    			TAILQ_FOREACH(im, &it->it_members, im_next) {
    				ctlm.ctlm_name =
    				    imcs_add_string(imcs, im_name(im));
    				ctlm.ctlm_type = im->im_refp->it_idx;
    				ctlm.ctlm_offsethi =
    				    CTF_OFFSET_TO_LMEMHI(im->im_off);
    				ctlm.ctlm_offsetlo =
    				    CTF_OFFSET_TO_LMEMLO(im->im_off);
    
    
    				dbuf_copy(&imcs->body, &ctlm, sizeof(ctlm));
    			}
    		}
    		break;
    	case CTF_K_FUNCTION:
    		TAILQ_FOREACH(im, &it->it_members, im_next) {
    			arg = im->im_refp->it_idx;
    			dbuf_copy(&imcs->body, &arg, sizeof(arg));
    		}
    		if (vlen & 1) {
    			arg = 0;
    			dbuf_copy(&imcs->body, &arg, sizeof(arg));
    		}
    		break;
    	case CTF_K_ENUM:
    		TAILQ_FOREACH(im, &it->it_members, im_next) {
    			struct ctf_enum	cte;
    
    			cte.cte_name = imcs_add_string(imcs, im_name(im));
    			cte.cte_value = im->im_ref;
    
    			dbuf_copy(&imcs->body, &cte, sizeof(cte));
    		}
    		break;
    	case CTF_K_POINTER:
    	case CTF_K_TYPEDEF:
    	case CTF_K_VOLATILE:
    	case CTF_K_CONST:
    	case CTF_K_RESTRICT:
    	default:
    		break;
    	}
    }
    
    void
    imcs_generate(struct imcs *imcs, struct ctf_header *cth, const char *label)
    {
    	struct itype		*it;
    	struct ctf_lblent	 lbl;
    
    	memset(imcs, 0, sizeof(*imcs));
    
    	dbuf_realloc(&imcs->body, DBUF_CHUNKSZ);
    	dbuf_realloc(&imcs->stab, DBUF_CHUNKSZ);
    
    	imcs->htab = hash_init(10);
    	if (imcs->htab == NULL)
    		err(1, "hash_init");
    
    	/* Add empty string */
    	dbuf_copy(&imcs->stab, "", 1);
    
    	/* We don't use parent label */
    	cth->cth_parlabel = 0;
    	cth->cth_parname = 0;
    
    	/* Insert a single label for all types. */
    	cth->cth_lbloff = 0;
    	lbl.ctl_label = imcs_add_string(imcs, label);
    	lbl.ctl_typeidx = tidx;
    	dbuf_copy(&imcs->body, &lbl, sizeof(lbl));
    
    	/* Insert objects */
    	cth->cth_objtoff = dbuf_pad(&imcs->body, 2);
    	TAILQ_FOREACH(it, &iobjq, it_symb)
    		imcs_add_obj(imcs, it);
    
    	/* Insert functions */
    	cth->cth_funcoff = dbuf_pad(&imcs->body, 2);
    	TAILQ_FOREACH(it, &ifuncq, it_symb)
    		imcs_add_func(imcs, it);
    
    	/* Insert types */
    	cth->cth_typeoff = dbuf_pad(&imcs->body, 4);
    	TAILQ_FOREACH(it, &itypeq, it_next) {
    		if (it->it_flags & (ITF_FUNC|ITF_OBJ))
    			continue;
    
    		imcs_add_type(imcs, it);
    	}
    
    	/* String table is written from its own buffer. */
    	cth->cth_stroff = imcs->body.coff;
    	cth->cth_strlen = imcs->stab.coff;
    }
    
    /*
     * Generate a CTF buffer from the internal type representation.
     */
    int
    generate(const char *path, const char *label, int compress)
    {
    	char			*p, *ctfdata = NULL;
    	ssize_t			 ctflen;
    	struct ctf_header	 cth;
    	struct imcs		 imcs;
    	int			 error = 0, fd;
    
    	memset(&cth, 0, sizeof(cth));
    
    	cth.cth_magic = CTF_MAGIC;
    	cth.cth_version = CTF_VERSION;
    
    #ifdef ZLIB
    	if (compress)
    		cth.cth_flags = CTF_F_COMPRESS;
    #endif /* ZLIB */
    
    	imcs_generate(&imcs, &cth, label);
    
    	ctflen = sizeof(cth) + imcs.body.coff + imcs.stab.coff;
    	p = ctfdata = xmalloc(ctflen);
    
    	memcpy(p, &cth, sizeof(cth));
    	p += sizeof(cth);
    
    	memcpy(p, imcs.body.data, imcs.body.coff);
    	p += imcs.body.coff;
    
    	memcpy(p, imcs.stab.data, imcs.stab.coff);
    	p += imcs.stab.coff;
    
    	assert((p - ctfdata) == ctflen);
    
    #ifdef ZLIB
    	if (compress) {
    		char *cdata;
    		size_t clen;
    
    		cdata = data_compress(ctfdata + sizeof(cth),
    		    ctflen - sizeof(cth), ctflen - sizeof(cth), &clen);
    		if (cdata == NULL) {
    			warnx("compressing CTF data");
    			free(ctfdata);
    			return -1;
    		}
    
    		memcpy(ctfdata + sizeof(cth), cdata, clen);
    		ctflen = clen + sizeof(cth);
    
    		free(cdata);
    	}
    #endif /* ZLIB */
    
    	fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
    	if (fd == -1) {
    		warn("open %s", path);
    		free(ctfdata);
    		return -1;
    	}
    
    	if (write(fd, ctfdata, ctflen) != ctflen) {
    		warn("unable to write %zd bytes for %s", ctflen, path);
    		error = -1;
    	}
    
    	close(fd);
    	free(ctfdata);
    	return error;
    }
    
    #ifdef ZLIB
    char *
    data_compress(const char *buf, size_t size, size_t len, size_t *pclen)
    {
    	z_stream		 stream;
    	char			*data;
    	int			 error;
    
    	data = malloc(len);
    	if (data == NULL) {
    		warn(NULL);
    		return NULL;
    	}
    
    	memset(&stream, 0, sizeof(stream));
    	stream.zalloc = Z_NULL;
    	stream.zfree = Z_NULL;
    	stream.opaque = Z_NULL;
    
    	if ((error = deflateInit(&stream, Z_BEST_COMPRESSION)) != Z_OK) {
    		warnx("zlib deflateInit failed: %s", zError(error));
    		goto exit;
    	}
    
    	stream.next_in = (void *)buf;
    	stream.avail_in = size;
    	stream.next_out = (unsigned char *)data;
    	stream.avail_out = len;
    
    	if ((error = deflate(&stream, Z_FINISH)) != Z_STREAM_END) {
    		warnx("zlib deflate failed: %s", zError(error));
    		deflateEnd(&stream);
    		goto exit;
    	}
    
    	if ((error = deflateEnd(&stream)) != Z_OK) {
    		warnx("zlib deflateEnd failed: %s", zError(error));
    		goto exit;
    	}
    
    	if (pclen != NULL)
    		*pclen = stream.total_out;
    
    	return data;
    
    exit:
    	free(data);
    	return NULL;
    }
    #endif /* ZLIB */