X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;f=bfd%2Fbfd.c;h=665f182559b37b4b9f61fdac19eb117badb9d0d8;hb=31cf148787509fcf33bc6adb05bbf16bee48835d;hp=5b336a9a0a2fb2464071e7ac3f535fd277a67885;hpb=f6fe1ccd62e4492aabda6a9a9d12da438d0ebf2b;p=deliverable%2Fbinutils-gdb.git diff --git a/bfd/bfd.c b/bfd/bfd.c index 5b336a9a0a..665f182559 100644 --- a/bfd/bfd.c +++ b/bfd/bfd.c @@ -1,5 +1,5 @@ /* Generic BFD library interface and support routines. - Copyright (C) 1990-2015 Free Software Foundation, Inc. + Copyright (C) 1990-2017 Free Software Foundation, Inc. Written by Cygnus Support. This file is part of BFD, the Binary File Descriptor library. @@ -46,11 +46,17 @@ CODE_FRAGMENT . .enum bfd_plugin_format . { -. bfd_plugin_uknown = 0, +. bfd_plugin_unknown = 0, . bfd_plugin_yes = 1, . bfd_plugin_no = 2 . }; . +.struct bfd_build_id +. { +. bfd_size_type size; +. bfd_byte data[1]; +. }; +. .struct bfd .{ . {* The filename the application opened the BFD with. *} @@ -85,7 +91,7 @@ CODE_FRAGMENT . ENUM_BITFIELD (bfd_direction) direction : 2; . . {* Format_specific flags. *} -. flagword flags : 18; +. flagword flags : 20; . . {* Values that may appear in the flags field of a BFD. These also . appear in the object_flags field of the bfd_target structure, where @@ -165,16 +171,24 @@ CODE_FRAGMENT . {* Compress sections in this BFD with SHF_COMPRESSED from gABI. *} .#define BFD_COMPRESS_GABI 0x20000 . +. {* Convert ELF common symbol type to STT_COMMON or STT_OBJECT in this +. BFD. *} +.#define BFD_CONVERT_ELF_COMMON 0x40000 +. +. {* Use the ELF STT_COMMON type in this BFD. *} +.#define BFD_USE_ELF_STT_COMMON 0x80000 +. . {* Flags bits to be saved in bfd_preserve_save. *} .#define BFD_FLAGS_SAVED \ -. (BFD_IN_MEMORY | BFD_COMPRESS | BFD_DECOMPRESS | BFD_PLUGIN \ -. | BFD_COMPRESS_GABI) +. (BFD_IN_MEMORY | BFD_COMPRESS | BFD_DECOMPRESS | BFD_LINKER_CREATED \ +. | BFD_PLUGIN | BFD_COMPRESS_GABI | BFD_CONVERT_ELF_COMMON \ +. | BFD_USE_ELF_STT_COMMON) . . {* Flags bits which are for BFD use only. *} .#define BFD_FLAGS_FOR_BFD_USE_MASK \ . (BFD_IN_MEMORY | BFD_COMPRESS | BFD_DECOMPRESS | BFD_LINKER_CREATED \ . | BFD_PLUGIN | BFD_TRADITIONAL_FORMAT | BFD_DETERMINISTIC_OUTPUT \ -. | BFD_COMPRESS_GABI) +. | BFD_COMPRESS_GABI | BFD_CONVERT_ELF_COMMON | BFD_USE_ELF_STT_COMMON) . . {* Is the file descriptor being cached? That is, can it be closed as . needed, and re-opened when accessed later? *} @@ -335,6 +349,9 @@ CODE_FRAGMENT . struct objalloc *, but we use void * to avoid requiring the inclusion . of objalloc.h. *} . void *memory; +. +. {* For input BFDs, the build ID, if the object has one. *} +. const struct bfd_build_id *build_id; .}; . .{* See note beside bfd_set_section_userdata. *} @@ -582,11 +599,11 @@ SUBSECTION problem. They call a BFD error handler function. This function may be overridden by the program. - The BFD error handler acts like printf. + The BFD error handler acts like vprintf. CODE_FRAGMENT . -.typedef void (*bfd_error_handler_type) (const char *, ...); +.typedef void (*bfd_error_handler_type) (const char *, va_list); . */ @@ -594,178 +611,264 @@ CODE_FRAGMENT static const char *_bfd_error_program_name; -/* This is the default routine to handle BFD error messages. - Like fprintf (stderr, ...), but also handles some extra format specifiers. - - %A section name from section. For group components, print group name too. - %B file name from bfd. For archive components, prints archive too. - - Note - because these two extra format specifiers require special handling - they are scanned for and processed in this function, before calling - vfprintf. This means that the *arguments* for these format specifiers - must be the first ones in the variable argument list, regardless of where - the specifiers appear in the format string. Thus for example calling - this function with a format string of: +/* This macro and _doprnt taken from libiberty _doprnt.c, tidied a + little and extended to handle '%A' and '%B'. 'L' as a modifer for + integer formats is used for bfd_vma and bfd_size_type args, which + vary in size depending on BFD configuration. */ - "blah %s blah %A blah %d blah %B" +#define PRINT_TYPE(TYPE) \ + do \ + { \ + TYPE value = va_arg (ap, TYPE); \ + result = fprintf (stream, specifier, value); \ + } while (0) - would involve passing the arguments as: - - "blah %s blah %A blah %d blah %B", - asection_for_the_%A, - bfd_for_the_%B, - string_for_the_%s, - integer_for_the_%d); - */ - -void -_bfd_default_error_handler (const char *fmt, ...) +static int +_doprnt (FILE *stream, const char *format, va_list ap) { - va_list ap; - char *bufp; - const char *new_fmt, *p; - size_t avail = 1000; - char buf[1000]; - - /* PR 4992: Don't interrupt output being sent to stdout. */ - fflush (stdout); - - if (_bfd_error_program_name != NULL) - fprintf (stderr, "%s: ", _bfd_error_program_name); - else - fprintf (stderr, "BFD: "); - - va_start (ap, fmt); - new_fmt = fmt; - bufp = buf; + const char *ptr = format; + char specifier[128]; + int total_printed = 0; - /* Reserve enough space for the existing format string. */ - avail -= strlen (fmt) + 1; - if (avail > 1000) - _exit (EXIT_FAILURE); - - p = fmt; - while (1) + while (*ptr != '\0') { - char *q; - size_t len, extra, trim; + int result; - p = strchr (p, '%'); - if (p == NULL || p[1] == '\0') + if (*ptr != '%') { - if (new_fmt == buf) - { - len = strlen (fmt); - memcpy (bufp, fmt, len + 1); - } - break; + /* While we have regular characters, print them. */ + char *end = strchr (ptr, '%'); + if (end != NULL) + result = fprintf (stream, "%.*s", (int) (end - ptr), ptr); + else + result = fprintf (stream, "%s", ptr); + ptr += result; } - - if (p[1] == 'A' || p[1] == 'B') + else { - len = p - fmt; - memcpy (bufp, fmt, len); - bufp += len; - fmt = p + 2; - new_fmt = buf; - - /* If we run out of space, tough, you lose your ridiculously - long file or section name. It's not safe to try to alloc - memory here; We might be printing an out of memory message. */ - if (avail == 0) + /* We have a format specifier! */ + char *sptr = specifier; + int wide_width = 0, short_width = 0; + + /* Copy the % and move forward. */ + *sptr++ = *ptr++; + + /* Move past flags. */ + while (strchr ("-+ #0", *ptr)) + *sptr++ = *ptr++; + + if (*ptr == '*') { - *bufp++ = '*'; - *bufp++ = '*'; - *bufp = '\0'; + int value = abs (va_arg (ap, int)); + sptr += sprintf (sptr, "%d", value); + ptr++; } else + /* Handle explicit numeric value. */ + while (ISDIGIT (*ptr)) + *sptr++ = *ptr++; + + if (*ptr == '.') { - if (p[1] == 'B') + /* Copy and go past the period. */ + *sptr++ = *ptr++; + if (*ptr == '*') { - bfd *abfd = va_arg (ap, bfd *); - - if (abfd == NULL) - /* Invoking %B with a null bfd pointer is an internal error. */ - abort (); - else if (abfd->my_archive) - snprintf (bufp, avail, "%s(%s)", - abfd->my_archive->filename, abfd->filename); - else - snprintf (bufp, avail, "%s", abfd->filename); + int value = abs (va_arg (ap, int)); + sptr += sprintf (sptr, "%d", value); + ptr++; } else + /* Handle explicit numeric value. */ + while (ISDIGIT (*ptr)) + *sptr++ = *ptr++; + } + while (strchr ("hlL", *ptr)) + { + switch (*ptr) { - asection *sec = va_arg (ap, asection *); - bfd *abfd; - const char *group = NULL; - struct coff_comdat_info *ci; - - if (sec == NULL) - /* Invoking %A with a null section pointer is an internal error. */ - abort (); - abfd = sec->owner; - if (abfd != NULL - && bfd_get_flavour (abfd) == bfd_target_elf_flavour - && elf_next_in_group (sec) != NULL - && (sec->flags & SEC_GROUP) == 0) - group = elf_group_name (sec); - else if (abfd != NULL - && bfd_get_flavour (abfd) == bfd_target_coff_flavour - && (ci = bfd_coff_get_comdat_section (sec->owner, - sec)) != NULL) - group = ci->name; - if (group != NULL) - snprintf (bufp, avail, "%s[%s]", sec->name, group); - else - snprintf (bufp, avail, "%s", sec->name); - } - len = strlen (bufp); - avail = avail - len + 2; - - /* We need to replace any '%' we printed by "%%". - First count how many. */ - q = bufp; - bufp += len; - extra = 0; - while ((q = strchr (q, '%')) != NULL) - { - ++q; - ++extra; + case 'h': + short_width = 1; + break; + case 'l': + wide_width++; + break; + case 'L': + wide_width = 2; + break; + default: + abort(); } + *sptr++ = *ptr++; + } - /* If there isn't room, trim off the end of the string. */ - q = bufp; - bufp += extra; - if (extra > avail) - { - trim = extra - avail; - bufp -= trim; - do - { - if (*--q == '%') - --extra; - } - while (--trim != 0); - *q = '\0'; - avail = extra; - } - avail -= extra; + /* Copy the type specifier, and NULL terminate. */ + *sptr++ = *ptr++; + *sptr = '\0'; - /* Now double all '%' chars, shuffling the string as we go. */ - while (extra != 0) - { - while ((q[extra] = *q) != '%') - --q; - q[--extra] = '%'; - --q; - } + switch (ptr[-1]) + { + case 'd': + case 'i': + case 'o': + case 'u': + case 'x': + case 'X': + case 'c': + { + /* Short values are promoted to int, so just copy it + as an int and trust the C library printf to cast it + to the right width. */ + if (short_width) + PRINT_TYPE (int); + else + { + /* L modifier for bfd_vma or bfd_size_type may be + either long long or long. */ + if (sptr[-2] == 'L') + { + sptr[-2] = 'l'; + if (BFD_ARCH_SIZE < 64 || BFD_HOST_64BIT_LONG) + wide_width = 1; + else + { + sptr[-1] = 'l'; + *sptr++ = ptr[-1]; + *sptr = '\0'; + } + } + + switch (wide_width) + { + case 0: + PRINT_TYPE (int); + break; + case 1: + PRINT_TYPE (long); + break; + case 2: + default: +#if defined (__MSVCRT__) + sptr[-3] = 'I'; + sptr[-2] = '6'; + sptr[-1] = '4'; + *sptr++ = ptr[-1]; + *sptr = '\0'; +#endif +#if defined (__GNUC__) || defined (HAVE_LONG_LONG) + PRINT_TYPE (long long); +#else + /* Fake it and hope for the best. */ + PRINT_TYPE (long); +#endif + break; + } + } + } + break; + case 'f': + case 'e': + case 'E': + case 'g': + case 'G': + { + if (wide_width == 0) + PRINT_TYPE (double); + else + { +#if defined (__GNUC__) || defined (HAVE_LONG_DOUBLE) + PRINT_TYPE (long double); +#else + /* Fake it and hope for the best. */ + PRINT_TYPE (double); +#endif + } + } + break; + case 's': + PRINT_TYPE (char *); + break; + case 'p': + PRINT_TYPE (void *); + break; + case '%': + fputc ('%', stream); + result = 1; + break; + case 'A': + { + asection *sec = va_arg (ap, asection *); + bfd *abfd; + const char *group = NULL; + struct coff_comdat_info *ci; + + if (sec == NULL) + /* Invoking %A with a null section pointer is an + internal error. */ + abort (); + abfd = sec->owner; + if (abfd != NULL + && bfd_get_flavour (abfd) == bfd_target_elf_flavour + && elf_next_in_group (sec) != NULL + && (sec->flags & SEC_GROUP) == 0) + group = elf_group_name (sec); + else if (abfd != NULL + && bfd_get_flavour (abfd) == bfd_target_coff_flavour + && (ci = bfd_coff_get_comdat_section (sec->owner, + sec)) != NULL) + group = ci->name; + if (group != NULL) + result = fprintf (stream, "%s[%s]", sec->name, group); + else + result = fprintf (stream, "%s", sec->name); + } + break; + case 'B': + { + bfd *abfd = va_arg (ap, bfd *); + + if (abfd == NULL) + /* Invoking %B with a null bfd pointer is an + internal error. */ + abort (); + else if (abfd->my_archive + && !bfd_is_thin_archive (abfd->my_archive)) + result = fprintf (stream, "%s(%s)", + abfd->my_archive->filename, abfd->filename); + else + result = fprintf (stream, "%s", abfd->filename); + } + break; + default: + abort(); } } - p = p + 2; + if (result == -1) + return -1; + total_printed += result; } - vfprintf (stderr, new_fmt, ap); - va_end (ap); + return total_printed; +} + +/* This is the default routine to handle BFD error messages. + Like fprintf (stderr, ...), but also handles some extra format specifiers. + + %A section name from section. For group components, print group name too. + %B file name from bfd. For archive components, prints archive too. */ + +static void +error_handler_internal (const char *fmt, va_list ap) +{ + /* PR 4992: Don't interrupt output being sent to stdout. */ + fflush (stdout); + + if (_bfd_error_program_name != NULL) + fprintf (stderr, "%s: ", _bfd_error_program_name); + else + fprintf (stderr, "BFD: "); + + _doprnt (stderr, fmt, ap); /* On AIX, putc is implemented as a macro that triggers a -Wunused-value warning, so use the fputc function to avoid it. */ @@ -779,7 +882,17 @@ _bfd_default_error_handler (const char *fmt, ...) function pointer permits a program linked against BFD to intercept the messages and deal with them itself. */ -bfd_error_handler_type _bfd_error_handler = _bfd_default_error_handler; +static bfd_error_handler_type _bfd_error_internal = error_handler_internal; + +void +_bfd_error_handler (const char *fmt, ...) +{ + va_list ap; + + va_start (ap, fmt); + _bfd_error_internal (fmt, ap); + va_end (ap); +} /* FUNCTION @@ -798,8 +911,8 @@ bfd_set_error_handler (bfd_error_handler_type pnew) { bfd_error_handler_type pold; - pold = _bfd_error_handler; - _bfd_error_handler = pnew; + pold = _bfd_error_internal; + _bfd_error_internal = pnew; return pold; } @@ -823,23 +936,6 @@ bfd_set_error_program_name (const char *name) _bfd_error_program_name = name; } -/* -FUNCTION - bfd_get_error_handler - -SYNOPSIS - bfd_error_handler_type bfd_get_error_handler (void); - -DESCRIPTION - Return the BFD error handler function. -*/ - -bfd_error_handler_type -bfd_get_error_handler (void) -{ - return _bfd_error_handler; -} - /* SUBSECTION BFD assert handler @@ -874,14 +970,14 @@ _bfd_default_assert_handler (const char *bfd_formatmsg, int bfd_line) { - (*_bfd_error_handler) (bfd_formatmsg, bfd_version, bfd_file, bfd_line); + _bfd_error_handler (bfd_formatmsg, bfd_version, bfd_file, bfd_line); } /* Similar to _bfd_error_handler, a program can decide to exit on an internal BFD error. We use a non-variadic type to simplify passing on parameters to other functions, e.g. _bfd_error_handler. */ -bfd_assert_handler_type _bfd_assert_handler = _bfd_default_assert_handler; +static bfd_assert_handler_type _bfd_assert_handler = _bfd_default_assert_handler; /* FUNCTION @@ -904,23 +1000,6 @@ bfd_set_assert_handler (bfd_assert_handler_type pnew) _bfd_assert_handler = pnew; return pold; } - -/* -FUNCTION - bfd_get_assert_handler - -SYNOPSIS - bfd_assert_handler_type bfd_get_assert_handler (void); - -DESCRIPTION - Return the BFD assert handler function. -*/ - -bfd_assert_handler_type -bfd_get_assert_handler (void) -{ - return _bfd_assert_handler; -} /* INODE @@ -1009,18 +1088,10 @@ DESCRIPTION section @var{sec} to the values @var{rel} and @var{count}. The argument @var{abfd} is ignored. +.#define bfd_set_reloc(abfd, asect, location, count) \ +. BFD_SEND (abfd, _bfd_set_reloc, (abfd, asect, location, count)) */ -void -bfd_set_reloc (bfd *ignore_abfd ATTRIBUTE_UNUSED, - sec_ptr asect, - arelent **location, - unsigned int count) -{ - asect->orelocation = location; - asect->reloc_count = count; -} - /* FUNCTION bfd_set_file_flags @@ -1069,6 +1140,7 @@ bfd_set_file_flags (bfd *abfd, flagword flags) void bfd_assert (const char *file, int line) { + /* xgettext:c-format */ (*_bfd_assert_handler) (_("BFD %s assertion fail %s:%d"), BFD_VERSION_STRING, file, line); } @@ -1080,14 +1152,16 @@ void _bfd_abort (const char *file, int line, const char *fn) { if (fn != NULL) - (*_bfd_error_handler) - (_("BFD %s internal error, aborting at %s line %d in %s\n"), + _bfd_error_handler + /* xgettext:c-format */ + (_("BFD %s internal error, aborting at %s:%d in %s\n"), BFD_VERSION_STRING, file, line, fn); else - (*_bfd_error_handler) - (_("BFD %s internal error, aborting at %s line %d\n"), + _bfd_error_handler + /* xgettext:c-format */ + (_("BFD %s internal error, aborting at %s:%d\n"), BFD_VERSION_STRING, file, line); - (*_bfd_error_handler) (_("Please report this bug.\n")); + _bfd_error_handler (_("Please report this bug.\n")); _exit (EXIT_FAILURE); } @@ -1313,7 +1387,7 @@ bfd_scan_vma (const char *string, const char **end, int base) if (sizeof (bfd_vma) <= sizeof (unsigned long)) return strtoul (string, (char **) end, base); -#ifdef HAVE_STRTOULL +#if defined (HAVE_STRTOULL) && defined (HAVE_LONG_LONG) if (sizeof (bfd_vma) <= sizeof (unsigned long long)) return strtoull (string, (char **) end, base); #endif @@ -1416,27 +1490,6 @@ DESCRIPTION */ -/* -FUNCTION - bfd_merge_private_bfd_data - -SYNOPSIS - bfd_boolean bfd_merge_private_bfd_data (bfd *ibfd, bfd *obfd); - -DESCRIPTION - Merge private BFD information from the BFD @var{ibfd} to the - the output file BFD @var{obfd} when linking. Return <> - on success, <> on error. Possible error returns are: - - o <> - - Not enough memory exists to create private data for @var{obfd}. - -.#define bfd_merge_private_bfd_data(ibfd, obfd) \ -. BFD_SEND (obfd, _bfd_merge_private_bfd_data, \ -. (ibfd, obfd)) - -*/ - /* FUNCTION bfd_set_private_flags @@ -1991,15 +2044,24 @@ bfd_update_compression_header (bfd *abfd, bfd_byte *contents, { Elf64_External_Chdr *echdr = (Elf64_External_Chdr *) contents; - bfd_put_64 (abfd, ELFCOMPRESS_ZLIB, &echdr->ch_type); + bfd_put_32 (abfd, ELFCOMPRESS_ZLIB, &echdr->ch_type); + bfd_put_32 (abfd, 0, &echdr->ch_reserved); bfd_put_64 (abfd, sec->size, &echdr->ch_size); bfd_put_64 (abfd, 1 << sec->alignment_power, &echdr->ch_addralign); } } else - /* Clear the SHF_COMPRESSED bit. */ - elf_section_flags (sec) &= ~SHF_COMPRESSED; + { + /* Clear the SHF_COMPRESSED bit. */ + elf_section_flags (sec) &= ~SHF_COMPRESSED; + + /* Write the zlib header. It should be "ZLIB" followed by + the uncompressed section size, 8 bytes in big-endian + order. */ + memcpy (contents, "ZLIB", 4); + bfd_putb64 (sec->size, contents + 4); + } } } else @@ -2013,11 +2075,12 @@ bfd_update_compression_header (bfd *abfd, bfd_byte *contents, SYNOPSIS bfd_boolean bfd_check_compression_header (bfd *abfd, bfd_byte *contents, asection *sec, - bfd_size_type uncompressed_size); + bfd_size_type *uncompressed_size); DESCRIPTION - Check the compression header at CONTENTS of SEC in ABFD with - the uncompressed size UNCOMPRESSED_SIZE. + Check the compression header at CONTENTS of SEC in ABFD and + store the uncompressed size in UNCOMPRESSED_SIZE if the + compression header is valid. RETURNS Return TRUE if the compression header is valid. @@ -2026,7 +2089,7 @@ RETURNS bfd_boolean bfd_check_compression_header (bfd *abfd, bfd_byte *contents, asection *sec, - bfd_size_type uncompressed_size) + bfd_size_type *uncompressed_size) { if (bfd_get_flavour (abfd) == bfd_target_elf_flavour && (elf_section_flags (sec) & SHF_COMPRESSED) != 0) @@ -2043,13 +2106,16 @@ bfd_check_compression_header (bfd *abfd, bfd_byte *contents, else { Elf64_External_Chdr *echdr = (Elf64_External_Chdr *) contents; - chdr.ch_type = bfd_get_64 (abfd, &echdr->ch_type); + chdr.ch_type = bfd_get_32 (abfd, &echdr->ch_type); chdr.ch_size = bfd_get_64 (abfd, &echdr->ch_size); chdr.ch_addralign = bfd_get_64 (abfd, &echdr->ch_addralign); } - return (chdr.ch_type == ELFCOMPRESS_ZLIB - && chdr.ch_size == uncompressed_size - && chdr.ch_addralign == 1U << sec->alignment_power); + if (chdr.ch_type == ELFCOMPRESS_ZLIB + && chdr.ch_addralign == 1U << sec->alignment_power) + { + *uncompressed_size = chdr.ch_size; + return TRUE; + } } return FALSE; @@ -2090,3 +2156,161 @@ bfd_get_compression_header_size (bfd *abfd, asection *sec) return 0; } + +/* +FUNCTION + bfd_convert_section_size + +SYNOPSIS + bfd_size_type bfd_convert_section_size + (bfd *ibfd, asection *isec, bfd *obfd, bfd_size_type size); + +DESCRIPTION + Convert the size @var{size} of the section @var{isec} in input + BFD @var{ibfd} to the section size in output BFD @var{obfd}. +*/ + +bfd_size_type +bfd_convert_section_size (bfd *ibfd, sec_ptr isec, bfd *obfd, + bfd_size_type size) +{ + bfd_size_type hdr_size; + + /* Do nothing if input file will be decompressed. */ + if ((ibfd->flags & BFD_DECOMPRESS)) + return size; + + /* Do nothing if either input or output aren't ELF. */ + if (bfd_get_flavour (ibfd) != bfd_target_elf_flavour + || bfd_get_flavour (obfd) != bfd_target_elf_flavour) + return size; + + /* Do nothing if ELF classes of input and output are the same. */ + if (get_elf_backend_data (ibfd)->s->elfclass + == get_elf_backend_data (obfd)->s->elfclass) + return size; + + /* Do nothing if the input section isn't a SHF_COMPRESSED section. */ + hdr_size = bfd_get_compression_header_size (ibfd, isec); + if (hdr_size == 0) + return size; + + /* Adjust the size of the output SHF_COMPRESSED section. */ + if (hdr_size == sizeof (Elf32_External_Chdr)) + return (size - sizeof (Elf32_External_Chdr) + + sizeof (Elf64_External_Chdr)); + else + return (size - sizeof (Elf64_External_Chdr) + + sizeof (Elf32_External_Chdr)); +} + +/* +FUNCTION + bfd_convert_section_contents + +SYNOPSIS + bfd_boolean bfd_convert_section_contents + (bfd *ibfd, asection *isec, bfd *obfd, + bfd_byte **ptr, bfd_size_type *ptr_size); + +DESCRIPTION + Convert the contents, stored in @var{*ptr}, of the section + @var{isec} in input BFD @var{ibfd} to output BFD @var{obfd} + if needed. The original buffer pointed to by @var{*ptr} may + be freed and @var{*ptr} is returned with memory malloc'd by this + function, and the new size written to @var{ptr_size}. +*/ + +bfd_boolean +bfd_convert_section_contents (bfd *ibfd, sec_ptr isec, bfd *obfd, + bfd_byte **ptr, bfd_size_type *ptr_size) +{ + bfd_byte *contents; + bfd_size_type ihdr_size, ohdr_size, size; + Elf_Internal_Chdr chdr; + bfd_boolean use_memmove; + + /* Do nothing if input file will be decompressed. */ + if ((ibfd->flags & BFD_DECOMPRESS)) + return TRUE; + + /* Do nothing if either input or output aren't ELF. */ + if (bfd_get_flavour (ibfd) != bfd_target_elf_flavour + || bfd_get_flavour (obfd) != bfd_target_elf_flavour) + return TRUE; + + /* Do nothing if ELF classes of input and output are the same. */ + if (get_elf_backend_data (ibfd)->s->elfclass + == get_elf_backend_data (obfd)->s->elfclass) + return TRUE; + + /* Do nothing if the input section isn't a SHF_COMPRESSED section. */ + ihdr_size = bfd_get_compression_header_size (ibfd, isec); + if (ihdr_size == 0) + return TRUE; + + contents = *ptr; + + /* Convert the contents of the input SHF_COMPRESSED section to + output. Get the input compression header and the size of the + output compression header. */ + if (ihdr_size == sizeof (Elf32_External_Chdr)) + { + Elf32_External_Chdr *echdr = (Elf32_External_Chdr *) contents; + chdr.ch_type = bfd_get_32 (ibfd, &echdr->ch_type); + chdr.ch_size = bfd_get_32 (ibfd, &echdr->ch_size); + chdr.ch_addralign = bfd_get_32 (ibfd, &echdr->ch_addralign); + + ohdr_size = sizeof (Elf64_External_Chdr); + + use_memmove = FALSE; + } + else + { + Elf64_External_Chdr *echdr = (Elf64_External_Chdr *) contents; + chdr.ch_type = bfd_get_32 (ibfd, &echdr->ch_type); + chdr.ch_size = bfd_get_64 (ibfd, &echdr->ch_size); + chdr.ch_addralign = bfd_get_64 (ibfd, &echdr->ch_addralign); + + ohdr_size = sizeof (Elf32_External_Chdr); + use_memmove = TRUE; + } + + size = bfd_get_section_size (isec) - ihdr_size + ohdr_size; + if (!use_memmove) + { + contents = (bfd_byte *) bfd_malloc (size); + if (contents == NULL) + return FALSE; + } + + /* Write out the output compression header. */ + if (ohdr_size == sizeof (Elf32_External_Chdr)) + { + Elf32_External_Chdr *echdr = (Elf32_External_Chdr *) contents; + bfd_put_32 (obfd, ELFCOMPRESS_ZLIB, &echdr->ch_type); + bfd_put_32 (obfd, chdr.ch_size, &echdr->ch_size); + bfd_put_32 (obfd, chdr.ch_addralign, &echdr->ch_addralign); + } + else + { + Elf64_External_Chdr *echdr = (Elf64_External_Chdr *) contents; + bfd_put_32 (obfd, ELFCOMPRESS_ZLIB, &echdr->ch_type); + bfd_put_32 (obfd, 0, &echdr->ch_reserved); + bfd_put_64 (obfd, chdr.ch_size, &echdr->ch_size); + bfd_put_64 (obfd, chdr.ch_addralign, &echdr->ch_addralign); + } + + /* Copy the compressed contents. */ + if (use_memmove) + memmove (contents + ohdr_size, *ptr + ihdr_size, size - ohdr_size); + else + { + memcpy (contents + ohdr_size, *ptr + ihdr_size, size - ohdr_size); + free (*ptr); + *ptr = contents; + } + + *ptr_size = size; + return TRUE; +}