88674f0395
pahole -J .tmp_linux.btf during Linux build process always crashes on my system. Problem is that when gobuffer is initialized via gobuffer__init(), it is in state where 'index' (AKA its size) is set to 1, but 'entries' is NULL. State corrects itself if 'gobuffer__add()' is invoked, as that will allocate buffer (even if added len == 0). But if __add() is never invoked because only anonymous symbols are present, one ends up with gobuffer that crashes gobuffer__copy. Instead of allocating single-byte buffer always I opted for checking if gobuffer entries is NULL before use in copy and compress - gobuffer__init() would need prototype change to report malloc failures, and it seems unnecessary to allocate memory always - even if file does not have any symbols after all. (gdb) bt #0 __memmove_sse2_unaligned_erms () at ../sysdeps/x86_64/multiarch/memmove-vec-unaligned-erms.S:287 #1 0x00007f2f2c1ec2ee in btf_elf__encode (btfe=0x5654e31e2e30, flags=flags@entry=0 '\000') at libbtf.c:750 #2 0x00007f2f2c1e9af0 in btf_encoder__encode () at btf_encoder.c:164 #3 0x00005654e2407599 in main (argc=3, argv=0x7ffcd8783f18) at pahole.c:1344 (gdb) frame 1 #1 0x00007f2f2c1ec2ee in btf_elf__encode (btfe=0x5654e31e2e30, flags=flags@entry=0 '\000') at libbtf.c:750 750 gobuffer__copy(btfe->strings, btf_elf__nohdr_data(btfe) + hdr->str_off); (gdb) print btfe->strings $1 = (struct gobuffer *) 0x5654e31db2c8 (gdb) print *btfe->strings $2 = {entries = 0x0, nr_entries = 0, index = 1, allocated_size = 0} (gdb) print btfe->types $3 = {entries = 0x5654e31e2ef0 "", nr_entries = 1, index = 16, allocated_size = 8192} (gdb) x /16bx btfe->types.entries 0x5654e31e2ef0: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x01 0x5654e31e2ef8: 0x04 0x00 0x00 0x00 0x20 0x00 0x00 0x00 Signed-off-by: Petr Vandrovec <petr@vmware.com> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
144 lines
2.8 KiB
C
144 lines
2.8 KiB
C
/*
|
|
SPDX-License-Identifier: GPL-2.0-only
|
|
|
|
Copyright (C) 2008 Arnaldo Carvalho de Melo <acme@redhat.com>
|
|
|
|
Grow only buffer, add entries but never delete
|
|
*/
|
|
|
|
#include "gobuffer.h"
|
|
|
|
#include <search.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <zlib.h>
|
|
#include <errno.h>
|
|
|
|
#include "dutil.h"
|
|
|
|
#define GOBUFFER__BCHUNK (8 * 1024)
|
|
#define GOBUFFER__ZCHUNK (8 * 1024)
|
|
|
|
void gobuffer__init(struct gobuffer *gb)
|
|
{
|
|
gb->entries = NULL;
|
|
gb->nr_entries = gb->allocated_size = 0;
|
|
/* 0 == NULL */
|
|
gb->index = 1;
|
|
}
|
|
|
|
struct gobuffer *gobuffer__new(void)
|
|
{
|
|
struct gobuffer *gb = malloc(sizeof(*gb));
|
|
|
|
if (gb != NULL)
|
|
gobuffer__init(gb);
|
|
|
|
return gb;
|
|
}
|
|
|
|
void __gobuffer__delete(struct gobuffer *gb)
|
|
{
|
|
free(gb->entries);
|
|
}
|
|
|
|
void gobuffer__delete(struct gobuffer *gb)
|
|
{
|
|
__gobuffer__delete(gb);
|
|
free(gb);
|
|
}
|
|
|
|
void *gobuffer__ptr(const struct gobuffer *gb, unsigned int s)
|
|
{
|
|
return s ? gb->entries + s : NULL;
|
|
}
|
|
|
|
int gobuffer__allocate(struct gobuffer *gb, unsigned int len)
|
|
{
|
|
const unsigned int rc = gb->index;
|
|
const unsigned int index = gb->index + len;
|
|
|
|
if (index >= gb->allocated_size) {
|
|
unsigned int allocated_size = (gb->allocated_size +
|
|
GOBUFFER__BCHUNK);
|
|
if (allocated_size < index)
|
|
allocated_size = index + GOBUFFER__BCHUNK;
|
|
char *entries = realloc(gb->entries, allocated_size);
|
|
|
|
if (entries == NULL)
|
|
return -ENOMEM;
|
|
|
|
gb->allocated_size = allocated_size;
|
|
gb->entries = entries;
|
|
}
|
|
|
|
gb->index = index;
|
|
return rc;
|
|
}
|
|
|
|
int gobuffer__add(struct gobuffer *gb, const void *s, unsigned int len)
|
|
{
|
|
const int rc = gobuffer__allocate(gb, len);
|
|
|
|
if (rc >= 0) {
|
|
++gb->nr_entries;
|
|
memcpy(gb->entries + rc, s, len);
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
void gobuffer__copy(const struct gobuffer *gb, void *dest)
|
|
{
|
|
if (gb->entries) {
|
|
memcpy(dest, gb->entries, gobuffer__size(gb));
|
|
} else {
|
|
/* gobuffer__size will be 0 or 1. */
|
|
memcpy(dest, "", gobuffer__size(gb));
|
|
}
|
|
}
|
|
|
|
const void *gobuffer__compress(struct gobuffer *gb, unsigned int *size)
|
|
{
|
|
z_stream z = {
|
|
.zalloc = Z_NULL,
|
|
.zfree = Z_NULL,
|
|
.opaque = Z_NULL,
|
|
.avail_in = gobuffer__size(gb),
|
|
.next_in = (Bytef *)(gobuffer__entries(gb) ? : ""),
|
|
};
|
|
void *bf = NULL;
|
|
unsigned int bf_size = 0;
|
|
|
|
if (deflateInit(&z, Z_BEST_COMPRESSION) != Z_OK)
|
|
goto out_free;
|
|
|
|
do {
|
|
const unsigned int new_bf_size = bf_size + GOBUFFER__ZCHUNK;
|
|
void *nbf = realloc(bf, new_bf_size);
|
|
|
|
if (nbf == NULL)
|
|
goto out_close_and_free;
|
|
|
|
bf = nbf;
|
|
z.avail_out = GOBUFFER__ZCHUNK;
|
|
z.next_out = (Bytef *)bf + bf_size;
|
|
bf_size = new_bf_size;
|
|
if (deflate(&z, Z_FINISH) == Z_STREAM_ERROR)
|
|
goto out_close_and_free;
|
|
} while (z.avail_out == 0);
|
|
|
|
deflateEnd(&z);
|
|
*size = bf_size - z.avail_out;
|
|
out:
|
|
return bf;
|
|
|
|
out_close_and_free:
|
|
deflateEnd(&z);
|
|
out_free:
|
|
free(bf);
|
|
bf = NULL;
|
|
goto out;
|
|
}
|