1da0c48c4Sopenharmony_ci/* Compress or decompress a section. 2da0c48c4Sopenharmony_ci Copyright (C) 2015, 2016 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_ci#include <stddef.h> 38da0c48c4Sopenharmony_ci#include <stdlib.h> 39da0c48c4Sopenharmony_ci#include <string.h> 40da0c48c4Sopenharmony_ci#include <zlib.h> 41da0c48c4Sopenharmony_ci 42da0c48c4Sopenharmony_ci/* Cleanup and return result. Don't leak memory. */ 43da0c48c4Sopenharmony_cistatic void * 44da0c48c4Sopenharmony_cido_deflate_cleanup (void *result, z_stream *z, void *out_buf, 45da0c48c4Sopenharmony_ci Elf_Data *cdatap) 46da0c48c4Sopenharmony_ci{ 47da0c48c4Sopenharmony_ci deflateEnd (z); 48da0c48c4Sopenharmony_ci free (out_buf); 49da0c48c4Sopenharmony_ci if (cdatap != NULL) 50da0c48c4Sopenharmony_ci free (cdatap->d_buf); 51da0c48c4Sopenharmony_ci return result; 52da0c48c4Sopenharmony_ci} 53da0c48c4Sopenharmony_ci 54da0c48c4Sopenharmony_ci#define deflate_cleanup(result, cdata) \ 55da0c48c4Sopenharmony_ci do_deflate_cleanup(result, &z, out_buf, cdata) 56da0c48c4Sopenharmony_ci 57da0c48c4Sopenharmony_ci/* Given a section, uses the (in-memory) Elf_Data to extract the 58da0c48c4Sopenharmony_ci original data size (including the given header size) and data 59da0c48c4Sopenharmony_ci alignment. Returns a buffer that has at least hsize bytes (for the 60da0c48c4Sopenharmony_ci caller to fill in with a header) plus zlib compressed date. Also 61da0c48c4Sopenharmony_ci returns the new buffer size in new_size (hsize + compressed data 62da0c48c4Sopenharmony_ci size). Returns (void *) -1 when FORCE is false and the compressed 63da0c48c4Sopenharmony_ci data would be bigger than the original data. */ 64da0c48c4Sopenharmony_civoid * 65da0c48c4Sopenharmony_ciinternal_function 66da0c48c4Sopenharmony_ci__libelf_compress (Elf_Scn *scn, size_t hsize, int ei_data, 67da0c48c4Sopenharmony_ci size_t *orig_size, size_t *orig_addralign, 68da0c48c4Sopenharmony_ci size_t *new_size, bool force) 69da0c48c4Sopenharmony_ci{ 70da0c48c4Sopenharmony_ci /* The compressed data is the on-disk data. We simplify the 71da0c48c4Sopenharmony_ci implementation a bit by asking for the (converted) in-memory 72da0c48c4Sopenharmony_ci data (which might be all there is if the user created it with 73da0c48c4Sopenharmony_ci elf_newdata) and then convert back to raw if needed before 74da0c48c4Sopenharmony_ci compressing. Should be made a bit more clever to directly 75da0c48c4Sopenharmony_ci use raw if that is directly available. */ 76da0c48c4Sopenharmony_ci Elf_Data *data = elf_getdata (scn, NULL); 77da0c48c4Sopenharmony_ci if (data == NULL) 78da0c48c4Sopenharmony_ci return NULL; 79da0c48c4Sopenharmony_ci 80da0c48c4Sopenharmony_ci /* When not forced and we immediately know we would use more data by 81da0c48c4Sopenharmony_ci compressing, because of the header plus zlib overhead (five bytes 82da0c48c4Sopenharmony_ci per 16 KB block, plus a one-time overhead of six bytes for the 83da0c48c4Sopenharmony_ci entire stream), don't do anything. */ 84da0c48c4Sopenharmony_ci Elf_Data *next_data = elf_getdata (scn, data); 85da0c48c4Sopenharmony_ci if (next_data == NULL && !force 86da0c48c4Sopenharmony_ci && data->d_size <= hsize + 5 + 6) 87da0c48c4Sopenharmony_ci return (void *) -1; 88da0c48c4Sopenharmony_ci 89da0c48c4Sopenharmony_ci *orig_addralign = data->d_align; 90da0c48c4Sopenharmony_ci *orig_size = data->d_size; 91da0c48c4Sopenharmony_ci 92da0c48c4Sopenharmony_ci /* Guess an output block size. 1/8th of the original Elf_Data plus 93da0c48c4Sopenharmony_ci hsize. Make the first chunk twice that size (25%), then increase 94da0c48c4Sopenharmony_ci by a block (12.5%) when necessary. */ 95da0c48c4Sopenharmony_ci size_t block = (data->d_size / 8) + hsize; 96da0c48c4Sopenharmony_ci size_t out_size = 2 * block; 97da0c48c4Sopenharmony_ci void *out_buf = malloc (out_size); 98da0c48c4Sopenharmony_ci if (out_buf == NULL) 99da0c48c4Sopenharmony_ci { 100da0c48c4Sopenharmony_ci __libelf_seterrno (ELF_E_NOMEM); 101da0c48c4Sopenharmony_ci return NULL; 102da0c48c4Sopenharmony_ci } 103da0c48c4Sopenharmony_ci 104da0c48c4Sopenharmony_ci /* Caller gets to fill in the header at the start. Just skip it here. */ 105da0c48c4Sopenharmony_ci size_t used = hsize; 106da0c48c4Sopenharmony_ci 107da0c48c4Sopenharmony_ci z_stream z; 108da0c48c4Sopenharmony_ci z.zalloc = Z_NULL; 109da0c48c4Sopenharmony_ci z.zfree = Z_NULL; 110da0c48c4Sopenharmony_ci z.opaque = Z_NULL; 111da0c48c4Sopenharmony_ci int zrc = deflateInit (&z, Z_BEST_COMPRESSION); 112da0c48c4Sopenharmony_ci if (zrc != Z_OK) 113da0c48c4Sopenharmony_ci { 114da0c48c4Sopenharmony_ci __libelf_seterrno (ELF_E_COMPRESS_ERROR); 115da0c48c4Sopenharmony_ci return deflate_cleanup(NULL, NULL); 116da0c48c4Sopenharmony_ci } 117da0c48c4Sopenharmony_ci 118da0c48c4Sopenharmony_ci Elf_Data cdata; 119da0c48c4Sopenharmony_ci cdata.d_buf = NULL; 120da0c48c4Sopenharmony_ci 121da0c48c4Sopenharmony_ci /* Loop over data buffers. */ 122da0c48c4Sopenharmony_ci int flush = Z_NO_FLUSH; 123da0c48c4Sopenharmony_ci do 124da0c48c4Sopenharmony_ci { 125da0c48c4Sopenharmony_ci /* Convert to raw if different endianness. */ 126da0c48c4Sopenharmony_ci cdata = *data; 127da0c48c4Sopenharmony_ci bool convert = ei_data != MY_ELFDATA && data->d_size > 0; 128da0c48c4Sopenharmony_ci if (convert) 129da0c48c4Sopenharmony_ci { 130da0c48c4Sopenharmony_ci /* Don't do this conversion in place, we might want to keep 131da0c48c4Sopenharmony_ci the original data around, caller decides. */ 132da0c48c4Sopenharmony_ci cdata.d_buf = malloc (data->d_size); 133da0c48c4Sopenharmony_ci if (cdata.d_buf == NULL) 134da0c48c4Sopenharmony_ci { 135da0c48c4Sopenharmony_ci __libelf_seterrno (ELF_E_NOMEM); 136da0c48c4Sopenharmony_ci return deflate_cleanup (NULL, NULL); 137da0c48c4Sopenharmony_ci } 138da0c48c4Sopenharmony_ci if (gelf_xlatetof (scn->elf, &cdata, data, ei_data) == NULL) 139da0c48c4Sopenharmony_ci return deflate_cleanup (NULL, &cdata); 140da0c48c4Sopenharmony_ci } 141da0c48c4Sopenharmony_ci 142da0c48c4Sopenharmony_ci z.avail_in = cdata.d_size; 143da0c48c4Sopenharmony_ci z.next_in = cdata.d_buf; 144da0c48c4Sopenharmony_ci 145da0c48c4Sopenharmony_ci /* Get next buffer to see if this is the last one. */ 146da0c48c4Sopenharmony_ci data = next_data; 147da0c48c4Sopenharmony_ci if (data != NULL) 148da0c48c4Sopenharmony_ci { 149da0c48c4Sopenharmony_ci *orig_addralign = MAX (*orig_addralign, data->d_align); 150da0c48c4Sopenharmony_ci *orig_size += data->d_size; 151da0c48c4Sopenharmony_ci next_data = elf_getdata (scn, data); 152da0c48c4Sopenharmony_ci } 153da0c48c4Sopenharmony_ci else 154da0c48c4Sopenharmony_ci flush = Z_FINISH; 155da0c48c4Sopenharmony_ci 156da0c48c4Sopenharmony_ci /* Flush one data buffer. */ 157da0c48c4Sopenharmony_ci do 158da0c48c4Sopenharmony_ci { 159da0c48c4Sopenharmony_ci z.avail_out = out_size - used; 160da0c48c4Sopenharmony_ci z.next_out = out_buf + used; 161da0c48c4Sopenharmony_ci zrc = deflate (&z, flush); 162da0c48c4Sopenharmony_ci if (zrc == Z_STREAM_ERROR) 163da0c48c4Sopenharmony_ci { 164da0c48c4Sopenharmony_ci __libelf_seterrno (ELF_E_COMPRESS_ERROR); 165da0c48c4Sopenharmony_ci return deflate_cleanup (NULL, convert ? &cdata : NULL); 166da0c48c4Sopenharmony_ci } 167da0c48c4Sopenharmony_ci used += (out_size - used) - z.avail_out; 168da0c48c4Sopenharmony_ci 169da0c48c4Sopenharmony_ci /* Bail out if we are sure the user doesn't want the 170da0c48c4Sopenharmony_ci compression forced and we are using more compressed data 171da0c48c4Sopenharmony_ci than original data. */ 172da0c48c4Sopenharmony_ci if (!force && flush == Z_FINISH && used >= *orig_size) 173da0c48c4Sopenharmony_ci return deflate_cleanup ((void *) -1, convert ? &cdata : NULL); 174da0c48c4Sopenharmony_ci 175da0c48c4Sopenharmony_ci if (z.avail_out == 0) 176da0c48c4Sopenharmony_ci { 177da0c48c4Sopenharmony_ci void *bigger = realloc (out_buf, out_size + block); 178da0c48c4Sopenharmony_ci if (bigger == NULL) 179da0c48c4Sopenharmony_ci { 180da0c48c4Sopenharmony_ci __libelf_seterrno (ELF_E_NOMEM); 181da0c48c4Sopenharmony_ci return deflate_cleanup (NULL, convert ? &cdata : NULL); 182da0c48c4Sopenharmony_ci } 183da0c48c4Sopenharmony_ci out_buf = bigger; 184da0c48c4Sopenharmony_ci out_size += block; 185da0c48c4Sopenharmony_ci } 186da0c48c4Sopenharmony_ci } 187da0c48c4Sopenharmony_ci while (z.avail_out == 0); /* Need more output buffer. */ 188da0c48c4Sopenharmony_ci 189da0c48c4Sopenharmony_ci if (convert) 190da0c48c4Sopenharmony_ci { 191da0c48c4Sopenharmony_ci free (cdata.d_buf); 192da0c48c4Sopenharmony_ci cdata.d_buf = NULL; 193da0c48c4Sopenharmony_ci } 194da0c48c4Sopenharmony_ci } 195da0c48c4Sopenharmony_ci while (flush != Z_FINISH); /* More data blocks. */ 196da0c48c4Sopenharmony_ci 197da0c48c4Sopenharmony_ci if (zrc != Z_STREAM_END) 198da0c48c4Sopenharmony_ci { 199da0c48c4Sopenharmony_ci __libelf_seterrno (ELF_E_COMPRESS_ERROR); 200da0c48c4Sopenharmony_ci return deflate_cleanup (NULL, NULL); 201da0c48c4Sopenharmony_ci } 202da0c48c4Sopenharmony_ci 203da0c48c4Sopenharmony_ci deflateEnd (&z); 204da0c48c4Sopenharmony_ci *new_size = used; 205da0c48c4Sopenharmony_ci return out_buf; 206da0c48c4Sopenharmony_ci} 207da0c48c4Sopenharmony_ci 208da0c48c4Sopenharmony_civoid * 209da0c48c4Sopenharmony_ciinternal_function 210da0c48c4Sopenharmony_ci__libelf_decompress (void *buf_in, size_t size_in, size_t size_out) 211da0c48c4Sopenharmony_ci{ 212da0c48c4Sopenharmony_ci /* Catch highly unlikely compression ratios so we don't allocate 213da0c48c4Sopenharmony_ci some giant amount of memory for nothing. The max compression 214da0c48c4Sopenharmony_ci factor 1032:1 comes from http://www.zlib.net/zlib_tech.html */ 215da0c48c4Sopenharmony_ci if (unlikely (size_out / 1032 > size_in)) 216da0c48c4Sopenharmony_ci { 217da0c48c4Sopenharmony_ci __libelf_seterrno (ELF_E_INVALID_DATA); 218da0c48c4Sopenharmony_ci return NULL; 219da0c48c4Sopenharmony_ci } 220da0c48c4Sopenharmony_ci 221da0c48c4Sopenharmony_ci /* Malloc might return NULL when requestion zero size. This is highly 222da0c48c4Sopenharmony_ci unlikely, it would only happen when the compression was forced. 223da0c48c4Sopenharmony_ci But we do need a non-NULL buffer to return and set as result. 224da0c48c4Sopenharmony_ci Just make sure to always allocate at least 1 byte. */ 225da0c48c4Sopenharmony_ci void *buf_out = malloc (size_out ?: 1); 226da0c48c4Sopenharmony_ci if (unlikely (buf_out == NULL)) 227da0c48c4Sopenharmony_ci { 228da0c48c4Sopenharmony_ci __libelf_seterrno (ELF_E_NOMEM); 229da0c48c4Sopenharmony_ci return NULL; 230da0c48c4Sopenharmony_ci } 231da0c48c4Sopenharmony_ci 232da0c48c4Sopenharmony_ci z_stream z = 233da0c48c4Sopenharmony_ci { 234da0c48c4Sopenharmony_ci .next_in = buf_in, 235da0c48c4Sopenharmony_ci .avail_in = size_in, 236da0c48c4Sopenharmony_ci .next_out = buf_out, 237da0c48c4Sopenharmony_ci .avail_out = size_out 238da0c48c4Sopenharmony_ci }; 239da0c48c4Sopenharmony_ci int zrc = inflateInit (&z); 240da0c48c4Sopenharmony_ci while (z.avail_in > 0 && likely (zrc == Z_OK)) 241da0c48c4Sopenharmony_ci { 242da0c48c4Sopenharmony_ci z.next_out = buf_out + (size_out - z.avail_out); 243da0c48c4Sopenharmony_ci zrc = inflate (&z, Z_FINISH); 244da0c48c4Sopenharmony_ci if (unlikely (zrc != Z_STREAM_END)) 245da0c48c4Sopenharmony_ci { 246da0c48c4Sopenharmony_ci zrc = Z_DATA_ERROR; 247da0c48c4Sopenharmony_ci break; 248da0c48c4Sopenharmony_ci } 249da0c48c4Sopenharmony_ci zrc = inflateReset (&z); 250da0c48c4Sopenharmony_ci } 251da0c48c4Sopenharmony_ci 252da0c48c4Sopenharmony_ci if (unlikely (zrc != Z_OK) || unlikely (z.avail_out != 0)) 253da0c48c4Sopenharmony_ci { 254da0c48c4Sopenharmony_ci free (buf_out); 255da0c48c4Sopenharmony_ci buf_out = NULL; 256da0c48c4Sopenharmony_ci __libelf_seterrno (ELF_E_DECOMPRESS_ERROR); 257da0c48c4Sopenharmony_ci } 258da0c48c4Sopenharmony_ci 259da0c48c4Sopenharmony_ci inflateEnd(&z); 260da0c48c4Sopenharmony_ci return buf_out; 261da0c48c4Sopenharmony_ci} 262da0c48c4Sopenharmony_ci 263da0c48c4Sopenharmony_civoid * 264da0c48c4Sopenharmony_ciinternal_function 265da0c48c4Sopenharmony_ci__libelf_decompress_elf (Elf_Scn *scn, size_t *size_out, size_t *addralign) 266da0c48c4Sopenharmony_ci{ 267da0c48c4Sopenharmony_ci GElf_Chdr chdr; 268da0c48c4Sopenharmony_ci if (gelf_getchdr (scn, &chdr) == NULL) 269da0c48c4Sopenharmony_ci return NULL; 270da0c48c4Sopenharmony_ci 271da0c48c4Sopenharmony_ci if (chdr.ch_type != ELFCOMPRESS_ZLIB) 272da0c48c4Sopenharmony_ci { 273da0c48c4Sopenharmony_ci __libelf_seterrno (ELF_E_UNKNOWN_COMPRESSION_TYPE); 274da0c48c4Sopenharmony_ci return NULL; 275da0c48c4Sopenharmony_ci } 276da0c48c4Sopenharmony_ci 277da0c48c4Sopenharmony_ci if (! powerof2 (chdr.ch_addralign)) 278da0c48c4Sopenharmony_ci { 279da0c48c4Sopenharmony_ci __libelf_seterrno (ELF_E_INVALID_ALIGN); 280da0c48c4Sopenharmony_ci return NULL; 281da0c48c4Sopenharmony_ci } 282da0c48c4Sopenharmony_ci 283da0c48c4Sopenharmony_ci /* Take the in-memory representation, so we can even handle a 284da0c48c4Sopenharmony_ci section that has just been constructed (maybe it was copied 285da0c48c4Sopenharmony_ci over from some other ELF file first with elf_newdata). This 286da0c48c4Sopenharmony_ci is slightly inefficient when the raw data needs to be 287da0c48c4Sopenharmony_ci converted since then we'll be converting the whole buffer and 288da0c48c4Sopenharmony_ci not just Chdr. */ 289da0c48c4Sopenharmony_ci Elf_Data *data = elf_getdata (scn, NULL); 290da0c48c4Sopenharmony_ci if (data == NULL) 291da0c48c4Sopenharmony_ci return NULL; 292da0c48c4Sopenharmony_ci 293da0c48c4Sopenharmony_ci int elfclass = scn->elf->class; 294da0c48c4Sopenharmony_ci size_t hsize = (elfclass == ELFCLASS32 295da0c48c4Sopenharmony_ci ? sizeof (Elf32_Chdr) : sizeof (Elf64_Chdr)); 296da0c48c4Sopenharmony_ci size_t size_in = data->d_size - hsize; 297da0c48c4Sopenharmony_ci void *buf_in = data->d_buf + hsize; 298da0c48c4Sopenharmony_ci void *buf_out = __libelf_decompress (buf_in, size_in, chdr.ch_size); 299da0c48c4Sopenharmony_ci *size_out = chdr.ch_size; 300da0c48c4Sopenharmony_ci *addralign = chdr.ch_addralign; 301da0c48c4Sopenharmony_ci return buf_out; 302da0c48c4Sopenharmony_ci} 303da0c48c4Sopenharmony_ci 304da0c48c4Sopenharmony_ci/* Assumes buf is a malloced buffer. */ 305da0c48c4Sopenharmony_civoid 306da0c48c4Sopenharmony_ciinternal_function 307da0c48c4Sopenharmony_ci__libelf_reset_rawdata (Elf_Scn *scn, void *buf, size_t size, size_t align, 308da0c48c4Sopenharmony_ci Elf_Type type) 309da0c48c4Sopenharmony_ci{ 310da0c48c4Sopenharmony_ci /* This is the new raw data, replace and possibly free old data. */ 311da0c48c4Sopenharmony_ci scn->rawdata.d.d_off = 0; 312da0c48c4Sopenharmony_ci scn->rawdata.d.d_version = EV_CURRENT; 313da0c48c4Sopenharmony_ci scn->rawdata.d.d_buf = buf; 314da0c48c4Sopenharmony_ci scn->rawdata.d.d_size = size; 315da0c48c4Sopenharmony_ci scn->rawdata.d.d_align = align; 316da0c48c4Sopenharmony_ci scn->rawdata.d.d_type = type; 317da0c48c4Sopenharmony_ci 318da0c48c4Sopenharmony_ci /* Existing existing data is no longer valid. */ 319da0c48c4Sopenharmony_ci scn->data_list_rear = NULL; 320da0c48c4Sopenharmony_ci if (scn->data_base != scn->rawdata_base) 321da0c48c4Sopenharmony_ci free (scn->data_base); 322da0c48c4Sopenharmony_ci scn->data_base = NULL; 323da0c48c4Sopenharmony_ci if (scn->elf->map_address == NULL 324da0c48c4Sopenharmony_ci || scn->rawdata_base == scn->zdata_base 325da0c48c4Sopenharmony_ci || (scn->flags & ELF_F_MALLOCED) != 0) 326da0c48c4Sopenharmony_ci free (scn->rawdata_base); 327da0c48c4Sopenharmony_ci 328da0c48c4Sopenharmony_ci scn->rawdata_base = buf; 329da0c48c4Sopenharmony_ci scn->flags |= ELF_F_MALLOCED; 330da0c48c4Sopenharmony_ci 331da0c48c4Sopenharmony_ci /* Pretend we (tried to) read the data from the file and setup the 332da0c48c4Sopenharmony_ci data (might have to convert the Chdr to native format). */ 333da0c48c4Sopenharmony_ci scn->data_read = 1; 334da0c48c4Sopenharmony_ci scn->flags |= ELF_F_FILEDATA; 335da0c48c4Sopenharmony_ci __libelf_set_data_list_rdlock (scn, 1); 336da0c48c4Sopenharmony_ci} 337da0c48c4Sopenharmony_ci 338da0c48c4Sopenharmony_ciint 339da0c48c4Sopenharmony_cielf_compress (Elf_Scn *scn, int type, unsigned int flags) 340da0c48c4Sopenharmony_ci{ 341da0c48c4Sopenharmony_ci if (scn == NULL) 342da0c48c4Sopenharmony_ci return -1; 343da0c48c4Sopenharmony_ci 344da0c48c4Sopenharmony_ci if ((flags & ~ELF_CHF_FORCE) != 0) 345da0c48c4Sopenharmony_ci { 346da0c48c4Sopenharmony_ci __libelf_seterrno (ELF_E_INVALID_OPERAND); 347da0c48c4Sopenharmony_ci return -1; 348da0c48c4Sopenharmony_ci } 349da0c48c4Sopenharmony_ci 350da0c48c4Sopenharmony_ci bool force = (flags & ELF_CHF_FORCE) != 0; 351da0c48c4Sopenharmony_ci 352da0c48c4Sopenharmony_ci Elf *elf = scn->elf; 353da0c48c4Sopenharmony_ci GElf_Ehdr ehdr; 354da0c48c4Sopenharmony_ci if (gelf_getehdr (elf, &ehdr) == NULL) 355da0c48c4Sopenharmony_ci return -1; 356da0c48c4Sopenharmony_ci 357da0c48c4Sopenharmony_ci int elfclass = elf->class; 358da0c48c4Sopenharmony_ci int elfdata = ehdr.e_ident[EI_DATA]; 359da0c48c4Sopenharmony_ci 360da0c48c4Sopenharmony_ci Elf64_Xword sh_flags; 361da0c48c4Sopenharmony_ci Elf64_Word sh_type; 362da0c48c4Sopenharmony_ci Elf64_Xword sh_addralign; 363da0c48c4Sopenharmony_ci if (elfclass == ELFCLASS32) 364da0c48c4Sopenharmony_ci { 365da0c48c4Sopenharmony_ci Elf32_Shdr *shdr = elf32_getshdr (scn); 366da0c48c4Sopenharmony_ci if (shdr == NULL) 367da0c48c4Sopenharmony_ci return -1; 368da0c48c4Sopenharmony_ci 369da0c48c4Sopenharmony_ci sh_flags = shdr->sh_flags; 370da0c48c4Sopenharmony_ci sh_type = shdr->sh_type; 371da0c48c4Sopenharmony_ci sh_addralign = shdr->sh_addralign; 372da0c48c4Sopenharmony_ci } 373da0c48c4Sopenharmony_ci else 374da0c48c4Sopenharmony_ci { 375da0c48c4Sopenharmony_ci Elf64_Shdr *shdr = elf64_getshdr (scn); 376da0c48c4Sopenharmony_ci if (shdr == NULL) 377da0c48c4Sopenharmony_ci return -1; 378da0c48c4Sopenharmony_ci 379da0c48c4Sopenharmony_ci sh_flags = shdr->sh_flags; 380da0c48c4Sopenharmony_ci sh_type = shdr->sh_type; 381da0c48c4Sopenharmony_ci sh_addralign = shdr->sh_addralign; 382da0c48c4Sopenharmony_ci } 383da0c48c4Sopenharmony_ci 384da0c48c4Sopenharmony_ci if ((sh_flags & SHF_ALLOC) != 0) 385da0c48c4Sopenharmony_ci { 386da0c48c4Sopenharmony_ci __libelf_seterrno (ELF_E_INVALID_SECTION_FLAGS); 387da0c48c4Sopenharmony_ci return -1; 388da0c48c4Sopenharmony_ci } 389da0c48c4Sopenharmony_ci 390da0c48c4Sopenharmony_ci if (sh_type == SHT_NULL || sh_type == SHT_NOBITS) 391da0c48c4Sopenharmony_ci { 392da0c48c4Sopenharmony_ci __libelf_seterrno (ELF_E_INVALID_SECTION_TYPE); 393da0c48c4Sopenharmony_ci return -1; 394da0c48c4Sopenharmony_ci } 395da0c48c4Sopenharmony_ci 396da0c48c4Sopenharmony_ci int compressed = (sh_flags & SHF_COMPRESSED); 397da0c48c4Sopenharmony_ci if (type == ELFCOMPRESS_ZLIB) 398da0c48c4Sopenharmony_ci { 399da0c48c4Sopenharmony_ci /* Compress/Deflate. */ 400da0c48c4Sopenharmony_ci if (compressed == 1) 401da0c48c4Sopenharmony_ci { 402da0c48c4Sopenharmony_ci __libelf_seterrno (ELF_E_ALREADY_COMPRESSED); 403da0c48c4Sopenharmony_ci return -1; 404da0c48c4Sopenharmony_ci } 405da0c48c4Sopenharmony_ci 406da0c48c4Sopenharmony_ci size_t hsize = (elfclass == ELFCLASS32 407da0c48c4Sopenharmony_ci ? sizeof (Elf32_Chdr) : sizeof (Elf64_Chdr)); 408da0c48c4Sopenharmony_ci size_t orig_size, orig_addralign, new_size; 409da0c48c4Sopenharmony_ci void *out_buf = __libelf_compress (scn, hsize, elfdata, 410da0c48c4Sopenharmony_ci &orig_size, &orig_addralign, 411da0c48c4Sopenharmony_ci &new_size, force); 412da0c48c4Sopenharmony_ci 413da0c48c4Sopenharmony_ci /* Compression would make section larger, don't change anything. */ 414da0c48c4Sopenharmony_ci if (out_buf == (void *) -1) 415da0c48c4Sopenharmony_ci return 0; 416da0c48c4Sopenharmony_ci 417da0c48c4Sopenharmony_ci /* Compression failed, return error. */ 418da0c48c4Sopenharmony_ci if (out_buf == NULL) 419da0c48c4Sopenharmony_ci return -1; 420da0c48c4Sopenharmony_ci 421da0c48c4Sopenharmony_ci /* Put the header in front of the data. */ 422da0c48c4Sopenharmony_ci if (elfclass == ELFCLASS32) 423da0c48c4Sopenharmony_ci { 424da0c48c4Sopenharmony_ci Elf32_Chdr chdr; 425da0c48c4Sopenharmony_ci chdr.ch_type = ELFCOMPRESS_ZLIB; 426da0c48c4Sopenharmony_ci chdr.ch_size = orig_size; 427da0c48c4Sopenharmony_ci chdr.ch_addralign = orig_addralign; 428da0c48c4Sopenharmony_ci if (elfdata != MY_ELFDATA) 429da0c48c4Sopenharmony_ci { 430da0c48c4Sopenharmony_ci CONVERT (chdr.ch_type); 431da0c48c4Sopenharmony_ci CONVERT (chdr.ch_size); 432da0c48c4Sopenharmony_ci CONVERT (chdr.ch_addralign); 433da0c48c4Sopenharmony_ci } 434da0c48c4Sopenharmony_ci memcpy (out_buf, &chdr, sizeof (Elf32_Chdr)); 435da0c48c4Sopenharmony_ci } 436da0c48c4Sopenharmony_ci else 437da0c48c4Sopenharmony_ci { 438da0c48c4Sopenharmony_ci Elf64_Chdr chdr; 439da0c48c4Sopenharmony_ci chdr.ch_type = ELFCOMPRESS_ZLIB; 440da0c48c4Sopenharmony_ci chdr.ch_reserved = 0; 441da0c48c4Sopenharmony_ci chdr.ch_size = orig_size; 442da0c48c4Sopenharmony_ci chdr.ch_addralign = sh_addralign; 443da0c48c4Sopenharmony_ci if (elfdata != MY_ELFDATA) 444da0c48c4Sopenharmony_ci { 445da0c48c4Sopenharmony_ci CONVERT (chdr.ch_type); 446da0c48c4Sopenharmony_ci CONVERT (chdr.ch_reserved); 447da0c48c4Sopenharmony_ci CONVERT (chdr.ch_size); 448da0c48c4Sopenharmony_ci CONVERT (chdr.ch_addralign); 449da0c48c4Sopenharmony_ci } 450da0c48c4Sopenharmony_ci memcpy (out_buf, &chdr, sizeof (Elf64_Chdr)); 451da0c48c4Sopenharmony_ci } 452da0c48c4Sopenharmony_ci 453da0c48c4Sopenharmony_ci /* Note we keep the sh_entsize as is, we assume it is setup 454da0c48c4Sopenharmony_ci correctly and ignored when SHF_COMPRESSED is set. */ 455da0c48c4Sopenharmony_ci if (elfclass == ELFCLASS32) 456da0c48c4Sopenharmony_ci { 457da0c48c4Sopenharmony_ci Elf32_Shdr *shdr = elf32_getshdr (scn); 458da0c48c4Sopenharmony_ci shdr->sh_size = new_size; 459da0c48c4Sopenharmony_ci shdr->sh_addralign = __libelf_type_align (ELFCLASS32, ELF_T_CHDR); 460da0c48c4Sopenharmony_ci shdr->sh_flags |= SHF_COMPRESSED; 461da0c48c4Sopenharmony_ci } 462da0c48c4Sopenharmony_ci else 463da0c48c4Sopenharmony_ci { 464da0c48c4Sopenharmony_ci Elf64_Shdr *shdr = elf64_getshdr (scn); 465da0c48c4Sopenharmony_ci shdr->sh_size = new_size; 466da0c48c4Sopenharmony_ci shdr->sh_addralign = __libelf_type_align (ELFCLASS64, ELF_T_CHDR); 467da0c48c4Sopenharmony_ci shdr->sh_flags |= SHF_COMPRESSED; 468da0c48c4Sopenharmony_ci } 469da0c48c4Sopenharmony_ci 470da0c48c4Sopenharmony_ci __libelf_reset_rawdata (scn, out_buf, new_size, 1, ELF_T_CHDR); 471da0c48c4Sopenharmony_ci 472da0c48c4Sopenharmony_ci /* The section is now compressed, we could keep the uncompressed 473da0c48c4Sopenharmony_ci data around, but since that might have been multiple Elf_Data 474da0c48c4Sopenharmony_ci buffers let the user uncompress it explicitly again if they 475da0c48c4Sopenharmony_ci want it to simplify bookkeeping. */ 476da0c48c4Sopenharmony_ci scn->zdata_base = NULL; 477da0c48c4Sopenharmony_ci 478da0c48c4Sopenharmony_ci return 1; 479da0c48c4Sopenharmony_ci } 480da0c48c4Sopenharmony_ci else if (type == 0) 481da0c48c4Sopenharmony_ci { 482da0c48c4Sopenharmony_ci /* Decompress/Inflate. */ 483da0c48c4Sopenharmony_ci if (compressed == 0) 484da0c48c4Sopenharmony_ci { 485da0c48c4Sopenharmony_ci __libelf_seterrno (ELF_E_NOT_COMPRESSED); 486da0c48c4Sopenharmony_ci return -1; 487da0c48c4Sopenharmony_ci } 488da0c48c4Sopenharmony_ci 489da0c48c4Sopenharmony_ci /* If the data is already decompressed (by elf_strptr), then we 490da0c48c4Sopenharmony_ci only need to setup the rawdata and section header. XXX what 491da0c48c4Sopenharmony_ci about elf_newdata? */ 492da0c48c4Sopenharmony_ci if (scn->zdata_base == NULL) 493da0c48c4Sopenharmony_ci { 494da0c48c4Sopenharmony_ci size_t size_out, addralign; 495da0c48c4Sopenharmony_ci void *buf_out = __libelf_decompress_elf (scn, &size_out, &addralign); 496da0c48c4Sopenharmony_ci if (buf_out == NULL) 497da0c48c4Sopenharmony_ci return -1; 498da0c48c4Sopenharmony_ci 499da0c48c4Sopenharmony_ci scn->zdata_base = buf_out; 500da0c48c4Sopenharmony_ci scn->zdata_size = size_out; 501da0c48c4Sopenharmony_ci scn->zdata_align = addralign; 502da0c48c4Sopenharmony_ci } 503da0c48c4Sopenharmony_ci 504da0c48c4Sopenharmony_ci /* Note we keep the sh_entsize as is, we assume it is setup 505da0c48c4Sopenharmony_ci correctly and ignored when SHF_COMPRESSED is set. */ 506da0c48c4Sopenharmony_ci if (elfclass == ELFCLASS32) 507da0c48c4Sopenharmony_ci { 508da0c48c4Sopenharmony_ci Elf32_Shdr *shdr = elf32_getshdr (scn); 509da0c48c4Sopenharmony_ci shdr->sh_size = scn->zdata_size; 510da0c48c4Sopenharmony_ci shdr->sh_addralign = scn->zdata_align; 511da0c48c4Sopenharmony_ci shdr->sh_flags &= ~SHF_COMPRESSED; 512da0c48c4Sopenharmony_ci } 513da0c48c4Sopenharmony_ci else 514da0c48c4Sopenharmony_ci { 515da0c48c4Sopenharmony_ci Elf64_Shdr *shdr = elf64_getshdr (scn); 516da0c48c4Sopenharmony_ci shdr->sh_size = scn->zdata_size; 517da0c48c4Sopenharmony_ci shdr->sh_addralign = scn->zdata_align; 518da0c48c4Sopenharmony_ci shdr->sh_flags &= ~SHF_COMPRESSED; 519da0c48c4Sopenharmony_ci } 520da0c48c4Sopenharmony_ci 521da0c48c4Sopenharmony_ci __libelf_reset_rawdata (scn, scn->zdata_base, 522da0c48c4Sopenharmony_ci scn->zdata_size, scn->zdata_align, 523da0c48c4Sopenharmony_ci __libelf_data_type (&ehdr, sh_type, 524da0c48c4Sopenharmony_ci scn->zdata_align)); 525da0c48c4Sopenharmony_ci 526da0c48c4Sopenharmony_ci return 1; 527da0c48c4Sopenharmony_ci } 528da0c48c4Sopenharmony_ci else 529da0c48c4Sopenharmony_ci { 530da0c48c4Sopenharmony_ci __libelf_seterrno (ELF_E_UNKNOWN_COMPRESSION_TYPE); 531da0c48c4Sopenharmony_ci return -1; 532da0c48c4Sopenharmony_ci } 533da0c48c4Sopenharmony_ci} 534