|
Packit Service |
97d2fb |
/* Compress or decompress a section.
|
|
Packit Service |
97d2fb |
Copyright (C) 2015, 2016 Red Hat, Inc.
|
|
Packit Service |
97d2fb |
This file is part of elfutils.
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
This file is free software; you can redistribute it and/or modify
|
|
Packit Service |
97d2fb |
it under the terms of either
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
* the GNU Lesser General Public License as published by the Free
|
|
Packit Service |
97d2fb |
Software Foundation; either version 3 of the License, or (at
|
|
Packit Service |
97d2fb |
your option) any later version
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
or
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
* the GNU General Public License as published by the Free
|
|
Packit Service |
97d2fb |
Software Foundation; either version 2 of the License, or (at
|
|
Packit Service |
97d2fb |
your option) any later version
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
or both in parallel, as here.
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
elfutils is distributed in the hope that it will be useful, but
|
|
Packit Service |
97d2fb |
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit Service |
97d2fb |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Packit Service |
97d2fb |
General Public License for more details.
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
You should have received copies of the GNU General Public License and
|
|
Packit Service |
97d2fb |
the GNU Lesser General Public License along with this program. If
|
|
Packit Service |
97d2fb |
not, see <http://www.gnu.org/licenses/>. */
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
#ifdef HAVE_CONFIG_H
|
|
Packit Service |
97d2fb |
# include <config.h>
|
|
Packit Service |
97d2fb |
#endif
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
#include <libelf.h>
|
|
Packit Service |
97d2fb |
#include <system.h>
|
|
Packit Service |
97d2fb |
#include "libelfP.h"
|
|
Packit Service |
97d2fb |
#include "common.h"
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
#include <stddef.h>
|
|
Packit Service |
97d2fb |
#include <stdlib.h>
|
|
Packit Service |
97d2fb |
#include <string.h>
|
|
Packit Service |
97d2fb |
#include <unistd.h>
|
|
Packit Service |
97d2fb |
#include <zlib.h>
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* Cleanup and return result. Don't leak memory. */
|
|
Packit Service |
97d2fb |
static void *
|
|
Packit Service |
97d2fb |
do_deflate_cleanup (void *result, z_stream *z, void *out_buf,
|
|
Packit Service |
97d2fb |
Elf_Data *cdatap)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
deflateEnd (z);
|
|
Packit Service |
97d2fb |
free (out_buf);
|
|
Packit Service |
97d2fb |
if (cdatap != NULL)
|
|
Packit Service |
97d2fb |
free (cdatap->d_buf);
|
|
Packit Service |
97d2fb |
return result;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
#define deflate_cleanup(result, cdata) \
|
|
Packit Service |
97d2fb |
do_deflate_cleanup(result, &z, out_buf, cdata)
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* Given a section, uses the (in-memory) Elf_Data to extract the
|
|
Packit Service |
97d2fb |
original data size (including the given header size) and data
|
|
Packit Service |
97d2fb |
alignment. Returns a buffer that has at least hsize bytes (for the
|
|
Packit Service |
97d2fb |
caller to fill in with a header) plus zlib compressed date. Also
|
|
Packit Service |
97d2fb |
returns the new buffer size in new_size (hsize + compressed data
|
|
Packit Service |
97d2fb |
size). Returns (void *) -1 when FORCE is false and the compressed
|
|
Packit Service |
97d2fb |
data would be bigger than the original data. */
|
|
Packit Service |
97d2fb |
void *
|
|
Packit Service |
97d2fb |
internal_function
|
|
Packit Service |
97d2fb |
__libelf_compress (Elf_Scn *scn, size_t hsize, int ei_data,
|
|
Packit Service |
97d2fb |
size_t *orig_size, size_t *orig_addralign,
|
|
Packit Service |
97d2fb |
size_t *new_size, bool force)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
/* The compressed data is the on-disk data. We simplify the
|
|
Packit Service |
97d2fb |
implementation a bit by asking for the (converted) in-memory
|
|
Packit Service |
97d2fb |
data (which might be all there is if the user created it with
|
|
Packit Service |
97d2fb |
elf_newdata) and then convert back to raw if needed before
|
|
Packit Service |
97d2fb |
compressing. Should be made a bit more clever to directly
|
|
Packit Service |
97d2fb |
use raw if that is directly available. */
|
|
Packit Service |
97d2fb |
Elf_Data *data = elf_getdata (scn, NULL);
|
|
Packit Service |
97d2fb |
if (data == NULL)
|
|
Packit Service |
97d2fb |
return NULL;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* When not forced and we immediately know we would use more data by
|
|
Packit Service |
97d2fb |
compressing, because of the header plus zlib overhead (five bytes
|
|
Packit Service |
97d2fb |
per 16 KB block, plus a one-time overhead of six bytes for the
|
|
Packit Service |
97d2fb |
entire stream), don't do anything. */
|
|
Packit Service |
97d2fb |
Elf_Data *next_data = elf_getdata (scn, data);
|
|
Packit Service |
97d2fb |
if (next_data == NULL && !force
|
|
Packit Service |
97d2fb |
&& data->d_size <= hsize + 5 + 6)
|
|
Packit Service |
97d2fb |
return (void *) -1;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
*orig_addralign = data->d_align;
|
|
Packit Service |
97d2fb |
*orig_size = data->d_size;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* Guess an output block size. 1/8th of the original Elf_Data plus
|
|
Packit Service |
97d2fb |
hsize. Make the first chunk twice that size (25%), then increase
|
|
Packit Service |
97d2fb |
by a block (12.5%) when necessary. */
|
|
Packit Service |
97d2fb |
size_t block = (data->d_size / 8) + hsize;
|
|
Packit Service |
97d2fb |
size_t out_size = 2 * block;
|
|
Packit Service |
97d2fb |
void *out_buf = malloc (out_size);
|
|
Packit Service |
97d2fb |
if (out_buf == NULL)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
__libelf_seterrno (ELF_E_NOMEM);
|
|
Packit Service |
97d2fb |
return NULL;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* Caller gets to fill in the header at the start. Just skip it here. */
|
|
Packit Service |
97d2fb |
size_t used = hsize;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
z_stream z;
|
|
Packit Service |
97d2fb |
z.zalloc = Z_NULL;
|
|
Packit Service |
97d2fb |
z.zfree = Z_NULL;
|
|
Packit Service |
97d2fb |
z.opaque = Z_NULL;
|
|
Packit Service |
97d2fb |
int zrc = deflateInit (&z, Z_BEST_COMPRESSION);
|
|
Packit Service |
97d2fb |
if (zrc != Z_OK)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
__libelf_seterrno (ELF_E_COMPRESS_ERROR);
|
|
Packit Service |
97d2fb |
return deflate_cleanup(NULL, NULL);
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
Elf_Data cdata;
|
|
Packit Service |
97d2fb |
cdata.d_buf = NULL;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* Loop over data buffers. */
|
|
Packit Service |
97d2fb |
int flush = Z_NO_FLUSH;
|
|
Packit Service |
97d2fb |
do
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
/* Convert to raw if different endianess. */
|
|
Packit Service |
97d2fb |
cdata = *data;
|
|
Packit Service |
97d2fb |
bool convert = ei_data != MY_ELFDATA && data->d_size > 0;
|
|
Packit Service |
97d2fb |
if (convert)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
/* Don't do this conversion in place, we might want to keep
|
|
Packit Service |
97d2fb |
the original data around, caller decides. */
|
|
Packit Service |
97d2fb |
cdata.d_buf = malloc (data->d_size);
|
|
Packit Service |
97d2fb |
if (cdata.d_buf == NULL)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
__libelf_seterrno (ELF_E_NOMEM);
|
|
Packit Service |
97d2fb |
return deflate_cleanup (NULL, NULL);
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
if (gelf_xlatetof (scn->elf, &cdata, data, ei_data) == NULL)
|
|
Packit Service |
97d2fb |
return deflate_cleanup (NULL, &cdata);
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
z.avail_in = cdata.d_size;
|
|
Packit Service |
97d2fb |
z.next_in = cdata.d_buf;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* Get next buffer to see if this is the last one. */
|
|
Packit Service |
97d2fb |
data = next_data;
|
|
Packit Service |
97d2fb |
if (data != NULL)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
*orig_addralign = MAX (*orig_addralign, data->d_align);
|
|
Packit Service |
97d2fb |
*orig_size += data->d_size;
|
|
Packit Service |
97d2fb |
next_data = elf_getdata (scn, data);
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
else
|
|
Packit Service |
97d2fb |
flush = Z_FINISH;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* Flush one data buffer. */
|
|
Packit Service |
97d2fb |
do
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
z.avail_out = out_size - used;
|
|
Packit Service |
97d2fb |
z.next_out = out_buf + used;
|
|
Packit Service |
97d2fb |
zrc = deflate (&z, flush);
|
|
Packit Service |
97d2fb |
if (zrc == Z_STREAM_ERROR)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
__libelf_seterrno (ELF_E_COMPRESS_ERROR);
|
|
Packit Service |
97d2fb |
return deflate_cleanup (NULL, convert ? &cdata : NULL);
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
used += (out_size - used) - z.avail_out;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* Bail out if we are sure the user doesn't want the
|
|
Packit Service |
97d2fb |
compression forced and we are using more compressed data
|
|
Packit Service |
97d2fb |
than original data. */
|
|
Packit Service |
97d2fb |
if (!force && flush == Z_FINISH && used >= *orig_size)
|
|
Packit Service |
97d2fb |
return deflate_cleanup ((void *) -1, convert ? &cdata : NULL);
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
if (z.avail_out == 0)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
void *bigger = realloc (out_buf, out_size + block);
|
|
Packit Service |
97d2fb |
if (bigger == NULL)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
__libelf_seterrno (ELF_E_NOMEM);
|
|
Packit Service |
97d2fb |
return deflate_cleanup (NULL, convert ? &cdata : NULL);
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
out_buf = bigger;
|
|
Packit Service |
97d2fb |
out_size += block;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
while (z.avail_out == 0); /* Need more output buffer. */
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
if (convert)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
free (cdata.d_buf);
|
|
Packit Service |
97d2fb |
cdata.d_buf = NULL;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
while (flush != Z_FINISH); /* More data blocks. */
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
if (zrc != Z_STREAM_END)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
__libelf_seterrno (ELF_E_COMPRESS_ERROR);
|
|
Packit Service |
97d2fb |
return deflate_cleanup (NULL, NULL);
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
deflateEnd (&z);
|
|
Packit Service |
97d2fb |
*new_size = used;
|
|
Packit Service |
97d2fb |
return out_buf;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
void *
|
|
Packit Service |
97d2fb |
internal_function
|
|
Packit Service |
97d2fb |
__libelf_decompress (void *buf_in, size_t size_in, size_t size_out)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
/* Catch highly unlikely compression ratios so we don't allocate
|
|
Packit Service |
97d2fb |
some giant amount of memory for nothing. The max compression
|
|
Packit Service |
97d2fb |
factor 1032:1 comes from http://www.zlib.net/zlib_tech.html */
|
|
Packit Service |
97d2fb |
if (unlikely (size_out / 1032 > size_in))
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
__libelf_seterrno (ELF_E_INVALID_DATA);
|
|
Packit Service |
97d2fb |
return NULL;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* Malloc might return NULL when requestion zero size. This is highly
|
|
Packit Service |
97d2fb |
unlikely, it would only happen when the compression was forced.
|
|
Packit Service |
97d2fb |
But we do need a non-NULL buffer to return and set as result.
|
|
Packit Service |
97d2fb |
Just make sure to always allocate at least 1 byte. */
|
|
Packit Service |
97d2fb |
void *buf_out = malloc (size_out ?: 1);
|
|
Packit Service |
97d2fb |
if (unlikely (buf_out == NULL))
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
__libelf_seterrno (ELF_E_NOMEM);
|
|
Packit Service |
97d2fb |
return NULL;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
z_stream z =
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
.next_in = buf_in,
|
|
Packit Service |
97d2fb |
.avail_in = size_in,
|
|
Packit Service |
97d2fb |
.next_out = buf_out,
|
|
Packit Service |
97d2fb |
.avail_out = size_out
|
|
Packit Service |
97d2fb |
};
|
|
Packit Service |
97d2fb |
int zrc = inflateInit (&z);
|
|
Packit Service |
97d2fb |
while (z.avail_in > 0 && likely (zrc == Z_OK))
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
z.next_out = buf_out + (size_out - z.avail_out);
|
|
Packit Service |
97d2fb |
zrc = inflate (&z, Z_FINISH);
|
|
Packit Service |
97d2fb |
if (unlikely (zrc != Z_STREAM_END))
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
zrc = Z_DATA_ERROR;
|
|
Packit Service |
97d2fb |
break;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
zrc = inflateReset (&z);
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
if (unlikely (zrc != Z_OK) || unlikely (z.avail_out != 0))
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
free (buf_out);
|
|
Packit Service |
97d2fb |
buf_out = NULL;
|
|
Packit Service |
97d2fb |
__libelf_seterrno (ELF_E_DECOMPRESS_ERROR);
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
inflateEnd(&z);
|
|
Packit Service |
97d2fb |
return buf_out;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
void *
|
|
Packit Service |
97d2fb |
internal_function
|
|
Packit Service |
97d2fb |
__libelf_decompress_elf (Elf_Scn *scn, size_t *size_out, size_t *addralign)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
GElf_Chdr chdr;
|
|
Packit Service |
97d2fb |
if (gelf_getchdr (scn, &chdr) == NULL)
|
|
Packit Service |
97d2fb |
return NULL;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
if (chdr.ch_type != ELFCOMPRESS_ZLIB)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
__libelf_seterrno (ELF_E_UNKNOWN_COMPRESSION_TYPE);
|
|
Packit Service |
97d2fb |
return NULL;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
if (! powerof2 (chdr.ch_addralign))
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
__libelf_seterrno (ELF_E_INVALID_ALIGN);
|
|
Packit Service |
97d2fb |
return NULL;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* Take the in-memory representation, so we can even handle a
|
|
Packit Service |
97d2fb |
section that has just been constructed (maybe it was copied
|
|
Packit Service |
97d2fb |
over from some other ELF file first with elf_newdata). This
|
|
Packit Service |
97d2fb |
is slightly inefficient when the raw data needs to be
|
|
Packit Service |
97d2fb |
converted since then we'll be converting the whole buffer and
|
|
Packit Service |
97d2fb |
not just Chdr. */
|
|
Packit Service |
97d2fb |
Elf_Data *data = elf_getdata (scn, NULL);
|
|
Packit Service |
97d2fb |
if (data == NULL)
|
|
Packit Service |
97d2fb |
return NULL;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
int elfclass = scn->elf->class;
|
|
Packit Service |
97d2fb |
size_t hsize = (elfclass == ELFCLASS32
|
|
Packit Service |
97d2fb |
? sizeof (Elf32_Chdr) : sizeof (Elf64_Chdr));
|
|
Packit Service |
97d2fb |
size_t size_in = data->d_size - hsize;
|
|
Packit Service |
97d2fb |
void *buf_in = data->d_buf + hsize;
|
|
Packit Service |
97d2fb |
void *buf_out = __libelf_decompress (buf_in, size_in, chdr.ch_size);
|
|
Packit Service |
97d2fb |
*size_out = chdr.ch_size;
|
|
Packit Service |
97d2fb |
*addralign = chdr.ch_addralign;
|
|
Packit Service |
97d2fb |
return buf_out;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* Assumes buf is a malloced buffer. */
|
|
Packit Service |
97d2fb |
void
|
|
Packit Service |
97d2fb |
internal_function
|
|
Packit Service |
97d2fb |
__libelf_reset_rawdata (Elf_Scn *scn, void *buf, size_t size, size_t align,
|
|
Packit Service |
97d2fb |
Elf_Type type)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
/* This is the new raw data, replace and possibly free old data. */
|
|
Packit Service |
97d2fb |
scn->rawdata.d.d_off = 0;
|
|
Packit Service |
97d2fb |
scn->rawdata.d.d_version = EV_CURRENT;
|
|
Packit Service |
97d2fb |
scn->rawdata.d.d_buf = buf;
|
|
Packit Service |
97d2fb |
scn->rawdata.d.d_size = size;
|
|
Packit Service |
97d2fb |
scn->rawdata.d.d_align = align;
|
|
Packit Service |
97d2fb |
scn->rawdata.d.d_type = type;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* Existing existing data is no longer valid. */
|
|
Packit Service |
97d2fb |
scn->data_list_rear = NULL;
|
|
Packit Service |
97d2fb |
if (scn->data_base != scn->rawdata_base)
|
|
Packit Service |
97d2fb |
free (scn->data_base);
|
|
Packit Service |
97d2fb |
scn->data_base = NULL;
|
|
Packit Service |
97d2fb |
if (scn->elf->map_address == NULL
|
|
Packit Service |
97d2fb |
|| scn->rawdata_base == scn->zdata_base
|
|
Packit Service |
97d2fb |
|| (scn->flags & ELF_F_MALLOCED) != 0)
|
|
Packit Service |
97d2fb |
free (scn->rawdata_base);
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
scn->rawdata_base = buf;
|
|
Packit Service |
97d2fb |
scn->flags |= ELF_F_MALLOCED;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* Pretend we (tried to) read the data from the file and setup the
|
|
Packit Service |
97d2fb |
data (might have to convert the Chdr to native format). */
|
|
Packit Service |
97d2fb |
scn->data_read = 1;
|
|
Packit Service |
97d2fb |
scn->flags |= ELF_F_FILEDATA;
|
|
Packit Service |
97d2fb |
__libelf_set_data_list_rdlock (scn, 1);
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
int
|
|
Packit Service |
97d2fb |
elf_compress (Elf_Scn *scn, int type, unsigned int flags)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
if (scn == NULL)
|
|
Packit Service |
97d2fb |
return -1;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
if ((flags & ~ELF_CHF_FORCE) != 0)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
__libelf_seterrno (ELF_E_INVALID_OPERAND);
|
|
Packit Service |
97d2fb |
return -1;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
bool force = (flags & ELF_CHF_FORCE) != 0;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
Elf *elf = scn->elf;
|
|
Packit Service |
97d2fb |
GElf_Ehdr ehdr;
|
|
Packit Service |
97d2fb |
if (gelf_getehdr (elf, &ehdr) == NULL)
|
|
Packit Service |
97d2fb |
return -1;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
int elfclass = elf->class;
|
|
Packit Service |
97d2fb |
int elfdata = ehdr.e_ident[EI_DATA];
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
Elf64_Xword sh_flags;
|
|
Packit Service |
97d2fb |
Elf64_Word sh_type;
|
|
Packit Service |
97d2fb |
Elf64_Xword sh_addralign;
|
|
Packit Service |
97d2fb |
if (elfclass == ELFCLASS32)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
Elf32_Shdr *shdr = elf32_getshdr (scn);
|
|
Packit Service |
97d2fb |
if (shdr == NULL)
|
|
Packit Service |
97d2fb |
return -1;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
sh_flags = shdr->sh_flags;
|
|
Packit Service |
97d2fb |
sh_type = shdr->sh_type;
|
|
Packit Service |
97d2fb |
sh_addralign = shdr->sh_addralign;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
else
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
Elf64_Shdr *shdr = elf64_getshdr (scn);
|
|
Packit Service |
97d2fb |
if (shdr == NULL)
|
|
Packit Service |
97d2fb |
return -1;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
sh_flags = shdr->sh_flags;
|
|
Packit Service |
97d2fb |
sh_type = shdr->sh_type;
|
|
Packit Service |
97d2fb |
sh_addralign = shdr->sh_addralign;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
if ((sh_flags & SHF_ALLOC) != 0)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
__libelf_seterrno (ELF_E_INVALID_SECTION_FLAGS);
|
|
Packit Service |
97d2fb |
return -1;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
if (sh_type == SHT_NULL || sh_type == SHT_NOBITS)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
__libelf_seterrno (ELF_E_INVALID_SECTION_TYPE);
|
|
Packit Service |
97d2fb |
return -1;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
int compressed = (sh_flags & SHF_COMPRESSED);
|
|
Packit Service |
97d2fb |
if (type == ELFCOMPRESS_ZLIB)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
/* Compress/Deflate. */
|
|
Packit Service |
97d2fb |
if (compressed == 1)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
__libelf_seterrno (ELF_E_ALREADY_COMPRESSED);
|
|
Packit Service |
97d2fb |
return -1;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
size_t hsize = (elfclass == ELFCLASS32
|
|
Packit Service |
97d2fb |
? sizeof (Elf32_Chdr) : sizeof (Elf64_Chdr));
|
|
Packit Service |
97d2fb |
size_t orig_size, orig_addralign, new_size;
|
|
Packit Service |
97d2fb |
void *out_buf = __libelf_compress (scn, hsize, elfdata,
|
|
Packit Service |
97d2fb |
&orig_size, &orig_addralign,
|
|
Packit Service |
97d2fb |
&new_size, force);
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* Compression would make section larger, don't change anything. */
|
|
Packit Service |
97d2fb |
if (out_buf == (void *) -1)
|
|
Packit Service |
97d2fb |
return 0;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* Compression failed, return error. */
|
|
Packit Service |
97d2fb |
if (out_buf == NULL)
|
|
Packit Service |
97d2fb |
return -1;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* Put the header in front of the data. */
|
|
Packit Service |
97d2fb |
if (elfclass == ELFCLASS32)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
Elf32_Chdr chdr;
|
|
Packit Service |
97d2fb |
chdr.ch_type = ELFCOMPRESS_ZLIB;
|
|
Packit Service |
97d2fb |
chdr.ch_size = orig_size;
|
|
Packit Service |
97d2fb |
chdr.ch_addralign = orig_addralign;
|
|
Packit Service |
97d2fb |
if (elfdata != MY_ELFDATA)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
CONVERT (chdr.ch_type);
|
|
Packit Service |
97d2fb |
CONVERT (chdr.ch_size);
|
|
Packit Service |
97d2fb |
CONVERT (chdr.ch_addralign);
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
memcpy (out_buf, &chdr, sizeof (Elf32_Chdr));
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
else
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
Elf64_Chdr chdr;
|
|
Packit Service |
97d2fb |
chdr.ch_type = ELFCOMPRESS_ZLIB;
|
|
Packit Service |
97d2fb |
chdr.ch_reserved = 0;
|
|
Packit Service |
97d2fb |
chdr.ch_size = orig_size;
|
|
Packit Service |
97d2fb |
chdr.ch_addralign = sh_addralign;
|
|
Packit Service |
97d2fb |
if (elfdata != MY_ELFDATA)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
CONVERT (chdr.ch_type);
|
|
Packit Service |
97d2fb |
CONVERT (chdr.ch_reserved);
|
|
Packit Service |
97d2fb |
CONVERT (chdr.ch_size);
|
|
Packit Service |
97d2fb |
CONVERT (chdr.ch_addralign);
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
memcpy (out_buf, &chdr, sizeof (Elf64_Chdr));
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* Note we keep the sh_entsize as is, we assume it is setup
|
|
Packit Service |
97d2fb |
correctly and ignored when SHF_COMPRESSED is set. */
|
|
Packit Service |
97d2fb |
if (elfclass == ELFCLASS32)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
Elf32_Shdr *shdr = elf32_getshdr (scn);
|
|
Packit Service |
97d2fb |
shdr->sh_size = new_size;
|
|
Packit Service |
97d2fb |
shdr->sh_addralign = __libelf_type_align (ELFCLASS32, ELF_T_CHDR);
|
|
Packit Service |
97d2fb |
shdr->sh_flags |= SHF_COMPRESSED;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
else
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
Elf64_Shdr *shdr = elf64_getshdr (scn);
|
|
Packit Service |
97d2fb |
shdr->sh_size = new_size;
|
|
Packit Service |
97d2fb |
shdr->sh_addralign = __libelf_type_align (ELFCLASS64, ELF_T_CHDR);
|
|
Packit Service |
97d2fb |
shdr->sh_flags |= SHF_COMPRESSED;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
__libelf_reset_rawdata (scn, out_buf, new_size, 1, ELF_T_CHDR);
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* The section is now compressed, we could keep the uncompressed
|
|
Packit Service |
97d2fb |
data around, but since that might have been multiple Elf_Data
|
|
Packit Service |
97d2fb |
buffers let the user uncompress it explicitly again if they
|
|
Packit Service |
97d2fb |
want it to simplify bookkeeping. */
|
|
Packit Service |
97d2fb |
scn->zdata_base = NULL;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
return 1;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
else if (type == 0)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
/* Decompress/Inflate. */
|
|
Packit Service |
97d2fb |
if (compressed == 0)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
__libelf_seterrno (ELF_E_NOT_COMPRESSED);
|
|
Packit Service |
97d2fb |
return -1;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* If the data is already decompressed (by elf_strptr), then we
|
|
Packit Service |
97d2fb |
only need to setup the rawdata and section header. XXX what
|
|
Packit Service |
97d2fb |
about elf_newdata? */
|
|
Packit Service |
97d2fb |
if (scn->zdata_base == NULL)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
size_t size_out, addralign;
|
|
Packit Service |
97d2fb |
void *buf_out = __libelf_decompress_elf (scn, &size_out, &addralign);
|
|
Packit Service |
97d2fb |
if (buf_out == NULL)
|
|
Packit Service |
97d2fb |
return -1;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
scn->zdata_base = buf_out;
|
|
Packit Service |
97d2fb |
scn->zdata_size = size_out;
|
|
Packit Service |
97d2fb |
scn->zdata_align = addralign;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* Note we keep the sh_entsize as is, we assume it is setup
|
|
Packit Service |
97d2fb |
correctly and ignored when SHF_COMPRESSED is set. */
|
|
Packit Service |
97d2fb |
if (elfclass == ELFCLASS32)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
Elf32_Shdr *shdr = elf32_getshdr (scn);
|
|
Packit Service |
97d2fb |
shdr->sh_size = scn->zdata_size;
|
|
Packit Service |
97d2fb |
shdr->sh_addralign = scn->zdata_align;
|
|
Packit Service |
97d2fb |
shdr->sh_flags &= ~SHF_COMPRESSED;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
else
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
Elf64_Shdr *shdr = elf64_getshdr (scn);
|
|
Packit Service |
97d2fb |
shdr->sh_size = scn->zdata_size;
|
|
Packit Service |
97d2fb |
shdr->sh_addralign = scn->zdata_align;
|
|
Packit Service |
97d2fb |
shdr->sh_flags &= ~SHF_COMPRESSED;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
__libelf_reset_rawdata (scn, scn->zdata_base,
|
|
Packit Service |
97d2fb |
scn->zdata_size, scn->zdata_align,
|
|
Packit Service |
97d2fb |
__libelf_data_type (elf, sh_type,
|
|
Packit Service |
97d2fb |
scn->zdata_align));
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
return 1;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
else
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
__libelf_seterrno (ELF_E_UNKNOWN_COMPRESSION_TYPE);
|
|
Packit Service |
97d2fb |
return -1;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
}
|