[PATCH v5] ld: Add --encoded-package-metadata
Benjamin Drung
benjamin.drung@canonical.com
Tue Jul 23 23:28:18 GMT 2024
Specifying the compiler flag `-Wl,--package-metadata=<JSON>` will not
work in case the JSON contains a comma, because compiler drivers eat
commas. Example:
```
$ echo "void main() { }" > test.c
$ gcc '-Wl,--package-metadata={"type":"deb","os":"ubuntu"}' test.c
/usr/bin/ld: cannot find "os":"ubuntu"}: No such file or directory
collect2: error: ld returned 1 exit status
```
The quotation marks in the JSON value do not work well with shell nor
make. Specifying the `--package-metadata` linker flag in a `LDFLAGS`
environment variable might loose its quotation marks when it hits the
final compiler call.
So introduce a `--encoded-package-metadata` linker flag that takes a
percent-encoded JSON. Percent-encoding is used because it is a
standard, simple to implement, and does take too many additional
characters.
Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=32003
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 c1e42139b8f..a1880ccf1bb 100644
--- a/ld/NEWS
+++ b/ld/NEWS
@@ -14,6 +14,11 @@ Changes in 2.43:
* 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