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 <system.h> 22 23#include <sys/types.h> 24#include <sys/stat.h> 25#include <fcntl.h> 26#include <inttypes.h> 27#include <libelf.h> 28#include <gelf.h> 29#include <stdbool.h> 30#include <stdio.h> 31#include <stdlib.h> 32#include <string.h> 33#include <unistd.h> 34 35 36int 37main (int argc, char *argv[]) 38{ 39 int result = 0; 40 int cnt; 41 42 if (argc < 3 43 || (strcmp (argv[1], "elf") != 0 44 && strcmp (argv[1], "gnu") != 0)) 45 { 46 printf ("Usage: (elf|gnu) files...\n"); 47 return -1; 48 } 49 50 int gnu; 51 if (strcmp (argv[1], "gnu") == 0) 52 gnu = 1; 53 else 54 gnu = 0; 55 56 elf_version (EV_CURRENT); 57 58 for (cnt = 2; cnt < argc; ++cnt) 59 { 60 int fd = open (argv[cnt], O_RDONLY); 61 62 Elf *elf = elf_begin (fd, ELF_C_READ, NULL); 63 if (elf == NULL) 64 { 65 printf ("%s not usable %s\n", argv[cnt], elf_errmsg (-1)); 66 result = 1; 67 close (fd); 68 continue; 69 } 70 71 /* To get the section names. */ 72 size_t strndx; 73 elf_getshdrstrndx (elf, &strndx); 74 75 Elf_Scn *scn = NULL; 76 while ((scn = elf_nextscn (elf, scn)) != NULL) 77 { 78 size_t idx = elf_ndxscn (scn); 79 GElf_Shdr mem; 80 GElf_Shdr *shdr = gelf_getshdr (scn, &mem); 81 const char *name = elf_strptr (elf, strndx, shdr->sh_name); 82 if (shdr->sh_type == SHT_NOBITS 83 || (shdr->sh_flags & SHF_ALLOC) != 0) 84 { 85 printf ("Cannot compress %zd %s\n", idx, name); 86 } 87 else if ((shdr->sh_flags & SHF_COMPRESSED) != 0 88 || startswith (name, ".zdebug")) 89 { 90 printf ("Already compressed %zd %s\n", idx, name); 91 } 92 else 93 { 94 size_t orig_size = shdr->sh_size; 95 printf ("Lets compress %zd %s, size: %" PRId64 "\n", 96 idx, name, shdr->sh_size); 97 Elf_Data *d = elf_getdata (scn, NULL); 98 if (d == NULL) 99 { 100 printf ("Couldn't get orig data for section %zd\n", idx); 101 return -1; 102 } 103 /* Make a copy so we can compare after 104 compression/decompression. */ 105 if (d->d_size != orig_size) 106 { 107 printf ("Unexpected data size for orig section %zd\n", idx); 108 return -1; 109 } 110 char *orig_buf = NULL; 111 if (orig_size > 0) 112 { 113 orig_buf = malloc (d->d_size); 114 if (orig_buf == NULL) 115 { 116 printf ("No memory to copy section %zd data\n", idx); 117 return -1; 118 } 119 memcpy (orig_buf, d->d_buf, orig_size); 120 } 121 122 bool forced = false; 123 if (gnu) 124 { 125 int res = elf_compress_gnu (scn, 1, 0); 126 if (res == 0) 127 { 128 forced = true; 129 res = elf_compress_gnu (scn, 1, ELF_CHF_FORCE); 130 } 131 if (res < 0) 132 { 133 printf ("elf_compress_gnu%sfailed for section %zd: %s\n", 134 forced ? " (forced) " : " ", 135 idx, elf_errmsg (-1)); 136 return -1; 137 } 138 } 139 else 140 { 141 int res = elf_compress (scn, ELFCOMPRESS_ZLIB, 0); 142 if (res == 0) 143 { 144 forced = true; 145 res = elf_compress (scn, ELFCOMPRESS_ZLIB, ELF_CHF_FORCE); 146 } 147 if (res < 0) 148 { 149 printf ("elf_compress%sfailed for section %zd: %s\n", 150 forced ? " (forced) " : " ", 151 idx, elf_errmsg (-1)); 152 return -1; 153 } 154 } 155 GElf_Shdr newmem; 156 GElf_Shdr *newshdr = gelf_getshdr (scn, &newmem); 157 size_t new_size = newshdr->sh_size; 158 d = elf_getdata (scn, NULL); 159 // Don't check this, might depend on zlib implementation. 160 // fprintf (stderr, " new_size: %zd\n", new_size); 161 if (d->d_size != new_size) 162 { 163 printf ("Unexpected data size for compressed section %zd\n", 164 idx); 165 return -1; 166 } 167 168 if (forced && new_size < orig_size) 169 { 170 printf ("section %zd forced to compress, but size smaller\n", 171 idx); 172 return -1; 173 } 174 175 if (! forced && new_size >= orig_size) 176 { 177 printf ("section %zd compressed to bigger size\n", 178 idx); 179 return -1; 180 } 181 182 if (new_size == orig_size 183 && (orig_buf == NULL 184 || memcmp (orig_buf, d->d_buf, orig_size) == 0)) 185 { 186 printf ("section %zd didn't compress\n", idx); 187 return -1; 188 } 189 190 if (gnu) 191 { 192 if (elf_compress_gnu (scn, 0, 0) < 0) 193 { 194 printf ("elf_[un]compress_gnu failed for section %zd: %s\n", 195 idx, elf_errmsg (-1)); 196 return -1; 197 } 198 } 199 else 200 { 201 if (elf_compress (scn, 0, 0) < 0) 202 { 203 printf ("elf_[un]compress failed for section %zd: %s\n", 204 idx, elf_errmsg (-1)); 205 return -1; 206 } 207 } 208 GElf_Shdr newermem; 209 GElf_Shdr *newershdr = gelf_getshdr (scn, &newermem); 210 size_t newer_size = newershdr->sh_size; 211 d = elf_getdata (scn, NULL); 212 // fprintf (stderr, " newer_size: %zd\n", newer_size); 213 if (d->d_size != newer_size) 214 { 215 printf ("Unexpected data size for compressed section %zd\n", 216 idx); 217 return -1; 218 } 219 if (newer_size != orig_size 220 && (orig_buf == NULL 221 || memcmp (orig_buf, d->d_buf, orig_size) != 0)) 222 { 223 printf ("section %zd didn't correctly uncompress\n", idx); 224 return -1; 225 } 226 free (orig_buf); 227 // Recompress the string table, just to make sure 228 // everything keeps working. See elf_strptr above. 229 if (! gnu && idx == strndx 230 && elf_compress (scn, ELFCOMPRESS_ZLIB, 0) < 0) 231 { 232 printf ("couldn't recompress section header strings: %s\n", 233 elf_errmsg (-1)); 234 return -1; 235 } 236 } 237 } 238 239 elf_end (elf); 240 close (fd); 241 } 242 243 return result; 244} 245