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>
* 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).
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:
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.
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';
+}
extern void print_spaces (int);
#define print_space() print_spaces (1)
extern void print_nl (void);
+extern void percent_decode (const char *, char *);
#endif
{{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" \
+ ] \
]
--- /dev/null
+#...
+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