[PATCH v4] ld: Add --encoded-package-metadata

Benjamin Drung benjamin.drung@canonical.com
Thu Jul 18 09:04:33 GMT 2024


Specifying the compiler flag `-Wl,--package-metadata=<JSON>` might not
work, because the shells might eat the quotation marks and the compiler
splits the JSON at the commas.

Ubuntu tried to using a specs file to set `--package-metadata` but that
turned out to be too fragile. autopkgtests might use the compiler flags
but the needed environment variables are not set in the test
environment. Debugging a crash of an application build with the -spec
parameter lacks the environment variables. People like to iteratively
continue building the software in the build directory while hacking on
the package and then have no environment variable set.

So introduce a `--encoded-package-metadata` linker flag that takes a
percent-encoded JSON. Percent-encoding is used because it is a
standard and simple to implement.

Bug-Ubutru: https://bugs.launchpad.net/bugs/2071468
Signed-off-by: Benjamin Drung <benjamin.drung@canonical.com>
---
 ld/NEWS                              |  5 +++
 ld/emultempl/elf.em                  | 19 +++++++++++
 ld/ld.texi                           | 15 ++++++++-
 ld/ldlex.h                           |  1 +
 ld/ldmisc.c                          | 49 ++++++++++++++++++++++++++++
 ld/ldmisc.h                          |  1 +
 ld/lexsup.c                          |  2 ++
 ld/testsuite/ld-elf/package-note.exp | 18 ++++++++++
 ld/testsuite/ld-elf/package-note2.rd |  6 ++++
 9 files changed, 115 insertions(+), 1 deletion(-)
 create mode 100644 ld/testsuite/ld-elf/package-note2.rd

diff --git a/ld/NEWS b/ld/NEWS
index e0b9341a8ef..250ce353068 100644
--- a/ld/NEWS
+++ b/ld/NEWS
@@ -12,6 +12,11 @@
 
 * Add -plugin-save-temps to store plugin intermediate files permanently.
 
+* The ELF linker now supports a new --encoded-package-metadata option that
+  is equivalent to --package-metadata, but takes the JSON payload
+  percent-encoded to ease passing around this option without getting the
+  JSON payload corrupted.
+
 Changes in 2.42:
 
 * Add -z mark-plt/-z nomark-plt options to x86-64 ELF linker to mark PLT
diff --git a/ld/emultempl/elf.em b/ld/emultempl/elf.em
index 863657e12f5..1a893f35416 100644
--- a/ld/emultempl/elf.em
+++ b/ld/emultempl/elf.em
@@ -813,6 +813,7 @@ EOF
 fi
 fragment <<EOF
     {"build-id", optional_argument, NULL, OPTION_BUILD_ID},
+    {"encoded-package-metadata", optional_argument, NULL, OPTION_ENCODED_PACKAGE_METADATA},
     {"package-metadata", optional_argument, NULL, OPTION_PACKAGE_METADATA},
     {"compress-debug-sections", required_argument, NULL, OPTION_COMPRESS_DEBUG},
     {"rosegment", no_argument, NULL, OPTION_ROSEGMENT},
@@ -864,6 +865,24 @@ gld${EMULATION_NAME}_handle_option (int optc)
 	ldelf_emit_note_gnu_build_id = xstrdup (optarg);
       break;
 
+    case OPTION_ENCODED_PACKAGE_METADATA:
+      free ((char *) ldelf_emit_note_fdo_package_metadata);
+      ldelf_emit_note_fdo_package_metadata = NULL;
+      if (optarg != NULL)
+	{
+	  size_t optarg_len = strlen (optarg);
+	  if (optarg_len > 0)
+	    {
+	      char *package_metadata = xmalloc (optarg_len + 1);
+	      int len = percent_decode (optarg, package_metadata);
+	      if (len < 0)
+		einfo (_("%F%P: Failed to decode percent-encoded package"
+		         " metadata \`%s'\n"), optarg);
+	      ldelf_emit_note_fdo_package_metadata = package_metadata;
+	    }
+        }
+      break;
+
     case OPTION_PACKAGE_METADATA:
       free ((char *) ldelf_emit_note_fdo_package_metadata);
       ldelf_emit_note_fdo_package_metadata = NULL;
diff --git a/ld/ld.texi b/ld/ld.texi
index 89e3913317a..28733b1ae8e 100644
--- a/ld/ld.texi
+++ b/ld/ld.texi
@@ -3234,6 +3234,19 @@ string identifying the original linked file does not change.
 Passing @code{none} for @var{style} disables the setting from any
 @code{--build-id} options earlier on the command line.
 
+@kindex --encoded-package-metadata=@var{PERCENT_ENCODED_JSON}
+@item --encoded-package-metadata=@var{PERCENT_ENCODED_JSON}
+Request the creation of a @code{.note.package} ELF note section.  The
+contents of the note are in JSON format, as per the package metadata
+specification.  For more information see:
+https://systemd.io/ELF_PACKAGE_METADATA/
+If the PERCENT_ENCODED_JSON argument is missing/empty then this will
+disable the creation of the metadata note, if one had been enabled by
+an earlier occurrence of the --encoded-package-metadata or
+--package-metadata option. PERCENT_ENCODED_JSON will be percent-decoded.
+If the linker has been built with libjansson, then the JSON string
+will be validated.
+
 @kindex --package-metadata=@var{JSON}
 @item --package-metadata=@var{JSON}
 Request the creation of a @code{.note.package} ELF note section.  The
@@ -3242,7 +3255,7 @@ specification.  For more information see:
 https://systemd.io/ELF_PACKAGE_METADATA/
 If the JSON argument is missing/empty then this will disable the
 creation of the metadata note, if one had been enabled by an earlier
-occurrence of the --package-metadata option.
+occurrence of the --encoded-package-metadata or --package-metadata option.
 If the linker has been built with libjansson, then the JSON string
 will be validated.
 @end table
diff --git a/ld/ldlex.h b/ld/ldlex.h
index defe3fcbbb9..225d95b1e90 100644
--- a/ld/ldlex.h
+++ b/ld/ldlex.h
@@ -284,6 +284,7 @@ enum option_values
   OPTION_EH_FRAME_HDR,
   OPTION_NO_EH_FRAME_HDR,
   OPTION_HASH_STYLE,
+  OPTION_ENCODED_PACKAGE_METADATA,
   OPTION_PACKAGE_METADATA,
   OPTION_AUDIT,
   OPTION_COMPRESS_DEBUG,
diff --git a/ld/ldmisc.c b/ld/ldmisc.c
index 180b24b3448..ff98226afd6 100644
--- a/ld/ldmisc.c
+++ b/ld/ldmisc.c
@@ -770,3 +770,52 @@ ld_abort (const char *file, int line, const char *fn)
   einfo (_("%F%P: please report this bug\n"));
   xexit (1);
 }
+
+/* Decode a hexadecimal character. Return -1 on error. */
+static int
+hexdecode (char c)
+{
+  if ('0' <= c && c <= '9')
+    return c - '0';
+  if ('A' <= c && c <= 'F')
+    return c - 'A' + 10;
+  if ('a' <= c && c <= 'f')
+    return c - 'a' + 10;
+  return -1;
+}
+
+/* Decode a percent-encoded string. dst must be at least the same size as src.
+   It can be converted in place. Returns the lenght of the decoded string
+   (without training null character) or -1 on error. */
+int
+percent_decode (const char *src, char *dst)
+{
+  int length = 0;
+  while (*src != '\0')
+    {
+      char c = *src++;
+      if (c != '%')
+	{
+	  *dst++ = c;
+	  length += 1;
+	  continue;
+	}
+      char next1 = *src++;
+      if (next1 == '%')
+	{
+	  *dst++ = '%';
+	  length += 1;
+	  continue;
+	}
+      int hex1 = hexdecode (next1);
+      if (hex1 == -1)
+	return -1;
+      int hex2 = hexdecode (*src++);
+      if (hex2 == -1)
+	return -1;
+      *dst++ = (char) ((hex1 << 4) + hex2);
+      length += 1;
+    }
+  *dst = '\0';
+  return length;
+}
diff --git a/ld/ldmisc.h b/ld/ldmisc.h
index 20289127c0a..815e8ccb548 100644
--- a/ld/ldmisc.h
+++ b/ld/ldmisc.h
@@ -39,5 +39,6 @@ do { info_assert(__FILE__,__LINE__); } while (0)
 extern void print_spaces (int);
 #define print_space() print_spaces (1)
 extern void print_nl (void);
+extern int percent_decode (const char *, char *);
 
 #endif
diff --git a/ld/lexsup.c b/ld/lexsup.c
index 4aa0124ce2f..60e329a713f 100644
--- a/ld/lexsup.c
+++ b/ld/lexsup.c
@@ -2279,6 +2279,8 @@ elf_static_list_options (FILE *file)
   fprintf (file, _("\
   --build-id[=STYLE]          Generate build ID note\n"));
   fprintf (file, _("\
+  --encoded-package-metadata[=PERCENT_ENCODED_JSON]   Generate package metadata note\n"));
+  fprintf (file, _("\
   --package-metadata[=JSON]   Generate package metadata note\n"));
   fprintf (file, _("\
   --compress-debug-sections=[none|zlib|zlib-gnu|zlib-gabi|zstd]\n\
diff --git a/ld/testsuite/ld-elf/package-note.exp b/ld/testsuite/ld-elf/package-note.exp
index 9f18705bd65..b4feef541b4 100644
--- a/ld/testsuite/ld-elf/package-note.exp
+++ b/ld/testsuite/ld-elf/package-note.exp
@@ -42,4 +42,22 @@ run_ld_link_tests [list \
 	{{readelf {--notes} package-note.rd}} \
 	"package-note.o" \
     ] \
+    [list \
+	"package-note1b.o" \
+	"--encoded-package-metadata=%7B%22foo%22%3A%22bar%22%7D" \
+	"" \
+	"" \
+	{start.s} \
+	{{readelf {--notes} package-note.rd}} \
+	"package-note1b.o" \
+    ] \
+    [list \
+	"package-note2.o" \
+	"--encoded-package-metadata=%7B%22name%22:%22binutils%22%2C%22foo%22%3A%22bar%22%7d" \
+	"" \
+	"" \
+	{start.s} \
+	{{readelf {--notes} package-note2.rd}} \
+	"package-note2.o" \
+    ] \
 ]
diff --git a/ld/testsuite/ld-elf/package-note2.rd b/ld/testsuite/ld-elf/package-note2.rd
new file mode 100644
index 00000000000..c24c37825ad
--- /dev/null
+++ b/ld/testsuite/ld-elf/package-note2.rd
@@ -0,0 +1,6 @@
+#...
+Displaying notes found in: \.note\.package
+\s+Owner\s+Data\s+size\s+Description
+\s+FDO\s+0x00000020\s+(Unknown note type:\s+\(0xcafe1a7e\)|FDO_PACKAGING_METADATA)
+\s+(description data:\s+.*|Packaging Metadata:\s+{"name":"binutils","foo":"bar"})
+#pass
-- 
2.43.0



More information about the Binutils mailing list