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 /* Allocated sections, or sections that are already are compressed 84 cannot (also) be GNU compressed. */ 85 if ((sh_flags & SHF_ALLOC) != 0 || (sh_flags & SHF_COMPRESSED)) 86 { 87 __libelf_seterrno (ELF_E_INVALID_SECTION_FLAGS); 88 return -1; 89 } 90 91 if (sh_type == SHT_NULL || sh_type == SHT_NOBITS) 92 { 93 __libelf_seterrno (ELF_E_INVALID_SECTION_TYPE); 94 return -1; 95 } 96 97 /* For GNU compression we cannot really know whether the section is 98 already compressed or not. Just try and see what happens... */ 99 // int compressed = (sh_flags & SHF_COMPRESSED); 100 if (inflate == 1) 101 { 102 size_t hsize = 4 + 8; /* GNU "ZLIB" + 8 byte size. */ 103 size_t orig_size, new_size, orig_addralign; 104 void *out_buf = __libelf_compress (scn, hsize, elfdata, 105 &orig_size, &orig_addralign, 106 &new_size, force); 107 108 /* Compression would make section larger, don't change anything. */ 109 if (out_buf == (void *) -1) 110 return 0; 111 112 /* Compression failed, return error. */ 113 if (out_buf == NULL) 114 return -1; 115 116 uint64_t be64_size = htobe64 (orig_size); 117 memmove (out_buf, "ZLIB", 4); 118 memmove (out_buf + 4, &be64_size, sizeof (be64_size)); 119 120 /* We don't know anything about sh_entsize, sh_addralign and 121 sh_flags won't have a SHF_COMPRESSED hint in the GNU format. 122 Just adjust the sh_size. */ 123 if (elfclass == ELFCLASS32) 124 { 125 Elf32_Shdr *shdr = elf32_getshdr (scn); 126 shdr->sh_size = new_size; 127 } 128 else 129 { 130 Elf64_Shdr *shdr = elf64_getshdr (scn); 131 shdr->sh_size = new_size; 132 } 133 134 __libelf_reset_rawdata (scn, out_buf, new_size, 1, ELF_T_BYTE); 135 136 /* The section is now compressed, we could keep the uncompressed 137 data around, but since that might have been multiple Elf_Data 138 buffers let the user uncompress it explicitly again if they 139 want it to simplify bookkeeping. */ 140 scn->zdata_base = NULL; 141 142 return 1; 143 } 144 else if (inflate == 0) 145 { 146 /* In theory the user could have constucted a compressed section 147 by hand. And in practice they do. For example when copying 148 a section from one file to another using elf_newdata. So we 149 have to use elf_getdata (not elf_rawdata). */ 150 Elf_Data *data = elf_getdata (scn, NULL); 151 if (data == NULL) 152 return -1; 153 154 size_t hsize = 4 + 8; /* GNU "ZLIB" + 8 byte size. */ 155 if (data->d_size < hsize || memcmp (data->d_buf, "ZLIB", 4) != 0) 156 { 157 __libelf_seterrno (ELF_E_NOT_COMPRESSED); 158 return -1; 159 } 160 161 /* There is a 12-byte header of "ZLIB" followed by 162 an 8-byte big-endian size. There is only one type and 163 Alignment isn't preserved separately. */ 164 uint64_t gsize; 165 memcpy (&gsize, data->d_buf + 4, sizeof gsize); 166 gsize = be64toh (gsize); 167 168 /* One more sanity check, size should be bigger than original 169 data size plus some overhead (4 chars ZLIB + 8 bytes size + 6 170 bytes zlib stream overhead + 5 bytes overhead max for one 16K 171 block) and should fit into a size_t. */ 172 if (gsize + 4 + 8 + 6 + 5 < data->d_size || gsize > SIZE_MAX) 173 { 174 __libelf_seterrno (ELF_E_NOT_COMPRESSED); 175 return -1; 176 } 177 178 size_t size = gsize; 179 size_t size_in = data->d_size - hsize; 180 void *buf_in = data->d_buf + hsize; 181 void *buf_out = __libelf_decompress (buf_in, size_in, size); 182 if (buf_out == NULL) 183 return -1; 184 185 /* We don't know anything about sh_entsize, sh_addralign and 186 sh_flags won't have a SHF_COMPRESSED hint in the GNU format. 187 Just adjust the sh_size. */ 188 if (elfclass == ELFCLASS32) 189 { 190 Elf32_Shdr *shdr = elf32_getshdr (scn); 191 shdr->sh_size = size; 192 } 193 else 194 { 195 Elf64_Shdr *shdr = elf64_getshdr (scn); 196 shdr->sh_size = size; 197 } 198 199 __libelf_reset_rawdata (scn, buf_out, size, sh_addralign, 200 __libelf_data_type (elf, sh_type, sh_addralign)); 201 202 scn->zdata_base = buf_out; 203 204 return 1; 205 } 206 else 207 { 208 __libelf_seterrno (ELF_E_UNKNOWN_COMPRESSION_TYPE); 209 return -1; 210 } 211 } 212