1 /* Compress or decompress a section. 2 Copyright (C) 2015 Red Hat, Inc. 3 This file is part of elfutils. 4 5 This file is free software; you can redistribute it and/or modify 6 it under the terms of either 7 8 * the GNU Lesser General Public License as published by the Free 9 Software Foundation; either version 3 of the License, or (at 10 your option) any later version 11 12 or 13 14 * the GNU General Public License as published by the Free 15 Software Foundation; either version 2 of the License, or (at 16 your option) any later version 17 18 or both in parallel, as here. 19 20 elfutils is distributed in the hope that it will be useful, but 21 WITHOUT ANY WARRANTY; without even the implied warranty of 22 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 23 General Public License for more details. 24 25 You should have received copies of the GNU General Public License and 26 the GNU Lesser General Public License along with this program. If 27 not, see <http://www.gnu.org/licenses/>. */ 28 29 #ifdef HAVE_CONFIG_H 30 # include <config.h> 31 #endif 32 33 #include <libelf.h> 34 #include "libelfP.h" 35 #include "common.h" 36 37 int 38 elf_compress_gnu (Elf_Scn *scn, int inflate, unsigned int flags) 39 { 40 if (scn == NULL) 41 return -1; 42 43 if ((flags & ~ELF_CHF_FORCE) != 0) 44 { 45 __libelf_seterrno (ELF_E_INVALID_OPERAND); 46 return -1; 47 } 48 49 bool force = (flags & ELF_CHF_FORCE) != 0; 50 51 Elf *elf = scn->elf; 52 GElf_Ehdr ehdr; 53 if (gelf_getehdr (elf, &ehdr) == NULL) 54 return -1; 55 56 int elfclass = elf->class; 57 int elfdata = ehdr.e_ident[EI_DATA]; 58 59 Elf64_Xword sh_flags; 60 Elf64_Word sh_type; 61 Elf64_Xword sh_addralign; 62 if (elfclass == ELFCLASS32) 63 { 64 Elf32_Shdr *shdr = elf32_getshdr (scn); 65 if (shdr == NULL) 66 return -1; 67 68 sh_flags = shdr->sh_flags; 69 sh_type = shdr->sh_type; 70 sh_addralign = shdr->sh_addralign; 71 } 72 else 73 { 74 Elf64_Shdr *shdr = elf64_getshdr (scn); 75 if (shdr == NULL) 76 return -1; 77 78 sh_flags = shdr->sh_flags; 79 sh_type = shdr->sh_type; 80 sh_addralign = shdr->sh_addralign; 81 } 82 83 if ((sh_flags & SHF_ALLOC) != 0) 84 { 85 __libelf_seterrno (ELF_E_INVALID_SECTION_FLAGS); 86 return -1; 87 } 88 89 if (sh_type == SHT_NULL || sh_type == SHT_NOBITS) 90 { 91 __libelf_seterrno (ELF_E_INVALID_SECTION_TYPE); 92 return -1; 93 } 94 95 /* For GNU compression we cannot really know whether the section is 96 already compressed or not. Just try and see what happens... */ 97 // int compressed = (sh_flags & SHF_COMPRESSED); 98 if (inflate == 1) 99 { 100 size_t hsize = 4 + 8; /* GNU "ZLIB" + 8 byte size. */ 101 size_t orig_size, new_size, orig_addralign; 102 void *out_buf = __libelf_compress (scn, hsize, elfdata, 103 &orig_size, &orig_addralign, 104 &new_size, force); 105 106 /* Compression would make section larger, don't change anything. */ 107 if (out_buf == (void *) -1) 108 return 0; 109 110 /* Compression failed, return error. */ 111 if (out_buf == NULL) 112 return -1; 113 114 uint64_t be64_size = htobe64 (orig_size); 115 memmove (out_buf, "ZLIB", 4); 116 memmove (out_buf + 4, &be64_size, sizeof (be64_size)); 117 118 /* We don't know anything about sh_entsize, sh_addralign and 119 sh_flags won't have a SHF_COMPRESSED hint in the GNU format. 120 Just adjust the sh_size. */ 121 if (elfclass == ELFCLASS32) 122 { 123 Elf32_Shdr *shdr = elf32_getshdr (scn); 124 shdr->sh_size = new_size; 125 } 126 else 127 { 128 Elf64_Shdr *shdr = elf64_getshdr (scn); 129 shdr->sh_size = new_size; 130 } 131 132 __libelf_reset_rawdata (scn, out_buf, new_size, 1, ELF_T_BYTE); 133 134 /* The section is now compressed, we could keep the uncompressed 135 data around, but since that might have been multiple Elf_Data 136 buffers let the user uncompress it explicitly again if they 137 want it to simplify bookkeeping. */ 138 scn->zdata_base = NULL; 139 140 return 1; 141 } 142 else if (inflate == 0) 143 { 144 /* In theory the user could have constucted a compressed section 145 by hand. But we always just take the rawdata directly and 146 decompress that. */ 147 Elf_Data *data = elf_rawdata (scn, NULL); 148 if (data == NULL) 149 return -1; 150 151 size_t hsize = 4 + 8; /* GNU "ZLIB" + 8 byte size. */ 152 if (data->d_size < hsize || memcmp (data->d_buf, "ZLIB", 4) != 0) 153 { 154 __libelf_seterrno (ELF_E_NOT_COMPRESSED); 155 return -1; 156 } 157 158 /* There is a 12-byte header of "ZLIB" followed by 159 an 8-byte big-endian size. There is only one type and 160 Alignment isn't preserved separately. */ 161 uint64_t gsize; 162 memcpy (&gsize, data->d_buf + 4, sizeof gsize); 163 gsize = be64toh (gsize); 164 165 /* One more sanity check, size should be bigger than original 166 data size plus some overhead (4 chars ZLIB + 8 bytes size + 6 167 bytes zlib stream overhead + 5 bytes overhead max for one 16K 168 block) and should fit into a size_t. */ 169 if (gsize + 4 + 8 + 6 + 5 < data->d_size || gsize > SIZE_MAX) 170 { 171 __libelf_seterrno (ELF_E_NOT_COMPRESSED); 172 return -1; 173 } 174 175 size_t size = gsize; 176 size_t size_in = data->d_size - hsize; 177 void *buf_in = data->d_buf + hsize; 178 void *buf_out = __libelf_decompress (buf_in, size_in, size); 179 if (buf_out == NULL) 180 return -1; 181 182 /* We don't know anything about sh_entsize, sh_addralign and 183 sh_flags won't have a SHF_COMPRESSED hint in the GNU format. 184 Just adjust the sh_size. */ 185 if (elfclass == ELFCLASS32) 186 { 187 Elf32_Shdr *shdr = elf32_getshdr (scn); 188 shdr->sh_size = size; 189 } 190 else 191 { 192 Elf64_Shdr *shdr = elf64_getshdr (scn); 193 shdr->sh_size = size; 194 } 195 196 __libelf_reset_rawdata (scn, buf_out, size, sh_addralign, 197 __libelf_data_type (elf, sh_type)); 198 199 scn->zdata_base = buf_out; 200 201 return 1; 202 } 203 else 204 { 205 __libelf_seterrno (ELF_E_UNKNOWN_COMPRESSION_TYPE); 206 return -1; 207 } 208 } 209