public inbox for binutils@sourceware.org
 help / color / mirror / Atom feed
From: Fangrui Song <i@maskray.me>
To: luca.boccassi@gmail.com
Cc: binutils@sourceware.org
Subject: Re: [PATCH] ld: add --package-metadata
Date: Mon, 16 May 2022 09:40:03 -0700	[thread overview]
Message-ID: <20220516164003.iivw6lthknhvce42@gmail.com> (raw)
In-Reply-To: <20220515191846.114257-1-luca.boccassi@gmail.com>

On 2022-05-15, luca.boccassi--- via Binutils wrote:
>From: Luca Boccassi <bluca@debian.org>
>
>Generate a .note.package FDO package metadata ELF note, following
>the spec: https://systemd.io/COREDUMP_PACKAGE_METADATA/
>
>If the jansson library is available at build time (and not explicitly
>disabled), link ld to it, and use it to validate that the input is
>correct JSON, to avoid writing garbage to the file.
>If no configure option is passed and jansson (or pkg-config) is not
>found, then it is just skipped (with a non-fatal warning). The
>configure option --enable-jansson can be used to explicitly enable it
>(error out when not found) or to explicitly disable it (don't even
>look for it). This allows bootstrappers (or others who are not
>interested) to seamlessly skip it without issues."
>---
>Now that this spec has shipped for all packages in Fedora 36, it seems
>like a good time to add a specific option for it to the default linker.
>Follows the same pattern of --build-id.

Both the "FDO" and the json dependency make me concerned -
if a linker script approach works quite well, why bother with a new
linker option with a very specific application?

--build-id is a bit different in that it adds a section whose content is
computed by the linker. I am not sure having --build-id implies
the necessity of --package-metadata.

>I've added an optional usage of libjansson to provide additional validation,
>so that it doesn't have to be reimplemented everywhere a thousand times over.
>For bootstrapping purposes the dep is optional and can be skipped, for example
>in Debian/Ubuntu it would be annotated with <!stage1> so that everything
>works out of the box.
>
> bfd/elf-bfd.h                        |   8 ++
> bfd/elf.c                            |   6 +-
> ld/Makefile.am                       |   6 +-
> ld/configure.ac                      |  35 ++++++++
> ld/emultempl/elf.em                  |   9 ++
> ld/ld.texi                           |   7 ++
> ld/ldelf.c                           | 121 ++++++++++++++++++++++++++-
> ld/ldelf.h                           |   2 +
> ld/lexsup.c                          |   2 +
> ld/testsuite/ld-elf/package-note.exp |  49 +++++++++++
> ld/testsuite/ld-elf/package-note.rd  |   6 ++
> 11 files changed, 244 insertions(+), 7 deletions(-)
> create mode 100644 ld/testsuite/ld-elf/package-note.exp
> create mode 100644 ld/testsuite/ld-elf/package-note.rd
>
>diff --git a/bfd/elf-bfd.h b/bfd/elf-bfd.h
>index c7c0a793b15..65bd1128263 100644
>--- a/bfd/elf-bfd.h
>+++ b/bfd/elf-bfd.h
>@@ -1918,6 +1918,14 @@ struct output_elf_obj_tdata
>     asection *sec;
>   } build_id;
>
>+  /* FDO_PACKAGING_METADATA note type info.  */
>+  struct
>+  {
>+    bool (*after_write_object_contents) (bfd *);
>+    const char *json;
>+    asection *sec;
>+  } package_metadata;
>+
>   /* Records the result of `get_program_header_size'.  */
>   bfd_size_type program_header_size;
>
>diff --git a/bfd/elf.c b/bfd/elf.c
>index c493aa5b172..558c630fd7b 100644
>--- a/bfd/elf.c
>+++ b/bfd/elf.c
>@@ -6779,8 +6779,10 @@ _bfd_elf_write_object_contents (bfd *abfd)
>     return false;
>
>   /* This is last since write_shdrs_and_ehdr can touch i_shdrp[0].  */
>-  if (t->o->build_id.after_write_object_contents != NULL)
>-    return (*t->o->build_id.after_write_object_contents) (abfd);
>+  if (t->o->build_id.after_write_object_contents != NULL && !(*t->o->build_id.after_write_object_contents) (abfd))
>+    return false;
>+  if (t->o->package_metadata.after_write_object_contents != NULL)
>+    return (*t->o->package_metadata.after_write_object_contents) (abfd);
>
>   return true;
> }
>diff --git a/ld/Makefile.am b/ld/Makefile.am
>index e53bef13bb8..a3cd7891840 100644
>--- a/ld/Makefile.am
>+++ b/ld/Makefile.am
>@@ -45,7 +45,7 @@ ELF_CLFAGS=-DELF_LIST_OPTIONS=@elf_list_options@ \
> 	   -DELF_PLT_UNWIND_LIST_OPTIONS=@elf_plt_unwind_list_options@
> WARN_CFLAGS = @WARN_CFLAGS@
> NO_WERROR = @NO_WERROR@
>-AM_CFLAGS = $(WARN_CFLAGS) $(ELF_CLFAGS)
>+AM_CFLAGS = $(WARN_CFLAGS) $(ELF_CLFAGS) $(JANSSON_CFLAGS)
>
> # We put the scripts in the directory $(scriptdir)/ldscripts.
> # We can't put the scripts in $(datadir) because the SEARCH_DIR
>@@ -964,8 +964,8 @@ ld_new_SOURCES = ldgram.y ldlex-wrapper.c lexsup.c ldlang.c mri.c ldctor.c ldmai
> 	ldwrite.c ldexp.c ldemul.c ldver.c ldmisc.c ldfile.c ldcref.c plugin.c \
> 	ldbuildid.c
> ld_new_DEPENDENCIES = $(EMULATION_OFILES) $(EMUL_EXTRA_OFILES) \
>-		      $(BFDLIB) $(LIBCTF) $(LIBIBERTY) $(LIBINTL_DEP)
>-ld_new_LDADD = $(EMULATION_OFILES) $(EMUL_EXTRA_OFILES) $(BFDLIB) $(LIBCTF) $(LIBIBERTY) $(LIBINTL) $(ZLIB)
>+		      $(BFDLIB) $(LIBCTF) $(LIBIBERTY) $(LIBINTL_DEP) $(JANSSON_LIBS)
>+ld_new_LDADD = $(EMULATION_OFILES) $(EMUL_EXTRA_OFILES) $(BFDLIB) $(LIBCTF) $(LIBIBERTY) $(LIBINTL) $(ZLIB) $(JANSSON_LIBS)
>
> # Dependency tracking for the generated emulation files.
> EXTRA_ld_new_SOURCES += $(ALL_EMULATION_SOURCES) $(ALL_64_EMULATION_SOURCES)
>diff --git a/ld/configure.ac b/ld/configure.ac
>index 0b29e810dde..fdfb4d46344 100644
>--- a/ld/configure.ac
>+++ b/ld/configure.ac
>@@ -287,6 +287,41 @@ fi
> AM_CONDITIONAL(ENABLE_LIBCTF, test "${enable_libctf}" = yes)
> AC_SUBST(enable_libctf)
>
>+# If pkg-config and libjansson are available, use them, skip if not.
>+# Allow explicitly enabling (error if not found)/disabling (skip even if found).
>+AC_ARG_ENABLE([jansson],
>+  [AS_HELP_STRING([--enable-jansson],
>+    [enable jansson [default=auto]])],
>+  [enable_jansson=$enableval],
>+  [enable_jansson="auto"])
>+
>+if test "x$enable_jansson" != "xno"; then
>+  PKG_PROG_PKG_CONFIG
>+  AS_IF([test -n "$PKG_CONFIG"],
>+    [
>+      PKG_CHECK_MODULES(JANSSON, [jansson],
>+        [
>+          AC_DEFINE(HAVE_JANSSON, 1, [The jansson library is to be used])
>+          AC_SUBST([JANSSON_CFLAGS])
>+          AC_SUBST([JANSSON_LIBS])
>+        ],
>+        [
>+          if test "x$enable_jansson" = "xyes"; then
>+            AC_MSG_ERROR([Cannot find jansson library])
>+          else
>+            AC_MSG_WARN([Cannot find jansson library])
>+          fi
>+        ])
>+    ],
>+    [
>+      if test "x$enable_jansson" = "xyes"; then
>+        AC_MSG_ERROR([Cannot find pkg-config])
>+      else
>+        AC_MSG_WARN([Cannot find pkg-config])
>+      fi
>+    ])
>+fi
>+
> AM_BINUTILS_WARNINGS
>
> AM_LC_MESSAGES
>diff --git a/ld/emultempl/elf.em b/ld/emultempl/elf.em
>index c027559908c..c52484f35d3 100644
>--- a/ld/emultempl/elf.em
>+++ b/ld/emultempl/elf.em
>@@ -572,6 +572,7 @@ enum elf_options
>   OPTION_EXCLUDE_LIBS,
>   OPTION_HASH_STYLE,
>   OPTION_BUILD_ID,
>+  OPTION_PACKAGE_METADATA,
>   OPTION_AUDIT,
>   OPTION_COMPRESS_DEBUG
> };
>@@ -602,6 +603,7 @@ EOF
> fi
> fragment <<EOF
>     {"build-id", optional_argument, NULL, OPTION_BUILD_ID},
>+    {"package-metadata", optional_argument, NULL, OPTION_PACKAGE_METADATA},
>     {"compress-debug-sections", required_argument, NULL, OPTION_COMPRESS_DEBUG},
> EOF
> if test x"$GENERATE_SHLIB_SCRIPT" = xyes; then
>@@ -650,6 +652,13 @@ gld${EMULATION_NAME}_handle_option (int optc)
> 	ldelf_emit_note_gnu_build_id = xstrdup (optarg);
>       break;
>
>+    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);
>+      break;
>+
>     case OPTION_COMPRESS_DEBUG:
>       if (strcasecmp (optarg, "none") == 0)
> 	link_info.compress_debug = COMPRESS_DEBUG_NONE;
>diff --git a/ld/ld.texi b/ld/ld.texi
>index 8cad8478140..8406560397c 100644
>--- a/ld/ld.texi
>+++ b/ld/ld.texi
>@@ -2935,6 +2935,13 @@ 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 --package-metadata=@var{JSON}
>+@item --package-metadata=@var{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/COREDUMP_PACKAGE_METADATA/
> @end table
>
> @c man end
>diff --git a/ld/ldelf.c b/ld/ldelf.c
>index 4094640b3f7..161fb476121 100644
>--- a/ld/ldelf.c
>+++ b/ld/ldelf.c
>@@ -39,6 +39,9 @@
> #include <glob.h>
> #endif
> #include "ldelf.h"
>+#ifdef HAVE_JANSSON
>+#include <jansson.h>
>+#endif
>
> struct dt_needed
> {
>@@ -49,6 +52,9 @@ struct dt_needed
> /* Style of .note.gnu.build-id section.  */
> const char *ldelf_emit_note_gnu_build_id;
>
>+/* Content of .note.package section.  */
>+const char *ldelf_emit_note_fdo_package_metadata;
>+
> /* These variables are required to pass information back and forth
>    between after_open and check_needed and stat_needed and vercheck.  */
>
>@@ -1249,7 +1255,8 @@ ldelf_after_open (int use_libpath, int native, int is_linux, int is_freebsd,
> 	}
>     }
>
>-  if (ldelf_emit_note_gnu_build_id != NULL)
>+  if (ldelf_emit_note_gnu_build_id != NULL ||
>+      ldelf_emit_note_fdo_package_metadata != NULL)
>     {
>       /* Find an ELF input.  */
>       for (abfd = link_info.input_bfds;
>@@ -1262,11 +1269,18 @@ ldelf_after_open (int use_libpath, int native, int is_linux, int is_freebsd,
>       /* PR 10555: If there are no ELF input files do not try to
> 	 create a .note.gnu-build-id section.  */
>       if (abfd == NULL
>-	  || !ldelf_setup_build_id (abfd))
>+	  || (ldelf_emit_note_gnu_build_id != NULL && !ldelf_setup_build_id (abfd)))
> 	{
> 	  free ((char *) ldelf_emit_note_gnu_build_id);
> 	  ldelf_emit_note_gnu_build_id = NULL;
> 	}
>+
>+      if (abfd == NULL
>+	  || (ldelf_emit_note_fdo_package_metadata != NULL && !ldelf_setup_package_metadata (abfd)))
>+	{
>+	  free ((char *) ldelf_emit_note_fdo_package_metadata);
>+	  ldelf_emit_note_fdo_package_metadata = NULL;
>+	}
>     }
>
>   get_elf_backend_data (link_info.output_bfd)->setup_gnu_properties (&link_info);
>@@ -1501,6 +1515,109 @@ ldelf_setup_build_id (bfd *ibfd)
>   return false;
> }
>
>+static bool
>+write_package_metadata (bfd *abfd)
>+{
>+  struct elf_obj_tdata *t = elf_tdata (abfd);
>+  const char *json;
>+  asection *asec;
>+  Elf_Internal_Shdr *i_shdr;
>+  unsigned char *contents, *json_bits;
>+  bfd_size_type size;
>+  file_ptr position;
>+  Elf_External_Note *e_note;
>+
>+  json = t->o->package_metadata.json;
>+  asec = t->o->package_metadata.sec;
>+  if (bfd_is_abs_section (asec->output_section))
>+    {
>+      einfo (_("%P: warning: .note.package section discarded,"
>+	       " --package-metadata ignored\n"));
>+      return true;
>+    }
>+  i_shdr = &elf_section_data (asec->output_section)->this_hdr;
>+
>+  if (i_shdr->contents == NULL)
>+    {
>+      if (asec->contents == NULL)
>+	asec->contents = (unsigned char *) xmalloc (asec->size);
>+      contents = asec->contents;
>+    }
>+  else
>+    contents = i_shdr->contents + asec->output_offset;
>+
>+  e_note = (Elf_External_Note *) contents;
>+  size = offsetof (Elf_External_Note, name[sizeof "FDO"]);
>+  size = (size + 3) & -(bfd_size_type) 4;
>+  json_bits = contents + size;
>+  size = asec->size - size;
>+
>+  /* Clear the package metadata field.  */
>+  memset (json_bits, 0, size);
>+
>+  bfd_h_put_32 (abfd, sizeof "FDO", &e_note->namesz);
>+  bfd_h_put_32 (abfd, size, &e_note->descsz);
>+  bfd_h_put_32 (abfd, FDO_PACKAGING_METADATA, &e_note->type);
>+  memcpy (e_note->name, "FDO", sizeof "FDO");
>+  memcpy (json_bits, json, size);
>+
>+  position = i_shdr->sh_offset + asec->output_offset;
>+  size = asec->size;
>+  return (bfd_seek (abfd, position, SEEK_SET) == 0
>+	  && bfd_bwrite (contents, size, abfd) == size);
>+}
>+
>+/* Make .note.package section.
>+   https://systemd.io/COREDUMP_PACKAGE_METADATA/  */
>+
>+bool
>+ldelf_setup_package_metadata (bfd *ibfd)
>+{
>+  asection *s;
>+  bfd_size_type size;
>+  flagword flags;
>+
>+  if (!ldelf_emit_note_fdo_package_metadata || strlen(ldelf_emit_note_fdo_package_metadata) == 0)
>+    return false;
>+
>+#ifdef HAVE_JANSSON
>+  json_error_t json_error;
>+  json_t *json = json_loads(ldelf_emit_note_fdo_package_metadata, 0, &json_error);
>+  if (!json)
>+    {
>+      einfo (_("%P: warning: --package-metadata=%s does not contain valid "
>+	       "JSON, ignoring: %s\n"), ldelf_emit_note_fdo_package_metadata,
>+         json_error.text);
>+      return false;
>+    }
>+  else
>+    json_decref(json);
>+#endif
>+
>+  size = offsetof (Elf_External_Note, name[sizeof "FDO"]);
>+  size += strlen(ldelf_emit_note_fdo_package_metadata) + 1;
>+  size = (size + 3) & -(bfd_size_type) 4;
>+
>+  flags = (SEC_ALLOC | SEC_LOAD | SEC_IN_MEMORY
>+	   | SEC_LINKER_CREATED | SEC_READONLY | SEC_DATA);
>+  s = bfd_make_section_anyway_with_flags (ibfd, ".note.package",
>+					  flags);
>+  if (s != NULL && bfd_set_section_alignment (s, 2))
>+    {
>+      struct elf_obj_tdata *t = elf_tdata (link_info.output_bfd);
>+      t->o->package_metadata.after_write_object_contents = &write_package_metadata;
>+      t->o->package_metadata.json = ldelf_emit_note_fdo_package_metadata;
>+      t->o->package_metadata.sec = s;
>+      elf_section_type (s) = SHT_NOTE;
>+      s->size = size;
>+      return true;
>+    }
>+
>+  einfo (_("%P: warning: cannot create .note.package section,"
>+	   " --package-metadata ignored\n"));
>+  return false;
>+}
>+
> /* Look through an expression for an assignment statement.  */
>
> static void
>diff --git a/ld/ldelf.h b/ld/ldelf.h
>index efa8b45851c..a3ded3dd04c 100644
>--- a/ld/ldelf.h
>+++ b/ld/ldelf.h
>@@ -19,6 +19,7 @@
>    MA 02110-1301, USA.  */
>
> extern const char *ldelf_emit_note_gnu_build_id;
>+extern const char *ldelf_emit_note_fdo_package_metadata;
>
> extern void ldelf_after_parse (void);
> extern bool ldelf_load_symbols (lang_input_statement_type *);
>@@ -26,6 +27,7 @@ extern void ldelf_before_plugin_all_symbols_read (int, int, int, int,
> 						  int, const char *);
> extern void ldelf_after_open (int, int, int, int, int, const char *);
> extern bool ldelf_setup_build_id (bfd *);
>+extern bool ldelf_setup_package_metadata (bfd *);
> extern void ldelf_append_to_separated_string (char **, char *);
> extern void ldelf_before_allocation (char *, char *, const char *);
> extern bool ldelf_open_dynamic_archive
>diff --git a/ld/lexsup.c b/ld/lexsup.c
>index 78190472907..3aeba07c3a7 100644
>--- a/ld/lexsup.c
>+++ b/ld/lexsup.c
>@@ -2144,6 +2144,8 @@ elf_static_list_options (FILE *file)
>   fprintf (file, _("\
>   --build-id[=STYLE]          Generate build ID note\n"));
>   fprintf (file, _("\
>+  --package-metadata[=JSON]   Generate package metadata note\n"));
>+  fprintf (file, _("\
>   --compress-debug-sections=[none|zlib|zlib-gnu|zlib-gabi]\n\
>                               Compress DWARF debug sections using zlib\n"));
> #ifdef DEFAULT_FLAG_COMPRESS_DEBUG
>diff --git a/ld/testsuite/ld-elf/package-note.exp b/ld/testsuite/ld-elf/package-note.exp
>new file mode 100644
>index 00000000000..51a790b25de
>--- /dev/null
>+++ b/ld/testsuite/ld-elf/package-note.exp
>@@ -0,0 +1,49 @@
>+# Expect script for --package-note tests.
>+#   Copyright (C) 2022 Free Software Foundation, Inc.
>+#
>+# This file is part of the GNU Binutils.
>+#
>+# This program is free software; you can redistribute it and/or modify
>+# it under the terms of the GNU General Public License as published by
>+# the Free Software Foundation; either version 3 of the License, or
>+# (at your option) any later version.
>+#
>+# This program is distributed in the hope that it will be useful,
>+# but WITHOUT ANY WARRANTY; without even the implied warranty of
>+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>+# GNU General Public License for more details.
>+#
>+# You should have received a copy of the GNU General Public License
>+# along with this program; if not, write to the Free Software
>+# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
>+# MA 02110-1301, USA.
>+#
>+
>+# Exclude non-ELF targets.
>+
>+if ![is_elf_format] {
>+    return
>+}
>+
>+if { [istarget frv-*-*] || [istarget lm32-*-*] } {
>+    return
>+}
>+
>+if { !([istarget *-*-linux*]
>+       || [istarget arm*-*-uclinuxfdpiceabi]
>+       || [istarget *-*-nacl*]
>+       || [istarget *-*-gnu*]) } then {
>+    return
>+}
>+
>+run_ld_link_tests [list \
>+    [list \
>+	"package-note.o" \
>+	"--package-metadata='{\"foo\":\"bar\"}'" \
>+	"" \
>+	"" \
>+	{start.s} \
>+	{{readelf {--notes} package-note.rd}} \
>+	"package-note.o" \
>+    ] \
>+]
>diff --git a/ld/testsuite/ld-elf/package-note.rd b/ld/testsuite/ld-elf/package-note.rd
>new file mode 100644
>index 00000000000..c65dd21cdac
>--- /dev/null
>+++ b/ld/testsuite/ld-elf/package-note.rd
>@@ -0,0 +1,6 @@
>+#...
>+Displaying notes found in: \.note\.package
>+  Owner                Data size 	Description
>+  FDO                  0x00000010	FDO_PACKAGING_METADATA
>+    Packaging Metadata: {"foo":"bar"}
>+#pass
>-- 
>2.35.1
>

  reply	other threads:[~2022-05-16 16:40 UTC|newest]

Thread overview: 22+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-05-15 19:18 luca.boccassi
2022-05-16 16:40 ` Fangrui Song [this message]
2022-05-17  6:03   ` Florian Weimer
2022-05-17 14:44     ` Luca Boccassi
2022-05-25  6:56       ` Fangrui Song
2022-05-25  7:53         ` Florian Weimer
2022-05-23 11:26 ` Luca Boccassi
2022-05-24  9:34   ` Jose E. Marchesi
2022-05-24 11:26     ` Luca Boccassi
2022-05-24 16:23 ` Nick Clifton
2022-05-24 18:38   ` Luca Boccassi
2022-05-25  8:45     ` Florian Weimer
2022-05-25 13:53       ` Luca Boccassi
2022-05-30 14:08         ` Florian Weimer
2022-05-24 16:28 ` Nick Clifton
2022-05-24 21:15 ` [PATCH v2] " luca.boccassi
2022-05-25  4:30   ` Alan Modra
2022-05-25  6:02     ` Alan Modra
2022-05-25 13:42       ` Luca Boccassi
2022-05-25 13:41 ` [PATCH v3] " luca.boccassi
2022-05-26  3:55   ` Alan Modra
2022-05-26 10:46     ` Luca Boccassi

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20220516164003.iivw6lthknhvce42@gmail.com \
    --to=i@maskray.me \
    --cc=binutils@sourceware.org \
    --cc=luca.boccassi@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).