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