| /* Compressed section support (intended for debug sections). |
| Copyright 2008, 2010 |
| 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 "config.h" |
| #include "sysdep.h" |
| #include "bfd.h" |
| #include "libbfd.h" |
| #ifdef HAVE_ZLIB_H |
| #include <zlib.h> |
| #endif |
| |
| #ifdef HAVE_ZLIB_H |
| static bfd_boolean |
| 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. */ |
| strm.zalloc = NULL; |
| strm.zfree = NULL; |
| strm.opaque = NULL; |
| strm.avail_in = compressed_size - 12; |
| strm.next_in = (Bytef*) compressed_buffer + 12; |
| strm.avail_out = uncompressed_size; |
| |
| rc = inflateInit (&strm); |
| while (strm.avail_in > 0) |
| { |
| if (rc != Z_OK) |
| return FALSE; |
| strm.next_out = ((Bytef*) uncompressed_buffer |
| + (uncompressed_size - strm.avail_out)); |
| rc = inflate (&strm, Z_FINISH); |
| if (rc != Z_STREAM_END) |
| return FALSE; |
| rc = inflateReset (&strm); |
| } |
| rc = inflateEnd (&strm); |
| return rc != Z_OK || strm.avail_out != 0 ? FALSE: TRUE; |
| } |
| #endif |
| |
| /* |
| FUNCTION |
| bfd_compress_section_contents |
| |
| SYNOPSIS |
| bfd_boolean bfd_compress_section_contents |
| (bfd *abfd, asection *section, bfd_byte *uncompressed_buffer, |
| bfd_size_type uncompressed_size); |
| |
| DESCRIPTION |
| |
| 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. If zlib |
| is not installed on this machine, the input is unmodified. |
| |
| Return @code{TRUE} if the full section contents is compressed |
| successfully. |
| */ |
| |
| bfd_boolean |
| bfd_compress_section_contents (bfd *abfd ATTRIBUTE_UNUSED, |
| sec_ptr sec ATTRIBUTE_UNUSED, |
| bfd_byte *uncompressed_buffer ATTRIBUTE_UNUSED, |
| bfd_size_type uncompressed_size ATTRIBUTE_UNUSED) |
| { |
| #ifndef HAVE_ZLIB_H |
| bfd_set_error (bfd_error_invalid_operation); |
| return FALSE; |
| #else |
| uLong compressed_size; |
| bfd_byte *compressed_buffer; |
| |
| compressed_size = compressBound (uncompressed_size) + 12; |
| compressed_buffer = (bfd_byte *) bfd_malloc (compressed_size); |
| |
| if (compress ((Bytef*) compressed_buffer + 12, |
| &compressed_size, |
| (const Bytef*) uncompressed_buffer, |
| uncompressed_size) != Z_OK) |
| { |
| free (compressed_buffer); |
| bfd_set_error (bfd_error_bad_value); |
| return FALSE; |
| } |
| |
| /* Write the zlib header. In this case, it should be "ZLIB" followed |
| by the uncompressed section size, 8 bytes in big-endian order. */ |
| memcpy (compressed_buffer, "ZLIB", 4); |
| compressed_buffer[11] = uncompressed_size; uncompressed_size >>= 8; |
| compressed_buffer[10] = uncompressed_size; uncompressed_size >>= 8; |
| compressed_buffer[9] = uncompressed_size; uncompressed_size >>= 8; |
| compressed_buffer[8] = uncompressed_size; uncompressed_size >>= 8; |
| compressed_buffer[7] = uncompressed_size; uncompressed_size >>= 8; |
| compressed_buffer[6] = uncompressed_size; uncompressed_size >>= 8; |
| compressed_buffer[5] = uncompressed_size; uncompressed_size >>= 8; |
| compressed_buffer[4] = uncompressed_size; |
| compressed_size += 12; |
| |
| /* Free the uncompressed contents if we compress in place. */ |
| if (uncompressed_buffer == sec->contents) |
| free (uncompressed_buffer); |
| |
| sec->contents = compressed_buffer; |
| sec->size = compressed_size; |
| sec->compress_status = COMPRESS_SECTION_DONE; |
| |
| return TRUE; |
| #endif /* HAVE_ZLIB_H */ |
| } |
| |
| /* |
| FUNCTION |
| bfd_get_full_section_contents |
| |
| SYNOPSIS |
| bfd_boolean 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. |
| */ |
| |
| bfd_boolean |
| bfd_get_full_section_contents (bfd *abfd, sec_ptr sec, bfd_byte **ptr) |
| { |
| bfd_size_type sz = sec->rawsize ? sec->rawsize : sec->size; |
| bfd_byte *p = *ptr; |
| bfd_boolean need_free, ret; |
| #ifdef HAVE_ZLIB_H |
| bfd_size_type compressed_size; |
| bfd_size_type uncompressed_size; |
| bfd_size_type rawsize; |
| bfd_byte *compressed_buffer; |
| bfd_byte *uncompressed_buffer; |
| #endif |
| |
| if (sz == 0) |
| return TRUE; |
| |
| switch (sec->compress_status) |
| { |
| case COMPRESS_SECTION_NONE: |
| if (p == NULL) |
| { |
| p = (bfd_byte *) bfd_malloc (sz); |
| if (p == NULL) |
| return FALSE; |
| need_free = TRUE; |
| *ptr = p; |
| } |
| else |
| need_free = FALSE; |
| ret = bfd_get_section_contents (abfd, sec, p, 0, sz); |
| if (!ret && need_free) |
| free (p); |
| return ret; |
| |
| case COMPRESS_SECTION_DONE: |
| if (p) |
| memcpy (p, sec->contents, sz); |
| else |
| *ptr = sec->contents; |
| return TRUE; |
| |
| case DECOMPRESS_SECTION_SIZED: |
| break; |
| |
| default: |
| abort (); |
| } |
| |
| #ifndef HAVE_ZLIB_H |
| bfd_set_error (bfd_error_invalid_operation); |
| return FALSE; |
| #else |
| /* Read in the full compressed section contents. */ |
| uncompressed_size = sec->size; |
| compressed_size = sec->compressed_size; |
| compressed_buffer = (bfd_byte *) bfd_malloc (compressed_size); |
| rawsize = sec->rawsize; |
| /* 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 = compressed_size; |
| sec->compress_status = COMPRESS_SECTION_NONE; |
| ret = bfd_get_section_contents (abfd, sec, compressed_buffer, |
| 0, compressed_size); |
| /* Restore rawsize and size. */ |
| sec->rawsize = rawsize; |
| sec->size = uncompressed_size; |
| if (!ret) |
| { |
| fail_compressed: |
| sec->compress_status = DECOMPRESS_SECTION_SIZED; |
| free (compressed_buffer); |
| return ret; |
| } |
| |
| /* Decompress to caller buffer directly if it is provided. */ |
| if (p) |
| uncompressed_buffer = p; |
| else |
| { |
| uncompressed_buffer = (bfd_byte *) bfd_malloc (uncompressed_size); |
| if (uncompressed_buffer == NULL) |
| goto fail_compressed; |
| } |
| |
| if (!decompress_contents (compressed_buffer, compressed_size, |
| uncompressed_buffer, uncompressed_size)) |
| { |
| sec->compress_status = DECOMPRESS_SECTION_SIZED; |
| free (compressed_buffer); |
| if (p == NULL) |
| free (uncompressed_buffer); |
| bfd_set_error (bfd_error_bad_value); |
| return FALSE; |
| } |
| |
| free (compressed_buffer); |
| if (p == NULL) |
| *ptr = uncompressed_buffer; |
| |
| sec->contents = uncompressed_buffer; |
| sec->compress_status = COMPRESS_SECTION_DONE; |
| |
| return TRUE; |
| #endif |
| } |
| |
| /* |
| FUNCTION |
| bfd_is_section_compressed |
| |
| SYNOPSIS |
| bfd_boolean bfd_is_section_compressed |
| (bfd *abfd, asection *section); |
| |
| DESCRIPTION |
| Return @code{TRUE} if @var{section} is compressed. |
| */ |
| |
| bfd_boolean |
| bfd_is_section_compressed (bfd *abfd, sec_ptr sec) |
| { |
| bfd_byte compressed_buffer [12]; |
| |
| /* Read the zlib header. In this case, it should be "ZLIB" followed |
| by the uncompressed section size, 8 bytes in big-endian order. */ |
| return (bfd_get_section_contents (abfd, sec, compressed_buffer, 0, 12) |
| && CONST_STRNEQ ((char*) compressed_buffer, "ZLIB")); |
| } |
| |
| /* |
| FUNCTION |
| bfd_init_section_decompress_status |
| |
| SYNOPSIS |
| bfd_boolean 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 or zlib is not installed on this machine. Otherwise, |
| return @code{TRUE}. |
| */ |
| |
| bfd_boolean |
| bfd_init_section_decompress_status (bfd *abfd ATTRIBUTE_UNUSED, |
| sec_ptr sec ATTRIBUTE_UNUSED) |
| { |
| #ifndef HAVE_ZLIB_H |
| bfd_set_error (bfd_error_invalid_operation); |
| return FALSE; |
| #else |
| bfd_byte compressed_buffer [12]; |
| bfd_size_type uncompressed_size; |
| |
| if (sec->rawsize != 0 |
| || sec->contents != NULL |
| || sec->compress_status != COMPRESS_SECTION_NONE |
| || !bfd_get_section_contents (abfd, sec, compressed_buffer, 0, 12)) |
| { |
| bfd_set_error (bfd_error_invalid_operation); |
| return FALSE; |
| } |
| |
| /* Read the zlib header. In this case, it should be "ZLIB" followed |
| by the uncompressed section size, 8 bytes in big-endian order. */ |
| if (! CONST_STRNEQ ((char*) compressed_buffer, "ZLIB")) |
| { |
| bfd_set_error (bfd_error_wrong_format); |
| return FALSE; |
| } |
| |
| uncompressed_size = compressed_buffer[4]; uncompressed_size <<= 8; |
| uncompressed_size += compressed_buffer[5]; uncompressed_size <<= 8; |
| uncompressed_size += compressed_buffer[6]; uncompressed_size <<= 8; |
| uncompressed_size += compressed_buffer[7]; uncompressed_size <<= 8; |
| uncompressed_size += compressed_buffer[8]; uncompressed_size <<= 8; |
| uncompressed_size += compressed_buffer[9]; uncompressed_size <<= 8; |
| uncompressed_size += compressed_buffer[10]; uncompressed_size <<= 8; |
| uncompressed_size += compressed_buffer[11]; |
| |
| sec->compressed_size = sec->size; |
| sec->size = uncompressed_size; |
| sec->compress_status = DECOMPRESS_SECTION_SIZED; |
| |
| return TRUE; |
| #endif |
| } |
| |
| /* |
| FUNCTION |
| bfd_init_section_compress_status |
| |
| SYNOPSIS |
| bfd_boolean 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 or zlib is not installed on this machine. Otherwise, |
| return @code{TRUE}. |
| */ |
| |
| bfd_boolean |
| bfd_init_section_compress_status (bfd *abfd ATTRIBUTE_UNUSED, |
| sec_ptr sec ATTRIBUTE_UNUSED) |
| { |
| #ifndef HAVE_ZLIB_H |
| bfd_set_error (bfd_error_invalid_operation); |
| return FALSE; |
| #else |
| bfd_size_type uncompressed_size; |
| bfd_byte *uncompressed_buffer; |
| bfd_boolean ret; |
| |
| /* 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); |
| if (!bfd_get_section_contents (abfd, sec, uncompressed_buffer, |
| 0, uncompressed_size)) |
| ret = FALSE; |
| else |
| ret = bfd_compress_section_contents (abfd, sec, |
| uncompressed_buffer, |
| uncompressed_size); |
| |
| free (uncompressed_buffer); |
| return ret; |
| #endif |
| } |