diff options
author | Chih-Hung Hsieh <chh@google.com> | 2016-01-25 10:12:35 -0800 |
---|---|---|
committer | Chih-Hung Hsieh <chh@google.com> | 2016-01-25 11:25:11 -0800 |
commit | 3ad5c39aed9ad8b3847015a8349053b7a1ac094f (patch) | |
tree | 482d3d6644a743b44c3fc79c95e2b6b5e30e6ab2 /libelf | |
parent | 97c644e689be3b7b3b7898513a38ead399d2285a (diff) | |
parent | 203f0a3eec8c630c5183fb9984d66339c1ea3c31 (diff) | |
download | android_external_elfutils-3ad5c39aed9ad8b3847015a8349053b7a1ac094f.tar.gz android_external_elfutils-3ad5c39aed9ad8b3847015a8349053b7a1ac094f.tar.bz2 android_external_elfutils-3ad5c39aed9ad8b3847015a8349053b7a1ac094f.zip |
Merge upstream 0.165 SHA '203f0a3'
* git merge 203f0a3
* See all upstream changes since the previous merge
in branch aosp/upstream-master:
git diff 9d1e236..203f0a3
* Android relevant upstream changes:
* Version number changed from 0.164 to 0.165
* libelf now depends on zlib
* Changed version number in generated files:
version.h, config.h
* No change to generated files:
libdw/known-dwarf.h
* Updated libelf/Android.mk:
* added new source files:
gelf_getchdr.c elf{32,64}_getchdr.c elf_compress.c
* added LOCAL_STATIC_LIBRARIES := libz
Change-Id: Ieb306d84ffcaf680ad6e74da28fcc8efbf00592a
Diffstat (limited to 'libelf')
-rwxr-xr-x | libelf/Android.mk | 8 | ||||
-rw-r--r-- | libelf/ChangeLog | 80 | ||||
-rw-r--r-- | libelf/Makefile.am | 9 | ||||
-rw-r--r-- | libelf/abstract.h | 20 | ||||
-rw-r--r-- | libelf/chdr_xlate.h | 33 | ||||
-rw-r--r-- | libelf/elf32_getchdr.c | 83 | ||||
-rw-r--r-- | libelf/elf32_getshdr.c | 5 | ||||
-rw-r--r-- | libelf/elf32_updatenull.c | 23 | ||||
-rw-r--r-- | libelf/elf64_getchdr.c | 30 | ||||
-rw-r--r-- | libelf/elf_begin.c | 2 | ||||
-rw-r--r-- | libelf/elf_compress.c | 516 | ||||
-rw-r--r-- | libelf/elf_compress_gnu.c | 208 | ||||
-rw-r--r-- | libelf/elf_end.c | 11 | ||||
-rw-r--r-- | libelf/elf_error.c | 44 | ||||
-rw-r--r-- | libelf/elf_getdata.c | 41 | ||||
-rw-r--r-- | libelf/elf_strptr.c | 51 | ||||
-rw-r--r-- | libelf/exttypes.h | 4 | ||||
-rw-r--r-- | libelf/gelf.h | 8 | ||||
-rw-r--r-- | libelf/gelf_fsize.c | 1 | ||||
-rw-r--r-- | libelf/gelf_getchdr.c | 69 | ||||
-rw-r--r-- | libelf/gelf_xlate.c | 4 | ||||
-rw-r--r-- | libelf/gelf_xlate.h | 1 | ||||
-rw-r--r-- | libelf/libelf.h | 100 | ||||
-rw-r--r-- | libelf/libelf.map | 10 | ||||
-rw-r--r-- | libelf/libelfP.h | 35 |
25 files changed, 1361 insertions, 35 deletions
diff --git a/libelf/Android.mk b/libelf/Android.mk index 1c0195c4..53d47be4 100755 --- a/libelf/Android.mk +++ b/libelf/Android.mk @@ -17,6 +17,7 @@ LOCAL_PATH := $(call my-dir) LIBELF_SRC_FILES := \ elf32_checksum.c \ elf32_fsize.c \ + elf32_getchdr.c \ elf32_getehdr.c \ elf32_getphdr.c \ elf32_getshdr.c \ @@ -29,6 +30,7 @@ LIBELF_SRC_FILES := \ elf32_xlatetom.c \ elf64_checksum.c \ elf64_fsize.c \ + elf64_getchdr.c \ elf64_getehdr.c \ elf64_getphdr.c \ elf64_getshdr.c \ @@ -42,6 +44,7 @@ LIBELF_SRC_FILES := \ elf_begin.c \ elf_clone.c \ elf_cntl.c \ + elf_compress.c \ elf_end.c \ elf_error.c \ elf_fill.c \ @@ -82,6 +85,7 @@ LIBELF_SRC_FILES := \ gelf_checksum.c \ gelf_fsize.c \ gelf_getauxv.c \ + gelf_getchdr.c \ gelf_getclass.c \ gelf_getdyn.c \ gelf_getehdr.c \ @@ -148,6 +152,8 @@ LOCAL_CFLAGS += -Wno-pointer-arith LOCAL_MODULE := libelf +LOCAL_STATIC_LIBRARIES := libz + LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH) include $(BUILD_HOST_STATIC_LIBRARY) @@ -171,6 +177,8 @@ LOCAL_C_INCLUDES := \ LOCAL_C_INCLUDES += $(LOCAL_PATH)/../bionic-fixup +LOCAL_STATIC_LIBRARIES := libz + LOCAL_CFLAGS += -DHAVE_CONFIG_H -std=gnu99 -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 # to suppress the "pointer of type ‘void *’ used in arithmetic" warning diff --git a/libelf/ChangeLog b/libelf/ChangeLog index fbe8e3ae..afe6a6a5 100644 --- a/libelf/ChangeLog +++ b/libelf/ChangeLog @@ -1,3 +1,83 @@ +2016-01-22 Chih-Hung Hsieh <chh@google.com> + + * elf_compress.c (__libelf_compress): Move nested function + 'do_deflate_cleanup' to file scope to compile with clang. + * elf_strptr.c (elf_strptr): Move nested function 'get_zdata' + to file scope to compile with clang. + +2016-01-13 Mark Wielaard <mjw@redhat.com> + + * libelf.h: Check SHF_COMPRESSED is defined. If not define it and the + associated ELF compression types/defines. + +2015-11-26 Mark Wielaard <mjw@redhat.com> + + * elf_compress.c (__libelf_decompress_elf): New function, extracted + from... + (elf_compress): here. Check zdata_base use __libelf_decompress_elf. + * elf_strptr.c (elf_strptr): If SHF_COMPRESSED check, uncompress and + use zdata. + * libelfP.h (struct Elf_Scn): Add zdata_size and zdata_align. + (__libelf_decompress_elf): New internal function definition. + +2015-10-21 Mark Wielaard <mjw@redhat.com> + + * Makefile.am (libelf_a_SOURCES): Add elf_compress.c and + elf_compress_gnu.c. + * elf_compress.c: New file. + * elf_compress_gnu.c: Likewise. + * elf_begin.c (file_read_elf): Make a writable copy of the shdrs + for ELF_C_READ_MMAP. + * 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. + * elf32_updatenull.c (updatenull_wrlock): Check sh_entsize against + uncompressed section data size if SHF_COMPRESSED. + * elf32_getshdr.c (load_shdr_wrlock): Adjust assert to account for + ELF_C_READ_MMAP. + * libelf.h: Define elf_compress and elf_compress_gnu. + * libelf.map (ELFUTILS_1.7): Add elf_compress and elf_compress_gnu. + * 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. + (__libelf_data_type): Likewise. + (struct Elf_Scn): Add zdata_base. + +2015-11-19 Mark Wielaard <mjw@redhat.com> + + * Makefile.am (libelf_a_SOURCES): Add elf32_getchdr.c, + elf64_getchdr.c and gelf_getchdr.c. + (noinst_HEADERS): Add chdr_xlate.h. + * abstract.h: Define Chdr32 and Chdr64. + * chdr_xlate.h: New file. + * elf32_getchdr.c: New file. + * elf64_getchdr.c: New file. + * elf_error.c: Add ELF_E_NOT_COMPRESSED, ELF_E_INVALID_SECTION_TYPE + and ELF_E_INVALID_SECTION_FLAGS. + * elf_getdata.c (__libelf_set_rawdata_wrlock): Set d_type to + ELF_T_CHDR for SHF_COMPRESSED sections. + * exttypes.h: Add Chdr32 and Chdr64. + * gelf.h (GElf_Chdr): New typedef. + (gelf_getchdr): New function definition. + * gelf_fsize.c (__libelf_type_sizes): Add ELF_T_CHDR. + * gelf_getchdr.c: New file. + * gelf_xlate.c (__elf_xfctstom): Add ELF_T_CHDR cvt_chdr. + * gelf_xlate.h: Add Chdr. + * libelf.h (Elf_Type): Add ELF_T_CHDR. + (elf32_getchdr): New function definition. + (elf64_getchdr): Likewise. + * libelf.map (ELFUTILS_1.7): New sections add elf32_getchdr, + elf64_getchdr and gelf_getchdr. + * libelfP.h: Add ELF_E_NOT_COMPRESSED, ELF_E_INVALID_SECTION_TYPE + and ELF_E_INVALID_SECTION_FLAGS. + 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..167a8322 100644 --- a/libelf/Makefile.am +++ b/libelf/Makefile.am @@ -1,6 +1,6 @@ ## Process this file with automake to create Makefile.in ## -## Copyright (C) 1996-2010 Red Hat, Inc. +## Copyright (C) 1996-2010, 2015 Red Hat, Inc. ## This file is part of elfutils. ## ## This file is free software; you can redistribute it and/or modify @@ -88,7 +88,9 @@ 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 \ + elf32_getchdr.c elf64_getchdr.c gelf_getchdr.c \ + elf_compress.c elf_compress_gnu.c libelf_pic_a_SOURCES = am_libelf_pic_a_OBJECTS = $(libelf_a_SOURCES:.c=.os) @@ -118,7 +120,8 @@ uninstall: uninstall-am rm -f $(DESTDIR)$(libdir)/libelf.so noinst_HEADERS = elf.h abstract.h common.h exttypes.h gelf_xlate.h libelfP.h \ - version_xlate.h gnuhash_xlate.h note_xlate.h dl-hash.h + version_xlate.h gnuhash_xlate.h note_xlate.h dl-hash.h \ + chdr_xlate.h EXTRA_DIST = libelf.map CLEANFILES += $(am_libelf_pic_a_OBJECTS) libelf.so.$(VERSION) diff --git a/libelf/abstract.h b/libelf/abstract.h index 53713eec..d4515f27 100644 --- a/libelf/abstract.h +++ b/libelf/abstract.h @@ -1,5 +1,5 @@ /* Abstract description of component ELF types. - Copyright (C) 1998, 1999, 2000, 2002, 2004, 2007 Red Hat, Inc. + Copyright (C) 1998, 1999, 2000, 2002, 2004, 2007, 2015 Red Hat, Inc. This file is part of elfutils. Written by Ulrich Drepper <drepper@redhat.com>, 1998. @@ -310,3 +310,21 @@ START (64, auxv_t, Ext##auxv_t) \ TYPE_XLATE (Elf64_cvt_Addr1 (&tdest->a_un.a_val, &tsrc->a_un.a_val);) \ TYPE_EXTRA (} a_un;) \ END (64, Ext##auxv_t) + +/* Note that there is actual compression data right after the Chdr. + So we also have a separate conversion function for the whole + section. */ +#define Chdr32(Ext) \ +START (32, Chdr, Ext##Chdr) \ + TYPE_NAME (ElfW2(32, Ext##Word), ch_type) \ + TYPE_NAME (ElfW2(32, Ext##Word), ch_size) \ + TYPE_NAME (ElfW2(32, Ext##Word), ch_addralign) \ +END (32, Ext##Chdr) + +#define Chdr64(Ext) \ +START (64, Chdr, Ext##Chdr) \ + TYPE_NAME (ElfW2(64, Ext##Word), ch_type) \ + TYPE_NAME (ElfW2(64, Ext##Word), ch_reserved) \ + TYPE_NAME (ElfW2(64, Ext##Xword), ch_size) \ + TYPE_NAME (ElfW2(64, Ext##Xword), ch_addralign) \ +END (64, Ext##Chdr) diff --git a/libelf/chdr_xlate.h b/libelf/chdr_xlate.h new file mode 100644 index 00000000..70782b43 --- /dev/null +++ b/libelf/chdr_xlate.h @@ -0,0 +1,33 @@ +#include "common.h" + +/* These functions convert a while section, one Chdr plus compression data. */ + +static void +Elf32_cvt_chdr (void *dest, const void *src, size_t len, int encode) +{ + if (len == 0) + return; + + /* Move everything over, if necessary, we only need to xlate the + header, not the compressed data following it. */ + if (dest != src) + memmove (dest, src, len); + + if (len >= sizeof (Elf32_Chdr)) + Elf32_cvt_Chdr (dest, src, sizeof (Elf32_Chdr), encode); +} + +static void +Elf64_cvt_chdr (void *dest, const void *src, size_t len, int encode) +{ + if (len == 0) + return; + + /* Move everything over, if necessary, we only need to xlate the + header, not the compressed data following it. */ + if (dest != src) + memmove (dest, src, len); + + if (len >= sizeof (Elf64_Chdr)) + Elf64_cvt_Chdr (dest, src, sizeof (Elf64_Chdr), encode); +} diff --git a/libelf/elf32_getchdr.c b/libelf/elf32_getchdr.c new file mode 100644 index 00000000..982a614c --- /dev/null +++ b/libelf/elf32_getchdr.c @@ -0,0 +1,83 @@ +/* Return section compression header. + 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" + +#ifndef LIBELFBITS +# define LIBELFBITS 32 +#endif + + +ElfW2(LIBELFBITS,Chdr) * +elfw2(LIBELFBITS,getchdr) (Elf_Scn *scn) +{ + ElfW2(LIBELFBITS,Shdr) *shdr = elfw2(LIBELFBITS,getshdr) (scn); + if (shdr == NULL) + return NULL; + + /* Must have SHF_COMPRESSED flag set. Allocated or no bits sections + can never be compressed. */ + if ((shdr->sh_flags & SHF_ALLOC) != 0) + { + __libelf_seterrno (ELF_E_INVALID_SECTION_FLAGS); + return NULL; + } + + if (shdr->sh_type == SHT_NULL + || shdr->sh_type == SHT_NOBITS) + { + __libelf_seterrno (ELF_E_INVALID_SECTION_TYPE); + return NULL; + } + + if ((shdr->sh_flags & SHF_COMPRESSED) == 0) + { + __libelf_seterrno (ELF_E_NOT_COMPRESSED); + return NULL; + } + + /* This makes sure the data is in the correct format, so we don't + need to swap fields. */ + Elf_Data *d = elf_getdata (scn, NULL); + if (d == NULL) + return NULL; + + if (d->d_size < sizeof (ElfW2(LIBELFBITS,Chdr)) || d->d_buf == NULL) + { + __libelf_seterrno (ELF_E_INVALID_DATA); + return NULL; + } + + return (ElfW2(LIBELFBITS,Chdr) *) d->d_buf; +} diff --git a/libelf/elf32_getshdr.c b/libelf/elf32_getshdr.c index 3a6375c1..237d9122 100644 --- a/libelf/elf32_getshdr.c +++ b/libelf/elf32_getshdr.c @@ -99,6 +99,7 @@ load_shdr_wrlock (Elf_Scn *scn) assert ((elf->flags & ELF_F_MALLOCED) || ehdr->e_ident[EI_DATA] != MY_ELFDATA + || elf->cmd == ELF_C_READ_MMAP || (! ALLOW_UNALIGNED && ((uintptr_t) file_shdr & (__alignof__ (ElfW2(LIBELFBITS,Shdr)) - 1)) != 0)); @@ -106,7 +107,9 @@ load_shdr_wrlock (Elf_Scn *scn) /* Now copy the data and at the same time convert the byte order. */ if (ehdr->e_ident[EI_DATA] == MY_ELFDATA) { - assert ((elf->flags & ELF_F_MALLOCED) || ! ALLOW_UNALIGNED); + assert ((elf->flags & ELF_F_MALLOCED) + || elf->cmd == ELF_C_READ_MMAP + || ! ALLOW_UNALIGNED); memcpy (shdr, file_shdr, size); } else diff --git a/libelf/elf32_updatenull.c b/libelf/elf32_updatenull.c index d3754d32..03de0321 100644 --- a/libelf/elf32_updatenull.c +++ b/libelf/elf32_updatenull.c @@ -382,12 +382,27 @@ __elfw2(LIBELFBITS,updatenull_wrlock) (Elf *elf, int *change_bop, size_t shnum) /* Check that the section size is actually a multiple of the entry size. */ - if (shdr->sh_entsize != 0 - && unlikely (shdr->sh_size % shdr->sh_entsize != 0) + if (shdr->sh_entsize != 0 && shdr->sh_entsize != 1 && (elf->flags & ELF_F_PERMISSIVE) == 0) { - __libelf_seterrno (ELF_E_INVALID_SHENTSIZE); - return -1; + /* For compressed sections check the uncompressed size. */ + ElfW2(LIBELFBITS,Word) sh_size; + if ((shdr->sh_flags & SHF_COMPRESSED) == 0) + sh_size = shdr->sh_size; + else + { + ElfW2(LIBELFBITS,Chdr) *chdr; + chdr = elfw2(LIBELFBITS,getchdr) (scn); + if (unlikely (chdr == NULL)) + return -1; + sh_size = chdr->ch_size; + } + + if (unlikely (sh_size % shdr->sh_entsize != 0)) + { + __libelf_seterrno (ELF_E_INVALID_SHENTSIZE); + return -1; + } } } diff --git a/libelf/elf64_getchdr.c b/libelf/elf64_getchdr.c new file mode 100644 index 00000000..6588b791 --- /dev/null +++ b/libelf/elf64_getchdr.c @@ -0,0 +1,30 @@ +/* Return section compression header. + 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/>. */ + +#define LIBELFBITS 64 +#include "elf32_getchdr.c" diff --git a/libelf/elf_begin.c b/libelf/elf_begin.c index d2920c4e..147f5f4b 100644 --- a/libelf/elf_begin.c +++ b/libelf/elf_begin.c @@ -340,6 +340,7 @@ file_read_elf (int fildes, void *map_address, unsigned char *e_ident, Elf32_Off e_shoff = elf->state.elf32.ehdr->e_shoff; if (map_address != NULL && e_ident[EI_DATA] == MY_ELFDATA + && cmd != ELF_C_READ_MMAP /* We need a copy to be able to write. */ && (ALLOW_UNALIGNED || (((uintptr_t) ((char *) ehdr + e_shoff) & (__alignof__ (Elf32_Shdr) - 1)) == 0))) @@ -441,6 +442,7 @@ file_read_elf (int fildes, void *map_address, unsigned char *e_ident, Elf64_Off e_shoff = elf->state.elf64.ehdr->e_shoff; if (map_address != NULL && e_ident[EI_DATA] == MY_ELFDATA + && cmd != ELF_C_READ_MMAP /* We need a copy to be able to write. */ && (ALLOW_UNALIGNED || (((uintptr_t) ((char *) ehdr + e_shoff) & (__alignof__ (Elf64_Shdr) - 1)) == 0))) diff --git a/libelf/elf_compress.c b/libelf/elf_compress.c new file mode 100644 index 00000000..4c7c35e1 --- /dev/null +++ b/libelf/elf_compress.c @@ -0,0 +1,516 @@ +/* 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 <sys/param.h> +#include <unistd.h> +#include <zlib.h> + +#ifndef MAX +# define MAX(a, b) ((a) > (b) ? (a) : (b)) +#endif + +/* Cleanup and return result. Don't leak memory. */ +static void * +do_deflate_cleanup (void *result, z_stream *z, void *out_buf, + int ei_data, Elf_Data *cdatap) +{ + deflateEnd (z); + free (out_buf); + if (ei_data != MY_ELFDATA) + free (cdatap->d_buf); + return result; +} + +#define deflate_cleanup(result) \ + do_deflate_cleanup(result, &z, out_buf, ei_data, &cdata) + +/* Given a section, uses the (in-memory) Elf_Data to extract the + original data size (including the given header size) and data + alignment. Returns a buffer that has at least hsize bytes (for the + caller to fill in with a header) plus zlib compressed date. Also + returns the new buffer size in new_size (hsize + compressed data + size). Returns (void *) -1 when FORCE is false and the compressed + data would be bigger than the original data. */ +void * +internal_function +__libelf_compress (Elf_Scn *scn, size_t hsize, int ei_data, + size_t *orig_size, size_t *orig_addralign, + size_t *new_size, bool force) +{ + /* 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; + + /* When not forced and we immediately know we would use more data by + compressing, because of the header plus zlib overhead (five bytes + per 16 KB block, plus a one-time overhead of six bytes for the + entire stream), don't do anything. */ + Elf_Data *next_data = elf_getdata (scn, data); + if (next_data == NULL && !force + && data->d_size <= hsize + 5 + 6) + return (void *) -1; + + *orig_addralign = data->d_align; + *orig_size = data->d_size; + + /* Guess an output block size. 1/8th of the original Elf_Data plus + hsize. Make the first chunk twice that size (25%), then increase + by a block (12.5%) when necessary. */ + size_t block = (data->d_size / 8) + hsize; + size_t out_size = 2 * block; + void *out_buf = malloc (out_size); + if (out_buf == NULL) + { + __libelf_seterrno (ELF_E_NOMEM); + return NULL; + } + + /* Caller gets to fill in the header at the start. Just skip it here. */ + 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; + } + + Elf_Data cdata; + cdata.d_buf = NULL; + + /* Loop over data buffers. */ + int flush = Z_NO_FLUSH; + do + { + /* Convert to raw if different endianess. */ + cdata = *data; + if (ei_data != MY_ELFDATA) + { + /* Don't do this conversion in place, we might want to keep + the original data around, caller decides. */ + cdata.d_buf = malloc (data->d_size); + if (cdata.d_buf == NULL) + { + __libelf_seterrno (ELF_E_NOMEM); + return deflate_cleanup (NULL); + } + if (gelf_xlatetof (scn->elf, &cdata, data, ei_data) == NULL) + return deflate_cleanup (NULL); + } + + z.avail_in = cdata.d_size; + z.next_in = cdata.d_buf; + + /* Get next buffer to see if this is the last one. */ + data = next_data; + if (data != NULL) + { + *orig_addralign = MAX (*orig_addralign, data->d_align); + *orig_size += data->d_size; + next_data = elf_getdata (scn, data); + } + else + flush = Z_FINISH; + + /* 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) + { + __libelf_seterrno (ELF_E_COMPRESS_ERROR); + return deflate_cleanup (NULL); + } + used += (out_size - used) - z.avail_out; + + /* Bail out if we are sure the user doesn't want the + compression forced and we are using more compressed data + than original data. */ + if (!force && flush == Z_FINISH && used >= *orig_size) + return deflate_cleanup ((void *) -1); + + if (z.avail_out == 0) + { + void *bigger = realloc (out_buf, out_size + block); + if (bigger == NULL) + { + __libelf_seterrno (ELF_E_NOMEM); + return deflate_cleanup (NULL); + } + out_buf = bigger; + out_size += block; + } + } + while (z.avail_out == 0); /* Need more output buffer. */ + + if (ei_data != MY_ELFDATA) + { + free (cdata.d_buf); + cdata.d_buf = NULL; + } + } + while (flush != Z_FINISH); /* More data blocks. */ + + zrc = deflateEnd (&z); + if (zrc != Z_OK) + { + __libelf_seterrno (ELF_E_COMPRESS_ERROR); + return deflate_cleanup (NULL); + } + + *new_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_decompress_elf (Elf_Scn *scn, size_t *size_out, size_t *addralign) +{ + GElf_Chdr chdr; + if (gelf_getchdr (scn, &chdr) == NULL) + return NULL; + + if (chdr.ch_type != ELFCOMPRESS_ZLIB) + { + __libelf_seterrno (ELF_E_UNKNOWN_COMPRESSION_TYPE); + return NULL; + } + + if (! powerof2 (chdr.ch_addralign)) + { + __libelf_seterrno (ELF_E_INVALID_ALIGN); + return NULL; + } + + /* Take the in-memory representation, so we can even handle a + section that has just been constructed (maybe it was copied + over from some other ELF file first with elf_newdata). This + is slightly inefficient when the raw data needs to be + converted since then we'll be converting the whole buffer and + not just Chdr. */ + Elf_Data *data = elf_getdata (scn, NULL); + if (data == NULL) + return NULL; + + int elfclass = scn->elf->class; + size_t hsize = (elfclass == ELFCLASS32 + ? sizeof (Elf32_Chdr) : sizeof (Elf64_Chdr)); + size_t size_in = data->d_size - hsize; + void *buf_in = data->d_buf + hsize; + void *buf_out = __libelf_decompress (buf_in, size_in, chdr.ch_size); + *size_out = chdr.ch_size; + *addralign = chdr.ch_addralign; + return buf_out; +} + +void +internal_function +__libelf_reset_rawdata (Elf_Scn *scn, void *buf, size_t size, size_t align, + Elf_Type type) +{ + /* This is the new raw data, replace and possibly free old data. */ + scn->rawdata.d.d_off = 0; + scn->rawdata.d.d_version = __libelf_version; + scn->rawdata.d.d_buf = buf; + scn->rawdata.d.d_size = size; + scn->rawdata.d.d_align = align; + scn->rawdata.d.d_type = type; + + /* 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, unsigned int flags) +{ + if (scn == NULL) + return -1; + + if ((flags & ~ELF_CHF_FORCE) != 0) + { + __libelf_seterrno (ELF_E_INVALID_OPERAND); + return -1; + } + + bool force = (flags & ELF_CHF_FORCE) != 0; + + Elf *elf = scn->elf; + GElf_Ehdr ehdr; + if (gelf_getehdr (elf, &ehdr) == NULL) + return -1; + + int elfclass = elf->class; + int elfdata = ehdr.e_ident[EI_DATA]; + + Elf64_Xword sh_flags; + Elf64_Word sh_type; + Elf64_Xword sh_addralign; + if (elfclass == ELFCLASS32) + { + Elf32_Shdr *shdr = elf32_getshdr (scn); + if (shdr == NULL) + return -1; + + sh_flags = shdr->sh_flags; + sh_type = shdr->sh_type; + sh_addralign = shdr->sh_addralign; + } + else + { + Elf64_Shdr *shdr = elf64_getshdr (scn); + if (shdr == NULL) + return -1; + + sh_flags = shdr->sh_flags; + sh_type = shdr->sh_type; + sh_addralign = shdr->sh_addralign; + } + + if ((sh_flags & SHF_ALLOC) != 0) + { + __libelf_seterrno (ELF_E_INVALID_SECTION_FLAGS); + return -1; + } + + if (sh_type == SHT_NULL || sh_type == SHT_NOBITS) + { + __libelf_seterrno (ELF_E_INVALID_SECTION_TYPE); + return -1; + } + + int compressed = (sh_flags & SHF_COMPRESSED); + if (type == ELFCOMPRESS_ZLIB) + { + /* Compress/Deflate. */ + if (compressed == 1) + { + __libelf_seterrno (ELF_E_ALREADY_COMPRESSED); + return -1; + } + + size_t hsize = (elfclass == ELFCLASS32 + ? sizeof (Elf32_Chdr) : sizeof (Elf64_Chdr)); + size_t orig_size, orig_addralign, new_size; + void *out_buf = __libelf_compress (scn, hsize, elfdata, + &orig_size, &orig_addralign, + &new_size, force); + + /* Compression would make section larger, don't change anything. */ + if (out_buf == (void *) -1) + return 0; + + /* Compression failed, return error. */ + if (out_buf == NULL) + return -1; + + /* 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 = orig_addralign; + if (elfdata != MY_ELFDATA) + { + CONVERT (chdr.ch_type); + CONVERT (chdr.ch_size); + CONVERT (chdr.ch_addralign); + } + memcpy (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 = sh_addralign; + if (elfdata != MY_ELFDATA) + { + CONVERT (chdr.ch_type); + CONVERT (chdr.ch_reserved); + CONVERT (chdr.ch_size); + CONVERT (chdr.ch_addralign); + } + memcpy (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. */ + if (elfclass == ELFCLASS32) + { + Elf32_Shdr *shdr = elf32_getshdr (scn); + shdr->sh_size = new_size; + shdr->sh_addralign = 1; + shdr->sh_flags |= SHF_COMPRESSED; + } + else + { + Elf64_Shdr *shdr = elf64_getshdr (scn); + shdr->sh_size = new_size; + shdr->sh_addralign = 1; + shdr->sh_flags |= SHF_COMPRESSED; + } + + __libelf_reset_rawdata (scn, out_buf, new_size, 1, ELF_T_CHDR); + + /* 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 1; + } + else if (type == 0) + { + /* Decompress/Inflate. */ + if (compressed == 0) + { + __libelf_seterrno (ELF_E_NOT_COMPRESSED); + return -1; + } + + /* If the data is already decompressed (by elf_strptr), then we + only need to setup the rawdata and section header. XXX what + about elf_newdata? */ + if (scn->zdata_base == NULL) + { + size_t size_out, addralign; + void *buf_out = __libelf_decompress_elf (scn, &size_out, &addralign); + if (buf_out == NULL) + return -1; + + scn->zdata_base = buf_out; + scn->zdata_size = size_out; + scn->zdata_align = addralign; + } + + /* Note we keep the sh_entsize as is, we assume it is setup + correctly and ignored when SHF_COMPRESSED is set. */ + if (elfclass == ELFCLASS32) + { + Elf32_Shdr *shdr = elf32_getshdr (scn); + shdr->sh_size = scn->zdata_size; + shdr->sh_addralign = scn->zdata_align; + shdr->sh_flags &= ~SHF_COMPRESSED; + } + else + { + Elf64_Shdr *shdr = elf64_getshdr (scn); + shdr->sh_size = scn->zdata_size; + shdr->sh_addralign = scn->zdata_align; + shdr->sh_flags &= ~SHF_COMPRESSED; + } + + __libelf_reset_rawdata (scn, scn->zdata_base, + scn->zdata_size, scn->zdata_align, + __libelf_data_type (elf, sh_type)); + + return 1; + } + 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..c35dc395 --- /dev/null +++ b/libelf/elf_compress_gnu.c @@ -0,0 +1,208 @@ +/* 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, unsigned int flags) +{ + if (scn == NULL) + return -1; + + if ((flags & ~ELF_CHF_FORCE) != 0) + { + __libelf_seterrno (ELF_E_INVALID_OPERAND); + return -1; + } + + bool force = (flags & ELF_CHF_FORCE) != 0; + + Elf *elf = scn->elf; + GElf_Ehdr ehdr; + if (gelf_getehdr (elf, &ehdr) == NULL) + return -1; + + int elfclass = elf->class; + int elfdata = ehdr.e_ident[EI_DATA]; + + Elf64_Xword sh_flags; + Elf64_Word sh_type; + Elf64_Xword sh_addralign; + if (elfclass == ELFCLASS32) + { + Elf32_Shdr *shdr = elf32_getshdr (scn); + if (shdr == NULL) + return -1; + + sh_flags = shdr->sh_flags; + sh_type = shdr->sh_type; + sh_addralign = shdr->sh_addralign; + } + else + { + Elf64_Shdr *shdr = elf64_getshdr (scn); + if (shdr == NULL) + return -1; + + sh_flags = shdr->sh_flags; + sh_type = shdr->sh_type; + sh_addralign = shdr->sh_addralign; + } + + if ((sh_flags & SHF_ALLOC) != 0) + { + __libelf_seterrno (ELF_E_INVALID_SECTION_FLAGS); + return -1; + } + + if (sh_type == SHT_NULL || sh_type == SHT_NOBITS) + { + __libelf_seterrno (ELF_E_INVALID_SECTION_TYPE); + return -1; + } + + /* For GNU compression we cannot really know whether the section is + already compressed or not. Just try and see what happens... */ + // int compressed = (sh_flags & SHF_COMPRESSED); + if (inflate == 1) + { + size_t hsize = 4 + 8; /* GNU "ZLIB" + 8 byte size. */ + size_t orig_size, new_size, orig_addralign; + void *out_buf = __libelf_compress (scn, hsize, elfdata, + &orig_size, &orig_addralign, + &new_size, force); + + /* Compression would make section larger, don't change anything. */ + if (out_buf == (void *) -1) + return 0; + + /* Compression failed, return error. */ + if (out_buf == NULL) + return -1; + + 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. */ + if (elfclass == ELFCLASS32) + { + Elf32_Shdr *shdr = elf32_getshdr (scn); + shdr->sh_size = new_size; + } + else + { + Elf64_Shdr *shdr = elf64_getshdr (scn); + shdr->sh_size = new_size; + } + + __libelf_reset_rawdata (scn, out_buf, new_size, 1, ELF_T_BYTE); + + /* 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 1; + } + 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. */ + if (elfclass == ELFCLASS32) + { + Elf32_Shdr *shdr = elf32_getshdr (scn); + shdr->sh_size = size; + } + else + { + Elf64_Shdr *shdr = elf64_getshdr (scn); + shdr->sh_size = size; + } + + __libelf_reset_rawdata (scn, buf_out, size, sh_addralign, + __libelf_data_type (elf, sh_type)); + + scn->zdata_base = buf_out; + + return 1; + } + 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..888b389a 100644 --- a/libelf/elf_error.c +++ b/libelf/elf_error.c @@ -230,6 +230,41 @@ core files") (ELF_E_NO_PHDR_IDX \ + sizeof "file has no program header") N_("invalid offset") + "\0" +#define ELF_E_INVALID_SECTION_TYPE_IDX \ + (ELF_E_INVALID_OFFSET_IDX \ + + sizeof "invalid offset") + N_("invalid section type") + "\0" +#define ELF_E_INVALID_SECTION_FLAGS_IDX \ + (ELF_E_INVALID_SECTION_TYPE_IDX \ + + sizeof "invalid section type") + N_("invalid section flags") + "\0" +#define ELF_E_NOT_COMPRESSED_IDX \ + (ELF_E_INVALID_SECTION_FLAGS_IDX \ + + sizeof "invalid section flags") + 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 +312,14 @@ 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_INVALID_SECTION_TYPE] = ELF_E_INVALID_SECTION_TYPE_IDX, + [ELF_E_INVALID_SECTION_FLAGS] = ELF_E_INVALID_SECTION_FLAGS_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..4ec94b98 100644 --- a/libelf/elf_getdata.c +++ b/libelf/elf_getdata.c @@ -106,6 +106,7 @@ const uint_fast8_t __libelf_type_aligns[EV_NUM - 1][ELFCLASSNUM - 1][ELF_T_NUM] [ELF_T_NHDR] = __alignof__ (ElfW2(Bits,Nhdr)), \ [ELF_T_GNUHASH] = __alignof__ (Elf32_Word), \ [ELF_T_AUXV] = __alignof__ (ElfW2(Bits,auxv_t)), \ + [ELF_T_CHDR] = __alignof__ (ElfW2(Bits,Chdr)), \ } [EV_CURRENT - 1] = { @@ -117,6 +118,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 +221,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 +238,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 +253,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 +263,10 @@ __libelf_set_rawdata_wrlock (Elf_Scn *scn) /* First a test whether the section is valid at all. */ size_t entsize; - if (type == SHT_HASH) + /* Compressed data has a header, but then compressed data. */ + 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 +343,13 @@ __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); - } + + /* Compressed data always has type ELF_T_CHDR regardless of the + section type. */ + if ((flags & SHF_COMPRESSED) != 0) + scn->rawdata.d.d_type = ELF_T_CHDR; 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/elf_strptr.c b/libelf/elf_strptr.c index c5138dc6..ea210459 100644 --- a/libelf/elf_strptr.c +++ b/libelf/elf_strptr.c @@ -37,6 +37,21 @@ #include "libelfP.h" +static void * +get_zdata (Elf_Scn *strscn) +{ + size_t zsize, zalign; + void *zdata = __libelf_decompress_elf (strscn, &zsize, &zalign); + if (zdata == NULL) + return NULL; + + strscn->zdata_base = zdata; + strscn->zdata_size = zsize; + strscn->zdata_align = zalign; + + return zdata; +} + char * elf_strptr (Elf *elf, size_t idx, size_t offset) { @@ -94,8 +109,16 @@ elf_strptr (Elf *elf, size_t idx, size_t offset) goto out; } - sh_size = shdr->sh_size; - if (unlikely (offset >= shdr->sh_size)) + if ((shdr->sh_flags & SHF_COMPRESSED) == 0) + sh_size = shdr->sh_size; + else + { + if (strscn->zdata_base == NULL && get_zdata (strscn) == NULL) + goto out; + sh_size = strscn->zdata_size; + } + + if (unlikely (offset >= sh_size)) { /* The given offset is too big, it is beyond this section. */ __libelf_seterrno (ELF_E_OFFSET_RANGE); @@ -112,8 +135,16 @@ elf_strptr (Elf *elf, size_t idx, size_t offset) goto out; } - sh_size = shdr->sh_size; - if (unlikely (offset >= shdr->sh_size)) + if ((shdr->sh_flags & SHF_COMPRESSED) == 0) + sh_size = shdr->sh_size; + else + { + if (strscn->zdata_base == NULL && get_zdata (strscn) == NULL) + goto out; + sh_size = strscn->zdata_size; + } + + if (unlikely (offset >= sh_size)) { /* The given offset is too big, it is beyond this section. */ __libelf_seterrno (ELF_E_OFFSET_RANGE); @@ -131,7 +162,17 @@ elf_strptr (Elf *elf, size_t idx, size_t offset) goto out; } - if (likely (strscn->data_list_rear == NULL)) + if (unlikely (strscn->zdata_base != NULL)) + { + /* Make sure the string is NUL terminated. Start from the end, + which very likely is a NUL char. */ + if (likely (memrchr (&strscn->zdata_base[offset], + '\0', sh_size - offset) != NULL)) + result = &strscn->zdata_base[offset]; + else + __libelf_seterrno (ELF_E_INVALID_INDEX); + } + else if (likely (strscn->data_list_rear == NULL)) { // XXX The above is currently correct since elf_newdata will // make sure to convert the rawdata into the datalist if diff --git a/libelf/exttypes.h b/libelf/exttypes.h index 8cb2aaec..7bacd654 100644 --- a/libelf/exttypes.h +++ b/libelf/exttypes.h @@ -1,5 +1,5 @@ /* External ELF types. - Copyright (C) 1998-2010 Red Hat, Inc. + Copyright (C) 1998-2010, 2015 Red Hat, Inc. This file is part of elfutils. Contributed by Ulrich Drepper <drepper@redhat.com>, 1998. @@ -75,6 +75,7 @@ Syminfo32 (Ext_); Move32 (Ext_); Lib32 (Ext_); auxv_t32 (Ext_); +Chdr32 (Ext_); Ehdr64 (Ext_); Phdr64 (Ext_); @@ -92,6 +93,7 @@ Syminfo64 (Ext_); Move64 (Ext_); Lib64 (Ext_); auxv_t64 (Ext_); +Chdr64 (Ext_); #undef START #undef END diff --git a/libelf/gelf.h b/libelf/gelf.h index e3f07404..1bc7ee72 100644 --- a/libelf/gelf.h +++ b/libelf/gelf.h @@ -1,5 +1,5 @@ /* This file defines generic ELF types, structures, and macros. - Copyright (C) 1999, 2000, 2001, 2002, 2004, 2005, 2007 Red Hat, Inc. + Copyright (C) 1999, 2000, 2001, 2002, 2004, 2005, 2007, 2015 Red Hat, Inc. This file is part of elfutils. This file is free software; you can redistribute it and/or modify @@ -85,6 +85,9 @@ typedef Elf64_Rela GElf_Rela; /* Program segment header. */ typedef Elf64_Phdr GElf_Phdr; +/* Header of a compressed section. */ +typedef Elf64_Chdr GElf_Chdr; + /* Dynamic section entry. */ typedef Elf64_Dyn GElf_Dyn; @@ -183,6 +186,9 @@ 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); +/* Get compression header of section if any. Returns NULL and sets + elf_errno if the section isn't compressed or an error occurred. */ +extern GElf_Chdr *gelf_getchdr (Elf_Scn *__scn, GElf_Chdr *__dst); /* Convert data structure from the representation in the file represented by ELF to their memory representation. */ diff --git a/libelf/gelf_fsize.c b/libelf/gelf_fsize.c index a124fa84..0c509265 100644 --- a/libelf/gelf_fsize.c +++ b/libelf/gelf_fsize.c @@ -68,6 +68,7 @@ const size_t __libelf_type_sizes[EV_NUM - 1][ELFCLASSNUM - 1][ELF_T_NUM] = [ELF_T_MOVE] = sizeof (ElfW2(LIBELFBITS, Ext_Move)), \ [ELF_T_LIB] = sizeof (ElfW2(LIBELFBITS, Ext_Lib)), \ [ELF_T_AUXV] = sizeof (ElfW2(LIBELFBITS, Ext_auxv_t)), \ + [ELF_T_CHDR] = sizeof (ElfW2(LIBELFBITS, Ext_Chdr)), \ [ELF_T_GNUHASH] = ELFW2(LIBELFBITS, FSZ_WORD) TYPE_SIZES (32) }, diff --git a/libelf/gelf_getchdr.c b/libelf/gelf_getchdr.c new file mode 100644 index 00000000..394bf4b3 --- /dev/null +++ b/libelf/gelf_getchdr.c @@ -0,0 +1,69 @@ +/* Return section compression header. + 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 "libelfP.h" +#include <gelf.h> +#include <stddef.h> + + +GElf_Chdr * +gelf_getchdr (Elf_Scn *scn, GElf_Chdr *dest) +{ + if (scn == NULL) + return NULL; + + if (dest == NULL) + { + __libelf_seterrno (ELF_E_INVALID_OPERAND); + return NULL; + } + + if (scn->elf->class == ELFCLASS32) + { + Elf32_Chdr *chdr = elf32_getchdr (scn); + if (chdr == NULL) + return NULL; + dest->ch_type = chdr->ch_type; + dest->ch_size = chdr->ch_size; + dest->ch_addralign = chdr->ch_addralign; + } + else + { + Elf64_Chdr *chdr = elf64_getchdr (scn); + if (chdr == NULL) + return NULL; + *dest = *chdr; + } + + return dest; +} +INTDEF(gelf_getchdr) diff --git a/libelf/gelf_xlate.c b/libelf/gelf_xlate.c index c5805e73..f3d3b7a0 100644 --- a/libelf/gelf_xlate.c +++ b/libelf/gelf_xlate.c @@ -166,6 +166,7 @@ union unaligned #include "version_xlate.h" #include "gnuhash_xlate.h" #include "note_xlate.h" +#include "chdr_xlate.h" /* Now the externally visible table with the function pointers. */ @@ -198,7 +199,8 @@ const xfct_t __elf_xfctstom[EV_NUM - 1][EV_NUM - 1][ELFCLASSNUM - 1][ELF_T_NUM] [ELF_T_SYMINFO] = ElfW2(Bits, cvt_Syminfo), \ [ELF_T_MOVE] = ElfW2(Bits, cvt_Move), \ [ELF_T_LIB] = ElfW2(Bits, cvt_Lib), \ - [ELF_T_AUXV] = ElfW2(Bits, cvt_auxv_t) + [ELF_T_AUXV] = ElfW2(Bits, cvt_auxv_t), \ + [ELF_T_CHDR] = ElfW2(Bits, cvt_chdr) define_xfcts (32), [ELF_T_GNUHASH] = Elf32_cvt_Word }, diff --git a/libelf/gelf_xlate.h b/libelf/gelf_xlate.h index f11eb90b..3c0e4bf6 100644 --- a/libelf/gelf_xlate.h +++ b/libelf/gelf_xlate.h @@ -50,6 +50,7 @@ TYPE (Syminfo, LIBELFBITS) TYPE (Move, LIBELFBITS) TYPE (Lib, LIBELFBITS) TYPE (auxv_t, LIBELFBITS) +TYPE (Chdr, LIBELFBITS) /* Prepare for the next round. */ diff --git a/libelf/libelf.h b/libelf/libelf.h index 54f7c29b..c0d6389f 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 @@ -35,6 +35,34 @@ /* Get the ELF types. */ #include <elf.h> +#ifndef SHF_COMPRESSED + /* Older glibc elf.h might not yet define the ELF compression types. */ + #define SHF_COMPRESSED (1 << 11) /* Section with compressed data. */ + + /* Section compression header. Used when SHF_COMPRESSED is set. */ + + typedef struct + { + Elf32_Word ch_type; /* Compression format. */ + Elf32_Word ch_size; /* Uncompressed data size. */ + Elf32_Word ch_addralign; /* Uncompressed data alignment. */ + } Elf32_Chdr; + + typedef struct + { + Elf64_Word ch_type; /* Compression format. */ + Elf64_Word ch_reserved; + Elf64_Xword ch_size; /* Uncompressed data size. */ + Elf64_Xword ch_addralign; /* Uncompressed data alignment. */ + } Elf64_Chdr; + + /* Legal values for ch_type (compression algorithm). */ + #define ELFCOMPRESS_ZLIB 1 /* ZLIB/DEFLATE algorithm. */ + #define ELFCOMPRESS_LOOS 0x60000000 /* Start of OS-specific. */ + #define ELFCOMPRESS_HIOS 0x6fffffff /* End of OS-specific. */ + #define ELFCOMPRESS_LOPROC 0x70000000 /* Start of processor-specific. */ + #define ELFCOMPRESS_HIPROC 0x7fffffff /* End of processor-specific. */ +#endif /* Known translation types. */ typedef enum @@ -64,6 +92,7 @@ typedef enum ELF_T_LIB, /* Elf32_Lib, Elf64_Lib, ... */ ELF_T_GNUHASH, /* GNU-style hash section. */ ELF_T_AUXV, /* Elf32_auxv_t, Elf64_auxv_t, ... */ + ELF_T_CHDR, /* Compressed, Elf32_Chdr, Elf64_Chdr, ... */ /* Keep this the last entry. */ ELF_T_NUM } Elf_Type; @@ -116,6 +145,12 @@ enum #define ELF_F_PERMISSIVE ELF_F_PERMISSIVE }; +/* Flags for elf_compress[_gnu]. */ +enum +{ + ELF_CHF_FORCE = 0x1 +#define ELF_CHF_FORCE ELF_CHF_FORCE +}; /* Identification values for recognized object files. */ typedef enum @@ -267,6 +302,62 @@ extern Elf32_Shdr *elf32_getshdr (Elf_Scn *__scn); /* Similar for ELFCLASS64. */ extern Elf64_Shdr *elf64_getshdr (Elf_Scn *__scn); +/* Returns compression header for a section if section data is + compressed. Returns NULL and sets elf_errno if the section isn't + compressed or an error occurred. */ +extern Elf32_Chdr *elf32_getchdr (Elf_Scn *__scn); +extern Elf64_Chdr *elf64_getchdr (Elf_Scn *__scn); + +/* Compress or decompress the data of a section and adjust the section + header. + + elf_compress works by setting or clearing the SHF_COMPRESS flag + from the section Shdr and will encode or decode a Elf32_Chdr or + Elf64_Chdr at the start of the section data. elf_compress_gnu will + encode or decode any section, but is traditionally only used for + sections that have a name starting with ".debug" when + uncompressed or ".zdebug" when compressed and stores just the + uncompressed size. The GNU compression method is deprecated and + should only be used for legacy support. + + elf_compress takes a compression type that should be either zero to + decompress or an ELFCOMPRESS algorithm to use for compression. + Currently only ELFCOMPRESS_ZLIB is supported. elf_compress_gnu + will compress in the traditional GNU compression format when + compress is one and decompress the section data when compress is + zero. + + The FLAGS argument can be zero or ELF_CHF_FORCE. If FLAGS contains + ELF_CHF_FORCE then it will always compress the section, even if + that would not reduce the size of the data section (including the + header). Otherwise elf_compress and elf_compress_gnu will compress + the section only if the total data size is reduced. + + On successful compression or decompression the function returns + one. If (not forced) compression is requested and the data section + would not actually reduce in size, the section is not actually + compressed and zero is returned. Otherwise -1 is returned and + elf_errno is set. + + It is an error to request compression for a section that already + has SHF_COMPRESSED set, or (for elf_compress) to request + decompression for an section that doesn't have SHF_COMPRESSED set. + It is always an error to call these functions on SHT_NOBITS + sections or if the section has the SHF_ALLOC flag set. + elf_compress_gnu will not check whether the section name starts + with ".debug" or .zdebug". It is the responsibilty of the caller + to make sure the deprecated GNU compression method is only called + on correctly named sections (and to change the name of the section + when using elf_compress_gnu). + + All previous returned Shdrs and 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, unsigned int flags); +extern int elf_compress_gnu (Elf_Scn *scn, int compress, unsigned int flags); /* Set or clear flags for ELF file. */ extern unsigned int elf_flagelf (Elf *__elf, Elf_Cmd __cmd, @@ -288,8 +379,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 then d_type is always set to + ELF_T_CHDR. */ 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..10dc5059 100644 --- a/libelf/libelf.map +++ b/libelf/libelf.map @@ -138,3 +138,13 @@ ELFUTILS_1.6 { global: elf_getphdrnum; } ELFUTILS_1.5; + +ELFUTILS_1.7 { + global: + elf32_getchdr; + elf64_getchdr; + gelf_getchdr; + + elf_compress; + elf_compress_gnu; +} ELFUTILS_1.6; diff --git a/libelf/libelfP.h b/libelf/libelfP.h index 993c6556..57ccbce4 100644 --- a/libelf/libelfP.h +++ b/libelf/libelfP.h @@ -1,5 +1,5 @@ /* Internal interfaces for libelf. - Copyright (C) 1998-2010 Red Hat, Inc. + Copyright (C) 1998-2010, 2015 Red Hat, Inc. This file is part of elfutils. Contributed by Ulrich Drepper <drepper@redhat.com>, 1998. @@ -38,6 +38,7 @@ #include <gelf.h> #include <errno.h> +#include <stdbool.h> #include <stdint.h> #include <stdio.h> #include <string.h> @@ -138,6 +139,13 @@ enum ELF_E_INVALID_PHDR, ELF_E_NO_PHDR, ELF_E_INVALID_OFFSET, + ELF_E_INVALID_SECTION_TYPE, + ELF_E_INVALID_SECTION_FLAGS, + 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 +238,10 @@ 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. */ + size_t zdata_size; /* If zdata_base != NULL, the size of data. */ + size_t zdata_align; /* If zdata_base != NULL, the addralign. */ + struct Elf_ScnList *list; /* Pointer to the section list element the data is in. */ }; @@ -438,6 +450,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 +595,22 @@ 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 *orig_addralign, + size_t *size, bool force) + internal_function; + +extern void * __libelf_decompress (void *buf_in, size_t size_in, + size_t size_out) internal_function; +extern void * __libelf_decompress_elf (Elf_Scn *scn, + size_t *size_out, size_t *addralign) + internal_function; + + +extern void __libelf_reset_rawdata (Elf_Scn *scn, void *buf, size_t size, + size_t align, Elf_Type type) + internal_function; + /* We often have to update a flag iff a value changed. Make this convenient. */ |