1 /* Copyright (C) 2015 Red Hat, Inc. 2 This file is part of elfutils. 3 4 This file is free software; you can redistribute it and/or modify 5 it under the terms of the GNU General Public License as published by 6 the Free Software Foundation; either version 3 of the License, or 7 (at your option) any later version. 8 9 elfutils is distributed in the hope that it will be useful, but 10 WITHOUT ANY WARRANTY; without even the implied warranty of 11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 GNU General Public License for more details. 13 14 You should have received a copy of the GNU General Public License 15 along with this program. If not, see <http://www.gnu.org/licenses/>. */ 16 17 #ifdef HAVE_CONFIG_H 18 # include <config.h> 19 #endif 20 21 #include <sys/types.h> 22 #include <sys/stat.h> 23 #include <fcntl.h> 24 #include <inttypes.h> 25 #include <libelf.h> 26 #include <gelf.h> 27 #include <stdbool.h> 28 #include <stdio.h> 29 #include <stdlib.h> 30 #include <string.h> 31 #include <unistd.h> 32 33 34 int 35 main (int argc, char *argv[]) 36 { 37 int result = 0; 38 int cnt; 39 40 if (argc < 3 41 || (strcmp (argv[1], "elf") != 0 42 && strcmp (argv[1], "gnu") != 0)) 43 { 44 printf ("Usage: (elf|gnu) files...\n"); 45 return -1; 46 } 47 48 int gnu; 49 if (strcmp (argv[1], "gnu") == 0) 50 gnu = 1; 51 else 52 gnu = 0; 53 54 elf_version (EV_CURRENT); 55 56 for (cnt = 2; cnt < argc; ++cnt) 57 { 58 int fd = open (argv[cnt], O_RDONLY); 59 60 Elf *elf = elf_begin (fd, ELF_C_READ, NULL); 61 if (elf == NULL) 62 { 63 printf ("%s not usable %s\n", argv[cnt], elf_errmsg (-1)); 64 result = 1; 65 close (fd); 66 continue; 67 } 68 69 /* To get the section names. */ 70 size_t strndx; 71 elf_getshdrstrndx (elf, &strndx); 72 73 Elf_Scn *scn = NULL; 74 while ((scn = elf_nextscn (elf, scn)) != NULL) 75 { 76 size_t idx = elf_ndxscn (scn); 77 GElf_Shdr mem; 78 GElf_Shdr *shdr = gelf_getshdr (scn, &mem); 79 const char *name = elf_strptr (elf, strndx, shdr->sh_name); 80 if (shdr->sh_type == SHT_NOBITS 81 || (shdr->sh_flags & SHF_ALLOC) != 0) 82 { 83 printf ("Cannot compress %zd %s\n", idx, name); 84 } 85 else if ((shdr->sh_flags & SHF_COMPRESSED) != 0 86 || strncmp (name, ".zdebug", strlen (".zdebug")) == 0) 87 { 88 printf ("Already compressed %zd %s\n", idx, name); 89 } 90 else 91 { 92 size_t orig_size = shdr->sh_size; 93 printf ("Lets compress %zd %s, size: %" PRId64 "\n", 94 idx, name, shdr->sh_size); 95 Elf_Data *d = elf_getdata (scn, NULL); 96 if (d == NULL) 97 { 98 printf ("Couldn't get orig data for section %zd\n", idx); 99 return -1; 100 } 101 /* Make a copy so we can compare after 102 compression/decompression. */ 103 if (d->d_size != orig_size) 104 { 105 printf ("Unexpected data size for orig section %zd\n", idx); 106 return -1; 107 } 108 char *orig_buf = malloc (d->d_size); 109 if (orig_size > 0 && orig_buf == NULL) 110 { 111 printf ("No memory to copy section %zd data\n", idx); 112 return -1; 113 } 114 if (orig_size > 0) 115 memcpy (orig_buf, d->d_buf, orig_size); 116 117 bool forced = false; 118 if (gnu) 119 { 120 int res = elf_compress_gnu (scn, 1, 0); 121 if (res == 0) 122 { 123 forced = true; 124 res = elf_compress_gnu (scn, 1, ELF_CHF_FORCE); 125 } 126 if (res < 0) 127 { 128 printf ("elf_compress_gnu%sfailed for section %zd: %s\n", 129 forced ? " (forced) " : " ", 130 idx, elf_errmsg (-1)); 131 return -1; 132 } 133 } 134 else 135 { 136 int res = elf_compress (scn, ELFCOMPRESS_ZLIB, 0); 137 if (res == 0) 138 { 139 forced = true; 140 res = elf_compress (scn, ELFCOMPRESS_ZLIB, ELF_CHF_FORCE); 141 } 142 if (res < 0) 143 { 144 printf ("elf_compress%sfailed for section %zd: %s\n", 145 forced ? " (forced) " : " ", 146 idx, elf_errmsg (-1)); 147 return -1; 148 } 149 } 150 GElf_Shdr newmem; 151 GElf_Shdr *newshdr = gelf_getshdr (scn, &newmem); 152 size_t new_size = newshdr->sh_size; 153 d = elf_getdata (scn, NULL); 154 // Don't check this, might depend on zlib implementation. 155 // fprintf (stderr, " new_size: %zd\n", new_size); 156 if (d->d_size != new_size) 157 { 158 printf ("Unexpected data size for compressed section %zd\n", 159 idx); 160 return -1; 161 } 162 163 if (forced && new_size < orig_size) 164 { 165 printf ("section %zd forced to compress, but size smaller\n", 166 idx); 167 return -1; 168 } 169 170 if (! forced && new_size >= orig_size) 171 { 172 printf ("section %zd compressed to bigger size\n", 173 idx); 174 return -1; 175 } 176 177 if (new_size == orig_size 178 && memcmp (orig_buf, d->d_buf, orig_size) == 0) 179 { 180 printf ("section %zd didn't compress\n", idx); 181 return -1; 182 } 183 184 if (gnu) 185 { 186 if (elf_compress_gnu (scn, 0, 0) < 0) 187 { 188 printf ("elf_[un]compress_gnu failed for section %zd: %s\n", 189 idx, elf_errmsg (-1)); 190 return -1; 191 } 192 } 193 else 194 { 195 if (elf_compress (scn, 0, 0) < 0) 196 { 197 printf ("elf_[un]compress failed for section %zd: %s\n", 198 idx, elf_errmsg (-1)); 199 return -1; 200 } 201 } 202 GElf_Shdr newermem; 203 GElf_Shdr *newershdr = gelf_getshdr (scn, &newermem); 204 size_t newer_size = newershdr->sh_size; 205 d = elf_getdata (scn, NULL); 206 // fprintf (stderr, " newer_size: %zd\n", newer_size); 207 if (d->d_size != newer_size) 208 { 209 printf ("Unexpected data size for compressed section %zd\n", 210 idx); 211 return -1; 212 } 213 if (newer_size != orig_size 214 && memcmp (orig_buf, d->d_buf, orig_size) != 0) 215 { 216 printf ("section %zd didn't correctly uncompress\n", idx); 217 return -1; 218 } 219 free (orig_buf); 220 // Recompress the string table, just to make sure 221 // everything keeps working. See elf_strptr above. 222 if (! gnu && idx == strndx 223 && elf_compress (scn, ELFCOMPRESS_ZLIB, 0) < 0) 224 { 225 printf ("couldn't recompress section header strings: %s\n", 226 elf_errmsg (-1)); 227 return -1; 228 } 229 } 230 } 231 232 elf_end (elf); 233 close (fd); 234 } 235 236 return result; 237 } 238