123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663 |
- /* Compressed section support (intended for debug sections).
- Copyright (C) 2008-2022 Free Software Foundation, Inc.
- This file is part of BFD, the Binary File Descriptor library.
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License, or
- (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
- MA 02110-1301, USA. */
- #include "sysdep.h"
- #include <zlib.h>
- #include "bfd.h"
- #include "libbfd.h"
- #include "safe-ctype.h"
- #define MAX_COMPRESSION_HEADER_SIZE 24
- static bool
- decompress_contents (bfd_byte *compressed_buffer,
- bfd_size_type compressed_size,
- bfd_byte *uncompressed_buffer,
- bfd_size_type uncompressed_size)
- {
- z_stream strm;
- int rc;
- /* It is possible the section consists of several compressed
- buffers concatenated together, so we uncompress in a loop. */
- /* PR 18313: The state field in the z_stream structure is supposed
- to be invisible to the user (ie us), but some compilers will
- still complain about it being used without initialisation. So
- we first zero the entire z_stream structure and then set the fields
- that we need. */
- memset (& strm, 0, sizeof strm);
- strm.avail_in = compressed_size;
- strm.next_in = (Bytef*) compressed_buffer;
- strm.avail_out = uncompressed_size;
- /* FIXME: strm.avail_in and strm.avail_out are typically unsigned
- int. Supporting sizes that don't fit in an unsigned int is
- possible but will require some rewriting of this function. */
- if (strm.avail_in != compressed_size || strm.avail_out != uncompressed_size)
- return false;
- BFD_ASSERT (Z_OK == 0);
- rc = inflateInit (&strm);
- while (strm.avail_in > 0 && strm.avail_out > 0)
- {
- if (rc != Z_OK)
- break;
- strm.next_out = ((Bytef*) uncompressed_buffer
- + (uncompressed_size - strm.avail_out));
- rc = inflate (&strm, Z_FINISH);
- if (rc != Z_STREAM_END)
- break;
- rc = inflateReset (&strm);
- }
- return inflateEnd (&strm) == Z_OK && rc == Z_OK && strm.avail_out == 0;
- }
- /* Compress data of the size specified in @var{uncompressed_size}
- and pointed to by @var{uncompressed_buffer} using zlib and store
- as the contents field. This function assumes the contents
- field was allocated using bfd_malloc() or equivalent.
- Return the uncompressed size if the full section contents is
- compressed successfully. Otherwise return 0. */
- static bfd_size_type
- bfd_compress_section_contents (bfd *abfd, sec_ptr sec,
- bfd_byte *uncompressed_buffer,
- bfd_size_type uncompressed_size)
- {
- uLong compressed_size;
- bfd_byte *buffer;
- bfd_size_type buffer_size;
- bool decompress;
- int zlib_size = 0;
- int orig_compression_header_size;
- bfd_size_type orig_uncompressed_size;
- unsigned int orig_uncompressed_alignment_pow;
- int header_size = bfd_get_compression_header_size (abfd, NULL);
- bool compressed
- = bfd_is_section_compressed_with_header (abfd, sec,
- &orig_compression_header_size,
- &orig_uncompressed_size,
- &orig_uncompressed_alignment_pow);
- /* Either ELF compression header or the 12-byte, "ZLIB" + 8-byte size,
- overhead in .zdebug* section. */
- if (!header_size)
- header_size = 12;
- if (compressed)
- {
- /* We shouldn't decompress unsupported compressed section. */
- if (orig_compression_header_size < 0)
- abort ();
- /* Different compression schemes. Just move the compressed section
- contents to the right position. */
- if (orig_compression_header_size == 0)
- {
- /* Convert it from .zdebug* section. Get the uncompressed
- size first. We need to subtract the 12-byte overhead in
- .zdebug* section. Set orig_compression_header_size to
- the 12-bye overhead. */
- orig_compression_header_size = 12;
- zlib_size = uncompressed_size - 12;
- }
- else
- {
- /* Convert it to .zdebug* section. */
- zlib_size = uncompressed_size - orig_compression_header_size;
- }
- /* Add the header size. */
- compressed_size = zlib_size + header_size;
- }
- else
- compressed_size = compressBound (uncompressed_size) + header_size;
- /* Uncompress if it leads to smaller size. */
- if (compressed && compressed_size > orig_uncompressed_size)
- {
- decompress = true;
- buffer_size = orig_uncompressed_size;
- }
- else
- {
- decompress = false;
- buffer_size = compressed_size;
- }
- buffer = (bfd_byte *) bfd_alloc (abfd, buffer_size);
- if (buffer == NULL)
- return 0;
- if (compressed)
- {
- sec->size = orig_uncompressed_size;
- if (decompress)
- {
- if (!decompress_contents (uncompressed_buffer
- + orig_compression_header_size,
- zlib_size, buffer, buffer_size))
- {
- bfd_set_error (bfd_error_bad_value);
- bfd_release (abfd, buffer);
- return 0;
- }
- free (uncompressed_buffer);
- bfd_set_section_alignment (sec, orig_uncompressed_alignment_pow);
- sec->contents = buffer;
- sec->compress_status = COMPRESS_SECTION_DONE;
- return orig_uncompressed_size;
- }
- else
- {
- bfd_update_compression_header (abfd, buffer, sec);
- memmove (buffer + header_size,
- uncompressed_buffer + orig_compression_header_size,
- zlib_size);
- }
- }
- else
- {
- if (compress ((Bytef*) buffer + header_size,
- &compressed_size,
- (const Bytef*) uncompressed_buffer,
- uncompressed_size) != Z_OK)
- {
- bfd_release (abfd, buffer);
- bfd_set_error (bfd_error_bad_value);
- return 0;
- }
- compressed_size += header_size;
- /* PR binutils/18087: If compression didn't make the section smaller,
- just keep it uncompressed. */
- if (compressed_size < uncompressed_size)
- bfd_update_compression_header (abfd, buffer, sec);
- else
- {
- /* NOTE: There is a small memory leak here since
- uncompressed_buffer is malloced and won't be freed. */
- bfd_release (abfd, buffer);
- sec->contents = uncompressed_buffer;
- sec->compress_status = COMPRESS_SECTION_NONE;
- return uncompressed_size;
- }
- }
- free (uncompressed_buffer);
- sec->contents = buffer;
- sec->size = compressed_size;
- sec->compress_status = COMPRESS_SECTION_DONE;
- return uncompressed_size;
- }
- /*
- FUNCTION
- bfd_get_full_section_contents
- SYNOPSIS
- bool bfd_get_full_section_contents
- (bfd *abfd, asection *section, bfd_byte **ptr);
- DESCRIPTION
- Read all data from @var{section} in BFD @var{abfd}, decompress
- if needed, and store in @var{*ptr}. If @var{*ptr} is NULL,
- return @var{*ptr} with memory malloc'd by this function.
- Return @code{TRUE} if the full section contents is retrieved
- successfully. If the section has no contents then this function
- returns @code{TRUE} but @var{*ptr} is set to NULL.
- */
- bool
- bfd_get_full_section_contents (bfd *abfd, sec_ptr sec, bfd_byte **ptr)
- {
- bfd_size_type sz;
- bfd_byte *p = *ptr;
- bool ret;
- bfd_size_type save_size;
- bfd_size_type save_rawsize;
- bfd_byte *compressed_buffer;
- unsigned int compression_header_size;
- if (abfd->direction != write_direction && sec->rawsize != 0)
- sz = sec->rawsize;
- else
- sz = sec->size;
- if (sz == 0)
- {
- *ptr = NULL;
- return true;
- }
- switch (sec->compress_status)
- {
- case COMPRESS_SECTION_NONE:
- if (p == NULL)
- {
- ufile_ptr filesize = bfd_get_file_size (abfd);
- if (filesize > 0
- && filesize < sz
- /* PR 24753: Linker created sections can be larger than
- the file size, eg if they are being used to hold stubs. */
- && (bfd_section_flags (sec) & SEC_LINKER_CREATED) == 0
- /* PR 24753: Sections which have no content should also be
- excluded as they contain no size on disk. */
- && (bfd_section_flags (sec) & SEC_HAS_CONTENTS) != 0
- /* The MMO file format supports its own special compression
- technique, but it uses COMPRESS_SECTION_NONE when loading
- a section's contents. */
- && bfd_get_flavour (abfd) != bfd_target_mmo_flavour)
- {
- /* PR 24708: Avoid attempts to allocate a ridiculous amount
- of memory. */
- bfd_set_error (bfd_error_file_truncated);
- _bfd_error_handler
- /* xgettext:c-format */
- (_("error: %pB(%pA) section size (%#" PRIx64 " bytes) is larger than file size (%#" PRIx64 " bytes)"),
- abfd, sec, (uint64_t) sz, (uint64_t) filesize);
- return false;
- }
- p = (bfd_byte *) bfd_malloc (sz);
- if (p == NULL)
- {
- /* PR 20801: Provide a more helpful error message. */
- if (bfd_get_error () == bfd_error_no_memory)
- _bfd_error_handler
- /* xgettext:c-format */
- (_("error: %pB(%pA) is too large (%#" PRIx64 " bytes)"),
- abfd, sec, (uint64_t) sz);
- return false;
- }
- }
- if (!bfd_get_section_contents (abfd, sec, p, 0, sz))
- {
- if (*ptr != p)
- free (p);
- return false;
- }
- *ptr = p;
- return true;
- case DECOMPRESS_SECTION_SIZED:
- /* Read in the full compressed section contents. */
- compressed_buffer = (bfd_byte *) bfd_malloc (sec->compressed_size);
- if (compressed_buffer == NULL)
- return false;
- save_rawsize = sec->rawsize;
- save_size = sec->size;
- /* Clear rawsize, set size to compressed size and set compress_status
- to COMPRESS_SECTION_NONE. If the compressed size is bigger than
- the uncompressed size, bfd_get_section_contents will fail. */
- sec->rawsize = 0;
- sec->size = sec->compressed_size;
- sec->compress_status = COMPRESS_SECTION_NONE;
- ret = bfd_get_section_contents (abfd, sec, compressed_buffer,
- 0, sec->compressed_size);
- /* Restore rawsize and size. */
- sec->rawsize = save_rawsize;
- sec->size = save_size;
- sec->compress_status = DECOMPRESS_SECTION_SIZED;
- if (!ret)
- goto fail_compressed;
- if (p == NULL)
- p = (bfd_byte *) bfd_malloc (sz);
- if (p == NULL)
- goto fail_compressed;
- compression_header_size = bfd_get_compression_header_size (abfd, sec);
- if (compression_header_size == 0)
- /* Set header size to the zlib header size if it is a
- SHF_COMPRESSED section. */
- compression_header_size = 12;
- if (!decompress_contents (compressed_buffer + compression_header_size,
- sec->compressed_size - compression_header_size, p, sz))
- {
- bfd_set_error (bfd_error_bad_value);
- if (p != *ptr)
- free (p);
- fail_compressed:
- free (compressed_buffer);
- return false;
- }
- free (compressed_buffer);
- *ptr = p;
- return true;
- case COMPRESS_SECTION_DONE:
- if (sec->contents == NULL)
- return false;
- if (p == NULL)
- {
- p = (bfd_byte *) bfd_malloc (sz);
- if (p == NULL)
- return false;
- *ptr = p;
- }
- /* PR 17512; file: 5bc29788. */
- if (p != sec->contents)
- memcpy (p, sec->contents, sz);
- return true;
- default:
- abort ();
- }
- }
- /*
- FUNCTION
- bfd_cache_section_contents
- SYNOPSIS
- void bfd_cache_section_contents
- (asection *sec, void *contents);
- DESCRIPTION
- Stash @var(contents) so any following reads of @var(sec) do
- not need to decompress again.
- */
- void
- bfd_cache_section_contents (asection *sec, void *contents)
- {
- if (sec->compress_status == DECOMPRESS_SECTION_SIZED)
- sec->compress_status = COMPRESS_SECTION_DONE;
- sec->contents = contents;
- sec->flags |= SEC_IN_MEMORY;
- }
- /*
- FUNCTION
- bfd_is_section_compressed_with_header
- SYNOPSIS
- bool bfd_is_section_compressed_with_header
- (bfd *abfd, asection *section,
- int *compression_header_size_p,
- bfd_size_type *uncompressed_size_p,
- unsigned int *uncompressed_alignment_power_p);
- DESCRIPTION
- Return @code{TRUE} if @var{section} is compressed. Compression
- header size is returned in @var{compression_header_size_p},
- uncompressed size is returned in @var{uncompressed_size_p}
- and the uncompressed data alignement power is returned in
- @var{uncompressed_align_pow_p}. If compression is
- unsupported, compression header size is returned with -1
- and uncompressed size is returned with 0.
- */
- bool
- bfd_is_section_compressed_with_header (bfd *abfd, sec_ptr sec,
- int *compression_header_size_p,
- bfd_size_type *uncompressed_size_p,
- unsigned int *uncompressed_align_pow_p)
- {
- bfd_byte header[MAX_COMPRESSION_HEADER_SIZE];
- int compression_header_size;
- int header_size;
- unsigned int saved = sec->compress_status;
- bool compressed;
- *uncompressed_align_pow_p = 0;
- compression_header_size = bfd_get_compression_header_size (abfd, sec);
- if (compression_header_size > MAX_COMPRESSION_HEADER_SIZE)
- abort ();
- header_size = compression_header_size ? compression_header_size : 12;
- /* Don't decompress the section. */
- sec->compress_status = COMPRESS_SECTION_NONE;
- /* Read the header. */
- if (bfd_get_section_contents (abfd, sec, header, 0, header_size))
- {
- if (compression_header_size == 0)
- /* In this case, it should be "ZLIB" followed by the uncompressed
- section size, 8 bytes in big-endian order. */
- compressed = startswith ((char*) header , "ZLIB");
- else
- compressed = true;
- }
- else
- compressed = false;
- *uncompressed_size_p = sec->size;
- if (compressed)
- {
- if (compression_header_size != 0)
- {
- if (!bfd_check_compression_header (abfd, header, sec,
- uncompressed_size_p,
- uncompressed_align_pow_p))
- compression_header_size = -1;
- }
- /* Check for the pathalogical case of a debug string section that
- contains the string ZLIB.... as the first entry. We assume that
- no uncompressed .debug_str section would ever be big enough to
- have the first byte of its (big-endian) size be non-zero. */
- else if (strcmp (sec->name, ".debug_str") == 0
- && ISPRINT (header[4]))
- compressed = false;
- else
- *uncompressed_size_p = bfd_getb64 (header + 4);
- }
- /* Restore compress_status. */
- sec->compress_status = saved;
- *compression_header_size_p = compression_header_size;
- return compressed;
- }
- /*
- FUNCTION
- bfd_is_section_compressed
- SYNOPSIS
- bool bfd_is_section_compressed
- (bfd *abfd, asection *section);
- DESCRIPTION
- Return @code{TRUE} if @var{section} is compressed.
- */
- bool
- bfd_is_section_compressed (bfd *abfd, sec_ptr sec)
- {
- int compression_header_size;
- bfd_size_type uncompressed_size;
- unsigned int uncompressed_align_power;
- return (bfd_is_section_compressed_with_header (abfd, sec,
- &compression_header_size,
- &uncompressed_size,
- &uncompressed_align_power)
- && compression_header_size >= 0
- && uncompressed_size > 0);
- }
- /*
- FUNCTION
- bfd_init_section_decompress_status
- SYNOPSIS
- bool bfd_init_section_decompress_status
- (bfd *abfd, asection *section);
- DESCRIPTION
- Record compressed section size, update section size with
- decompressed size and set compress_status to
- DECOMPRESS_SECTION_SIZED.
- Return @code{FALSE} if the section is not a valid compressed
- section. Otherwise, return @code{TRUE}.
- */
- bool
- bfd_init_section_decompress_status (bfd *abfd, sec_ptr sec)
- {
- bfd_byte header[MAX_COMPRESSION_HEADER_SIZE];
- int compression_header_size;
- int header_size;
- bfd_size_type uncompressed_size;
- unsigned int uncompressed_alignment_power = 0;
- z_stream strm;
- compression_header_size = bfd_get_compression_header_size (abfd, sec);
- if (compression_header_size > MAX_COMPRESSION_HEADER_SIZE)
- abort ();
- header_size = compression_header_size ? compression_header_size : 12;
- /* Read the header. */
- if (sec->rawsize != 0
- || sec->contents != NULL
- || sec->compress_status != COMPRESS_SECTION_NONE
- || !bfd_get_section_contents (abfd, sec, header, 0, header_size))
- {
- bfd_set_error (bfd_error_invalid_operation);
- return false;
- }
- if (compression_header_size == 0)
- {
- /* In this case, it should be "ZLIB" followed by the uncompressed
- section size, 8 bytes in big-endian order. */
- if (! startswith ((char*) header, "ZLIB"))
- {
- bfd_set_error (bfd_error_wrong_format);
- return false;
- }
- uncompressed_size = bfd_getb64 (header + 4);
- }
- else if (!bfd_check_compression_header (abfd, header, sec,
- &uncompressed_size,
- &uncompressed_alignment_power))
- {
- bfd_set_error (bfd_error_wrong_format);
- return false;
- }
- /* PR28530, reject sizes unsupported by decompress_contents. */
- strm.avail_in = sec->size;
- strm.avail_out = uncompressed_size;
- if (strm.avail_in != sec->size || strm.avail_out != uncompressed_size)
- {
- bfd_set_error (bfd_error_nonrepresentable_section);
- return false;
- }
- sec->compressed_size = sec->size;
- sec->size = uncompressed_size;
- bfd_set_section_alignment (sec, uncompressed_alignment_power);
- sec->compress_status = DECOMPRESS_SECTION_SIZED;
- return true;
- }
- /*
- FUNCTION
- bfd_init_section_compress_status
- SYNOPSIS
- bool bfd_init_section_compress_status
- (bfd *abfd, asection *section);
- DESCRIPTION
- If open for read, compress section, update section size with
- compressed size and set compress_status to COMPRESS_SECTION_DONE.
- Return @code{FALSE} if the section is not a valid compressed
- section. Otherwise, return @code{TRUE}.
- */
- bool
- bfd_init_section_compress_status (bfd *abfd, sec_ptr sec)
- {
- bfd_size_type uncompressed_size;
- bfd_byte *uncompressed_buffer;
- /* Error if not opened for read. */
- if (abfd->direction != read_direction
- || sec->size == 0
- || sec->rawsize != 0
- || sec->contents != NULL
- || sec->compress_status != COMPRESS_SECTION_NONE)
- {
- bfd_set_error (bfd_error_invalid_operation);
- return false;
- }
- /* Read in the full section contents and compress it. */
- uncompressed_size = sec->size;
- uncompressed_buffer = (bfd_byte *) bfd_malloc (uncompressed_size);
- /* PR 21431 */
- if (uncompressed_buffer == NULL)
- return false;
- if (!bfd_get_section_contents (abfd, sec, uncompressed_buffer,
- 0, uncompressed_size))
- return false;
- uncompressed_size = bfd_compress_section_contents (abfd, sec,
- uncompressed_buffer,
- uncompressed_size);
- return uncompressed_size != 0;
- }
- /*
- FUNCTION
- bfd_compress_section
- SYNOPSIS
- bool bfd_compress_section
- (bfd *abfd, asection *section, bfd_byte *uncompressed_buffer);
- DESCRIPTION
- If open for write, compress section, update section size with
- compressed size and set compress_status to COMPRESS_SECTION_DONE.
- Return @code{FALSE} if compression fail. Otherwise, return
- @code{TRUE}.
- */
- bool
- bfd_compress_section (bfd *abfd, sec_ptr sec, bfd_byte *uncompressed_buffer)
- {
- bfd_size_type uncompressed_size = sec->size;
- /* Error if not opened for write. */
- if (abfd->direction != write_direction
- || uncompressed_size == 0
- || uncompressed_buffer == NULL
- || sec->contents != NULL
- || sec->compressed_size != 0
- || sec->compress_status != COMPRESS_SECTION_NONE)
- {
- bfd_set_error (bfd_error_invalid_operation);
- return false;
- }
- /* Compress it. */
- return bfd_compress_section_contents (abfd, sec, uncompressed_buffer,
- uncompressed_size) != 0;
- }
|