aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenjamin Drung <benjamin.drung@canonical.com>2024-11-18 11:38:25 +0100
committerJan Beulich <jbeulich@suse.com>2024-11-18 11:38:25 +0100
commitb0cc81e87087bb8a6b12dc1e4fd7f2591927977b (patch)
treec8d753b1e4c066c636cb50fe43992f84f62a314f
parent20d9fb448c375d5f521eaed8cfc0c49a44803bd1 (diff)
downloadbinutils-b0cc81e87087bb8a6b12dc1e4fd7f2591927977b.zip
binutils-b0cc81e87087bb8a6b12dc1e4fd7f2591927977b.tar.gz
binutils-b0cc81e87087bb8a6b12dc1e4fd7f2591927977b.tar.bz2
ld: Support percent-encoded JSON in --package-metadata
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 support percent-encoded and %[string] encoded JSON data in the `--package-metadata` linker flag. Percent-encoding is used because it is a standard, simple to implement, and does take too many additional characters. %[string] encoding is supported for having a more readable encoding. 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>
-rw-r--r--ld/NEWS4
-rw-r--r--ld/emultempl/elf.em12
-rw-r--r--ld/ld.texi5
-rw-r--r--ld/ldmisc.c78
-rw-r--r--ld/ldmisc.h1
-rw-r--r--ld/testsuite/ld-elf/package-note.exp36
-rw-r--r--ld/testsuite/ld-elf/package-note2.rd6
7 files changed, 140 insertions, 2 deletions
diff --git a/ld/NEWS b/ld/NEWS
index f7de85b..98c3595 100644
--- a/ld/NEWS
+++ b/ld/NEWS
@@ -8,6 +8,10 @@ Changes in 2.44:
* Add a "--build-id=xx" option, if built with the xxhash library. This
produces a 128-bit hash, 2-4x faster than md5 or sha1.
+* The ELF linker option --package-metadata supports percent-encoded and
+ %[string] encoded JSON payload now to ease passing around this option
+ without getting the JSON payload corrupted.
+
Changes in 2.43:
* Add support for LoongArch DT_RELR (compressed R_LARCH_RELATIVE).
diff --git a/ld/emultempl/elf.em b/ld/emultempl/elf.em
index 2e86572..dae610e 100644
--- a/ld/emultempl/elf.em
+++ b/ld/emultempl/elf.em
@@ -867,8 +867,16 @@ gld${EMULATION_NAME}_handle_option (int optc)
case OPTION_PACKAGE_METADATA:
free ((char *) ldelf_emit_note_fdo_package_metadata);
ldelf_emit_note_fdo_package_metadata = NULL;
- if (optarg != NULL && strlen(optarg) > 0)
- ldelf_emit_note_fdo_package_metadata = xstrdup (optarg);
+ if (optarg != NULL)
+ {
+ size_t len = strlen (optarg);
+ if (len > 0)
+ {
+ char *package_metadata = xmalloc (len + 1);
+ percent_decode (optarg, package_metadata);
+ ldelf_emit_note_fdo_package_metadata = package_metadata;
+ }
+ }
break;
case OPTION_COMPRESS_DEBUG:
diff --git a/ld/ld.texi b/ld/ld.texi
index c66ac5d..417af3e 100644
--- a/ld/ld.texi
+++ b/ld/ld.texi
@@ -3253,6 +3253,11 @@ 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/
+The JSON argument support percent-encoding and following %[string]
+(where string refers to the name in HTML's Named Character References)
+encoding: @samp{%[comma]} for @samp{,}, @samp{%[lbrace]} for @samp{@{},
+@samp{%[quot]} for @samp{"}, @samp{%[rbrace]} for @samp{@}}, and
+@samp{%[space]} for space character.
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.
diff --git a/ld/ldmisc.c b/ld/ldmisc.c
index 180b24b..26c2763 100644
--- a/ld/ldmisc.c
+++ b/ld/ldmisc.c
@@ -770,3 +770,81 @@ 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 and/or %[string] encoded string. dst must be at least
+ the same size as src. It can be converted in place.
+
+ Following %[string] encodings are supported:
+
+ %[comma] for ,
+ %[lbrace] for {
+ %[quot] for "
+ %[rbrace] for }
+ %[space] for ' '
+
+ The percent decoding behaves the same as Python's urllib.parse.unquote. */
+void
+percent_decode (const char *src, char *dst)
+{
+ while (*src != '\0')
+ {
+ char c = *src++;
+ if (c == '%')
+ {
+ char next1 = *src;
+ int hex1 = hexdecode (next1);
+ if (hex1 != -1)
+ {
+ int hex2 = hexdecode (*(src + 1));
+ if (hex2 != -1)
+ {
+ c = (char) ((hex1 << 4) + hex2);
+ src += 2;
+ }
+ }
+ else if (next1 == '[')
+ {
+ if (strncmp (src + 1, "comma]", 6) == 0)
+ {
+ c = ',';
+ src += 7;
+ }
+ else if (strncmp (src + 1, "lbrace]", 7) == 0)
+ {
+ c = '{';
+ src += 8;
+ }
+ else if (strncmp (src + 1, "quot]", 5) == 0)
+ {
+ c = '"';
+ src += 6;
+ }
+ else if (strncmp (src + 1, "rbrace]", 7) == 0)
+ {
+ c = '}';
+ src += 8;
+ }
+ else if (strncmp (src + 1, "space]", 6) == 0)
+ {
+ c = ' ';
+ src += 7;
+ }
+ }
+ }
+ *dst++ = c;
+ }
+ *dst = '\0';
+}
diff --git a/ld/ldmisc.h b/ld/ldmisc.h
index 2028912..ff73d79 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 void percent_decode (const char *, char *);
#endif
diff --git a/ld/testsuite/ld-elf/package-note.exp b/ld/testsuite/ld-elf/package-note.exp
index 9f18705..a7f4777 100644
--- a/ld/testsuite/ld-elf/package-note.exp
+++ b/ld/testsuite/ld-elf/package-note.exp
@@ -42,4 +42,40 @@ run_ld_link_tests [list \
{{readelf {--notes} package-note.rd}} \
"package-note.o" \
] \
+ [list \
+ "package-note1b.o" \
+ "--package-metadata=%7B%22foo%22%3A%22bar%22%7D" \
+ "" \
+ "" \
+ {start.s} \
+ {{readelf {--notes} package-note.rd}} \
+ "package-note1b.o" \
+ ] \
+ [list \
+ "package-note1c.o" \
+ "--package-metadata=%\[lbrace\]%\[quot\]foo%\[quot\]:%\[quot\]bar%\[quot\]%\[rbrace\]" \
+ "" \
+ "" \
+ {start.s} \
+ {{readelf {--notes} package-note.rd}} \
+ "package-note1c.o" \
+ ] \
+ [list \
+ "package-note2.o" \
+ "--package-metadata=%7B%22name%22:%22binutils%22%2C%22ver%22%3A%22x%20%%22%7d" \
+ "" \
+ "" \
+ {start.s} \
+ {{readelf {--notes} package-note2.rd}} \
+ "package-note2.o" \
+ ] \
+ [list \
+ "package-note2b.o" \
+ "--package-metadata={%\[quot\]name%\[quot\]:%\[quot\]binutils%\[quot\]%\[comma\]%\[quot\]ver%\[quot\]:%\[quot\]x%\[space\]%%\[quot\]}" \
+ "" \
+ "" \
+ {start.s} \
+ {{readelf {--notes} package-note2.rd}} \
+ "package-note2b.o" \
+ ] \
]
diff --git a/ld/testsuite/ld-elf/package-note2.rd b/ld/testsuite/ld-elf/package-note2.rd
new file mode 100644
index 0000000..91c0f6d
--- /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","ver":"x %"})
+#pass