]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blobdiff - binutils/resbin.c
Support APX zero-upper
[thirdparty/binutils-gdb.git] / binutils / resbin.c
index 7c8f5019641bb5fc84c1a495428938056021e164..615b3c473104fe0f335ea5961a5fae07f584bd80 100644 (file)
@@ -1,6 +1,5 @@
 /* resbin.c -- manipulate the Windows binary resource format.
-   Copyright 1997, 1998, 1999, 2002, 2003, 2007
-   Free Software Foundation, Inc.
+   Copyright (C) 1997-2024 Free Software Foundation, Inc.
    Written by Ian Lance Taylor, Cygnus Support.
    Rewritten by Kai Tietz, Onevision.
 
@@ -8,7 +7,7 @@
 
    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 2 of the License, or
+   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,
@@ -21,6 +20,7 @@
    Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
    02110-1301, USA.  */
 
+
 /* This file contains functions to convert between the binary resource
    format and the internal structures that we want to use.  The same
    binary resource format is used in both res and COFF files.  */
@@ -54,7 +54,7 @@ static rc_res_resource *bin_to_res_group_cursor (windres_bfd *, const bfd_byte *
 static rc_res_resource *bin_to_res_group_icon (windres_bfd *, const bfd_byte *, rc_uint_type);
 static rc_res_resource *bin_to_res_version (windres_bfd *, const bfd_byte *, rc_uint_type);
 static rc_res_resource *bin_to_res_userdata (windres_bfd *, const bfd_byte *, rc_uint_type);
-static rc_res_resource *bin_to_res_toolbar (windres_bfd *, const bfd_byte *, rc_uint_type);
+static rc_res_resource *bin_to_res_toolbar (windres_bfd *, const bfd_byte *);
 static void get_version_header (windres_bfd *, const bfd_byte *, rc_uint_type, const char *,
                                unichar **, rc_uint_type *, rc_uint_type *, rc_uint_type *,
                                rc_uint_type *);
@@ -105,7 +105,7 @@ bin_to_res (windres_bfd *wrbfd, rc_res_id type, const bfd_byte *data,
        case RT_VERSION:
          return bin_to_res_version (wrbfd, data, length);
        case RT_TOOLBAR:
-         return  bin_to_res_toolbar (wrbfd, data, length);
+         return  bin_to_res_toolbar (wrbfd, data);
 
        }
     }
@@ -226,7 +226,7 @@ bin_to_res_menu (windres_bfd *wrbfd, const bfd_byte *data, rc_uint_type length)
 {
   rc_res_resource *r;
   rc_menu *m;
-  rc_uint_type version, read;
+  rc_uint_type version, got;
 
   r = (rc_res_resource *) res_alloc (sizeof *r);
   r->type = RES_TYPE_MENU;
@@ -244,7 +244,7 @@ bin_to_res_menu (windres_bfd *wrbfd, const bfd_byte *data, rc_uint_type length)
       if (length < 4)
        toosmall (_("menu header"));
       m->help = 0;
-      m->items = bin_to_res_menuitems (wrbfd, data + 4, length - 4, &read);
+      m->items = bin_to_res_menuitems (wrbfd, data + 4, length - 4, &got);
     }
   else if (version == 1)
     {
@@ -257,7 +257,7 @@ bin_to_res_menu (windres_bfd *wrbfd, const bfd_byte *data, rc_uint_type length)
       if (offset + 4 >= length)
        toosmall (_("menuex offset"));
       m->items = bin_to_res_menuexitems (wrbfd, data + 4 + offset,
-                                        length - (4 + offset), &read);
+                                        length - (4 + offset), &got);
     }
   else
     fatal (_("unsupported menu version %d"), (int) version);
@@ -269,14 +269,14 @@ bin_to_res_menu (windres_bfd *wrbfd, const bfd_byte *data, rc_uint_type length)
 
 static rc_menuitem *
 bin_to_res_menuitems (windres_bfd *wrbfd, const bfd_byte *data, rc_uint_type length,
-                     rc_uint_type *read)
+                     rc_uint_type *got)
 {
   rc_menuitem *first, **pp;
 
   first = NULL;
   pp = &first;
 
-  *read = 0;
+  *got = 0;
 
   while (length > 0)
     {
@@ -333,7 +333,7 @@ bin_to_res_menuitems (windres_bfd *wrbfd, const bfd_byte *data, rc_uint_type len
 
       data += itemlen;
       length -= itemlen;
-      *read += itemlen;
+      *got += itemlen;
 
       if ((flags & MENUITEM_ENDMENU) != 0)
        return first;
@@ -346,14 +346,14 @@ bin_to_res_menuitems (windres_bfd *wrbfd, const bfd_byte *data, rc_uint_type len
 
 static rc_menuitem *
 bin_to_res_menuexitems (windres_bfd *wrbfd, const bfd_byte *data, rc_uint_type length,
-                       rc_uint_type *read)
+                       rc_uint_type *got)
 {
   rc_menuitem *first, **pp;
 
   first = NULL;
   pp = &first;
 
-  *read = 0;
+  *got = 0;
 
   while (length > 0)
     {
@@ -407,7 +407,7 @@ bin_to_res_menuexitems (windres_bfd *wrbfd, const bfd_byte *data, rc_uint_type l
 
       data += itemlen;
       length -= itemlen;
-      *read += itemlen;
+      *got += itemlen;
 
       if ((flags & 0x80) != 0)
        return first;
@@ -574,8 +574,6 @@ bin_to_res_dialog (windres_bfd *wrbfd, const bfd_byte *data, rc_uint_type length
        dc->data = NULL;
       else
        {
-         off = (off + 3) &~ 3;
-
          if (length < off + datalen)
            toosmall (_("dialog control data"));
 
@@ -908,7 +906,7 @@ get_version_header (windres_bfd *wrbfd, const bfd_byte *data, rc_uint_type lengt
   if (length < 8)
     toosmall (key);
 
-  *len = windres_get_16 (wrbfd, data, 2);
+  *len = (windres_get_16 (wrbfd, data, 2) + 3) & ~3;
   *vallen = windres_get_16 (wrbfd, data + 2, 2);
   *type = windres_get_16 (wrbfd, data + 4, 2);
 
@@ -961,13 +959,17 @@ bin_to_res_version (windres_bfd *wrbfd, const bfd_byte *data, rc_uint_type lengt
   get_version_header (wrbfd, data, length, "VS_VERSION_INFO",
                      (unichar **) NULL, &verlen, &vallen, &type, &off);
 
-  if ((unsigned int) verlen != length)
-    fatal (_("version length %d does not match resource length %lu"),
-          (int) verlen, (unsigned long) length);
+  /* PR 17512: The verlen field does not include padding length.  */
+  if (verlen > length)
+    fatal (_("version length %lu greater than resource length %lu"),
+          (unsigned long) verlen, (unsigned long) length);
 
   if (type != 0)
     fatal (_("unexpected version type %d"), (int) type);
 
+  /* PR 27686: Ignore any padding bytes after the end of the version structure.  */
+  length = verlen;
+
   data += off;
   length -= off;
 
@@ -1026,7 +1028,7 @@ bin_to_res_version (windres_bfd *wrbfd, const bfd_byte *data, rc_uint_type lengt
 
       if (ch == 'S')
        {
-         rc_ver_stringinfo **ppvs;
+         rc_ver_stringtable **ppvst;
 
          vi->type = VERINFO_STRING;
 
@@ -1040,60 +1042,78 @@ bin_to_res_version (windres_bfd *wrbfd, const bfd_byte *data, rc_uint_type lengt
          data += off;
          length -= off;
 
-         get_version_header (wrbfd, data, length, (const char *) NULL,
-                             &vi->u.string.language, &verlen, &vallen,
-                             &type, &off);
+         verlen -= off;
 
-         if (vallen != 0)
-           fatal (_("unexpected version stringtable value length %ld"), (long) vallen);
+         vi->u.string.stringtables = NULL;
+         ppvst = &vi->u.string.stringtables;
 
-         data += off;
-         length -= off;
-         verlen -= off;
+         while (verlen > 0)
+           {
+             rc_ver_stringtable *vst;
+             rc_uint_type stverlen;
+             rc_ver_stringinfo **ppvs;
 
-         vi->u.string.strings = NULL;
-         ppvs = &vi->u.string.strings;
+             if (length < 8)
+               toosmall (_("version stringtable"));
 
-         /* It's convenient to round verlen to a 4 byte alignment,
-             since we round the subvariables in the loop.  */
-         verlen = (verlen + 3) &~ 3;
+             vst = (rc_ver_stringtable *) res_alloc (sizeof (rc_ver_stringtable));
 
-         while (verlen > 0)
+             get_version_header (wrbfd, data, length, (const char *) NULL,
+                                 &vst->language, &stverlen, &vallen, &type, &off);
+
+             if (vallen != 0)
+               fatal (_("unexpected version stringtable value length %ld"), (long) vallen);
+
+             data += off;
+             length -= off;
+             verlen -= off;
+
+         stverlen -= off;
+
+         vst->strings = NULL;
+         ppvs = &vst->strings;
+
+         while (stverlen > 0)
            {
              rc_ver_stringinfo *vs;
-             rc_uint_type subverlen, vslen, valoff;
+             rc_uint_type sverlen, vslen, valoff;
 
-             vs = (rc_ver_stringinfo *) res_alloc (sizeof *vs);
+             if (length < 8)
+               toosmall (_("version string"));
 
-             get_version_header (wrbfd, data, length,
-                                 (const char *) NULL, &vs->key, &subverlen,
-                                 &vallen, &type, &off);
+             vs = (rc_ver_stringinfo *) res_alloc (sizeof (rc_ver_stringinfo));
 
-             subverlen = (subverlen + 3) &~ 3;
+             get_version_header (wrbfd, data, length, (const char *) NULL,
+                                 &vs->key, &sverlen, &vallen, &type, &off);
 
              data += off;
              length -= off;
 
              vs->value = get_unicode (wrbfd, data, length, &vslen);
              valoff = vslen * 2 + 2;
-             valoff = (valoff + 3) &3;
+             valoff = (valoff + 3) & ~3;
 
-             if (off + valoff != subverlen)
+             if (off + valoff != sverlen)
                fatal (_("unexpected version string length %ld != %ld + %ld"),
-                      (long) subverlen, (long) off, (long) valoff);
-
-             vs->next = NULL;
-             *ppvs = vs;
-             ppvs = &vs->next;
+                      (long) sverlen, (long) off, (long) valoff);
 
              data += valoff;
              length -= valoff;
 
-             if (verlen < subverlen)
+             if (stverlen < sverlen)
                fatal (_("unexpected version string length %ld < %ld"),
-                      (long) verlen, (long) subverlen);
+                      (long) verlen, (long) sverlen);
+             stverlen -= sverlen;
+             verlen -= sverlen;
 
-             verlen -= subverlen;
+             vs->next = NULL;
+             *ppvs = vs;
+             ppvs = &vs->next;
+           }
+
+         vst->next = NULL;
+         *ppvst = vst;
+         ppvst = &vst->next;
            }
        }
       else if (ch == 'V')
@@ -1146,8 +1166,15 @@ bin_to_res_version (windres_bfd *wrbfd, const bfd_byte *data, rc_uint_type lengt
              vallen -= 4;
            }
        }
+      else if (ch == 0)
+       {
+         if (length == 8)
+           /* Padding - skip.  */
+           break;
+         fatal (_("nul bytes found in version string"));
+       }
       else
-       fatal (_("unexpected version string"));
+       fatal (_("unexpected version string character: %x"), ch);
 
       vi->next = NULL;
       *pp = vi;
@@ -1189,7 +1216,7 @@ bin_to_res_userdata (windres_bfd *wrbfd ATTRIBUTE_UNUSED, const bfd_byte *data,
 }
 \f
 static rc_res_resource *
-bin_to_res_toolbar (windres_bfd *wrbfd, const bfd_byte *data, rc_uint_type length)
+bin_to_res_toolbar (windres_bfd *wrbfd, const bfd_byte *data)
 {
   rc_toolbar *ri;
   rc_res_resource *r;
@@ -1202,7 +1229,6 @@ bin_to_res_toolbar (windres_bfd *wrbfd, const bfd_byte *data, rc_uint_type lengt
   ri->items = NULL;
 
   data += 12;
-  length -= 12;
   for (i=0 ; i < ri->nitems; i++)
   {
     rc_toolbar_item *it;
@@ -1211,7 +1237,6 @@ bin_to_res_toolbar (windres_bfd *wrbfd, const bfd_byte *data, rc_uint_type lengt
     it->id.u.id = (int) windres_get_32 (wrbfd, data, 4);
     it->prev = it->next = NULL;
     data += 4;
-    length -= 4;
     if(ri->items) {
       rc_toolbar_item *ii = ri->items;
       while (ii->next != NULL)
@@ -1300,7 +1325,7 @@ resid_to_bin (windres_bfd *wrbfd, rc_uint_type off, rc_res_id id)
       if (wrbfd)
        {
          struct bin_res_id bri;
-         
+
          windres_put_16 (wrbfd, bri.sig, 0xffff);
          windres_put_16 (wrbfd, bri.id, id.u.id);
          set_windres_bfd_content (wrbfd, &bri, off, BIN_RES_ID);
@@ -1356,12 +1381,8 @@ static rc_uint_type
 res_to_bin_accelerator (windres_bfd *wrbfd, rc_uint_type off,
                        const rc_accelerator *accelerators)
 {
-  bindata *first, **pp;
   const rc_accelerator *a;
 
-  first = NULL;
-  pp = &first;
-
   for (a = accelerators; a != NULL; a = a->next)
     {
       if (wrbfd)
@@ -1542,7 +1563,7 @@ res_to_bin_dialog (windres_bfd *wrbfd, rc_uint_type off, const rc_dialog *dialog
              windres_put_32 (wrbfd, bdc.id, dc->id);
              set_windres_bfd_content (wrbfd, &bdc, off, BIN_DIALOGEX_CONTROL_SIZE);
            }
-       }      
+       }
       off += (dialogex != 0 ? BIN_DIALOGEX_CONTROL_SIZE : BIN_DIALOG_CONTROL_SIZE);
 
       off = resid_to_bin (wrbfd, off, dc->class);
@@ -1560,7 +1581,6 @@ res_to_bin_dialog (windres_bfd *wrbfd, rc_uint_type off, const rc_dialog *dialog
        {
          rc_uint_type saved_off = off;
          rc_uint_type old_off;
-         off += (4 - ((off - off_delta) & 3)) & 3;
 
          old_off = off;
          off = res_to_bin_rcdata (wrbfd, off, dc->data);
@@ -1568,10 +1588,10 @@ res_to_bin_dialog (windres_bfd *wrbfd, rc_uint_type off, const rc_dialog *dialog
            old_off = off = saved_off;
          if (wrbfd)
            windres_put_16 (wrbfd, dc_rclen, off - old_off);
-           }
+       }
       if (wrbfd)
        set_windres_bfd_content (wrbfd, dc_rclen, marker, 2);
-       }
+    }
 
   if (wrbfd)
     {
@@ -1870,6 +1890,7 @@ res_to_bin_stringtable (windres_bfd *wrbfd, rc_uint_type off,
       unichar *s;
 
       slen = (rc_uint_type) st->strings[i].length;
+      if (slen == 0xffffffff) slen = 0;
       s = st->strings[i].string;
 
       length = 2 + slen * 2;
@@ -2007,52 +2028,62 @@ res_to_bin_versioninfo (windres_bfd *wrbfd, rc_uint_type off,
          abort ();
        case VERINFO_STRING:
          {
-           struct bin_ver_info bvsd;
-           rc_uint_type vs_off;
-           const rc_ver_stringinfo *vs;
+           const rc_ver_stringtable *vst;
 
            off = string_to_unicode_bin (wrbfd, off, "StringFileInfo");
-           off += (4 - ((off - off_delta) & 3)) & 3;
-
-           vs_off = off;
 
-           off += BIN_VER_INFO_SIZE;
+           if (!vi->u.string.stringtables)
+             off += (4 - ((off - off_delta) & 3)) & 3;
 
-           off = unicode_to_bin (wrbfd, off, vi->u.string.language);
-
-           for (vs = vi->u.string.strings; vs != NULL; vs = vs->next)
+           for (vst = vi->u.string.stringtables; vst != NULL; vst = vst->next)
              {
-               struct bin_ver_info bvss;
-               rc_uint_type vss_off,str_off;
+               struct bin_ver_info bvst;
+               rc_uint_type vst_off;
+               const rc_ver_stringinfo *vs;
 
                off += (4 - ((off - off_delta) & 3)) & 3;
 
-               vss_off = off;
+               vst_off = off;
                off += BIN_VER_INFO_SIZE;
 
-               off = unicode_to_bin (wrbfd, off, vs->key);
+               off = unicode_to_bin (wrbfd, off, vst->language);
 
-               off += (4 - ((off - off_delta) & 3)) & 3;
+               for (vs = vst->strings; vs != NULL; vs = vs->next)
+                 {
+                   struct bin_ver_info bvs;
+                   rc_uint_type vs_off, str_off;
+
+                   off += (4 - ((off - off_delta) & 3)) & 3;
+
+                   vs_off = off;
+                   off += BIN_VER_INFO_SIZE;
+
+                   off = unicode_to_bin (wrbfd, off, vs->key);
+
+                   off += (4 - ((off - off_delta) & 3)) & 3;
+
+                   str_off = off;
+                   off = unicode_to_bin (wrbfd, off, vs->value);
+
+                   if (wrbfd)
+                     {
+                       windres_put_16 (wrbfd, bvs.size, off - vs_off);
+                       windres_put_16 (wrbfd, bvs.sig1, (off - str_off) / 2);
+                       windres_put_16 (wrbfd, bvs.sig2, 1);
+                       set_windres_bfd_content (wrbfd, &bvs, vs_off,
+                                                BIN_VER_INFO_SIZE);
+                     }
+                 }
 
-               str_off = off;
-               off = unicode_to_bin (wrbfd, off, vs->value);
                if (wrbfd)
                  {
-                   windres_put_16 (wrbfd, bvss.size, off - vss_off);
-                   windres_put_16 (wrbfd, bvss.sig1, (off - str_off) / 2);
-                   windres_put_16 (wrbfd, bvss.sig2, 1);
-                   set_windres_bfd_content (wrbfd, &bvss, vss_off,
-                                            BIN_VER_INFO_SIZE);
+                   windres_put_16 (wrbfd, bvst.size, off - vst_off);
+                   windres_put_16 (wrbfd, bvst.sig1, 0);
+                   windres_put_16 (wrbfd, bvst.sig2, 1);
+                   set_windres_bfd_content (wrbfd, &bvst, vst_off,
+                                            BIN_VER_INFO_SIZE);
                  }
              }
-           if (wrbfd)
-             {
-               windres_put_16 (wrbfd, bvsd.size, off - vs_off);
-               windres_put_16 (wrbfd, bvsd.sig1, 0);
-               windres_put_16 (wrbfd, bvsd.sig2, 0);
-               set_windres_bfd_content (wrbfd, &bvsd, vs_off,
-                                        BIN_VER_INFO_SIZE);
-             }
            break;
          }
 
@@ -2102,9 +2133,9 @@ res_to_bin_versioninfo (windres_bfd *wrbfd, rc_uint_type off,
 
       if (wrbfd)
        {
-         windres_put_16 (wrbfd, bv.size, off-bv_off);
+         windres_put_16 (wrbfd, bv.size, off - bv_off);
          windres_put_16 (wrbfd, bv.sig1, 0);
-         windres_put_16 (wrbfd, bv.sig2, 0);
+         windres_put_16 (wrbfd, bv.sig2, 1);
          set_windres_bfd_content (wrbfd, &bv, bv_off,
                                   BIN_VER_INFO_SIZE);
        }