summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Wielaard <mjw@redhat.com>2015-10-21 01:33:56 +0200
committerMark Wielaard <mjw@redhat.com>2015-11-02 16:32:39 +0100
commit1362745282b44aac8d9b1a4fd7cc250d1b797cc5 (patch)
tree0c036073e0fafc5f69acfe64be3ae70726628b84
parentb1a665cf5efc1661dc6cda0b74155052145ee3c6 (diff)
downloadelfutils-1362745282b44aac8d9b1a4fd7cc250d1b797cc5.tar.gz
libelf: Add elf_compress and elf_compress_gnu.
Signed-off-by: Mark Wielaard <mjw@redhat.com>
-rw-r--r--libelf/ChangeLog26
-rw-r--r--libelf/Makefile.am3
-rw-r--r--libelf/elf_compress.c409
-rw-r--r--libelf/elf_compress_gnu.c150
-rw-r--r--libelf/elf_end.c11
-rw-r--r--libelf/elf_error.c32
-rw-r--r--libelf/elf_getdata.c36
-rw-r--r--libelf/gelf.h1
-rw-r--r--libelf/libelf.h43
-rw-r--r--libelf/libelf.map6
-rw-r--r--libelf/libelfP.h22
-rw-r--r--tests/ChangeLog15
-rw-r--r--tests/Makefile.am10
-rw-r--r--tests/elfgetzdata.c101
-rw-r--r--tests/elfputzdata.c200
-rw-r--r--tests/msg_tst.c7
-rwxr-xr-xtests/run-elfgetzdata.sh70
-rwxr-xr-xtests/run-elfputzdata.sh174
18 files changed, 1293 insertions, 23 deletions
diff --git a/libelf/ChangeLog b/libelf/ChangeLog
index fbe8e3ae..663a461b 100644
--- a/libelf/ChangeLog
+++ b/libelf/ChangeLog
@@ -1,3 +1,29 @@
+2015-10-21 Mark Wielaard <mjw@redhat.com>
+
+ * Makefile.am (libelf_a_SOURCES): Add elf32_zscn_compress.c,
+ elf64_zscn_compress.c and gelf_zscn_compress.c.
+ * elf32_zscn_compress.c: New file.
+ * elf64_zscn_compress.c: Likewise.
+ * gelf_zscn_compress.c: Likewise.
+ * elf_end.c (elf_end): Free zdata_base.
+ * elf_error.c: Add ELF_E_ALREADY_COMPRESSED,
+ ELF_E_UNKNOWN_COMPRESSION_TYPE, ELF_E_COMPRESS_ERROR and
+ ELF_E_DECOMPRESS_ERROR.
+ * elf_data.c (__libelf_data_type): New internal function extracted
+ from convert_data.
+ (convert_data): Handle SHF_COMPRESSED.
+ * gelf.h: Define gelf_zscn_compress.
+ * libelf.h: Define elf32_zscn_compress and elf64_zscn_compress.
+ * libelf.map (ELFUTILS_1.7): Add elf32_zscn_compress,
+ elf64_zscn_compress and gelf_zscn_compress.
+ * libelfP.h: Add ELF_E_ALREADY_COMPRESSED,
+ ELF_E_UNKNOWN_COMPRESSION_TYPE, ELF_E_COMPRESS_ERROR and
+ ELF_E_DECOMPRESS_ERROR. Define __libelf_data_type.
+ (__libelf_compress): New internal function definition.
+ (__libelf_decompress): Likewise.
+ (__libelf_reset_rawdata): Likewise.
+ (struct Elf_Scn): Add zdata_base.
+
2015-10-16 Mark Wielaard <mjw@redhat.com>
* Makefile.am (libelf_so_LDLIBS): Add -lz.
diff --git a/libelf/Makefile.am b/libelf/Makefile.am
index 4a4131c1..9e12fb37 100644
--- a/libelf/Makefile.am
+++ b/libelf/Makefile.am
@@ -88,7 +88,8 @@ libelf_a_SOURCES = elf_version.c elf_hash.c elf_error.c elf_fill.c \
elf32_offscn.c elf64_offscn.c gelf_offscn.c \
elf_getaroff.c \
elf_gnu_hash.c \
- elf_scnshndx.c
+ elf_scnshndx.c \
+ elf_compress.c elf_compress_gnu.c
libelf_pic_a_SOURCES =
am_libelf_pic_a_OBJECTS = $(libelf_a_SOURCES:.c=.os)
diff --git a/libelf/elf_compress.c b/libelf/elf_compress.c
new file mode 100644
index 00000000..a9b4f1b9
--- /dev/null
+++ b/libelf/elf_compress.c
@@ -0,0 +1,409 @@
+/* Compress or decompress a section.
+ Copyright (C) 2015 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <libelf.h>
+#include "libelfP.h"
+#include "common.h"
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <zlib.h>
+
+#ifndef MAX
+# define MAX(a, b) ((a) > (b) ? (a) : (b))
+#endif
+
+void *
+internal_function
+__libelf_compress (Elf_Scn *scn, size_t hsize, int ei_data,
+ size_t orig_size, size_t *size)
+{
+ /* The compressed data is the on-disk data. We simplify the
+ implementation a bit by asking for the (converted) in-memory
+ data (which might be all there is if the user created it with
+ elf_newdata) and then convert back to raw if needed before
+ compressing. Should be made a bit more clever to directly
+ use raw if that is directly available. */
+ Elf_Data *data = elf_getdata (scn, NULL);
+ if (data == NULL)
+ return NULL;
+
+ /* Guess an output size. 1/8th of the original Elf_Data or at least 8K. */
+ size_t block = MAX (orig_size, data->d_size) / 8;
+ if (block < 8 * 1024)
+ block = 8 * 1024;
+ size_t out_size = block;
+ void *out_buf = malloc (out_size);
+ if (out_buf == NULL)
+ {
+ __libelf_seterrno (ELF_E_NOMEM);
+ return NULL;
+ }
+
+ size_t used = hsize;
+
+ z_stream z;
+ z.zalloc = Z_NULL;
+ z.zfree = Z_NULL;
+ z.opaque = Z_NULL;
+ int zrc = deflateInit (&z, Z_BEST_COMPRESSION);
+ if (zrc != Z_OK)
+ {
+ __libelf_seterrno (ELF_E_COMPRESS_ERROR);
+ return NULL;
+ }
+
+ /* Cleanup and NULL return on error. Don't leak memory. */
+ void *deflate_error (int err)
+ {
+ __libelf_seterrno (err);
+ deflateEnd (&z);
+ free (out_buf);
+ return NULL;
+ }
+
+ /* Loop over data buffers. */
+ int flush;
+ do
+ {
+ /* Convert to raw if different endianess. */
+ if (ei_data != MY_ELFDATA)
+ {
+ if (gelf_xlatetof (scn->elf, data, data, ei_data) == NULL)
+ return deflate_error;
+ }
+ z.avail_in = data->d_size;
+ z.next_in = data->d_buf;
+ /* Get next buffer to see if this is the last one. */
+ data = elf_getdata (scn, data);
+ flush = data == NULL ? Z_FINISH : Z_NO_FLUSH;
+ /* Flush one data buffer. */
+ do
+ {
+ z.avail_out = out_size - used;
+ z.next_out = out_buf + used;
+ zrc = deflate (&z, flush);
+ if (zrc == Z_STREAM_ERROR)
+ return deflate_error (ELF_E_COMPRESS_ERROR);
+ used += (out_size - used) - z.avail_out;
+ if (z.avail_out == 0)
+ {
+ void *bigger = realloc (out_buf, out_size + block);
+ if (bigger == NULL)
+ return deflate_error (ELF_E_NOMEM);
+ out_buf = bigger;
+ out_size += block;
+ }
+ }
+ while (z.avail_out == 0); /* Need more output buffer. */
+ }
+ while (flush != Z_FINISH); /* More data blocks. */
+
+ zrc = deflateEnd (&z);
+ if (zrc != Z_OK)
+ return deflate_error (ELF_E_COMPRESS_ERROR);
+
+ *size = used;
+ return out_buf;
+}
+
+void *
+internal_function
+__libelf_decompress (void *buf_in, size_t size_in, size_t size_out)
+{
+ void *buf_out = malloc (size_out);
+ if (unlikely (buf_out == NULL))
+ {
+ __libelf_seterrno (ELF_E_NOMEM);
+ return NULL;
+ }
+
+ z_stream z =
+ {
+ .next_in = buf_in,
+ .avail_in = size_in,
+ .next_out = buf_out,
+ .avail_out = size_out
+ };
+ int zrc = inflateInit (&z);
+ while (z.avail_in > 0 && likely (zrc == Z_OK))
+ {
+ z.next_out = buf_out + (size_out - z.avail_out);
+ zrc = inflate (&z, Z_FINISH);
+ if (unlikely (zrc != Z_STREAM_END))
+ {
+ zrc = Z_DATA_ERROR;
+ break;
+ }
+ zrc = inflateReset (&z);
+ }
+ if (likely (zrc == Z_OK))
+ zrc = inflateEnd (&z);
+
+ if (unlikely (zrc != Z_OK) || unlikely (z.avail_out != 0))
+ {
+ free (buf_out);
+ __libelf_seterrno (ELF_E_DECOMPRESS_ERROR);
+ return NULL;
+ }
+
+ return buf_out;
+}
+
+void
+internal_function
+__libelf_reset_rawdata (Elf_Scn *scn, void *buf, size_t size, size_t align)
+{
+ /* This is the new raw data, replace and possibly free old data. */
+ scn->rawdata.d.d_buf = buf;
+ scn->rawdata.d.d_size = size;
+ scn->rawdata.d.d_align = align;
+
+ /* Existing existing data is no longer valid. */
+ scn->data_list_rear = NULL;
+ if (scn->data_base != scn->rawdata_base)
+ free (scn->data_base);
+ scn->data_base = NULL;
+ if (scn->elf->map_address == NULL
+ || scn->rawdata_base == scn->zdata_base)
+ free (scn->rawdata_base);
+
+ scn->rawdata_base = buf;
+}
+
+int
+elf_compress (Elf_Scn *scn, int type)
+{
+ if (scn == NULL)
+ return -1;
+
+ Elf *elf = scn->elf;
+ GElf_Ehdr ehdr;
+ if (gelf_getehdr (elf, &ehdr) == NULL)
+ return NULL;
+
+ int elfclass = elf->class;
+ int elfdata = ehdr.e_ident[EI_DATA];
+
+ GElf_Shdr shdr;
+ if (gelf_getshdr (scn, &shdr) == NULL)
+ return NULL;
+
+ int compressed = (shdr.sh_flags & SHF_COMPRESSED);
+ if (type == ELFCOMPRESS_ZLIB)
+ {
+ /* Compress/Deflate. */
+ if (compressed == 1)
+ {
+ __libelf_seterrno (ELF_E_ALREADY_COMPRESSED);
+ return -1;
+ }
+
+ // XXX sh_size vs Elf_Data sizes. likewise for addralign.
+ size_t orig_size = shdr.sh_size;
+ size_t hsize = (elfclass == ELFCLASS32
+ ? sizeof (Elf32_Chdr) : sizeof (Elf64_Chdr));
+ size_t size;
+ void *out_buf = __libelf_compress (scn, hsize, elfdata,
+ orig_size, &size);
+ if (out_buf == NULL)
+ return NULL;
+
+ /* Put the header in front of the data. */
+ if (elfclass == ELFCLASS32)
+ {
+ Elf32_Chdr chdr;
+ chdr.ch_type = ELFCOMPRESS_ZLIB;
+ chdr.ch_size = orig_size;
+ chdr.ch_addralign = shdr.sh_addralign;
+ if (elfdata != MY_ELFDATA)
+ {
+ CONVERT (chdr.ch_type);
+ CONVERT (chdr.ch_size);
+ CONVERT (chdr.ch_addralign);
+ }
+ memmove (out_buf, &chdr, sizeof (Elf32_Chdr));
+ }
+ else
+ {
+ Elf64_Chdr chdr;
+ chdr.ch_type = ELFCOMPRESS_ZLIB;
+ chdr.ch_reserved = 0;
+ chdr.ch_size = orig_size;
+ chdr.ch_addralign = shdr.sh_addralign;
+ if (elfdata != MY_ELFDATA)
+ {
+ CONVERT (chdr.ch_type);
+ CONVERT (chdr.ch_reserved);
+ CONVERT (chdr.ch_size);
+ CONVERT (chdr.ch_addralign);
+ }
+ memmove (out_buf, &chdr, sizeof (Elf64_Chdr));
+ }
+
+ /* Note we keep the sh_entsize as is, we assume it is setup
+ correctly and ignored when SHF_COMPRESSED is set. */
+ shdr.sh_size = size;
+ shdr.sh_addralign = 1;
+ shdr.sh_flags |= SHF_COMPRESSED;
+ // XXX Don't! this sets dirty flag...
+ gelf_update_shdr (scn, &shdr);
+
+ __libelf_reset_rawdata (scn, out_buf, size, 1);
+
+ /* The section is now compressed, we could keep the uncompressed
+ data around, but since that might have been multiple Elf_Data
+ buffers let the user uncompress it explicitly again if they
+ want it to simplify bookkeeping. */
+ scn->zdata_base = NULL;
+
+ return 0;
+ }
+ else if (type == 0)
+ {
+ /* Decompress/Inflate. */
+ if (compressed == 0)
+ {
+ __libelf_seterrno (ELF_E_NOT_COMPRESSED);
+ return -1;
+ }
+
+ /* In theory the user could have constucted a compressed section
+ by hand. But we always just take the rawdata directly and
+ decompress that. */
+ Elf_Data *data = elf_rawdata (scn, NULL);
+ if (data == NULL)
+ return -1;
+
+ /* Extract the header data first. */
+ int ctype;
+ size_t size;
+ size_t align;
+ size_t hsize;
+ if (elfclass == ELFCLASS32)
+ {
+ hsize = sizeof (Elf32_Chdr);
+ if (data->d_size < hsize)
+ {
+ __libelf_seterrno (ELF_E_INVALID_DATA);
+ return -1;
+ }
+
+ Elf32_Chdr chdr;
+ if (elfdata == MY_ELFDATA
+ && (ALLOW_UNALIGNED
+ || (((uintptr_t) data->d_buf & (__alignof__ (Elf32_Chdr) - 1))
+ == 0)))
+ chdr = *(Elf32_Chdr *) data->d_buf;
+ else
+ memcpy (&chdr, data->d_buf, hsize);
+
+ if (elfdata == MY_ELFDATA)
+ {
+ ctype = chdr.ch_type;
+ size = chdr.ch_size;
+ align = chdr.ch_addralign;
+ }
+ else
+ {
+ CONVERT_TO (ctype, chdr.ch_type);
+ CONVERT_TO (size, chdr.ch_size);
+ CONVERT_TO (align, chdr.ch_addralign);
+ }
+ }
+ else
+ {
+ hsize = sizeof (Elf64_Chdr);
+ if (data->d_size < hsize)
+ {
+ __libelf_seterrno (ELF_E_INVALID_DATA);
+ return -1;
+ }
+
+ Elf64_Chdr chdr;
+ if (elfdata == MY_ELFDATA
+ && (ALLOW_UNALIGNED
+ || (((uintptr_t) data->d_buf & (__alignof__ (Elf64_Chdr) - 1))
+ == 0)))
+ chdr = *(Elf64_Chdr *) data->d_buf;
+ else
+ memcpy (&chdr, data->d_buf, hsize);
+
+ if (elfdata == MY_ELFDATA)
+ {
+ ctype = chdr.ch_type;
+ size = chdr.ch_size;
+ align = chdr.ch_addralign;
+ }
+ else
+ {
+ CONVERT_TO (ctype, chdr.ch_type);
+ CONVERT_TO (size, chdr.ch_size);
+ CONVERT_TO (align, chdr.ch_addralign);
+ }
+ }
+
+ if (ctype != ELFCOMPRESS_ZLIB)
+ {
+ __libelf_seterrno (ELF_E_UNKNOWN_COMPRESSION_TYPE);
+ return -1;
+ }
+
+ /* rawdata_base must be non-NULL since we found a compressed header. */
+ size_t size_in = data->d_size - hsize;
+ void *buf_in = data->d_buf + hsize;
+ void *buf_out = __libelf_decompress (buf_in, size_in, size);
+ if (buf_out == NULL)
+ return -1;
+
+ /* Note we keep the sh_entsize as is, we assume it is setup
+ correctly and ignored when SHF_COMPRESSED is set. */
+ shdr.sh_size = size;
+ shdr.sh_addralign = align;
+ shdr.sh_flags &= ~SHF_COMPRESSED;
+ // XXX Don't! this sets dirty flag...
+ gelf_update_shdr (scn, &shdr);
+
+ __libelf_reset_rawdata (scn, buf_out, size, align);
+
+ scn->zdata_base = buf_out;
+
+ return 0;
+ }
+ else
+ {
+ __libelf_seterrno (ELF_E_UNKNOWN_COMPRESSION_TYPE);
+ return -1;
+ }
+}
diff --git a/libelf/elf_compress_gnu.c b/libelf/elf_compress_gnu.c
new file mode 100644
index 00000000..635df710
--- /dev/null
+++ b/libelf/elf_compress_gnu.c
@@ -0,0 +1,150 @@
+/* Compress or decompress a section.
+ Copyright (C) 2015 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <libelf.h>
+#include "libelfP.h"
+#include "common.h"
+
+int
+elf_compress_gnu (Elf_Scn *scn, int inflate)
+{
+ if (scn == NULL)
+ return -1;
+
+ Elf *elf = scn->elf;
+ GElf_Ehdr ehdr;
+ if (gelf_getehdr (elf, &ehdr) == NULL)
+ return NULL;
+
+ int elfdata = ehdr.e_ident[EI_DATA];
+
+ GElf_Shdr shdr;
+ if (gelf_getshdr (scn, &shdr) == NULL)
+ return NULL;
+
+ /* For GNU compression we cannot really know whether the section is
+ already compressed or not. Just try and see what happens... */
+ // int compressed = (shdr.sh_flags & SHF_COMPRESSED);
+ if (inflate == 1)
+ {
+ // XXX sh_size vs Elf_Data sizes. likewise for addralign.
+ size_t orig_size = shdr.sh_size;
+ size_t hsize = 4 + 8; /* GNU "ZLIB" + 8 byte size. */
+ size_t size;
+ void *out_buf = __libelf_compress (scn, hsize, elfdata,
+ orig_size, &size);
+ if (out_buf == NULL)
+ return NULL;
+
+ uint64_t be64_size = htobe64 (orig_size);
+ memmove (out_buf, "ZLIB", 4);
+ memmove (out_buf + 4, &be64_size, sizeof (be64_size));
+
+ /* We don't know anything about sh_entsize, sh_addralign and
+ sh_flags won't have a SHF_COMPRESSED hint in the GNU format.
+ Just adjust the sh_size. */
+ shdr.sh_size = size;
+
+ // XXX Don't! this sets dirty flag...
+ gelf_update_shdr (scn, &shdr);
+
+ __libelf_reset_rawdata (scn, out_buf, size, 1);
+
+ /* The section is now compressed, we could keep the uncompressed
+ data around, but since that might have been multiple Elf_Data
+ buffers let the user uncompress it explicitly again if they
+ want it to simplify bookkeeping. */
+ scn->zdata_base = NULL;
+
+ return 0;
+ }
+ else if (inflate == 0)
+ {
+ /* In theory the user could have constucted a compressed section
+ by hand. But we always just take the rawdata directly and
+ decompress that. */
+ Elf_Data *data = elf_rawdata (scn, NULL);
+ if (data == NULL)
+ return -1;
+
+ size_t hsize = 4 + 8; /* GNU "ZLIB" + 8 byte size. */
+ if (data->d_size < hsize || memcmp (data->d_buf, "ZLIB", 4) != 0)
+ {
+ __libelf_seterrno (ELF_E_NOT_COMPRESSED);
+ return -1;
+ }
+
+ /* There is a 12-byte header of "ZLIB" followed by
+ an 8-byte big-endian size. There is only one type and
+ Alignment isn't preserved separately. */
+ uint64_t gsize;
+ memcpy (&gsize, data->d_buf + 4, sizeof gsize);
+ gsize = be64toh (gsize);
+
+ /* One more sanity check, size should be bigger than original
+ data size plus some overhead (4 chars ZLIB + 8 bytes size + 6
+ bytes zlib stream overhead + 5 bytes overhead max for one 16K
+ block) and should fit into a size_t. */
+ if (gsize + 4 + 8 + 6 + 5 < data->d_size || gsize > SIZE_MAX)
+ {
+ __libelf_seterrno (ELF_E_NOT_COMPRESSED);
+ return -1;
+ }
+
+ size_t size = gsize;
+ size_t size_in = data->d_size - hsize;
+ void *buf_in = data->d_buf + hsize;
+ void *buf_out = __libelf_decompress (buf_in, size_in, size);
+ if (buf_out == NULL)
+ return -1;
+
+ /* We don't know anything about sh_entsize, sh_addralign and
+ sh_flags won't have a SHF_COMPRESSED hint in the GNU format.
+ Just adjust the sh_size. */
+ shdr.sh_size = size;
+
+ // XXX Don't! this sets dirty flag...
+ gelf_update_shdr (scn, &shdr);
+
+ __libelf_reset_rawdata (scn, buf_out, size, shdr.sh_addralign);
+
+ scn->zdata_base = buf_out;
+
+ return 0;
+ }
+ else
+ {
+ __libelf_seterrno (ELF_E_UNKNOWN_COMPRESSION_TYPE);
+ return -1;
+ }
+
+}
diff --git a/libelf/elf_end.c b/libelf/elf_end.c
index 7ea876c3..fde17b50 100644
--- a/libelf/elf_end.c
+++ b/libelf/elf_end.c
@@ -150,6 +150,12 @@ elf_end (Elf *elf)
/* It doesn't matter which pointer. */
free (scn->shdr.e32);
+ /* Free zdata if uncompressed, but not yet used as
+ rawdata_base. If it is already used it will be
+ freed below. */
+ if (scn->zdata_base != scn->rawdata_base)
+ free (scn->zdata_base);
+
/* If the file has the same byte order and the
architecture doesn't require overly stringent
alignment the raw data buffer is the same as the
@@ -158,8 +164,9 @@ elf_end (Elf *elf)
free (scn->data_base);
/* The section data is allocated if we couldn't mmap
- the file. */
- if (elf->map_address == NULL)
+ the file. Or if we had to decompress. */
+ if (elf->map_address == NULL
+ || scn->rawdata_base == scn->zdata_base)
free (scn->rawdata_base);
/* Free the list of data buffers for the section.
diff --git a/libelf/elf_error.c b/libelf/elf_error.c
index d6bdaab0..20c2f171 100644
--- a/libelf/elf_error.c
+++ b/libelf/elf_error.c
@@ -230,6 +230,31 @@ core files")
(ELF_E_NO_PHDR_IDX \
+ sizeof "file has no program header")
N_("invalid offset")
+ "\0"
+#define ELF_E_NOT_COMPRESSED_IDX \
+ (ELF_E_INVALID_OFFSET_IDX \
+ + sizeof "invalid offset")
+ N_("section does not contain compressed data")
+ "\0"
+#define ELF_E_ALREADY_COMPRESSED_IDX \
+ (ELF_E_NOT_COMPRESSED_IDX \
+ + sizeof "section does not contain compressed data")
+ N_("section contains compressed data")
+ "\0"
+#define ELF_E_UNKNOWN_COMPRESSION_TYPE_IDX \
+ (ELF_E_ALREADY_COMPRESSED_IDX \
+ + sizeof "section contains compressed data")
+ N_("unknown compression type")
+ "\0"
+#define ELF_E_COMPRESS_ERROR_IDX \
+ (ELF_E_UNKNOWN_COMPRESSION_TYPE_IDX \
+ + sizeof "unknown compression type")
+ N_("cannot compress data")
+ "\0"
+#define ELF_E_DECOMPRESS_ERROR_IDX \
+ (ELF_E_COMPRESS_ERROR_IDX \
+ + sizeof "cannot compress data")
+ N_("cannot decompress data")
};
@@ -277,7 +302,12 @@ static const uint_fast16_t msgidx[ELF_E_NUM] =
[ELF_E_GROUP_NOT_REL] = ELF_E_GROUP_NOT_REL_IDX,
[ELF_E_INVALID_PHDR] = ELF_E_INVALID_PHDR_IDX,
[ELF_E_NO_PHDR] = ELF_E_NO_PHDR_IDX,
- [ELF_E_INVALID_OFFSET] = ELF_E_INVALID_OFFSET_IDX
+ [ELF_E_INVALID_OFFSET] = ELF_E_INVALID_OFFSET_IDX,
+ [ELF_E_NOT_COMPRESSED] = ELF_E_NOT_COMPRESSED_IDX,
+ [ELF_E_ALREADY_COMPRESSED] = ELF_E_ALREADY_COMPRESSED_IDX,
+ [ELF_E_UNKNOWN_COMPRESSION_TYPE] = ELF_E_UNKNOWN_COMPRESSION_TYPE_IDX,
+ [ELF_E_COMPRESS_ERROR] = ELF_E_COMPRESS_ERROR_IDX,
+ [ELF_E_DECOMPRESS_ERROR] = ELF_E_DECOMPRESS_ERROR_IDX
};
#define nmsgidx ((int) (sizeof (msgidx) / sizeof (msgidx[0])))
diff --git a/libelf/elf_getdata.c b/libelf/elf_getdata.c
index 9a567e51..74513755 100644
--- a/libelf/elf_getdata.c
+++ b/libelf/elf_getdata.c
@@ -117,6 +117,22 @@ const uint_fast8_t __libelf_type_aligns[EV_NUM - 1][ELFCLASSNUM - 1][ELF_T_NUM]
#endif
+Elf_Type
+internal_function
+__libelf_data_type (Elf *elf, int sh_type)
+{
+ /* Some broken ELF ABI for 64-bit machines use the wrong hash table
+ entry size. See elf-knowledge.h for more information. */
+ if (sh_type == SHT_HASH && elf->class == ELFCLASS64)
+ {
+ GElf_Ehdr ehdr_mem;
+ GElf_Ehdr *ehdr = __gelf_getehdr_rdlock (elf, &ehdr_mem);
+ return (SH_ENTSIZE_HASH (ehdr) == 4 ? ELF_T_WORD : ELF_T_XWORD);
+ }
+ else
+ return shtype_map[LIBELF_EV_IDX][TYPEIDX (sh_type)];
+}
+
/* Convert the data in the current section. */
static void
convert_data (Elf_Scn *scn, int version __attribute__ ((unused)), int eclass,
@@ -204,6 +220,7 @@ __libelf_set_rawdata_wrlock (Elf_Scn *scn)
Elf64_Off offset;
Elf64_Xword size;
Elf64_Xword align;
+ Elf64_Xword flags;
int type;
Elf *elf = scn->elf;
@@ -220,6 +237,7 @@ __libelf_set_rawdata_wrlock (Elf_Scn *scn)
size = shdr->sh_size;
type = shdr->sh_type;
align = shdr->sh_addralign;
+ flags = shdr->sh_flags;
}
else
{
@@ -234,6 +252,7 @@ __libelf_set_rawdata_wrlock (Elf_Scn *scn)
size = shdr->sh_size;
type = shdr->sh_type;
align = shdr->sh_addralign;
+ flags = shdr->sh_flags;
}
/* If the section has no data (for whatever reason), leave the `d_buf'
@@ -243,7 +262,9 @@ __libelf_set_rawdata_wrlock (Elf_Scn *scn)
/* First a test whether the section is valid at all. */
size_t entsize;
- if (type == SHT_HASH)
+ if ((flags & SHF_COMPRESSED) != 0)
+ entsize = 1;
+ else if (type == SHT_HASH)
{
GElf_Ehdr ehdr_mem;
GElf_Ehdr *ehdr = __gelf_getehdr_rdlock (elf, &ehdr_mem);
@@ -320,17 +341,10 @@ __libelf_set_rawdata_wrlock (Elf_Scn *scn)
}
scn->rawdata.d.d_size = size;
- /* Some broken ELF ABI for 64-bit machines use the wrong hash table
- entry size. See elf-knowledge.h for more information. */
- if (type == SHT_HASH && elf->class == ELFCLASS64)
- {
- GElf_Ehdr ehdr_mem;
- GElf_Ehdr *ehdr = __gelf_getehdr_rdlock (elf, &ehdr_mem);
- scn->rawdata.d.d_type
- = (SH_ENTSIZE_HASH (ehdr) == 4 ? ELF_T_WORD : ELF_T_XWORD);
- }
+ if ((flags & SHF_COMPRESSED) != 0)
+ scn->rawdata.d.d_type = ELF_T_BYTE;
else
- scn->rawdata.d.d_type = shtype_map[LIBELF_EV_IDX][TYPEIDX (type)];
+ scn->rawdata.d.d_type = __libelf_data_type (elf, type);
scn->rawdata.d.d_off = 0;
/* Make sure the alignment makes sense. d_align should be aligned both
diff --git a/libelf/gelf.h b/libelf/gelf.h
index e3f07404..29afc85a 100644
--- a/libelf/gelf.h
+++ b/libelf/gelf.h
@@ -183,7 +183,6 @@ extern int gelf_update_phdr (Elf *__elf, int __ndx, GElf_Phdr *__src);
/* Create new program header with PHNUM entries. */
extern unsigned long int gelf_newphdr (Elf *__elf, size_t __phnum);
-
/* Convert data structure from the representation in the file represented
by ELF to their memory representation. */
extern Elf_Data *gelf_xlatetom (Elf *__elf, Elf_Data *__dest,
diff --git a/libelf/libelf.h b/libelf/libelf.h
index 54f7c29b..a376d880 100644
--- a/libelf/libelf.h
+++ b/libelf/libelf.h
@@ -1,5 +1,5 @@
/* Interface for libelf.
- Copyright (C) 1998-2010 Red Hat, Inc.
+ Copyright (C) 1998-2010, 2015 Red Hat, Inc.
This file is part of elfutils.
This file is free software; you can redistribute it and/or modify
@@ -268,6 +268,40 @@ extern Elf32_Shdr *elf32_getshdr (Elf_Scn *__scn);
extern Elf64_Shdr *elf64_getshdr (Elf_Scn *__scn);
+/* When type is ELF_ZSCN_T_NONE then ch_type is ignored and the raw
+ section data is decompressed if the section was compressed before.
+ The returned section header will have an updated sh_size,
+ sh_addralign and will have SHF_COMPRESSED cleared from the
+ sh_flags. sh_entsize isn't changed. If the section wasn't
+ compressed, or an error occurred while decompressing the section
+ date NULL is returned and elf_err is set.
+
+ When type is ELF_ZSCN_T_GNU or ELF_ZSCN_T_ELF ch_type must be a
+ valid ELFCOMPRESS algorithm. Currently the only valid value is
+ ELFCOMPRESS_ZLIB. If the section wasn't compressed yet then the
+ Elf_Data is converted to the raw format of the Elf image and
+ compressed using the given algorithm. The correct compression
+ format header will be added at the start of the new raw data for the
+ section. The returned section header will have an updated sh_size,
+ sh_addralign and if type was ELF_ZSCN_T_ELF sh_flags will have
+ SHF_COMPRESSED set. sh_entsize isn't changed. If the section data
+ was already compressed, or an unknown compression type or algorithm
+ was given or if an error occurred while compressing NULL is returned
+ and elf_err is set.
+
+ Too switch between GNU and ELF compression types or to use another
+ compression algorithm one has to uncompress the section first and
+ then compress using the other type/algorithm.
+
+ All previous returned Elf_Data buffers are invalidated by this call
+ and should no longer be accessed.
+
+ Note that although this changes the header and data returned it
+ doesn't mark the section as dirty. To keep the changes when calling
+ elf_update the section has to be flagged ELF_F_DIRTY. */
+extern int elf_compress (Elf_Scn *scn, int type);
+extern int elf_compress_gnu (Elf_Scn *scn, int compress);
+
/* Set or clear flags for ELF file. */
extern unsigned int elf_flagelf (Elf *__elf, Elf_Cmd __cmd,
unsigned int __flags);
@@ -288,8 +322,11 @@ extern unsigned int elf_flagshdr (Elf_Scn *__scn, Elf_Cmd __cmd,
unsigned int __flags);
-/* Get data from section while translating from file representation
- to memory representation. */
+/* Get data from section while translating from file representation to
+ memory representation. The Elf_Data d_type is set based on the
+ section type if known. Otherwise d_type is set to ELF_T_BYTE. If
+ the section contains compressed data d_type is also set to
+ ELF_T_BYTE. */
extern Elf_Data *elf_getdata (Elf_Scn *__scn, Elf_Data *__data);
/* Get uninterpreted section content. */
diff --git a/libelf/libelf.map b/libelf/libelf.map
index de6d912a..b2075600 100644
--- a/libelf/libelf.map
+++ b/libelf/libelf.map
@@ -138,3 +138,9 @@ ELFUTILS_1.6 {
global:
elf_getphdrnum;
} ELFUTILS_1.5;
+
+ELFUTILS_1.7 {
+ global:
+ elf_compress;
+ elf_compress_gnu;
+} ELFUTILS_1.6;
diff --git a/libelf/libelfP.h b/libelf/libelfP.h
index 993c6556..a50e44b4 100644
--- a/libelf/libelfP.h
+++ b/libelf/libelfP.h
@@ -138,6 +138,11 @@ enum
ELF_E_INVALID_PHDR,
ELF_E_NO_PHDR,
ELF_E_INVALID_OFFSET,
+ ELF_E_NOT_COMPRESSED,
+ ELF_E_ALREADY_COMPRESSED,
+ ELF_E_UNKNOWN_COMPRESSION_TYPE,
+ ELF_E_COMPRESS_ERROR,
+ ELF_E_DECOMPRESS_ERROR,
/* Keep this as the last entry. */
ELF_E_NUM
};
@@ -230,6 +235,8 @@ struct Elf_Scn
char *rawdata_base; /* The unmodified data of the section. */
char *data_base; /* The converted data of the section. */
+ char *zdata_base; /* The uncompressed data of the section. */
+
struct Elf_ScnList *list; /* Pointer to the section list element the
data is in. */
};
@@ -438,6 +445,11 @@ extern const uint_fast8_t __libelf_type_aligns[EV_NUM - 1][ELFCLASSNUM - 1][ELF_
# define __libelf_type_align(class, type) 1
#endif
+/* Given an Elf handle and a section type returns the Elf_Data d_type.
+ Should not be called when SHF_COMPRESSED is set, the d_type should
+ be ELF_T_BYTE. */
+extern Elf_Type __libelf_data_type (Elf *elf, int sh_type) internal_function;
+
/* The libelf API does not have such a function but it is still useful.
Get the memory size for the given type.
@@ -578,6 +590,16 @@ extern GElf_Sym *__gelf_getsym_internal (Elf_Data *__data, int __ndx,
extern uint32_t __libelf_crc32 (uint32_t crc, unsigned char *buf, size_t len)
attribute_hidden;
+extern void * __libelf_compress (Elf_Scn *scn, size_t hsize, int ei_data,
+ size_t orig_size, size_t *size)
+ internal_function;
+
+extern void * __libelf_decompress (void *buf_in, size_t size_in,
+ size_t size_out) internal_function;
+
+extern void __libelf_reset_rawdata (Elf_Scn *scn, void *buf, size_t size,
+ size_t align) internal_function;
+
/* We often have to update a flag iff a value changed. Make this
convenient. */
diff --git a/tests/ChangeLog b/tests/ChangeLog
index 042e3a4c..84fba1a1 100644
--- a/tests/ChangeLog
+++ b/tests/ChangeLog
@@ -1,3 +1,18 @@
+2015-10-21 Mark Wielaard <mjw@redhat.com>
+
+ * Makefile.am (check_PROGRAMS): Add elfgetzdata and elfputzdata.
+ (TESTS): Add run-elfgetzdata.sh and run-elfputzdata.sh.
+ (EXTRA_DIST: Likewise.
+ (elfgetzdata_LDADD): New variable.
+ (elfputzdata_LDADD): Likewise.
+ * elfgetzdata.c: New file.
+ * elfputzdata.c: Likewise.
+ * msg_tst.c: Handle ELF_E_ALREADY_COMPRESSED,
+ ELF_E_UNKNOWN_COMPRESSION_TYPE, ELF_E_COMPRESS_ERROR and
+ ELF_E_DECOMPRESS_ERROR.
+ * run-elfgetzdata.sh: New test.
+ * run-elfputzdata.sh: Likewise.
+
2015-10-28 Mark Wielaard <mjw@redhat.com>
* run-readelf-z.sh: New test.
diff --git a/tests/Makefile.am b/tests/Makefile.am
index e9c95535..1db72fc3 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -52,7 +52,8 @@ check_PROGRAMS = arextract arsymtest newfile saridx scnnames sectiondump \
backtrace-data backtrace-dwarf debuglink debugaltlink \
buildid deleted deleted-lib.so aggregate_size vdsosyms \
getsrc_die strptr newdata elfstrtab dwfl-proc-attach \
- elfshphehdr elfstrmerge dwelfgetchdr
+ elfshphehdr elfstrmerge dwelfgetchdr \
+ elfgetzdata elfputzdata
asm_TESTS = asm-tst1 asm-tst2 asm-tst3 asm-tst4 asm-tst5 \
asm-tst6 asm-tst7 asm-tst8 asm-tst9
@@ -120,7 +121,8 @@ TESTS = run-arextract.sh run-arsymtest.sh newfile test-nlist \
run-readelf-dwz-multi.sh run-allfcts-multi.sh run-deleted.sh \
run-linkmap-cut.sh run-aggregate-size.sh vdsosyms run-readelf-A.sh \
run-getsrc-die.sh run-strptr.sh newdata elfstrtab dwfl-proc-attach \
- elfshphehdr run-lfs-symbols.sh run-dwelfgetchdr.sh
+ elfshphehdr run-lfs-symbols.sh run-dwelfgetchdr.sh \
+ run-elfgetzdata.sh run-elfputzdata.sh
if !BIARCH
export ELFUTILS_DISABLE_BIARCH = 1
@@ -308,7 +310,7 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh \
run-lfs-symbols.sh lfs-symbols testfile-nolfs.bz2 \
testfile-zgnu32.bz2 testfile-zgnu64.bz2 \
testfile-zgabi32.bz2 testfile-zgabi64.bz2 \
- run-dwelfgetchdr.sh
+ run-dwelfgetchdr.sh run-elfgetzdata.sh run-elfputzdata
if USE_VALGRIND
valgrind_cmd='valgrind -q --error-exitcode=1 --run-libc-freeres=no'
@@ -458,6 +460,8 @@ dwfl_proc_attach_LDFLAGS = -pthread $(AM_LDFLAGS)
elfshphehdr_LDADD =$(libelf)
elfstrmerge_LDADD = $(libebl) $(libelf)
dwelfgetchdr_LDADD = $(libelf) $(libdw)
+elfgetzdata_LDADD = $(libelf)
+elfputzdata_LDADD = $(libelf)
if GCOV
check: check-am coverage
diff --git a/tests/elfgetzdata.c b/tests/elfgetzdata.c
new file mode 100644
index 00000000..9176d4f5
--- /dev/null
+++ b/tests/elfgetzdata.c
@@ -0,0 +1,101 @@
+/* Copyright (C) 2015 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <libelf.h>
+#include <gelf.h>
+#include <stdio.h>
+#include <unistd.h>
+
+
+int
+main (int argc, char *argv[])
+{
+ int result = 0;
+ int cnt;
+
+ elf_version (EV_CURRENT);
+
+ for (cnt = 1; cnt < argc; ++cnt)
+ {
+ int fd = open (argv[cnt], O_RDONLY);
+
+ Elf *elf = elf_begin (fd, ELF_C_READ, NULL);
+ if (elf == NULL)
+ {
+ printf ("%s not usable %s\n", argv[cnt], elf_errmsg (-1));
+ result = 1;
+ close (fd);
+ continue;
+ }
+
+ /* To get the section names. */
+ size_t strndx;
+ elf_getshdrstrndx (elf, &strndx);
+
+ Elf_Scn *scn = NULL;
+ while ((scn = elf_nextscn (elf, scn)) != NULL)
+ {
+ size_t idx = elf_ndxscn (scn);
+ GElf_Shdr mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &mem);
+ const char *name = elf_strptr (elf, strndx, shdr->sh_name);
+ if ((shdr->sh_flags & SHF_COMPRESSED) != 0)
+ {
+ /* Real compressed section. */
+ if (elf_compress (scn, 0) != 0)
+ {
+ printf ("elf_compress failed for section %zd: %s\n",
+ idx, elf_errmsg (-1));
+ return -1;
+ }
+ Elf_Data *d = elf_getdata (scn, NULL);
+ printf ("%zd: %s, ELF compressed, size: %zx\n",
+ idx, name, d->d_size);
+ }
+ else
+ {
+ /* Maybe an old GNU compressed .z section? */
+ if (name[0] == '.' && name[1] == 'z')
+ {
+ if (elf_compress_gnu (scn, 0) != 0)
+ {
+ printf ("elf_compress_gnu failed for section %zd: %s\n",
+ idx, elf_errmsg (-1));
+ return -1;
+ }
+ Elf_Data *d = elf_getdata (scn, NULL);
+ printf ("%zd: %s, GNU compressed, size: %zx\n",
+ idx, name, d->d_size);
+ }
+ else
+ printf ("%zd: %s, NOT compressed\n", idx, name);
+ }
+ }
+
+ elf_end (elf);
+ close (fd);
+ }
+
+ return result;
+}
diff --git a/tests/elfputzdata.c b/tests/elfputzdata.c
new file mode 100644
index 00000000..2ab512c1
--- /dev/null
+++ b/tests/elfputzdata.c
@@ -0,0 +1,200 @@
+/* Copyright (C) 2015 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <libelf.h>
+#include <gelf.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+
+int
+main (int argc, char *argv[])
+{
+ int result = 0;
+ int cnt;
+
+ if (argc < 3
+ || (strcmp (argv[1], "elf") != 0
+ && strcmp (argv[1], "gnu") != 0))
+ {
+ printf ("Usage: (elf|gnu) files...\n");
+ return -1;
+ }
+
+ int gnu;
+ if (strcmp (argv[1], "gnu") == 0)
+ gnu = 1;
+ else
+ gnu = 0;
+
+ elf_version (EV_CURRENT);
+
+ for (cnt = 2; cnt < argc; ++cnt)
+ {
+ int fd = open (argv[cnt], O_RDONLY);
+
+ Elf *elf = elf_begin (fd, ELF_C_READ, NULL);
+ if (elf == NULL)
+ {
+ printf ("%s not usable %s\n", argv[cnt], elf_errmsg (-1));
+ result = 1;
+ close (fd);
+ continue;
+ }
+
+ /* To get the section names. */
+ size_t strndx;
+ elf_getshdrstrndx (elf, &strndx);
+
+ Elf_Scn *scn = NULL;
+ while ((scn = elf_nextscn (elf, scn)) != NULL)
+ {
+ size_t idx = elf_ndxscn (scn);
+ GElf_Shdr mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &mem);
+ const char *name = elf_strptr (elf, strndx, shdr->sh_name);
+ if (idx == strndx)
+ {
+ printf ("Not compressing section string table %zd\n", idx);
+ }
+ else if (shdr->sh_type == SHT_NOBITS
+ || (shdr->sh_flags & SHF_ALLOC) != 0)
+ {
+ printf ("Cannot compress %zd %s\n", idx, name);
+ }
+ else if ((shdr->sh_flags & SHF_COMPRESSED) != 0
+ || strncmp (name, ".zdebug", strlen (".zdebug") == 0))
+ {
+ printf ("Already compressed %zd %s\n", idx, name);
+ }
+ else
+ {
+ size_t orig_size = shdr->sh_size;
+ printf ("Lets compress %zd %s, size: %zd\n",
+ idx, name, shdr->sh_size);
+ Elf_Data *d = elf_getdata (scn, NULL);
+ if (d == NULL)
+ {
+ printf ("Couldn't get orig data for section %zd\n", idx);
+ return -1;
+ }
+ /* Make a copy so we can compare after
+ compression/decompression. */
+ if (d->d_size != orig_size)
+ {
+ printf ("Unexpected data size for orig section %zd\n", idx);
+ return -1;
+ }
+ char *orig_buf = malloc (d->d_size);
+ if (orig_buf == NULL)
+ {
+ printf ("No memory to copy section %zd data\n", idx);
+ return -1;
+ }
+ memcpy (orig_buf, d->d_buf, orig_size);
+
+ if (gnu)
+ {
+ if (elf_compress_gnu (scn, 1) == -1)
+ {
+ printf ("elf_compress_gnu failed for section %zd: %s\n",
+ idx, elf_errmsg (-1));
+ return -1;
+ }
+ }
+ else
+ {
+ if (elf_compress (scn, ELFCOMPRESS_ZLIB) == -1)
+ {
+ printf ("elf_compress failed for section %zd: %s\n",
+ idx, elf_errmsg (-1));
+ return -1;
+ }
+ }
+ GElf_Shdr newmem;
+ GElf_Shdr *newshdr = gelf_getshdr (scn, &newmem);
+ size_t new_size = newshdr->sh_size;
+ d = elf_getdata (scn, NULL);
+ // Don't check this, might depend on zlib implementation.
+ // fprintf (stderr, " new_size: %zd\n", new_size);
+ if (d->d_size != new_size)
+ {
+ printf ("Unexpected data size for compressed section %zd\n",
+ idx);
+ return -1;
+ }
+ if (new_size == orig_size
+ && memcmp (orig_buf, d->d_buf, orig_size) == 0)
+ {
+ printf ("section %zd didn't compress\n", idx);
+ return -1;
+ }
+
+ if (gnu)
+ {
+ if (elf_compress_gnu (scn, 0) == -1)
+ {
+ printf ("elf_[un]compress_gnu failed for section %zd: %s\n",
+ idx, elf_errmsg (-1));
+ return -1;
+ }
+ }
+ else
+ {
+ if (elf_compress (scn, 0) == -1)
+ {
+ printf ("elf_[un]compress failed for section %zd: %s\n",
+ idx, elf_errmsg (-1));
+ return -1;
+ }
+ }
+ GElf_Shdr newermem;
+ GElf_Shdr *newershdr = gelf_getshdr (scn, &newermem);
+ size_t newer_size = newershdr->sh_size;
+ d = elf_getdata (scn, NULL);
+ // fprintf (stderr, " newer_size: %zd\n", newer_size);
+ if (d->d_size != newer_size)
+ {
+ printf ("Unexpected data size for compressed section %zd\n",
+ idx);
+ return -1;
+ }
+ if (newer_size != orig_size
+ && memcmp (orig_buf, d->d_buf, orig_size) != 0)
+ {
+ printf ("section %zd didn't correctly uncompress\n", idx);
+ return -1;
+ }
+ free (orig_buf);
+ }
+ }
+
+ elf_end (elf);
+ close (fd);
+ }
+
+ return result;
+}
diff --git a/tests/msg_tst.c b/tests/msg_tst.c
index 10ff0f73..7f20d108 100644
--- a/tests/msg_tst.c
+++ b/tests/msg_tst.c
@@ -74,7 +74,12 @@ static struct
"program header only allowed in executables, shared objects, \
and core files" },
{ ELF_E_NO_PHDR, "file has no program header" },
- { ELF_E_INVALID_OFFSET, "invalid offset" }
+ { ELF_E_INVALID_OFFSET, "invalid offset" },
+ { ELF_E_NOT_COMPRESSED, "section does not contain compressed data" },
+ { ELF_E_ALREADY_COMPRESSED, "section contains compressed data" },
+ { ELF_E_UNKNOWN_COMPRESSION_TYPE, "unknown compression type" },
+ { ELF_E_COMPRESS_ERROR, "cannot compress data" },
+ { ELF_E_DECOMPRESS_ERROR, "cannot decompress data" }
};
diff --git a/tests/run-elfgetzdata.sh b/tests/run-elfgetzdata.sh
new file mode 100755
index 00000000..c03ef6ae
--- /dev/null
+++ b/tests/run-elfgetzdata.sh
@@ -0,0 +1,70 @@
+#! /bin/sh
+# Copyright (C) 2015 Red Hat, Inc.
+# This file is part of elfutils.
+#
+# This file is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# elfutils is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+. $srcdir/test-subr.sh
+
+# See run-elfgetchdr.sh for testfiles.
+
+testfiles testfile-zgnu64
+testrun_compare ${abs_top_builddir}/tests/elfgetzdata testfile-zgnu64 <<\EOF
+1: .text, NOT compressed
+2: .zdebug_aranges, GNU compressed, size: 60
+3: .zdebug_info, GNU compressed, size: aa
+4: .debug_abbrev, NOT compressed
+5: .zdebug_line, GNU compressed, size: 8d
+6: .shstrtab, NOT compressed
+7: .symtab, NOT compressed
+8: .strtab, NOT compressed
+EOF
+
+testfiles testfile-zgabi64
+testrun_compare ${abs_top_builddir}/tests/elfgetzdata testfile-zgabi64 <<\EOF
+1: .text, NOT compressed
+2: .debug_aranges, ELF compressed, size: 60
+3: .debug_info, ELF compressed, size: aa
+4: .debug_abbrev, NOT compressed
+5: .debug_line, ELF compressed, size: 8d
+6: .shstrtab, NOT compressed
+7: .symtab, NOT compressed
+8: .strtab, NOT compressed
+EOF
+
+testfiles testfile-zgnu32
+testrun_compare ${abs_top_builddir}/tests/elfgetzdata testfile-zgnu32 <<\EOF
+1: .text, NOT compressed
+2: .zdebug_aranges, GNU compressed, size: 40
+3: .zdebug_info, GNU compressed, size: 9a
+4: .debug_abbrev, NOT compressed
+5: .zdebug_line, GNU compressed, size: 85
+6: .shstrtab, NOT compressed
+7: .symtab, NOT compressed
+8: .strtab, NOT compressed
+EOF
+
+testfiles testfile-zgabi32
+testrun_compare ${abs_top_builddir}/tests/elfgetzdata testfile-zgabi32 <<\EOF
+1: .text, NOT compressed
+2: .debug_aranges, ELF compressed, size: 40
+3: .debug_info, ELF compressed, size: 9a
+4: .debug_abbrev, NOT compressed
+5: .debug_line, ELF compressed, size: 85
+6: .shstrtab, NOT compressed
+7: .symtab, NOT compressed
+8: .strtab, NOT compressed
+EOF
+
+exit 0
diff --git a/tests/run-elfputzdata.sh b/tests/run-elfputzdata.sh
new file mode 100755
index 00000000..da423b5e
--- /dev/null
+++ b/tests/run-elfputzdata.sh
@@ -0,0 +1,174 @@
+#! /bin/sh
+# Copyright (C) 2015 Red Hat, Inc.
+# This file is part of elfutils.
+#
+# This file is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# elfutils is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+. $srcdir/test-subr.sh
+
+# Random ELF32 testfile
+testfiles testfile4
+
+testrun_compare ${abs_top_builddir}/tests/elfputzdata gnu testfile4 <<\EOF
+Cannot compress 1 .interp
+Cannot compress 2 .note.ABI-tag
+Cannot compress 3 .hash
+Cannot compress 4 .dynsym
+Cannot compress 5 .dynstr
+Cannot compress 6 .gnu.version
+Cannot compress 7 .gnu.version_r
+Cannot compress 8 .rel.got
+Cannot compress 9 .rel.plt
+Cannot compress 10 .init
+Cannot compress 11 .plt
+Cannot compress 12 .text
+Cannot compress 13 .fini
+Cannot compress 14 .rodata
+Cannot compress 15 .data
+Cannot compress 16 .eh_frame
+Cannot compress 17 .gcc_except_table
+Cannot compress 18 .ctors
+Cannot compress 19 .dtors
+Cannot compress 20 .got
+Cannot compress 21 .dynamic
+Lets compress 22 .sbss, size: 0
+Cannot compress 23 .bss
+Lets compress 24 .stab, size: 21540
+Lets compress 25 .stabstr, size: 57297
+Lets compress 26 .comment, size: 648
+Lets compress 27 .debug_aranges, size: 56
+Lets compress 28 .debug_pubnames, size: 93
+Lets compress 29 .debug_info, size: 960
+Lets compress 30 .debug_abbrev, size: 405
+Lets compress 31 .debug_line, size: 189
+Lets compress 32 .note, size: 240
+Not compressing section string table 33
+Lets compress 34 .symtab, size: 5488
+Lets compress 35 .strtab, size: 5727
+EOF
+
+testrun_compare ${abs_top_builddir}/tests/elfputzdata elf testfile4 <<\EOF
+Cannot compress 1 .interp
+Cannot compress 2 .note.ABI-tag
+Cannot compress 3 .hash
+Cannot compress 4 .dynsym
+Cannot compress 5 .dynstr
+Cannot compress 6 .gnu.version
+Cannot compress 7 .gnu.version_r
+Cannot compress 8 .rel.got
+Cannot compress 9 .rel.plt
+Cannot compress 10 .init
+Cannot compress 11 .plt
+Cannot compress 12 .text
+Cannot compress 13 .fini
+Cannot compress 14 .rodata
+Cannot compress 15 .data
+Cannot compress 16 .eh_frame
+Cannot compress 17 .gcc_except_table
+Cannot compress 18 .ctors
+Cannot compress 19 .dtors
+Cannot compress 20 .got
+Cannot compress 21 .dynamic
+Lets compress 22 .sbss, size: 0
+Cannot compress 23 .bss
+Lets compress 24 .stab, size: 21540
+Lets compress 25 .stabstr, size: 57297
+Lets compress 26 .comment, size: 648
+Lets compress 27 .debug_aranges, size: 56
+Lets compress 28 .debug_pubnames, size: 93
+Lets compress 29 .debug_info, size: 960
+Lets compress 30 .debug_abbrev, size: 405
+Lets compress 31 .debug_line, size: 189
+Lets compress 32 .note, size: 240
+Not compressing section string table 33
+Lets compress 34 .symtab, size: 5488
+Lets compress 35 .strtab, size: 5727
+EOF
+
+# Random ELF64 testfile
+testfiles testfile12
+
+testrun_compare ${abs_top_builddir}/tests/elfputzdata gnu testfile12 <<\EOF
+Cannot compress 1 .hash
+Cannot compress 2 .dynsym
+Cannot compress 3 .dynstr
+Cannot compress 4 .gnu.version
+Cannot compress 5 .gnu.version_r
+Cannot compress 6 .rela.dyn
+Cannot compress 7 .rela.plt
+Cannot compress 8 .init
+Cannot compress 9 .plt
+Cannot compress 10 .text
+Cannot compress 11 .fini
+Cannot compress 12 .rodata
+Cannot compress 13 .eh_frame_hdr
+Cannot compress 14 .eh_frame
+Cannot compress 15 .data
+Cannot compress 16 .dynamic
+Cannot compress 17 .ctors
+Cannot compress 18 .dtors
+Cannot compress 19 .jcr
+Cannot compress 20 .got
+Cannot compress 21 .bss
+Lets compress 22 .comment, size: 246
+Lets compress 23 .debug_aranges, size: 192
+Lets compress 24 .debug_pubnames, size: 26
+Lets compress 25 .debug_info, size: 3468
+Lets compress 26 .debug_abbrev, size: 341
+Lets compress 27 .debug_line, size: 709
+Lets compress 28 .debug_frame, size: 56
+Lets compress 29 .debug_str, size: 2235
+Lets compress 30 .debug_macinfo, size: 10518
+Not compressing section string table 31
+Lets compress 32 .symtab, size: 1944
+Lets compress 33 .strtab, size: 757
+EOF
+
+testrun_compare ${abs_top_builddir}/tests/elfputzdata elf testfile12 <<\EOF
+Cannot compress 1 .hash
+Cannot compress 2 .dynsym
+Cannot compress 3 .dynstr
+Cannot compress 4 .gnu.version
+Cannot compress 5 .gnu.version_r
+Cannot compress 6 .rela.dyn
+Cannot compress 7 .rela.plt
+Cannot compress 8 .init
+Cannot compress 9 .plt
+Cannot compress 10 .text
+Cannot compress 11 .fini
+Cannot compress 12 .rodata
+Cannot compress 13 .eh_frame_hdr
+Cannot compress 14 .eh_frame
+Cannot compress 15 .data
+Cannot compress 16 .dynamic
+Cannot compress 17 .ctors
+Cannot compress 18 .dtors
+Cannot compress 19 .jcr
+Cannot compress 20 .got
+Cannot compress 21 .bss
+Lets compress 22 .comment, size: 246
+Lets compress 23 .debug_aranges, size: 192
+Lets compress 24 .debug_pubnames, size: 26
+Lets compress 25 .debug_info, size: 3468
+Lets compress 26 .debug_abbrev, size: 341
+Lets compress 27 .debug_line, size: 709
+Lets compress 28 .debug_frame, size: 56
+Lets compress 29 .debug_str, size: 2235
+Lets compress 30 .debug_macinfo, size: 10518
+Not compressing section string table 31
+Lets compress 32 .symtab, size: 1944
+Lets compress 33 .strtab, size: 757
+EOF
+
+exit 0