Hi,
This is hopefully the final patch set to add SHF_COMPRESSED section support to libelf, libdw and some of the elfutils utilities.
All XXXs in the code have been resolved. There has been a small interface change (as discussed on the Generic System V Application Binary Interface mailinglist) to add a ELF_CHF_FORCE flag to elf_compress and elf_compress_gnu and a slight change in return value (zero now means that the section wasn't compressed because it wasn't beneficial).
This version also makes it so that libdw and libdwfl functions will automatically handle compressed ELF sections, extends the eu-readelf support a little, makes elflint recognize and check compressed sections and adds a new eu-elfcompress utility that can be used to compress or uncompress ELF sections.
The code can also be found on the mjw/compress4 branch in git.
Comments welcome.
Kurt, this should solve the issue on Debian powerpc that you were seeing. If you could test the code on the mjw/compress4 branch that would be great.
Thanks,
Mark
Helper function to get the size of a GNU compressed zdebug section.
Signed-off-by: Mark Wielaard mjw@redhat.com --- libdw/ChangeLog | 4 ++ libdw/libdw.map | 5 ++ libdwelf/ChangeLog | 7 ++ libdwelf/Makefile.am | 4 +- libdwelf/dwelf_scn_gnu_compressed_size.c | 77 ++++++++++++++++++++++ libdwelf/libdwelf.h | 9 ++- tests/ChangeLog | 12 ++++ tests/Makefile.am | 10 ++- tests/dwelfgnucompressed.c | 104 +++++++++++++++++++++++++++++ tests/run-dwelfgnucompressed.sh | 108 +++++++++++++++++++++++++++++++ tests/testfile-zgnu32.bz2 | Bin 0 -> 780 bytes tests/testfile-zgnu32be.bz2 | Bin 0 -> 779 bytes tests/testfile-zgnu64.bz2 | Bin 0 -> 785 bytes tests/testfile-zgnu64be.bz2 | Bin 0 -> 795 bytes 14 files changed, 334 insertions(+), 6 deletions(-) create mode 100644 libdwelf/dwelf_scn_gnu_compressed_size.c create mode 100644 tests/dwelfgnucompressed.c create mode 100755 tests/run-dwelfgnucompressed.sh create mode 100755 tests/testfile-zgnu32.bz2 create mode 100755 tests/testfile-zgnu32be.bz2 create mode 100755 tests/testfile-zgnu64.bz2 create mode 100755 tests/testfile-zgnu64be.bz2
diff --git a/libdw/ChangeLog b/libdw/ChangeLog index f8b4793..dfe8333 100644 --- a/libdw/ChangeLog +++ b/libdw/ChangeLog @@ -1,3 +1,7 @@ +2015-10-28 Mark Wielaard mjw@redhat.com + + * libdw.map (ELFUTILS_0.165): New. Add dwelf_scn_gnu_compressed_size. + 2015-12-02 Mark Wielaard mjw@redhat.com
* fde.c (intern_fde): Don't leak duplicate FDEs. diff --git a/libdw/libdw.map b/libdw/libdw.map index 1d4cbb0..1681b6b 100644 --- a/libdw/libdw.map +++ b/libdw/libdw.map @@ -322,3 +322,8 @@ ELFUTILS_0.161 { dwarf_macro_getparamcnt; dwarf_macro_param; } ELFUTILS_0.160; + +ELFUTILS_0.165 { + global: + dwelf_scn_gnu_compressed_size; +} ELFUTILS_0.161; diff --git a/libdwelf/ChangeLog b/libdwelf/ChangeLog index f182ecf..fe8af1b 100644 --- a/libdwelf/ChangeLog +++ b/libdwelf/ChangeLog @@ -1,3 +1,10 @@ +2015-10-28 Mark Wielaard mjw@redhat.com + + * Makefile.am (libdwelf_a_SOURCES): Add + dwelf_scn_gnu_compressed_size.c. + * dwelf_scn_gnu_compressed_size.c: Likewise. + * libdwelf.h (dwelf_scn_gnu_compressed_size): New declaration. + 2015-10-14 Chih-Hung Hsieh chh@google.com
* dwelf_elf_gnu_build_id.c (find_elf_build_id): Move nested function diff --git a/libdwelf/Makefile.am b/libdwelf/Makefile.am index cd4b7dd..4de4b2e 100644 --- a/libdwelf/Makefile.am +++ b/libdwelf/Makefile.am @@ -2,7 +2,7 @@ ## ## Process this file with automake to create Makefile.in ## -## Copyright (C) 2014 Red Hat, Inc. +## Copyright (C) 2014, 2015 Red Hat, Inc. ## This file is part of elfutils. ## ## This file is free software; you can redistribute it and/or modify @@ -40,7 +40,7 @@ pkginclude_HEADERS = libdwelf.h noinst_HEADERS = libdwelfP.h
libdwelf_a_SOURCES = dwelf_elf_gnu_debuglink.c dwelf_dwarf_gnu_debugaltlink.c \ - dwelf_elf_gnu_build_id.c + dwelf_elf_gnu_build_id.c dwelf_scn_gnu_compressed_size.c
libdwelf = $(libdw)
diff --git a/libdwelf/dwelf_scn_gnu_compressed_size.c b/libdwelf/dwelf_scn_gnu_compressed_size.c new file mode 100644 index 0000000..d39b702 --- /dev/null +++ b/libdwelf/dwelf_scn_gnu_compressed_size.c @@ -0,0 +1,77 @@ +/* Return size of GNU compressed 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 "libdwelfP.h" +#include "libelfP.h" + +ssize_t +dwelf_scn_gnu_compressed_size (Elf_Scn *scn) +{ + if (scn == NULL) + return -1; + + GElf_Shdr shdr; + if (gelf_getshdr (scn, &shdr) == NULL) + return -1; + + /* Allocated or no bits sections can never be compressed. */ + if ((shdr.sh_flags & SHF_ALLOC) != 0 + || shdr.sh_type == SHT_NULL + || shdr.sh_type == SHT_NOBITS) + return -1; + + Elf_Data *d = elf_rawdata (scn, NULL); + if (d == NULL) + return -1; + + if (d->d_size >= 4 + 8 + && memcmp (d->d_buf, "ZLIB", 4) == 0) + { + /* 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 size; + memcpy (&size, d->d_buf + 4, sizeof size); + size = be64toh (size); + + /* 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 (size + 4 + 8 + 6 + 5 < d->d_size || size > SIZE_MAX) + return -1; + + return size; + } + + return -1; +} diff --git a/libdwelf/libdwelf.h b/libdwelf/libdwelf.h index e16dc0f..7f7f679 100644 --- a/libdwelf/libdwelf.h +++ b/libdwelf/libdwelf.h @@ -1,5 +1,5 @@ /* Interfaces for libdwelf. DWARF ELF Low-level Functions. - Copyright (C) 2014 Red Hat, Inc. + Copyright (C) 2014, 2015 Red Hat, Inc. This file is part of elfutils.
This file is free software; you can redistribute it and/or modify @@ -65,6 +65,13 @@ extern ssize_t dwelf_dwarf_gnu_debugaltlink (Dwarf *dwarf, note. Returns -1 in case of malformed data or other errors. */ extern ssize_t dwelf_elf_gnu_build_id (Elf *elf, const void **build_idp);
+/* Returns the size of the uncompressed data of a GNU compressed + section. The section name should start with .zdebug (but this + isn't checked by this function). If the section isn't compressed + (the section data doesn't start with ZLIB) -1 is returned. If an + error occured -1 is returned and elf_errno is set. */ +extern ssize_t dwelf_scn_gnu_compressed_size (Elf_Scn *scn); + #ifdef __cplusplus } #endif diff --git a/tests/ChangeLog b/tests/ChangeLog index cfef289..23e973e 100644 --- a/tests/ChangeLog +++ b/tests/ChangeLog @@ -1,3 +1,15 @@ +2015-10-28 Mark Wielaard mjw@redhat.com + + * dwelfgnucompressed.c: New file. + * run-dwelfgnucompressed.sh: New test. + * testfile-zgnu32.bz2: New testfile. + * testfile-zgnu64.bz2: Likewise. + * Makefile.am (check_PROGRAMS): Add dwelfgnucompressed. + (TESTS): Add run-dwelfgnucompressed.sh. + (EXTRA_DIST): Add run-dwelfgnucompressed.sh, testfile-zgnu32.bz2, + testfile-zgnu64.bz2, testfile-zgnu32be.bz2, testfile-zgnu64be.bz2. + (dwelfgnucompressed_LDADD): New variable. + 2015-12-31 Mark Wielaard mjw@redhat.com
* elfstrmerge.c (main): Warn about STT_SECTION symbol for shstrhndx. diff --git a/tests/Makefile.am b/tests/Makefile.am index 744f695..d9ec97f 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -52,7 +52,7 @@ 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 + elfshphehdr elfstrmerge dwelfgnucompressed
asm_TESTS = asm-tst1 asm-tst2 asm-tst3 asm-tst4 asm-tst5 \ asm-tst6 asm-tst7 asm-tst8 asm-tst9 @@ -120,7 +120,7 @@ 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 + elfshphehdr run-lfs-symbols.sh run-dwelfgnucompressed.sh
if !BIARCH export ELFUTILS_DISABLE_BIARCH = 1 @@ -304,7 +304,10 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh \ testfile-x32-core.bz2 testfile-x32.bz2 \ backtrace.x32.core.bz2 backtrace.x32.exec.bz2 \ testfile-x32-s.bz2 testfile-x32-d.bz2 testfile-x32-debug.bz2 \ - run-lfs-symbols.sh lfs-symbols testfile-nolfs.bz2 + run-lfs-symbols.sh lfs-symbols testfile-nolfs.bz2 \ + testfile-zgnu32.bz2 testfile-zgnu64.bz2 \ + testfile-zgnu32be.bz2 testfile-zgnu64be.bz2 \ + run-dwelfgnucompressed.sh
if USE_VALGRIND valgrind_cmd='valgrind -q --leak-check=full --error-exitcode=1' @@ -453,6 +456,7 @@ dwfl_proc_attach_LDADD = $(libdw) dwfl_proc_attach_LDFLAGS = -pthread $(AM_LDFLAGS) elfshphehdr_LDADD =$(libelf) elfstrmerge_LDADD = $(libebl) $(libelf) +dwelfgnucompressed_LDADD = $(libelf) $(libdw)
if GCOV check: check-am coverage diff --git a/tests/dwelfgnucompressed.c b/tests/dwelfgnucompressed.c new file mode 100644 index 0000000..0132271 --- /dev/null +++ b/tests/dwelfgnucompressed.c @@ -0,0 +1,104 @@ +/* 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 <assert.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <inttypes.h> + +#include ELFUTILS_HEADER(elf) +#include ELFUTILS_HEADER(dwelf) +#include <gelf.h> + +#include <stdio.h> +#include <unistd.h> +#include <string.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; + } + + size_t shdrstrndx; + if (elf_getshdrstrndx (elf, &shdrstrndx) == -1) + { + printf ("elf_getshdrstrnd failed %s\n", elf_errmsg (-1)); + result = 1; + close (fd); + continue; + } + + Elf_Scn *scn = NULL; + while ((scn = elf_nextscn (elf, scn)) != NULL) + { + int idx = elf_ndxscn (scn); + GElf_Shdr shdr; + if (gelf_getshdr (scn, &shdr) == NULL) + { + printf ("gelf_getshdr failed: %s\n", elf_errmsg (-1)); + result = 1; + break; + } + + const char *sname = elf_strptr (elf, shdrstrndx, shdr.sh_name); + if (sname == NULL) + { + printf ("couldn't get section name: %s\n", elf_errmsg (-1)); + result = 1; + break; + } + + if (strncmp(".zdebug", sname, strlen (".zdebug")) == 0) + { + ssize_t size; + if ((size = dwelf_scn_gnu_compressed_size (scn)) == -1) + { + printf ("dwelf_scn_gnu_compressed_size failed: %s\n", + elf_errmsg (-1)); + result = 1; + break; + } + printf ("section %d: GNU Compressed size: %zx\n", idx, size); + } + } + + elf_end (elf); + close (fd); + } + + return result; +} diff --git a/tests/run-dwelfgnucompressed.sh b/tests/run-dwelfgnucompressed.sh new file mode 100755 index 0000000..b93a56f --- /dev/null +++ b/tests/run-dwelfgnucompressed.sh @@ -0,0 +1,108 @@ +#! /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 + +# = funcs.s = +# .globl testfunc +# testfunc: +# nop +# ret +# .type testfunc, @function +# .size testfunc, .-testfunc +# +# .globl testfunc2 +# testfunc2: +# call testfunc +# nop +# nop +# ret +# .type testfunc2, @function +# .size testfunc2, .-testfunc2 +# +# .globl functest3 +# functest3: +# jmp local +# nop +# nop +# local: +# call testfunc2 +# ret +# .type functest3, @function +# .size functest3, .-functest3 + +# = start.s = +# .global _start +# _start: +# call functest3 +# nop +# nop +# nop +# nop +# nop +# nop +# nop +# nop +# nop +# nop +# nop +# nop +# nop +# nop +# nop +# nop +# ret +# .type _start, @function +# .size _start, .-_start + +# gas --compress-debug-sections=zlib-gnu -32 -g -o start.o start.s +# gas --compress-debug-sections=zlib-gnu -32 -g -o funcs.o funcs.s +# ld --compress-debug-sections=zlib-gnu -melf_i386 -g -o zgnu32 funcs.o start.o + +# gas --compress-debug-sections=zlib-gnu -64 -g -o start.o start.s +# gas --compress-debug-sections=zlib-gnu -64 -g -o funcs.o funcs.s +# ld --compress-debug-sections=zlib-gnu -g -o zgnu32 funcs.o start.o + +testfiles testfile-zgnu64 +testrun_compare ${abs_top_builddir}/tests/dwelfgnucompressed testfile-zgnu64 <<\EOF +section 2: GNU Compressed size: 60 +section 3: GNU Compressed size: aa +section 5: GNU Compressed size: 8d +EOF + +testfiles testfile-zgnu64be +testrun_compare ${abs_top_builddir}/tests/dwelfgnucompressed testfile-zgnu64be <<\EOF +section 3: GNU Compressed size: 60 +section 4: GNU Compressed size: 7e +section 6: GNU Compressed size: 8d +EOF + +testfiles testfile-zgnu32 +testrun_compare ${abs_top_builddir}/tests/dwelfgnucompressed testfile-zgnu32 <<\EOF +section 2: GNU Compressed size: 40 +section 3: GNU Compressed size: 9a +section 5: GNU Compressed size: 85 +EOF + +testfiles testfile-zgnu32be +testrun_compare ${abs_top_builddir}/tests/dwelfgnucompressed testfile-zgnu32be <<\EOF +section 3: GNU Compressed size: 40 +section 4: GNU Compressed size: 6e +section 6: GNU Compressed size: 85 +EOF + +exit 0 diff --git a/tests/testfile-zgnu32.bz2 b/tests/testfile-zgnu32.bz2 new file mode 100755 index 0000000000000000000000000000000000000000..9d622cad1739554cbf744ed471936a5b470a1148 GIT binary patch literal 780 zcmV+n1M~bsT4*^jL0KkKSrTWQ^8f*#fB*mY{V8v8d@c9vWp)4e|9$6Wcs(<4h;aTe z5FikJ=rF(n7&(9jrb#_2eiC|*LYQqS>6F@@ri}nJ(<9Vrw15E9)bfX^p`+A#gCVDq z4@u=brbdrVH8xQO69i<yOafq-ATR(4fC+#RrVzwnBSsN~(V!VJWCog4$RU$J(@Cai zJs<|AYB4Yb!enXcVlsLFj0vUyOaKX^0GJvns$}&v(9p@FLqKRW(U8P4XaE2MAPoS- zGz@?M27m#}asxKZGK?5R5Ji21Q5yEQp)t=CDGIUfz~cARS5HPYDIDn^z*Io^;ucCG zHtw5j1JVN%5M<aEaQzS(kf4#HkdcQ~U5_4t5gwNWlu%JfBnd+C+JOL;D8wQZF?0(h z0WOF@r3nv6KpG%Mlpc^!E8?J&1<>d*V&OQFa-<(wM2O-fvjV(_WuK~}L`}JT+6*v? z@PvLx6jkqXsEz0tj@aqIVi|>*F}7JmQeNKp2q8)VtOh|gNf!B(%@PR61Pg#*GZ0}Q zh?tN~AhiHqgPs-vA>?X*jD})!yxK9i7U!mx(?AX7^f8ha0Kr}l#gkYiY0&P?TsWqz zgPku?abrnHwn9;J$q4Hcd1g7jrm|~MR^X|eXh=RcYH!v?t21DCzSShq&|M!(*$p0# znI6VpOnc$iSR|^Dice_i$5>Moj2M!7vitt+OyG&}EO38S%?@!YnnO&6(ZVN{qyws| zGzk|UeDRR8U@$Om5aE9mVfe=ZFw!V7PrIK9WkqBMYXv@w1-v=R!XW5n=U7tC0tu{D z@51bza!6+ME|4m=1@H}vxWAo!+Znwvoc$Xaf;P37800QW!|ru3Jg58d#|dOd@igw6 z`IfS>^ZO3bs3pqHF+dyyj2?s!?G_Tazec-uIe9~Sndh~#cDxZWqrsIkBZnGB&e}OK zHlE)MBE?jULAULAI+1SNVLi0$3x5~em;RCdo^-MyT)<$CHOCUDGHPo>2rNs#@pmLs Kg$V&BdCxGFj#bD2
literal 0 HcmV?d00001
diff --git a/tests/testfile-zgnu32be.bz2 b/tests/testfile-zgnu32be.bz2 new file mode 100755 index 0000000000000000000000000000000000000000..c1dd589e49cee6f2381a4bb4afcda3101b917ee0 GIT binary patch literal 779 zcmV+m1N8htT4*^jL0KkKS-pkc)&K$cfB*mg|LcBleVc!89d-ZbpLO#=dXzUvV8UnW z7(gQnAvVAQJ)m1N15pV*PgBT&>J0z@000d%9-uS>)B{6601W^DdVteLnj1|Z)J-&+ z35kiCnrO%jng9SA00SU20000005miPfB=I4MhS@1CJ~UtFpMSuMiG$7rb9+XAke@} zObLk5fQ*?i2~9+rYI<oWr1XZGdW{ByH6Bm^H1!P)2Gl)AsP!}hLqKQ-fNAOjO$LAf zi~NQvzZOx`L-5fZuGQiciG@>%gU+i@V?2sWdGmsg8No#;A6Y=2oO=|o1p}vsAR+}~ ziUc4MFAiUXPSFAjyhK8rg*)yp-3C+a1+9FmN%EKqkik4xNt;#)=#2yx;O25+w{CEf zLk&R;1mploZ02Ud6Eih5z-ZG3a9M;+ku=;08bo2<NL{TjM-p2xUMU&uDq4AvGX(Ag zGT`QDpn4}Rz&v)5#W9p<GpI;xC1XH4zNj1Nk7^h?-Npv0eQ2$#$(ySxVqqwgIGUIB z&tjk=P?_kuOkbkqLuK313`Qmj2tBH?o-c~i<uQ?t>}3{BT%KJROM-y}tU*Z%3)X6b zft1<wb*e!DOt^caEE>?BWidCCo^;FfQed-NgyK3cx<P;AfK3NUo^AXvP^2O_b=8MR zYnaza#(xHRG+5gMb{@EA;=}0vcWGI%70g|^Z0DU^W~%}EO?rc^8@<T-Bggr1j(MEC z?=;W`=~7HL?)LD*UK=L{Q&I6I%S-hRe?kaLMoKtw0&FYt#3TjgB3#bMXUqUva;X+Z zMR)40BtsDYumi7gqMd?mjDzz-ZnOoE!QHut)zbu0a#n$3rfSkfkwK*j&!xYjwZ@Ps zmnsRE1b}D)Op4b9>G?9T1VPF0QBa_-04N}L>fq2iW#7DgZglWTY$oyQ<xP|6`#0Ze zUesmTelm^Y$B7U<hX?JV2*5FUKm^w-cy-ue1Kpzr7NZ6#R_d7K>x$EYWrR=hcO+AV J2@BX=?O^e0RDJ*e
literal 0 HcmV?d00001
diff --git a/tests/testfile-zgnu64.bz2 b/tests/testfile-zgnu64.bz2 new file mode 100755 index 0000000000000000000000000000000000000000..1bc2c0906a4f2404d2df2b6e8aa6e096665ae2ba GIT binary patch literal 785 zcmV+s1Md7nT4*^jL0KkKSz)bY&;S9*|NsAg{cnDGdPCoQOgR7N-|)9?bS8IDd`9o2 z7zTBCrZ&I=8-Zrb6eJT(rlFdeWc3XXQ%w(1p`g$PjWjfApfU{r4Kf;Or>M{X05r+9 zG|B29WMVX6O&JCOG-;s084Ltq6C+GaOh$~1AkmP(CYS&d6A(2|Q$Y<iG}A*Nrhw2g zG&BGjXwVG+0MGyc003wJ001OXMwHJ&hMOcFlhpLo&<&}kK+wnlHlP|BXwVt~pc)MT z0000B9Rr$@2_y;e9?^JK@+lRwy39}0Bo#9AK4d}dW1Dv`6m~*9j{iwEDa>xjg)bd_ zUdu<RxT+JHCa@~!;F5@Abq0j5FLm)OzKZ8fD4Syd0abQaw>eg+DPevzr7;bpn;Th1 zsW1Y}X<fT%hAY9r!mzfjhsfs<VmnFZ1l$UmLhj?;3KKA-6pW3!*1S1d!A1`H07Sxz zGqVT<cT8*Q&c3SOsU)*OS_<i-AMGDQb!i&Ep_5YyOC6&XhgqVY!Ub`ZIIt*>d&0Vk zbEm&G2DtWbB8?LmmNPp(n>dMQD*7a#I6UVB8<{ZlAaTx&AJ>QmVwM^25)Mp{<kGvS z#~|**b7PfIwnve1AXl%Dg2zG#bxUsQIZjDb;rdl9*FjW~kbo5frXVaujV=gNsA})C zw9az0jD#@str2VCht9cq(<4YydvVe94Y3@ciZBcZ3;<{K6@lsM3apwOKcp1;-Y7&^ z*b*;$uOCz;{p<M*My=!U-Y#sp;UPyPa}q>`^kC)#xfr(3)Zb{d#KWOL89@ojL5W^u z8jxfbHicM{3;_da7Nanl<N3(Jb0C?3PzC@gRP{DwgQ|X}>UO7*2zC>Aglp#qA*#rP zr35q4TDw?1pr<H+d(Nkm{f1ld0t4t?il)m1ug$6Z2FaC)M2<gFUX=ng816Q2H@K*n zR7a_46XY26m0)=3?c6jRn$ztB3`FV)L1KDIql=OU45bk<Fr~~RZ6P+snE2O-ngEH~ PKjQ94rwS4bHLRKdHyTwY
literal 0 HcmV?d00001
diff --git a/tests/testfile-zgnu64be.bz2 b/tests/testfile-zgnu64be.bz2 new file mode 100755 index 0000000000000000000000000000000000000000..390c9c90bd3c54e364e1378aff44680732e8b1e6 GIT binary patch literal 795 zcmV+$1LXWdT4*^jL0KkKS%F8Zh5!N}fB*mg{{4RKd`jPcK|KHG-+9VjQXY3fU|LL$ zP!uyxem1}YCj((fXpx{v=*dT@c|&S#K-z!?L8j6?pbym#NIgcFjXgo>Jx@uA>SP)o zNYUvuZBHcdNE)VzrqZ6K!iJ5hkQx90X{LsN0000014BbV00000JwyzQMvMugAiyS# zG#DcxfQ$lUX^Dx5(UF82G8hEY00LrS1d5ucsfo1&(9z{I8&EPDJwwuZngG*410yDy zG#UWOp`#!&0LTCU4PykM3cNbpgog2<O`hzUS=gszB1KD}fdoKw;2jfxv`S4-!tmxw znTIczHdI|}!UY-&XmA688vhbE6P163(tEkUb;EGO?m7Sk1_2F@ssaMZ)ljDk;F{1B z^Q@>F5frr&iJ(o5wjoPkB1R=pA&F~Pi}8tJFhwSSq{J%mfnv<SGohoHciw}i)Ri{6 zA!;UbU?AfCD&)KARDn5@RisEZ*`^T%2OFN8qT>n^iQf2e<k3MCVHNvV5-67j%&kxg z;fRf)wAsFJnNtjwMmZTQ-^l=3G1xd8*Tx`HF0SLA!E>lrkTdz3s<+C%qbNabP>SLx zPpq}p44MHTX9*zz7J6-;v&Rh8w#hB-%Gn8n>!{5GHF#AqQ}tw5&$kW~C~e*{_@tQ> zW0B9B%#B)9M=+X!1oX`+s+qYZuCPkq2{+gem+sC<R<vy-d1Ky_FDxt^cGJI^nzFJH z?G#WmlpECmpmz*`TJJnJ#WUQ~{9sY&892s(LX+wxaQL-QhwOO3y7`R4i$y|CeIirn z;0+}9FiT*6wi2N9#CQdkZthz!m_eeOATLZSw^amOvORJt5pbLnHc5~i<c;>)l_Oga z<dp?D0@SntKpG%<0yKX6hFUDPBr}1c22|$`g!+HYE15rO8fNqtc2cd&tB%V1vg?eE zZCrSZj7>`Jg-mKc>s6_JV-ELiYR!FkurjXhcr-b*r(3iB+0nUW%*de&=_p~pjDE&3 ZzTJchqgKZVGDMmFF64@Ep&<f~R}5;vWhDRr
literal 0 HcmV?d00001
Elf_Data of a compressed section has type ELF_T_CHDR. This type can be xlated to the file or memory representation. This will make sure the Chdr is in the correct endianess. The compressed data following the Chdr isn't translated.
Signed-off-by: Mark Wielaard mjw@redhat.com --- libelf/ChangeLog | 28 +++++ libelf/Makefile.am | 8 +- libelf/abstract.h | 20 +++- libelf/chdr_xlate.h | 33 ++++++ libelf/elf32_getchdr.c | 83 ++++++++++++++ libelf/{gelf_xlate.h => elf64_getchdr.c} | 34 +----- libelf/elf_error.c | 20 +++- libelf/elf_getdata.c | 20 +++- libelf/exttypes.h | 4 +- libelf/gelf.h | 8 +- libelf/gelf_fsize.c | 1 + libelf/{gelf_xlate.h => gelf_getchdr.c} | 71 +++++++----- libelf/gelf_xlate.c | 4 +- libelf/gelf_xlate.h | 1 + libelf/libelf.h | 8 +- libelf/libelf.map | 7 ++ libelf/libelfP.h | 5 +- tests/ChangeLog | 17 +++ tests/Makefile.am | 11 +- tests/elfgetchdr.c | 124 ++++++++++++++++++++ tests/msg_tst.c | 5 +- tests/run-elfgetchdr.sh | 188 +++++++++++++++++++++++++++++++ tests/testfile-zgabi32.bz2 | Bin 0 -> 767 bytes tests/testfile-zgabi32be.bz2 | Bin 0 -> 769 bytes tests/testfile-zgabi64.bz2 | Bin 0 -> 795 bytes tests/testfile-zgabi64be.bz2 | Bin 0 -> 780 bytes 26 files changed, 623 insertions(+), 77 deletions(-) create mode 100644 libelf/chdr_xlate.h create mode 100644 libelf/elf32_getchdr.c copy libelf/{gelf_xlate.h => elf64_getchdr.c} (53%) copy libelf/{gelf_xlate.h => gelf_getchdr.c} (53%) create mode 100644 tests/elfgetchdr.c create mode 100755 tests/run-elfgetchdr.sh create mode 100755 tests/testfile-zgabi32.bz2 create mode 100755 tests/testfile-zgabi32be.bz2 create mode 100755 tests/testfile-zgabi64.bz2 create mode 100755 tests/testfile-zgabi64be.bz2
diff --git a/libelf/ChangeLog b/libelf/ChangeLog index fbe8e3a..2ed4a67 100644 --- a/libelf/ChangeLog +++ b/libelf/ChangeLog @@ -1,3 +1,31 @@ +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 4a4131c..8a678b8 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,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 \ + elf32_getchdr.c elf64_getchdr.c gelf_getchdr.c
libelf_pic_a_SOURCES = am_libelf_pic_a_OBJECTS = $(libelf_a_SOURCES:.c=.os) @@ -118,7 +119,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 53713ee..d4515f2 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 0000000..70782b4 --- /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 0000000..982a614 --- /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/gelf_xlate.h b/libelf/elf64_getchdr.c similarity index 53% copy from libelf/gelf_xlate.h copy to libelf/elf64_getchdr.c index f11eb90..6588b79 100644 --- a/libelf/gelf_xlate.h +++ b/libelf/elf64_getchdr.c @@ -1,7 +1,6 @@ -/* Helper file for type conversion function generation. - Copyright (C) 1998, 1999, 2000, 2002, 2004, 2007 Red Hat, Inc. +/* Return section compression header. + Copyright (C) 2015 Red Hat, Inc. This file is part of elfutils. - Contributed by Ulrich Drepper drepper@redhat.com, 1998.
This file is free software; you can redistribute it and/or modify it under the terms of either @@ -27,30 +26,5 @@ the GNU Lesser General Public License along with this program. If not, see http://www.gnu.org/licenses/. */
- -/* Simple types. */ -FUNDAMENTAL (ADDR, Addr, LIBELFBITS); -FUNDAMENTAL (OFF, Off, LIBELFBITS); -FUNDAMENTAL (HALF, Half, LIBELFBITS); -FUNDAMENTAL (WORD, Word, LIBELFBITS); -FUNDAMENTAL (SWORD, Sword, LIBELFBITS); -FUNDAMENTAL (XWORD, Xword, LIBELFBITS); -FUNDAMENTAL (SXWORD, Sxword, LIBELFBITS); - -/* The structured types. */ -TYPE (Ehdr, LIBELFBITS) -TYPE (Phdr, LIBELFBITS) -TYPE (Shdr, LIBELFBITS) -TYPE (Sym, LIBELFBITS) -TYPE (Rel, LIBELFBITS) -TYPE (Rela, LIBELFBITS) -TYPE (Note, LIBELFBITS) -TYPE (Dyn, LIBELFBITS) -TYPE (Syminfo, LIBELFBITS) -TYPE (Move, LIBELFBITS) -TYPE (Lib, LIBELFBITS) -TYPE (auxv_t, LIBELFBITS) - - -/* Prepare for the next round. */ -#undef LIBELFBITS +#define LIBELFBITS 64 +#include "elf32_getchdr.c" diff --git a/libelf/elf_error.c b/libelf/elf_error.c index d6bdaab..d97e3bd 100644 --- a/libelf/elf_error.c +++ b/libelf/elf_error.c @@ -230,6 +230,21 @@ 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") };
@@ -277,7 +292,10 @@ 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 }; #define nmsgidx ((int) (sizeof (msgidx) / sizeof (msgidx[0])))
diff --git a/libelf/elf_getdata.c b/libelf/elf_getdata.c index 9a567e5..bd1f068 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] = { @@ -204,6 +205,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 +222,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 +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; }
/* If the section has no data (for whatever reason), leave the `d_buf' @@ -243,7 +247,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,10 +327,15 @@ __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) + + /* 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 if (type == SHT_HASH && elf->class == ELFCLASS64) { + /* Some broken ELF ABI for 64-bit machines use the wrong hash table + entry size. See elf-knowledge.h for more information. */ GElf_Ehdr ehdr_mem; GElf_Ehdr *ehdr = __gelf_getehdr_rdlock (elf, &ehdr_mem); scn->rawdata.d.d_type diff --git a/libelf/exttypes.h b/libelf/exttypes.h index 8cb2aae..7bacd65 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 e3f0740..1bc7ee7 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 a124fa8..0c50926 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_xlate.h b/libelf/gelf_getchdr.c similarity index 53% copy from libelf/gelf_xlate.h copy to libelf/gelf_getchdr.c index f11eb90..394bf4b 100644 --- a/libelf/gelf_xlate.h +++ b/libelf/gelf_getchdr.c @@ -1,7 +1,6 @@ -/* Helper file for type conversion function generation. - Copyright (C) 1998, 1999, 2000, 2002, 2004, 2007 Red Hat, Inc. +/* Return section compression header. + Copyright (C) 2015 Red Hat, Inc. This file is part of elfutils. - Contributed by Ulrich Drepper drepper@redhat.com, 1998.
This file is free software; you can redistribute it and/or modify it under the terms of either @@ -27,30 +26,44 @@ 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
-/* Simple types. */ -FUNDAMENTAL (ADDR, Addr, LIBELFBITS); -FUNDAMENTAL (OFF, Off, LIBELFBITS); -FUNDAMENTAL (HALF, Half, LIBELFBITS); -FUNDAMENTAL (WORD, Word, LIBELFBITS); -FUNDAMENTAL (SWORD, Sword, LIBELFBITS); -FUNDAMENTAL (XWORD, Xword, LIBELFBITS); -FUNDAMENTAL (SXWORD, Sxword, LIBELFBITS); - -/* The structured types. */ -TYPE (Ehdr, LIBELFBITS) -TYPE (Phdr, LIBELFBITS) -TYPE (Shdr, LIBELFBITS) -TYPE (Sym, LIBELFBITS) -TYPE (Rel, LIBELFBITS) -TYPE (Rela, LIBELFBITS) -TYPE (Note, LIBELFBITS) -TYPE (Dyn, LIBELFBITS) -TYPE (Syminfo, LIBELFBITS) -TYPE (Move, LIBELFBITS) -TYPE (Lib, LIBELFBITS) -TYPE (auxv_t, LIBELFBITS) - - -/* Prepare for the next round. */ -#undef LIBELFBITS +#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 c5805e7..f3d3b7a 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 f11eb90..3c0e4bf 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 54f7c29..49234c8 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 @@ -64,6 +64,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; @@ -267,6 +268,11 @@ 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);
/* Set or clear flags for ELF file. */ extern unsigned int elf_flagelf (Elf *__elf, Elf_Cmd __cmd, diff --git a/libelf/libelf.map b/libelf/libelf.map index de6d912..d402ccc 100644 --- a/libelf/libelf.map +++ b/libelf/libelf.map @@ -138,3 +138,10 @@ ELFUTILS_1.6 { global: elf_getphdrnum; } ELFUTILS_1.5; + +ELFUTILS_1.7 { + global: + elf32_getchdr; + elf64_getchdr; + gelf_getchdr; +} ELFUTILS_1.6; \ No newline at end of file diff --git a/libelf/libelfP.h b/libelf/libelfP.h index 993c655..8ff8478 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.
@@ -138,6 +138,9 @@ 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, /* Keep this as the last entry. */ ELF_E_NUM }; diff --git a/tests/ChangeLog b/tests/ChangeLog index 23e973e..932c513 100644 --- a/tests/ChangeLog +++ b/tests/ChangeLog @@ -1,5 +1,22 @@ 2015-10-28 Mark Wielaard mjw@redhat.com
+ * elfgetchdr.c: New file. + * run-elfgetchdr.sh: New test. + * testfile-zgabi32.bz2: New testfile. + * testfile-zgabi32be.bz2: Likewise. + * testfile-zgabi64.bz2: Likewise. + * testfile-zgabi64be.bz2: Likewise. + * Makefile.am (check_PROGRAMS): Add elfgetchdr. + (TESTS): Add run-elfgetchdr.sh. + (EXTRA_DIST): Add run-elfgetchdr.sh, testfile-zgabi32.bz2, + testfile-zgabi32be.bz2, testfile-zgabi64.bz2, testfile-zgabi64be.bz2. + (welfgetchdr_LDADD): New variable. + * msg_tst.c: Add ELF_E_NOT_COMPRESSED, ELF_E_INVALID_SECTION_TYPE + and ELF_E_INVALID_SECTION_FLAGS, + + +2015-10-28 Mark Wielaard mjw@redhat.com + * dwelfgnucompressed.c: New file. * run-dwelfgnucompressed.sh: New test. * testfile-zgnu32.bz2: New testfile. diff --git a/tests/Makefile.am b/tests/Makefile.am index d9ec97f..e4b0fef 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -52,7 +52,7 @@ 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 dwelfgnucompressed + elfshphehdr elfstrmerge dwelfgnucompressed elfgetchdr
asm_TESTS = asm-tst1 asm-tst2 asm-tst3 asm-tst4 asm-tst5 \ asm-tst6 asm-tst7 asm-tst8 asm-tst9 @@ -120,7 +120,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-dwelfgnucompressed.sh + elfshphehdr run-lfs-symbols.sh run-dwelfgnucompressed.sh \ + run-elfgetchdr.sh
if !BIARCH export ELFUTILS_DISABLE_BIARCH = 1 @@ -307,7 +308,10 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh \ run-lfs-symbols.sh lfs-symbols testfile-nolfs.bz2 \ testfile-zgnu32.bz2 testfile-zgnu64.bz2 \ testfile-zgnu32be.bz2 testfile-zgnu64be.bz2 \ - run-dwelfgnucompressed.sh + run-dwelfgnucompressed.sh \ + testfile-zgabi32.bz2 testfile-zgabi64.bz2 \ + testfile-zgabi32be.bz2 testfile-zgabi64be.bz2 \ + run-elfgetchdr.sh
if USE_VALGRIND valgrind_cmd='valgrind -q --leak-check=full --error-exitcode=1' @@ -457,6 +461,7 @@ dwfl_proc_attach_LDFLAGS = -pthread $(AM_LDFLAGS) elfshphehdr_LDADD =$(libelf) elfstrmerge_LDADD = $(libebl) $(libelf) dwelfgnucompressed_LDADD = $(libelf) $(libdw) +elfgetchdr_LDADD = $(libelf) $(libdw)
if GCOV check: check-am coverage diff --git a/tests/elfgetchdr.c b/tests/elfgetchdr.c new file mode 100644 index 0000000..44ba178 --- /dev/null +++ b/tests/elfgetchdr.c @@ -0,0 +1,124 @@ +/* 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 <assert.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <inttypes.h> + +#include ELFUTILS_HEADER(elf) +#include ELFUTILS_HEADER(dwelf) +#include <gelf.h> + +#include <stdio.h> +#include <unistd.h> +#include <string.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; + } + + size_t shdrstrndx; + if (elf_getshdrstrndx (elf, &shdrstrndx) == -1) + { + printf ("elf_getshdrstrnd failed %s\n", elf_errmsg (-1)); + result = 1; + close (fd); + continue; + } + + Elf_Scn *scn = NULL; + while ((scn = elf_nextscn (elf, scn)) != NULL) + { + int idx = elf_ndxscn (scn); + GElf_Shdr shdr; + if (gelf_getshdr (scn, &shdr) == NULL) + { + printf ("gelf_getshdr failed: %s\n", elf_errmsg (-1)); + result = 1; + break; + } + + if ((shdr.sh_flags & SHF_COMPRESSED) != 0) + { + GElf_Chdr chdr; + if (gelf_getchdr (scn, &chdr) == NULL) + { + printf ("gelf_getchdr failed: %s\n", elf_errmsg (-1)); + result = 1; + break; + } + + printf ("section %d: ELF Compressed ch_type: %" PRId32 + ", ch_size: %" PRIx64 ", ch_addralign: %" PRIx64 "\n", + idx, chdr.ch_type, chdr.ch_size, chdr.ch_addralign); + } + else + { + const char *sname = elf_strptr (elf, shdrstrndx, shdr.sh_name); + if (sname == NULL) + { + printf ("couldn't get section name: %s\n", elf_errmsg (-1)); + result = 1; + break; + } + + /* This duplicates what the dwelfgnucompressed testcase does. */ + if (strncmp(".zdebug", sname, strlen (".zdebug")) == 0) + { + ssize_t size; + if ((size = dwelf_scn_gnu_compressed_size (scn)) == -1) + { + printf ("dwelf_scn_gnu_compressed_size failed: %s\n", + elf_errmsg (-1)); + result = 1; + break; + } + printf ("section %d: GNU Compressed size: %zx\n", idx, size); + } + else + printf ("section %d: NOT Compressed\n", idx); + } + } + + elf_end (elf); + close (fd); + } + + return result; +} diff --git a/tests/msg_tst.c b/tests/msg_tst.c index 10ff0f7..539c5dd 100644 --- a/tests/msg_tst.c +++ b/tests/msg_tst.c @@ -74,7 +74,10 @@ 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_INVALID_SECTION_TYPE , "invalid section type" }, + { ELF_E_INVALID_SECTION_FLAGS , "invalid section flags" }, + { ELF_E_NOT_COMPRESSED, "section does not contain compressed data" } };
diff --git a/tests/run-elfgetchdr.sh b/tests/run-elfgetchdr.sh new file mode 100755 index 0000000..7a422f3 --- /dev/null +++ b/tests/run-elfgetchdr.sh @@ -0,0 +1,188 @@ +#! /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 + +# = funcs.s = +# .globl testfunc +# testfunc: +# nop +# ret +# .type testfunc, @function +# .size testfunc, .-testfunc +# +# .globl testfunc2 +# testfunc2: +# call testfunc +# nop +# nop +# ret +# .type testfunc2, @function +# .size testfunc2, .-testfunc2 +# +# .globl functest3 +# functest3: +# jmp local +# nop +# nop +# local: +# call testfunc2 +# ret +# .type functest3, @function +# .size functest3, .-functest3 + +# = start.s = +# .global _start +# _start: +# call functest3 +# nop +# nop +# nop +# nop +# nop +# nop +# nop +# nop +# nop +# nop +# nop +# nop +# nop +# nop +# nop +# nop +# ret +# .type _start, @function +# .size _start, .-_start + +# gas --compress-debug-sections=zlib-gnu -32 -g -o start.o start.s +# gas --compress-debug-sections=zlib-gnu -32 -g -o funcs.o funcs.s +# ld --compress-debug-sections=zlib-gnu -melf_i386 -g -o zgnu32 funcs.o start.o + +# gas --compress-debug-sections=zlib-gnu -64 -g -o start.o start.s +# gas --compress-debug-sections=zlib-gnu -64 -g -o funcs.o funcs.s +# ld --compress-debug-sections=zlib-gnu -g -o zgnu64 funcs.o start.o + +# gas --compress-debug-sections=zlib-gabi -32 -g -o start.o start.s +# gas --compress-debug-sections=zlib-gabi -32 -g -o funcs.o funcs.s +# ld --compress-debug-sections=zlib-gabi -melf_i386 -g -o zgabi32 funcs.o start.o + +# gas --compress-debug-sections=zlib-gabi -64 -g -o start.o start.s +# gas --compress-debug-sections=zlib-gabi -64 -g -o funcs.o funcs.s +# ld --compress-debug-sections=zlib-gabi -g -o zgabi64 funcs.o start.o + +testfiles testfile-zgnu64 +testrun_compare ${abs_top_builddir}/tests/elfgetchdr testfile-zgnu64 <<\EOF +section 1: NOT Compressed +section 2: GNU Compressed size: 60 +section 3: GNU Compressed size: aa +section 4: NOT Compressed +section 5: GNU Compressed size: 8d +section 6: NOT Compressed +section 7: NOT Compressed +section 8: NOT Compressed +EOF + +testfiles testfile-zgabi64 +testrun_compare ${abs_top_builddir}/tests/elfgetchdr testfile-zgabi64 <<\EOF +section 1: NOT Compressed +section 2: ELF Compressed ch_type: 1, ch_size: 60, ch_addralign: 10 +section 3: ELF Compressed ch_type: 1, ch_size: aa, ch_addralign: 1 +section 4: NOT Compressed +section 5: ELF Compressed ch_type: 1, ch_size: 8d, ch_addralign: 1 +section 6: NOT Compressed +section 7: NOT Compressed +section 8: NOT Compressed +EOF + +testfiles testfile-zgnu32 +testrun_compare ${abs_top_builddir}/tests/elfgetchdr testfile-zgnu32 <<\EOF +section 1: NOT Compressed +section 2: GNU Compressed size: 40 +section 3: GNU Compressed size: 9a +section 4: NOT Compressed +section 5: GNU Compressed size: 85 +section 6: NOT Compressed +section 7: NOT Compressed +section 8: NOT Compressed +EOF + +testfiles testfile-zgabi32 +testrun_compare ${abs_top_builddir}/tests/elfgetchdr testfile-zgabi32 <<\EOF +section 1: NOT Compressed +section 2: ELF Compressed ch_type: 1, ch_size: 40, ch_addralign: 8 +section 3: ELF Compressed ch_type: 1, ch_size: 9a, ch_addralign: 1 +section 4: NOT Compressed +section 5: ELF Compressed ch_type: 1, ch_size: 85, ch_addralign: 1 +section 6: NOT Compressed +section 7: NOT Compressed +section 8: NOT Compressed +EOF + +testfiles testfile-zgnu64be +testrun_compare ${abs_top_builddir}/tests/elfgetchdr testfile-zgnu64be <<\EOF +section 1: NOT Compressed +section 2: NOT Compressed +section 3: GNU Compressed size: 60 +section 4: GNU Compressed size: 7e +section 5: NOT Compressed +section 6: GNU Compressed size: 8d +section 7: NOT Compressed +section 8: NOT Compressed +section 9: NOT Compressed +EOF + +testfiles testfile-zgabi64be +testrun_compare ${abs_top_builddir}/tests/elfgetchdr testfile-zgabi64be <<\EOF +section 1: NOT Compressed +section 2: NOT Compressed +section 3: ELF Compressed ch_type: 1, ch_size: 60, ch_addralign: 10 +section 4: ELF Compressed ch_type: 1, ch_size: 7e, ch_addralign: 1 +section 5: NOT Compressed +section 6: ELF Compressed ch_type: 1, ch_size: 8d, ch_addralign: 1 +section 7: NOT Compressed +section 8: NOT Compressed +section 9: NOT Compressed +EOF + +testfiles testfile-zgnu32be +testrun_compare ${abs_top_builddir}/tests/elfgetchdr testfile-zgnu32be <<\EOF +section 1: NOT Compressed +section 2: NOT Compressed +section 3: GNU Compressed size: 40 +section 4: GNU Compressed size: 6e +section 5: NOT Compressed +section 6: GNU Compressed size: 85 +section 7: NOT Compressed +section 8: NOT Compressed +section 9: NOT Compressed +EOF + +testfiles testfile-zgabi32be +testrun_compare ${abs_top_builddir}/tests/elfgetchdr testfile-zgabi32be <<\EOF +section 1: NOT Compressed +section 2: NOT Compressed +section 3: ELF Compressed ch_type: 1, ch_size: 40, ch_addralign: 8 +section 4: ELF Compressed ch_type: 1, ch_size: 6e, ch_addralign: 1 +section 5: NOT Compressed +section 6: ELF Compressed ch_type: 1, ch_size: 85, ch_addralign: 1 +section 7: NOT Compressed +section 8: NOT Compressed +section 9: NOT Compressed +EOF + +exit 0 diff --git a/tests/testfile-zgabi32.bz2 b/tests/testfile-zgabi32.bz2 new file mode 100755 index 0000000000000000000000000000000000000000..6159dbcea3a74cc1851ac69e8d985d3224b9fe3f GIT binary patch literal 767 zcmV<b0s#F&T4*^jL0KkKSu?W*00050|NsB@{V8s7d~EmpWmW(8|9$6Wcs(<4h;aTe z5FikJ=rF(nGl7hZHB3r+Owu%H(0Z8-83CXH=?sQ|Gy_0nXaS~%fEo=LjRQ?I$QqiO zG>n98A(KNRO&S0V0iXZ?00000003wJ03g#N38PFP#4$9$5s{`LiGUL(011$2zyJ}G z36lU!BLt?Rnx^#x^){o_)6`~w6DB5src6vs6B9#CFeVcqWYEBx01X%dG|0&#AtP84 z2+AmAh{6?1>!;MnfJ-$c%yXTxV0GETe=$D)H(|(ZxHGvVfDVf&lp`3&`J{fB4hRJ# zWt^rf;I8rX36>`i)Po&r?#x3kkiZ1WdJ-Ix9E5<5n2RCQDJF^0I*4%o;GjSyDw&*_ zA~I$QWD@C#B4OOb;U!Oti;XaBDNa-_Nh;Je^sV<cA!Lp^g9*b)K|u`D=@WIvQBz1z zAkO>aKl=ljr6^~I{$!3b(HjqI*Gs9!4c+G#sfu7zA_!>QO(I1T*)Tlqh&l#$so>MB zCWR5x!t_(ch~r^F#F)p1Ps=N7Qa(%df0E!*#+xh1fZ;_y5z2Tm`4pejY68R&jtcJv z3V?iGVu>UThy#KEi;~}vH-3v#;lEO0+Qinmn!XI-DS)>_#uZ!b?)k=KN1Kvl&XVYI zcrgc=9`#e0c8<b=hGp_`qXG_iIprKyp3zEj%P3_qMNn3AQY42c3s8nOh;+G*P!6~Z zgQlizUWiCE5I{S#jRt);*)d6>;=nhC5TnNp9^s-ig8^vf8KF3UK=8}C$W>&!h~a3E zslbNN2H-Rw%9-l5xPv?4xTTF9RiOm5*k)nRy)0Lhz{-U-saEpkx{#Y~Y;-4gpp5&% z4ZwoK3Kt1|JW`<Q1EVX5)iGC{vm@-XG?+AFJ$UNg&5IAYM_jM`Us@6+VhouHPnU(J x5@fLs)tWGq0%19=(faJX>8_VVz=f!nIAj-AL5RFEq~iaJxgwk>NEz9JfB?<JRQUh^
literal 0 HcmV?d00001
diff --git a/tests/testfile-zgabi32be.bz2 b/tests/testfile-zgabi32be.bz2 new file mode 100755 index 0000000000000000000000000000000000000000..f1f5eb5c7928ee9b3c91d5770da6008f9444df94 GIT binary patch literal 769 zcmV+c1OEI%T4*^jL0KkKSyL6bcK`wWfB*mg|LcBleUoo*9A*FKpLO#^dXzUvV8UnW z7(gQnAvVAQGlf~2!X}eM%6ggx)Bp_t27^EzpbZ9q&;T?5(?dW5LqkmljUH3d4NWGd zr<C%Tk5JQ0Kmnit05s4vXaE2J0000DGyniWh{7@$34&x|V3-6j0T=>dF*Gom88AZ# z!Z1TaCPqvGQ&5CzpOrkLO#^8IM$`Z_22BU30qST!Q~&?~0BF(b28NGN00=zoFg}t7 zYNrUue$2@Q&T}q6s~^6`ww<P6D6>e0pgX)fX3u9PyC~~jOtV`PMCNcsBQlUNj!Yon z0ISz&m%}969K!Dv0mmdxebsk8TYhvZQEz7k0!XEG!0h4+;7mOPs3C9;2O*nq=Lsak zNXeMOLjdG37g+}k9APlcs7x`SOcjN4iRMi{?hP_I+8&2XO;E0+2BMVA_FSz!C^H3I zDCB|5yo1oHyRZ)(dgAzHD!W2JlZe9*sH8#0<B@K%3>@6kJ$*pM@{Mp8Z*<9GFd|TZ zi44$RsX>jPA)^yUvnjUd%c$*d9+NgWK-G|9stL;l2w;pLa-VRNgLMZYX)zc;VG~eB z>cV!!AaXMsR=9qcIHg>BqKqXQo~j_(&f(~a!c7K{ILt?53N#n}Fan7?QIXha5F7{s zc-Ae=Z@O8Hg0uH4KPU$UruV`RPw)OWC0qRIbYBTU<{`kal9W>tJQyg${J8U&j$VhE z=rLhvlMVZ?dUIEb(Pk#k<0h#mtaJJBjHby;A51(vHEpcU_KMIu0&u4Vl&IBJ=hmEX zGG_M_BAv#Hj}(hY_US-wg@A>C6LsYk9L)@!69HK0g;PX11f+?lOMgXIO6@*r3Qq}x z9uq{i78G<=laFWlEhH>pLml6K>EVYT+7vq_Xn_wGD#_#JH7C#8IDc1{?Wf#CJ)`W1 z@LC8x{Iy9nioDPOHRV+m*kLAINyLk+!CeZji=Jz%0|J8-pW^OFrwS4ZVz&<9l~qNG
literal 0 HcmV?d00001
diff --git a/tests/testfile-zgabi64.bz2 b/tests/testfile-zgabi64.bz2 new file mode 100755 index 0000000000000000000000000000000000000000..3b44f089e98c7fe7f90969cbbefbca5d8a16c36a GIT binary patch literal 795 zcmV+$1LXWdT4*^jL0KkKSwf>T@&EzcfB*k~{cnC*dPo0UO(_58-}tj`bSHOEd`9o2 z7zTBCrZ&I=90EiYH87LZ{YIvkO$`HTG|<8g0BAG;qfImb292TyhCl(ML7)eudYWxd zQ2-elX{II-iIIt;O)@etBLZLu(WXWwfr2zLFhe5~0x$qf2mr{_O))TxOpHw$X_1kE z7!v?SjWRJb3=yG`f*BZ?5r6_<5}Kxv)bvbA;+PptG{`hygFrOXPe?Q#riM*4$kC%f zWFDhKL7|`;001U};A;hb{Aj`zK81A|M7&D~BuA$);<a{EMD*FL{Y>Q~N{<g+k8OQZ zwM3-f=@m84&3P)XEi3zrxOW!yfaJu41)ZzF!;(T(sghxvZFxn_U2S!C`A~)!Yb9TK zby}KxL=GUCpF;@)2{KKjLr*{i!82@D9xg64CR(V}L2r?h)E$(n@g(9{>&=G)Md{J8 z;Tl>ZrExv>#+y8LL>HquUpxblMMMH(m5B3wQBQMeP8fyKxJC*L<@UL+!hNYts%H`e z8Oe;q&DuqRXmVaKL{7+f(u)ro>C#Y8#4L-`+l1(>Mob23JD#b|A{keX4t+(dnu;wQ z0lXh(xp7N=YL7Bh1j2Tl4M-^#=5v!qLySIdDC$62yrKfVcgQOM9)ti_2KC|Y#G?Kh zp$WSE(n)3!hAlG@#$}5v<mK5tkFdj_$gCf(>RK7^7L&Co{7Ee9>&hKtzVPv^dU|Rf zEZP*{u^K+Fh4gu(>vP_5Q2`5k)&yL@iTA+s87Zvd_=BSTNJ34N($qv%))V~UqZYz` z1dRH~T&4PBgv|mrgy{0$uSCKL^5Ba6p$_V-y0SDcdJ~9d5u6diP)Boaop?xUIMW`4 z>SnHDu#pB$bk^XB2cG&$mgx-`3XB{crG5W8SpXn{+b?QDku3zT$LpIpD_A9_lN3@P z85`aa?w`5Yi9<-p_IMk|gAt`rA03fx4Gb18K9E6zO&7`<AnDpHJP@hn4v9gQi02TZ Z%-Q_&a*{Ly1>#@g?ntK!5(rdgen5r;U~B*Y
literal 0 HcmV?d00001
diff --git a/tests/testfile-zgabi64be.bz2 b/tests/testfile-zgabi64be.bz2 new file mode 100755 index 0000000000000000000000000000000000000000..d819ae3d48dcf1bd15dfb5ef86f6101dce5c8e9d GIT binary patch literal 780 zcmV+n1M~bsT4*^jL0KkKS;4%Z*8l=8fB*mg{{4RKcv9bAL{$Ih-)YKTQXY3fU|LL$ zP!uyxem1}YD}l6-H8g`tnVM7bnomtkBNG}ClOCy&gkTc@28p#BLS}@-dW}5*MgVBh zhNgp21jI5jH5*9NMw$Qs13)qW0B8UJ000Jn000OXFh&4_Oay6yGz?5eOax&XVqpvf z$Y{hgVqlCW089WRQAU#lo=9YRjEy}_4FEL6Xc_|{k)}W%pwIvSX`lvx1|S5f10-P< z)o#|6H`hqv)CZ7nVLQWzznFOLW<=0<>5_C`l5+J$2;u}$UJB6GqiRfsA-wi_Ku0bV zF;cgCK-xNH1RJ_aE8AIRl`5qKYps9^Eg+4}yhH|x;91<p)d<*BWJb~<F+nIW6KS@< z35*a=Vp4=OmGctg4FS|>4SPr`u_a7sv}GV?I@1@UrJ#5m$@HL#(pHFrgZHC13l=~# zte&+H1+~pE+eR*{k>``HHxTO7Dtx73+LRnoaTpftRj5-UxB6m|h^dSejY!B}LR6EY zW6OZu7ELec#U+m}gw+W~@$kr(2MY>aMv~^$oJ6zC&Z1vO^@s)o-EOau=${QR#x7gr z2L{_FK{&NM2-Y*o`E|3_e+`Tk!S+xL138Bcu@B}*dhPwNy6h()lR!vC0r6_<vgzM? zLUxL3m#nJ<)3(s?P$;BJWty=@OfK0aGMLZT!AwUgXERk6YC#{fjg*BN%%}z#Hbw@M zk9;XjkrBpVH6(H~2;K)u7b=Jo2J1Q8t(pe_*EIw)NOtfJqmJcn12#o+u^bn{6U`bf z+S!1D3O%=Gx<(Ypi2#H^bHz|e`NyzTLQ$$SWO9WDzbe~HqR_`hHKTBepj0LUU=w82 zf!Y*nejiXFUJx}j7cD(54$db2|Nnn6T{7{XZKSK%ujUa*YoP<Xt^Dc+H$-LvWI7jj z77J1D-%m-A8^L`!_VJ=wZs$soFU_#rk6y2!9UtV3c~aX}U-!=~3oh;ALBTyYh`W+0 K!i0tm<p#KZ=uF1|
literal 0 HcmV?d00001
-z, --decompress Show compression information for compressed sections (when used with -S).
Signed-off-by: Mark Wielaard mjw@redhat.com --- src/ChangeLog | 8 ++ src/readelf.c | 66 +++++++++++++++- tests/ChangeLog | 6 ++ tests/Makefile.am | 3 +- tests/run-readelf-z.sh | 202 +++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 281 insertions(+), 4 deletions(-) create mode 100755 tests/run-readelf-z.sh
diff --git a/src/ChangeLog b/src/ChangeLog index 0bbfa5b..9724221 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,11 @@ +2015-10-16 Mark Wielaard mjw@redhat.com + + * readelf.c (argp_option): Describe --decompress,-z. + (print_decompress): New bool. + (parse_opt): Handle -z. + (elf_ch_type_name): New function. + (print_shdr): Print section compress information. + 2015-12-31 Mark Wielaard mjw@redhat.com
* elflint.c (check_symtab): Add _edata and _end (plus extra underscore diff --git a/src/readelf.c b/src/readelf.c index 5f6e4ed..1d507cf 100644 --- a/src/readelf.c +++ b/src/readelf.c @@ -48,6 +48,7 @@ #include "../libelf/libelfP.h" #include "../libelf/common.h" #include "../libebl/libeblP.h" +#include "../libdwelf/libdwelf.h" #include "../libdw/libdwP.h" #include "../libdwfl/libdwflP.h" #include "../libdw/memory-access.h" @@ -112,6 +113,8 @@ static const struct argp_option options[] = N_("Display just offsets instead of resolving values to addresses in DWARF data"), 0 }, { "wide", 'W', NULL, 0, N_("Ignored for compatibility (lines always wide)"), 0 }, + { "decompress", 'z', NULL, 0, + N_("Show compression information for compressed sections (when used with -S)."), 0 }, { NULL, 0, NULL, 0, NULL, 0 } };
@@ -190,6 +193,9 @@ static bool decodedaranges = false; /* True if we should print the .debug_aranges section using libdw. */ static bool decodedline = false;
+/* True if we want to show more information about compressed sections. */ +static bool print_decompress = false; + /* Select printing of debugging sections. */ static enum section_e { @@ -479,6 +485,9 @@ parse_opt (int key, char *arg, break; case 'W': /* Ignored. */ break; + case 'z': + print_decompress = true; + break; case ELF_INPUT_SECTION: if (arg == NULL) elf_input_section = ".gnu_debugdata"; @@ -1065,6 +1074,17 @@ get_visibility_type (int value) } }
+static const char * +elf_ch_type_name (unsigned int code) +{ + if (code == 0) + return "NONE"; + + if (code == ELFCOMPRESS_ZLIB) + return "ZLIB"; + + return "UNKNOWN"; +}
/* Print the section headers. */ static void @@ -1091,6 +1111,14 @@ There are %d section headers, starting at offset %#" PRIx64 ":\n\ else puts (gettext ("[Nr] Name Type Addr Off Size ES Flags Lk Inf Al"));
+ if (print_decompress) + { + if (ehdr->e_ident[EI_CLASS] == ELFCLASS32) + puts (gettext (" [Compression Size Al]")); + else + puts (gettext (" [Compression Size Al]")); + } + for (cnt = 0; cnt < shnum; ++cnt) { Elf_Scn *scn = elf_getscn (ebl->elf, cnt); @@ -1128,25 +1156,57 @@ There are %d section headers, starting at offset %#" PRIx64 ":\n\ *cp++ = 'G'; if (shdr->sh_flags & SHF_TLS) *cp++ = 'T'; + if (shdr->sh_flags & SHF_COMPRESSED) + *cp++ = 'C'; if (shdr->sh_flags & SHF_ORDERED) *cp++ = 'O'; if (shdr->sh_flags & SHF_EXCLUDE) *cp++ = 'E'; *cp = '\0';
+ const char *sname; char buf[128]; + sname = elf_strptr (ebl->elf, shstrndx, shdr->sh_name) ?: "<corrupt>"; printf ("[%2zu] %-20s %-12s %0*" PRIx64 " %0*" PRIx64 " %0*" PRIx64 " %2" PRId64 " %-5s %2" PRId32 " %3" PRId32 " %2" PRId64 "\n", - cnt, - elf_strptr (ebl->elf, shstrndx, shdr->sh_name) - ?: "<corrupt>", + cnt, sname, ebl_section_type_name (ebl, shdr->sh_type, buf, sizeof (buf)), ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? 8 : 16, shdr->sh_addr, ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? 6 : 8, shdr->sh_offset, ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? 6 : 8, shdr->sh_size, shdr->sh_entsize, flagbuf, shdr->sh_link, shdr->sh_info, shdr->sh_addralign); + + if (print_decompress) + { + if ((shdr->sh_flags & SHF_COMPRESSED) != 0) + { + GElf_Chdr chdr; + if (gelf_getchdr (scn, &chdr) != NULL) + printf (" [ELF %s (%" PRId32 ") %0*" PRIx64 + " %2" PRId64 "]\n", + elf_ch_type_name (chdr.ch_type), + chdr.ch_type, + ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? 6 : 8, + chdr.ch_size, chdr.ch_addralign); + else + error (0, 0, + gettext ("bad compression header for section %zd: %s"), + elf_ndxscn (scn), elf_errmsg (-1)); + } + else if (strncmp(".zdebug", sname, strlen (".zdebug")) == 0) + { + ssize_t size; + if ((size = dwelf_scn_gnu_compressed_size (scn)) >= 0) + printf (" [GNU ZLIB %0*" PRIx64 " ]\n", + ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? 6 : 8, size); + else + error (0, 0, + gettext ("bad gnu compressed size for section %zd: %s"), + elf_ndxscn (scn), elf_errmsg (-1)); + } + } }
fputc_unlocked ('\n', stdout); diff --git a/tests/ChangeLog b/tests/ChangeLog index 932c513..d083877 100644 --- a/tests/ChangeLog +++ b/tests/ChangeLog @@ -1,5 +1,11 @@ 2015-10-28 Mark Wielaard mjw@redhat.com
+ * run-readelf-z.sh: New test. + * Makefile.am (TESTS): Add run-readelf-z.sh. + (EXTRA_DIST): Likewise. + +2015-10-28 Mark Wielaard mjw@redhat.com + * elfgetchdr.c: New file. * run-elfgetchdr.sh: New test. * testfile-zgabi32.bz2: New testfile. diff --git a/tests/Makefile.am b/tests/Makefile.am index e4b0fef..71235c5 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -90,7 +90,7 @@ TESTS = run-arextract.sh run-arsymtest.sh newfile test-nlist \ run-readelf-test1.sh run-readelf-test2.sh run-readelf-test3.sh \ run-readelf-test4.sh run-readelf-twofiles.sh \ run-readelf-macro.sh run-readelf-loc.sh \ - run-readelf-aranges.sh run-readelf-line.sh \ + run-readelf-aranges.sh run-readelf-line.sh run-readelf-z.sh \ run-native-test.sh run-bug1-test.sh \ run-debuglink.sh run-debugaltlink.sh run-buildid.sh \ dwfl-bug-addr-overflow run-addrname-test.sh \ @@ -209,6 +209,7 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh \ run-readelf-macro.sh testfilemacro.bz2 \ run-readelf-loc.sh testfileloc.bz2 \ run-readelf-aranges.sh run-readelf-line.sh testfilefoobarbaz.bz2 \ + run-readelf-z.sh \ run-readelf-dwz-multi.sh libtestfile_multi_shared.so.bz2 \ testfile_multi.dwz.bz2 testfile_multi_main.bz2 \ testfile-dwzstr.bz2 testfile-dwzstr.multi.bz2 \ diff --git a/tests/run-readelf-z.sh b/tests/run-readelf-z.sh new file mode 100755 index 0000000..6dbd2f1 --- /dev/null +++ b/tests/run-readelf-z.sh @@ -0,0 +1,202 @@ +#! /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}/src/readelf -z -S testfile-zgnu64 <<\EOF +There are 9 section headers, starting at offset 0x3e0: + +Section Headers: +[Nr] Name Type Addr Off Size ES Flags Lk Inf Al + [Compression Size Al] +[ 0] NULL 0000000000000000 00000000 00000000 0 0 0 0 +[ 1] .text PROGBITS 0000000000400078 00000078 0000002a 0 AX 0 0 1 +[ 2] .zdebug_aranges PROGBITS 0000000000000000 00000260 00000032 0 0 0 16 + [GNU ZLIB 00000060 ] +[ 3] .zdebug_info PROGBITS 0000000000000000 00000292 0000006f 0 0 0 1 + [GNU ZLIB 000000aa ] +[ 4] .debug_abbrev PROGBITS 0000000000000000 00000301 00000028 0 0 0 1 +[ 5] .zdebug_line PROGBITS 0000000000000000 00000329 0000005b 0 0 0 1 + [GNU ZLIB 0000008d ] +[ 6] .shstrtab STRTAB 0000000000000000 00000384 00000059 0 0 0 1 +[ 7] .symtab SYMTAB 0000000000000000 000000a8 00000168 24 8 8 8 +[ 8] .strtab STRTAB 0000000000000000 00000210 0000004b 0 0 0 1 + +EOF + +testfiles testfile-zgnu64be +testrun_compare ${abs_top_builddir}/src/readelf -z -S testfile-zgnu64be <<\EOF +There are 10 section headers, starting at offset 0x438: + +Section Headers: +[Nr] Name Type Addr Off Size ES Flags Lk Inf Al + [Compression Size Al] +[ 0] NULL 0000000000000000 00000000 00000000 0 0 0 0 +[ 1] .text PROGBITS 0000000010000078 00000078 00000074 0 AX 0 0 8 +[ 2] .eh_frame PROGBITS 00000000100000ec 000000ec 00000000 0 A 0 0 4 +[ 3] .zdebug_aranges PROGBITS 0000000000000000 000002c0 00000034 0 0 0 16 + [GNU ZLIB 00000060 ] +[ 4] .zdebug_info PROGBITS 0000000000000000 000002f4 00000059 0 0 0 1 + [GNU ZLIB 0000007e ] +[ 5] .debug_abbrev PROGBITS 0000000000000000 0000034d 00000028 0 0 0 1 +[ 6] .zdebug_line PROGBITS 0000000000000000 00000375 0000005b 0 0 0 1 + [GNU ZLIB 0000008d ] +[ 7] .shstrtab STRTAB 0000000000000000 000003d0 00000063 0 0 0 1 +[ 8] .symtab SYMTAB 0000000000000000 000000f0 00000180 24 9 9 8 +[ 9] .strtab STRTAB 0000000000000000 00000270 00000044 0 0 0 1 + +EOF + +testfiles testfile-zgabi64 +testrun_compare ${abs_top_builddir}/src/readelf -z -S testfile-zgabi64 <<\EOF +There are 9 section headers, starting at offset 0x400: + +Section Headers: +[Nr] Name Type Addr Off Size ES Flags Lk Inf Al + [Compression Size Al] +[ 0] NULL 0000000000000000 00000000 00000000 0 0 0 0 +[ 1] .text PROGBITS 0000000000400078 00000078 0000002a 0 AX 0 0 1 +[ 2] .debug_aranges PROGBITS 0000000000000000 00000260 0000003e 0 C 0 0 16 + [ELF ZLIB (1) 00000060 16] +[ 3] .debug_info PROGBITS 0000000000000000 0000029e 0000007b 0 C 0 0 1 + [ELF ZLIB (1) 000000aa 1] +[ 4] .debug_abbrev PROGBITS 0000000000000000 00000319 00000028 0 0 0 1 +[ 5] .debug_line PROGBITS 0000000000000000 00000341 00000067 0 C 0 0 1 + [ELF ZLIB (1) 0000008d 1] +[ 6] .shstrtab STRTAB 0000000000000000 000003a8 00000056 0 0 0 1 +[ 7] .symtab SYMTAB 0000000000000000 000000a8 00000168 24 8 8 8 +[ 8] .strtab STRTAB 0000000000000000 00000210 0000004b 0 0 0 1 + +EOF + +testfiles testfile-zgabi64be +testrun_compare ${abs_top_builddir}/src/readelf -z -S testfile-zgabi64be <<\EOF +There are 10 section headers, starting at offset 0x458: + +Section Headers: +[Nr] Name Type Addr Off Size ES Flags Lk Inf Al + [Compression Size Al] +[ 0] NULL 0000000000000000 00000000 00000000 0 0 0 0 +[ 1] .text PROGBITS 0000000010000078 00000078 00000074 0 AX 0 0 8 +[ 2] .eh_frame PROGBITS 00000000100000ec 000000ec 00000000 0 A 0 0 4 +[ 3] .debug_aranges PROGBITS 0000000000000000 000002c0 00000040 0 C 0 0 16 + [ELF ZLIB (1) 00000060 16] +[ 4] .debug_info PROGBITS 0000000000000000 00000300 00000065 0 C 0 0 1 + [ELF ZLIB (1) 0000007e 1] +[ 5] .debug_abbrev PROGBITS 0000000000000000 00000365 00000028 0 0 0 1 +[ 6] .debug_line PROGBITS 0000000000000000 0000038d 00000067 0 C 0 0 1 + [ELF ZLIB (1) 0000008d 1] +[ 7] .shstrtab STRTAB 0000000000000000 000003f4 00000060 0 0 0 1 +[ 8] .symtab SYMTAB 0000000000000000 000000f0 00000180 24 9 9 8 +[ 9] .strtab STRTAB 0000000000000000 00000270 00000044 0 0 0 1 + +EOF + +testfiles testfile-zgnu32 +testrun_compare ${abs_top_builddir}/src/readelf -z -S testfile-zgnu32 <<\EOF +There are 9 section headers, starting at offset 0x33c: + +Section Headers: +[Nr] Name Type Addr Off Size ES Flags Lk Inf Al + [Compression Size Al] +[ 0] NULL 00000000 000000 000000 0 0 0 0 +[ 1] .text PROGBITS 08048054 000054 00002a 0 AX 0 0 1 +[ 2] .zdebug_aranges PROGBITS 00000000 0001c0 000031 0 0 0 8 + [GNU ZLIB 000040 ] +[ 3] .zdebug_info PROGBITS 00000000 0001f1 00006f 0 0 0 1 + [GNU ZLIB 00009a ] +[ 4] .debug_abbrev PROGBITS 00000000 000260 000028 0 0 0 1 +[ 5] .zdebug_line PROGBITS 00000000 000288 00005a 0 0 0 1 + [GNU ZLIB 000085 ] +[ 6] .shstrtab STRTAB 00000000 0002e2 000059 0 0 0 1 +[ 7] .symtab SYMTAB 00000000 000080 0000f0 16 8 8 4 +[ 8] .strtab STRTAB 00000000 000170 00004b 0 0 0 1 + +EOF + +testfiles testfile-zgnu32be +testrun_compare ${abs_top_builddir}/src/readelf -z -S testfile-zgnu32be <<\EOF +There are 10 section headers, starting at offset 0x390: + +Section Headers: +[Nr] Name Type Addr Off Size ES Flags Lk Inf Al + [Compression Size Al] +[ 0] NULL 00000000 000000 000000 0 0 0 0 +[ 1] .text PROGBITS 01800054 000054 000074 0 AX 0 0 1 +[ 2] .eh_frame PROGBITS 018000c8 0000c8 000000 0 A 0 0 4 +[ 3] .zdebug_aranges PROGBITS 00000000 000220 000033 0 0 0 8 + [GNU ZLIB 000040 ] +[ 4] .zdebug_info PROGBITS 00000000 000253 000058 0 0 0 1 + [GNU ZLIB 00006e ] +[ 5] .debug_abbrev PROGBITS 00000000 0002ab 000028 0 0 0 1 +[ 6] .zdebug_line PROGBITS 00000000 0002d3 000059 0 0 0 1 + [GNU ZLIB 000085 ] +[ 7] .shstrtab STRTAB 00000000 00032c 000063 0 0 0 1 +[ 8] .symtab SYMTAB 00000000 0000c8 000110 16 9 9 4 +[ 9] .strtab STRTAB 00000000 0001d8 000045 0 0 0 1 + +EOF + +testfiles testfile-zgabi32 +testrun_compare ${abs_top_builddir}/src/readelf -z -S testfile-zgabi32 <<\EOF +There are 9 section headers, starting at offset 0x338: + +Section Headers: +[Nr] Name Type Addr Off Size ES Flags Lk Inf Al + [Compression Size Al] +[ 0] NULL 00000000 000000 000000 0 0 0 0 +[ 1] .text PROGBITS 08048054 000054 00002a 0 AX 0 0 1 +[ 2] .debug_aranges PROGBITS 00000000 0001c0 000031 0 C 0 0 8 + [ELF ZLIB (1) 000040 8] +[ 3] .debug_info PROGBITS 00000000 0001f1 00006f 0 C 0 0 1 + [ELF ZLIB (1) 00009a 1] +[ 4] .debug_abbrev PROGBITS 00000000 000260 000028 0 0 0 1 +[ 5] .debug_line PROGBITS 00000000 000288 00005a 0 C 0 0 1 + [ELF ZLIB (1) 000085 1] +[ 6] .shstrtab STRTAB 00000000 0002e2 000056 0 0 0 1 +[ 7] .symtab SYMTAB 00000000 000080 0000f0 16 8 8 4 +[ 8] .strtab STRTAB 00000000 000170 00004b 0 0 0 1 + +EOF + +testfiles testfile-zgabi32be +testrun_compare ${abs_top_builddir}/src/readelf -z -S testfile-zgabi32be <<\EOF +There are 10 section headers, starting at offset 0x38c: + +Section Headers: +[Nr] Name Type Addr Off Size ES Flags Lk Inf Al + [Compression Size Al] +[ 0] NULL 00000000 000000 000000 0 0 0 0 +[ 1] .text PROGBITS 01800054 000054 000074 0 AX 0 0 1 +[ 2] .eh_frame PROGBITS 018000c8 0000c8 000000 0 A 0 0 4 +[ 3] .debug_aranges PROGBITS 00000000 000220 000033 0 C 0 0 8 + [ELF ZLIB (1) 000040 8] +[ 4] .debug_info PROGBITS 00000000 000253 000058 0 C 0 0 1 + [ELF ZLIB (1) 00006e 1] +[ 5] .debug_abbrev PROGBITS 00000000 0002ab 000028 0 0 0 1 +[ 6] .debug_line PROGBITS 00000000 0002d3 000059 0 C 0 0 1 + [ELF ZLIB (1) 000085 1] +[ 7] .shstrtab STRTAB 00000000 00032c 000060 0 0 0 1 +[ 8] .symtab SYMTAB 00000000 0000c8 000110 16 9 9 4 +[ 9] .strtab STRTAB 00000000 0001d8 000045 0 0 0 1 + +EOF + +exit 0
Signed-off-by: Mark Wielaard mjw@redhat.com --- libelf/ChangeLog | 30 +++ libelf/Makefile.am | 3 +- libelf/elf32_getshdr.c | 5 +- libelf/elf32_updatenull.c | 23 ++- libelf/elf_begin.c | 2 + libelf/elf_compress.c | 490 ++++++++++++++++++++++++++++++++++++++++++++++ libelf/elf_compress_gnu.c | 208 ++++++++++++++++++++ libelf/elf_end.c | 11 +- libelf/elf_error.c | 26 ++- libelf/elf_getdata.c | 27 ++- libelf/libelf.h | 64 +++++- libelf/libelf.map | 5 +- libelf/libelfP.h | 24 +++ tests/ChangeLog | 15 ++ tests/Makefile.am | 10 +- tests/elfgetzdata.c | 113 +++++++++++ tests/elfputzdata.c | 232 ++++++++++++++++++++++ tests/msg_tst.c | 6 +- tests/run-elfgetzdata.sh | 214 ++++++++++++++++++++ tests/run-elfputzdata.sh | 340 ++++++++++++++++++++++++++++++++ 20 files changed, 1822 insertions(+), 26 deletions(-) create mode 100644 libelf/elf_compress.c create mode 100644 libelf/elf_compress_gnu.c create mode 100644 tests/elfgetzdata.c create mode 100644 tests/elfputzdata.c create mode 100755 tests/run-elfgetzdata.sh create mode 100755 tests/run-elfputzdata.sh
diff --git a/libelf/ChangeLog b/libelf/ChangeLog index 2ed4a67..1e21400 100644 --- a/libelf/ChangeLog +++ b/libelf/ChangeLog @@ -1,3 +1,33 @@ +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, diff --git a/libelf/Makefile.am b/libelf/Makefile.am index 8a678b8..167a832 100644 --- a/libelf/Makefile.am +++ b/libelf/Makefile.am @@ -89,7 +89,8 @@ libelf_a_SOURCES = elf_version.c elf_hash.c elf_error.c elf_fill.c \ elf_getaroff.c \ elf_gnu_hash.c \ elf_scnshndx.c \ - elf32_getchdr.c elf64_getchdr.c gelf_getchdr.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) diff --git a/libelf/elf32_getshdr.c b/libelf/elf32_getshdr.c index 3a6375c..237d912 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 d3754d3..03de032 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/elf_begin.c b/libelf/elf_begin.c index d2920c4..147f5f4 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 0000000..17b80f2 --- /dev/null +++ b/libelf/elf_compress.c @@ -0,0 +1,490 @@ +/* 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 + +/* 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; + + /* Cleanup and return result. Don't leak memory. */ + void *deflate_cleanup (void *result) + { + deflateEnd (&z); + free (out_buf); + if (ei_data != MY_ELFDATA) + free (cdata.d_buf); + return result; + } + + /* 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_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; + } + + GElf_Chdr chdr; + if (gelf_getchdr (scn, &chdr) == NULL) + return -1; + + if (chdr.ch_type != ELFCOMPRESS_ZLIB) + { + __libelf_seterrno (ELF_E_UNKNOWN_COMPRESSION_TYPE); + return -1; + } + + if (! powerof2 (chdr.ch_addralign)) + { + __libelf_seterrno (ELF_E_INVALID_ALIGN); + return -1; + } + + /* 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 -1; + + 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); + 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. */ + if (elfclass == ELFCLASS32) + { + Elf32_Shdr *shdr = elf32_getshdr (scn); + shdr->sh_size = chdr.ch_size; + shdr->sh_addralign = chdr.ch_addralign; + shdr->sh_flags &= ~SHF_COMPRESSED; + } + else + { + Elf64_Shdr *shdr = elf64_getshdr (scn); + shdr->sh_size = chdr.ch_size; + shdr->sh_addralign = chdr.ch_addralign; + shdr->sh_flags &= ~SHF_COMPRESSED; + } + + __libelf_reset_rawdata (scn, buf_out, chdr.ch_size, chdr.ch_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_compress_gnu.c b/libelf/elf_compress_gnu.c new file mode 100644 index 0000000..c35dc39 --- /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 7ea876c..fde17b5 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 d97e3bd..888b389 100644 --- a/libelf/elf_error.c +++ b/libelf/elf_error.c @@ -245,6 +245,26 @@ core files") (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") };
@@ -295,7 +315,11 @@ static const uint_fast16_t msgidx[ELF_E_NUM] = [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_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 bd1f068..4ec94b9 100644 --- a/libelf/elf_getdata.c +++ b/libelf/elf_getdata.c @@ -118,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, @@ -332,17 +348,8 @@ __libelf_set_rawdata_wrlock (Elf_Scn *scn) section type. */ if ((flags & SHF_COMPRESSED) != 0) scn->rawdata.d.d_type = ELF_T_CHDR; - else if (type == SHT_HASH && elf->class == ELFCLASS64) - { - /* Some broken ELF ABI for 64-bit machines use the wrong hash table - entry size. See elf-knowledge.h for more information. */ - 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); - } 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/libelf.h b/libelf/libelf.h index 49234c8..364e776 100644 --- a/libelf/libelf.h +++ b/libelf/libelf.h @@ -117,6 +117,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 @@ -274,6 +280,57 @@ extern Elf64_Shdr *elf64_getshdr (Elf_Scn *__scn); 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, unsigned int __flags); @@ -294,8 +351,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 d402ccc..10dc505 100644 --- a/libelf/libelf.map +++ b/libelf/libelf.map @@ -144,4 +144,7 @@ ELFUTILS_1.7 { elf32_getchdr; elf64_getchdr; gelf_getchdr; -} ELFUTILS_1.6; \ No newline at end of file + + elf_compress; + elf_compress_gnu; +} ELFUTILS_1.6; diff --git a/libelf/libelfP.h b/libelf/libelfP.h index 8ff8478..50f9217 100644 --- a/libelf/libelfP.h +++ b/libelf/libelfP.h @@ -38,6 +38,7 @@ #include <gelf.h>
#include <errno.h> +#include <stdbool.h> #include <stdint.h> #include <stdio.h> #include <string.h> @@ -141,6 +142,10 @@ enum 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 }; @@ -233,6 +238,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. */ }; @@ -441,6 +448,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.
@@ -581,6 +593,18 @@ 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_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. */ diff --git a/tests/ChangeLog b/tests/ChangeLog index d083877..8ddf32b 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 71235c5..131ed46 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 dwelfgnucompressed elfgetchdr + elfshphehdr elfstrmerge dwelfgnucompressed elfgetchdr \ + elfgetzdata elfputzdata
asm_TESTS = asm-tst1 asm-tst2 asm-tst3 asm-tst4 asm-tst5 \ asm-tst6 asm-tst7 asm-tst8 asm-tst9 @@ -121,7 +122,8 @@ TESTS = run-arextract.sh run-arsymtest.sh newfile test-nlist \ 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-dwelfgnucompressed.sh \ - run-elfgetchdr.sh + run-elfgetchdr.sh \ + run-elfgetzdata.sh run-elfputzdata.sh
if !BIARCH export ELFUTILS_DISABLE_BIARCH = 1 @@ -312,7 +314,7 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh \ run-dwelfgnucompressed.sh \ testfile-zgabi32.bz2 testfile-zgabi64.bz2 \ testfile-zgabi32be.bz2 testfile-zgabi64be.bz2 \ - run-elfgetchdr.sh + run-elfgetchdr.sh run-elfgetzdata.sh run-elfputzdata.sh
if USE_VALGRIND valgrind_cmd='valgrind -q --leak-check=full --error-exitcode=1' @@ -463,6 +465,8 @@ elfshphehdr_LDADD =$(libelf) elfstrmerge_LDADD = $(libebl) $(libelf) dwelfgnucompressed_LDADD = $(libelf) $(libdw) elfgetchdr_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 0000000..82afbe5 --- /dev/null +++ b/tests/elfgetzdata.c @@ -0,0 +1,113 @@ +/* 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 <stdbool.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + + +int +main (int argc, char *argv[]) +{ + int result = 0; + int cnt; + + if (argc < 3 + || (strcmp (argv[1], "read") != 0 + && strcmp (argv[1], "mmap") != 0)) + { + printf ("Usage: (read|mmap) files...\n"); + return -1; + } + + bool mmap = strcmp (argv[1], "mmap") == 0; + + elf_version (EV_CURRENT); + + for (cnt = 2; cnt < argc; ++cnt) + { + int fd = open (argv[cnt], O_RDONLY); + + Elf *elf = elf_begin (fd, mmap ? ELF_C_READ_MMAP : 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) < 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) < 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 0000000..65654f8 --- /dev/null +++ b/tests/elfputzdata.c @@ -0,0 +1,232 @@ +/* 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 <stdbool.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_size > 0 && orig_buf == NULL) + { + printf ("No memory to copy section %zd data\n", idx); + return -1; + } + if (orig_size > 0) + memcpy (orig_buf, d->d_buf, orig_size); + + bool forced = false; + if (gnu) + { + int res = elf_compress_gnu (scn, 1, 0); + if (res == 0) + { + forced = true; + res = elf_compress_gnu (scn, 1, ELF_CHF_FORCE); + } + if (res < 0) + { + printf ("elf_compress_gnu%sfailed for section %zd: %s\n", + forced ? " (forced) " : " ", + idx, elf_errmsg (-1)); + return -1; + } + } + else + { + int res = elf_compress (scn, ELFCOMPRESS_ZLIB, 0); + if (res == 0) + { + forced = true; + res = elf_compress (scn, ELFCOMPRESS_ZLIB, ELF_CHF_FORCE); + } + if (res < 0) + { + printf ("elf_compress%sfailed for section %zd: %s\n", + forced ? " (forced) " : " ", + 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 (forced && new_size < orig_size) + { + printf ("section %zd forced to compress, but size smaller\n", + idx); + return -1; + } + + if (! forced && new_size >= orig_size) + { + printf ("section %zd compressed to bigger size\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, 0) < 0) + { + printf ("elf_[un]compress_gnu failed for section %zd: %s\n", + idx, elf_errmsg (-1)); + return -1; + } + } + else + { + if (elf_compress (scn, 0, 0) < 0) + { + 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 539c5dd..7baea0a 100644 --- a/tests/msg_tst.c +++ b/tests/msg_tst.c @@ -77,7 +77,11 @@ and core files" }, { ELF_E_INVALID_OFFSET, "invalid offset" }, { ELF_E_INVALID_SECTION_TYPE , "invalid section type" }, { ELF_E_INVALID_SECTION_FLAGS , "invalid section flags" }, - { ELF_E_NOT_COMPRESSED, "section does not contain compressed data" } + { 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 0000000..e2df308 --- /dev/null +++ b/tests/run-elfgetzdata.sh @@ -0,0 +1,214 @@ +#! /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 read 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 + +testrun_compare ${abs_top_builddir}/tests/elfgetzdata mmap 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-zgnu64be +testrun_compare ${abs_top_builddir}/tests/elfgetzdata read testfile-zgnu64be <<\EOF +1: .text, NOT compressed +2: .eh_frame, NOT compressed +3: .zdebug_aranges, GNU compressed, size: 60 +4: .zdebug_info, GNU compressed, size: 7e +5: .debug_abbrev, NOT compressed +6: .zdebug_line, GNU compressed, size: 8d +7: .shstrtab, NOT compressed +8: .symtab, NOT compressed +9: .strtab, NOT compressed +EOF + +testrun_compare ${abs_top_builddir}/tests/elfgetzdata mmap testfile-zgnu64be <<\EOF +1: .text, NOT compressed +2: .eh_frame, NOT compressed +3: .zdebug_aranges, GNU compressed, size: 60 +4: .zdebug_info, GNU compressed, size: 7e +5: .debug_abbrev, NOT compressed +6: .zdebug_line, GNU compressed, size: 8d +7: .shstrtab, NOT compressed +8: .symtab, NOT compressed +9: .strtab, NOT compressed +EOF + +testfiles testfile-zgabi64 +testrun_compare ${abs_top_builddir}/tests/elfgetzdata read 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 + +testrun_compare ${abs_top_builddir}/tests/elfgetzdata mmap 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-zgabi64be +testrun_compare ${abs_top_builddir}/tests/elfgetzdata read testfile-zgabi64be <<\EOF +1: .text, NOT compressed +2: .eh_frame, NOT compressed +3: .debug_aranges, ELF compressed, size: 60 +4: .debug_info, ELF compressed, size: 7e +5: .debug_abbrev, NOT compressed +6: .debug_line, ELF compressed, size: 8d +7: .shstrtab, NOT compressed +8: .symtab, NOT compressed +9: .strtab, NOT compressed +EOF + +testrun_compare ${abs_top_builddir}/tests/elfgetzdata mmap testfile-zgabi64be <<\EOF +1: .text, NOT compressed +2: .eh_frame, NOT compressed +3: .debug_aranges, ELF compressed, size: 60 +4: .debug_info, ELF compressed, size: 7e +5: .debug_abbrev, NOT compressed +6: .debug_line, ELF compressed, size: 8d +7: .shstrtab, NOT compressed +8: .symtab, NOT compressed +9: .strtab, NOT compressed +EOF + +testfiles testfile-zgnu32 +testrun_compare ${abs_top_builddir}/tests/elfgetzdata read 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 + +testrun_compare ${abs_top_builddir}/tests/elfgetzdata mmap 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-zgnu32be +testrun_compare ${abs_top_builddir}/tests/elfgetzdata read testfile-zgnu32be <<\EOF +1: .text, NOT compressed +2: .eh_frame, NOT compressed +3: .zdebug_aranges, GNU compressed, size: 40 +4: .zdebug_info, GNU compressed, size: 6e +5: .debug_abbrev, NOT compressed +6: .zdebug_line, GNU compressed, size: 85 +7: .shstrtab, NOT compressed +8: .symtab, NOT compressed +9: .strtab, NOT compressed +EOF + +testrun_compare ${abs_top_builddir}/tests/elfgetzdata mmap testfile-zgnu32be <<\EOF +1: .text, NOT compressed +2: .eh_frame, NOT compressed +3: .zdebug_aranges, GNU compressed, size: 40 +4: .zdebug_info, GNU compressed, size: 6e +5: .debug_abbrev, NOT compressed +6: .zdebug_line, GNU compressed, size: 85 +7: .shstrtab, NOT compressed +8: .symtab, NOT compressed +9: .strtab, NOT compressed +EOF + +testfiles testfile-zgabi32 +testrun_compare ${abs_top_builddir}/tests/elfgetzdata read 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 + +testrun_compare ${abs_top_builddir}/tests/elfgetzdata mmap 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 + +testfiles testfile-zgabi32be +testrun_compare ${abs_top_builddir}/tests/elfgetzdata read testfile-zgabi32be <<\EOF +1: .text, NOT compressed +2: .eh_frame, NOT compressed +3: .debug_aranges, ELF compressed, size: 40 +4: .debug_info, ELF compressed, size: 6e +5: .debug_abbrev, NOT compressed +6: .debug_line, ELF compressed, size: 85 +7: .shstrtab, NOT compressed +8: .symtab, NOT compressed +9: .strtab, NOT compressed +EOF + +testrun_compare ${abs_top_builddir}/tests/elfgetzdata mmap testfile-zgabi32be <<\EOF +1: .text, NOT compressed +2: .eh_frame, NOT compressed +3: .debug_aranges, ELF compressed, size: 40 +4: .debug_info, ELF compressed, size: 6e +5: .debug_abbrev, NOT compressed +6: .debug_line, ELF compressed, size: 85 +7: .shstrtab, NOT compressed +8: .symtab, NOT compressed +9: .strtab, NOT compressed +EOF + +exit 0 diff --git a/tests/run-elfputzdata.sh b/tests/run-elfputzdata.sh new file mode 100755 index 0000000..e9a352e --- /dev/null +++ b/tests/run-elfputzdata.sh @@ -0,0 +1,340 @@ +#! /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 + +# Random ELF64BE testfile +testfiles testfileppc64 + +testrun_compare ${abs_top_builddir}/tests/elfputzdata gnu testfileppc64 <<\EOF +Cannot compress 1 .interp +Cannot compress 2 .note.ABI-tag +Cannot compress 3 .note.gnu.build-id +Cannot compress 4 .gnu.hash +Cannot compress 5 .dynsym +Cannot compress 6 .dynstr +Cannot compress 7 .gnu.version +Cannot compress 8 .gnu.version_r +Cannot compress 9 .rela.plt +Cannot compress 10 .init +Cannot compress 11 .text +Cannot compress 12 .fini +Cannot compress 13 .rodata +Cannot compress 14 .eh_frame_hdr +Cannot compress 15 .eh_frame +Cannot compress 16 .init_array +Cannot compress 17 .fini_array +Cannot compress 18 .jcr +Cannot compress 19 .dynamic +Cannot compress 20 .data +Cannot compress 21 .opd +Cannot compress 22 .got +Cannot compress 23 .plt +Cannot compress 24 .bss +Lets compress 25 .comment, size: 88 +Lets compress 26 .debug_aranges, size: 96 +Lets compress 27 .debug_info, size: 363 +Lets compress 28 .debug_abbrev, size: 315 +Lets compress 29 .debug_line, size: 119 +Lets compress 30 .debug_frame, size: 96 +Lets compress 31 .debug_str, size: 174 +Lets compress 32 .debug_loc, size: 171 +Lets compress 33 .debug_ranges, size: 32 +Not compressing section string table 34 +Lets compress 35 .symtab, size: 1800 +Lets compress 36 .strtab, size: 602 +EOF + +testrun_compare ${abs_top_builddir}/tests/elfputzdata elf testfileppc64 <<\EOF +Cannot compress 1 .interp +Cannot compress 2 .note.ABI-tag +Cannot compress 3 .note.gnu.build-id +Cannot compress 4 .gnu.hash +Cannot compress 5 .dynsym +Cannot compress 6 .dynstr +Cannot compress 7 .gnu.version +Cannot compress 8 .gnu.version_r +Cannot compress 9 .rela.plt +Cannot compress 10 .init +Cannot compress 11 .text +Cannot compress 12 .fini +Cannot compress 13 .rodata +Cannot compress 14 .eh_frame_hdr +Cannot compress 15 .eh_frame +Cannot compress 16 .init_array +Cannot compress 17 .fini_array +Cannot compress 18 .jcr +Cannot compress 19 .dynamic +Cannot compress 20 .data +Cannot compress 21 .opd +Cannot compress 22 .got +Cannot compress 23 .plt +Cannot compress 24 .bss +Lets compress 25 .comment, size: 88 +Lets compress 26 .debug_aranges, size: 96 +Lets compress 27 .debug_info, size: 363 +Lets compress 28 .debug_abbrev, size: 315 +Lets compress 29 .debug_line, size: 119 +Lets compress 30 .debug_frame, size: 96 +Lets compress 31 .debug_str, size: 174 +Lets compress 32 .debug_loc, size: 171 +Lets compress 33 .debug_ranges, size: 32 +Not compressing section string table 34 +Lets compress 35 .symtab, size: 1800 +Lets compress 36 .strtab, size: 602 +EOF + +# Random ELF32BE testfile +testfiles testfileppc32 + +testrun_compare ${abs_top_builddir}/tests/elfputzdata gnu testfileppc32 <<\EOF +Cannot compress 1 .interp +Cannot compress 2 .note.ABI-tag +Cannot compress 3 .note.gnu.build-id +Cannot compress 4 .gnu.hash +Cannot compress 5 .dynsym +Cannot compress 6 .dynstr +Cannot compress 7 .gnu.version +Cannot compress 8 .gnu.version_r +Cannot compress 9 .rela.dyn +Cannot compress 10 .rela.plt +Cannot compress 11 .init +Cannot compress 12 .text +Cannot compress 13 .fini +Cannot compress 14 .rodata +Cannot compress 15 .eh_frame_hdr +Cannot compress 16 .eh_frame +Cannot compress 17 .init_array +Cannot compress 18 .fini_array +Cannot compress 19 .jcr +Cannot compress 20 .got2 +Cannot compress 21 .dynamic +Cannot compress 22 .got +Cannot compress 23 .plt +Cannot compress 24 .data +Cannot compress 25 .sdata +Cannot compress 26 .bss +Lets compress 27 .comment, size: 88 +Lets compress 28 .debug_aranges, size: 64 +Lets compress 29 .debug_info, size: 319 +Lets compress 30 .debug_abbrev, size: 318 +Lets compress 31 .debug_line, size: 109 +Lets compress 32 .debug_frame, size: 64 +Lets compress 33 .debug_str, size: 179 +Lets compress 34 .debug_loc, size: 99 +Lets compress 35 .debug_ranges, size: 16 +Not compressing section string table 36 +Lets compress 37 .symtab, size: 1232 +Lets compress 38 .strtab, size: 569 +EOF + +testrun_compare ${abs_top_builddir}/tests/elfputzdata elf testfileppc32 <<\EOF +Cannot compress 1 .interp +Cannot compress 2 .note.ABI-tag +Cannot compress 3 .note.gnu.build-id +Cannot compress 4 .gnu.hash +Cannot compress 5 .dynsym +Cannot compress 6 .dynstr +Cannot compress 7 .gnu.version +Cannot compress 8 .gnu.version_r +Cannot compress 9 .rela.dyn +Cannot compress 10 .rela.plt +Cannot compress 11 .init +Cannot compress 12 .text +Cannot compress 13 .fini +Cannot compress 14 .rodata +Cannot compress 15 .eh_frame_hdr +Cannot compress 16 .eh_frame +Cannot compress 17 .init_array +Cannot compress 18 .fini_array +Cannot compress 19 .jcr +Cannot compress 20 .got2 +Cannot compress 21 .dynamic +Cannot compress 22 .got +Cannot compress 23 .plt +Cannot compress 24 .data +Cannot compress 25 .sdata +Cannot compress 26 .bss +Lets compress 27 .comment, size: 88 +Lets compress 28 .debug_aranges, size: 64 +Lets compress 29 .debug_info, size: 319 +Lets compress 30 .debug_abbrev, size: 318 +Lets compress 31 .debug_line, size: 109 +Lets compress 32 .debug_frame, size: 64 +Lets compress 33 .debug_str, size: 179 +Lets compress 34 .debug_loc, size: 99 +Lets compress 35 .debug_ranges, size: 16 +Not compressing section string table 36 +Lets compress 37 .symtab, size: 1232 +Lets compress 38 .strtab, size: 569 +EOF + +exit 0
libdw used its own code for decompressing GNU style .zdebug sections. Using elf_compress and elf_compress_gnu instead makes it possible to transparently use either GNU style or ELF style compressed data.
Signed-off-by: Mark Wielaard mjw@redhat.com --- libdw/ChangeLog | 13 ++++ libdw/dwarf_begin_elf.c | 156 ++++++++++++++++-------------------------------- libdw/dwarf_end.c | 20 ------- libdw/libdwP.h | 11 ---- 4 files changed, 63 insertions(+), 137 deletions(-)
diff --git a/libdw/ChangeLog b/libdw/ChangeLog index dfe8333..8162c36 100644 --- a/libdw/ChangeLog +++ b/libdw/ChangeLog @@ -1,3 +1,16 @@ +2015-12-18 Mark Wielaard mjw@redhat.com + + * libdwP.h (struct Dwarf): Remove sectiondata_gzip_mask. + (__libdw_free_zdata): Remove. + * dwarf_begin_elf.c (inflate_section): Remove. + (check_section): Remove __libdw_free_zdata calls. Use elf_compress + and elf_compress_gnu to decompress if necessary. + (valid_p): Remove __libdw_free_zdata calls. + (scngrp_read): Use elf_compress if section is compressed. Remove + __libdw_free_zdata calls. + * dwarf_end.c (__libdw_free_zdata): Remove. + (dwarf_end): Don't call __libdw_free_zdata. + 2015-10-28 Mark Wielaard mjw@redhat.com
* libdw.map (ELFUTILS_0.165): New. Add dwelf_scn_gnu_compressed_size. diff --git a/libdw/dwarf_begin_elf.c b/libdw/dwarf_begin_elf.c index 63532fd..6f25e42 100644 --- a/libdw/dwarf_begin_elf.c +++ b/libdw/dwarf_begin_elf.c @@ -44,13 +44,6 @@
#include "libdwP.h"
-#if USE_ZLIB -# include <endian.h> -# define crc32 loser_crc32 -# include <zlib.h> -# undef crc32 -#endif -
/* Section names. */ static const char dwarf_scnnames[IDX_last][18] = @@ -71,70 +64,6 @@ static const char dwarf_scnnames[IDX_last][18] = }; #define ndwarf_scnnames (sizeof (dwarf_scnnames) / sizeof (dwarf_scnnames[0]))
-#if USE_ZLIB -static Elf_Data * -inflate_section (Elf_Data * data) -{ - /* There is a 12-byte header of "ZLIB" followed by - an 8-byte big-endian size. */ - - if (unlikely (data->d_size < 4 + 8) - || unlikely (memcmp (data->d_buf, "ZLIB", 4) != 0)) - return NULL; - - uint64_t size; - memcpy (&size, data->d_buf + 4, sizeof size); - size = be64toh (size); - - /* Check for unsigned overflow so malloc always allocated - enough memory for both the Elf_Data header and the - uncompressed section data. */ - if (unlikely (sizeof (Elf_Data) + size < size)) - return NULL; - - Elf_Data *zdata = malloc (sizeof (Elf_Data) + size); - if (unlikely (zdata == NULL)) - return NULL; - - zdata->d_buf = &zdata[1]; - zdata->d_type = ELF_T_BYTE; - zdata->d_version = EV_CURRENT; - zdata->d_size = size; - zdata->d_off = 0; - zdata->d_align = 1; - - z_stream z = - { - .next_in = data->d_buf + 4 + 8, - .avail_in = data->d_size - 4 - 8, - .next_out = zdata->d_buf, - .avail_out = zdata->d_size - }; - int zrc = inflateInit (&z); - while (z.avail_in > 0 && likely (zrc == Z_OK)) - { - z.next_out = zdata->d_buf + (zdata->d_size - 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 (zdata); - return NULL; - } - - return zdata; -} -#endif - static Dwarf * check_section (Dwarf *result, GElf_Ehdr *ehdr, Elf_Scn *scn, bool inscngrp) { @@ -173,7 +102,6 @@ check_section (Dwarf *result, GElf_Ehdr *ehdr, Elf_Scn *scn, bool inscngrp) /* The section name must be valid. Otherwise is the ELF file invalid. */ err: - __libdw_free_zdata (result); Dwarf_Sig8_Hash_free (&result->sig8_hash); __libdw_seterrno (DWARF_E_INVALID_ELF); free (result); @@ -182,14 +110,14 @@ check_section (Dwarf *result, GElf_Ehdr *ehdr, Elf_Scn *scn, bool inscngrp)
/* Recognize the various sections. Most names start with .debug_. */ size_t cnt; - bool compressed = false; + bool gnu_compressed = false; for (cnt = 0; cnt < ndwarf_scnnames; ++cnt) if (strcmp (scnname, dwarf_scnnames[cnt]) == 0) break; else if (scnname[0] == '.' && scnname[1] == 'z' && strcmp (&scnname[2], &dwarf_scnnames[cnt][1]) == 0) { - compressed = true; + gnu_compressed = true; break; }
@@ -201,40 +129,41 @@ check_section (Dwarf *result, GElf_Ehdr *ehdr, Elf_Scn *scn, bool inscngrp) /* A section appears twice. That's bad. We ignore the section. */ return result;
+ /* We cannot know whether or not a GNU compressed section has already + been uncompressed or not, so ignore any errors. */ + if (gnu_compressed) + elf_compress_gnu (scn, 0, 0); + + if ((shdr->sh_flags & SHF_COMPRESSED) != 0) + { + if (elf_compress (scn, 0, 0) < 0) + { + /* If we failed to decompress the section and it's the + debug_info section, then fail with specific error rather + than the generic NO_DWARF. Without debug_info we can't do + anything (see also valid_p()). */ + if (cnt == IDX_debug_info) + { + Dwarf_Sig8_Hash_free (&result->sig8_hash); + __libdw_seterrno (DWARF_E_COMPRESSED_ERROR); + free (result); + return NULL; + } + return result; + } + } + /* Get the section data. */ Elf_Data *data = elf_getdata (scn, NULL); - if (data == NULL || data->d_size == 0) + if (data == NULL) + goto err; + + if (data->d_buf == NULL || data->d_size == 0) /* No data actually available, ignore it. */ return result;
/* We can now read the section data into results. */ - if (!compressed) - result->sectiondata[cnt] = data; - else - { - /* A compressed section. */ - -#if USE_ZLIB - Elf_Data *inflated = inflate_section(data); - if (inflated != NULL) - { - result->sectiondata[cnt] = inflated; - result->sectiondata_gzip_mask |= 1U << cnt; - } -#endif - - /* If we failed to decompress the section and it's the debug_info section, - * then fail with specific error rather than the generic NO_DWARF. Without - * debug_info we can't do anything (see also valid_p()). */ - if (result->sectiondata[cnt] == NULL && cnt == IDX_debug_info) - { - __libdw_free_zdata (result); - Dwarf_Sig8_Hash_free (&result->sig8_hash); - __libdw_seterrno (DWARF_E_COMPRESSED_ERROR); - free (result); - return NULL; - } - } + result->sectiondata[cnt] = data;
return result; } @@ -253,7 +182,6 @@ valid_p (Dwarf *result) if (likely (result != NULL) && unlikely (result->sectiondata[IDX_debug_info] == NULL)) { - __libdw_free_zdata (result); Dwarf_Sig8_Hash_free (&result->sig8_hash); __libdw_seterrno (DWARF_E_NO_DWARF); free (result); @@ -265,7 +193,6 @@ valid_p (Dwarf *result) result->fake_loc_cu = (Dwarf_CU *) calloc (1, sizeof (Dwarf_CU)); if (unlikely (result->fake_loc_cu == NULL)) { - __libdw_free_zdata (result); Dwarf_Sig8_Hash_free (&result->sig8_hash); __libdw_seterrno (DWARF_E_NOMEM); free (result); @@ -301,13 +228,31 @@ global_read (Dwarf *result, Elf *elf, GElf_Ehdr *ehdr) static Dwarf * scngrp_read (Dwarf *result, Elf *elf, GElf_Ehdr *ehdr, Elf_Scn *scngrp) { + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (scngrp, &shdr_mem); + if (shdr == NULL) + { + Dwarf_Sig8_Hash_free (&result->sig8_hash); + __libdw_seterrno (DWARF_E_INVALID_ELF); + free (result); + return NULL; + } + + if ((shdr->sh_flags & SHF_COMPRESSED) != 0 + && elf_compress (scngrp, 0, 0) < 0) + { + Dwarf_Sig8_Hash_free (&result->sig8_hash); + __libdw_seterrno (DWARF_E_COMPRESSED_ERROR); + free (result); + return NULL; + } + /* SCNGRP is the section descriptor for a section group which might contain debug sections. */ Elf_Data *data = elf_getdata (scngrp, NULL); if (data == NULL) { /* We cannot read the section content. Fail! */ - __libdw_free_zdata (result); Dwarf_Sig8_Hash_free (&result->sig8_hash); free (result); return NULL; @@ -324,7 +269,6 @@ scngrp_read (Dwarf *result, Elf *elf, GElf_Ehdr *ehdr, Elf_Scn *scngrp) { /* A section group refers to a non-existing section. Should never happen. */ - __libdw_free_zdata (result); Dwarf_Sig8_Hash_free (&result->sig8_hash); __libdw_seterrno (DWARF_E_INVALID_ELF); free (result); diff --git a/libdw/dwarf_end.c b/libdw/dwarf_end.c index 2108063..6c6d985 100644 --- a/libdw/dwarf_end.c +++ b/libdw/dwarf_end.c @@ -57,24 +57,6 @@ cu_free (void *arg) }
-#if USE_ZLIB -void -internal_function -__libdw_free_zdata (Dwarf *dwarf) -{ - unsigned int gzip_mask = dwarf->sectiondata_gzip_mask; - while (gzip_mask != 0) - { - int i = ffs (gzip_mask); - assert (i > 0); - --i; - assert (i < IDX_last); - free (dwarf->sectiondata[i]); - gzip_mask &= ~(1U << i); - } -} -#endif - int dwarf_end (Dwarf *dwarf) { @@ -110,8 +92,6 @@ dwarf_end (Dwarf *dwarf) /* Free the pubnames helper structure. */ free (dwarf->pubnames_sets);
- __libdw_free_zdata (dwarf); - /* Free the ELF descriptor if necessary. */ if (dwarf->free_elf) elf_end (dwarf->elf); diff --git a/libdw/libdwP.h b/libdw/libdwP.h index 2d233f6..5d095a7 100644 --- a/libdw/libdwP.h +++ b/libdw/libdwP.h @@ -147,11 +147,6 @@ struct Dwarf /* The section data. */ Elf_Data *sectiondata[IDX_last];
-#if USE_ZLIB - /* The 1 << N bit is set if sectiondata[N] is malloc'd decompressed data. */ - unsigned int sectiondata_gzip_mask:IDX_last; -#endif - /* True if the file has a byte order different from the host. */ bool other_byte_order;
@@ -440,12 +435,6 @@ extern void *__libdw_allocate (Dwarf *dbg, size_t minsize, size_t align) /* Default OOM handler. */ extern void __libdw_oom (void) __attribute ((noreturn, visibility ("hidden")));
-#if USE_ZLIB -extern void __libdw_free_zdata (Dwarf *dwarf) internal_function; -#else -# define __libdw_free_zdata(dwarf) ((void) (dwarf)) -#endif - /* Allocate the internal data for a unit not seen before. */ extern struct Dwarf_CU *__libdw_intern_next_unit (Dwarf *dbg, bool debug_types) __nonnull_attribute__ (1) internal_function;
This makes usage of the libdwfl symbol functions work out of the box even when some sections (string, symbol or xndx) are compressed. For ET_REL files this makes relocations just work by making sure the target section is decompressed first before relocations are applied.
Signed-off-by: Mark Wielaard mjw@redhat.com --- libdwfl/ChangeLog | 10 ++++ libdwfl/dwfl_module_getdwarf.c | 99 ++++++++++++++++++++++++++++++++--- libdwfl/relocate.c | 116 ++++++++++++++++++++++++++++++++--------- libebl/ChangeLog | 4 ++ libebl/eblopenbackend.c | 4 +- 5 files changed, 201 insertions(+), 32 deletions(-)
diff --git a/libdwfl/ChangeLog b/libdwfl/ChangeLog index 30a5205..b653564 100644 --- a/libdwfl/ChangeLog +++ b/libdwfl/ChangeLog @@ -1,3 +1,13 @@ +2015-12-18 Mark Wielaard mjw@redhat.com + + * dwfl_module_getdwarf.c (find_symtab): Uncompress symstr, xndx, sym + sections and aux_str, aux_xndx and aux_sym sections if necessary. + * relocate.c (relocate_getsym): Uncompress symtab and symtab_shndx + if necessary. + (resolve_symbol): Uncompress strtab section if necessary. + (relocate_section): Uncompress the section the relocations apply to + if necessary. + 2015-12-01 Mark Wielaard mjw@redhat.com
* link_map.c (dwfl_link_map_report): Track whether in.d_buf comes diff --git a/libdwfl/dwfl_module_getdwarf.c b/libdwfl/dwfl_module_getdwarf.c index 8483fa2..f86baf7 100644 --- a/libdwfl/dwfl_module_getdwarf.c +++ b/libdwfl/dwfl_module_getdwarf.c @@ -1120,10 +1120,39 @@ find_symtab (Dwfl_Module *mod) goto aux_cleanup; /* This cleans up some more and tries find_dynsym. */ }
- /* Cache the data; MOD->syments and MOD->first_global were set above. */ + /* Cache the data; MOD->syments and MOD->first_global were set + above. If any of the sections is compressed, uncompress it + first. Only the string data setion could theoretically be + compressed GNU style (as .zdebug_str). Everything else only ELF + gabi style (SHF_COMPRESSED). */ + + Elf_Scn *symstrscn = elf_getscn (mod->symfile->elf, strshndx); + if (symstrscn == NULL) + goto elferr; + + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (symstrscn, &shdr_mem); + if (shdr == NULL) + goto elferr; + + size_t shstrndx; + if (elf_getshdrstrndx (mod->symfile->elf, &shstrndx) < 0) + goto elferr; + + const char *sname = elf_strptr (mod->symfile->elf, shstrndx, shdr->sh_name); + if (sname == NULL) + goto elferr; + + if (strncmp (sname, ".zdebug", strlen (".zdebug")) == 0) + /* Try to uncompress, but it might already have been, an error + might just indicate, already uncompressed. */ + elf_compress_gnu (symstrscn, 0, 0);
- mod->symstrdata = elf_getdata (elf_getscn (mod->symfile->elf, strshndx), - NULL); + if ((shdr->sh_flags & SHF_COMPRESSED) != 0) + if (elf_compress (symstrscn, 0, 0) < 0) + goto elferr; + + mod->symstrdata = elf_getdata (symstrscn, NULL); if (mod->symstrdata == NULL || mod->symstrdata->d_buf == NULL) goto elferr;
@@ -1131,17 +1160,33 @@ find_symtab (Dwfl_Module *mod) mod->symxndxdata = NULL; else { + shdr = gelf_getshdr (xndxscn, &shdr_mem); + if (shdr == NULL) + goto elferr; + + if ((shdr->sh_flags & SHF_COMPRESSED) != 0) + if (elf_compress (xndxscn, 0, 0) < 0) + goto elferr; + mod->symxndxdata = elf_getdata (xndxscn, NULL); if (mod->symxndxdata == NULL || mod->symxndxdata->d_buf == NULL) goto elferr; }
+ shdr = gelf_getshdr (symscn, &shdr_mem); + if (shdr == NULL) + goto elferr; + + if ((shdr->sh_flags & SHF_COMPRESSED) != 0) + if (elf_compress (symscn, 0, 0) < 0) + goto elferr; + mod->symdata = elf_getdata (symscn, NULL); if (mod->symdata == NULL || mod->symdata->d_buf == NULL) goto elferr;
// Sanity check number of symbols. - GElf_Shdr shdr_mem, *shdr = gelf_getshdr (symscn, &shdr_mem); + shdr = gelf_getshdr (symscn, &shdr_mem); if (shdr == NULL || shdr->sh_entsize == 0 || mod->syments > mod->symdata->d_size / shdr->sh_entsize || (size_t) mod->first_global > mod->syments) @@ -1164,9 +1209,33 @@ find_symtab (Dwfl_Module *mod) return; }
- mod->aux_symstrdata = elf_getdata (elf_getscn (mod->aux_sym.elf, - aux_strshndx), - NULL); + Elf_Scn *aux_strscn = elf_getscn (mod->aux_sym.elf, aux_strshndx); + if (aux_strscn == NULL) + goto elferr; + + shdr = gelf_getshdr (aux_strscn, &shdr_mem); + if (shdr == NULL) + goto elferr; + + size_t aux_shstrndx; + if (elf_getshdrstrndx (mod->aux_sym.elf, &aux_shstrndx) < 0) + goto elferr; + + sname = elf_strptr (mod->aux_sym.elf, aux_shstrndx, + shdr->sh_name); + if (sname == NULL) + goto elferr; + + if (strncmp (sname, ".zdebug", strlen (".zdebug")) == 0) + /* Try to uncompress, but it might already have been, an error + might just indicate, already uncompressed. */ + elf_compress_gnu (aux_strscn, 0, 0); + + if ((shdr->sh_flags & SHF_COMPRESSED) != 0) + if (elf_compress (aux_strscn, 0, 0) < 0) + goto elferr; + + mod->aux_symstrdata = elf_getdata (aux_strscn, NULL); if (mod->aux_symstrdata == NULL || mod->aux_symstrdata->d_buf == NULL) goto aux_cleanup;
@@ -1174,12 +1243,28 @@ find_symtab (Dwfl_Module *mod) mod->aux_symxndxdata = NULL; else { + shdr = gelf_getshdr (aux_xndxscn, &shdr_mem); + if (shdr == NULL) + goto elferr; + + if ((shdr->sh_flags & SHF_COMPRESSED) != 0) + if (elf_compress (aux_xndxscn, 0, 0) < 0) + goto elferr; + mod->aux_symxndxdata = elf_getdata (aux_xndxscn, NULL); if (mod->aux_symxndxdata == NULL || mod->aux_symxndxdata->d_buf == NULL) goto aux_cleanup; }
+ shdr = gelf_getshdr (aux_symscn, &shdr_mem); + if (shdr == NULL) + goto elferr; + + if ((shdr->sh_flags & SHF_COMPRESSED) != 0) + if (elf_compress (aux_symscn, 0, 0) < 0) + goto elferr; + mod->aux_symdata = elf_getdata (aux_symscn, NULL); if (mod->aux_symdata == NULL || mod->aux_symdata->d_buf == NULL) goto aux_cleanup; diff --git a/libdwfl/relocate.c b/libdwfl/relocate.c index 2dc6737..fc88df3 100644 --- a/libdwfl/relocate.c +++ b/libdwfl/relocate.c @@ -123,23 +123,32 @@ relocate_getsym (Dwfl_Module *mod, { GElf_Shdr shdr_mem, *shdr = gelf_getshdr (scn, &shdr_mem); if (shdr != NULL) - switch (shdr->sh_type) - { - default: - continue; - case SHT_SYMTAB: - cache->symelf = relocated; - cache->symdata = elf_getdata (scn, NULL); - cache->strtabndx = shdr->sh_link; - if (unlikely (cache->symdata == NULL)) - return DWFL_E_LIBELF; - break; - case SHT_SYMTAB_SHNDX: - cache->symxndxdata = elf_getdata (scn, NULL); - if (unlikely (cache->symxndxdata == NULL)) + { + /* We need uncompressed data. */ + if ((shdr->sh_type == SHT_SYMTAB + || shdr->sh_type == SHT_SYMTAB_SHNDX) + && (shdr->sh_flags & SHF_COMPRESSED) != 0) + if (elf_compress (scn, 0, 0) < 0) return DWFL_E_LIBELF; - break; - } + + switch (shdr->sh_type) + { + default: + continue; + case SHT_SYMTAB: + cache->symelf = relocated; + cache->symdata = elf_getdata (scn, NULL); + cache->strtabndx = shdr->sh_link; + if (unlikely (cache->symdata == NULL)) + return DWFL_E_LIBELF; + break; + case SHT_SYMTAB_SHNDX: + cache->symxndxdata = elf_getdata (scn, NULL); + if (unlikely (cache->symxndxdata == NULL)) + return DWFL_E_LIBELF; + break; + } + } if (cache->symdata != NULL && cache->symxndxdata != NULL) break; } @@ -203,9 +212,34 @@ resolve_symbol (Dwfl_Module *referer, struct reloc_symtab_cache *symtab, /* Cache the strtab for this symtab. */ assert (referer->symfile == NULL || referer->symfile->elf != symtab->symelf); - symtab->symstrdata = elf_getdata (elf_getscn (symtab->symelf, - symtab->strtabndx), - NULL); + + Elf_Scn *scn = elf_getscn (symtab->symelf, symtab->strtabndx); + if (scn == NULL) + return DWFL_E_LIBELF; + + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + if (shdr == NULL) + return DWFL_E_LIBELF; + + if (symtab->symshstrndx == SHN_UNDEF + && elf_getshdrstrndx (symtab->symelf, &symtab->symshstrndx) < 0) + return DWFL_E_LIBELF; + + const char *sname = elf_strptr (symtab->symelf, symtab->symshstrndx, + shdr->sh_name); + if (sname == NULL) + return DWFL_E_LIBELF; + + /* If the section is already decompressed, that isn't an error. */ + if (strncmp (sname, ".zdebug", strlen (".zdebug")) == 0) + elf_compress_gnu (scn, 0, 0); + + if ((shdr->sh_flags & SHF_COMPRESSED) != 0) + if (elf_compress (scn, 0, 0) < 0) + return DWFL_E_LIBELF; + + symtab->symstrdata = elf_getdata (scn, NULL); if (unlikely (symtab->symstrdata == NULL || symtab->symstrdata->d_buf == NULL)) return DWFL_E_LIBELF; @@ -446,22 +480,56 @@ relocate_section (Dwfl_Module *mod, Elf *relocated, const GElf_Ehdr *ehdr, Elf_Scn *scn, GElf_Shdr *shdr, Elf_Scn *tscn, bool debugscn, bool partial) { - /* First, fetch the name of the section these relocations apply to. */ + /* First, fetch the name of the section these relocations apply to. + Then try to decompress both relocation and target section. */ GElf_Shdr tshdr_mem; GElf_Shdr *tshdr = gelf_getshdr (tscn, &tshdr_mem); + if (tshdr == NULL) + return DWFL_E_LIBELF; + const char *tname = elf_strptr (relocated, shstrndx, tshdr->sh_name); if (tname == NULL) return DWFL_E_LIBELF;
- if (unlikely (tshdr->sh_type == SHT_NOBITS) || unlikely (tshdr->sh_size == 0)) - /* No contents to relocate. */ - return DWFL_E_NOERROR; - if (debugscn && ! ebl_debugscn_p (mod->ebl, tname)) /* This relocation section is not for a debugging section. Nothing to do here. */ return DWFL_E_NOERROR;
+ if (strncmp (tname, ".zdebug", strlen ("zdebug")) == 0) + elf_compress_gnu (tscn, 0, 0); + + if ((tshdr->sh_flags & SHF_COMPRESSED) != 0) + if (elf_compress (tscn, 0, 0) < 0) + return DWFL_E_LIBELF; + + /* Reload Shdr in case section was just decompressed. */ + tshdr = gelf_getshdr (tscn, &tshdr_mem); + if (tshdr == NULL) + return DWFL_E_LIBELF; + + if (unlikely (tshdr->sh_type == SHT_NOBITS) + || unlikely (tshdr->sh_size == 0)) + /* No contents to relocate. */ + return DWFL_E_NOERROR; + + const char *sname = elf_strptr (relocated, shstrndx, shdr->sh_name); + if (sname == NULL) + return DWFL_E_LIBELF; + + if (strncmp (sname, ".zdebug", strlen ("zdebug")) == 0) + elf_compress_gnu (scn, 0, 0); + + if ((shdr->sh_flags & SHF_COMPRESSED) != 0) + if (elf_compress (scn, 0, 0) < 0) + return DWFL_E_LIBELF; + + /* Reload Shdr in case section was just decompressed. */ + GElf_Shdr shdr_mem; + shdr = gelf_getshdr (scn, &shdr_mem); + if (shdr == NULL) + return DWFL_E_LIBELF; + /* Fetch the section data that needs the relocations applied. */ Elf_Data *tdata = elf_rawdata (tscn, NULL); if (tdata == NULL) diff --git a/libebl/ChangeLog b/libebl/ChangeLog index d040c08..4d365c5 100644 --- a/libebl/ChangeLog +++ b/libebl/ChangeLog @@ -1,3 +1,7 @@ +2015-12-18 Mark Wielaard mjw@redhat.com + + * eblopenbackend.c (default_debugscn_p): Also match .zdebug sections. + 2015-09-24 Jose E. Marchesi jose.marchesi@oracle.com
* Makefile.am (AM_CFLAGS): Use -fPIC instead of -fpic to avoid diff --git a/libebl/eblopenbackend.c b/libebl/eblopenbackend.c index b301400..372ef2a 100644 --- a/libebl/eblopenbackend.c +++ b/libebl/eblopenbackend.c @@ -662,7 +662,9 @@ default_debugscn_p (const char *name) const size_t ndwarf_scn_names = (sizeof (dwarf_scn_names) / sizeof (dwarf_scn_names[0])); for (size_t cnt = 0; cnt < ndwarf_scn_names; ++cnt) - if (strcmp (name, dwarf_scn_names[cnt]) == 0) + if (strcmp (name, dwarf_scn_names[cnt]) == 0 + || (strncmp (name, ".zdebug", strlen (".zdebug")) == 0 + && strcmp (&name[2], &dwarf_scn_names[cnt][1]) == 0)) return true;
return false;
When printing a (non-string, non-data) section use uncompressed data when possible. For dumping hex and string sections (-x and -p) -z will dump the uncompressed data (otherwise the compressed data is dumped).
-z, --decompress Show compression information for compressed sections (when used with -S); decompress section before dumping data (when used with -p or -x)
Includes test cases for ET_REL files using compressed relocation (target) debug sections to test libdwfl transparent uncompression of sections.
Signed-off-by: Mark Wielaard mjw@redhat.com --- src/ChangeLog | 13 ++ src/readelf.c | 124 +++++++++++++++--- tests/ChangeLog | 7 + tests/Makefile.am | 7 +- tests/run-readelf-zdebug-rel.sh | 144 +++++++++++++++++++++ tests/run-readelf-zp.sh | 271 +++++++++++++++++++++++++++++++++++++++ tests/run-readelf-zx.sh | 66 ++++++++++ tests/testfile-debug-rel-g.o.bz2 | Bin 0 -> 1238 bytes tests/testfile-debug-rel-z.o.bz2 | Bin 0 -> 1225 bytes tests/testfile-debug-rel.o.bz2 | Bin 0 -> 1069 bytes 10 files changed, 609 insertions(+), 23 deletions(-) create mode 100755 tests/run-readelf-zdebug-rel.sh create mode 100755 tests/run-readelf-zp.sh create mode 100755 tests/run-readelf-zx.sh create mode 100644 tests/testfile-debug-rel-g.o.bz2 create mode 100644 tests/testfile-debug-rel-z.o.bz2 create mode 100644 tests/testfile-debug-rel.o.bz2
diff --git a/src/ChangeLog b/src/ChangeLog index 9724221..1b5be54 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,16 @@ +2015-10-20 Mark Wielaard mjw@redhat.com + + * readelf.c (options): Expand -z help text. + (dump_data_section): Check whether we need and can decompress section + data and call elf_rawzdata if so, + (print_string_section): Likewise. + (elf_contains_chdrs): New function. + (process_elf_file): Rename print_unrelocated to print_unchanged, + use elf_contains_chdrs. + (print_scngrp): Check whether section is compressed before use. + (print_symtab): Likewise. + (handle_hash): Likewise. + 2015-10-16 Mark Wielaard mjw@redhat.com
* readelf.c (argp_option): Describe --decompress,-z. diff --git a/src/readelf.c b/src/readelf.c index 1d507cf..85fa92b 100644 --- a/src/readelf.c +++ b/src/readelf.c @@ -114,7 +114,7 @@ static const struct argp_option options[] = { "wide", 'W', NULL, 0, N_("Ignored for compatibility (lines always wide)"), 0 }, { "decompress", 'z', NULL, 0, - N_("Show compression information for compressed sections (when used with -S)."), 0 }, + N_("Show compression information for compressed sections (when used with -S); decompress section before dumping data (when used with -p or -x)"), 0 }, { NULL, 0, NULL, 0, NULL, 0 } };
@@ -806,6 +806,20 @@ process_file (int fd, const char *fname, bool only_one) close (fd); }
+/* Check whether there are any compressed sections in the ELF file. */ +static bool +elf_contains_chdrs (Elf *elf) +{ + Elf_Scn *scn = NULL; + while ((scn = elf_nextscn (elf, scn)) != NULL) + { + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + if (shdr != NULL && (shdr->sh_flags & SHF_COMPRESSED) != 0) + return true; + } + return false; +}
/* Process one ELF file. */ static void @@ -844,17 +858,21 @@ process_elf_file (Dwfl_Module *dwflmod, int fd) gettext ("cannot determine number of program headers: %s"), elf_errmsg (-1));
- /* For an ET_REL file, libdwfl has adjusted the in-core shdrs - and may have applied relocation to some sections. - So we need to get a fresh Elf handle on the file to display those. */ - bool print_unrelocated = (print_section_header - || print_relocations - || dump_data_sections != NULL - || print_notes); + /* For an ET_REL file, libdwfl has adjusted the in-core shdrs and + may have applied relocation to some sections. If there are any + compressed sections, any pass (or libdw/libdwfl) might have + uncompressed them. So we need to get a fresh Elf handle on the + file to display those. */ + bool print_unchanged = ((print_section_header + || print_relocations + || dump_data_sections != NULL + || print_notes) + && (ehdr->e_type == ET_REL + || elf_contains_chdrs (ebl->elf)));
Elf *pure_elf = NULL; Ebl *pure_ebl = ebl; - if (ehdr->e_type == ET_REL && print_unrelocated) + if (print_unchanged) { /* Read the file afresh. */ off_t aroff = elf_getaroff (elf); @@ -1503,7 +1521,17 @@ print_scngrp (Ebl *ebl) GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
if (shdr != NULL && shdr->sh_type == SHT_GROUP) - handle_scngrp (ebl, scn, shdr); + { + if ((shdr->sh_flags & SHF_COMPRESSED) != 0) + { + if (elf_compress (scn, 0, 0) < 0) + printf ("WARNING: %s [%zd]\n", + gettext ("Couldn't uncompress section"), + elf_ndxscn (scn)); + shdr = gelf_getshdr (scn, &shdr_mem); + } + handle_scngrp (ebl, scn, shdr); + } } }
@@ -2202,7 +2230,17 @@ print_symtab (Ebl *ebl, int type) GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
if (shdr != NULL && shdr->sh_type == (GElf_Word) type) - handle_symtab (ebl, scn, shdr); + { + if ((shdr->sh_flags & SHF_COMPRESSED) != 0) + { + if (elf_compress (scn, 0, 0) < 0) + printf ("WARNING: %s [%zd]\n", + gettext ("Couldn't uncompress section"), + elf_ndxscn (scn)); + shdr = gelf_getshdr (scn, &shdr_mem); + } + handle_symtab (ebl, scn, shdr); + } } }
@@ -3271,6 +3309,16 @@ handle_hash (Ebl *ebl)
if (likely (shdr != NULL)) { + if ((shdr->sh_type == SHT_HASH || shdr->sh_type == SHT_GNU_HASH) + && (shdr->sh_flags & SHF_COMPRESSED) != 0) + { + if (elf_compress (scn, 0, 0) < 0) + printf ("WARNING: %s [%zd]\n", + gettext ("Couldn't uncompress section"), + elf_ndxscn (scn)); + shdr = gelf_getshdr (scn, &shdr_mem); + } + if (shdr->sh_type == SHT_HASH) { if (ebl_sysvhash_entrysize (ebl) == sizeof (Elf64_Xword)) @@ -8320,11 +8368,9 @@ print_debug (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr) int n; for (n = 0; n < ndebug_sections; ++n) if (strcmp (name, debug_sections[n].name) == 0 -#if USE_ZLIB || (name[0] == '.' && name[1] == 'z' && debug_sections[n].name[1] == 'd' && strcmp (&name[2], &debug_sections[n].name[1]) == 0) -#endif ) { if ((print_debug_sections | implicit_debug_sections) @@ -9456,16 +9502,34 @@ dump_data_section (Elf_Scn *scn, const GElf_Shdr *shdr, const char *name) elf_ndxscn (scn), name); else { + if (print_decompress) + { + /* We try to decompress the section, but keep the old shdr around + so we can show both the original shdr size and the uncompressed + data size. */ + if ((shdr->sh_flags & SHF_COMPRESSED) != 0) + elf_compress (scn, 0, 0); + else if (strncmp (name, ".zdebug", strlen (".zdebug")) == 0) + elf_compress_gnu (scn, 0, 0); + } + Elf_Data *data = elf_rawdata (scn, NULL); if (data == NULL) error (0, 0, gettext ("cannot get data for section [%zu] '%s': %s"), elf_ndxscn (scn), name, elf_errmsg (-1)); else { - printf (gettext ("\nHex dump of section [%zu] '%s', %" PRIu64 - " bytes at offset %#0" PRIx64 ":\n"), - elf_ndxscn (scn), name, - shdr->sh_size, shdr->sh_offset); + if (data->d_size == shdr->sh_size) + printf (gettext ("\nHex dump of section [%zu] '%s', %" PRIu64 + " bytes at offset %#0" PRIx64 ":\n"), + elf_ndxscn (scn), name, + shdr->sh_size, shdr->sh_offset); + else + printf (gettext ("\nHex dump of section [%zu] '%s', %" PRIu64 + " bytes (%zd uncompressed) at offset %#0" + PRIx64 ":\n"), + elf_ndxscn (scn), name, + shdr->sh_size, data->d_size, shdr->sh_offset); hex_dump (data->d_buf, data->d_size); } } @@ -9479,16 +9543,34 @@ print_string_section (Elf_Scn *scn, const GElf_Shdr *shdr, const char *name) elf_ndxscn (scn), name); else { + if (print_decompress) + { + /* We try to decompress the section, but keep the old shdr around + so we can show both the original shdr size and the uncompressed + data size. */ + if ((shdr->sh_flags & SHF_COMPRESSED) != 0) + elf_compress (scn, 0, 0); + else if (strncmp (name, ".zdebug", strlen (".zdebug")) == 0) + elf_compress_gnu (scn, 0, 0); + } + Elf_Data *data = elf_rawdata (scn, NULL); if (data == NULL) error (0, 0, gettext ("cannot get data for section [%zu] '%s': %s"), elf_ndxscn (scn), name, elf_errmsg (-1)); else { - printf (gettext ("\nString section [%zu] '%s' contains %" PRIu64 - " bytes at offset %#0" PRIx64 ":\n"), - elf_ndxscn (scn), name, - shdr->sh_size, shdr->sh_offset); + if (data->d_size == shdr->sh_size) + printf (gettext ("\nString section [%zu] '%s' contains %" PRIu64 + " bytes at offset %#0" PRIx64 ":\n"), + elf_ndxscn (scn), name, + shdr->sh_size, shdr->sh_offset); + else + printf (gettext ("\nString section [%zu] '%s' contains %" PRIu64 + " bytes (%zd uncompressed) at offset %#0" + PRIx64 ":\n"), + elf_ndxscn (scn), name, + shdr->sh_size, data->d_size, shdr->sh_offset);
const char *start = data->d_buf; const char *const limit = start + data->d_size; diff --git a/tests/ChangeLog b/tests/ChangeLog index 8ddf32b..6e9abbe 100644 --- a/tests/ChangeLog +++ b/tests/ChangeLog @@ -1,3 +1,10 @@ +2015-10-20 Mark Wielaard mjw@redhat.com + + * run-readelf-zx.sh: New test. + * run-readelf-zp.sh: Likewise. + * Makefile.am (TESTS): Add run-readelf-zx.sh and run-readelf-zp.sh. + (EXTRA_DIST): Likewise. + 2015-10-21 Mark Wielaard mjw@redhat.com
* Makefile.am (check_PROGRAMS): Add elfgetzdata and elfputzdata. diff --git a/tests/Makefile.am b/tests/Makefile.am index 131ed46..7e243e9 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -117,7 +117,7 @@ TESTS = run-arextract.sh run-arsymtest.sh newfile test-nlist \ run-backtrace-core-s390x.sh run-backtrace-core-s390.sh \ run-backtrace-core-aarch64.sh \ run-backtrace-demangle.sh run-stack-d-test.sh run-stack-i-test.sh \ - run-stack-demangled-test.sh \ + run-stack-demangled-test.sh run-readelf-zx.sh run-readelf-zp.sh \ 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 \ @@ -143,7 +143,7 @@ TESTS += run-readelf-s.sh run-dwflsyms.sh endif
if ZLIB -TESTS += run-readelf-zdebug.sh +TESTS += run-readelf-zdebug.sh run-readelf-zdebug-rel.sh endif
if HAVE_LIBASM @@ -298,6 +298,9 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh \ run-stack-demangled-test.sh \ testfiledwarfinlines.bz2 testfiledwarfinlines.core.bz2 \ run-readelf-zdebug.sh testfile-debug.bz2 testfile-zdebug.bz2 \ + run-readelf-zdebug-rel.sh testfile-debug-rel.o.bz2 \ + testfile-debug-rel-g.o.bz2 testfile-debug-rel-z.o.bz2 \ + run-readelf-zx.sh run-readelf-zp.sh \ run-deleted.sh run-linkmap-cut.sh linkmap-cut-lib.so.bz2 \ linkmap-cut.bz2 linkmap-cut.core.bz2 \ run-aggregate-size.sh testfile-sizes1.o.bz2 testfile-sizes2.o.bz2 \ diff --git a/tests/run-readelf-zdebug-rel.sh b/tests/run-readelf-zdebug-rel.sh new file mode 100755 index 0000000..88ea5bf --- /dev/null +++ b/tests/run-readelf-zdebug-rel.sh @@ -0,0 +1,144 @@ +#! /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 + +# - testfile-zdebug-rel.c +# #define UINT64_MAX 18446744073709551615UL +# +# int +# main (int argc, char **argv) +# { +# unsigned long a = UINT64_MAX - 8; +# unsigned long b = 42 + argc; +# +# if ( a + b < b ) +# argc = a + argc; +# else +# b--; +# +# return a - b; +# } +# +# gcc -Og -g -Xassembler --compress-debug-sections=none \ +# -c -o testfile-debug-rel.o testfile-zdebug-rel.c +# gcc -Og -g -Xassembler --compress-debug-sections=zlib-gnu \ +# -c -o testfile-debug-rel-g.o testfile-zdebug-rel.c +# gcc -Og -g -Xassembler --compress-debug-sections=zlib-gabi \ +# -c -o testfile-debug-rel-z.o testfile-zdebug-rel.c + +testfiles testfile-debug-rel.o testfile-debug-rel-g.o testfile-debug-rel-z.o +tempfiles readelf.out +tempfiles info.out loc.out + +cat > info.out << \EOF + +DWARF section [ 4] '.debug_info' at offset 0x58: + [Offset] + Compilation unit at offset 0: + Version: 4, Abbreviation section offset: 0, Address size: 8, Offset size: 4 + [ b] compile_unit + producer (strp) "GNU C11 5.3.1 20151207 (Red Hat 5.3.1-2) -mtune=generic -march=x86-64 -g -Og" + language (data1) C99 (12) + name (strp) "testfile-zdebug-rel.c" + comp_dir (strp) "/tmp" + low_pc (addr) 000000000000000000 + high_pc (data8) 24 (0x0000000000000018) + stmt_list (sec_offset) 0 + [ 2d] subprogram + external (flag_present) + name (strp) "main" + decl_file (data1) 1 + decl_line (data1) 4 + prototyped (flag_present) + type (ref4) [ 80] + low_pc (addr) 000000000000000000 + high_pc (data8) 24 (0x0000000000000018) + frame_base (exprloc) + [ 0] call_frame_cfa + GNU_all_call_sites (flag_present) + sibling (ref4) [ 80] + [ 4e] formal_parameter + name (strp) "argc" + decl_file (data1) 1 + decl_line (data1) 4 + type (ref4) [ 80] + location (sec_offset) location list [ 0] + [ 5d] formal_parameter + name (strp) "argv" + decl_file (data1) 1 + decl_line (data1) 4 + type (ref4) [ 87] + location (exprloc) + [ 0] reg4 + [ 6a] variable + name (string) "a" + decl_file (data1) 1 + decl_line (data1) 6 + type (ref4) [ 9a] + const_value (sdata) -9 + [ 74] variable + name (string) "b" + decl_file (data1) 1 + decl_line (data1) 7 + type (ref4) [ 9a] + location (exprloc) + [ 0] reg5 + [ 80] base_type + byte_size (data1) 4 + encoding (data1) signed (5) + name (string) "int" + [ 87] pointer_type + byte_size (data1) 8 + type (ref4) [ 8d] + [ 8d] pointer_type + byte_size (data1) 8 + type (ref4) [ 93] + [ 93] base_type + byte_size (data1) 1 + encoding (data1) signed_char (6) + name (strp) "char" + [ 9a] base_type + byte_size (data1) 8 + encoding (data1) unsigned (7) + name (strp) "long unsigned int" +EOF + +cat info.out | testrun_compare ${abs_top_builddir}/src/readelf -U --debug-dump=info testfile-debug-rel.o + +cat info.out | sed -e "s/'.debug_info'/'.zdebug_info'/" | testrun_compare ${abs_top_builddir}/src/readelf -U --debug-dump=info testfile-debug-rel-g.o + +cat info.out | testrun_compare ${abs_top_builddir}/src/readelf -U --debug-dump=info testfile-debug-rel-z.o + +cat > loc.out << \EOF + +DWARF section [ 7] '.debug_loc' at offset 0x185: + [ 0] 000000000000000000..0x0000000000000003 [ 0] reg5 + 0x0000000000000003..0x0000000000000010 [ 0] breg5 -42 + [ 2] stack_value + 0x0000000000000010..0x0000000000000018 [ 0] GNU_entry_value: + [ 0] reg5 + [ 3] stack_value +EOF + +cat loc.out | testrun_compare ${abs_top_builddir}/src/readelf -U --debug-dump=loc testfile-debug-rel.o + +cat loc.out | sed -e "s/'.debug_loc' at offset 0x185/'.zdebug_loc' at offset 0x138/" | testrun_compare ${abs_top_builddir}/src/readelf -U --debug-dump=loc testfile-debug-rel-g.o + +cat loc.out | sed -e "s/at offset 0x185/at offset 0x150/" | testrun_compare ${abs_top_builddir}/src/readelf -U --debug-dump=loc testfile-debug-rel-z.o + +exit 0 diff --git a/tests/run-readelf-zp.sh b/tests/run-readelf-zp.sh new file mode 100755 index 0000000..872126c --- /dev/null +++ b/tests/run-readelf-zp.sh @@ -0,0 +1,271 @@ +#! /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-readelf-zdebug.sh for testfile. + +testfiles testfile-zdebug +testrun_compare ${abs_top_builddir}/src/readelf -z -p.zdebug_str testfile-zdebug <<\EOF + +String section [35] '.zdebug_str' contains 2431 bytes (6433 uncompressed) at offset 0x1da3: + [ 0] UINT64_MAX 18446744073709551615UL + [ 22] __DBL_DENORM_MIN__ ((double)4.94065645841246544177e-324L) + [ 5c] __linux 1 + [ 66] __SIZEOF_SIZE_T__ 8 + [ 7a] __UINTPTR_TYPE__ long unsigned int + [ 9d] __SIZEOF_POINTER__ 8 + [ b2] __UINT8_MAX__ 255 + [ c4] __PTRDIFF_MAX__ 9223372036854775807L + [ e9] __DEC64_MANT_DIG__ 16 + [ ff] __FLT_RADIX__ 2 + [ 10f] __DEC32_MIN__ 1E-95DF + [ 125] __unix__ 1 + [ 130] testfile-zdebug.c + [ 142] __UINT_LEAST64_MAX__ 18446744073709551615UL + [ 16e] __SIZEOF_WINT_T__ 4 + [ 182] __LONG_MAX__ 9223372036854775807L + [ 1a4] __LDBL_MIN__ 3.36210314311209350626e-4932L + [ 1cf] __GCC_ATOMIC_SHORT_LOCK_FREE 2 + [ 1ee] __LP64__ 1 + [ 1f9] __UINT64_C(c) c ## UL + [ 20f] __DBL_HAS_INFINITY__ 1 + [ 226] __SSE2_MATH__ 1 + [ 236] __linux__ 1 + [ 242] __STDC_HOSTED__ 1 + [ 254] __WINT_MIN__ 0U + [ 264] __x86_64__ 1 + [ 271] __UINT32_TYPE__ unsigned int + [ 28e] __UINT_LEAST8_MAX__ 255 + [ 2a6] __DEC64_SUBNORMAL_MIN__ 0.000000000000001E-383DD + [ 2d7] __FLT_MAX__ 3.40282346638528859812e+38F + [ 2ff] long unsigned int + [ 311] __DBL_MANT_DIG__ 53 + [ 325] linux 1 + [ 32d] __DBL_HAS_QUIET_NAN__ 1 + [ 345] __UINT8_TYPE__ unsigned char + [ 362] __DEC32_MAX_EXP__ 97 + [ 377] __INT32_TYPE__ int + [ 38a] __SIG_ATOMIC_TYPE__ int + [ 3a2] __DEC64_MAX_EXP__ 385 + [ 3b8] __DBL_MIN_EXP__ (-1021) + [ 3d0] _LP64 1 + [ 3d8] __LDBL_HAS_INFINITY__ 1 + [ 3f0] __INT_FAST64_TYPE__ long int + [ 40d] __gnu_linux__ 1 + [ 41d] __GCC_ATOMIC_WCHAR_T_LOCK_FREE 2 + [ 43e] __UINT_FAST64_TYPE__ long unsigned int + [ 465] __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__ + [ 48c] __UINT16_MAX__ 65535 + [ 4a1] __FLOAT_WORD_ORDER__ __ORDER_LITTLE_ENDIAN__ + [ 4ce] __PRAGMA_REDEFINE_EXTNAME 1 + [ 4ea] __INT_LEAST16_TYPE__ short int + [ 509] __k8__ 1 + [ 512] __DECIMAL_DIG__ 21 + [ 525] main + [ 52a] __DBL_MAX__ ((double)1.79769313486231570815e+308L) + [ 55d] __INT16_TYPE__ short int + [ 576] __LDBL_HAS_QUIET_NAN__ 1 + [ 58f] __SIZEOF_DOUBLE__ 8 + [ 5a3] __DEC32_SUBNORMAL_MIN__ 0.000001E-95DF + [ 5ca] __ATOMIC_SEQ_CST 5 + [ 5dd] __UINT64_TYPE__ long unsigned int + [ 5ff] __INT_LEAST32_TYPE__ int + [ 618] __INT_LEAST64_MAX__ 9223372036854775807L + [ 641] __OPTIMIZE__ 1 + [ 650] __INTMAX_C(c) c ## L + [ 665] __GCC_ATOMIC_CHAR32_T_LOCK_FREE 2 + [ 687] __INT_FAST8_MAX__ 127 + [ 69d] __PTRDIFF_TYPE__ long int + [ 6b7] __LDBL_MIN_EXP__ (-16381) + [ 6d1] __SIZEOF_LONG_LONG__ 8 + [ 6e8] __FLT_DIG__ 6 + [ 6f6] __UINTMAX_MAX__ 18446744073709551615UL + [ 71d] __SIZEOF_WCHAR_T__ 4 + [ 732] __INT64_C(c) c ## L + [ 746] __UINTPTR_MAX__ 18446744073709551615UL + [ 76d] __FLT_MAX_10_EXP__ 38 + [ 783] __FLT_MIN__ 1.17549435082228750797e-38F + [ 7ab] __UINT_LEAST64_TYPE__ long unsigned int + [ 7d3] __SIZEOF_LONG_DOUBLE__ 16 + [ 7ed] __SIZE_MAX__ 18446744073709551615UL + [ 811] __INT8_C(c) c + [ 81f] __amd64__ 1 + [ 82b] __INT_LEAST64_TYPE__ long int + [ 849] __INT_FAST64_MAX__ 9223372036854775807L + [ 871] __DEC_EVAL_METHOD__ 2 + [ 887] __DEC32_MAX__ 9.999999E96DF + [ 8a3] __GNUC_MINOR__ 8 + [ 8b4] __WCHAR_MAX__ 2147483647 + [ 8cd] __SIZE_TYPE__ long unsigned int + [ 8ed] __INT8_MAX__ 127 + [ 8fe] __INTMAX_MAX__ 9223372036854775807L + [ 922] __ATOMIC_HLE_RELEASE 131072 + [ 93e] __FLT_HAS_QUIET_NAN__ 1 + [ 956] __DBL_EPSILON__ ((double)2.22044604925031308085e-16L) + [ 98c] __FLT_MIN_EXP__ (-125) + [ 9a3] __INT_LEAST8_MAX__ 127 + [ 9ba] __SIZEOF_INT128__ 16 + [ 9cf] __INTPTR_MAX__ 9223372036854775807L + [ 9f3] __INTPTR_TYPE__ long int + [ a0c] __LDBL_MIN_10_EXP__ (-4931) + [ a28] __GCC_ATOMIC_POINTER_LOCK_FREE 2 + [ a49] __UINT_LEAST32_MAX__ 4294967295U + [ a6a] __SIZEOF_SHORT__ 2 + [ a7d] __LDBL_MAX_10_EXP__ 4932 + [ a96] __INT16_C(c) c + [ aa5] __MMX__ 1 + [ aaf] unix 1 + [ ab6] __FLT_MAX_EXP__ 128 + [ aca] __DEC64_MAX__ 9.999999999999999E384DD + [ af0] __FLT_EPSILON__ 1.19209289550781250000e-7F + [ b1b] __INT_FAST16_TYPE__ long int + [ b38] __VERSION__ "4.8.2 20140120 (Red Hat 4.8.2-15)" + [ b68] __GCC_ATOMIC_LLONG_LOCK_FREE 2 + [ b87] __DEC128_MIN_EXP__ (-6142) + [ ba2] __ATOMIC_RELEASE 3 + [ bb5] __GNUC_PATCHLEVEL__ 2 + [ bcb] __UINT_FAST64_MAX__ 18446744073709551615UL + [ bf6] __DBL_DECIMAL_DIG__ 17 + [ c0d] __DBL_DIG__ 15 + [ c1c] __FLT_MANT_DIG__ 24 + [ c30] __FLT_DECIMAL_DIG__ 9 + [ c46] __INT16_MAX__ 32767 + [ c5a] __DEC128_MIN__ 1E-6143DL + [ c73] __BIGGEST_ALIGNMENT__ 16 + [ c8c] __INT64_MAX__ 9223372036854775807L + [ caf] __INT_FAST32_TYPE__ long int + [ ccc] __GCC_ATOMIC_INT_LOCK_FREE 2 + [ ce9] __DEC128_MAX_EXP__ 6145 + [ d01] __GCC_HAVE_SYNC_COMPARE_AND_SWAP_4 1 + [ d26] __FXSR__ 1 + [ d31] __INT8_TYPE__ signed char + [ d4b] __ATOMIC_ACQ_REL 4 + [ d5e] __UINT_LEAST16_MAX__ 65535 + [ d79] __UINTMAX_TYPE__ long unsigned int + [ d9c] __UINT_FAST8_MAX__ 255 + [ db3] __ORDER_BIG_ENDIAN__ 4321 + [ dcd] __INT_LEAST32_MAX__ 2147483647 + [ dec] __UINT_LEAST16_TYPE__ short unsigned int + [ e15] __INT_FAST8_TYPE__ signed char + [ e34] __DBL_MAX_EXP__ 1024 + [ e49] __STDC__ 1 + [ e54] __ELF__ 1 + [ e5e] __FLT_EVAL_METHOD__ 0 + [ e74] __ATOMIC_ACQUIRE 2 + [ e87] __DEC64_EPSILON__ 1E-15DD + [ ea1] __INT32_MAX__ 2147483647 + [ eba] __GCC_ATOMIC_CHAR_LOCK_FREE 2 + [ ed8] __DEC128_EPSILON__ 1E-33DL + [ ef3] __UINT_FAST8_TYPE__ unsigned char + [ f15] __amd64 1 + [ f1f] __DEC32_MIN_EXP__ (-94) + [ f37] __GCC_HAVE_DWARF2_CFI_ASM 1 + [ f53] __LDBL_DIG__ 18 + [ f63] __UINT32_MAX__ 4294967295U + [ f7e] __GNUC_GNU_INLINE__ 1 + [ f94] __SSE2__ 1 + [ f9f] __ATOMIC_HLE_ACQUIRE 65536 + [ fba] __SSE_MATH__ 1 + [ fc9] __INT_FAST16_MAX__ 9223372036854775807L + [ ff1] __LDBL_MAX__ 1.18973149535723176502e+4932L + [ 101c] __DBL_MIN__ ((double)2.22507385850720138309e-308L) + [ 104f] __DEC128_MANT_DIG__ 34 + [ 1066] __INT32_C(c) c + [ 1075] __DEC64_MIN_EXP__ (-382) + [ 108e] __WCHAR_MIN__ (-__WCHAR_MAX__ - 1) + [ 10b1] __GCC_ATOMIC_CHAR16_T_LOCK_FREE 2 + [ 10d3] __LDBL_MAX_EXP__ 16384 + [ 10ea] __DEC32_MANT_DIG__ 7 + [ 10ff] __DEC128_MAX__ 9.999999999999999999999999999999999E6144DL + [ 1139] __CHAR32_TYPE__ unsigned int + [ 1156] __INT_LEAST8_TYPE__ signed char + [ 1176] __UINT16_C(c) c + [ 1186] __GCC_ATOMIC_BOOL_LOCK_FREE 2 + [ 11a4] __SIZEOF_FLOAT__ 4 + [ 11b7] __GCC_HAVE_SYNC_COMPARE_AND_SWAP_8 1 + [ 11dc] __DBL_MAX_10_EXP__ 308 + [ 11f3] __LDBL_EPSILON__ 1.08420217248550443401e-19L + [ 1220] __ORDER_PDP_ENDIAN__ 3412 + [ 123a] __ORDER_LITTLE_ENDIAN__ 1234 + [ 1257] __WINT_TYPE__ unsigned int + [ 1272] __unix 1 + [ 127b] __ATOMIC_RELAXED 0 + [ 128e] __UINT_FAST32_MAX__ 18446744073709551615UL + [ 12b9] __INT_FAST32_MAX__ 9223372036854775807L + [ 12e1] __SIG_ATOMIC_MAX__ 2147483647 + [ 12ff] __UINT_FAST32_TYPE__ long unsigned int + [ 1326] __INT_MAX__ 2147483647 + [ 133d] __GXX_ABI_VERSION 1002 + [ 1354] __SIZEOF_INT__ 4 + [ 1365] char + [ 136a] __UINT_FAST16_TYPE__ long unsigned int + [ 1391] __LDBL_DENORM_MIN__ 3.64519953188247460253e-4951L + [ 13c3] __WINT_MAX__ 4294967295U + [ 13dc] __FLT_HAS_INFINITY__ 1 + [ 13f3] __SHRT_MAX__ 32767 + [ 1406] __INT_LEAST16_MAX__ 32767 + [ 1420] __LONG_LONG_MAX__ 9223372036854775807LL + [ 1448] __SIZEOF_LONG__ 8 + [ 145a] __INTMAX_TYPE__ long int + [ 1473] __LDBL_HAS_DENORM__ 1 + [ 1489] __code_model_small__ 1 + [ 14a0] __REGISTER_PREFIX__ + [ 14b5] __ATOMIC_CONSUME 1 + [ 14c8] __DEC128_SUBNORMAL_MIN__ 0.000000000000000000000000000000001E-6143DL + [ 150d] __GNUC__ 4 + [ 1518] __UINT16_TYPE__ short unsigned int + [ 153b] __SSE__ 1 + [ 1545] __UINT32_C(c) c ## U + [ 155a] __k8 1 + [ 1561] __UINTMAX_C(c) c ## UL + [ 1578] __GCC_HAVE_SYNC_COMPARE_AND_SWAP_2 1 + [ 159d] __SIZEOF_PTRDIFF_T__ 8 + [ 15b4] __CHAR_BIT__ 8 + [ 15c3] __SIG_ATOMIC_MIN__ (-__SIG_ATOMIC_MAX__ - 1) + [ 15f0] __DEC32_EPSILON__ 1E-6DF + [ 1609] __UINT_LEAST32_TYPE__ unsigned int + [ 162c] __DBL_HAS_DENORM__ 1 + [ 1641] /tmp + [ 1646] __LDBL_MANT_DIG__ 64 + [ 165b] __GCC_ATOMIC_LONG_LOCK_FREE 2 + [ 1679] __DECIMAL_BID_FORMAT__ 1 + [ 1692] __FLT_MIN_10_EXP__ (-37) + [ 16ab] __GCC_ATOMIC_TEST_AND_SET_TRUEVAL 1 + [ 16cf] __WCHAR_TYPE__ int + [ 16e2] __FINITE_MATH_ONLY__ 0 + [ 16f9] argc + [ 16fe] __USER_LABEL_PREFIX__ + [ 1715] __CHAR16_TYPE__ short unsigned int + [ 1738] __UINT64_MAX__ 18446744073709551615UL + [ 175e] __UINT8_C(c) c + [ 176d] __x86_64 1 + [ 1778] __UINT_LEAST8_TYPE__ unsigned char + [ 179b] __INT64_TYPE__ long int + [ 17b3] __GCC_HAVE_SYNC_COMPARE_AND_SWAP_1 1 + [ 17d8] argv + [ 17dd] __GNUC_RH_RELEASE__ 15 + [ 17f4] __UINT_FAST16_MAX__ 18446744073709551615UL + [ 181f] __FLT_HAS_DENORM__ 1 + [ 1834] __DEC64_MIN__ 1E-383DD + [ 184b] __DBL_MIN_10_EXP__ (-307) + [ 1865] __FLT_DENORM_MIN__ 1.40129846432481707092e-45F + [ 1894] GNU C 4.8.2 20140120 (Red Hat 4.8.2-15) -mtune=generic -march=x86-64 -g3 -O3 -fuse-ld=gold -fno-asynchronous-unwind-tables + [ 190f] __SCHAR_MAX__ 127 +EOF + +exit 0 diff --git a/tests/run-readelf-zx.sh b/tests/run-readelf-zx.sh new file mode 100755 index 0000000..994528c --- /dev/null +++ b/tests/run-readelf-zx.sh @@ -0,0 +1,66 @@ +#! /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}/src/readelf -z -x.zdebug_aranges testfile-zgnu64 <<\EOF + +Hex dump of section [2] '.zdebug_aranges', 50 bytes (96 uncompressed) at offset 0x260: + 0x00000000 2c000000 02000000 00000800 00000000 ,............... + 0x00000010 78004000 00000000 14000000 00000000 x.@............. + 0x00000020 00000000 00000000 00000000 00000000 ................ + 0x00000030 2c000000 02005500 00000800 00000000 ,.....U......... + 0x00000040 8c004000 00000000 16000000 00000000 ..@............. + 0x00000050 00000000 00000000 00000000 00000000 ................ +EOF + +testfiles testfile-zgabi64 +testrun_compare ${abs_top_builddir}/src/readelf -z -x.debug_aranges testfile-zgabi64 <<\EOF + +Hex dump of section [2] '.debug_aranges', 62 bytes (96 uncompressed) at offset 0x260: + 0x00000000 2c000000 02000000 00000800 00000000 ,............... + 0x00000010 78004000 00000000 14000000 00000000 x.@............. + 0x00000020 00000000 00000000 00000000 00000000 ................ + 0x00000030 2c000000 02005500 00000800 00000000 ,.....U......... + 0x00000040 8c004000 00000000 16000000 00000000 ..@............. + 0x00000050 00000000 00000000 00000000 00000000 ................ +EOF + +testfiles testfile-zgnu32 +testrun_compare ${abs_top_builddir}/src/readelf -z -x.zdebug_aranges testfile-zgnu32 <<\EOF + +Hex dump of section [2] '.zdebug_aranges', 49 bytes (64 uncompressed) at offset 0x1c0: + 0x00000000 1c000000 02000000 00000400 00000000 ................ + 0x00000010 54800408 14000000 00000000 00000000 T............... + 0x00000020 1c000000 02004d00 00000400 00000000 ......M......... + 0x00000030 68800408 16000000 00000000 00000000 h............... +EOF + +testfiles testfile-zgabi32 +testrun_compare ${abs_top_builddir}/src/readelf -z -x.debug_aranges testfile-zgabi32 <<\EOF + +Hex dump of section [2] '.debug_aranges', 49 bytes (64 uncompressed) at offset 0x1c0: + 0x00000000 1c000000 02000000 00000400 00000000 ................ + 0x00000010 54800408 14000000 00000000 00000000 T............... + 0x00000020 1c000000 02004d00 00000400 00000000 ......M......... + 0x00000030 68800408 16000000 00000000 00000000 h............... +EOF + +exit 0 diff --git a/tests/testfile-debug-rel-g.o.bz2 b/tests/testfile-debug-rel-g.o.bz2 new file mode 100644 index 0000000000000000000000000000000000000000..b8c94e7e0f7b45925204d7832db9e9103b0b61e7 GIT binary patch literal 1238 zcmV;{1S$JMT4*^jL0KkKStlD=iU0$VfB*mg|L*Sh|NC42_jUjO|NhSZU;cVUJwpA? zSRp$_q{z?%QQ@I3%aa3C^pG+&Hlt5XQ}sO}c|+7aO{4=&9-|QSO%EsxjWhs1RA_oY zX!L3{Xvj1+pwns^JrF%XriK&EQ$i+)flo=|H9t|Oh)jg_KUBu1)YEA+WHvwu$&<(< z6CsFV21W>Aj6+AEG{B5ZBN2f#&_qzkG7N~*O)>xg00006fB*rY00Te(000000000% zrbb3XKrn%(nhb+OLqKRWG-3e40LW-yn1BN&5tB_c28|gsXwcBfrU(fXiA?oI)YC>y z8Z^)YK+tFnGypVcG8$=+0000000*c50iZMh00HgC%fUDng3_!ol)_OkgVv}$Mm4)2 zq+yEDK4fMM4`IswaD)cL1XQ+t;8t6RCe0S7jorbV9(W?4xjYmQP<?YhHQn2OgZL3l zQV<3s7VruO2pNJ#FdZnU;t^4Q#xbPUvt1JRl=W+1L%mZx>&b;pkv97I)<UhSAf9D} zKoFs&#Ks_4X}o4jvn8OTV}u~kT&V7{<yFp2L_wti0hWpaL|KR|i?tF2B?*Fwp(GVd zNkP)EI1N^{X=tN#tt<T84VyKMDNQRTB?cf(p*A)o+8aQI+ikE@tpQBcM2oAqWeVqZ zY9pkn0nnr%avY7(={dUY7&xzN1R@XGA=i>`20gHRO-T`aZKCfG5`o0*SadF#n52oo zIjpd8IW>Qn=sLD|sjxbdLc4DS1$#uhnnV&7?WBzLKK39sm_A%qUg@8B*Auy+qVGyo zl@lxDs<DF6v@7!q3CL1kWb4L8n?TZ;Nr?GIfFyO>$Xt97wz?KS^buy#l3XwqnknKm zDflQA5kUNABzR_9(v*x+v)mqdbO`Fw3oya>G1E;5C2xxzsD1XozZM)tyHWub_+ZnE z&|Oq8u_QItJ%0-3WaihvVaBE^O@>7~h*Q&8Mo0xtb8fA5;V>eXM47T}rMYAVOBq|b zouCS6PxFW~`A>wl2!Or}KvD>Sfj}QCm68BR9f&|s82pV#lw!|HmMWWOi$rTpSa1Rx ztJVb8izV#6;58Ue<;IwZ!--aiAAckXUffVIwsg|!)jFYAB~so}on^+x#%bIwG6L7+ z?Dleh8n<s7O{LlmIRRv~QjH9uS(f(54Se&?_Y~op2@f4g4S-omMQ%0kuz~;{dUJsx zPUtfdA}8u$HfM4{MVVNI*aR_;E&>}ZCtH!JQXm8%D(I3C6^9jB4i_yo;rQo~V|h1k z)Rk52AP1d>vC+oqNVgVHq!kqcIt1CgwQ<JoLlBGztUAep3)?$R!2japg%L@@Q>cq; zB!U#FP&I~bDo$DffucqL_H+@F4M!P?EcyZjsD-1QJ_QM#Fv?<9xM)ucuwp)Wa$x`( zp_J9+uxW223OmY;nt;<DidrTx1KBCgA)?e>(cCtz1kT#I3udloDrq)0m60G|DPV<j zb<cg5kn+<nDUUOUpUS|w1f9h)U(&vw^=!(o+R<4>cj@otL!&$9NwR7bS+o(3#K^Aq zo;n6gobmFpAe$YY`Z$?i<}Gx$6tH>v2y5imv5hXL0l#I@X8*<9kxmpO3C7l9fJQSN Au>b%7
literal 0 HcmV?d00001
diff --git a/tests/testfile-debug-rel-z.o.bz2 b/tests/testfile-debug-rel-z.o.bz2 new file mode 100644 index 0000000000000000000000000000000000000000..1cdac790e0baa3cc9ba6e4d00b4b1cd7d8001aa6 GIT binary patch literal 1225 zcmV;)1UCCZT4*^jL0KkKS*B;@X#fMNfB*mg|L*Sh|NG0|_ig|G|NhVY+y1r%JwkoX z7y&y)q{z?%SGoXnyLOidsg!8+(t4VD2-8ePhJerw01SXM8UO*IpwIvS00HU-hJf^d zG|&wmpotSrCN!Be#4=<WG}A_dKr#bBXfy*r0004?G#UU6001;-02%;@fSQ=p%|ihX zPyhe`000dD0000000000000010ilp-lT8B&pfoheqf7|M$)Lz&$TBp@(WXJ8KnxM4 zgAmXF$)S@08Vwo*i7BMo3VNQAHmBuA^-VoP)b&3?jUJ(sMt}oP6lnD}hNIL0>I{vj z(|V)I4@eCS4X6y78U}+wnx3Fx1&W2thyx~#86bhn76HJ9lWn3&1+VDVzA1nxDBboF zzeT|5PqKn312V7CZxelId3hIOb0Ckx7q50Y381xx@Fp<XhlCM&8a<AZYrJUrswJo} z0e~Pde*uYsOr(;aVFF+jBEN_rm-CtqSqSL}b66_P-R-<=o%grI?t6U=91Xixg2OyH z07drB<pBv|(#_8KXQFG^;vt~tHTw$FOdb7Lkqsyk3~4}uSrEHNP;-RBfKq5kM|i|2 zcNiJ%Zkklt(_DnG=|ShS%xO&0`&g7Q2{we-*nzY*2@Q?5*cI9XV%5JxKSKt_B(i$K z?yYVrm#kNryLncppTUBa+98C^g9;P+ovwG;n17s$XwrO&m2V6t+At&(LCN_HICuFH zo=yYcXNctI-ty>aetMmGNDXZ9yY*oK;Ivlfk|89qnnLt=JrscS@pw|@{xH3+E>`1n zYR%&Ew!bD_3~b0%8UqD~g;p)lz@L9HGHnA(<7KzA<w5|a&DMN;+<3ir%zyL;C4}{& z&Q4u|f)=(E9fY9fb1O*H;%lCbKH2yf@{v?#rGyJKkbCZYMG{ExLSXkRHVKy$P*X5T zr-%XxS`5>MzzO7Wp?F5*6kOwRxifqr#qO-T8(vpCO6U83WZPB?<x|UAtf2ZrRjnqs ziH)Vdb)F;;XIJKd>o{aHQy0#zQZzl&Tuxo48hG}og%V3f91usLWjdGTvKxF+-*C>R z0#b?rFX+MOhNV{w9ZA{I7C09r9NhV6nm2JNyA+w=NK=fZUP256VI;k{kky$SDf`bB zHg&n_P*gAlO7u`?%Z`2IJbuhJT5?=>`X1q4gtccrRjFNXUS`K+f$+7ETg$<AHB{*7 z%c#uqJW&d34s{T~NX@FrTNsF%h+rTMU?GuMguNj|8X|_=x4{F;6Qk<6HH0+~12deY zCoHxRFvnXeBy<8$2@k|wtT$kALOLS=!5|_jE6VzU{@DR=*P?kRC2TOV;FYUq8%mwU ztjW?05E0-H#DV4lb>x_s)P)IQZ6ji6B@<GCrD$CuGl~}|#A#B>1OS6N<SER%MZ~p$ z3{Ot*VHk-atElQBMrKvHc>o*q(_xm<7jQ!iCG2Bn2}`1ddgNrJd2`Ig%bM;1kbEl# z!2p653NPl0HqS0D+oM`x)Qi+VW2z+6Q4bg|uQ}%fyDqh6u=%z6<2Z76xI&syPmjKl n#-FA!tBK1*rIZS!rcANvPqz=OHc0}1e~Y;yoG3_BGxD^6o#83v
literal 0 HcmV?d00001
diff --git a/tests/testfile-debug-rel.o.bz2 b/tests/testfile-debug-rel.o.bz2 new file mode 100644 index 0000000000000000000000000000000000000000..a3f8dffa65792a829407c74ff29c5842485e0bde GIT binary patch literal 1069 zcmV+|1k(FLT4*^jL0KkKS<clVasUI-fB*mg|Lo@X|NGnRmudh1-}uj~$(NKt06+-9 z2m$~AsMyd0LExZLn8Q;bgvBr>q2@_8ri`1_JxvVMG-v>1Xfz(7ka~bUOc2w`Jg2Di z)X)r`p$ShWl=VEOGHQCBnHrB!00E#4000000000005kxD35k;dFeWCAF&F?Q0UBgz zz)dn>6C(fs0T?EX1R%m<WWY=biK9$L011FbnHn$?Oqc}7zyJV738Mi5M2MO+BPw|( zsp3O=nrJmWOoyPDn3@?h^k^Y~2pJlBMuwP1&;n+OiAtET(-EM?Wal&+3R$JaDJeB1 zQ-=PVQ>am0WQfBz7S!q()P6agi5O4h5PLdK<pKz+dK=Rr4$*t{Dp6v_8Pbab7X`5q z2FfXw@~*#f#5F2Un5WGU6tguJ2qZ@(Dna60ro=`k2#8)Jryc39&f+}-aE-9?=-SKB ztJ=HJ=h(9d>XH#^;$Q*-6<7cp<j>?%ph#X1kN3;B)3sp0#}Nz{yMtMFjxD_^r{01l z(u5$;lnD~zKyV$w))W}bBnoXIQqmPn0YgSxGg=5}S6V1wkW$cg+E&@oVVKgg>1fgF z!a9r!T1ae5o-{4c(3F}<wArG-A)U5@7$cTr!VxB$ZLnU;5N(;4bL8ec%!K!BixUx^ z@R~sl8M@t)3a+_CibOX<0h#tcS1O3Xj!4RFqbfj94o~l4-^syD?9IYi%@KI}xe?Ey zdMS%0oVXLyqoHnKN%K1pu~^21cVBBZXwIVYMX8|8XvS$KVnC9c^6A1-b-xS5PIw`A zB7bMm#?rM#NVOW<^A(0_*qbP5ccERDJc38tX16)LQ$!}vG_hJn;K+o#Ep}0Gfyf{g zrnDO5@1nMPsOwB7VKEg;KgVv2I%y?d79@dRp+O=~y&<Yu6f^mcZ+($wwV=Tx8Uvn6 z7>91(Sft62+@pRQV~v@Wsp14&!NS)=R2`coBD$aWRj~A5$g39!?$#Goto3rnkat3M z{AsQ#V`*kG!e!gw{$mZD`pvP{FO-yr>#oJ1C^$AMHP~A%Mq2g^JIJv&HvK|@%SnKq z3%|K;?0hv`8uO_>;JZ%^Ky*lMHa`a)5|<6Q<4|7y)Kj6221~VLFv(#~vt_Ubw4apa zl}=Vn^E)eI9NZez52JbwP1@?6btuP7S2@?!_`^Z?5lytN)i|)G!b@YgR>T1Og@9LU zmL{f~XL;@{gAd`)I+}tnMByzA7fqKLjDkYTN);5FIgli}>#9bdK1}2XqM!q%1r8EV z5J>2lS3(I75P=>oND(*;Pmv=GVhSdsBk=%M6cwT#6Q)eYU|K7RhskDulZ;wT>?1H$ nqKWlv^6#}&Z@Sg(m8_2VZDp;m^D<&9-|=@OQ-ui(?NS#Y^HJj5
literal 0 HcmV?d00001
elf_strptr indexes into the section data. This is defined as index into the uncompressed data of the section. If the section is compressed make sure the uncompressed data is available, but don't really decompress the section header (elf_getdata will still return compressed data).
Signed-off-by: Mark Wielaard mjw@redhat.com --- libelf/ChangeLog | 10 +++ libelf/elf_compress.c | 97 ++++++++++++++++----------- libelf/elf_strptr.c | 50 ++++++++++++-- libelf/libelfP.h | 6 ++ tests/ChangeLog | 11 ++++ tests/Makefile.am | 8 ++- tests/elfputzdata.c | 15 +++-- tests/run-elfputzdata.sh | 16 ++--- tests/run-zstrptr.sh | 167 +++++++++++++++++++++++++++++++++++++++++++++++ tests/zstrptr.c | 124 +++++++++++++++++++++++++++++++++++ 10 files changed, 445 insertions(+), 59 deletions(-) create mode 100755 tests/run-zstrptr.sh create mode 100644 tests/zstrptr.c
diff --git a/libelf/ChangeLog b/libelf/ChangeLog index 1e21400..3a1fe91 100644 --- a/libelf/ChangeLog +++ b/libelf/ChangeLog @@ -1,3 +1,13 @@ +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 diff --git a/libelf/elf_compress.c b/libelf/elf_compress.c index 17b80f2..ec5b717 100644 --- a/libelf/elf_compress.c +++ b/libelf/elf_compress.c @@ -248,6 +248,47 @@ __libelf_decompress (void *buf_in, size_t size_in, size_t size_out) 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, @@ -424,62 +465,42 @@ elf_compress (Elf_Scn *scn, int type, unsigned int flags) return -1; }
- GElf_Chdr chdr; - if (gelf_getchdr (scn, &chdr) == NULL) - return -1; - - if (chdr.ch_type != ELFCOMPRESS_ZLIB) + /* 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) { - __libelf_seterrno (ELF_E_UNKNOWN_COMPRESSION_TYPE); - return -1; + 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; }
- if (! powerof2 (chdr.ch_addralign)) - { - __libelf_seterrno (ELF_E_INVALID_ALIGN); - return -1; - } - - /* 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 -1; - - 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); - 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. */ if (elfclass == ELFCLASS32) { Elf32_Shdr *shdr = elf32_getshdr (scn); - shdr->sh_size = chdr.ch_size; - shdr->sh_addralign = chdr.ch_addralign; + 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 = chdr.ch_size; - shdr->sh_addralign = chdr.ch_addralign; + shdr->sh_size = scn->zdata_size; + shdr->sh_addralign = scn->zdata_align; shdr->sh_flags &= ~SHF_COMPRESSED; }
- __libelf_reset_rawdata (scn, buf_out, chdr.ch_size, chdr.ch_addralign, + __libelf_reset_rawdata (scn, scn->zdata_base, + scn->zdata_size, scn->zdata_align, __libelf_data_type (elf, sh_type));
- scn->zdata_base = buf_out; - return 1; } else diff --git a/libelf/elf_strptr.c b/libelf/elf_strptr.c index c5138dc..e3b5876 100644 --- a/libelf/elf_strptr.c +++ b/libelf/elf_strptr.c @@ -83,6 +83,20 @@ elf_strptr (Elf *elf, size_t idx, size_t offset) } }
+ void *get_zdata (void) + { + 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; + } + size_t sh_size = 0; if (elf->class == ELFCLASS32) { @@ -94,8 +108,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 () == 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 +134,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 () == 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 +161,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/libelfP.h b/libelf/libelfP.h index 50f9217..57ccbce 100644 --- a/libelf/libelfP.h +++ b/libelf/libelfP.h @@ -239,6 +239,8 @@ struct Elf_Scn 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. */ @@ -600,6 +602,10 @@ extern void * __libelf_compress (Elf_Scn *scn, size_t hsize, int ei_data,
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) diff --git a/tests/ChangeLog b/tests/ChangeLog index 6e9abbe..6f26ced 100644 --- a/tests/ChangeLog +++ b/tests/ChangeLog @@ -1,3 +1,14 @@ +2015-11-26 Mark Wielaard mjw@redhat.com + + * zstrptr.c: New file. + * run-zstrptr.sh: New test. + * elfputzdata.c (main): (re)compress .shstrtab. + * run-elfputzdata.sh: Expect .shstrtab compression. + * Makefile.am (check_PROGRAMS): Add zstrptr. + (TESTS): Add run-zstrptr.sh. + (EXTRA_DIST): Likewise. + (zstrptr_LDADD): New variable. + 2015-10-20 Mark Wielaard mjw@redhat.com
* run-readelf-zx.sh: New test. diff --git a/tests/Makefile.am b/tests/Makefile.am index 7e243e9..9660fee 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -53,7 +53,7 @@ check_PROGRAMS = arextract arsymtest newfile saridx scnnames sectiondump \ buildid deleted deleted-lib.so aggregate_size vdsosyms \ getsrc_die strptr newdata elfstrtab dwfl-proc-attach \ elfshphehdr elfstrmerge dwelfgnucompressed elfgetchdr \ - elfgetzdata elfputzdata + elfgetzdata elfputzdata zstrptr
asm_TESTS = asm-tst1 asm-tst2 asm-tst3 asm-tst4 asm-tst5 \ asm-tst6 asm-tst7 asm-tst8 asm-tst9 @@ -123,7 +123,7 @@ TESTS = run-arextract.sh run-arsymtest.sh newfile test-nlist \ run-getsrc-die.sh run-strptr.sh newdata elfstrtab dwfl-proc-attach \ elfshphehdr run-lfs-symbols.sh run-dwelfgnucompressed.sh \ run-elfgetchdr.sh \ - run-elfgetzdata.sh run-elfputzdata.sh + run-elfgetzdata.sh run-elfputzdata.sh run-zstrptr.sh
if !BIARCH export ELFUTILS_DISABLE_BIARCH = 1 @@ -317,7 +317,8 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh \ run-dwelfgnucompressed.sh \ testfile-zgabi32.bz2 testfile-zgabi64.bz2 \ testfile-zgabi32be.bz2 testfile-zgabi64be.bz2 \ - run-elfgetchdr.sh run-elfgetzdata.sh run-elfputzdata.sh + run-elfgetchdr.sh run-elfgetzdata.sh run-elfputzdata.sh \ + run-zstrptr.sh
if USE_VALGRIND valgrind_cmd='valgrind -q --leak-check=full --error-exitcode=1' @@ -470,6 +471,7 @@ dwelfgnucompressed_LDADD = $(libelf) $(libdw) elfgetchdr_LDADD = $(libelf) $(libdw) elfgetzdata_LDADD = $(libelf) elfputzdata_LDADD = $(libelf) +zstrptr_LDADD = $(libelf)
if GCOV check: check-am coverage diff --git a/tests/elfputzdata.c b/tests/elfputzdata.c index 65654f8..7e6f011 100644 --- a/tests/elfputzdata.c +++ b/tests/elfputzdata.c @@ -77,11 +77,7 @@ main (int argc, char *argv[]) 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 + if (shdr->sh_type == SHT_NOBITS || (shdr->sh_flags & SHF_ALLOC) != 0) { printf ("Cannot compress %zd %s\n", idx, name); @@ -221,6 +217,15 @@ main (int argc, char *argv[]) return -1; } free (orig_buf); + // Recompress the string table, just to make sure + // everything keeps working. See elf_strptr above. + if (! gnu && idx == strndx + && elf_compress (scn, ELFCOMPRESS_ZLIB, 0) < 0) + { + printf ("couldn't recompress section header strings: %s\n", + elf_errmsg (-1)); + return -1; + } } }
diff --git a/tests/run-elfputzdata.sh b/tests/run-elfputzdata.sh index e9a352e..b882b3f 100755 --- a/tests/run-elfputzdata.sh +++ b/tests/run-elfputzdata.sh @@ -53,7 +53,7 @@ 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 33 .shstrtab, size: 320 Lets compress 34 .symtab, size: 5488 Lets compress 35 .strtab, size: 5727 EOF @@ -91,7 +91,7 @@ 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 33 .shstrtab, size: 320 Lets compress 34 .symtab, size: 5488 Lets compress 35 .strtab, size: 5727 EOF @@ -130,7 +130,7 @@ 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 31 .shstrtab, size: 308 Lets compress 32 .symtab, size: 1944 Lets compress 33 .strtab, size: 757 EOF @@ -166,7 +166,7 @@ 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 31 .shstrtab, size: 308 Lets compress 32 .symtab, size: 1944 Lets compress 33 .strtab, size: 757 EOF @@ -208,7 +208,7 @@ Lets compress 30 .debug_frame, size: 96 Lets compress 31 .debug_str, size: 174 Lets compress 32 .debug_loc, size: 171 Lets compress 33 .debug_ranges, size: 32 -Not compressing section string table 34 +Lets compress 34 .shstrtab, size: 352 Lets compress 35 .symtab, size: 1800 Lets compress 36 .strtab, size: 602 EOF @@ -247,7 +247,7 @@ Lets compress 30 .debug_frame, size: 96 Lets compress 31 .debug_str, size: 174 Lets compress 32 .debug_loc, size: 171 Lets compress 33 .debug_ranges, size: 32 -Not compressing section string table 34 +Lets compress 34 .shstrtab, size: 352 Lets compress 35 .symtab, size: 1800 Lets compress 36 .strtab, size: 602 EOF @@ -291,7 +291,7 @@ Lets compress 32 .debug_frame, size: 64 Lets compress 33 .debug_str, size: 179 Lets compress 34 .debug_loc, size: 99 Lets compress 35 .debug_ranges, size: 16 -Not compressing section string table 36 +Lets compress 36 .shstrtab, size: 370 Lets compress 37 .symtab, size: 1232 Lets compress 38 .strtab, size: 569 EOF @@ -332,7 +332,7 @@ Lets compress 32 .debug_frame, size: 64 Lets compress 33 .debug_str, size: 179 Lets compress 34 .debug_loc, size: 99 Lets compress 35 .debug_ranges, size: 16 -Not compressing section string table 36 +Lets compress 36 .shstrtab, size: 370 Lets compress 37 .symtab, size: 1232 Lets compress 38 .strtab, size: 569 EOF diff --git a/tests/run-zstrptr.sh b/tests/run-zstrptr.sh new file mode 100755 index 0000000..254dcd8 --- /dev/null +++ b/tests/run-zstrptr.sh @@ -0,0 +1,167 @@ +#! /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 + +# A random 32bit and 64bit testfile +testfiles testfile testfile10 + +testrun_compare ${abs_top_builddir}/tests/zstrptr testfile <<\EOF +Strings in section 32 (compressed): +[0] '' +[1] '.symtab' +[9] '.strtab' +[11] '.shstrtab' +[1b] '.interp' +[23] '.note.ABI-tag' +[31] '.hash' +[37] '.dynsym' +[3f] '.dynstr' +[47] '.gnu.version' +[54] '.gnu.version_r' +[63] '.rel.got' +[6c] '.rel.plt' +[75] '.init' +[7b] '.plt' +[80] '.text' +[86] '.fini' +[8c] '.rodata' +[94] '.data' +[9a] '.eh_frame' +[a4] '.ctors' +[ab] '.dtors' +[b2] '.got' +[b7] '.dynamic' +[c0] '.sbss' +[c6] '.bss' +[cb] '.stab' +[d1] '.stabstr' +[da] '.comment' +[e3] '.debug_aranges' +[f2] '.debug_pubnames' +[102] '.debug_info' +[10e] '.debug_abbrev' +[11c] '.debug_line' +[128] '.note' +Strings in section 32 (uncompressed): +[0] '' +[1] '.symtab' +[9] '.strtab' +[11] '.shstrtab' +[1b] '.interp' +[23] '.note.ABI-tag' +[31] '.hash' +[37] '.dynsym' +[3f] '.dynstr' +[47] '.gnu.version' +[54] '.gnu.version_r' +[63] '.rel.got' +[6c] '.rel.plt' +[75] '.init' +[7b] '.plt' +[80] '.text' +[86] '.fini' +[8c] '.rodata' +[94] '.data' +[9a] '.eh_frame' +[a4] '.ctors' +[ab] '.dtors' +[b2] '.got' +[b7] '.dynamic' +[c0] '.sbss' +[c6] '.bss' +[cb] '.stab' +[d1] '.stabstr' +[da] '.comment' +[e3] '.debug_aranges' +[f2] '.debug_pubnames' +[102] '.debug_info' +[10e] '.debug_abbrev' +[11c] '.debug_line' +[128] '.note' +EOF + +testrun_compare ${abs_top_builddir}/tests/zstrptr testfile10 <<\EOF +Strings in section 30 (compressed): +[0] '' +[1] '.symtab' +[9] '.strtab' +[11] '.shstrtab' +[1b] '.hash' +[21] '.dynsym' +[29] '.dynstr' +[31] '.gnu.version' +[3e] '.gnu.version_r' +[4d] '.rela.dyn' +[57] '.init' +[5d] '.text' +[63] '.fini' +[69] '.eh_frame' +[73] '.data' +[79] '.dynamic' +[82] '.ctors' +[89] '.dtors' +[90] '.jcr' +[95] '.plt' +[9a] '.got' +[9f] '.sdata' +[a6] '.sbss' +[ac] '.bss' +[b1] '.comment' +[ba] '.debug_aranges' +[c9] '.debug_pubnames' +[d9] '.debug_abbrev' +[e7] '.debug_line' +[f3] '.debug_frame' +[100] '.debug_str' +[10b] '.rela.debug_info' +Strings in section 30 (uncompressed): +[0] '' +[1] '.symtab' +[9] '.strtab' +[11] '.shstrtab' +[1b] '.hash' +[21] '.dynsym' +[29] '.dynstr' +[31] '.gnu.version' +[3e] '.gnu.version_r' +[4d] '.rela.dyn' +[57] '.init' +[5d] '.text' +[63] '.fini' +[69] '.eh_frame' +[73] '.data' +[79] '.dynamic' +[82] '.ctors' +[89] '.dtors' +[90] '.jcr' +[95] '.plt' +[9a] '.got' +[9f] '.sdata' +[a6] '.sbss' +[ac] '.bss' +[b1] '.comment' +[ba] '.debug_aranges' +[c9] '.debug_pubnames' +[d9] '.debug_abbrev' +[e7] '.debug_line' +[f3] '.debug_frame' +[100] '.debug_str' +[10b] '.rela.debug_info' +EOF + +exit 0 diff --git a/tests/zstrptr.c b/tests/zstrptr.c new file mode 100644 index 0000000..6d8e19f --- /dev/null +++ b/tests/zstrptr.c @@ -0,0 +1,124 @@ +/* Test program for elf_strptr function. + 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 <errno.h> +#include <fcntl.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include ELFUTILS_HEADER(elf) +#include <gelf.h> + +int +main (int argc, char *argv[]) +{ + if (argc != 2) + { + printf ("No ELF file given as argument"); + exit (1); + } + + const char *fname = argv[1]; + + // Initialize libelf. + elf_version (EV_CURRENT); + + /* Read the ELF from disk now. */ + int fd = open (fname, O_RDONLY); + if (fd == -1) + { + printf ("cannot open `%s' read-only: %s\n", fname, strerror (errno)); + exit (1); + } + + Elf *elf = elf_begin (fd, ELF_C_READ, NULL); + if (elf == NULL) + { + printf ("cannot create ELF descriptor read-only: %s\n", elf_errmsg (-1)); + exit (1); + } + + size_t ndx; + if (elf_getshdrstrndx (elf, &ndx) != 0) + { + printf ("cannot get section header table index: %s\n", elf_errmsg (-1)); + exit (1); + } + + if (ndx == SHN_UNDEF) + { + printf ("ELF file `%s' doesn't have a section header table index", fname); + exit (1); + } + + Elf_Scn *scn = elf_getscn (elf, ndx); + if (scn == NULL) + { + printf ("Couldn't get section %zd: %s\n", ndx, elf_errmsg (-1)); + exit (1); + } + + void print_strings (void) + { + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + + printf ("Strings in section %zd (%s):\n", ndx, + ((shdr->sh_flags & SHF_COMPRESSED) != 0 + ? "compressed" : "uncompressed")); + + size_t off = 0; + const char *str = elf_strptr (elf, ndx, off); + while (str != NULL) + { + printf ("[%zx] '%s'\n", off, str); + off += strlen (str) + 1; + str = elf_strptr (elf, ndx, off); + } + } + + if (elf_compress (scn, ELFCOMPRESS_ZLIB, 0) < 0) + { + printf ("Couldn't compress section %zd: %s\n", ndx, elf_errmsg (-1)); + exit (1); + } + print_strings (); + + if (elf_compress (scn, 0, 0) < 0) + { + printf ("Couldn't decompress section %zd: %s\n", ndx, elf_errmsg (-1)); + exit (1); + } + print_strings (); + + if (elf_end (elf) != 0) + { + printf ("failure in elf_end: %s\n", elf_errmsg (-1)); + exit (1); + } + + close (fd); + + return 0; +}
SHF_COMPRESSED is a valid section flag, it can optionally be on any special section, but it cannot be used on NOBITS sections or together with SHF_ALLOC. A section that has SHF_COMPRESSED set must have a valid Chdr.
Signed-off-by: Mark Wielaard mjw@redhat.com --- src/ChangeLog | 8 ++++++++ src/elflint.c | 45 +++++++++++++++++++++++++++++++++++---------- 2 files changed, 43 insertions(+), 10 deletions(-)
diff --git a/src/ChangeLog b/src/ChangeLog index 1b5be54..a6d4a97 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,11 @@ +2015-12-18 Mark Wielaard mjw@redhat.com + + * elflint.c (section_flags_string): Add NEWFLAG COMPRESSED. + (check_sections): SHF_COMPRESSED can be on any special section. + SHF_COMPRESSED is a valid section flag. SHF_COMPRESSED cannot + be used together with SHF_ALLOC or with SHT_NOBITS. Should have + a valid Chdr. + 2015-10-20 Mark Wielaard mjw@redhat.com
* readelf.c (options): Expand -z help text. diff --git a/src/elflint.c b/src/elflint.c index bb97f59..7a7b9ce 100644 --- a/src/elflint.c +++ b/src/elflint.c @@ -1,5 +1,5 @@ /* Pedantic checking of ELF files compliance with gABI/psABI spec. - Copyright (C) 2001-2014 Red Hat, Inc. + Copyright (C) 2001-2015 Red Hat, Inc. This file is part of elfutils. Written by Ulrich Drepper drepper@redhat.com, 2001.
@@ -2750,7 +2750,8 @@ section_flags_string (GElf_Word flags, char *buf, size_t len) NEWFLAG (LINK_ORDER), NEWFLAG (OS_NONCONFORMING), NEWFLAG (GROUP), - NEWFLAG (TLS) + NEWFLAG (TLS), + NEWFLAG (COMPRESSED) }; #undef NEWFLAG const size_t nknown_flags = sizeof (known_flags) / sizeof (known_flags[0]); @@ -3698,7 +3699,8 @@ zeroth section has nonzero link value while ELF header does not signal overflow size_t versym_scnndx = 0; for (size_t cnt = 1; cnt < shnum; ++cnt) { - shdr = gelf_getshdr (elf_getscn (ebl->elf, cnt), &shdr_mem); + Elf_Scn *scn = elf_getscn (ebl->elf, cnt); + shdr = gelf_getshdr (scn, &shdr_mem); if (shdr == NULL) { ERROR (gettext ("\ @@ -3748,9 +3750,11 @@ section [%2d] '%s' has wrong type: expected %s, is %s\n"), if (special_sections[s].attrflag == exact || special_sections[s].attrflag == exact_or_gnuld) { - /* Except for the link order and group bit all the - other bits should match exactly. */ - if ((shdr->sh_flags & ~(SHF_LINK_ORDER | SHF_GROUP)) + /* Except for the link order, group bit and + compression flag all the other bits should + match exactly. */ + if ((shdr->sh_flags + & ~(SHF_LINK_ORDER | SHF_GROUP | SHF_COMPRESSED)) != special_sections[s].attr && (special_sections[s].attrflag == exact || !gnuld)) ERROR (gettext ("\ @@ -3766,9 +3770,10 @@ section [%2zu] '%s' has wrong flags: expected %s, is %s\n"), { if ((shdr->sh_flags & special_sections[s].attr) != special_sections[s].attr - || ((shdr->sh_flags & ~(SHF_LINK_ORDER | SHF_GROUP - | special_sections[s].attr - | special_sections[s].attr2)) + || ((shdr->sh_flags + & ~(SHF_LINK_ORDER | SHF_GROUP | SHF_COMPRESSED + | special_sections[s].attr + | special_sections[s].attr2)) != 0)) ERROR (gettext ("\ section [%2zu] '%s' has wrong flags: expected %s and possibly %s, is %s\n"), @@ -3871,7 +3876,8 @@ section [%2zu] '%s': size not multiple of entry size\n"),
#define ALL_SH_FLAGS (SHF_WRITE | SHF_ALLOC | SHF_EXECINSTR | SHF_MERGE \ | SHF_STRINGS | SHF_INFO_LINK | SHF_LINK_ORDER \ - | SHF_OS_NONCONFORMING | SHF_GROUP | SHF_TLS) + | SHF_OS_NONCONFORMING | SHF_GROUP | SHF_TLS \ + | SHF_COMPRESSED) if (shdr->sh_flags & ~(GElf_Xword) ALL_SH_FLAGS) { GElf_Xword sh_flags = shdr->sh_flags & ~(GElf_Xword) ALL_SH_FLAGS; @@ -3901,6 +3907,25 @@ section [%2zu] '%s': thread-local data sections address not zero\n"), // XXX TODO more tests!? }
+ if (shdr->sh_flags & SHF_COMPRESSED) + { + if (shdr->sh_flags & SHF_ALLOC) + ERROR (gettext ("\ +section [%2zu] '%s': allocated section cannot be compressed\n"), + cnt, section_name (ebl, cnt)); + + if (shdr->sh_type == SHT_NOBITS) + ERROR (gettext ("\ +section [%2zu] '%s': nobits section cannot be compressed\n"), + cnt, section_name (ebl, cnt)); + + GElf_Chdr chdr; + if (gelf_getchdr (scn, &chdr) == NULL) + ERROR (gettext ("\ +section [%2zu] '%s': compressed section with no compression header: %s\n"), + cnt, section_name (ebl, cnt), elf_errmsg (-1)); + } + if (shdr->sh_link >= shnum) ERROR (gettext ("\ section [%2zu] '%s': invalid section reference in link value\n"),
Usage: elfcompress [OPTION...] FILE... Compress or decompress sections in an ELF file.
-f, --force Force compression of section even if it would become larger -n, --name=SECTION SECTION name to (de)compress, SECTION is an extended wildcard pattern (defaults to '.?(z)debug*') -o, --output=FILE Place (de)compressed output into FILE -p, --permissive Relax a few rules to handle slightly broken ELF files -q, --quiet Be silent when a section cannot be compressed -t, --type=TYPE What type of compression to apply. TYPE can be 'none' (decompress), 'zlib' (ELF ZLIB compression, the default, 'zlib-gabi' is an alias) or 'zlib-gnu' (.zdebug GNU style compression, 'gnu' is an alias) -v, --verbose Print a message for each section being (de)compressed -?, --help Give this help list --usage Give a short usage message -V, --version Print program version
Signed-off-by: Mark Wielaard mjw@redhat.com --- src/ChangeLog | 6 + src/Makefile.am | 3 +- src/elfcompress.c | 1321 ++++++++++++++++++++++++++++++++++++++++++++ tests/ChangeLog | 6 + tests/Makefile.am | 5 +- tests/run-compress-test.sh | 102 ++++ 6 files changed, 1440 insertions(+), 3 deletions(-) create mode 100644 src/elfcompress.c create mode 100755 tests/run-compress-test.sh
diff --git a/src/ChangeLog b/src/ChangeLog index a6d4a97..ae2129d 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,9 @@ +2015-12-16 Mark Wielaard mjw@redhat.com + + * elfcompress.c: New file. + * Makefile.am (bin_PROGRAMS): Add elfcompress. + (elfcompress_LDADD): New variable. + 2015-12-18 Mark Wielaard mjw@redhat.com
* elflint.c (section_flags_string): Add NEWFLAG COMPRESSED. diff --git a/src/Makefile.am b/src/Makefile.am index e2c494c..a39df27 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -35,7 +35,7 @@ native_ld = @native_ld@ base_cpu = @base_cpu@
bin_PROGRAMS = readelf nm size strip ld elflint findtextrel addr2line \ - elfcmp objdump ranlib strings ar unstrip stack + elfcmp objdump ranlib strings ar unstrip stack elfcompress
ld_dsos = libld_elf_i386_pic.a @@ -125,6 +125,7 @@ strings_LDADD = $(libelf) $(libeu) $(argp_LDADD) ar_LDADD = libar.a $(libelf) $(libeu) $(argp_LDADD) unstrip_LDADD = $(libebl) $(libelf) $(libdw) $(libeu) $(argp_LDADD) -ldl stack_LDADD = $(libebl) $(libelf) $(libdw) $(libeu) $(argp_LDADD) -ldl $(demanglelib) +elfcompress_LDADD = $(libebl) $(libelf) $(libdw) $(libeu) $(argp_LDADD)
ldlex.o: ldscript.c ldlex_no_Werror = yes diff --git a/src/elfcompress.c b/src/elfcompress.c new file mode 100644 index 0000000..5be2375 --- /dev/null +++ b/src/elfcompress.c @@ -0,0 +1,1321 @@ +/* Compress or decompress an ELF file. + 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/. */ + +#include <config.h> +#include <assert.h> +#include <argp.h> +#include <error.h> +#include <stdbool.h> +#include <stdlib.h> +#include <inttypes.h> +#include <stdio.h> +#include <string.h> +#include <locale.h> +#include <fcntl.h> +#include <fnmatch.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include ELFUTILS_HEADER(elf) +#include ELFUTILS_HEADER(ebl) +#include <gelf.h> +#include "system.h" + +/* Name and version of program. */ +static void print_version (FILE *stream, struct argp_state *state); +ARGP_PROGRAM_VERSION_HOOK_DEF = print_version; + +/* Bug report address. */ +ARGP_PROGRAM_BUG_ADDRESS_DEF = PACKAGE_BUGREPORT; + +static int verbose = 0; /* < 0, no warnings, > 0 extra verbosity. */ +static bool force = false; +static bool permissive = false; +static const char *foutput = NULL; + +#define T_UNSET 0 +#define T_DECOMPRESS 1 /* none */ +#define T_COMPRESS_ZLIB 2 /* zlib */ +#define T_COMPRESS_GNU 3 /* zlib-gnu */ +static int type = T_UNSET; + +static void +print_version (FILE *stream, struct argp_state *state __attribute__ ((unused))) +{ + fprintf (stream, "elfcompress (%s) %s\n", PACKAGE_NAME, PACKAGE_VERSION); +} + +struct section_pattern +{ + char *pattern; + struct section_pattern *next; +}; + +static struct section_pattern *patterns = NULL; + +static void +add_pattern (const char *pattern) +{ + struct section_pattern *p = xmalloc (sizeof *p); + p->pattern = xstrdup (pattern); + p->next = patterns; + patterns = p; +} + +static void +free_patterns (void) +{ + struct section_pattern *pattern = patterns; + while (pattern != NULL) + { + struct section_pattern *p = pattern; + pattern = p->next; + free (p->pattern); + free (p); + } +} + +static error_t +parse_opt (int key, char *arg __attribute__ ((unused)), + struct argp_state *state __attribute__ ((unused))) +{ + switch (key) + { + case 'v': + verbose++; + break; + + case 'q': + verbose--; + + case 'f': + force = true; + break; + + case 'p': + permissive = true; + break; + + case 'n': + add_pattern (arg); + break; + + case 'o': + if (foutput != NULL) + argp_error (state, N_("-o option specified twice")); + else + foutput = arg; + break; + + case 't': + if (type != T_UNSET) + argp_error (state, N_("-t option specified twice")); + + if (strcmp ("none", arg) == 0) + type = T_DECOMPRESS; + else if (strcmp ("zlib", arg) == 0 || strcmp ("zlib-gabi", arg) == 0) + type = T_COMPRESS_ZLIB; + else if (strcmp ("zlib-gnu", arg) == 0 || strcmp ("gnu", arg) == 0) + type = T_COMPRESS_GNU; + else + argp_error (state, N_("unknown compression type '%s'"), arg); + break; + + case ARGP_KEY_SUCCESS: + if (type == T_UNSET) + type = T_COMPRESS_ZLIB; + if (patterns == NULL) + add_pattern (".?(z)debug*"); + break; + + case ARGP_KEY_NO_ARGS: + /* We need at least one input file. */ + argp_error (state, N_("No input file given")); + break; + + case ARGP_KEY_ARGS: + if (foutput != NULL && state->argc - state->next > 1) + argp_error (state, + N_("Only one input file allowed together with '-o'")); + /* We only use this for checking the number of arguments, we don't + actually want to consume them, so fallthrough. */ + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + +static bool +section_name_matches (const char *name) +{ + struct section_pattern *pattern = patterns; + while (pattern != NULL) + { + if (fnmatch (pattern->pattern, name, FNM_EXTMATCH) == 0) + return true; + pattern = pattern->next; + } + return false; +} + +static int +setshdrstrndx (Elf *elf, GElf_Ehdr *ehdr, size_t ndx) +{ + if (ndx < SHN_LORESERVE) + ehdr->e_shstrndx = ndx; + else + { + ehdr->e_shstrndx = SHN_XINDEX; + Elf_Scn *zscn = elf_getscn (elf, 0); + GElf_Shdr zshdr_mem; + GElf_Shdr *zshdr = gelf_getshdr (zscn, &zshdr_mem); + if (zshdr == NULL) + return -1; + zshdr->sh_link = ndx; + if (gelf_update_shdr (zscn, zshdr) == 0) + return -1; + } + + if (gelf_update_ehdr (elf, ehdr) == 0) + return -1; + + return 0; +} + +static int +compress_section (Elf_Scn *scn, size_t orig_size, const char *name, + const char *newname, size_t ndx, + bool gnu, bool compress, bool report_verbose) +{ + int res; + unsigned int flags = compress && force ? ELF_CHF_FORCE : 0; + if (gnu) + res = elf_compress_gnu (scn, compress ? 1 : 0, flags); + else + res = elf_compress (scn, compress ? ELFCOMPRESS_ZLIB : 0, flags); + + if (res < 0) + error (0, 0, "Couldn't decompress section [%zd] %s: %s", + ndx, name, elf_errmsg (-1)); + else + { + if (compress && res == 0) + { + if (verbose >= 0) + printf ("[%zd] %s NOT compressed, wouldn't be smaller\n", + ndx, name); + } + + if (report_verbose && res > 0) + { + printf ("[%zd] %s %s", ndx, name, + compress ? "compressed" : "decompressed"); + if (newname != NULL) + printf (" -> %s", newname); + + /* Reload shdr, it has changed. */ + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + if (shdr == NULL) + { + error (0, 0, "Couldn't get shdr for section [%zd]", ndx); + return -1; + } + float new = shdr->sh_size; + float orig = orig_size ?: 1; + printf (" (%" PRIu64 " => %" PRIu64 " %.2f%%)\n", + orig_size, shdr->sh_size, (new / orig) * 100); + } + } + + return res; +} + +static int +process_file (const char *fname) +{ + if (verbose > 0) + printf ("processing: %s\n", fname); + + /* The input ELF. */ + int fd = -1; + Elf *elf = NULL; + + /* The output ELF. */ + char *fnew = NULL; + int fdnew = -1; + Elf *elfnew = NULL; + + /* Buffer for (one) new section name if necessary. */ + char *snamebuf = NULL; + + /* String table (and symbol table), if section names need adjusting. */ + struct Ebl_Strtab *names = NULL; + struct Ebl_Strent **scnstrents = NULL; + struct Ebl_Strent **symstrents = NULL; + char **scnnames = NULL; + + /* Section data from names. */ + void *namesbuf = NULL; + + /* Which sections match and need to be (un)compressed. */ + unsigned int *sections = NULL; + + /* How many sections are we talking about? */ + size_t shnum = 0; + +#define WORD_BITS (8U * sizeof (unsigned int)) + void set_section (size_t ndx) + { + sections[ndx / WORD_BITS] |= (1U << (ndx % WORD_BITS)); + } + + bool get_section (size_t ndx) + { + return (sections[ndx / WORD_BITS] & (1U << (ndx % WORD_BITS))) != 0; + } + + int cleanup (int res) + { + elf_end (elf); + close (fd); + + elf_end (elfnew); + close (fdnew); + + if (fnew != NULL) + { + unlink (fnew); + free (fnew); + fnew = NULL; + } + + free (snamebuf); + if (names != NULL) + { + ebl_strtabfree (names); + free (scnstrents); + free (symstrents); + free (namesbuf); + if (scnnames != NULL) + { + for (size_t n = 0; n < shnum; n++) + free (scnnames[n]); + free (scnnames); + } + } + + free (sections); + + return res; + } + + fd = open (fname, O_RDONLY); + if (fd < 0) + { + error (0, errno, "Couldn't open %s\n", fname); + return cleanup (-1); + } + + elf = elf_begin (fd, ELF_C_READ, NULL); + if (elf == NULL) + { + error (0, 0, "Couldn't open ELF file %s for reading: %s", + fname, elf_errmsg (-1)); + return cleanup (-1); + } + + /* We dont' handle ar files (or anything else), we probably should. */ + Elf_Kind kind = elf_kind (elf); + if (kind != ELF_K_ELF) + { + if (kind == ELF_K_AR) + error (0, 0, "Cannot handle ar files: %s", fname); + else + error (0, 0, "Unknown file type: %s", fname); + return cleanup (-1); + } + + struct stat st; + if (fstat (fd, &st) != 0) + { + error (0, errno, "Couldn't fstat %s", fname); + return cleanup (-1); + } + + GElf_Ehdr ehdr; + if (gelf_getehdr (elf, &ehdr) == NULL) + { + error (0, 0, "Couldn't get ehdr for %s: %s", fname, elf_errmsg (-1)); + return cleanup (-1); + } + + /* Get the section header string table. */ + size_t shdrstrndx; + if (elf_getshdrstrndx (elf, &shdrstrndx) != 0) + { + error (0, 0, "Couldn't get section header string table index in %s: %s", + fname, elf_errmsg (-1)); + return cleanup (-1); + } + + /* How many sections are we talking about? */ + if (elf_getshdrnum (elf, &shnum) != 0) + { + error (0, 0, "Couldn't get number of sections in %s: %s", + fname, elf_errmsg (1)); + return cleanup (-1); + } + + if (shnum == 0) + { + error (0, 0, "ELF file %s has no sections", fname); + return cleanup (-1); + } + + sections = xcalloc (shnum / 8 + 1, sizeof (unsigned int)); + + size_t phnum; + if (elf_getphdrnum (elf, &phnum) != 0) + { + error (0, 0, "Couldn't get phdrnum: %s", elf_errmsg (-1)); + return cleanup (-1); + } + + /* Whether we need to adjust any section names (going to/from GNU + naming). If so we'll need to build a new section header string + table. */ + bool adjust_names = false; + + /* If there are phdrs we want to maintain the layout of the + allocated sections in the file. */ + bool layout = phnum != 0; + + /* While going through all sections keep track of last section data + offset if needed to keep the layout. We are responsible for + adding the section offsets and headers (e_shoff) in that case + (which we will place after the last section). */ + GElf_Off last_offset = 0; + if (layout) + last_offset = (ehdr.e_phoff + + gelf_fsize (elf, ELF_T_PHDR, phnum, EV_CURRENT)); + + /* Which section, if any, is a symbol table that shares a string + table with the section header string table? */ + size_t symtabndx = 0; + + /* We do three passes over all sections. + + First an inspection pass over the old Elf to see which section + data needs to be copied and/or transformed, which sections need a + names change and whether there is a symbol table that might need + to be adjusted be if the section header name table is changed. + + Second a collection pass that creates the Elf sections and copies + the data. This pass will compress/decompress section data when + needed. And it will collect all data needed if we'll need to + construct a new string table. Afterwards the new string table is + constructed. + + Third a fixup/adjustment pass over the new Elf that will adjust + any section references (names) and adjust the layout based on the + new sizes of the sections if necessary. This pass is optional if + we aren't responsible for the layout and the section header + string table hasn't been changed. */ + + /* Inspection pass. */ + size_t maxnamelen = 0; + Elf_Scn *scn = NULL; + while ((scn = elf_nextscn (elf, scn)) != NULL) + { + size_t ndx = elf_ndxscn (scn); + if (ndx > shnum) + { + error (0, 0, "Unexpected section number %zd, expected only %zd", + ndx, shnum); + cleanup (-1); + } + + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + if (shdr == NULL) + { + error (0, 0, "Couldn't get shdr for section %zd", ndx); + return cleanup (-1); + } + + const char *sname = elf_strptr (elf, shdrstrndx, shdr->sh_name); + if (sname == NULL) + { + error (0, 0, "Couldn't get name for section %zd", ndx); + return cleanup (-1); + } + + if (section_name_matches (sname)) + { + if (shdr->sh_type != SHT_NOBITS + && (shdr->sh_flags & SHF_ALLOC) == 0) + { + set_section (ndx); + /* Check if we might want to change this section name. */ + if (! adjust_names + && ((type != T_COMPRESS_GNU + && strncmp (sname, ".zdebug", + strlen (".zdebug")) == 0) + || (type == T_COMPRESS_GNU + && strncmp (sname, ".debug", + strlen (".debug")) == 0))) + adjust_names = true; + + /* We need a buffer this large if we change the names. */ + if (adjust_names) + { + size_t slen = strlen (sname); + if (slen > maxnamelen) + maxnamelen = slen; + } + } + else + if (verbose >= 0) + printf ("[%zd] %s ignoring %s section\n", ndx, sname, + (shdr->sh_type == SHT_NOBITS ? "no bits" : "allocated")); + } + + if (shdr->sh_type == SHT_SYMTAB) + { + /* Check if we might have to adjust the symbol name indexes. */ + if (shdr->sh_link == shdrstrndx) + { + if (symtabndx != 0) + { + error (0, 0, + "Multiple symbol tables (%zd, %zd) using the same string table unsupported", symtabndx, ndx); + return cleanup (-1); + } + symtabndx = ndx; + } + } + + /* Keep track of last allocated data offset. */ + if (layout) + if ((shdr->sh_flags & SHF_ALLOC) != 0) + { + GElf_Off off = shdr->sh_offset + (shdr->sh_type != SHT_NOBITS + ? shdr->sh_size : 0); + if (last_offset < off) + last_offset = off; + } + } + + if (adjust_names) + { + names = ebl_strtabinit (true); + if (names == NULL) + { + error (0, 0, "Not enough memory for new strtab"); + return cleanup (-1); + } + scnstrents = xmalloc (shnum + * sizeof (struct Ebl_Strent *)); + scnnames = xcalloc (shnum, sizeof (char *)); + } + + /* Create a new (temporary) ELF file for the result. */ + if (foutput == NULL) + { + size_t fname_len = strlen (fname); + fnew = xmalloc (fname_len + sizeof (".XXXXXX")); + strcpy (mempcpy (fnew, fname, fname_len), ".XXXXXX"); + fdnew = mkstemp (fnew); + } + else + { + fnew = xstrdup (foutput); + fdnew = open (fnew, O_WRONLY | O_CREAT, st.st_mode & ALLPERMS); + } + + if (fdnew < 0) + { + error (0, errno, "Couldn't create output file %s", fnew); + /* Since we didn't create it we don't want to try to unlink it. */ + free (fnew); + fnew = NULL; + return cleanup (-1); + } + + elfnew = elf_begin (fdnew, ELF_C_WRITE, NULL); + if (elfnew == NULL) + { + error (0, 0, "Couldn't open new ELF %s for writing: %s", + fnew, elf_errmsg (-1)); + return cleanup (-1); + } + + /* Create the new ELF header and copy over all the data. */ + if (gelf_newehdr (elfnew, gelf_getclass (elf)) == 0) + { + error (0, 0, "Couldn't create new ehdr: %s", elf_errmsg (-1)); + return cleanup (-1); + } + + GElf_Ehdr newehdr; + if (gelf_getehdr (elfnew, &newehdr) == NULL) + { + error (0, 0, "Couldn't get new ehdr: %s", elf_errmsg (-1)); + return cleanup (-1); + } + + newehdr.e_ident[EI_DATA] = ehdr.e_ident[EI_DATA]; + newehdr.e_type = ehdr.e_type; + newehdr.e_machine = ehdr.e_machine; + newehdr.e_version = ehdr.e_version; + newehdr.e_entry = ehdr.e_entry; + newehdr.e_flags = ehdr.e_flags; + + if (gelf_update_ehdr (elfnew, &newehdr) == 0) + { + error (0, 0, "Couldn't update ehdr: %s", elf_errmsg (-1)); + return cleanup (-1); + } + + /* Copy over the phdrs as is. */ + if (phnum != 0) + { + if (gelf_newphdr (elfnew, phnum) == 0) + { + error (0, 0, "Couldn't create phdrs: %s", elf_errmsg (-1)); + return cleanup (-1); + } + + for (size_t cnt = 0; cnt < phnum; ++cnt) + { + GElf_Phdr phdr_mem; + GElf_Phdr *phdr = gelf_getphdr (elf, cnt, &phdr_mem); + if (phdr == NULL) + { + error (0, 0, "Couldn't get phdr %zd: %s", cnt, elf_errmsg (-1)); + return cleanup (-1); + } + if (gelf_update_phdr (elfnew, cnt, phdr) == 0) + { + error (0, 0, "Couldn't create phdr %zd: %s", cnt, + elf_errmsg (-1)); + return cleanup (-1); + } + } + } + + /* Possibly add a 'z' and zero terminator. */ + if (maxnamelen > 0) + snamebuf = xmalloc (maxnamelen + 2); + + /* We might want to read/adjust the section header strings and + symbol tables. If so, and those sections are to be compressed + then we will have to decompress it during the collection pass and + compress it again in the fixup pass. Don't compress unnecessary + and keep track of whether or not to compress them (later in the + fixup pass). Also record the original size, so we can report the + difference later when we do compress. */ + int shstrtab_compressed = T_UNSET; + size_t shstrtab_size = 0; + char *shstrtab_name = NULL; + char *shstrtab_newname = NULL; + int symtab_compressed = T_UNSET; + size_t symtab_size = 0; + char *symtab_name = NULL; + char *symtab_newname = NULL; + + /* Collection pass. Copy over the sections, (de)compresses matching + sections, collect names of sections and symbol table if + necessary. */ + scn = NULL; + while ((scn = elf_nextscn (elf, scn)) != NULL) + { + size_t ndx = elf_ndxscn (scn); + assert (ndx < shnum); + + /* (de)compress if section matched. */ + char *sname = NULL; + char *newname = NULL; + if (get_section (ndx)) + { + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + if (shdr == NULL) + { + error (0, 0, "Couldn't get shdr for section %zd", ndx); + return cleanup (-1); + } + + uint64_t size = shdr->sh_size; + sname = elf_strptr (elf, shdrstrndx, shdr->sh_name); + if (sname == NULL) + { + error (0, 0, "Couldn't get name for section %zd", ndx); + return cleanup (-1); + } + + /* strdup sname, the shdrstrndx section itself might be + (de)compressed, invalidating the string pointers. */ + sname = xstrdup (sname); + + /* We might want to decompress (and rename), but not + compress during this pass since we might need the section + data in later passes. Skip those sections for now and + compress them in the fixup pass. */ + bool skip_compress_section = (adjust_names + && (ndx == shdrstrndx + || ndx == symtabndx)); + + switch (type) + { + case T_DECOMPRESS: + if ((shdr->sh_flags & SHF_COMPRESSED) != 0) + { + if (compress_section (scn, size, sname, NULL, ndx, + false, false, verbose > 0) < 0) + return cleanup (-1); + } + else if (strncmp (sname, ".zdebug", strlen (".zdebug")) == 0) + { + snamebuf[0] = '.'; + strcpy (&snamebuf[1], &sname[2]); + newname = snamebuf; + if (compress_section (scn, size, sname, newname, ndx, + true, false, verbose > 0) < 0) + return cleanup (-1); + } + else if (verbose > 0) + printf ("[%zd] %s already decompressed\n", ndx, sname); + break; + + case T_COMPRESS_GNU: + if (strncmp (sname, ".debug", strlen (".debug")) == 0) + { + if ((shdr->sh_flags & SHF_COMPRESSED) != 0) + { + /* First decompress to recompress GNU style. + Don't report even when verbose. */ + if (compress_section (scn, size, sname, NULL, ndx, + false, false, false) < 0) + return cleanup (-1); + } + + snamebuf[0] = '.'; + snamebuf[1] = 'z'; + strcpy (&snamebuf[2], &sname[1]); + newname = snamebuf; + + if (skip_compress_section) + { + if (ndx == shdrstrndx) + { + shstrtab_size = size; + shstrtab_compressed = T_COMPRESS_GNU; + shstrtab_name = xstrdup (sname); + shstrtab_newname = xstrdup (newname); + } + else + { + symtab_size = size; + symtab_compressed = T_COMPRESS_GNU; + symtab_name = xstrdup (sname); + symtab_newname = xstrdup (newname); + } + } + else + { + int res = compress_section (scn, size, sname, newname, + ndx, true, true, + verbose > 0); + if (res < 0) + return cleanup (-1); + + if (res == 0) + newname = NULL; + } + } + else if (verbose >= 0) + { + if (strncmp (sname, ".zdebug", strlen (".zdebug")) == 0) + printf ("[%zd] %s unchanged, already GNU compressed", + ndx, sname); + else + printf ("[%zd] %s cannot GNU compress section not starting with .debug\n", + ndx, sname); + } + break; + + case T_COMPRESS_ZLIB: + if ((shdr->sh_flags & SHF_COMPRESSED) == 0) + { + if (strncmp (sname, ".zdebug", strlen (".zdebug")) == 0) + { + /* First decompress to recompress zlib style. + Don't report even when verbose. */ + if (compress_section (scn, size, sname, NULL, ndx, + true, false, false) < 0) + return cleanup (-1); + + snamebuf[0] = '.'; + strcpy (&snamebuf[1], &sname[2]); + newname = snamebuf; + } + + if (skip_compress_section) + { + if (ndx == shdrstrndx) + { + shstrtab_size = size; + shstrtab_compressed = T_COMPRESS_ZLIB; + shstrtab_name = xstrdup (sname); + shstrtab_newname = (newname == NULL + ? NULL : xstrdup (newname)); + } + else + { + symtab_size = size; + symtab_compressed = T_COMPRESS_ZLIB; + symtab_name = xstrdup (sname); + symtab_newname = (newname == NULL + ? NULL : xstrdup (newname)); + } + } + else if (compress_section (scn, size, sname, newname, ndx, + false, true, verbose > 0) < 0) + return cleanup (-1); + } + else if (verbose > 0) + printf ("[%zd] %s already compressed\n", ndx, sname); + break; + } + + free (sname); + } + + Elf_Scn *newscn = elf_newscn (elfnew); + if (newscn == NULL) + { + error (0, 0, "Couldn't create new section %zd", ndx); + return cleanup (-1); + } + + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + if (shdr == NULL) + { + error (0, 0, "Couldn't get shdr for section %zd", ndx); + return cleanup (-1); + } + + if (gelf_update_shdr (newscn, shdr) == 0) + { + error (0, 0, "Couldn't update section header %zd", ndx); + return cleanup (-1); + } + + /* Except for the section header string table all data can be + copied as is. The section header string table will be + created later and the symbol table might be fixed up if + necessary. */ + if (! adjust_names || ndx != shdrstrndx) + { + Elf_Data *data = elf_getdata (scn, NULL); + if (data == NULL) + { + error (0, 0, "Couldn't get data from section %zd", ndx); + return cleanup (-1); + } + + Elf_Data *newdata = elf_newdata (newscn); + if (newdata == NULL) + { + error (0, 0, "Couldn't create new data for section %zd", ndx); + return cleanup (-1); + } + + *newdata = *data; + } + + /* Keep track of the (new) section names. */ + if (adjust_names) + { + char *name; + if (newname != NULL) + name = newname; + else + { + name = elf_strptr (elf, shdrstrndx, shdr->sh_name); + if (name == NULL) + { + error (0, 0, "Couldn't get name for section [%zd]", ndx); + return cleanup (-1); + } + } + + /* We need to keep a copy of the name till the strtab is done. */ + name = scnnames[ndx] = xstrdup (name); + if ((scnstrents[ndx] = ebl_strtabadd (names, name, 0)) == NULL) + { + error (0, 0, "No memory to add section name string table"); + return cleanup (-1); + } + + /* If the symtab shares strings then add those too. */ + if (ndx == symtabndx) + { + /* If the section is (still) compressed we'll need to + uncompress it first to adjust the data, then + recompress it in the fixup pass. */ + if (symtab_compressed == T_UNSET) + { + size_t size = shdr->sh_size; + if ((shdr->sh_flags == SHF_COMPRESSED) != 0) + { + /* Don't report the (internal) uncompression. */ + if (compress_section (newscn, size, sname, NULL, ndx, + false, false, false) < 0) + return cleanup (-1); + + symtab_size = size; + symtab_compressed = T_COMPRESS_ZLIB; + } + else if (strncmp (name, ".zdebug", strlen (".zdebug")) == 0) + { + /* Don't report the (internal) uncompression. */ + if (compress_section (newscn, size, sname, NULL, ndx, + true, false, false) < 0) + return cleanup (-1); + + symtab_size = size; + symtab_compressed = T_COMPRESS_GNU; + } + } + + Elf_Data *symd = elf_getdata (newscn, NULL); + if (symd == NULL) + { + error (0, 0, "Couldn't get symtab data for section [%zd] %s", + ndx, name); + return cleanup (-1); + } + size_t elsize = gelf_fsize (elfnew, ELF_T_SYM, 1, EV_CURRENT); + size_t syms = symd->d_size / elsize; + symstrents = xmalloc (syms * sizeof (struct Ebl_Strent *)); + for (size_t i = 0; i < syms; i++) + { + GElf_Sym sym_mem; + GElf_Sym *sym = gelf_getsym (symd, i, &sym_mem); + if (sym == NULL) + { + error (0, 0, "Couldn't get symbol %zd", i); + return cleanup (-1); + } + if (sym->st_name != 0) + { + /* Note we take the name from the original ELF, + since the new one will not have setup the + strtab yet. */ + const char *symname = elf_strptr (elf, shdrstrndx, + sym->st_name); + if (symname == NULL) + { + error (0, 0, "Couldn't get symbol %zd name", i); + return cleanup (-1); + } + symstrents[i] = ebl_strtabadd (names, symname, 0); + if (symstrents[i] == NULL) + { + error (0, 0, "No memory to add to symbol name"); + return cleanup (-1); + } + } + } + } + } + } + + if (adjust_names) + { + /* We got all needed strings, put the new data in the shstrtab. */ + if (verbose > 0) + printf ("[%zd] Updating section string table\n", shdrstrndx); + + scn = elf_getscn (elfnew, shdrstrndx); + if (scn == NULL) + { + error (0, 0, "Couldn't get new section header string table [%zd]", + shdrstrndx); + return cleanup (-1); + } + + Elf_Data *data = elf_newdata (scn); + if (data == NULL) + { + error (0, 0, "Couldn't create new section header string table data"); + return cleanup (-1); + } + ebl_strtabfinalize (names, data); + namesbuf = data->d_buf; + + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + if (shdr == NULL) + { + error (0, 0, "Couldn't get shdr for new section strings %zd", + shdrstrndx); + return cleanup (-1); + } + + /* Note that we also might have to compress and possibly set + sh_off below */ + shdr->sh_name = ebl_strtaboffset (scnstrents[shdrstrndx]); + shdr->sh_type = SHT_STRTAB; + shdr->sh_flags = 0; + shdr->sh_addr = 0; + shdr->sh_offset = 0; + shdr->sh_size = data->d_size; + shdr->sh_link = SHN_UNDEF; + shdr->sh_info = SHN_UNDEF; + shdr->sh_addralign = 1; + shdr->sh_entsize = 0; + + if (gelf_update_shdr (scn, shdr) == 0) + { + error (0, 0, "Couldn't update new section strings [%zd]", + shdrstrndx); + return cleanup (-1); + } + + /* We might have to compress the data if the user asked us to, + or if the section was already compressed (and the user didn't + ask for decompression). Note somewhat identical code for + symtab below. */ + if (shstrtab_compressed == T_UNSET) + { + /* The user didn't ask for compression, but maybe it was + compressed in the original ELF file. */ + Elf_Scn *oldscn = elf_getscn (elf, shdrstrndx); + if (oldscn == NULL) + { + error (0, 0, "Couldn't get section header string table [%zd]", + shdrstrndx); + return cleanup (-1); + } + + shdr = gelf_getshdr (oldscn, &shdr_mem); + if (shdr == NULL) + { + error (0, 0, "Couldn't get shdr for old section strings [%zd]", + shdrstrndx); + return cleanup (-1); + } + + shstrtab_name = elf_strptr (elf, shdrstrndx, shdr->sh_name); + if (shstrtab_name == NULL) + { + error (0, 0, "Couldn't get name for old section strings [%zd]", + shdrstrndx); + return cleanup (-1); + } + + shstrtab_size = shdr->sh_size; + if ((shdr->sh_flags & SHF_COMPRESSED) != 0) + shstrtab_compressed = T_COMPRESS_ZLIB; + else if (strncmp (shstrtab_name, ".zdebug", strlen (".zdebug")) == 0) + shstrtab_compressed = T_COMPRESS_GNU; + } + + /* Should we (re)compress? */ + if (shstrtab_compressed != T_UNSET) + { + if (compress_section (scn, shstrtab_size, shstrtab_name, + shstrtab_newname, shdrstrndx, + shstrtab_compressed == T_COMPRESS_GNU, + true, verbose > 0) < 0) + return cleanup (-1); + } + } + + /* Make sure to re-get the new ehdr. Adding phdrs and shdrs will + have changed it. */ + if (gelf_getehdr (elfnew, &newehdr) == NULL) + { + error (0, 0, "Couldn't re-get new ehdr: %s", elf_errmsg (-1)); + return cleanup (-1); + } + + /* Set this after the sections have been created, otherwise section + zero might not exist yet. */ + if (setshdrstrndx (elfnew, &newehdr, shdrstrndx) != 0) + { + error (0, 0, "Couldn't set new shdrstrndx: %s", elf_errmsg (-1)); + return cleanup (-1); + } + + /* Fixup pass. Adjust string table references, symbol table and + layout if necessary. */ + if (layout || adjust_names) + { + scn = NULL; + while ((scn = elf_nextscn (elfnew, scn)) != NULL) + { + size_t ndx = elf_ndxscn (scn); + + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + if (shdr == NULL) + { + error (0, 0, "Couldn't get shdr for section %zd", ndx); + return cleanup (-1); + } + + /* Keep the offset of allocated sections so they are at the + same place in the file. Add (possibly changed) + unallocated ones after the allocated ones. */ + if ((shdr->sh_flags & SHF_ALLOC) == 0) + { + /* Zero means one. No alignment constraints. */ + size_t addralign = shdr->sh_addralign ?: 1; + last_offset = (last_offset + addralign - 1) & ~(addralign - 1); + shdr->sh_offset = last_offset; + if (shdr->sh_type != SHT_NOBITS) + last_offset += shdr->sh_size; + } + + if (adjust_names) + shdr->sh_name = ebl_strtaboffset (scnstrents[ndx]); + + if (gelf_update_shdr (scn, shdr) == 0) + { + error (0, 0, "Couldn't update section header %zd", ndx); + return cleanup (-1); + } + + if (adjust_names && ndx == symtabndx) + { + if (verbose > 0) + printf ("[%zd] Updating symbol table\n", symtabndx); + + Elf_Data *symd = elf_getdata (scn, NULL); + if (symd == NULL) + { + error (0, 0, "Couldn't get new symtab data section [%zd]", + ndx); + return cleanup (-1); + } + size_t elsize = gelf_fsize (elfnew, ELF_T_SYM, 1, EV_CURRENT); + size_t syms = symd->d_size / elsize; + for (size_t i = 0; i < syms; i++) + { + GElf_Sym sym_mem; + GElf_Sym *sym = gelf_getsym (symd, i, &sym_mem); + if (sym == NULL) + { + error (0, 0, "2 Couldn't get symbol %zd", i); + return cleanup (-1); + } + + if (sym->st_name != 0) + { + sym->st_name = ebl_strtaboffset (symstrents[i]); + + if (gelf_update_sym (symd, i, sym) == 0) + { + error (0, 0, "Couldn't update symbol %zd", i); + return cleanup (-1); + } + } + } + + /* We might have to compress the data if the user asked + us to, or if the section was already compressed (and + the user didn't ask for decompression). Note + somewhat identical code for shstrtab above. */ + if (symtab_compressed == T_UNSET) + { + /* The user didn't ask for compression, but maybe it was + compressed in the original ELF file. */ + Elf_Scn *oldscn = elf_getscn (elf, symtabndx); + if (oldscn == NULL) + { + error (0, 0, "Couldn't get symbol table [%zd]", + symtabndx); + return cleanup (-1); + } + + shdr = gelf_getshdr (oldscn, &shdr_mem); + if (shdr == NULL) + { + error (0, 0, "Couldn't get old symbol table shdr [%zd]", + symtabndx); + return cleanup (-1); + } + + symtab_name = elf_strptr (elf, shdrstrndx, shdr->sh_name); + if (symtab_name == NULL) + { + error (0, 0, "Couldn't get old symbol table name [%zd]", + symtabndx); + return cleanup (-1); + } + + symtab_size = shdr->sh_size; + if ((shdr->sh_flags & SHF_COMPRESSED) != 0) + symtab_compressed = T_COMPRESS_ZLIB; + else if (strncmp (symtab_name, ".zdebug", + strlen (".zdebug")) == 0) + symtab_compressed = T_COMPRESS_GNU; + } + + /* Should we (re)compress? */ + if (symtab_compressed != T_UNSET) + { + if (compress_section (scn, symtab_size, symtab_name, + symtab_newname, symtabndx, + symtab_compressed == T_COMPRESS_GNU, + true, verbose > 0) < 0) + return cleanup (-1); + } + } + } + } + + /* If we have phdrs we want elf_update to layout the SHF_ALLOC + sections precisely as in the original file. In that case we are + also responsible for setting phoff and shoff */ + if (layout) + { + if (gelf_getehdr (elfnew, &newehdr) == NULL) + { + error (0, 0, "Couldn't get ehdr: %s", elf_errmsg (-1)); + return cleanup (-1); + } + + /* Position the shdrs after the last (unallocated) section. */ + const size_t offsize = gelf_fsize (elfnew, ELF_T_OFF, 1, EV_CURRENT); + newehdr.e_shoff = ((last_offset + offsize - 1) + & ~((GElf_Off) (offsize - 1))); + + /* The phdrs go in the same place as in the original file. + Normally right after the ELF header. */ + newehdr.e_phoff = ehdr.e_phoff; + + if (gelf_update_ehdr (elfnew, &newehdr) == 0) + { + error (0, 0, "Couldn't update ehdr: %s", elf_errmsg (-1)); + return cleanup (-1); + } + } + + elf_flagelf (elfnew, ELF_C_SET, ((layout ? ELF_F_LAYOUT : 0) + | (permissive ? ELF_F_PERMISSIVE : 0))); + + if (elf_update (elfnew, ELF_C_WRITE) < 0) + { + error (0, 0, "Couldn't write %s: %s", fnew, elf_errmsg (-1)); + return cleanup (-1); + } + + elf_end (elfnew); + elfnew = NULL; + + /* Try to match mode and owner.group of the original file. */ + if (fchmod (fdnew, st.st_mode & ALLPERMS) != 0) + if (verbose >= 0) + error (0, errno, "Couldn't fchmod %s", fnew); + if (fchown (fdnew, st.st_uid, st.st_gid) != 0) + if (verbose >= 0) + error (0, errno, "Couldn't fchown %s", fnew); + + /* Finally replace the old file with the new file. */ + if (foutput == NULL) + if (rename (fnew, fname) != 0) + { + error (0, errno, "Couldn't rename %s to %s", fnew, fname); + return cleanup (-1); + } + + /* We are finally done with the new file, don't unlink it now. */ + free (fnew); + fnew = NULL; + + return cleanup (0); +} + +int +main (int argc, char **argv) +{ + const struct argp_option options[] = + { + { "output", 'o', "FILE", 0, + N_("Place (de)compressed output into FILE"), + 0 }, + { "type", 't', "TYPE", 0, + N_("What type of compression to apply. TYPE can be 'none' (decompress), 'zlib' (ELF ZLIB compression, the default, 'zlib-gabi' is an alias) or 'zlib-gnu' (.zdebug GNU style compression, 'gnu' is an alias)"), + 0 }, + { "name", 'n', "SECTION", 0, + N_("SECTION name to (de)compress, SECTION is an extended wildcard pattern (defaults to '.?(z)debug*')"), + 0 }, + { "verbose", 'v', NULL, 0, + N_("Print a message for each section being (de)compressed"), + 0 }, + { "force", 'f', NULL, 0, + N_("Force compression of section even if it would become larger"), + 0 }, + { "permissive", 'p', NULL, 0, + N_("Relax a few rules to handle slightly broken ELF files"), + 0 }, + { "quiet", 'q', NULL, 0, + N_("Be silent when a section cannot be compressed"), + 0 }, + { NULL, 0, NULL, 0, NULL, 0 } + }; + + const struct argp argp = + { + .options = options, + .parser = parse_opt, + .args_doc = N_("FILE..."), + .doc = N_("Compress or decompress sections in an ELF file.") + }; + + int remaining; + if (argp_parse (&argp, argc, argv, 0, &remaining, NULL) != 0) + return EXIT_FAILURE; + + /* Should already be handled by ARGP_KEY_NO_ARGS case above, + just sanity check. */ + if (remaining >= argc) + error (EXIT_FAILURE, 0, N_("No input file given")); + + /* Likewise for the ARGP_KEY_ARGS case above, an extra sanity check. */ + if (foutput != NULL && remaining + 1 < argc) + error (EXIT_FAILURE, 0, + N_("Only one input file allowed together with '-o'")); + + elf_version (EV_CURRENT); + + /* Process all the remaining files. */ + int result = 0; + do + result |= process_file (argv[remaining]); + while (++remaining < argc); + + free_patterns (); + return result; +} diff --git a/tests/ChangeLog b/tests/ChangeLog index 6f26ced..d8e1e45 100644 --- a/tests/ChangeLog +++ b/tests/ChangeLog @@ -1,3 +1,9 @@ +2015-12-16 Mark Wielaard mjw@redhat.com + + * run-compress-test.sh: New test. + * Makefile.am (TESTS): Add run-compress-test.sh. + (EXTRA_DISTS): Likewise. + 2015-11-26 Mark Wielaard mjw@redhat.com
* zstrptr.c: New file. diff --git a/tests/Makefile.am b/tests/Makefile.am index 9660fee..d67fc7f 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -123,7 +123,8 @@ TESTS = run-arextract.sh run-arsymtest.sh newfile test-nlist \ run-getsrc-die.sh run-strptr.sh newdata elfstrtab dwfl-proc-attach \ elfshphehdr run-lfs-symbols.sh run-dwelfgnucompressed.sh \ run-elfgetchdr.sh \ - run-elfgetzdata.sh run-elfputzdata.sh run-zstrptr.sh + run-elfgetzdata.sh run-elfputzdata.sh run-zstrptr.sh \ + run-compress-test.sh
if !BIARCH export ELFUTILS_DISABLE_BIARCH = 1 @@ -318,7 +319,7 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh \ testfile-zgabi32.bz2 testfile-zgabi64.bz2 \ testfile-zgabi32be.bz2 testfile-zgabi64be.bz2 \ run-elfgetchdr.sh run-elfgetzdata.sh run-elfputzdata.sh \ - run-zstrptr.sh + run-zstrptr.sh run-compress-test.sh
if USE_VALGRIND valgrind_cmd='valgrind -q --leak-check=full --error-exitcode=1' diff --git a/tests/run-compress-test.sh b/tests/run-compress-test.sh new file mode 100755 index 0000000..a6a298f --- /dev/null +++ b/tests/run-compress-test.sh @@ -0,0 +1,102 @@ +#! /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 + +# uncompress -> gnucompress -> uncompress -> elfcompress -> uncompress +testrun_elfcompress_file() +{ + infile="$1" + uncompressedfile="${infile}.uncompressed" + tempfiles "$uncompressedfile" + + echo "uncompress $infile -> $uncompressedfile" + testrun ${abs_top_builddir}/src/elfcompress -v -t none -o ${uncompressedfile} ${infile} + testrun ${abs_top_builddir}/src/elflint --gnu-ld ${uncompressedfile} + + SIZE_uncompressed=$(stat -c%s $uncompressedfile) + + gnucompressedfile="${infile}.gnu" + tempfiles "$gnucompressedfile" + echo "compress gnu $uncompressedfile -> $gnucompressedfile" + testrun ${abs_top_builddir}/src/elfcompress -v -t gnu -o ${gnucompressedfile} ${uncompressedfile} + testrun ${abs_top_builddir}/src/elflint --gnu-ld ${gnucompressedfile} + + SIZE_gnucompressed=$(stat -c%s $gnucompressedfile) + test $SIZE_gnucompressed -lt $SIZE_uncompressed || + { echo "*** failure $gnucompressedfile not smaller"; exit -1; } + + gnuuncompressedfile="${infile}.gnu.uncompressed" + tempfiles "$gnuuncompressedfile" + echo "uncompress $gnucompressedfile -> $gnuuncompressedfile" + testrun ${abs_top_builddir}/src/elfcompress -v -t none -o ${gnuuncompressedfile} ${gnucompressedfile} + testrun ${abs_top_builddir}/src/elfcmp ${uncompressedfile} ${gnuuncompressedfile} + + elfcompressedfile="${infile}.gabi" + tempfiles "$elfcompressedfile" + echo "compress gabi $uncompressedfile -> $elfcompressedfile" + testrun ${abs_top_builddir}/src/elfcompress -v -t zlib -o ${elfcompressedfile} ${uncompressedfile} + testrun ${abs_top_builddir}/src/elflint --gnu-ld ${elfcompressedfile} + + SIZE_elfcompressed=$(stat -c%s $elfcompressedfile) + test $SIZE_elfcompressed -lt $SIZE_uncompressed || + { echo "*** failure $elfcompressedfile not smaller"; exit -1; } + + elfuncompressedfile="${infile}.gabi.uncompressed" + tempfiles "$elfuncompressedfile" + echo "uncompress $elfcompressedfile -> $elfuncompressedfile" + testrun ${abs_top_builddir}/src/elfcompress -v -t none -o ${elfuncompressedfile} ${elfcompressedfile} + testrun ${abs_top_builddir}/src/elfcmp ${uncompressedfile} ${elfuncompressedfile} +} + +testrun_elfcompress() +{ + testfile="$1" + testfiles ${testfile} + testrun_elfcompress_file ${testfile} + + # Merge the string tables to make things a little more interesting. + mergedfile="${testfile}.merged" + tempfiles ${mergedfile} + echo "merging string tables ${testfile} -> ${mergedfile}" + testrun ${abs_top_builddir}/tests/elfstrmerge -o ${mergedfile} ${testfile} + testrun_elfcompress_file ${mergedfile} +} + +# Random ELF32 testfile +testrun_elfcompress testfile4 + +# Random ELF64 testfile +testrun_elfcompress testfile12 + +# Random ELF64BE testfile +testrun_elfcompress testfileppc64 + +# Random ELF32BE testfile +testrun_elfcompress testfileppc32 + +# Already compressed files +testrun_elfcompress testfile-zgnu64 +testrun_elfcompress testfile-zgnu64be +testrun_elfcompress testfile-zgabi64 +testrun_elfcompress testfile-zgabi64be +testrun_elfcompress testfile-zgnu32 +testrun_elfcompress testfile-zgnu32be +testrun_elfcompress testfile-zgabi32 +testrun_elfcompress testfile-zgabi32be + +exit 0
On Thu, 2015-12-31 at 23:43 +0100, Mark Wielaard wrote:
This is hopefully the final patch set to add SHF_COMPRESSED section support to libelf, libdw and some of the elfutils utilities.
I now pushed these 10 patches to master. I would have liked some more comments on the implementation, but I have tested it a lot and believe it is fairly solid. With this in we should have all pending patches for a 0.165 release on the master branch now.
Cheers,
Mark
elfutils-devel@lists.fedorahosted.org