From 479edf0a6a61159486f14d5e62403f8769cc591d Mon Sep 17 00:00:00 2001
From: Nick Clifton <nickc@redhat.com>
Date: Wed, 10 Jul 2024 15:01:39 +0100
Subject: Add support for a .base64 pseudo-op to gas

  PR 31964
---
 gas/read.c | 323 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 323 insertions(+)

(limited to 'gas/read.c')

diff --git a/gas/read.c b/gas/read.c
index 5b411e9..e6bbb89 100644
--- a/gas/read.c
+++ b/gas/read.c
@@ -357,6 +357,7 @@ static const pseudo_typeS potable[] = {
   {"balign", s_align_bytes, 0},
   {"balignw", s_align_bytes, -2},
   {"balignl", s_align_bytes, -4},
+  {"base64", s_base64, 0},
 /* block  */
 #ifdef HANDLE_BUNDLE
   {"bundle_align_mode", s_bundle_align_mode, 0},
@@ -5448,6 +5449,328 @@ stringer_append_char (int c, int bitsize)
     FRAG_APPEND_1_CHAR (c);
 }
 
+/* Code for handling base64 encoded strings.
+   Based upon code in sharutils' lib/base64.c source file, written by
+   Simon Josefsson.  Which was partially adapted from GNU MailUtils
+   (mailbox/filter_trans.c, as of 2004-11-28) and improved by review
+   from Paul Eggert, Bruno Haible, and Stepan Kasal.  */
+
+#define B64(_)           \
+  (  (_) == 'A' ? 0      \
+   : (_) == 'B' ? 1      \
+   : (_) == 'C' ? 2      \
+   : (_) == 'D' ? 3      \
+   : (_) == 'E' ? 4      \
+   : (_) == 'F' ? 5      \
+   : (_) == 'G' ? 6      \
+   : (_) == 'H' ? 7      \
+   : (_) == 'I' ? 8      \
+   : (_) == 'J' ? 9      \
+   : (_) == 'K' ? 10     \
+   : (_) == 'L' ? 11     \
+   : (_) == 'M' ? 12     \
+   : (_) == 'N' ? 13     \
+   : (_) == 'O' ? 14     \
+   : (_) == 'P' ? 15     \
+   : (_) == 'Q' ? 16     \
+   : (_) == 'R' ? 17     \
+   : (_) == 'S' ? 18     \
+   : (_) == 'T' ? 19     \
+   : (_) == 'U' ? 20     \
+   : (_) == 'V' ? 21     \
+   : (_) == 'W' ? 22     \
+   : (_) == 'X' ? 23     \
+   : (_) == 'Y' ? 24     \
+   : (_) == 'Z' ? 25     \
+   : (_) == 'a' ? 26     \
+   : (_) == 'b' ? 27     \
+   : (_) == 'c' ? 28     \
+   : (_) == 'd' ? 29     \
+   : (_) == 'e' ? 30     \
+   : (_) == 'f' ? 31     \
+   : (_) == 'g' ? 32     \
+   : (_) == 'h' ? 33     \
+   : (_) == 'i' ? 34     \
+   : (_) == 'j' ? 35     \
+   : (_) == 'k' ? 36     \
+   : (_) == 'l' ? 37     \
+   : (_) == 'm' ? 38     \
+   : (_) == 'n' ? 39     \
+   : (_) == 'o' ? 40     \
+   : (_) == 'p' ? 41     \
+   : (_) == 'q' ? 42     \
+   : (_) == 'r' ? 43     \
+   : (_) == 's' ? 44     \
+   : (_) == 't' ? 45     \
+   : (_) == 'u' ? 46     \
+   : (_) == 'v' ? 47     \
+   : (_) == 'w' ? 48     \
+   : (_) == 'x' ? 49     \
+   : (_) == 'y' ? 50     \
+   : (_) == 'z' ? 51     \
+   : (_) == '0' ? 52     \
+   : (_) == '1' ? 53     \
+   : (_) == '2' ? 54     \
+   : (_) == '3' ? 55     \
+   : (_) == '4' ? 56     \
+   : (_) == '5' ? 57     \
+   : (_) == '6' ? 58     \
+   : (_) == '7' ? 59     \
+   : (_) == '8' ? 60     \
+   : (_) == '9' ? 61     \
+   : (_) == '+' ? 62     \
+   : (_) == '/' ? 63     \
+   : -1)
+
+static const signed char b64[0x100] =
+{
+  B64 (0),   B64 (1),   B64 (2),   B64 (3),
+  B64 (4),   B64 (5),   B64 (6),   B64 (7),
+  B64 (8),   B64 (9),   B64 (10),  B64 (11),
+  B64 (12),  B64 (13),  B64 (14),  B64 (15),
+  B64 (16),  B64 (17),  B64 (18),  B64 (19),
+  B64 (20),  B64 (21),  B64 (22),  B64 (23),
+  B64 (24),  B64 (25),  B64 (26),  B64 (27),
+  B64 (28),  B64 (29),  B64 (30),  B64 (31),
+  B64 (32),  B64 (33),  B64 (34),  B64 (35),
+  B64 (36),  B64 (37),  B64 (38),  B64 (39),
+  B64 (40),  B64 (41),  B64 (42),  B64 (43),
+  B64 (44),  B64 (45),  B64 (46),  B64 (47),
+  B64 (48),  B64 (49),  B64 (50),  B64 (51),
+  B64 (52),  B64 (53),  B64 (54),  B64 (55),
+  B64 (56),  B64 (57),  B64 (58),  B64 (59),
+  B64 (60),  B64 (61),  B64 (62),  B64 (63),
+  B64 (64),  B64 (65),  B64 (66),  B64 (67),
+  B64 (68),  B64 (69),  B64 (70),  B64 (71),
+  B64 (72),  B64 (73),  B64 (74),  B64 (75),
+  B64 (76),  B64 (77),  B64 (78),  B64 (79),
+  B64 (80),  B64 (81),  B64 (82),  B64 (83),
+  B64 (84),  B64 (85),  B64 (86),  B64 (87),
+  B64 (88),  B64 (89),  B64 (90),  B64 (91),
+  B64 (92),  B64 (93),  B64 (94),  B64 (95),
+  B64 (96),  B64 (97),  B64 (98),  B64 (99),
+  B64 (100), B64 (101), B64 (102), B64 (103),
+  B64 (104), B64 (105), B64 (106), B64 (107),
+  B64 (108), B64 (109), B64 (110), B64 (111),
+  B64 (112), B64 (113), B64 (114), B64 (115),
+  B64 (116), B64 (117), B64 (118), B64 (119),
+  B64 (120), B64 (121), B64 (122), B64 (123),
+  B64 (124), B64 (125), B64 (126), B64 (127),
+  B64 (128), B64 (129), B64 (130), B64 (131),
+  B64 (132), B64 (133), B64 (134), B64 (135),
+  B64 (136), B64 (137), B64 (138), B64 (139),
+  B64 (140), B64 (141), B64 (142), B64 (143),
+  B64 (144), B64 (145), B64 (146), B64 (147),
+  B64 (148), B64 (149), B64 (150), B64 (151),
+  B64 (152), B64 (153), B64 (154), B64 (155),
+  B64 (156), B64 (157), B64 (158), B64 (159),
+  B64 (160), B64 (161), B64 (162), B64 (163),
+  B64 (164), B64 (165), B64 (166), B64 (167),
+  B64 (168), B64 (169), B64 (170), B64 (171),
+  B64 (172), B64 (173), B64 (174), B64 (175),
+  B64 (176), B64 (177), B64 (178), B64 (179),
+  B64 (180), B64 (181), B64 (182), B64 (183),
+  B64 (184), B64 (185), B64 (186), B64 (187),
+  B64 (188), B64 (189), B64 (190), B64 (191),
+  B64 (192), B64 (193), B64 (194), B64 (195),
+  B64 (196), B64 (197), B64 (198), B64 (199),
+  B64 (200), B64 (201), B64 (202), B64 (203),
+  B64 (204), B64 (205), B64 (206), B64 (207),
+  B64 (208), B64 (209), B64 (210), B64 (211),
+  B64 (212), B64 (213), B64 (214), B64 (215),
+  B64 (216), B64 (217), B64 (218), B64 (219),
+  B64 (220), B64 (221), B64 (222), B64 (223),
+  B64 (224), B64 (225), B64 (226), B64 (227),
+  B64 (228), B64 (229), B64 (230), B64 (231),
+  B64 (232), B64 (233), B64 (234), B64 (235),
+  B64 (236), B64 (237), B64 (238), B64 (239),
+  B64 (240), B64 (241), B64 (242), B64 (243),
+  B64 (244), B64 (245), B64 (246), B64 (247),
+  B64 (248), B64 (249), B64 (250), B64 (251),
+  B64 (252), B64 (253), B64 (254), B64 (255)
+};
+
+static bool
+is_base64_char (unsigned int c)
+{
+  return (c < 0x100) && (b64[c] != -1);
+}
+
+static void
+decode_base64_and_append (unsigned int b[4], int len)
+{
+  gas_assert (len > 1);
+
+  FRAG_APPEND_1_CHAR ((b64[b[0]] << 2) | (b64[b[1]] >> 4));
+  if (len == 2) return;
+  FRAG_APPEND_1_CHAR (((b64[b[1]] << 4) & 0xf0) | (b64[b[2]] >> 2));
+  if (len == 3) return;
+  FRAG_APPEND_1_CHAR (((b64[b[2]] << 6) & 0xc0) | b64[b[3]]);
+}
+
+/* Accept one or more comma separated, base64 encoded strings.  Decode them
+   and store them at the current point in the current section.  The strings
+   must be enclosed in double quotes.  Line breaks, quoted characters and
+   escaped characters are not allowed.  Only the characters "A-Za-z0-9+/" are
+   accepted inside the string.  The string must be a multiple of four
+   characters in length.  If the encoded string does not fit this requirement
+   it may use one or more '=' characters at the end as padding.  */
+
+void
+s_base64 (int dummy ATTRIBUTE_UNUSED)
+{
+  unsigned int c;
+
+  /* If we have been switched into the abs_section then we
+     will not have an obstack onto which we can hang strings.  */
+  if (now_seg == absolute_section)
+    {
+      as_bad (_("base64 strings must be placed into a section"));
+      ignore_rest_of_line ();
+      return;
+    }
+
+  if (is_it_end_of_statement ())
+    {
+      as_bad (_("a string must follow the .base64 pseudo-op"));
+      return;
+    }
+
+#ifdef md_flush_pending_output
+  md_flush_pending_output ();
+#endif
+
+#ifdef md_cons_align
+  md_cons_align (1);
+#endif
+
+  do
+    {
+      SKIP_ALL_WHITESPACE ();
+
+      c = * input_line_pointer ++;
+
+      if (c != '"')
+	{
+	  as_bad (_("expected double quote enclosed string as argument to .base64 pseudo-op"));
+	  ignore_rest_of_line ();
+	  return;
+	}
+
+      /* Read a block of four base64 encoded characters.  */
+      int i;
+      unsigned int b[4];
+      bool seen_equals = false;
+
+    loop:
+      for (i = 0; i < 4; i++)
+      	{
+	  c = * input_line_pointer ++;
+
+	  if (c >= 256 || is_end_of_line [c])
+	    {
+	      as_bad (_("end of line encountered inside .base64 string"));
+	      ignore_rest_of_line ();
+	      return;
+	    }
+
+	  if (c == '"')
+	    {
+	      /* We allow this.  But only if there were enough
+		 characters to form a valid base64 encoding.  */
+	      if (i > 1)
+		{
+		  as_warn (_(".base64 string terminated early"));
+		  -- input_line_pointer;
+		  break;
+		}
+
+	      as_bad (_(".base64 string terminated unexpectedly"));
+	      ignore_rest_of_line ();
+	      return;
+	    }
+
+	  if (seen_equals && c != '=')
+	    {
+	      as_bad (_("equals character only allowed at end of .base64 string"));
+	      ignore_rest_of_line ();
+	      return;
+	    }
+
+	  if (c == '=')
+	    {
+	      if (i == 0)
+		{
+		  as_bad (_("the equals character cannot start a block of four base64 encoded bytes"));
+		  ignore_rest_of_line ();
+		  return;
+		}
+	      else if (i == 1)
+		{
+		  as_bad (_("the equals character cannot be the second character in a block of four base64 encoded bytes"));
+		  ignore_rest_of_line ();
+		  return;
+		}
+
+	      seen_equals = true;
+	    }
+	  else if (! is_base64_char (c))
+	    {
+	      if (ISPRINT (c))
+		as_bad (_("invalid character '%c' found inside .base64 string"), c);
+	      else
+		as_bad (_("invalid character %#x found inside .base64 string"), c);
+	      ignore_rest_of_line ();
+	      return;
+	    }
+
+	  b[i] = c;
+	}
+
+      if (seen_equals && i == 4)
+	{
+	  -- i;
+	  if (b[2] == '=')
+	    -- i;
+	}
+
+      /* We have a block of up to four valid base64 encoded bytes.  */
+      decode_base64_and_append (b, i);
+
+      /* Check the next character.  */
+      c = * input_line_pointer ++;
+
+      if (is_base64_char (c))
+	{
+	  if (seen_equals)
+	    {
+	      as_bad (_("no base64 characters expected after '=' padding characters"));
+	      ignore_rest_of_line ();
+	      return;
+	    }
+
+	  -- input_line_pointer;
+	  goto loop;
+	}
+      else if (c != '"')
+	{
+	  as_bad (_(".base64 string must have a terminating double quote character"));
+	  ignore_rest_of_line ();
+	  return;
+	}
+
+      SKIP_ALL_WHITESPACE ();
+      
+      c = * input_line_pointer ++;
+    }
+  while (c == ',');
+
+  -- input_line_pointer;
+
+  demand_empty_rest_of_line ();
+}
+
+
 /* Worker to do .ascii etc statements.
    Reads 0 or more ',' separated, double-quoted strings.
    Caller should have checked need_pass_2 is FALSE because we don't
-- 
cgit v1.1