diff options
Diffstat (limited to 'bfd/compress.c')
-rw-r--r-- | bfd/compress.c | 396 |
1 files changed, 340 insertions, 56 deletions
diff --git a/bfd/compress.c b/bfd/compress.c index fe1b0fd..171de77 100644 --- a/bfd/compress.c +++ b/bfd/compress.c @@ -27,49 +27,309 @@ #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_uncompress_section_contents + bfd_compress_section_contents SYNOPSIS - bfd_boolean bfd_uncompress_section_contents - (bfd_byte **buffer, bfd_size_type *size); + bfd_boolean bfd_compress_section_contents + (bfd *abfd, asection *section, bfd_byte *uncompressed_buffer, + bfd_size_type uncompressed_size); DESCRIPTION - Uncompresses a section that was compressed using zlib, in place. At - the call to this function, *@var{buffer} and *@var{size} should point - to the section contents to be uncompressed. At the end of the - function, *@var{buffer} and *@var{size} will point to the uncompressed - contents. This function assumes *BUFFER was allocated using - bfd_malloc() or equivalent. If the section is not a valid compressed - section, or zlib is not installed on this machine, the input is - unmodified. - - Returns @code{FALSE} if unable to uncompress successfully; in that case - the input is unmodified. Otherwise, returns @code{TRUE}. + 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_uncompress_section_contents (bfd_byte **buffer ATTRIBUTE_UNUSED, - bfd_size_type *size ATTRIBUTE_UNUSED) +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 - bfd_size_type compressed_size = *size; - bfd_byte *compressed_buffer = *buffer; + bfd_size_type 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; - z_stream strm; - int rc; - bfd_size_type header_size = 12; +#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 (compressed_size < header_size - || ! CONST_STRNEQ ((char*) compressed_buffer, "ZLIB")) - return FALSE; + 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; @@ -79,42 +339,66 @@ bfd_uncompress_section_contents (bfd_byte **buffer ATTRIBUTE_UNUSED, uncompressed_size += compressed_buffer[10]; uncompressed_size <<= 8; uncompressed_size += compressed_buffer[11]; - /* 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 - header_size; - strm.next_in = (Bytef*) compressed_buffer + header_size; - strm.avail_out = uncompressed_size; - uncompressed_buffer = (bfd_byte *) bfd_malloc (uncompressed_size); - if (! uncompressed_buffer) - return FALSE; + sec->compressed_size = sec->size; + sec->size = uncompressed_size; + sec->compress_status = DECOMPRESS_SECTION_SIZED; - rc = inflateInit (&strm); - while (strm.avail_in > 0) + 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) { - if (rc != Z_OK) - goto fail; - strm.next_out = ((Bytef*) uncompressed_buffer - + (uncompressed_size - strm.avail_out)); - rc = inflate (&strm, Z_FINISH); - if (rc != Z_STREAM_END) - goto fail; - rc = inflateReset (&strm); + bfd_set_error (bfd_error_invalid_operation); + return FALSE; } - rc = inflateEnd (&strm); - if (rc != Z_OK - || strm.avail_out != 0) - goto fail; - free (compressed_buffer); - *buffer = uncompressed_buffer; - *size = uncompressed_size; - return TRUE; + /* 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); - fail: free (uncompressed_buffer); - return FALSE; -#endif /* HAVE_ZLIB_H */ + return ret; +#endif } |