aboutsummaryrefslogtreecommitdiff
path: root/bfd/coffgen.c
diff options
context:
space:
mode:
authorTristan Gingold <tgingold@free.fr>2023-05-31 11:20:55 +0100
committerNick Clifton <nickc@redhat.com>2023-05-31 11:20:55 +0100
commit768d1d879be2d134e049521f28d4d5e03b69bafc (patch)
tree0f3eb8bab0eac4aa5909bef7aaf590ab138c4c25 /bfd/coffgen.c
parenta15891aaea006d06066573449efbda353dd2863e (diff)
downloadgdb-768d1d879be2d134e049521f28d4d5e03b69bafc.zip
gdb-768d1d879be2d134e049521f28d4d5e03b69bafc.tar.gz
gdb-768d1d879be2d134e049521f28d4d5e03b69bafc.tar.bz2
pe/coff - add support for base64 encoded long section names
PR 30444 * coffcode.h (coff_write_object_contents): Handle base64 encoding on PE. Also check for too large string table. * coffgen.c (extract_long_section_name): New function extracted from ... (make_a_section_from_file): ... here. Add support for base64 long section names. (decode_base64): New function.
Diffstat (limited to 'bfd/coffgen.c')
-rw-r--r--bfd/coffgen.c113
1 files changed, 96 insertions, 17 deletions
diff --git a/bfd/coffgen.c b/bfd/coffgen.c
index d4c14fb..c81f67c 100644
--- a/bfd/coffgen.c
+++ b/bfd/coffgen.c
@@ -44,6 +44,69 @@
#include "libcoff.h"
#include "hashtab.h"
+/* Extract a long section name at STRINDEX and copy it to the bfd objstack.
+ Return NULL in case of error. */
+
+static char *
+extract_long_section_name(bfd *abfd, unsigned long strindex)
+{
+ const char *strings;
+ char *name;
+
+ strings = _bfd_coff_read_string_table (abfd);
+ if (strings == NULL)
+ return NULL;
+ if ((bfd_size_type)(strindex + 2) >= obj_coff_strings_len (abfd))
+ return NULL;
+ strings += strindex;
+ name = (char *) bfd_alloc (abfd, (bfd_size_type) strlen (strings) + 1);
+ if (name == NULL)
+ return NULL;
+ strcpy (name, strings);
+
+ return name;
+}
+
+/* Decode a base 64 coded string at STR of length LEN, and write the result
+ to RES. Return true on success.
+ Return false in case of invalid character or overflow. */
+
+static bool
+decode_base64 (const char *str, unsigned len, uint32_t *res)
+{
+ unsigned i;
+ uint32_t val;
+
+ val = 0;
+ for (i = 0; i < len; i++)
+ {
+ char c = str[i];
+ unsigned d;
+
+ if (c >= 'A' && c <= 'Z')
+ d = c - 'A';
+ else if (c >= 'a' && c <= 'z')
+ d = c - 'a' + 26;
+ else if (c >= '0' && c <= '9')
+ d = c - '0' + 52;
+ else if (c == '+')
+ d = 62;
+ else if (c == '/')
+ d = 63;
+ else
+ return false;
+
+ /* Check for overflow. */
+ if ((val >> 26) != 0)
+ return false;
+
+ val = (val << 6) + d;
+ }
+
+ *res = val;
+ return true;
+}
+
/* Take a section header read from a coff file (in HOST byte order),
and make a BFD "section" out of it. This is used by ECOFF. */
@@ -68,32 +131,48 @@ make_a_section_from_file (bfd *abfd,
if (bfd_coff_set_long_section_names (abfd, bfd_coff_long_section_names (abfd))
&& hdr->s_name[0] == '/')
{
- char buf[SCNNMLEN];
- long strindex;
- char *p;
- const char *strings;
-
/* Flag that this BFD uses long names, even though the format might
expect them to be off by default. This won't directly affect the
format of any output BFD created from this one, but the information
can be used to decide what to do. */
bfd_coff_set_long_section_names (abfd, true);
- memcpy (buf, hdr->s_name + 1, SCNNMLEN - 1);
- buf[SCNNMLEN - 1] = '\0';
- strindex = strtol (buf, &p, 10);
- if (*p == '\0' && strindex >= 0)
+
+ if (hdr->s_name[1] == '/')
{
- strings = _bfd_coff_read_string_table (abfd);
- if (strings == NULL)
- return false;
- if ((bfd_size_type)(strindex + 2) >= obj_coff_strings_len (abfd))
+ /* LLVM extension: the '/' is followed by another '/' and then by
+ the index in the strtab encoded in base64 without NUL at the
+ end. */
+ uint32_t strindex;
+
+ /* Decode the index. No overflow is expected as the string table
+ length is at most 2^32 - 1 (the length is written on the first
+ four bytes).
+ Also, contrary to RFC 4648, all the characters must be decoded,
+ there is no padding. */
+ if (!decode_base64 (hdr->s_name + 2, SCNNMLEN - 2, &strindex))
return false;
- strings += strindex;
- name = (char *) bfd_alloc (abfd,
- (bfd_size_type) strlen (strings) + 1 + 1);
+
+ name = extract_long_section_name (abfd, strindex);
if (name == NULL)
return false;
- strcpy (name, strings);
+ }
+ else
+ {
+ /* PE classic long section name. The '/' is followed by the index
+ in the strtab. The index is formatted as a decimal string. */
+ char buf[SCNNMLEN];
+ long strindex;
+ char *p;
+
+ memcpy (buf, hdr->s_name + 1, SCNNMLEN - 1);
+ buf[SCNNMLEN - 1] = '\0';
+ strindex = strtol (buf, &p, 10);
+ if (*p == '\0' && strindex >= 0)
+ {
+ name = extract_long_section_name (abfd, strindex);
+ if (name == NULL)
+ return false;
+ }
}
}