public inbox for binutils@sourceware.org
 help / color / mirror / Atom feed
* [RFC] Symbol meta-information ELF extension
@ 2020-02-18 10:25 Jozef Lawrynowicz
  2020-02-18 12:58 ` H.J. Lu
  0 siblings, 1 reply; 8+ messages in thread
From: Jozef Lawrynowicz @ 2020-02-18 10:25 UTC (permalink / raw)
  To: binutils

[-- Attachment #1: Type: text/plain, Size: 4895 bytes --]

Hi,

I've been working with Texas Instruments to develop an ELF extension which
allows additional information about symbols ("symbol meta-information") to
be stored in ELF files, in a section called .symtab_meta.

The aim of symbol meta-information is to provide a extensible format for
propagating additional information about symbols from the source code through to
the link stage.

I hope I can get some feedback on this proposal, and its current implementation,
from the upstream community so I can make any adjustments required for the
eventual upstreaming of this feature.

TI spec'd out this functionality and are implementing it into their soon-to-be
released ARM Clang/LLVM toolchain fork. They will also be looking to upstream
it to Clang/LLVM, so it is not GNU-specific.

So far, the behaviour of two new C/C++ attributes, "retain" and "location", and
their mapping to symbol meta-information have been spec'd out:
* "retain" - a symbol meta-information entry for the symbol informs the linker
  not to garbage collect the section containing the symbol, even if it appears
  unused.
* "location" - a symbol meta-information entry describes the VMA the
  linker should try to place the corresponding symbol at (similar to the "at"
  attribute in the ARM compiler).

So far these attributes making use of symbol meta-information are mainly
targeted at embedded software developers, where the ability to save objects from
garbage collection or place objects at specific addresses can sometimes be
necessary. Being able to describe this behaviour in the source code without
having to modify linker scripts arguably improves the developer experience.

In the patches attached to this RFC, the general mechanism for the symbol
meta-information functionality has been implemented in the BFD library and
plumbed into much of Binutils:
* GAS supports the ".sym_meta_info" directive which describes a symbol
  meta-information entry. It consolidates symbol meta-information entries into a
  table which is placed in the .symtab_meta section of the output object file.
* LD supports the consolidation of symbol meta-information from input object
  files into a single table, and will save symbols with an accompanying
  SMK_RETAIN meta-information entry from garbage collection.
* objdump supports dumping the symbol meta-information table with the
  --symtab-meta option.
* objcopy maintains the integrity of symbol meta-information as the input BFD is
  modified.

The high-level list of functionality that is not currently implemented in this
RFC, but I intend to add before submitting the patches for inclusion into
Binutils is:
* Support the placement of symbols at a VMA specified with an SMK_LOCATION
  meta-information entry in LD.
* Dump the symbol meta-information table in readelf.
* Support output formats other than ELF when linking ELF input object files with
  symbol meta-information.
* Clarify expected behaviour of objcopy/strip with "retained" symbols. Might
  need a new option to enable/disable the removal of these symbols.

I've added documentation to the individual tools as appropriate, a generic
description of the format of .symtab_meta and the entries within it is in
the ELF backends section of the BFD manual.

I've benchmarked the performance of LD when linking the Linux kernel
with/without symbol meta-information. There are ~120k symbols in the linked
Linux kernel, and after adding 120k new, randomly named symbols with
meta-information, there is no observable change in the time it takes to link
between these new symbols having meta-information or not.
I tested this in 3 combinations:
- 60k local symbols, 60k global symbols
- 120k local symbols
- 120k global symbols

I have some specific questions which I hope someone can provide some suggestions
for:
* Does this functionality need to be gated with a configure flag?
  I guess that partly depends on if a maintainer decides it should be on or off
  by default.
* I've positioned the .symtab_meta section to be emitted immediately after
  .symtab. I couldn't find anything in the ELF spec that enforces the positions
  of .symtab relative to .shstrtab and .strtab, but I recognize that these
  sections' relative positions have been the same for a long time. Are there any
  possible issues relating to this?

Also, any general feedback on the proposal and/or implementation is welcome.

I've additionally attached a rough GCC patch which emits .sym_meta_info
directives when __attribute__((retain)) is used on declarations of functions or
data.

I've built the attached patches with --enable-targets=all and confirmed there
are no issues there.
I've regtested and confirmed the new tests work for msp430-elf, arm-eabi,
x86-64-pc-linux-gnu, and also built and regtested the native configuration on a
i686-pc-linux-gnu host.
I additionally regtested for i386-pe, to check a non-ELF target.

Thanks,
Jozef

[-- Attachment #2: 0001-BFD-Support-symbol-meta-information-ELF-extension.patch --]
[-- Type: text/x-patch, Size: 51818 bytes --]

From c34fa873abfd8866b1b253c3fd73220b0c55ffe3 Mon Sep 17 00:00:00 2001
From: Jozef Lawrynowicz <jozef.l@mittosystems.com>
Date: Tue, 18 Feb 2020 10:06:30 +0000
Subject: [PATCH 1/2] BFD: Support symbol meta-information ELF extension

bfd/ChangeLog:

2020-02-18  Jozef Lawrynowicz  <jozef.l@mittosystems.com>

	* elf-bfd.h (struct bfd_elf_metasym_hash_entry): New.
	(bfd_elf_slurp_symtab_meta): New prototype.
	(bfd_elf_write_symtab_meta): New prototype.
	(bfd_elf_add_metasym): New prototype.
	(bfd_elf_copy_metasyms): New prototype.
	(FOR_MSYM_IN_MSYM_HASH_TABLE): Define.
	(struct bfd_elf_metasym_hash_table): New.
	(bfd_elf_metasym_hash_lookup): New prototype.
	(struct elf_obj_tdata): New.
	(elf_symtabmeta): Define.
	(elf_symtabmeta_hdr): Define.
	(elf_metasym_count): Define.
	(elf_symtabmeta_version): Define.
	(elf_symtabmeta_symtab_hash): Define.
	* elf.c (bfd_section_from_shdr)[SHT_GNU_SYMTAB_META]: Initialize
	elf_symtabmeta and elf_symtabmeta_hdr.
	(special_section_s): Add .symtab_meta.
	(assign_section_numbers): Set elf_symtabmeta and add .symtab_meta ref to
	.strtab.
	(_bfd_elf_init_file_header): Set .symtab_meta sh_name if there are
	metasyms to output.
	(bfd_elf_slurp_symtab_meta): Document structure of .symtab_meta.
	New.
	(write_uleb128): New.
	(uleb128_size): New.
	(bfd_elf_write_symtab_meta): New.
	(_bfd_elf_metasym_hash_newfunc): New.
	(_bfd_elf_metasym_hash_table_init): New.
	(bfd_elf_metasym_hash_lookup): New.
	(bfd_elf_add_metasym): New.
	(bfd_elf_copy_metasyms): New.
	(swap_out_syms): Call bfd_elf_write_symtab_meta before returning.
	* elflink.c (elf_link_add_object_symbols): Read in symbols as array of
	asymbols. Populate symtab_hdr->contents with local ELF symbols.
	Call bfd_elf_slurp_symtab_meta.
	Mark symbols corresponding to SMK_RETAIN metasymbols to prevent garbage
	collection.
	(_bfd_elf_link_adjust_metasym): New.
	(elf_link_input_bfd): After calculating the output .symtab index of a
	local symbol call, _bfd_elf_link_adjust_metasym.
	(bfd_elf_final_link): Call bfd_elf_write_symtab_meta and set output
	file position of .symtab_meta.

binutils/ChangeLog:

2020-02-18  Jozef Lawrynowicz  <jozef.l@mittosystems.com>

	* doc/binutils.texi: Document --symtab-meta objdump option.
	* objcopy.c (copy_object): Copy metasyms from ibfd to obfd.
	* objdump.c (enum option_values): Add --symtab-meta.
	(dump_meta_symbols): New.
	(dump_bfd): Slurp .symtab and dump .symtab_meta if --symtab-meta is
	passed.
	(main): Handle OPTION_SYMTAB_META.
	* readelf.c (get_section_type_name): Handle SHT_GNU_SYMTAB_META.

gas/ChangeLog:

2020-02-18  Jozef Lawrynowicz  <jozef.l@mittosystems.com>

	* config/obj-elf.c (obj_elf_sym_meta_info): New.
	* doc/as.texi: Document .sym_meta_info directive.

include/ChangeLog:

2020-02-18  Jozef Lawrynowicz  <jozef.l@mittosystems.com>

	* elf/common.h (SHT_GNU_SYMTAB_META): Define.

ld/ChangeLog:

2020-02-18  Jozef Lawrynowicz  <jozef.l@mittosystems.com>

	* ld.texi: Document the effect of SMK_RETAIN and SMK_LOCATION symbol
	meta-information kinds.
---
 bfd/elf-bfd.h              | 123 +++++++-
 bfd/elf.c                  | 605 +++++++++++++++++++++++++++++++++++++
 bfd/elflink.c              | 110 +++++++
 binutils/doc/binutils.texi |  23 ++
 binutils/objcopy.c         |  13 +
 binutils/objdump.c         |  61 +++-
 binutils/readelf.c         |   1 +
 gas/config/obj-elf.c       |  90 ++++++
 gas/doc/as.texi            |  38 +++
 include/elf/common.h       |   1 +
 ld/ld.texi                 |   7 +
 11 files changed, 1070 insertions(+), 2 deletions(-)

diff --git a/bfd/elf-bfd.h b/bfd/elf-bfd.h
index cbbba153f4..238955658e 100644
--- a/bfd/elf-bfd.h
+++ b/bfd/elf-bfd.h
@@ -529,6 +529,89 @@ struct elf_sym_strtab
   unsigned long destshndx_index;
 };
 
+/* The "kind" of a symbol meta-information entry.  */
+typedef enum
+{
+  SMK_NONE = 0,
+
+  /* The associated symbol should be retained in the output file, even if
+     it would normally be garbage collected by the linker.  */
+  SMK_RETAIN,
+
+  /* The input section containing the associated symbol should be placed at the
+     given address by the linker, if possible.  */
+  SMK_LOCATION,
+}
+bfd_elf_metasym_kind;
+
+/* A symbol meta-information entry, abbreviated to "metasym".
+   This stores additional information about an ELF symbol.
+   metasyms are stored in a hash table keyed off the name of the corresponding
+   ELF symbol.  */
+struct bfd_elf_metasym_hash_entry
+{
+  /* Base hash table entry structure.  */
+  struct bfd_hash_entry root;
+
+  /* A pointer to the BFD symbol this meta-information entry relates to.
+     We use this for a few different things:
+     - We can derive other information or structures (section, name etc.)
+       from an asymbol easily.
+     - The index of output symbols in .symtab is readily available via
+       sym->udata.i (except when linking).
+     - In most binutils programs a complete array of asymbols has already been
+       computed when loading the BFD, or is available when outputting the BFD
+       via abfd->outsymbols.  */
+  asymbol *sym;
+
+  /* To help differentiate between local symbols with the same name, we use the
+     ID of the input BFD they originated from, in conjuction with their st_value
+     and st_shndx stored with the below elfsym.  */
+  unsigned int bfd_id;
+
+  /* The Elf_Internal_Sym stored inside an asymbol is not a pointer, but we
+     need to be able to store a pointer to an Elf_Internal_Sym which will get
+     updated if relaxations change the st_value of the symbol.  */
+  Elf_Internal_Sym *elfsym;
+
+  /* See the definition of bfd_elf_metasym_kind above.  */
+  bfd_elf_metasym_kind kind;
+
+  /* The value of the meta-information for this symbol.  So far this is
+     either a boolean or an address.  */
+  bfd_vma value;
+
+  /* The index into the symbol table of the symbol this meta-information entry
+     relates to.  This is generally not finalized until we are about to write
+     out the .symtab_meta section, but we get the opportunity to calculate
+     the final index in advance of writing out .symtab_meta in some
+     situations.  */
+  unsigned int index;
+
+  /* When multiple metasyms correspond to different ELF symbols which have the
+     same name, we store them in a linked list which starts with the first (and
+     only) of those metasyms which is stored in the metasym hash table.  */
+  struct bfd_elf_metasym_hash_entry *next;
+};
+
+bfd_boolean bfd_elf_slurp_symtab_meta (bfd *ibfd,
+				       bfd *obfd,
+				       asymbol **symbol_table,
+				       size_t symcount);
+
+bfd_boolean bfd_elf_write_symtab_meta (bfd *abfd,
+				       struct bfd_link_info *link_info);
+
+bfd_boolean bfd_elf_add_metasym (bfd *abfd,
+				 bfd *ibfd,
+				 asymbol *sym,
+				 Elf_Internal_Sym *elfsym,
+				 bfd_elf_metasym_kind kind,
+				 bfd_vma value,
+				 bfd_vma index);
+
+bfd_boolean bfd_elf_copy_metasyms (bfd *obfd, bfd *ibfd);
+
 struct bfd_link_needed_list
 {
   struct bfd_link_needed_list *next;
@@ -682,6 +765,18 @@ struct elf_link_hash_table
 #define is_elf_hash_table(htab)						\
   (((struct bfd_link_hash_table *) (htab))->type == bfd_link_elf_hash_table)
 
+/* Convenience macro for iterating over every metasym in the metasym hash
+   table.  I must be an unsigned int, HASH_ENTRY is "* bfd_hash_entry" and
+   MSYM_HASH_ENTRY is "* bfd_elf_metasym_hash_entry".  */
+#define FOR_MSYM_IN_MSYM_HASH_TABLE(ABFD, I, HASH_ENTRY, MSYM_HASH_ENTRY)  \
+  for (I = 0; I < elf_tdata(ABFD)->metasym_hash_table.table.size; I++) \
+  /* Same hash, different symbol name.  */ \
+  for (HASH_ENTRY = elf_tdata(ABFD)->metasym_hash_table.table.table[I]; \
+       HASH_ENTRY != NULL; HASH_ENTRY = HASH_ENTRY->next) \
+  /* Same hash, same symbol name, different symbol.  */\
+  for (MSYM_HASH_ENTRY = (struct bfd_elf_metasym_hash_entry *)HASH_ENTRY; \
+       MSYM_HASH_ENTRY != NULL; MSYM_HASH_ENTRY = MSYM_HASH_ENTRY->next) \
+
 /* Used by bfd_sym_from_r_symndx to cache a small number of local
    symbols.  */
 #define LOCAL_SYM_CACHE_SIZE 32
@@ -1844,6 +1939,24 @@ enum dynamic_lib_link_class {
   DYN_NO_NEEDED = 8
 };
 
+/* This is the metasym hash table.  It is a derived class of
+   bfd_hash_table.  */
+struct bfd_elf_metasym_hash_table
+{
+  /* The hash table itself.  */
+  struct bfd_hash_table table;
+  unsigned int symtabmeta_version : 8;
+  char *symtab_hash;
+};
+
+struct bfd_elf_metasym_hash_entry *
+bfd_elf_metasym_hash_lookup (struct bfd_elf_metasym_hash_table *table,
+			     const char *name,
+			     Elf_Internal_Sym *elfsym,
+			     unsigned int bfd_id,
+			     bfd_boolean create,
+			     bfd_boolean copy);
+
 /* Some private data is stashed away for future use using the tdata pointer
    in the bfd structure.  */
 
@@ -1853,6 +1966,7 @@ struct elf_obj_tdata
   Elf_Internal_Shdr **elf_sect_ptr;
   Elf_Internal_Phdr *phdr;
   Elf_Internal_Shdr symtab_hdr;
+  Elf_Internal_Shdr symtabmeta_hdr;
   Elf_Internal_Shdr shstrtab_hdr;
   Elf_Internal_Shdr strtab_hdr;
   Elf_Internal_Shdr dynsymtab_hdr;
@@ -1943,9 +2057,11 @@ struct elf_obj_tdata
      section's group.  Used to optimize subsequent group searches.  */
   unsigned int group_search_offset;
 
-  unsigned int symtab_section, dynsymtab_section;
+  unsigned int symtab_section, dynsymtab_section, symtabmeta_section;
   unsigned int dynversym_section, dynverdef_section, dynverref_section;
 
+  struct bfd_elf_metasym_hash_table metasym_hash_table;
+
   /* An identifier used to distinguish different target
      specific extensions to this structure.  */
   ENUM_BITFIELD (elf_target_id) object_id : 6;
@@ -1989,10 +2105,12 @@ struct elf_obj_tdata
 #define elf_stack_flags(bfd)	(elf_tdata(bfd) -> o->stack_flags)
 #define elf_shstrtab(bfd)	(elf_tdata(bfd) -> o->strtab_ptr)
 #define elf_onesymtab(bfd)	(elf_tdata(bfd) -> symtab_section)
+#define elf_symtabmeta(bfd)	(elf_tdata(bfd) -> symtabmeta_section)
 #define elf_symtab_shndx_list(bfd)	(elf_tdata(bfd) -> symtab_shndx_list)
 #define elf_strtab_sec(bfd)	(elf_tdata(bfd) -> o->strtab_section)
 #define elf_shstrtab_sec(bfd)	(elf_tdata(bfd) -> o->shstrtab_section)
 #define elf_symtab_hdr(bfd)	(elf_tdata(bfd) -> symtab_hdr)
+#define elf_symtabmeta_hdr(bfd)	(elf_tdata(bfd) -> symtabmeta_hdr)
 #define elf_dynsymtab(bfd)	(elf_tdata(bfd) -> dynsymtab_section)
 #define elf_dynversym(bfd)	(elf_tdata(bfd) -> dynversym_section)
 #define elf_dynverdef(bfd)	(elf_tdata(bfd) -> dynverdef_section)
@@ -2023,6 +2141,9 @@ struct elf_obj_tdata
 #define elf_properties(bfd) (elf_tdata (bfd) -> properties)
 #define elf_has_no_copy_on_protected(bfd) \
   (elf_tdata(bfd) -> has_no_copy_on_protected)
+#define elf_metasym_count(bfd)	(elf_tdata(bfd) -> metasym_hash_table.table.count)
+#define elf_symtabmeta_version(bfd)	(elf_tdata(bfd) -> metasym_hash_table.symtabmeta_version)
+#define elf_symtabmeta_symtab_hash(bfd)	(elf_tdata(bfd) -> metasym_hash_table.symtab_hash)
 \f
 extern void _bfd_elf_swap_verdef_in
   (bfd *, const Elf_External_Verdef *, Elf_Internal_Verdef *);
diff --git a/bfd/elf.c b/bfd/elf.c
index c85face630..c173c90f6d 100644
--- a/bfd/elf.c
+++ b/bfd/elf.c
@@ -44,6 +44,7 @@ SECTION
 #include "libiberty.h"
 #include "safe-ctype.h"
 #include "elf-linux-core.h"
+#include "sha1.h"
 
 #ifdef CORE_HEADER
 #include CORE_HEADER
@@ -2518,6 +2519,16 @@ found - ignoring all but the first"),
       ret = _bfd_elf_make_section_from_shdr (abfd, hdr, name, shindex);
       goto success;
 
+    case SHT_GNU_SYMTAB_META:
+      /* We don't create a BFD section for .symtab_meta, since its contents is
+	 only useful when converted to internal format, which we'll do later in
+	 bfd_elf_slurp_symtab_meta.  We also want to make sure .symtab_meta is
+	 output on our terms and it's not automatically copied from an input BFD
+	 to the output BFD.  */
+      elf_symtabmeta (abfd) = shindex;
+      elf_symtabmeta_hdr (abfd) = *hdr;
+      goto success;
+
     case SHT_SHLIB:
       goto success;
 
@@ -2752,6 +2763,7 @@ static const struct bfd_elf_special_section special_sections_s[] =
   { STRING_COMMA_LEN (".shstrtab"), 0, SHT_STRTAB, 0 },
   { STRING_COMMA_LEN (".strtab"),   0, SHT_STRTAB, 0 },
   { STRING_COMMA_LEN (".symtab"),   0, SHT_SYMTAB, 0 },
+  { STRING_COMMA_LEN (".symtab_meta"),   0, SHT_GNU_SYMTAB_META, 0 },
   /* See struct bfd_elf_special_section declaration for the semantics of
      this special case where .prefix_length != strlen (.prefix).  */
   { ".stabstr",			5,  3, SHT_STRTAB, 0 },
@@ -3792,6 +3804,12 @@ assign_section_numbers (bfd *abfd, struct bfd_link_info *link_info)
     {
       elf_onesymtab (abfd) = section_number++;
       _bfd_elf_strtab_addref (elf_shstrtab (abfd), t->symtab_hdr.sh_name);
+      if (elf_metasym_count (abfd) > 0)
+	{
+	  elf_symtabmeta (abfd) = section_number++;
+	  _bfd_elf_strtab_addref (elf_shstrtab (abfd),
+				  t->symtabmeta_hdr.sh_name);
+	}
       if (section_number > ((SHN_LORESERVE - 2) & 0xFFFF))
 	{
 	  elf_section_list *entry;
@@ -3847,6 +3865,8 @@ assign_section_numbers (bfd *abfd, struct bfd_link_info *link_info)
   if (need_symtab)
     {
       i_shdrp[elf_onesymtab (abfd)] = &t->symtab_hdr;
+      if (elf_metasym_count (abfd) > 0)
+	i_shdrp[elf_symtabmeta (abfd)] = &t->symtabmeta_hdr;
       if (elf_numsections (abfd) > (SHN_LORESERVE & 0xFFFF))
 	{
 	  elf_section_list * entry = elf_symtab_shndx_list (abfd);
@@ -6455,6 +6475,14 @@ _bfd_elf_init_file_header (bfd *abfd,
 
   elf_tdata (abfd)->symtab_hdr.sh_name =
     (unsigned int) _bfd_elf_strtab_add (shstrtab, ".symtab", FALSE);
+  /* We want .symtab_meta placed after .symtab.  */
+  if (elf_metasym_count (abfd) > 0)
+    {
+      elf_symtabmeta_hdr (abfd).sh_name =
+	(unsigned int) _bfd_elf_strtab_add (shstrtab, ".symtab_meta", FALSE);
+      if (elf_symtabmeta_hdr (abfd).sh_name == (unsigned int) -1)
+	return FALSE;
+    }
   elf_tdata (abfd)->strtab_hdr.sh_name =
     (unsigned int) _bfd_elf_strtab_add (shstrtab, ".strtab", FALSE);
   elf_tdata (abfd)->shstrtab_hdr.sh_name =
@@ -8002,6 +8030,575 @@ _bfd_elf_copy_private_symbol_data (bfd *ibfd,
   return TRUE;
 }
 
+\f
+/*
+SUBSECTION
+  Symbol meta-information
+
+  Symbol meta-information entries, referred to internally as @samp{metasyms},
+  are stored in the symbol meta-information table, an ELF section called
+  @code{.symtab_meta}.
+
+  The structure of @code{.symtab_meta} is as follows:
+  @table @dfn
+
+  @item version
+  a single unsigned byte indicating the version of the format of
+  @code{.symtab_meta}.
+  Currently two versions exist
+  @itemize
+  @item Version 1 does not contain a SHA-1 hash of @code{.symtab}
+  @item Version 2 stores a SHA-1 hash of @code{.symtab}
+  @end itemize
+
+  @item SHA-1 hash of @code{.symtab} (format version 2 only)
+  A 20-bit SHA-1 hash of the contents of @code{.symtab} from
+  the point at which @code{.symtab_meta} was created.  This exists to confirm
+  that the symbol indices stored in symbol meta-information entries are
+  reliable.
+
+  @item symbol meta-information entries
+  A list of symbol meta-information entries fills out the rest of the table.
+  The structure of a symbol meta-information entry is as follows:
+  @table @samp
+  @item kind
+  single unsigned byte indicating the type of entry this is.  Currently two
+  kinds are supported
+  @itemize
+  @item @samp{SMK_RETAIN} enumerates as @samp{1} and describes whether the
+  section containing this symbol should be saved from linker garbage collection.
+  @item @samp{SMK_LOCATION} enumerates as @samp{2} and indicates that the
+  section containing this symbol should be placed at the given VMA in the linked
+  output file.
+  @end itemize
+  @item value
+  A ULEB128 value which takes on a different meaning depending on the
+  @samp{kind} of symbol meta-information this entry is.
+  @itemize
+  @item For @samp{SMK_RETAIN}, the value is either @samp{1} to indicate that
+  this symbol should be saved from linker garbage collection, or @samp{0} if it
+  should be garbage collected as normal.
+  @item For @samp{SMK_LOCATION}, the value indicates the VMA to place symbol's
+  section at.
+  @end itemize
+  @item index
+  A ULEB128 value which indicates the index in @code{.symtab} of the ELF symbol
+  this meta-information entry relates to.
+  @end table
+
+ @end table
+*/
+
+/* Read symbol meta-information from IBFD, and store the internal representation
+   in OBFD if it is non-null, otherwise use IBFD.  */
+bfd_boolean
+bfd_elf_slurp_symtab_meta (bfd *ibfd,
+			   bfd *obfd,
+			   asymbol **symbol_table,
+			   size_t symcount)
+{
+  char *slurped_sha;
+  char symtab_sha[20];
+  char version;
+  bfd_size_type symtab_size;
+  bfd_size_type remaining_size;
+  char *raw_symtab;
+  asymbol *sym = NULL;
+  bfd_elf_metasym_kind kind;
+  bfd_vma value;
+  bfd_vma fix_index;
+  bfd_size_type amt = 0;
+  Elf_Internal_Sym *firstsym;
+  bfd_byte *buf;
+
+  /* If the BFD to slurp the meta-information into hasn't been specified, use
+     IBFD.  */
+  if (obfd == NULL)
+    obfd = ibfd;
+
+  /* Nothing to slurp, no error.  */
+  if (symbol_table == NULL || elf_symtabmeta (ibfd) == 0)
+    return TRUE;
+
+  slurped_sha = bfd_malloc (20);
+  symtab_size = elf_symtab_hdr (ibfd).sh_size;
+  raw_symtab = bfd_malloc (symtab_size);
+  if (slurped_sha == NULL || raw_symtab == NULL)
+    return FALSE;
+
+  /* We need to read in the contents of .symtab so we can calculate the SHA of
+     it.  Even when elf_symtab_hdr->contents is initialized, it does not
+     have the full contents we need.  */
+  if (bfd_seek (ibfd, elf_symtab_hdr (ibfd).sh_offset, SEEK_SET) != 0
+      || bfd_bread (raw_symtab, symtab_size, ibfd) != symtab_size)
+    {
+      free (raw_symtab);
+      return FALSE;
+    }
+  sha1_buffer ((void *)raw_symtab, symtab_size, symtab_sha);
+  free (raw_symtab);
+
+  if (bfd_seek (ibfd, elf_symtabmeta_hdr (ibfd).sh_offset, SEEK_SET) != 0
+      || bfd_bread (&version, sizeof (version), ibfd) != sizeof (version))
+    return FALSE;
+
+  /* We thought there was a table to slurp, but the version is 0.  */
+  if (version == 0)
+    return FALSE;
+
+  /* Check if we've already loaded the .symtab_meta version from a previous
+     input BFD.  */
+  if (elf_symtabmeta_version (obfd)
+      && (elf_symtabmeta_version (obfd) != version))
+    _bfd_error_handler
+      (_("warning: .symtab_meta version of %pB (v%d) does not match %pB (v%d)"),
+       ibfd, version, obfd, elf_symtabmeta_version (obfd));
+  else
+    elf_symtabmeta_version (obfd) = version;
+
+  if (bfd_bread (slurped_sha, 20, ibfd) != 20)
+    return FALSE;
+
+  /* This is only re-used for objdump/readelf.  */
+  elf_symtabmeta_symtab_hash (obfd) = slurped_sha;
+
+  /* Validate the expected SHA of .symtab against the actual SHA.  */
+  if (memcmp (symtab_sha, slurped_sha, 20) != 0)
+    {
+      int i;
+      _bfd_error_handler
+	(_("warning: SHA-1 hash of .symtab stored in .symtab_meta of %pB does "
+	   "not match the actual SHA-1 of .symtab"), ibfd);
+      fprintf (stderr, "Actual SHA-1 of .symtab:\n");
+      for (i = 0; i < 20; i++)
+	fprintf (stderr, "%02x", symtab_sha[i] & 0xFF);
+      fprintf (stderr, "\nExpected SHA-1 of .symtab stored in .symtab_meta:\n");
+      for (i = 0; i < 20; i++)
+	fprintf (stderr, "%02x", slurped_sha[i] & 0xFF);
+      fprintf (stderr, "\n");
+      return FALSE;
+    }
+
+  /* The initial undefined symbol from the external symbol table does not
+     get copied to the array of symbols in symbol_table when
+     bfd_canonicalize_symtab has been used to get an array of BFD symbols.
+     So we must subtract 1 from the indicies that have been read from the
+     object file.  This assumption should always hold, but just incase we check
+     the first element's values to see if it's the initial undefined entry.  */
+  firstsym = &elf_symbol_from (ibfd, symbol_table[0])->internal_elf_sym;
+  if (firstsym->st_value == 0
+      && firstsym->st_size == 0
+      && firstsym->st_name == 0
+      && firstsym->st_info == 0
+      && firstsym->st_other == 0
+      && firstsym->st_shndx == SHN_UNDEF)
+    fix_index = 0;
+  else
+    fix_index = 1;
+
+  /* 21 is the size of the version byte plus the slurped symtab SHA.  */
+  remaining_size = elf_symtabmeta_hdr (ibfd).sh_size - 21;
+  buf = bfd_malloc (remaining_size);
+  if (bfd_bread (buf, remaining_size, ibfd) != remaining_size)
+    goto error_return;
+
+  /* From now on we'll be reading in the symbol meta-information
+     entries themselves.  */
+  while (amt < remaining_size)
+    {
+      unsigned int bytes_read;
+      bfd_vma index;
+      Elf_Internal_Sym *elfsym = NULL;
+
+      kind = *(buf + amt++);
+      if (kind != SMK_RETAIN && kind != SMK_LOCATION)
+	goto error_return;
+
+      value = _bfd_read_unsigned_leb128 (ibfd, buf + amt, &bytes_read);
+      amt += bytes_read;
+      index = _bfd_read_unsigned_leb128 (ibfd, buf + amt,
+							&bytes_read);
+      amt += bytes_read;
+
+      index -= fix_index;
+      if (index >= symcount)
+	goto error_return;
+
+      sym = symbol_table[index];
+
+      if (elf_tdata (ibfd)->symtab_hdr.contents != NULL)
+	{
+	  elfsym = (Elf_Internal_Sym *) elf_tdata (ibfd)->symtab_hdr.contents
+	    + index + 1;
+	}
+      else
+	{
+	  /* FIXME When linking we have initialized symtab_hdr.contents, to
+	     ensure we have an elfsym pointer that will get updated as
+	     relaxations affect the st_value of a symbol.  For other programs,
+	     symtab_hdr->contents has not been initialized, so if the st_value
+	     of a local symbol corresponding to a metasym changes, we won't be
+	     able to identify that symbol...  */
+	}
+
+
+      if (!bfd_elf_add_metasym (obfd, ibfd, sym, elfsym, kind, value, index))
+	goto error_return;
+    }
+  free (buf);
+  return TRUE;
+
+error_return:
+  free (buf);
+  return FALSE;
+}
+
+/* Write VAL in uleb128 format to P, returning a pointer to the
+   following byte.  */
+static bfd_byte *
+write_uleb128 (bfd_byte *p, unsigned int val)
+{
+  bfd_byte c;
+  do
+    {
+      c = val & 0x7f;
+      val >>= 7;
+      if (val)
+	c |= 0x80;
+      *(p++) = c;
+    }
+  while (val);
+  return p;
+}
+
+/* Return the number of bytes needed by I in uleb128 format.  */
+static int
+uleb128_size (unsigned int i)
+{
+  int size;
+  size = 1;
+  while (i >= 0x80)
+    {
+      i >>= 7;
+      size++;
+    }
+  return size;
+}
+
+/* Write out the contents of the .symtab_meta section to ABFD.  */
+bfd_boolean
+bfd_elf_write_symtab_meta (bfd *abfd, struct bfd_link_info *link_info)
+{
+  Elf_Internal_Shdr *symtabmeta_hdr;
+  bfd_byte * contents;
+  bfd_size_type amt = 0;
+  bfd_size_type total_size;
+  Elf_Internal_Shdr *symtab_hdr;
+  bfd_byte *symtab_sha;
+  unsigned int i;
+  struct bfd_hash_entry *hash_entry;
+  struct bfd_elf_metasym_hash_entry *msym;
+
+  /* Don't output .symtab_meta at all if there are no symbols or meta
+     symbols.  */
+  if (bfd_get_symcount (abfd) == 0 || elf_metasym_count (abfd) == 0)
+    return TRUE;
+
+  symtab_sha = bfd_malloc (20);
+  if (symtab_sha == NULL)
+    return FALSE;
+
+  symtabmeta_hdr = &elf_symtabmeta_hdr (abfd);
+  symtabmeta_hdr->sh_type = SHT_GNU_SYMTAB_META;
+  symtabmeta_hdr->sh_addralign = 1;
+
+  /* The size of .symtab_hash and .symtab_meta version.  */
+  total_size = 21;
+
+  /* First we need to run through all the meta symbols to calculate how much
+     space needs to be reserved for the data that will be written as uleb128
+     (value and index).  We'll write out the data afterwards.
+     We also set the index of the corresponding symbol in the output symbol
+     table at msym->index, if it is not already set.  */
+  FOR_MSYM_IN_MSYM_HASH_TABLE (abfd, i, hash_entry, msym)
+    {
+      if (abfd->is_linker_output)
+	{
+	  /* We can get the index of global symbols (that will be
+	     output) from the linker hash table.
+	     For local symbols, _bfd_elf_link_adjust_metasym already set
+	     the correct index for us.  */
+	  if (msym->sym->flags & BSF_GLOBAL)
+	    {
+	      struct elf_link_hash_entry *h;
+	      h = elf_link_hash_lookup (elf_hash_table (link_info),
+					msym->sym->name, FALSE, FALSE, FALSE);
+	      if (h && h->indx > 0)
+		msym->index = h->indx;
+	    }
+	}
+      else
+	msym->index = msym->sym->udata.i;
+
+      if (msym->index > 0)
+	{
+	  total_size += uleb128_size (msym->index);
+	  total_size += uleb128_size (msym->value);
+	  /* Increment 1 byte for the size of the metasym kind.  */
+	  total_size++;
+	}
+      else
+	{
+	  /* We haven't found the symbol, assume it's been removed.
+	     Marking the index as 0 means this metasym won't be output.  */
+	  msym->index = 0;
+	}
+    }
+
+  contents = bfd_malloc (total_size);
+  if (contents == NULL)
+    return FALSE;
+
+  /* Write out the symtab_meta version.  */
+  contents[0] = 2;
+  amt = 1;
+
+  /* Calculate the SHA-1 of symtab.  */
+  if (abfd->is_linker_output)
+    {
+      bfd_size_type symtab_size = elf_symtab_hdr (abfd).sh_size;
+      char *raw_symtab = bfd_malloc (symtab_size);
+      if (raw_symtab == NULL)
+	return FALSE;
+
+      /* When linking, .symtab has never been stored completely in its final
+	 form, so we need to read it in.  */
+      if (bfd_seek (abfd, elf_symtab_hdr (abfd).sh_offset, SEEK_SET) != 0
+	  || bfd_bread (raw_symtab, symtab_size, abfd) != symtab_size)
+	return FALSE;
+
+      sha1_buffer (raw_symtab, symtab_size, symtab_sha);
+      free (raw_symtab);
+    }
+  else
+    {
+      symtab_hdr = &elf_symtab_hdr (abfd);
+      if (symtab_hdr->contents == NULL)
+	return FALSE;
+      sha1_buffer ((void *)symtab_hdr->contents, symtab_hdr->sh_size,
+		   symtab_sha);
+    }
+
+  memcpy (contents + amt, symtab_sha, 20);
+  amt += 20;
+
+  FOR_MSYM_IN_MSYM_HASH_TABLE (abfd, i, hash_entry, msym)
+    {
+      /* We've removed this entry.  */
+      if (msym->index == 0)
+	continue;
+
+      /* Symbol meta-information entries consist of:
+	 - kind: (single unsigned byte)
+	 - value: (uleb128)
+	 - index: (uleb128).  */
+      contents[amt++] = msym->kind;
+
+      amt += ((bfd_size_type) write_uleb128 (contents + amt, msym->value)
+	      - (bfd_size_type) (contents + amt));
+
+      amt += ((bfd_size_type) write_uleb128 (contents + amt, msym->index)
+	      - (bfd_size_type) (contents + amt));
+    }
+
+  /* Check that we've written out as much as we were expecting to write
+     out.  */
+  if (amt != total_size)
+    return FALSE;
+  symtabmeta_hdr->sh_size = total_size;
+  symtabmeta_hdr->contents = contents;
+
+  return TRUE;
+}
+
+/* Initialize an empty metasym hash table.  */
+static bfd_boolean
+_bfd_elf_metasym_hash_table_init
+(struct bfd_elf_metasym_hash_table *table,
+ struct bfd_hash_entry *(*newfunc) (struct bfd_hash_entry *,
+				    struct bfd_hash_table *,
+				    const char *),
+ unsigned int entsize)
+{
+  /* We may have already initialized the symtabmeta_version and symtab_hash
+     fields, so we don't touch them here.  */
+
+  return bfd_hash_table_init (&table->table, newfunc, entsize);
+}
+
+static struct bfd_hash_entry *
+_bfd_elf_metasym_hash_newfunc (struct bfd_hash_entry *entry,
+			       struct bfd_hash_table *table,
+			       const char *string)
+{
+  /* Allocate the structure if it has not already been allocated by a
+     subclass.  */
+  if (entry == NULL)
+    {
+      entry = (struct bfd_hash_entry *)
+	bfd_hash_allocate (table, sizeof (struct bfd_elf_metasym_hash_entry));
+      if (entry == NULL)
+	return entry;
+    }
+
+  /* Call the allocation method of the superclass.  */
+  entry = bfd_hash_newfunc (entry, table, string);
+  if (entry)
+    {
+      struct bfd_elf_metasym_hash_entry *h
+	= (struct bfd_elf_metasym_hash_entry *) entry;
+
+      /* Initialize the local fields.  */
+      memset ((char *) &h->root + sizeof (h->root), 0,
+	      sizeof (*h) - sizeof (h->root));
+    }
+
+  return entry;
+}
+
+struct bfd_elf_metasym_hash_entry *
+bfd_elf_metasym_hash_lookup (struct bfd_elf_metasym_hash_table *table,
+			     const char *name,
+			     Elf_Internal_Sym *elfsym,
+			     unsigned int bfd_id,
+			     bfd_boolean create,
+			     bfd_boolean copy)
+{
+  struct bfd_elf_metasym_hash_entry *msym;
+  struct bfd_elf_metasym_hash_entry *tsym;
+
+  if (table == NULL || name == NULL)
+    return NULL;
+
+  msym = ((struct bfd_elf_metasym_hash_entry *)
+	  bfd_hash_lookup (&table->table, name, create, copy));
+
+  /* This is a new entry or no entry was found.  */
+  if (msym == NULL || msym->sym == NULL)
+    return msym;
+
+  /* Look for metasyms with the same name in the chain attached to the first
+     metasym hash entry for that name.  */
+  for (tsym = msym; tsym != NULL; tsym = tsym->next)
+    {
+      if (tsym->elfsym->st_value == elfsym->st_value
+	  && tsym->elfsym->st_shndx == elfsym->st_shndx
+	  && tsym->bfd_id == bfd_id)
+	return tsym;
+      if (create && tsym->next == NULL)
+	{
+	  /* This is the last symbol in the chain and we haven't found a
+	     match, so create a new entry.  */
+	  struct bfd_elf_metasym_hash_entry * nsym
+	    = bfd_malloc (sizeof (struct bfd_elf_metasym_hash_entry));
+	  /* Initialize the local fields.  */
+	  memset ((char *) &nsym->root + sizeof (nsym->root), 0,
+		  sizeof (*nsym) - sizeof (nsym->root));
+	  nsym->root.string = name;
+	  tsym->next = nsym;
+	  return nsym;
+	}
+    }
+  /* A matching metasym was not found.  */
+  return NULL;
+}
+
+
+/* Add a symbol meta-information entry to the table stored in ABFD.  */
+bfd_boolean
+bfd_elf_add_metasym (bfd *abfd,
+		     bfd *ibfd,
+		     asymbol *sym,
+		     Elf_Internal_Sym *elfsym,
+		     bfd_elf_metasym_kind kind,
+		     bfd_vma value,
+		     bfd_vma index)
+{
+  struct bfd_elf_metasym_hash_entry * msym;
+  if (ibfd == NULL)
+    ibfd = abfd;
+
+  if (elf_metasym_count(abfd) == 0)
+    {
+      _bfd_elf_metasym_hash_table_init (&elf_tdata(abfd)->metasym_hash_table,
+					_bfd_elf_metasym_hash_newfunc,
+					sizeof (struct
+						bfd_elf_metasym_hash_entry));
+    }
+
+  /* When we have to use this elfsym pointer, st_value and won't get updated
+     if relaxations change the address of the symbol.  */
+  if (elfsym == NULL)
+    elfsym = &(elf_symbol_from (abfd, sym))->internal_elf_sym;
+
+  msym = bfd_elf_metasym_hash_lookup (&elf_tdata(abfd)->metasym_hash_table,
+				      sym->name, elfsym, ibfd->id, TRUE, FALSE);
+  if (msym == NULL)
+    return FALSE;
+
+  msym->sym = sym;
+  msym->kind = kind;
+  msym->value = value;
+  /* This stored index might become unreliable in any program that can modify
+     the symbol table, it gets updated again when necessary before
+     outputting .symtab_meta.  */
+  msym->index = index;
+  msym->elfsym = elfsym;
+  msym->bfd_id = ibfd->id;
+  return TRUE;
+}
+
+/* Copy the symbol meta-information table from IBFD to OBFD.
+   If the table doesn't exist in IBFD, don't do anything.
+   If the table is empty in IBFD, only copy the version and .symtab hash.  */
+bfd_boolean
+bfd_elf_copy_metasyms (bfd *obfd, bfd *ibfd)
+{
+  unsigned int i;
+  struct bfd_hash_entry *hash_entry;
+  struct bfd_elf_metasym_hash_entry *msym;
+  if (elf_symtabmeta_version (obfd) != 0)
+    {
+      /* The symbol meta-information table in OBFD appears to already be
+	 initialized, this is unexpected.  */
+      return FALSE;
+    }
+  else if (elf_symtabmeta_version (ibfd) == 0)
+    return TRUE;
+
+  elf_symtabmeta_version (obfd) = elf_symtabmeta_version (ibfd);
+  elf_symtabmeta_symtab_hash (obfd) = elf_symtabmeta_symtab_hash (ibfd);
+  if (elf_metasym_count (ibfd) == 0)
+    {
+      /* There is a symbol meta-information table, but no entries, so don't go
+	 any further.  */
+      return TRUE;
+    }
+
+  /* XXX Just copying the pointer to symtabmeta_hdr works for objcopy but
+     something more sophisticated may be needed if this function is used in
+     other programs.  Likewise for copying the asymbol from msym->sym.  */
+  elf_symtabmeta_hdr (obfd) = elf_symtabmeta_hdr (ibfd);
+
+  FOR_MSYM_IN_MSYM_HASH_TABLE (ibfd, i, hash_entry, msym)
+    if (!bfd_elf_add_metasym (obfd, ibfd, msym->sym, msym->elfsym, msym->kind,
+			      msym->value, msym->index))
+      return FALSE;
+  return TRUE;
+}
+
+\f
+
 /* Swap out the symbols.  */
 
 static bfd_boolean
@@ -8360,6 +8957,14 @@ error_return:
   symstrtab_hdr->sh_info = 0;
   symstrtab_hdr->sh_addralign = 1;
 
+  /* After .symtab has been finalized, write out .symtab_meta.  */
+  if (!bfd_elf_write_symtab_meta (abfd, NULL))
+    {
+      _bfd_error_handler (_("%pB: failed to write out .symtab_meta"),
+			  abfd);
+      return FALSE;
+    }
+
   return TRUE;
 }
 
diff --git a/bfd/elflink.c b/bfd/elflink.c
index 3add9f18bd..b3477f2108 100644
--- a/bfd/elflink.c
+++ b/bfd/elflink.c
@@ -4273,6 +4273,48 @@ error_free_dyn:
       elf_dt_audit (abfd) = audit;
     }
 
+  /* FIXME: We could support writing out to some other formats e.g. binary.  */
+  if (elf_tdata (abfd)->symtabmeta_hdr.sh_size > 0
+      && bfd_get_flavour (info->output_bfd) == bfd_target_elf_flavour)
+    {
+      /* Read in the symbol table for the input BFD so we can set up the
+	 symbol meta-information.  */
+      Elf_Internal_Shdr *symtab_hdr;
+      long symsize = bfd_get_symtab_upper_bound (abfd);
+      BFD_ASSERT (symsize >= 0);
+
+      asymbol **sympp = NULL;
+      sympp = (asymbol **) bfd_malloc (symsize);
+      BFD_ASSERT (sympp != NULL);
+
+      symcount = bfd_canonicalize_symtab (abfd, sympp);
+
+      symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
+      /* If relaxation of relocations results in any changes to the st_value of
+	 Elf_Internal_Symbols, then that symbol in symtab_hdr->contents will be
+	 updated accordingly.  So we need to make sure that the
+	 Elf_Internal_Sym stored in metasyms is a pointer to the corresponding
+	 symbol in symtab_hdr->contents.  */
+      if (symtab_hdr->contents == NULL && symtab_hdr->sh_info != 0)
+	{
+	  /* We only need local syms, and it seems standard to only fill
+	     symtab_hdr->contents with local syms anyway.  */
+	  symtab_hdr->contents = (bfd_byte *)
+	    bfd_elf_get_elf_syms (abfd,
+				  symtab_hdr,
+				  symtab_hdr->sh_info, 0,
+				  NULL, NULL, NULL);
+	}
+
+
+      if (!bfd_elf_slurp_symtab_meta (abfd, info->output_bfd, sympp, symcount))
+	{
+	  _bfd_error_handler (_("%pB: failed to read in .symtab_meta"),
+			      abfd);
+	  return FALSE;
+	}
+    }
+
   /* If this is a dynamic object, we always link against the .dynsym
      symbol table, not the .symtab symbol table.  The dynamic linker
      will only see the .dynsym symbol table, so there is no reason to
@@ -5140,6 +5182,34 @@ error_free_dyn:
 	}
     }
 
+  /* Now the hash table has been initialized, mark the ELF symbols
+     corresponding to metasymbols with SMK_RETAIN, to prevent garbage
+     collection.  */
+  if (is_elf_hash_table (htab) && elf_metasym_count (info->output_bfd) > 0)
+    {
+      unsigned int i;
+      struct bfd_hash_entry *hash_entry;
+      struct bfd_elf_metasym_hash_entry *msym;
+      FOR_MSYM_IN_MSYM_HASH_TABLE (info->output_bfd, i, hash_entry, msym)
+	{
+	  if (msym->kind != SMK_RETAIN)
+	    continue;
+	  struct elf_link_hash_entry *h;
+	  h = elf_link_hash_lookup (htab, msym->sym->name, FALSE, FALSE, FALSE);
+
+	  if (h != NULL)
+	    {
+	      h->root.type = bfd_link_hash_defined;
+	      h->mark = 1;
+	      h->root.u.def.section = msym->sym->section;
+	      h->root.u.def.section->flags |= SEC_KEEP;
+	    }
+	  else
+	    /* For local symbols, this is all that's needed.  */
+	    msym->sym->section->flags |= SEC_KEEP;
+	}
+    }
+
   if (info->lto_plugin_active
       && !bfd_link_relocatable (info)
       && (abfd->flags & BFD_PLUGIN) == 0
@@ -10427,6 +10497,26 @@ _bfd_elf_check_kept_section (asection *sec, struct bfd_link_info *info)
   return kept;
 }
 
+/* Set the output symbol table index of the local ELF symbols corresponding to
+   metasyms.  This is our only chance to get the correct value for that index.
+   We only do this for local symbols, since we can get the output symbol table
+   index of global symbols from the linker hash table when we go to write out
+   .symtab_meta.  */
+static void
+_bfd_elf_link_adjust_metasym (bfd *obfd, bfd *ibfd, Elf_Internal_Sym *isym,
+			      long idx)
+{
+  Elf_Internal_Shdr *symtab_hdr = &elf_tdata (ibfd)->symtab_hdr;
+  const char *string = bfd_elf_sym_name (ibfd, symtab_hdr, isym, NULL);
+
+  struct bfd_elf_metasym_hash_entry *h =
+    (struct bfd_elf_metasym_hash_entry *)
+    bfd_elf_metasym_hash_lookup (&elf_tdata(obfd)->metasym_hash_table,
+				 string, isym, ibfd->id, FALSE, FALSE);
+  if (h)
+    h->index = idx;
+}
+
 /* Link an input file into the linker output file.  This function
    handles all the sections and relocations of the input file at once.
    This is so that we only have to read the local symbols once, and
@@ -10651,6 +10741,8 @@ elf_link_input_bfd (struct elf_final_link_info *flinfo, bfd *input_bfd)
 	}
 
       indx = bfd_get_symcount (output_bfd);
+      if (elf_metasym_count (output_bfd) > 0)
+	_bfd_elf_link_adjust_metasym (output_bfd, input_bfd, isym, indx);
       ret = elf_link_output_symstrtab (flinfo, name, &osym, isec, NULL);
       if (ret == 0)
 	return FALSE;
@@ -12580,6 +12672,24 @@ bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info)
       return FALSE;
     }
 
+  /* Write out .symtab_meta.  */
+  if (bfd_get_symcount (abfd) > 0 && elf_metasym_count (abfd) > 0)
+    {
+      Elf_Internal_Shdr *symtabmeta_hdr = &elf_tdata (abfd)->symtabmeta_hdr;
+
+      if (!bfd_elf_write_symtab_meta (flinfo.output_bfd, info))
+	{
+	  _bfd_error_handler (_("%pB: failed to write out .symtab_meta"),
+			      flinfo.output_bfd);
+	  return FALSE;
+	}
+
+      elf_next_file_pos (abfd) =
+	_bfd_elf_assign_file_position_for_section (symtabmeta_hdr,
+						   elf_next_file_pos (abfd),
+						   TRUE);
+    }
+
   /* Adjust the relocs to have the correct symbol indices.  */
   for (o = abfd->sections; o != NULL; o = o->next)
     {
diff --git a/binutils/doc/binutils.texi b/binutils/doc/binutils.texi
index 289d7b14a3..6235404f3d 100644
--- a/binutils/doc/binutils.texi
+++ b/binutils/doc/binutils.texi
@@ -2153,6 +2153,7 @@ objdump [@option{-a}|@option{--archive-headers}]
         [@option{--ctf=}@var{section}]
         [@option{-G}|@option{--stabs}]
         [@option{-t}|@option{--syms}]
+        [@option{--symtab-meta}]
         [@option{-T}|@option{--dynamic-syms}]
         [@option{-x}|@option{--all-headers}]
         [@option{-w}|@option{--wide}]
@@ -2811,6 +2812,28 @@ The symbol is the name of a function (F) or a file (f) or an object
 (O) or just a normal symbol (a space).
 @end table
 
+@item --symtab-meta
+@cindex symbol meta-information entries, printing
+@cindex .symtab-meta
+Print the symbol meta-information entries of the file.
+
+@smallexample
+SYMBOL META-INFORMATION TABLE:
+Idx	Kind		Value		Sym idx	Name
+0:	SMK_RETAIN	0x1		24	var_global_retain
+1:	SMK_RETAIN	0x1		11	var_global_static_retain
+2:	SMK_RETAIN	0x1		22	fn_retain
+3:	SMK_RETAIN	0x1		12	fn_static_retain
+@end smallexample
+
+The values in the @samp{Sym idx} column indicate the symbol's index in
+@code{.symtab}.  The symbol names in the @samp{Name} column describe the ELF
+symbol this symbol meta-information entry relates to.
+
+The meaning of the possible values for @samp{Kind} and
+@samp{Value} are described in the @code{GAS} documentation for the
+@code{.sym_meta_info} directive.
+
 @item -T
 @itemx --dynamic-syms
 @cindex dynamic symbol table entries, printing
diff --git a/binutils/objcopy.c b/binutils/objcopy.c
index fd94d63773..1b613dbef2 100644
--- a/binutils/objcopy.c
+++ b/binutils/objcopy.c
@@ -2785,6 +2785,19 @@ copy_object (bfd *ibfd, bfd *obfd, const bfd_arch_info_type *input_arch)
       osympp = isympp = NULL;
     }
 
+  /* FIXME: We could support writing out to some other formats e.g. binary.  */
+  if (isympp && bfd_get_flavour (ibfd) == bfd_target_elf_flavour
+      && bfd_get_flavour (obfd) == bfd_target_elf_flavour)
+    {
+      if (!bfd_elf_slurp_symtab_meta (ibfd, NULL, isympp, symcount)
+	  || !bfd_elf_copy_metasyms (obfd, ibfd))
+	{
+	  bfd_nonfatal_message (NULL, obfd, NULL,
+			       _("error: failed to read in .symtab_meta"));
+	  return FALSE;
+	}
+    }
+
   /* BFD mandates that all output sections be created and sizes set before
      any output is done.  Thus, we traverse all sections multiple times.  */
   bfd_map_over_sections (ibfd, setup_section, obfd);
diff --git a/binutils/objdump.c b/binutils/objdump.c
index 8182dcc362..fd7fbe26e0 100644
--- a/binutils/objdump.c
+++ b/binutils/objdump.c
@@ -87,6 +87,7 @@ static int dump_section_contents;	/* -s */
 static int dump_section_headers;	/* -h */
 static bfd_boolean dump_file_header;	/* -f */
 static int dump_symtab;			/* -t */
+static int dump_symtab_meta;		/* --symtab-meta */
 static int dump_dynamic_symtab;		/* -T */
 static int dump_reloc_info;		/* -r */
 static int dump_dynamic_reloc_info;	/* -R */
@@ -328,7 +329,8 @@ enum option_values
     OPTION_SOURCE_COMMENT,
     OPTION_CTF,
     OPTION_CTF_PARENT,
-    OPTION_VISUALIZE_JUMPS
+    OPTION_VISUALIZE_JUMPS,
+    OPTION_SYMTAB_META
   };
 
 static struct option long_options[]=
@@ -378,6 +380,7 @@ static struct option long_options[]=
   {"start-address", required_argument, NULL, OPTION_START_ADDRESS},
   {"stop-address", required_argument, NULL, OPTION_STOP_ADDRESS},
   {"syms", no_argument, NULL, 't'},
+  {"symtab-meta", no_argument, NULL, OPTION_SYMTAB_META},
   {"target", required_argument, NULL, 'b'},
   {"version", no_argument, NULL, 'V'},
   {"wide", no_argument, NULL, 'w'},
@@ -4323,6 +4326,39 @@ dump_data (bfd *abfd)
   bfd_map_over_sections (abfd, dump_section, NULL);
 }
 
+static void
+dump_meta_symbols (bfd *abfd)
+{
+  int ii = 0;
+  unsigned int i;
+  struct bfd_hash_entry *hash_entry;
+  struct bfd_elf_metasym_hash_entry *msym;
+  if (elf_symtabmeta_version (abfd) == 0)
+    {
+      printf ("NO SYMBOL META-INFORMATION TABLE\n");
+      return;
+    }
+  printf ("SYMBOL META-INFORMATION TABLE:\n");
+  printf ("Idx\tKind\t\tValue\t\tSym idx\tName\n");
+
+  FOR_MSYM_IN_MSYM_HASH_TABLE (abfd, i, hash_entry, msym)
+    {
+      /* Col width: 8 chars.  */
+      printf ("%d:\t", ii);
+      /* Col width: 16 chars.  */
+      printf ("%s\t", (msym->kind == SMK_RETAIN ? "SMK_RETAIN"
+		       : "SMK_LOCATION"));
+      /* Col width: 16 chars.  */
+      printf ("0x%lx%s", msym->value, (msym->value > 0xFFFFFFFF ? "\t"
+				       : "\t\t"));
+      /* Col width: 8 chars.  */
+      printf ("%d\t", msym->index + 1);
+      /* FIXME: 32 chars left for the name.  */
+      printf ("%s\n", msym->sym->name);
+      ii++;
+    }
+}
+
 /* Should perhaps share code and display with nm?  */
 
 static void
@@ -4777,6 +4813,7 @@ dump_bfd (bfd *abfd, bfd_boolean is_mainfile)
       || dump_reloc_info
       || disassemble
       || dump_debugging
+      || dump_symtab_meta
       || dump_dwarf_section_info)
     {
       syms = slurp_symtab (abfd);
@@ -4813,6 +4850,22 @@ dump_bfd (bfd *abfd, bfd_boolean is_mainfile)
 	}
     }
 
+  if (dump_symtab_meta && syms != NULL)
+    {
+      if (bfd_get_flavour (abfd) != bfd_target_elf_flavour)
+	{
+	  non_fatal (_("%s: --symtab-meta is only supported for ELF"),
+		     bfd_get_filename (abfd));
+	  exit_status = 1;
+	}
+      else if (!bfd_elf_slurp_symtab_meta (abfd, NULL, syms, symcount))
+	{
+	  non_fatal (_("%s: failed to read in .symtab_meta"),
+		     bfd_get_filename (abfd));
+	  exit_status = 1;
+	}
+    }
+
   if (dump_section_headers)
     dump_headers (abfd);
 
@@ -4832,6 +4885,8 @@ dump_bfd (bfd *abfd, bfd_boolean is_mainfile)
     dump_symbols (abfd, FALSE);
   if (dump_dynamic_symtab)
     dump_symbols (abfd, TRUE);
+  if (dump_symtab_meta)
+    dump_meta_symbols (abfd);
   if (dump_dwarf_section_info)
     dump_dwarf (abfd);
   if (dump_ctf_section_info)
@@ -5311,6 +5366,10 @@ main (int argc, char **argv)
 	case OPTION_CTF_PARENT:
 	  dump_ctf_parent_name = xstrdup (optarg);
 	  break;
+	case OPTION_SYMTAB_META:
+	  dump_symtab_meta = 1;
+	  seenflag = TRUE;
+	  break;
 	case 'G':
 	  dump_stab_section_info = TRUE;
 	  seenflag = TRUE;
diff --git a/binutils/readelf.c b/binutils/readelf.c
index 58667dd407..d4f5f182a7 100644
--- a/binutils/readelf.c
+++ b/binutils/readelf.c
@@ -4389,6 +4389,7 @@ get_section_type_name (Filedata * filedata, unsigned int sh_type)
 		    case SHT_GNU_ATTRIBUTES: result = "GNU_ATTRIBUTES"; break;
 		    case SHT_GNU_HASH: result = "GNU_HASH"; break;
 		    case SHT_GNU_LIBLIST: result = "GNU_LIBLIST"; break;
+		    case SHT_GNU_SYMTAB_META: result = "GNU_SYMTAB_META"; break;
 		    default:
 		      result = NULL;
 		      break;
diff --git a/gas/config/obj-elf.c b/gas/config/obj-elf.c
index a7225983f6..5bee8115fe 100644
--- a/gas/config/obj-elf.c
+++ b/gas/config/obj-elf.c
@@ -72,6 +72,7 @@ static void obj_elf_weak (int);
 static void obj_elf_local (int);
 static void obj_elf_visibility (int);
 static void obj_elf_symver (int);
+static void obj_elf_sym_meta_info (int);
 static void obj_elf_subsection (int);
 static void obj_elf_popsection (int);
 static void obj_elf_gnu_attribute (int);
@@ -109,6 +110,9 @@ static const pseudo_typeS elf_pseudo_table[] =
   /* This is a GNU extension to handle symbol versions.  */
   {"symver", obj_elf_symver, 0},
 
+  /* This is an ELF extension to handle symbol meta-information.  */
+  {"sym_meta_info", obj_elf_sym_meta_info, 0},
+
   /* A GNU extension to change subsection only.  */
   {"subsection", obj_elf_subsection, 0},
 
@@ -1422,6 +1426,92 @@ obj_elf_struct (int i)
 #endif
 }
 
+/* Parse a directive of the form:
+   .sym_meta_info NAME, KIND, VALUE
+   NAME is the name of the symbol the meta-information entry relates to.
+   KIND is SMK_{RETAIN,LOCATION} (defined in bfd/elf-bfd.h).
+   VALUE is of type bfd_vma.  */
+void
+obj_elf_sym_meta_info (int arg ATTRIBUTE_UNUSED)
+{
+  char c;
+  bfd_vma value;
+  bfd_elf_metasym_kind kind = SMK_NONE;
+  char *meta_kind;
+  symbolS *sym;
+  asymbol *bfdsym;
+
+  /* Read the name of the symbol this meta-information relates to.  */
+  sym = get_sym_from_input_line_and_check ();
+  c = *input_line_pointer;
+  bfdsym = symbol_get_bfdsym (sym);
+
+  if (*input_line_pointer != ',')
+    {
+      as_bad (_("expected comma after name in .sym_meta_info"));
+      ignore_rest_of_line ();
+      return;
+    }
+  ++input_line_pointer;
+  SKIP_WHITESPACE ();
+
+  /* Read the "kind" of the meta-information.  */
+  c = get_symbol_name (& meta_kind);
+  if (strcmp (meta_kind, "SMK_RETAIN") == 0)
+    kind = SMK_RETAIN;
+  else if (strcmp (meta_kind, "SMK_LOCATION") == 0)
+    kind = SMK_LOCATION;
+  else
+    {
+      as_bad (_("unrecognized symbol meta-information kind \"%s\""), meta_kind);
+      /* get_symbol_name modified the comma after the kind into a NULL character
+	 so we must progress past it to prevent a "junk at EOL" message.  */
+      if (!is_end_of_line[(unsigned char)c])
+	++input_line_pointer;
+      ignore_rest_of_line ();
+      return;
+    }
+
+  if (c != ',')
+    {
+      as_bad (_("expected comma after kind in .sym_meta_info"));
+      ignore_rest_of_line ();
+      return;
+    }
+  ++input_line_pointer;
+  SKIP_WHITESPACE ();
+
+  if (!is_end_of_line[(unsigned char)*input_line_pointer])
+    {
+      /* This automatically handles both hex and decimal values.  */
+      value = get_absolute_expression ();
+      switch (kind)
+	{
+	case SMK_RETAIN:
+	  if (value > 1)
+	    as_bad (_("bad value \"%lu\" for SMK_RETAIN meta-information"),
+		    value);
+	  break;
+	case SMK_LOCATION:
+	  /* An invalid value would be an address that exceeds the memory range
+	     of the target, but we'll leave it for the linker to warn about
+	     these sorts of issues.  */
+	  break;
+	default:
+	  abort ();
+	}
+    }
+  else
+    {
+      as_bad (_("expected value in .sym_meta_info directive for symbol \"%s\""),
+	      bfdsym->name);
+      return;
+    }
+  demand_empty_rest_of_line ();
+
+  bfd_elf_add_metasym (stdoutput, NULL, bfdsym, NULL, kind, value, 0);
+}
+
 static void
 obj_elf_subsection (int ignore ATTRIBUTE_UNUSED)
 {
diff --git a/gas/doc/as.texi b/gas/doc/as.texi
index 1554c51ad2..7ca4051ecb 100644
--- a/gas/doc/as.texi
+++ b/gas/doc/as.texi
@@ -4497,6 +4497,7 @@ Some machine configurations provide additional directives.
 * Struct::			@code{.struct @var{expression}}
 @ifset ELF
 * SubSection::                  @code{.subsection}
+* SymMetaInfo::			@code{.sym_meta_info @var{symbol}, @var{kind}, @var{value}}
 * Symver::                      @code{.symver @var{name},@var{name2@@nodename}}
 @end ifset
 
@@ -7069,6 +7070,43 @@ section is not changed.  The replaced subsection is put onto the section stack
 in place of the then current top of stack subsection.
 @end ifset
 
+@ifset ELF
+@node SymMetaInfo
+@section @code{.sym_meta_info @var{symbol}, @var{kind}, @var{value}}
+@cindex @code{sym_meta_info} directive
+@cindex symbol metadata
+@cindex symbol meta-information entry
+@cindex .symtab_meta
+Use the @code{.sym_meta_info} directive to add a symbol meta-information entry
+for the specified @var{symbol}.
+
+Symbol meta-information entries are stored in a
+special section @code{.symtab_meta}.  Symbol meta-information is an ELF
+extension, see the binutils documentation for more detailed information.
+
+Only the BFD linker can currently make use of symbol meta-information to retain
+or set the location of symbols.
+
+Accepted values of @var{kind} are:
+@table @t
+@item @code{SMK_RETAIN}
+A symbol marked with @code{SMK_RETAIN} will be retained at link time even if
+garbage collection (enabled with @code{-gc-sections}) would remove the section
+containing the symbol.  @var{value} is 1 if the symbol is to be retained, or 0
+otherwise.
+
+@item @code{SMK_LOCATION}
+@code{SMK_LOCATION} indicates that the @code{VMA} of symbol should be set to
+@var{value}, if possible.  Currently only the input section containing the
+symbol definition can be placed at a specific address, so for best results the
+desired symbol should be within its own input section.
+
+@var{value} must be an integer which is a valid address on the target system.
+Both decimal and hexadecimal values are accepted.
+@end table
+
+@end ifset
+
 @ifset ELF
 @node Symver
 @section @code{.symver}
diff --git a/include/elf/common.h b/include/elf/common.h
index 53b72445ca..8e6db7b289 100644
--- a/include/elf/common.h
+++ b/include/elf/common.h
@@ -513,6 +513,7 @@
 #define SHT_GNU_ATTRIBUTES 0x6ffffff5	/* Object attributes */
 #define SHT_GNU_HASH	0x6ffffff6	/* GNU style symbol hash table */
 #define SHT_GNU_LIBLIST	0x6ffffff7	/* List of prelink dependencies */
+#define SHT_GNU_SYMTAB_META 0x6ffffff8	/* .symtab_meta section */
 
 /* The next three section types are defined by Solaris, and are named
    SHT_SUNW*.  We use them in GNU code, so we also define SHT_GNU*
diff --git a/ld/ld.texi b/ld/ld.texi
index 9bb3d55587..540c8fc022 100644
--- a/ld/ld.texi
+++ b/ld/ld.texi
@@ -1658,6 +1658,9 @@ specified either by one of the options @samp{--entry},
 @samp{--undefined}, or @samp{--gc-keep-exported} or by a @code{ENTRY}
 command in the linker script.
 
+As an ELF extension, sections which contain symbols marked with
+@code{SMK_RETAIN} symbol meta-information will not be garbage collected.
+
 @kindex --print-gc-sections
 @kindex --no-print-gc-sections
 @cindex garbage collection
@@ -4418,6 +4421,10 @@ Specifying @var{address} for a section will change the value of the
 location counter, provided that the section is non-empty.  (Empty
 sections are ignored).
 
+As an ELF extension, sections which contain symbols marked with
+@code{SMK_LOCATION} symbol meta-information will be placed at the the address
+specified in the meta-information, overriding any linker script placement.
+
 @node Input Section
 @subsection Input Section Description
 @cindex input sections
-- 
2.17.1


[-- Attachment #3: 0002-TESTSUITE-Add-symbol-meta-information-tests.patch --]
[-- Type: text/x-patch, Size: 58598 bytes --]

From a7e7c00b1bcbb1e48c4d7d7c213502406f657f37 Mon Sep 17 00:00:00 2001
From: Jozef Lawrynowicz <jozef.l@mittosystems.com>
Date: Tue, 18 Feb 2020 10:06:30 +0000
Subject: [PATCH 2/2] TESTSUITE: Add symbol meta-information tests

binutils/ChangeLog:

2020-02-17  Jozef Lawrynowicz  <jozef.l@mittosystems.com>

	* testsuite/binutils-all/metasym-1.d: New test.
	* testsuite/binutils-all/metasym-1.s: New test.
	* testsuite/binutils-all/metasym-2.d: New test.
	* testsuite/binutils-all/metasym-3.d: New test.
	* testsuite/binutils-all/metasym-4.d: New test.
	* testsuite/binutils-all/metasym-5.d: New test.
	* testsuite/binutils-all/metasym-6.d: New test.
	* testsuite/binutils-all/metasym-7.d: New test.
	* testsuite/binutils-all/metasym-8.d: New test.
	* testsuite/binutils-all/metasym-9.d: New test.
	* testsuite/binutils-all/objcopy.exp: Run new tests.

gas/ChangeLog:

2020-02-17  Jozef Lawrynowicz  <jozef.l@mittosystems.com>

	* testsuite/gas/elf/elf.exp: Run new tests.
	* testsuite/gas/elf/metasym-0.d: New test.
	* testsuite/gas/elf/metasym-0.s: New test.
	* testsuite/gas/elf/metasym-1.d: New test.
	* testsuite/gas/elf/metasym-1.s: New test.
	* testsuite/gas/elf/metasym-bad-1.d: New test.
	* testsuite/gas/elf/metasym-bad-1.l: New test.
	* testsuite/gas/elf/metasym-bad-1.s: New test.
	* testsuite/gas/elf/metasym-bad-2.d: New test.
	* testsuite/gas/elf/metasym-bad-2.l: New test.
	* testsuite/gas/elf/metasym-bad-2.s: New test.
	* testsuite/gas/elf/metasym-bad-3.d: New test.
	* testsuite/gas/elf/metasym-bad-3.l: New test.
	* testsuite/gas/elf/metasym-bad-3.s: New test.

ld/ChangeLog:

2020-02-17  Jozef Lawrynowicz  <jozef.l@mittosystems.com>

	* testsuite/ld-elf/elf.exp: Run metasym-4 test.
	* testsuite/ld-elf/metasym-0.d: New test.
	* testsuite/ld-elf/metasym-1.d: New test.
	* testsuite/ld-elf/metasym-1.s: New test.
	* testsuite/ld-elf/metasym-2.d: New test.
	* testsuite/ld-elf/metasym-2.s: New test.
	* testsuite/ld-elf/metasym-3.d: New test.
	* testsuite/ld-elf/metasym-3.s: New test.
	* testsuite/ld-elf/metasym-4.od: New test.
	* testsuite/ld-elf/metasym-5-1.d: New test.
	* testsuite/ld-elf/metasym-5-2.d: New test.
	* testsuite/ld-elf/metasym-5-local-dup-1.s: New test.
	* testsuite/ld-elf/metasym-5-local-dup-2.s: New test.
	* testsuite/ld-i386/load1-nacl.d: Use --syms instead of --sym option.
	* testsuite/ld-i386/load1.d: Likewise.
	* testsuite/ld-i386/pie1-nacl.d: Likewise.
	* testsuite/ld-i386/pie1.d: Likewise.
	* testsuite/ld-i386/pr20244-1a.d: Likewise.
	* testsuite/ld-i386/pr20244-2a.d: Likewise.
	* testsuite/ld-msp430-elf/metasym-relax.d: New test.
	* testsuite/ld-msp430-elf/metasym-relax.s: New test.
	* testsuite/ld-msp430-elf/msp430-elf.exp: Run new test.
	* testsuite/ld-x86-64/load1a-nacl.d: Use --syms instead of --sym option.
	* testsuite/ld-x86-64/load1a.d: Likewise.
	* testsuite/ld-x86-64/load1b-nacl.d: Likewise.
	* testsuite/ld-x86-64/load1b.d: Likewise.
---
 binutils/testsuite/binutils-all/metasym-1.d |  17 +++
 binutils/testsuite/binutils-all/metasym-1.s | 138 ++++++++++++++++++++
 binutils/testsuite/binutils-all/metasym-2.d |  11 ++
 binutils/testsuite/binutils-all/metasym-3.d |   9 ++
 binutils/testsuite/binutils-all/metasym-4.d |  17 +++
 binutils/testsuite/binutils-all/metasym-5.d |  18 +++
 binutils/testsuite/binutils-all/metasym-6.d |  18 +++
 binutils/testsuite/binutils-all/metasym-7.d |  18 +++
 binutils/testsuite/binutils-all/metasym-8.d |  18 +++
 binutils/testsuite/binutils-all/metasym-9.d |  18 +++
 binutils/testsuite/binutils-all/objcopy.exp |  13 ++
 gas/testsuite/gas/elf/elf.exp               |   6 +
 gas/testsuite/gas/elf/metasym-0.d           |   8 ++
 gas/testsuite/gas/elf/metasym-0.s           |  35 +++++
 gas/testsuite/gas/elf/metasym-1.d           |  17 +++
 gas/testsuite/gas/elf/metasym-1.s           | 138 ++++++++++++++++++++
 gas/testsuite/gas/elf/metasym-bad-1.d       |   2 +
 gas/testsuite/gas/elf/metasym-bad-1.l       |   2 +
 gas/testsuite/gas/elf/metasym-bad-1.s       |   9 ++
 gas/testsuite/gas/elf/metasym-bad-2.d       |   2 +
 gas/testsuite/gas/elf/metasym-bad-2.l       |   2 +
 gas/testsuite/gas/elf/metasym-bad-2.s       |   9 ++
 gas/testsuite/gas/elf/metasym-bad-3.d       |   2 +
 gas/testsuite/gas/elf/metasym-bad-3.l       |   2 +
 gas/testsuite/gas/elf/metasym-bad-3.s       |   9 ++
 ld/testsuite/ld-elf/elf.exp                 |  10 ++
 ld/testsuite/ld-elf/metasym-0.d             |  14 ++
 ld/testsuite/ld-elf/metasym-1.d             |  27 ++++
 ld/testsuite/ld-elf/metasym-1.s             | 110 ++++++++++++++++
 ld/testsuite/ld-elf/metasym-2.d             |  40 ++++++
 ld/testsuite/ld-elf/metasym-2.s             |  68 ++++++++++
 ld/testsuite/ld-elf/metasym-3.d             |  40 ++++++
 ld/testsuite/ld-elf/metasym-3.s             |  68 ++++++++++
 ld/testsuite/ld-elf/metasym-4.od            |  38 ++++++
 ld/testsuite/ld-elf/metasym-5-1.d           |  30 +++++
 ld/testsuite/ld-elf/metasym-5-2.d           |  23 ++++
 ld/testsuite/ld-elf/metasym-5-local-dup-1.s |  16 +++
 ld/testsuite/ld-elf/metasym-5-local-dup-2.s |   7 +
 ld/testsuite/ld-i386/load1-nacl.d           |   2 +-
 ld/testsuite/ld-i386/load1.d                |   2 +-
 ld/testsuite/ld-i386/pie1-nacl.d            |   2 +-
 ld/testsuite/ld-i386/pie1.d                 |   2 +-
 ld/testsuite/ld-i386/pr20244-1a.d           |   2 +-
 ld/testsuite/ld-i386/pr20244-2a.d           |   2 +-
 ld/testsuite/ld-msp430-elf/metasym-relax.d  |  28 ++++
 ld/testsuite/ld-msp430-elf/metasym-relax.s  |  38 ++++++
 ld/testsuite/ld-msp430-elf/msp430-elf.exp   |   2 +
 ld/testsuite/ld-x86-64/load1a-nacl.d        |   2 +-
 ld/testsuite/ld-x86-64/load1a.d             |   2 +-
 ld/testsuite/ld-x86-64/load1b-nacl.d        |   2 +-
 ld/testsuite/ld-x86-64/load1b.d             |   2 +-
 51 files changed, 1107 insertions(+), 10 deletions(-)
 create mode 100644 binutils/testsuite/binutils-all/metasym-1.d
 create mode 100644 binutils/testsuite/binutils-all/metasym-1.s
 create mode 100644 binutils/testsuite/binutils-all/metasym-2.d
 create mode 100644 binutils/testsuite/binutils-all/metasym-3.d
 create mode 100644 binutils/testsuite/binutils-all/metasym-4.d
 create mode 100644 binutils/testsuite/binutils-all/metasym-5.d
 create mode 100644 binutils/testsuite/binutils-all/metasym-6.d
 create mode 100644 binutils/testsuite/binutils-all/metasym-7.d
 create mode 100644 binutils/testsuite/binutils-all/metasym-8.d
 create mode 100644 binutils/testsuite/binutils-all/metasym-9.d
 create mode 100644 gas/testsuite/gas/elf/metasym-0.d
 create mode 100644 gas/testsuite/gas/elf/metasym-0.s
 create mode 100644 gas/testsuite/gas/elf/metasym-1.d
 create mode 100644 gas/testsuite/gas/elf/metasym-1.s
 create mode 100644 gas/testsuite/gas/elf/metasym-bad-1.d
 create mode 100644 gas/testsuite/gas/elf/metasym-bad-1.l
 create mode 100644 gas/testsuite/gas/elf/metasym-bad-1.s
 create mode 100644 gas/testsuite/gas/elf/metasym-bad-2.d
 create mode 100644 gas/testsuite/gas/elf/metasym-bad-2.l
 create mode 100644 gas/testsuite/gas/elf/metasym-bad-2.s
 create mode 100644 gas/testsuite/gas/elf/metasym-bad-3.d
 create mode 100644 gas/testsuite/gas/elf/metasym-bad-3.l
 create mode 100644 gas/testsuite/gas/elf/metasym-bad-3.s
 create mode 100644 ld/testsuite/ld-elf/metasym-0.d
 create mode 100644 ld/testsuite/ld-elf/metasym-1.d
 create mode 100644 ld/testsuite/ld-elf/metasym-1.s
 create mode 100644 ld/testsuite/ld-elf/metasym-2.d
 create mode 100644 ld/testsuite/ld-elf/metasym-2.s
 create mode 100644 ld/testsuite/ld-elf/metasym-3.d
 create mode 100644 ld/testsuite/ld-elf/metasym-3.s
 create mode 100644 ld/testsuite/ld-elf/metasym-4.od
 create mode 100644 ld/testsuite/ld-elf/metasym-5-1.d
 create mode 100644 ld/testsuite/ld-elf/metasym-5-2.d
 create mode 100644 ld/testsuite/ld-elf/metasym-5-local-dup-1.s
 create mode 100644 ld/testsuite/ld-elf/metasym-5-local-dup-2.s
 create mode 100644 ld/testsuite/ld-msp430-elf/metasym-relax.d
 create mode 100644 ld/testsuite/ld-msp430-elf/metasym-relax.s

diff --git a/binutils/testsuite/binutils-all/metasym-1.d b/binutils/testsuite/binutils-all/metasym-1.d
new file mode 100644
index 0000000000..9c15435e7d
--- /dev/null
+++ b/binutils/testsuite/binutils-all/metasym-1.d
@@ -0,0 +1,17 @@
+#PROG: objcopy
+#name: Symbol meta-information: objcopy -R
+#source: metasym-1.s
+#objcopy: -R .data.var_static_retain
+#objdump: --symtab-meta
+
+.*:.*
+
+SYMBOL META-INFORMATION TABLE:
+Idx[ 	]+Kind[ 	]+Value[ 	]+Sym idx[ 	]+Name
+0:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+var_global_retain_no_sec
+1:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+var_static_retain_no_sec
+2:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+var_global_retain
+3:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+fn_static_retain_no_sec
+4:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+fn_global_retain_no_sec
+5:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+fn_static_retain
+6:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+fn_global_retain
diff --git a/binutils/testsuite/binutils-all/metasym-1.s b/binutils/testsuite/binutils-all/metasym-1.s
new file mode 100644
index 0000000000..0668d92319
--- /dev/null
+++ b/binutils/testsuite/binutils-all/metasym-1.s
@@ -0,0 +1,138 @@
+.text
+/* Global variable marked with "retain" attribute (global symbol).  */
+	.global	var_global_retain
+	.section	.data.var_global_retain,"aw"
+	.balign 2
+	.type	var_global_retain, "object"
+	.sym_meta_info	var_global_retain, SMK_RETAIN, 1
+	.size	var_global_retain, 2
+var_global_retain:
+	.short	1
+
+/* Global variable (global symbol).  */
+	.global	var_global_no_retain
+	.section	.data.var_global_no_retain,"aw"
+	.balign 2
+	.type	var_global_no_retain, "object"
+	.size	var_global_no_retain, 2
+var_global_no_retain:
+	.short	2
+
+/* Static global variable marked with "retain" attribute (local symbol).  */
+	.section	.data.var_static_retain,"aw"
+	.balign 2
+	.type	var_static_retain, "object"
+	.sym_meta_info	var_static_retain, SMK_RETAIN, 1
+	.size	var_static_retain, 2
+var_static_retain:
+	.short	1
+
+/* Static global variable (local symbol).  */
+	.section	.data.var_static_no_retain,"aw"
+	.balign 2
+	.type	var_static_no_retain, "object"
+	.size	var_static_no_retain, 2
+var_static_no_retain:
+	.short	3
+
+/* Function marked with "retain" attribute (global symbol).  */
+	.section	.text.fn_global_retain,"ax","progbits"
+	.balign 2
+	.global	fn_global_retain
+	.type	fn_global_retain, "function"
+	.sym_meta_info	fn_global_retain, SMK_RETAIN, 1
+fn_global_retain:
+	.size	fn_global_retain, 24
+
+/* Function (global symbol).  */
+	.section	.text.fn_no_retain,"ax","progbits"
+	.balign 2
+	.global	fn_no_retain
+	.type	fn_no_retain, "function"
+fn_no_retain:
+	.size	fn_no_retain, 24
+
+/* Static function marked with "retain" attribute (local symbol).  */
+	.section	.text.fn_static_retain,"ax","progbits"
+	.balign 2
+	.type	fn_static_retain, "function"
+	.sym_meta_info	fn_static_retain, SMK_RETAIN, 1
+fn_static_retain:
+	.size	fn_static_retain, 16
+
+/* Static function (local symbol).  */
+	.section	.text.fn_static_no_retain,"ax","progbits"
+	.balign 2
+	.type	fn_static_no_retain, "function"
+fn_static_no_retain:
+	.size	fn_static_no_retain, 16
+
+/* Now we do the same as above but without the symbols having their own
+   sections.  */
+
+.text
+/* Global variable marked with "retain" attribute (global symbol).  */
+	.global	var_global_retain_no_sec
+	.balign 2
+	.type	var_global_retain_no_sec, "object"
+	.sym_meta_info	var_global_retain_no_sec, SMK_RETAIN, 1
+	.size	var_global_retain_no_sec, 2
+var_global_retain_no_sec:
+	.short	1
+
+/* Global variable (global symbol).  */
+	.global	var_global_no_retain_no_sec
+	.balign 2
+	.type	var_global_no_retain_no_sec, "object"
+	.size	var_global_no_retain_no_sec, 2
+var_global_no_retain_no_sec:
+	.short	2
+
+/* Static global variable marked with "retain" attribute (local symbol).  */
+	.balign 2
+	.type	var_static_retain_no_sec, "object"
+	.sym_meta_info	var_static_retain_no_sec, SMK_RETAIN, 1
+	.size	var_static_retain_no_sec, 2
+var_static_retain_no_sec:
+	.short	1
+
+/* Static global variable (local symbol).  */
+	.balign 2
+	.type	var_static_no_retain_no_sec, "object"
+	.size	var_static_no_retain_no_sec, 2
+var_static_no_retain_no_sec:
+	.short	3
+
+/* Function marked with "retain" attribute (global symbol).  */
+	.balign 2
+	.global	fn_global_retain_no_sec
+	.type	fn_global_retain_no_sec, "function"
+	.sym_meta_info	fn_global_retain_no_sec, SMK_RETAIN, 1
+fn_global_retain_no_sec:
+	.size	fn_global_retain_no_sec, 24
+
+/* Function (global symbol).  */
+	.balign 2
+	.global	fn_no_retain_no_sec
+	.type	fn_no_retain_no_sec, "function"
+fn_no_retain_no_sec:
+	.size	fn_no_retain_no_sec, 24
+
+/* Static function marked with "retain" attribute (local symbol).  */
+	.balign 2
+	.type	fn_static_retain_no_sec, "function"
+	.sym_meta_info	fn_static_retain_no_sec, SMK_RETAIN, 1
+fn_static_retain_no_sec:
+	.size	fn_static_retain_no_sec, 16
+
+/* Static function (local symbol).  */
+	.balign 2
+	.type	fn_static_no_retain_no_sec, "function"
+fn_static_no_retain_no_sec:
+	.size	fn_static_no_retain_no_sec, 16
+
+	.balign 2
+	.global	main
+	.type	main, "function"
+main:
+	.size	main, 8
diff --git a/binutils/testsuite/binutils-all/metasym-2.d b/binutils/testsuite/binutils-all/metasym-2.d
new file mode 100644
index 0000000000..e8ed8acb81
--- /dev/null
+++ b/binutils/testsuite/binutils-all/metasym-2.d
@@ -0,0 +1,11 @@
+#PROG: objcopy
+#name: Symbol meta-information: objcopy -j
+#source: metasym-1.s
+#objcopy: -j .data.var_static_retain
+#objdump: --symtab-meta
+
+.*:.*
+
+SYMBOL META-INFORMATION TABLE:
+Idx[ 	]+Kind[ 	]+Value[ 	]+Sym idx[ 	]+Name
+0:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+var_static_retain
diff --git a/binutils/testsuite/binutils-all/metasym-3.d b/binutils/testsuite/binutils-all/metasym-3.d
new file mode 100644
index 0000000000..47c917f5ef
--- /dev/null
+++ b/binutils/testsuite/binutils-all/metasym-3.d
@@ -0,0 +1,9 @@
+#PROG: objcopy
+#name: Symbol meta-information: objcopy -S
+#source: metasym-1.s
+#objcopy: -S
+#objdump: --symtab-meta
+
+.*:.*
+
+NO SYMBOL META-INFORMATION TABLE
diff --git a/binutils/testsuite/binutils-all/metasym-4.d b/binutils/testsuite/binutils-all/metasym-4.d
new file mode 100644
index 0000000000..708083580d
--- /dev/null
+++ b/binutils/testsuite/binutils-all/metasym-4.d
@@ -0,0 +1,17 @@
+#PROG: objcopy
+#name: Symbol meta-information: objcopy -N
+#source: metasym-1.s
+#objcopy: -N fn_global_retain
+#objdump: --symtab-meta
+
+.*:.*
+
+SYMBOL META-INFORMATION TABLE:
+Idx[ 	]+Kind[ 	]+Value[ 	]+Sym idx[ 	]+Name
+0:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+var_global_retain_no_sec
+1:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+var_static_retain_no_sec
+2:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+var_global_retain
+3:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+var_static_retain
+4:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+fn_static_retain_no_sec
+5:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+fn_global_retain_no_sec
+6:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+fn_static_retain
diff --git a/binutils/testsuite/binutils-all/metasym-5.d b/binutils/testsuite/binutils-all/metasym-5.d
new file mode 100644
index 0000000000..4548128cfc
--- /dev/null
+++ b/binutils/testsuite/binutils-all/metasym-5.d
@@ -0,0 +1,18 @@
+#PROG: objcopy
+#name: Symbol meta-information: objcopy --extract-symbol
+#source: metasym-1.s
+#objcopy: --extract-symbol
+#objdump: --symtab-meta
+
+.*:.*
+
+SYMBOL META-INFORMATION TABLE:
+Idx[ 	]+Kind[ 	]+Value[ 	]+Sym idx[ 	]+Name
+0:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+var_global_retain_no_sec
+1:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+var_static_retain_no_sec
+2:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+var_global_retain
+3:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+var_static_retain
+4:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+fn_static_retain_no_sec
+5:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+fn_global_retain_no_sec
+6:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+fn_static_retain
+7:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+fn_global_retain
diff --git a/binutils/testsuite/binutils-all/metasym-6.d b/binutils/testsuite/binutils-all/metasym-6.d
new file mode 100644
index 0000000000..a234017863
--- /dev/null
+++ b/binutils/testsuite/binutils-all/metasym-6.d
@@ -0,0 +1,18 @@
+#PROG: objcopy
+#name: Symbol meta-information: objcopy --localize-symbol
+#source: metasym-1.s
+#objcopy: --localize-symbol fn_global_retain
+#objdump: --symtab-meta
+
+.*:.*
+
+SYMBOL META-INFORMATION TABLE:
+Idx[ 	]+Kind[ 	]+Value[ 	]+Sym idx[ 	]+Name
+0:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+var_global_retain_no_sec
+1:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+var_static_retain_no_sec
+2:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+var_global_retain
+3:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+var_static_retain
+4:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+fn_static_retain_no_sec
+5:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+fn_global_retain_no_sec
+6:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+fn_static_retain
+7:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+fn_global_retain
diff --git a/binutils/testsuite/binutils-all/metasym-7.d b/binutils/testsuite/binutils-all/metasym-7.d
new file mode 100644
index 0000000000..0ad8e56eb5
--- /dev/null
+++ b/binutils/testsuite/binutils-all/metasym-7.d
@@ -0,0 +1,18 @@
+#PROG: objcopy
+#name: Symbol meta-information: objcopy --globalize-symbol
+#source: metasym-1.s
+#objcopy: --globalize-symbol var_static_retain
+#objdump: --symtab-meta
+
+.*:.*
+
+SYMBOL META-INFORMATION TABLE:
+Idx[ 	]+Kind[ 	]+Value[ 	]+Sym idx[ 	]+Name
+0:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+var_global_retain_no_sec
+1:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+var_static_retain_no_sec
+2:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+var_global_retain
+3:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+var_static_retain
+4:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+fn_static_retain_no_sec
+5:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+fn_global_retain_no_sec
+6:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+fn_static_retain
+7:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+fn_global_retain
diff --git a/binutils/testsuite/binutils-all/metasym-8.d b/binutils/testsuite/binutils-all/metasym-8.d
new file mode 100644
index 0000000000..eb4435734f
--- /dev/null
+++ b/binutils/testsuite/binutils-all/metasym-8.d
@@ -0,0 +1,18 @@
+#PROG: objcopy
+#name: Symbol meta-information: objcopy --weaken-symbol
+#source: metasym-1.s
+#objcopy: --weaken-symbol var_global_retain
+#objdump: --symtab-meta
+
+.*:.*
+
+SYMBOL META-INFORMATION TABLE:
+Idx[ 	]+Kind[ 	]+Value[ 	]+Sym idx[ 	]+Name
+0:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+var_global_retain_no_sec
+1:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+var_static_retain_no_sec
+2:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+var_global_retain
+3:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+var_static_retain
+4:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+fn_static_retain_no_sec
+5:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+fn_global_retain_no_sec
+6:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+fn_static_retain
+7:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+fn_global_retain
diff --git a/binutils/testsuite/binutils-all/metasym-9.d b/binutils/testsuite/binutils-all/metasym-9.d
new file mode 100644
index 0000000000..425222a997
--- /dev/null
+++ b/binutils/testsuite/binutils-all/metasym-9.d
@@ -0,0 +1,18 @@
+#PROG: objcopy
+#name: Symbol meta-information: objcopy --redefine-sym
+#source: metasym-1.s
+#objcopy: --redefine-sym var_global_retain=new_var_global_retain
+#objdump: --symtab-meta
+
+.*:.*
+
+SYMBOL META-INFORMATION TABLE:
+Idx[ 	]+Kind[ 	]+Value[ 	]+Sym idx[ 	]+Name
+0:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+var_global_retain_no_sec
+1:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+var_static_retain_no_sec
+2:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+var_static_retain
+3:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+fn_static_retain_no_sec
+4:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+fn_global_retain_no_sec
+5:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+fn_static_retain
+6:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+fn_global_retain
+7:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+new_var_global_retain
diff --git a/binutils/testsuite/binutils-all/objcopy.exp b/binutils/testsuite/binutils-all/objcopy.exp
index 549b064e96..f9275ab0c2 100644
--- a/binutils/testsuite/binutils-all/objcopy.exp
+++ b/binutils/testsuite/binutils-all/objcopy.exp
@@ -1077,6 +1077,18 @@ proc objcopy_test_elf_common_symbols {} {
     }
 }
 
+proc objcopy_test_symtab_meta {} {
+    global srcdir
+    global subdir
+
+    set test_list [lsort [glob -nocomplain $srcdir/$subdir/metasym-*.d]]
+    foreach t $test_list {
+        # We need to strip the ".d", but can leave the dirname.
+        verbose [file rootname $t]
+        run_dump_test [file rootname $t]
+    }
+}
+
 # ia64 specific tests
 if { ([istarget "ia64-*-elf*"]
        || [istarget "ia64-*-linux*"]) } {
@@ -1115,6 +1127,7 @@ if [is_elf_format] {
 	run_dump_test "note-6-32"
     }
     run_dump_test "note-5"
+    objcopy_test_symtab_meta
 }
 
 run_dump_test "copy-2"
diff --git a/gas/testsuite/gas/elf/elf.exp b/gas/testsuite/gas/elf/elf.exp
index b0522af3bb..d9d50ea2da 100644
--- a/gas/testsuite/gas/elf/elf.exp
+++ b/gas/testsuite/gas/elf/elf.exp
@@ -301,6 +301,12 @@ if { [is_elf_format] } then {
 
     run_dump_test "bignums"
     
+    run_dump_test "metasym-0"
+    run_dump_test "metasym-1"
+    run_dump_test "metasym-bad-1"
+    run_dump_test "metasym-bad-2"
+    run_dump_test "metasym-bad-3"
+
     load_lib gas-dg.exp
     dg-init
     dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/err-*.s $srcdir/$subdir/warn-*.s]] "" ""
diff --git a/gas/testsuite/gas/elf/metasym-0.d b/gas/testsuite/gas/elf/metasym-0.d
new file mode 100644
index 0000000000..70d8419374
--- /dev/null
+++ b/gas/testsuite/gas/elf/metasym-0.d
@@ -0,0 +1,8 @@
+#objdump : --symtab-meta
+#name : no symbol meta-information
+# Verify that an empty symbol meta-information table is displayed when there
+# are no .sym_meta_info directives in the input file.
+
+.*: .*
+
+NO SYMBOL META-INFORMATION TABLE
diff --git a/gas/testsuite/gas/elf/metasym-0.s b/gas/testsuite/gas/elf/metasym-0.s
new file mode 100644
index 0000000000..7f3b4c56eb
--- /dev/null
+++ b/gas/testsuite/gas/elf/metasym-0.s
@@ -0,0 +1,35 @@
+.text
+	.global	var_global_no_retain
+	.section	.data.var_global_no_retain,"aw"
+	.balign 2
+	.type	var_global_no_retain,"object"
+	.size	var_global_no_retain, 2
+var_global_no_retain:
+	.short	2
+
+	.section	.data.var_static_no_retain,"aw"
+	.balign 2
+	.type	var_static_no_retain,"object"
+	.size	var_static_no_retain, 2
+var_static_no_retain:
+	.short	3
+
+	.section	.text.fn_no_retain,"ax",%progbits
+	.balign 2
+	.global	fn_no_retain
+	.type	fn_no_retain,"function"
+fn_no_retain:
+	.size	fn_no_retain, 24
+
+	.section	.text.fn_static_no_retain,"ax",%progbits
+	.balign 2
+	.type	fn_static_no_retain,"function"
+fn_static_no_retain:
+	.size	fn_static_no_retain, 16
+
+	.section	.text.main,"ax",%progbits
+	.balign 2
+	.global	main
+	.type	main,"function"
+main:
+	.size	main, 8
diff --git a/gas/testsuite/gas/elf/metasym-1.d b/gas/testsuite/gas/elf/metasym-1.d
new file mode 100644
index 0000000000..c77c67e46c
--- /dev/null
+++ b/gas/testsuite/gas/elf/metasym-1.d
@@ -0,0 +1,17 @@
+#objdump : --symtab-meta
+#name : symbol meta-information
+# Verify that symbol meta-information entries have been successfully recorded
+# for .sym_meta_info directives in the input file.
+
+.*: .*
+
+SYMBOL META-INFORMATION TABLE:
+Idx[ 	]+Kind[ 	]+Value[ 	]+Sym idx[ 	]+Name
+0:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+var_global_retain_no_sec
+1:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+var_static_retain_no_sec
+2:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+var_global_retain
+3:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+var_static_retain
+4:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+fn_static_retain_no_sec
+5:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+fn_global_retain_no_sec
+6:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+fn_static_retain
+7:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+fn_global_retain
diff --git a/gas/testsuite/gas/elf/metasym-1.s b/gas/testsuite/gas/elf/metasym-1.s
new file mode 100644
index 0000000000..0668d92319
--- /dev/null
+++ b/gas/testsuite/gas/elf/metasym-1.s
@@ -0,0 +1,138 @@
+.text
+/* Global variable marked with "retain" attribute (global symbol).  */
+	.global	var_global_retain
+	.section	.data.var_global_retain,"aw"
+	.balign 2
+	.type	var_global_retain, "object"
+	.sym_meta_info	var_global_retain, SMK_RETAIN, 1
+	.size	var_global_retain, 2
+var_global_retain:
+	.short	1
+
+/* Global variable (global symbol).  */
+	.global	var_global_no_retain
+	.section	.data.var_global_no_retain,"aw"
+	.balign 2
+	.type	var_global_no_retain, "object"
+	.size	var_global_no_retain, 2
+var_global_no_retain:
+	.short	2
+
+/* Static global variable marked with "retain" attribute (local symbol).  */
+	.section	.data.var_static_retain,"aw"
+	.balign 2
+	.type	var_static_retain, "object"
+	.sym_meta_info	var_static_retain, SMK_RETAIN, 1
+	.size	var_static_retain, 2
+var_static_retain:
+	.short	1
+
+/* Static global variable (local symbol).  */
+	.section	.data.var_static_no_retain,"aw"
+	.balign 2
+	.type	var_static_no_retain, "object"
+	.size	var_static_no_retain, 2
+var_static_no_retain:
+	.short	3
+
+/* Function marked with "retain" attribute (global symbol).  */
+	.section	.text.fn_global_retain,"ax","progbits"
+	.balign 2
+	.global	fn_global_retain
+	.type	fn_global_retain, "function"
+	.sym_meta_info	fn_global_retain, SMK_RETAIN, 1
+fn_global_retain:
+	.size	fn_global_retain, 24
+
+/* Function (global symbol).  */
+	.section	.text.fn_no_retain,"ax","progbits"
+	.balign 2
+	.global	fn_no_retain
+	.type	fn_no_retain, "function"
+fn_no_retain:
+	.size	fn_no_retain, 24
+
+/* Static function marked with "retain" attribute (local symbol).  */
+	.section	.text.fn_static_retain,"ax","progbits"
+	.balign 2
+	.type	fn_static_retain, "function"
+	.sym_meta_info	fn_static_retain, SMK_RETAIN, 1
+fn_static_retain:
+	.size	fn_static_retain, 16
+
+/* Static function (local symbol).  */
+	.section	.text.fn_static_no_retain,"ax","progbits"
+	.balign 2
+	.type	fn_static_no_retain, "function"
+fn_static_no_retain:
+	.size	fn_static_no_retain, 16
+
+/* Now we do the same as above but without the symbols having their own
+   sections.  */
+
+.text
+/* Global variable marked with "retain" attribute (global symbol).  */
+	.global	var_global_retain_no_sec
+	.balign 2
+	.type	var_global_retain_no_sec, "object"
+	.sym_meta_info	var_global_retain_no_sec, SMK_RETAIN, 1
+	.size	var_global_retain_no_sec, 2
+var_global_retain_no_sec:
+	.short	1
+
+/* Global variable (global symbol).  */
+	.global	var_global_no_retain_no_sec
+	.balign 2
+	.type	var_global_no_retain_no_sec, "object"
+	.size	var_global_no_retain_no_sec, 2
+var_global_no_retain_no_sec:
+	.short	2
+
+/* Static global variable marked with "retain" attribute (local symbol).  */
+	.balign 2
+	.type	var_static_retain_no_sec, "object"
+	.sym_meta_info	var_static_retain_no_sec, SMK_RETAIN, 1
+	.size	var_static_retain_no_sec, 2
+var_static_retain_no_sec:
+	.short	1
+
+/* Static global variable (local symbol).  */
+	.balign 2
+	.type	var_static_no_retain_no_sec, "object"
+	.size	var_static_no_retain_no_sec, 2
+var_static_no_retain_no_sec:
+	.short	3
+
+/* Function marked with "retain" attribute (global symbol).  */
+	.balign 2
+	.global	fn_global_retain_no_sec
+	.type	fn_global_retain_no_sec, "function"
+	.sym_meta_info	fn_global_retain_no_sec, SMK_RETAIN, 1
+fn_global_retain_no_sec:
+	.size	fn_global_retain_no_sec, 24
+
+/* Function (global symbol).  */
+	.balign 2
+	.global	fn_no_retain_no_sec
+	.type	fn_no_retain_no_sec, "function"
+fn_no_retain_no_sec:
+	.size	fn_no_retain_no_sec, 24
+
+/* Static function marked with "retain" attribute (local symbol).  */
+	.balign 2
+	.type	fn_static_retain_no_sec, "function"
+	.sym_meta_info	fn_static_retain_no_sec, SMK_RETAIN, 1
+fn_static_retain_no_sec:
+	.size	fn_static_retain_no_sec, 16
+
+/* Static function (local symbol).  */
+	.balign 2
+	.type	fn_static_no_retain_no_sec, "function"
+fn_static_no_retain_no_sec:
+	.size	fn_static_no_retain_no_sec, 16
+
+	.balign 2
+	.global	main
+	.type	main, "function"
+main:
+	.size	main, 8
diff --git a/gas/testsuite/gas/elf/metasym-bad-1.d b/gas/testsuite/gas/elf/metasym-bad-1.d
new file mode 100644
index 0000000000..150122ee43
--- /dev/null
+++ b/gas/testsuite/gas/elf/metasym-bad-1.d
@@ -0,0 +1,2 @@
+#name : symbol meta-information bad SMK
+#error_output : metasym-bad-1.l
diff --git a/gas/testsuite/gas/elf/metasym-bad-1.l b/gas/testsuite/gas/elf/metasym-bad-1.l
new file mode 100644
index 0000000000..206df0f5ee
--- /dev/null
+++ b/gas/testsuite/gas/elf/metasym-bad-1.l
@@ -0,0 +1,2 @@
+[^:]*: Assembler messages:
+[^:]*:6: Error: unrecognized symbol meta-information kind "SMK_FOO"
diff --git a/gas/testsuite/gas/elf/metasym-bad-1.s b/gas/testsuite/gas/elf/metasym-bad-1.s
new file mode 100644
index 0000000000..9d5e357c71
--- /dev/null
+++ b/gas/testsuite/gas/elf/metasym-bad-1.s
@@ -0,0 +1,9 @@
+.text
+	.global	var_global_retain
+	.section	.data.var_global_retain,"aw"
+	.balign 2
+	.type	var_global_retain, "object"
+	.sym_meta_info	var_global_retain, SMK_FOO, 1
+	.size	var_global_retain, 2
+var_global_retain:
+	.short	1
diff --git a/gas/testsuite/gas/elf/metasym-bad-2.d b/gas/testsuite/gas/elf/metasym-bad-2.d
new file mode 100644
index 0000000000..a10d64831b
--- /dev/null
+++ b/gas/testsuite/gas/elf/metasym-bad-2.d
@@ -0,0 +1,2 @@
+#name : symbol meta-information bad SMK_RETAIN value
+#error_output : metasym-bad-2.l
diff --git a/gas/testsuite/gas/elf/metasym-bad-2.l b/gas/testsuite/gas/elf/metasym-bad-2.l
new file mode 100644
index 0000000000..3060ac6220
--- /dev/null
+++ b/gas/testsuite/gas/elf/metasym-bad-2.l
@@ -0,0 +1,2 @@
+[^:]*: Assembler messages:
+[^:]*:6: Error: bad value "2" for SMK_RETAIN meta-information
diff --git a/gas/testsuite/gas/elf/metasym-bad-2.s b/gas/testsuite/gas/elf/metasym-bad-2.s
new file mode 100644
index 0000000000..bb65856896
--- /dev/null
+++ b/gas/testsuite/gas/elf/metasym-bad-2.s
@@ -0,0 +1,9 @@
+.text
+	.global	var_global_retain
+	.section	.data.var_global_retain,"aw"
+	.balign 2
+	.type	var_global_retain, "object"
+	.sym_meta_info	var_global_retain, SMK_RETAIN, 2
+	.size	var_global_retain, 2
+var_global_retain:
+	.short	1
diff --git a/gas/testsuite/gas/elf/metasym-bad-3.d b/gas/testsuite/gas/elf/metasym-bad-3.d
new file mode 100644
index 0000000000..e40518b68e
--- /dev/null
+++ b/gas/testsuite/gas/elf/metasym-bad-3.d
@@ -0,0 +1,2 @@
+#name : symbol meta-information bad SMK_RETAIN value
+#error_output : metasym-bad-3.l
diff --git a/gas/testsuite/gas/elf/metasym-bad-3.l b/gas/testsuite/gas/elf/metasym-bad-3.l
new file mode 100644
index 0000000000..e7704fc82f
--- /dev/null
+++ b/gas/testsuite/gas/elf/metasym-bad-3.l
@@ -0,0 +1,2 @@
+[^:]*: Assembler messages:
+[^:]*:6: Error: expected value in .sym_meta_info directive for symbol "var_global_retain"
diff --git a/gas/testsuite/gas/elf/metasym-bad-3.s b/gas/testsuite/gas/elf/metasym-bad-3.s
new file mode 100644
index 0000000000..55062e0c97
--- /dev/null
+++ b/gas/testsuite/gas/elf/metasym-bad-3.s
@@ -0,0 +1,9 @@
+.text
+	.global	var_global_retain
+	.section	.data.var_global_retain,"aw"
+	.balign 2
+	.type	var_global_retain, "object"
+	.sym_meta_info	var_global_retain, SMK_RETAIN,
+	.size	var_global_retain, 2
+var_global_retain:
+	.short	1
diff --git a/ld/testsuite/ld-elf/elf.exp b/ld/testsuite/ld-elf/elf.exp
index 7b8e8f6f3c..77ab4c3590 100644
--- a/ld/testsuite/ld-elf/elf.exp
+++ b/ld/testsuite/ld-elf/elf.exp
@@ -235,6 +235,16 @@ if [check_gc_sections_available] {
 	]
 }
 
+# Symbol meta tests
+run_ld_link_tests [list \
+    [list "Build libmeta.a" \
+	"" "" "" \
+	{metasym-2.s metasym-3.s} {} "libmeta.a" ] \
+    [list "Symbol meta-information: Link using archive" \
+	"" "-e main -gc-sections -u fn_no_retain_3 -Ltmpdir -lmeta" "" \
+	{metasym-1.s} {{objdump "--symtab-meta -t" metasym-4.od}} "metasym.out" ] \
+]
+
 set LDFLAGS $old_ldflags
 set ASFLAGS $old_asflags
 
diff --git a/ld/testsuite/ld-elf/metasym-0.d b/ld/testsuite/ld-elf/metasym-0.d
new file mode 100644
index 0000000000..c17a26920c
--- /dev/null
+++ b/ld/testsuite/ld-elf/metasym-0.d
@@ -0,0 +1,14 @@
+# As an initial sanity check, ensure that unused functions in the test source
+# we used for subsequent metasym really are gc'ed from the linked executable.
+#name : symbol meta-information (test removal of unused sections)
+#ld: -gc-sections -e main
+#source: metasym-1.s
+#objdump : -t
+#failif
+
+.*:.*
+
+SYMBOL TABLE:
+#...
+.*no_retain.*
+#...
diff --git a/ld/testsuite/ld-elf/metasym-1.d b/ld/testsuite/ld-elf/metasym-1.d
new file mode 100644
index 0000000000..33206ccb4d
--- /dev/null
+++ b/ld/testsuite/ld-elf/metasym-1.d
@@ -0,0 +1,27 @@
+#name : symbol meta-information
+#ld: -gc-sections -e main
+#source: metasym-1.s
+#objdump : -t --symtab-meta
+
+.*:.*
+
+SYMBOL TABLE:
+#...
+.*(var_global_retain|var_static_retain|fn_global_retain|fn_static_retain).*
+#...
+.*(var_global_retain|var_static_retain|fn_global_retain|fn_static_retain).*
+#...
+.*(var_global_retain|var_static_retain|fn_global_retain|fn_static_retain).*
+#...
+.*(var_global_retain|var_static_retain|fn_global_retain|fn_static_retain).*
+#...
+SYMBOL META-INFORMATION TABLE:
+Idx[ 	]+Kind[ 	]+Value[ 	]+Sym idx[ 	]+Name
+0:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+var_global_retain_no_sec
+1:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+var_static_retain_no_sec
+2:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+var_global_retain
+3:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+var_static_retain
+4:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+fn_static_retain_no_sec
+5:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+fn_global_retain_no_sec
+6:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+fn_static_retain
+7:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+fn_global_retain
diff --git a/ld/testsuite/ld-elf/metasym-1.s b/ld/testsuite/ld-elf/metasym-1.s
new file mode 100644
index 0000000000..4c1ee34fb8
--- /dev/null
+++ b/ld/testsuite/ld-elf/metasym-1.s
@@ -0,0 +1,110 @@
+.text
+/* Global variable marked with "retain" attribute (global symbol).  */
+	.global	var_global_retain
+	.section	.data.var_global_retain,"aw"
+	.balign 2
+	.type	var_global_retain, "object"
+	.sym_meta_info	var_global_retain, SMK_RETAIN, 1
+	.size	var_global_retain, 2
+var_global_retain:
+	.short	1
+
+/* Global variable (global symbol).  */
+	.global	var_global_no_retain
+	.section	.data.var_global_no_retain,"aw"
+	.balign 2
+	.type	var_global_no_retain, "object"
+	.size	var_global_no_retain, 2
+var_global_no_retain:
+	.short	2
+
+/* Static global variable marked with "retain" attribute (local symbol).  */
+	.section	.data.var_static_retain,"aw"
+	.balign 2
+	.type	var_static_retain, "object"
+	.sym_meta_info	var_static_retain, SMK_RETAIN, 1
+	.size	var_static_retain, 2
+var_static_retain:
+	.short	1
+
+/* Static global variable (local symbol).  */
+	.section	.data.var_static_no_retain,"aw"
+	.balign 2
+	.type	var_static_no_retain, "object"
+	.size	var_static_no_retain, 2
+var_static_no_retain:
+	.short	3
+
+/* Function marked with "retain" attribute (global symbol).  */
+	.section	.text.fn_global_retain,"ax","progbits"
+	.balign 2
+	.global	fn_global_retain
+	.type	fn_global_retain, "function"
+	.sym_meta_info	fn_global_retain, SMK_RETAIN, 1
+fn_global_retain:
+	.size	fn_global_retain, 24
+
+/* Function (global symbol).  */
+	.section	.text.fn_no_retain,"ax","progbits"
+	.balign 2
+	.global	fn_no_retain
+	.type	fn_no_retain, "function"
+fn_no_retain:
+	.size	fn_no_retain, 24
+
+/* Static function marked with "retain" attribute (local symbol).  */
+	.section	.text.fn_static_retain,"ax","progbits"
+	.balign 2
+	.type	fn_static_retain, "function"
+	.sym_meta_info	fn_static_retain, SMK_RETAIN, 1
+fn_static_retain:
+	.size	fn_static_retain, 16
+
+/* Static function (local symbol).  */
+	.section	.text.fn_static_no_retain,"ax","progbits"
+	.balign 2
+	.type	fn_static_no_retain, "function"
+fn_static_no_retain:
+	.size	fn_static_no_retain, 16
+
+/* Now we do the same as above but without the symbols having their own
+   sections.  */
+
+.text
+/* Global variable marked with "retain" attribute (global symbol).  */
+	.global	var_global_retain_no_sec
+	.balign 2
+	.type	var_global_retain_no_sec, "object"
+	.sym_meta_info	var_global_retain_no_sec, SMK_RETAIN, 1
+	.size	var_global_retain_no_sec, 2
+var_global_retain_no_sec:
+	.short	1
+
+/* Static global variable marked with "retain" attribute (local symbol).  */
+	.balign 2
+	.type	var_static_retain_no_sec, "object"
+	.sym_meta_info	var_static_retain_no_sec, SMK_RETAIN, 1
+	.size	var_static_retain_no_sec, 2
+var_static_retain_no_sec:
+	.short	1
+
+/* Function marked with "retain" attribute (global symbol).  */
+	.balign 2
+	.global	fn_global_retain_no_sec
+	.type	fn_global_retain_no_sec, "function"
+	.sym_meta_info	fn_global_retain_no_sec, SMK_RETAIN, 1
+fn_global_retain_no_sec:
+	.size	fn_global_retain_no_sec, 24
+
+/* Static function marked with "retain" attribute (local symbol).  */
+	.balign 2
+	.type	fn_static_retain_no_sec, "function"
+	.sym_meta_info	fn_static_retain_no_sec, SMK_RETAIN, 1
+fn_static_retain_no_sec:
+	.size	fn_static_retain_no_sec, 16
+
+	.balign 2
+	.global	main
+	.type	main, "function"
+main:
+	.size	main, 8
diff --git a/ld/testsuite/ld-elf/metasym-2.d b/ld/testsuite/ld-elf/metasym-2.d
new file mode 100644
index 0000000000..c986c5b040
--- /dev/null
+++ b/ld/testsuite/ld-elf/metasym-2.d
@@ -0,0 +1,40 @@
+#name : symbol meta-information with two object files
+#ld: -gc-sections -e main
+#source: metasym-1.s
+#source: metasym-2.s
+#objdump : -t --symtab-meta
+
+.*:.*
+
+SYMBOL TABLE:
+#...
+.*(var_global_retain|var_static_retain|fn_global_retain|fn_static_retain)(_2)*
+#...
+.*(var_global_retain|var_static_retain|fn_global_retain|fn_static_retain)(_2)*
+#...
+.*(var_global_retain|var_static_retain|fn_global_retain|fn_static_retain)(_2)*
+#...
+.*(var_global_retain|var_static_retain|fn_global_retain|fn_static_retain)(_2)*
+#...
+.*(var_global_retain|var_static_retain|fn_global_retain|fn_static_retain)(_2)*
+#...
+.*(var_global_retain|var_static_retain|fn_global_retain|fn_static_retain)(_2)*
+#...
+.*(var_global_retain|var_static_retain|fn_global_retain|fn_static_retain)(_2)*
+#...
+.*(var_global_retain|var_static_retain|fn_global_retain|fn_static_retain)(_2)*
+#...
+SYMBOL META-INFORMATION TABLE:
+Idx[ 	]+Kind[ 	]+Value[ 	]+Sym idx[ 	]+Name
+0:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+var_static_retain_2
+1:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+fn_static_retain_2
+2:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+var_global_retain_no_sec
+3:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+var_static_retain_no_sec
+4:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+var_global_retain
+5:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+var_static_retain
+6:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+fn_global_retain_2
+7:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+fn_static_retain_no_sec
+8:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+fn_global_retain_no_sec
+9:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+var_global_retain_2
+10:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+fn_static_retain
+11:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+fn_global_retain
diff --git a/ld/testsuite/ld-elf/metasym-2.s b/ld/testsuite/ld-elf/metasym-2.s
new file mode 100644
index 0000000000..4bd63fca41
--- /dev/null
+++ b/ld/testsuite/ld-elf/metasym-2.s
@@ -0,0 +1,68 @@
+.text
+/* Global variable marked with "retain" attribute (global symbol).  */
+	.global	var_global_retain_2
+	.section	.data.var_global_retain_2,"aw"
+	.balign 2
+	.type	var_global_retain_2, "object"
+	.sym_meta_info	var_global_retain_2, SMK_RETAIN, 1
+	.size	var_global_retain_2, 2
+var_global_retain_2:
+	.short	1
+
+/* Global variable (global symbol).  */
+	.global	var_global_no_retain_2
+	.section	.data.var_global_no_retain_2,"aw"
+	.balign 2
+	.type	var_global_no_retain_2, "object"
+	.size	var_global_no_retain_2, 2
+var_global_no_retain_2:
+	.short	2
+
+/* Static global variable marked with "retain" attribute (local symbol).  */
+	.section	.data.var_static_retain_2,"aw"
+	.balign 2
+	.type	var_static_retain_2, "object"
+	.sym_meta_info	var_static_retain_2, SMK_RETAIN, 1
+	.size	var_static_retain_2, 2
+var_static_retain_2:
+	.short	1
+
+/* Static global variable (local symbol).  */
+	.section	.data.var_static_no_retain_2,"aw"
+	.balign 2
+	.type	var_static_no_retain_2, "object"
+	.size	var_static_no_retain_2, 2
+var_static_no_retain_2:
+	.short	3
+
+/* Function marked with "retain" attribute (global symbol).  */
+	.section	.text.fn_global_retain_2,"ax","progbits"
+	.balign 2
+	.global	fn_global_retain_2
+	.type	fn_global_retain_2, "function"
+	.sym_meta_info	fn_global_retain_2, SMK_RETAIN, 1
+fn_global_retain_2:
+	.size	fn_global_retain_2, 24
+
+/* Function (global symbol).  */
+	.section	.text.fn_no_retain_2,"ax","progbits"
+	.balign 2
+	.global	fn_no_retain_2
+	.type	fn_no_retain_2, "function"
+fn_no_retain_2:
+	.size	fn_no_retain_2, 24
+
+/* Static function marked with "retain" attribute (local symbol).  */
+	.section	.text.fn_static_retain_2,"ax","progbits"
+	.balign 2
+	.type	fn_static_retain_2, "function"
+	.sym_meta_info	fn_static_retain_2, SMK_RETAIN, 1
+fn_static_retain_2:
+	.size	fn_static_retain_2, 16
+
+/* Static function (local symbol).  */
+	.section	.text.fn_static_no_retain_2,"ax","progbits"
+	.balign 2
+	.type	fn_static_no_retain_2, "function"
+fn_static_no_retain_2:
+	.size	fn_static_no_retain_2, 16
diff --git a/ld/testsuite/ld-elf/metasym-3.d b/ld/testsuite/ld-elf/metasym-3.d
new file mode 100644
index 0000000000..ee66527e8a
--- /dev/null
+++ b/ld/testsuite/ld-elf/metasym-3.d
@@ -0,0 +1,40 @@
+#name : symbol meta-information with two object files (relocatable link)
+#ld: -gc-sections -e main -r
+#source: metasym-1.s
+#source: metasym-2.s
+#objdump : -t --symtab-meta
+
+.*:.*
+
+SYMBOL TABLE:
+#...
+.*(var_global_retain|var_static_retain|fn_global_retain|fn_static_retain)(_2)*
+#...
+.*(var_global_retain|var_static_retain|fn_global_retain|fn_static_retain)(_2)*
+#...
+.*(var_global_retain|var_static_retain|fn_global_retain|fn_static_retain)(_2)*
+#...
+.*(var_global_retain|var_static_retain|fn_global_retain|fn_static_retain)(_2)*
+#...
+.*(var_global_retain|var_static_retain|fn_global_retain|fn_static_retain)(_2)*
+#...
+.*(var_global_retain|var_static_retain|fn_global_retain|fn_static_retain)(_2)*
+#...
+.*(var_global_retain|var_static_retain|fn_global_retain|fn_static_retain)(_2)*
+#...
+.*(var_global_retain|var_static_retain|fn_global_retain|fn_static_retain)(_2)*
+#...
+SYMBOL META-INFORMATION TABLE:
+Idx[ 	]+Kind[ 	]+Value[ 	]+Sym idx[ 	]+Name
+0:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+var_static_retain_2
+1:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+fn_static_retain_2
+2:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+var_global_retain_no_sec
+3:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+var_static_retain_no_sec
+4:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+var_global_retain
+5:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+var_static_retain
+6:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+fn_global_retain_2
+7:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+fn_static_retain_no_sec
+8:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+fn_global_retain_no_sec
+9:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+var_global_retain_2
+10:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+fn_static_retain
+11:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+fn_global_retain
diff --git a/ld/testsuite/ld-elf/metasym-3.s b/ld/testsuite/ld-elf/metasym-3.s
new file mode 100644
index 0000000000..60c586392c
--- /dev/null
+++ b/ld/testsuite/ld-elf/metasym-3.s
@@ -0,0 +1,68 @@
+.text
+/* Global variable marked with "retain" attribute (global symbol).  */
+	.global	var_global_retain_3
+	.section	.data.var_global_retain_3,"aw"
+	.balign 2
+	.type	var_global_retain_3, "object"
+	.sym_meta_info	var_global_retain_3, SMK_RETAIN, 1
+	.size	var_global_retain_3, 2
+var_global_retain_3:
+	.short	1
+
+/* Global variable (global symbol).  */
+	.global	var_global_no_retain_3
+	.section	.data.var_global_no_retain_3,"aw"
+	.balign 2
+	.type	var_global_no_retain_3, "object"
+	.size	var_global_no_retain_3, 2
+var_global_no_retain_3:
+	.short	2
+
+/* Static global variable marked with "retain" attribute (local symbol).  */
+	.section	.data.var_static_retain_3,"aw"
+	.balign 2
+	.type	var_static_retain_3, "object"
+	.sym_meta_info	var_static_retain_3, SMK_RETAIN, 1
+	.size	var_static_retain_3, 2
+var_static_retain_3:
+	.short	1
+
+/* Static global variable (local symbol).  */
+	.section	.data.var_static_no_retain_3,"aw"
+	.balign 2
+	.type	var_static_no_retain_3, "object"
+	.size	var_static_no_retain_3, 2
+var_static_no_retain_3:
+	.short	3
+
+/* Function marked with "retain" attribute (global symbol).  */
+	.section	.text.fn_global_retain_3,"ax","progbits"
+	.balign 2
+	.global	fn_global_retain_3
+	.type	fn_global_retain_3, "function"
+	.sym_meta_info	fn_global_retain_3, SMK_RETAIN, 1
+fn_global_retain_3:
+	.size	fn_global_retain_3, 24
+
+/* Function (global symbol).  */
+	.section	.text.fn_no_retain_3,"ax","progbits"
+	.balign 2
+	.global	fn_no_retain_3
+	.type	fn_no_retain_3, "function"
+fn_no_retain_3:
+	.size	fn_no_retain_3, 24
+
+/* Static function marked with "retain" attribute (local symbol).  */
+	.section	.text.fn_static_retain_3,"ax","progbits"
+	.balign 2
+	.type	fn_static_retain_3, "function"
+	.sym_meta_info	fn_static_retain_3, SMK_RETAIN, 1
+fn_static_retain_3:
+	.size	fn_static_retain_3, 16
+
+/* Static function (local symbol).  */
+	.section	.text.fn_static_no_retain_3,"ax","progbits"
+	.balign 2
+	.type	fn_static_no_retain_3, "function"
+fn_static_no_retain_3:
+	.size	fn_static_no_retain_3, 16
diff --git a/ld/testsuite/ld-elf/metasym-4.od b/ld/testsuite/ld-elf/metasym-4.od
new file mode 100644
index 0000000000..281cf1a55b
--- /dev/null
+++ b/ld/testsuite/ld-elf/metasym-4.od
@@ -0,0 +1,38 @@
+# Pass -u fn_no_retain_3 on the command line to pull in the symbols from
+# metasym-3.o, which has been built into libmeta.a.
+# Test that all the "retain" symbols from this file are retained, but none of
+# the "retain" symbols from metasym-2.o.
+.*:.*
+
+SYMBOL TABLE:
+#...
+.*(var_global_retain|var_static_retain|fn_global_retain|fn_static_retain)(_3)*
+#...
+.*(var_global_retain|var_static_retain|fn_global_retain|fn_static_retain)(_3)*
+#...
+.*(var_global_retain|var_static_retain|fn_global_retain|fn_static_retain)(_3)*
+#...
+.*(var_global_retain|var_static_retain|fn_global_retain|fn_static_retain)(_3)*
+#...
+.*(var_global_retain|var_static_retain|fn_global_retain|fn_static_retain)(_3)*
+#...
+.*(var_global_retain|var_static_retain|fn_global_retain|fn_static_retain)(_3)*
+#...
+.*(var_global_retain|var_static_retain|fn_global_retain|fn_static_retain)(_3)*
+#...
+.*(var_global_retain|var_static_retain|fn_global_retain|fn_static_retain)(_3)*
+#...
+SYMBOL META-INFORMATION TABLE:
+Idx[ 	]+Kind[ 	]+Value[ 	]+Sym idx[ 	]+Name
+0:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+var_static_retain_3
+1:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+var_global_retain_no_sec
+2:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+var_static_retain_no_sec
+3:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+var_global_retain
+4:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+var_static_retain
+5:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+fn_static_retain_no_sec
+6:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+fn_static_retain_3
+7:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+fn_global_retain_no_sec
+8:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+fn_static_retain
+9:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+fn_global_retain
+10:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+fn_global_retain_3
+11:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+var_global_retain_3
diff --git a/ld/testsuite/ld-elf/metasym-5-1.d b/ld/testsuite/ld-elf/metasym-5-1.d
new file mode 100644
index 0000000000..e54fe42a85
--- /dev/null
+++ b/ld/testsuite/ld-elf/metasym-5-1.d
@@ -0,0 +1,30 @@
+# Test that symbol meta-information entries always refer to the correct symbol,
+# even when there are two symbols with the same name.
+# In the sources for this test, there are two declaration of the local symbol
+# "value_is_1_if_has_retain_attr", but only one has the "retain" attribute applied.
+# After linking, we remove the .data section which contains the instance of the
+# variable that is not marked as "retain", and then check that meta-information
+# is still valid. This confirms that the meta-information entry for
+# "value_is_1_if_has_retain_attr" always pointed to the correct symbol.
+# We perform a relocatable link so we can remove the .data section without
+# complaints from objcopy.
+#name: Symbol meta-information: Local symbols with the same name (part 1)
+#ld: -r -gc-sections -e main
+#source: metasym-1.s
+#source: metasym-5-local-dup-1.s
+#source: metasym-5-local-dup-2.s
+#objcopy_linked_file: -R .data
+#objdump : --symtab-meta
+.*:.*
+
+SYMBOL META-INFORMATION TABLE:
+Idx[ 	]+Kind[ 	]+Value[ 	]+Sym idx[ 	]+Name
+0:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+var_global_retain_no_sec
+1:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+var_static_retain_no_sec
+2:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+var_global_retain
+3:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+var_static_retain
+4:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+fn_static_retain_no_sec
+5:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+value_is_1_if_has_retain_attr
+6:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+fn_global_retain_no_sec
+7:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+fn_static_retain
+8:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+fn_global_retain
diff --git a/ld/testsuite/ld-elf/metasym-5-2.d b/ld/testsuite/ld-elf/metasym-5-2.d
new file mode 100644
index 0000000000..674b5b925c
--- /dev/null
+++ b/ld/testsuite/ld-elf/metasym-5-2.d
@@ -0,0 +1,23 @@
+# See metasym-5-1.d for information about this test.
+# In this part, we swap the order of the sources to ensure the meta-information
+# has deliberately tracked the correct variable (that it's not a coincidence).
+#name: Symbol meta-information: Local symbols with the same name (part 2)
+#ld: -r -gc-sections -e main
+#source: metasym-1.s
+#source: metasym-5-local-dup-2.s
+#source: metasym-5-local-dup-1.s
+#objcopy_linked_file: -R .data
+#objdump : --symtab-meta
+.*:.*
+
+SYMBOL META-INFORMATION TABLE:
+Idx[ 	]+Kind[ 	]+Value[ 	]+Sym idx[ 	]+Name
+0:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+var_global_retain_no_sec
+1:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+var_static_retain_no_sec
+2:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+var_global_retain
+3:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+var_static_retain
+4:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+fn_static_retain_no_sec
+5:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+value_is_1_if_has_retain_attr
+6:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+fn_global_retain_no_sec
+7:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+fn_static_retain
+8:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+fn_global_retain
diff --git a/ld/testsuite/ld-elf/metasym-5-local-dup-1.s b/ld/testsuite/ld-elf/metasym-5-local-dup-1.s
new file mode 100644
index 0000000000..137773f02e
--- /dev/null
+++ b/ld/testsuite/ld-elf/metasym-5-local-dup-1.s
@@ -0,0 +1,16 @@
+/* To ensure this version of value_is_1_if_has_retain_attr is kept in the output
+   file, put it in a section with another data object we're definitley
+   keeping.  */
+.section	.data,"aw"
+	.balign 2
+	.type	keeper, "object"
+	.sym_meta_info	keeper, SMK_RETAIN, 1
+	.size	keeper, 2
+keeper:
+	.short	22
+
+	.balign 2
+	.type	value_is_1_if_has_retain_attr, "object"
+	.size	value_is_1_if_has_retain_attr, 2
+value_is_1_if_has_retain_attr:
+	.short	2
diff --git a/ld/testsuite/ld-elf/metasym-5-local-dup-2.s b/ld/testsuite/ld-elf/metasym-5-local-dup-2.s
new file mode 100644
index 0000000000..f4b4d2c474
--- /dev/null
+++ b/ld/testsuite/ld-elf/metasym-5-local-dup-2.s
@@ -0,0 +1,7 @@
+.section	.data.value_is_1_if_has_retain_attr,"aw"
+	.balign 2
+	.type	value_is_1_if_has_retain_attr, "object"
+	.sym_meta_info	value_is_1_if_has_retain_attr, SMK_RETAIN, 1
+	.size	value_is_1_if_has_retain_attr, 2
+value_is_1_if_has_retain_attr:
+	.short	1
diff --git a/ld/testsuite/ld-i386/load1-nacl.d b/ld/testsuite/ld-i386/load1-nacl.d
index 317d1c4054..a56981ebfe 100644
--- a/ld/testsuite/ld-i386/load1-nacl.d
+++ b/ld/testsuite/ld-i386/load1-nacl.d
@@ -1,7 +1,7 @@
 #source: load1.s
 #as: --32
 #ld: -melf_i386
-#objdump: -dw --sym
+#objdump: -dw --syms
 #target: i?86-*-nacl* x86_64-*-nacl*
 
 .*: +file format .*
diff --git a/ld/testsuite/ld-i386/load1.d b/ld/testsuite/ld-i386/load1.d
index 8e8db9f43b..f4a6f63a64 100644
--- a/ld/testsuite/ld-i386/load1.d
+++ b/ld/testsuite/ld-i386/load1.d
@@ -1,6 +1,6 @@
 #as: --32 -mrelax-relocations=yes
 #ld: -melf_i386 -z noseparate-code
-#objdump: -dw --sym
+#objdump: -dw --syms
 #notarget: i?86-*-nacl* x86_64-*-nacl*
 
 .*: +file format .*
diff --git a/ld/testsuite/ld-i386/pie1-nacl.d b/ld/testsuite/ld-i386/pie1-nacl.d
index aafd6a7ef5..a161b4e0ae 100644
--- a/ld/testsuite/ld-i386/pie1-nacl.d
+++ b/ld/testsuite/ld-i386/pie1-nacl.d
@@ -1,7 +1,7 @@
 #source: pie1.s
 #as: --32
 #ld: -pie -melf_i386 -z relro
-#objdump: -dw --sym
+#objdump: -dw --syms
 #target: i?86-*-nacl* x86_64-*-nacl*
 
 .*: +file format .*
diff --git a/ld/testsuite/ld-i386/pie1.d b/ld/testsuite/ld-i386/pie1.d
index e7beaa2400..42f8fb5efd 100644
--- a/ld/testsuite/ld-i386/pie1.d
+++ b/ld/testsuite/ld-i386/pie1.d
@@ -1,6 +1,6 @@
 #as: --32
 #ld: -pie -melf_i386 -z relro -z noseparate-code
-#objdump: -dw --sym
+#objdump: -dw --syms
 #notarget: i?86-*-nacl* x86_64-*-nacl*
 
 .*: +file format .*
diff --git a/ld/testsuite/ld-i386/pr20244-1a.d b/ld/testsuite/ld-i386/pr20244-1a.d
index 98aaa9e01b..4644607207 100644
--- a/ld/testsuite/ld-i386/pr20244-1a.d
+++ b/ld/testsuite/ld-i386/pr20244-1a.d
@@ -1,7 +1,7 @@
 #source: pr20244-1.s
 #as: --32
 #ld: -m elf_i386 -z noseparate-code
-#objdump: --sym -dw
+#objdump: --syms -dw
 #notarget: i?86-*-nacl* x86_64-*-nacl*
 
 .*: +file format .*
diff --git a/ld/testsuite/ld-i386/pr20244-2a.d b/ld/testsuite/ld-i386/pr20244-2a.d
index 40c6e32c94..d51cf68eca 100644
--- a/ld/testsuite/ld-i386/pr20244-2a.d
+++ b/ld/testsuite/ld-i386/pr20244-2a.d
@@ -1,7 +1,7 @@
 #source: pr20244-2.s
 #as: --32
 #ld: -m elf_i386 -z noseparate-code
-#objdump: --sym -dw
+#objdump: --syms -dw
 #notarget: i?86-*-nacl* x86_64-*-nacl*
 
 .*: +file format .*
diff --git a/ld/testsuite/ld-msp430-elf/metasym-relax.d b/ld/testsuite/ld-msp430-elf/metasym-relax.d
new file mode 100644
index 0000000000..a6cb7b3f36
--- /dev/null
+++ b/ld/testsuite/ld-msp430-elf/metasym-relax.d
@@ -0,0 +1,28 @@
+# Ensure that symbol-meta-information is emitted correctly even if the values
+# of symbols change during the link process, due to branch relaxation.
+# The source for this test has BR instructions which will get relaxed to JMP,
+# which will reduce symbol values. It's important that the functions are not
+# in their own sections.
+#name : symbol meta-information with relaxed branches
+#ld: -e main
+#source: metasym-relax.s
+#objdump : -t --symtab-meta
+
+.*:.*
+
+SYMBOL TABLE:
+#...
+.*(var_global_retain|var_static_retain|fn_global_retain|fn_static_retain).*
+#...
+.*(var_global_retain|var_static_retain|fn_global_retain|fn_static_retain).*
+#...
+.*(var_global_retain|var_static_retain|fn_global_retain|fn_static_retain).*
+#...
+.*(var_global_retain|var_static_retain|fn_global_retain|fn_static_retain).*
+#...
+SYMBOL META-INFORMATION TABLE:
+Idx[ 	]+Kind[ 	]+Value[ 	]+Sym idx[ 	]+Name
+0:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+var_global_retain
+1:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+var_static_retain
+2:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+fn_static_retain
+3:[ 	]+SMK_RETAIN[ 	]+0x1[ 	]+[0-9]+[ 	]+fn_global_retain
diff --git a/ld/testsuite/ld-msp430-elf/metasym-relax.s b/ld/testsuite/ld-msp430-elf/metasym-relax.s
new file mode 100644
index 0000000000..5fcfa7f016
--- /dev/null
+++ b/ld/testsuite/ld-msp430-elf/metasym-relax.s
@@ -0,0 +1,38 @@
+.text
+	.global	var_global_retain
+.data
+	.balign 2
+	.type	var_global_retain, @object
+	.sym_meta_info	var_global_retain, SMK_RETAIN, 1
+	.size	var_global_retain, 2
+var_global_retain:
+	.short	1
+	.balign 2
+	.type	var_static_retain, @object
+	.sym_meta_info	var_static_retain, SMK_RETAIN, 1
+	.size	var_static_retain, 2
+var_static_retain:
+	.short	1
+.text
+	.balign 2
+main:
+.L6:
+	BR	#.L6
+	.size	main, .-main
+	.global	fn_global_retain
+	.type	fn_global_retain, @function
+	.sym_meta_info	fn_global_retain, SMK_RETAIN, 1
+fn_global_retain:
+.L2:
+	BR	#.L2
+	.size	fn_global_retain, .-fn_global_retain
+	.balign 2
+	.type	fn_static_retain, @function
+	.sym_meta_info	fn_static_retain, SMK_RETAIN, 1
+fn_static_retain:
+.L4:
+	BR	#.L4
+	.size	fn_static_retain, .-fn_static_retain
+	.balign 2
+	.global	main
+	.type	main, @function
diff --git a/ld/testsuite/ld-msp430-elf/msp430-elf.exp b/ld/testsuite/ld-msp430-elf/msp430-elf.exp
index e9a0fdc629..82813e799b 100644
--- a/ld/testsuite/ld-msp430-elf/msp430-elf.exp
+++ b/ld/testsuite/ld-msp430-elf/msp430-elf.exp
@@ -163,6 +163,8 @@ set msp430warntests {
         {{ld warn-no-lower-data.r}} "warn-no-lower-data"}
 }
 
+run_dump_test metasym-relax
+
 # Don't run further tests when msp430 ISA is selected
 if {[string match "*-mcpu=msp430 *" [board_info [target_info name] multilib_flags]]
   || [string match "*-mcpu=msp430" [board_info [target_info name] multilib_flags]]} {
diff --git a/ld/testsuite/ld-x86-64/load1a-nacl.d b/ld/testsuite/ld-x86-64/load1a-nacl.d
index f038400a5e..b6c92c0f00 100644
--- a/ld/testsuite/ld-x86-64/load1a-nacl.d
+++ b/ld/testsuite/ld-x86-64/load1a-nacl.d
@@ -1,7 +1,7 @@
 #source: load1.s
 #as: --64
 #ld: -melf_x86_64
-#objdump: -dw --sym
+#objdump: -dw --syms
 #target: x86_64-*-nacl*
 
 .*: +file format .*
diff --git a/ld/testsuite/ld-x86-64/load1a.d b/ld/testsuite/ld-x86-64/load1a.d
index 09dacb4f24..0991b775e9 100644
--- a/ld/testsuite/ld-x86-64/load1a.d
+++ b/ld/testsuite/ld-x86-64/load1a.d
@@ -1,7 +1,7 @@
 #source: load1.s
 #as: --64 -mrelax-relocations=yes
 #ld: -melf_x86_64 -z max-page-size=0x200000 -z noseparate-code
-#objdump: -dw --sym
+#objdump: -dw --syms
 #notarget: x86_64-*-nacl*
 
 .*: +file format .*
diff --git a/ld/testsuite/ld-x86-64/load1b-nacl.d b/ld/testsuite/ld-x86-64/load1b-nacl.d
index f4ab3bea07..077d9f2aaa 100644
--- a/ld/testsuite/ld-x86-64/load1b-nacl.d
+++ b/ld/testsuite/ld-x86-64/load1b-nacl.d
@@ -1,7 +1,7 @@
 #source: load1.s
 #as: --x32
 #ld: -melf32_x86_64
-#objdump: -dw --sym
+#objdump: -dw --syms
 #target: x86_64-*-nacl*
 
 .*: +file format .*
diff --git a/ld/testsuite/ld-x86-64/load1b.d b/ld/testsuite/ld-x86-64/load1b.d
index 74cc05962b..e16441061b 100644
--- a/ld/testsuite/ld-x86-64/load1b.d
+++ b/ld/testsuite/ld-x86-64/load1b.d
@@ -1,7 +1,7 @@
 #source: load1.s
 #as: --x32 -mrelax-relocations=yes
 #ld: -melf32_x86_64 -z max-page-size=0x200000 -z noseparate-code
-#objdump: -dw --sym
+#objdump: -dw --syms
 #notarget: x86_64-*-nacl*
 
 .*: +file format .*
-- 
2.17.1


[-- Attachment #4: gcc-support-retain-attribute-meta-info.patch --]
[-- Type: text/x-patch, Size: 4261 bytes --]

From d2c9df194c7e7bcb37bd54cd39cef7b7b8a74ec5 Mon Sep 17 00:00:00 2001
From: Jozef Lawrynowicz <jozef.l@mittosystems.com>
Date: Tue, 18 Feb 2020 10:21:04 +0000
Subject: [PATCH] META: Add retain attribute and emit .sym_meta_info

---
 gcc/c-family/c-attribs.c   | 14 ++++++++++++++
 gcc/config/elfos.h         |  9 +++++++++
 gcc/config/msp430/msp430.c |  3 +++
 3 files changed, 26 insertions(+)

diff --git a/gcc/c-family/c-attribs.c b/gcc/c-family/c-attribs.c
index 2ea5fd5ff46..a758e8ee2a0 100644
--- a/gcc/c-family/c-attribs.c
+++ b/gcc/c-family/c-attribs.c
@@ -94,6 +94,7 @@ static tree handle_aligned_attribute (tree *, tree, tree, int, bool *);
 static tree handle_warn_if_not_aligned_attribute (tree *, tree, tree,
 						  int, bool *);
 static tree handle_noinit_attribute (tree *, tree, tree, int, bool *);
+static tree handle_retain_attribute (tree *, tree, tree, int, bool *);
 static tree handle_weak_attribute (tree *, tree, tree, int, bool *) ;
 static tree handle_noplt_attribute (tree *, tree, tree, int, bool *) ;
 static tree handle_alias_ifunc_attribute (bool, tree *, tree, tree, bool *);
@@ -484,6 +485,8 @@ const struct attribute_spec c_common_attribute_table[] =
 			      handle_noinit_attribute, attr_noinit_exclusions },
   { "access",		      1, 3, false, true, true, false,
 			      handle_access_attribute, NULL },
+  { "retain",		      0, 0, true, false, false, false,
+			      handle_retain_attribute, NULL },
   { NULL,                     0, 0, false, false, false, false, NULL, NULL }
 };
 
@@ -2258,6 +2261,17 @@ handle_noinit_attribute (tree * node,
   return res;
 }
 
+static tree
+handle_retain_attribute (tree * node,
+			 tree   name,
+			 tree   args,
+			 int    flags,
+			 bool *no_add_attrs)
+{
+  /* The "retain" attribute implies "used".  */
+  return handle_used_attribute (node, name, args, flags, no_add_attrs);
+}
+
 
 /* Handle a "noplt" attribute; arguments as in
    struct attribute_spec.handler.  */
diff --git a/gcc/config/elfos.h b/gcc/config/elfos.h
index 74a3eafda6b..59f89203de2 100644
--- a/gcc/config/elfos.h
+++ b/gcc/config/elfos.h
@@ -289,6 +289,9 @@ see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
   do								\
     {								\
       ASM_OUTPUT_TYPE_DIRECTIVE (FILE, NAME, "function");	\
+      /* Change to ASM_OUTPUT_SYM_META_INFO */ \
+      if (lookup_attribute ("retain", DECL_ATTRIBUTES (DECL)) != NULL_TREE) \
+	  fprintf (FILE, "\t.sym_meta_info\t%s, SMK_RETAIN, 1\n", NAME); \
       ASM_DECLARE_RESULT (FILE, DECL_RESULT (DECL));		\
       ASM_OUTPUT_FUNCTION_LABEL (FILE, NAME, DECL);		\
     }								\
@@ -305,6 +308,9 @@ see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
   do								\
     {								\
       ASM_OUTPUT_TYPE_DIRECTIVE (FILE, NAME, "function");	\
+      /* Change to ASM_OUTPUT_SYM_META_INFO */ \
+      if (lookup_attribute ("retain", DECL_ATTRIBUTES (DECL)) != NULL_TREE) \
+	  fprintf (FILE, "\t.sym_meta_info\t%s, SMK_RETAIN, 1\n", NAME); \
       ASM_DECLARE_RESULT (FILE, DECL_RESULT (DECL));		\
       ASM_OUTPUT_FUNCTION_LABEL (FILE, NAME, DECL);		\
     }								\
@@ -334,6 +340,9 @@ see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
 	ASM_OUTPUT_TYPE_DIRECTIVE (FILE, NAME, "gnu_unique_object");	\
       else								\
 	ASM_OUTPUT_TYPE_DIRECTIVE (FILE, NAME, "object");		\
+      /* Change to ASM_OUTPUT_SYM_META_INFO */ \
+      if (lookup_attribute ("retain", DECL_ATTRIBUTES (DECL)) != NULL_TREE) \
+	  fprintf (FILE, "\t.sym_meta_info\t%s, SMK_RETAIN, 1\n", NAME); \
 									\
       size_directive_output = 0;					\
       if (!flag_inhibit_size_directive					\
diff --git a/gcc/config/msp430/msp430.c b/gcc/config/msp430/msp430.c
index 25d191694ef..93875a27e35 100644
--- a/gcc/config/msp430/msp430.c
+++ b/gcc/config/msp430/msp430.c
@@ -1767,6 +1767,9 @@ msp430_start_function (FILE *file, const char *name, tree decl)
 
   switch_to_section (function_section (decl));
   ASM_OUTPUT_TYPE_DIRECTIVE (file, name, "function");
+  /* Change to ASM_OUTPUT_SYM_META_INFO */
+  if (lookup_attribute ("retain", DECL_ATTRIBUTES (decl)) != NULL_TREE)
+    fprintf (file, "\t.sym_meta_info\t%s, SMK_RETAIN, 1\n", name);
   ASM_OUTPUT_FUNCTION_LABEL (file, name, decl);
 }
 
-- 
2.17.1


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [RFC] Symbol meta-information ELF extension
  2020-02-18 10:25 [RFC] Symbol meta-information ELF extension Jozef Lawrynowicz
@ 2020-02-18 12:58 ` H.J. Lu
  2020-02-18 13:47   ` Jozef Lawrynowicz
  0 siblings, 1 reply; 8+ messages in thread
From: H.J. Lu @ 2020-02-18 12:58 UTC (permalink / raw)
  To: Jozef Lawrynowicz; +Cc: binutils

On Tue, Feb 18, 2020 at 2:26 AM Jozef Lawrynowicz
<jozef.l@mittosystems.com> wrote:
>
> Hi,
>
> I've been working with Texas Instruments to develop an ELF extension which
> allows additional information about symbols ("symbol meta-information") to
> be stored in ELF files, in a section called .symtab_meta.
>
> The aim of symbol meta-information is to provide a extensible format for
> propagating additional information about symbols from the source code through to
> the link stage.
>
> I hope I can get some feedback on this proposal, and its current implementation,
> from the upstream community so I can make any adjustments required for the
> eventual upstreaming of this feature.

Have you looked at Solaris syminfo section

https://docs.oracle.com/cd/E26502_01/html/E26507/chapter7-17.html

Can you extend it to do what you want?

-- 
H.J.

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [RFC] Symbol meta-information ELF extension
  2020-02-18 12:58 ` H.J. Lu
@ 2020-02-18 13:47   ` Jozef Lawrynowicz
  2020-02-18 13:59     ` H.J. Lu
  0 siblings, 1 reply; 8+ messages in thread
From: Jozef Lawrynowicz @ 2020-02-18 13:47 UTC (permalink / raw)
  To: H.J. Lu; +Cc: binutils

On Tue, 18 Feb 2020 04:58:02 -0800
"H.J. Lu" <hjl.tools@gmail.com> wrote:

> On Tue, Feb 18, 2020 at 2:26 AM Jozef Lawrynowicz
> <jozef.l@mittosystems.com> wrote:
> >
> > Hi,
> >
> > I've been working with Texas Instruments to develop an ELF extension which
> > allows additional information about symbols ("symbol meta-information") to
> > be stored in ELF files, in a section called .symtab_meta.
> >
> > The aim of symbol meta-information is to provide a extensible format for
> > propagating additional information about symbols from the source code through to
> > the link stage.
> >
> > I hope I can get some feedback on this proposal, and its current implementation,
> > from the upstream community so I can make any adjustments required for the
> > eventual upstreaming of this feature.  
> 
> Have you looked at Solaris syminfo section
> 
> https://docs.oracle.com/cd/E26502_01/html/E26507/chapter7-17.html
> 
> Can you extend it to do what you want?
> 

It looks like there is only some minimal support for the Syminfo section in
Binutils. The support is only there so that readelf can dump information about
the Syminfo section from an object file.

There's no implementation to enable the creation of a Syminfo section with
user-specified flags for certain symbols, nor are there any hooks into any
other Binutils programs, so I can't see any advantage to try and leverage that
existing functionality. 

Thanks,
Jozef

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [RFC] Symbol meta-information ELF extension
  2020-02-18 13:47   ` Jozef Lawrynowicz
@ 2020-02-18 13:59     ` H.J. Lu
  2020-02-18 14:39       ` Jozef Lawrynowicz
  0 siblings, 1 reply; 8+ messages in thread
From: H.J. Lu @ 2020-02-18 13:59 UTC (permalink / raw)
  To: Jozef Lawrynowicz; +Cc: binutils

On Tue, Feb 18, 2020 at 5:47 AM Jozef Lawrynowicz
<jozef.l@mittosystems.com> wrote:
>
> On Tue, 18 Feb 2020 04:58:02 -0800
> "H.J. Lu" <hjl.tools@gmail.com> wrote:
>
> > On Tue, Feb 18, 2020 at 2:26 AM Jozef Lawrynowicz
> > <jozef.l@mittosystems.com> wrote:
> > >
> > > Hi,
> > >
> > > I've been working with Texas Instruments to develop an ELF extension which
> > > allows additional information about symbols ("symbol meta-information") to
> > > be stored in ELF files, in a section called .symtab_meta.
> > >
> > > The aim of symbol meta-information is to provide a extensible format for
> > > propagating additional information about symbols from the source code through to
> > > the link stage.
> > >
> > > I hope I can get some feedback on this proposal, and its current implementation,
> > > from the upstream community so I can make any adjustments required for the
> > > eventual upstreaming of this feature.
> >
> > Have you looked at Solaris syminfo section
> >
> > https://docs.oracle.com/cd/E26502_01/html/E26507/chapter7-17.html
> >
> > Can you extend it to do what you want?
> >
>
> It looks like there is only some minimal support for the Syminfo section in
> Binutils. The support is only there so that readelf can dump information about
> the Syminfo section from an object file.
>
> There's no implementation to enable the creation of a Syminfo section with
> user-specified flags for certain symbols, nor are there any hooks into any
> other Binutils programs, so I can't see any advantage to try and leverage that
> existing functionality.

syminfo is a very useful ELF extension.  My question is if binutils supports
syminfo, can it be extended to meet your need?


-- 
H.J.

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [RFC] Symbol meta-information ELF extension
  2020-02-18 13:59     ` H.J. Lu
@ 2020-02-18 14:39       ` Jozef Lawrynowicz
  2020-02-18 14:51         ` H.J. Lu
  0 siblings, 1 reply; 8+ messages in thread
From: Jozef Lawrynowicz @ 2020-02-18 14:39 UTC (permalink / raw)
  To: H.J. Lu; +Cc: binutils

On Tue, 18 Feb 2020 05:58:34 -0800
"H.J. Lu" <hjl.tools@gmail.com> wrote:

> On Tue, Feb 18, 2020 at 5:47 AM Jozef Lawrynowicz
> <jozef.l@mittosystems.com> wrote:
> >
> > On Tue, 18 Feb 2020 04:58:02 -0800
> > "H.J. Lu" <hjl.tools@gmail.com> wrote:
> >  
> > > On Tue, Feb 18, 2020 at 2:26 AM Jozef Lawrynowicz
> > > <jozef.l@mittosystems.com> wrote:  
> > > >
> > > > Hi,
> > > >
> > > > I've been working with Texas Instruments to develop an ELF extension which
> > > > allows additional information about symbols ("symbol meta-information") to
> > > > be stored in ELF files, in a section called .symtab_meta.
> > > >
> > > > The aim of symbol meta-information is to provide a extensible format for
> > > > propagating additional information about symbols from the source code through to
> > > > the link stage.
> > > >
> > > > I hope I can get some feedback on this proposal, and its current implementation,
> > > > from the upstream community so I can make any adjustments required for the
> > > > eventual upstreaming of this feature.  
> > >
> > > Have you looked at Solaris syminfo section
> > >
> > > https://docs.oracle.com/cd/E26502_01/html/E26507/chapter7-17.html
> > >
> > > Can you extend it to do what you want?
> > >  
> >
> > It looks like there is only some minimal support for the Syminfo section in
> > Binutils. The support is only there so that readelf can dump information about
> > the Syminfo section from an object file.
> >
> > There's no implementation to enable the creation of a Syminfo section with
> > user-specified flags for certain symbols, nor are there any hooks into any
> > other Binutils programs, so I can't see any advantage to try and leverage that
> > existing functionality.  
> 
> syminfo is a very useful ELF extension.  My question is if binutils supports
> syminfo, can it be extended to meet your need?
> 
> 

Well, a syminfo entry has a field to store flags which describes the extra
information about the symbol, similar to the "kind" field of a symbol
meta-information entry.

typedef struct {
        Elf64_Half      si_boundto;
        Elf64_Half      si_flags;
} Elf64_Syminfo;

Meanwhile, syminfo augments the si_flags value by using si_boundto to point
to an index in the .dynamic section. This is similar to how the "value" field of
a symbol meta-information entry augments its "kind" field, but this
functionality must work in static executables which do not have a .dynamic
section.

So one way to extend syminfo to meet the general need of this proposed symbol
meta-information functionality then we would need a new field to store the
value associated with the "si_flags".

typedef struct {
        Elf64_Half      si_boundto;
        Elf64_Half      si_flags;
	Elf64_Addr	si_value;
} Elf64_Syminfo_new;

But there are more caveats given that I've worked from a spec which has been
followed for an implementation in Clang/LLVM, so if in Binutils we base the
symbol meta-information functionality off Solaris syminfo we lose compatibility
with this other tool.

Even though adding the above si_value field would meet the *general* need of
the new implementation, the implementation details between the syminfo and
symbol meta-information are very different.

I do understand the desire not to fragment Binutils with multiple
implementations of similar functionality.

If this will be a sticking point for upstreaming then I will need to consider
some other options. A key aim of the upstreaming effort of this functioniality
is that the GNU and Clang/LLVM implementations are compatible.

Thanks,
Jozef

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [RFC] Symbol meta-information ELF extension
  2020-02-18 14:39       ` Jozef Lawrynowicz
@ 2020-02-18 14:51         ` H.J. Lu
  2020-02-18 18:55           ` Jozef Lawrynowicz
  0 siblings, 1 reply; 8+ messages in thread
From: H.J. Lu @ 2020-02-18 14:51 UTC (permalink / raw)
  To: Jozef Lawrynowicz; +Cc: binutils

On Tue, Feb 18, 2020 at 6:38 AM Jozef Lawrynowicz
<jozef.l@mittosystems.com> wrote:
>
> On Tue, 18 Feb 2020 05:58:34 -0800
> "H.J. Lu" <hjl.tools@gmail.com> wrote:
>
> > On Tue, Feb 18, 2020 at 5:47 AM Jozef Lawrynowicz
> > <jozef.l@mittosystems.com> wrote:
> > >
> > > On Tue, 18 Feb 2020 04:58:02 -0800
> > > "H.J. Lu" <hjl.tools@gmail.com> wrote:
> > >
> > > > On Tue, Feb 18, 2020 at 2:26 AM Jozef Lawrynowicz
> > > > <jozef.l@mittosystems.com> wrote:
> > > > >
> > > > > Hi,
> > > > >
> > > > > I've been working with Texas Instruments to develop an ELF extension which
> > > > > allows additional information about symbols ("symbol meta-information") to
> > > > > be stored in ELF files, in a section called .symtab_meta.
> > > > >
> > > > > The aim of symbol meta-information is to provide a extensible format for
> > > > > propagating additional information about symbols from the source code through to
> > > > > the link stage.
> > > > >
> > > > > I hope I can get some feedback on this proposal, and its current implementation,
> > > > > from the upstream community so I can make any adjustments required for the
> > > > > eventual upstreaming of this feature.
> > > >
> > > > Have you looked at Solaris syminfo section
> > > >
> > > > https://docs.oracle.com/cd/E26502_01/html/E26507/chapter7-17.html
> > > >
> > > > Can you extend it to do what you want?
> > > >
> > >
> > > It looks like there is only some minimal support for the Syminfo section in
> > > Binutils. The support is only there so that readelf can dump information about
> > > the Syminfo section from an object file.
> > >
> > > There's no implementation to enable the creation of a Syminfo section with
> > > user-specified flags for certain symbols, nor are there any hooks into any
> > > other Binutils programs, so I can't see any advantage to try and leverage that
> > > existing functionality.
> >
> > syminfo is a very useful ELF extension.  My question is if binutils supports
> > syminfo, can it be extended to meet your need?
> >
> >
>
> Well, a syminfo entry has a field to store flags which describes the extra
> information about the symbol, similar to the "kind" field of a symbol
> meta-information entry.
>
> typedef struct {
>         Elf64_Half      si_boundto;
>         Elf64_Half      si_flags;
> } Elf64_Syminfo;
>
> Meanwhile, syminfo augments the si_flags value by using si_boundto to point
> to an index in the .dynamic section. This is similar to how the "value" field of
> a symbol meta-information entry augments its "kind" field, but this
> functionality must work in static executables which do not have a .dynamic
> section.
>
> So one way to extend syminfo to meet the general need of this proposed symbol
> meta-information functionality then we would need a new field to store the
> value associated with the "si_flags".
>
> typedef struct {
>         Elf64_Half      si_boundto;
>         Elf64_Half      si_flags;
>         Elf64_Addr      si_value;
> } Elf64_Syminfo_new;

You should start a discussion to extend ELF symbol info at

https://groups.google.com/forum/#!forum/generic-abi

> But there are more caveats given that I've worked from a spec which has been
> followed for an implementation in Clang/LLVM, so if in Binutils we base the
> symbol meta-information functionality off Solaris syminfo we lose compatibility
> with this other tool.
>
> Even though adding the above si_value field would meet the *general* need of
> the new implementation, the implementation details between the syminfo and
> symbol meta-information are very different.
>
> I do understand the desire not to fragment Binutils with multiple
> implementations of similar functionality.
>
> If this will be a sticking point for upstreaming then I will need to consider
> some other options. A key aim of the upstreaming effort of this functioniality
> is that the GNU and Clang/LLVM implementations are compatible.
>
> Thanks,
> Jozef



-- 
H.J.

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [RFC] Symbol meta-information ELF extension
  2020-02-18 14:51         ` H.J. Lu
@ 2020-02-18 18:55           ` Jozef Lawrynowicz
  2020-02-18 21:43             ` H.J. Lu
  0 siblings, 1 reply; 8+ messages in thread
From: Jozef Lawrynowicz @ 2020-02-18 18:55 UTC (permalink / raw)
  To: H.J. Lu; +Cc: binutils

On Tue, 18 Feb 2020 06:50:58 -0800
"H.J. Lu" <hjl.tools@gmail.com> wrote:

> On Tue, Feb 18, 2020 at 6:38 AM Jozef Lawrynowicz
> <jozef.l@mittosystems.com> wrote:
> >
> > On Tue, 18 Feb 2020 05:58:34 -0800
> > "H.J. Lu" <hjl.tools@gmail.com> wrote:
> >  
> > > On Tue, Feb 18, 2020 at 5:47 AM Jozef Lawrynowicz
> > > <jozef.l@mittosystems.com> wrote:  
> > > >
> > > > On Tue, 18 Feb 2020 04:58:02 -0800
> > > > "H.J. Lu" <hjl.tools@gmail.com> wrote:
> > > >  
> > > > > On Tue, Feb 18, 2020 at 2:26 AM Jozef Lawrynowicz
> > > > > <jozef.l@mittosystems.com> wrote:  
> > > > > >
> > > > > > Hi,
> > > > > >
> > > > > > I've been working with Texas Instruments to develop an ELF extension which
> > > > > > allows additional information about symbols ("symbol meta-information") to
> > > > > > be stored in ELF files, in a section called .symtab_meta.
> > > > > >
> > > > > > The aim of symbol meta-information is to provide a extensible format for
> > > > > > propagating additional information about symbols from the source code through to
> > > > > > the link stage.
> > > > > >
> > > > > > I hope I can get some feedback on this proposal, and its current implementation,
> > > > > > from the upstream community so I can make any adjustments required for the
> > > > > > eventual upstreaming of this feature.  
> > > > >
> > > > > Have you looked at Solaris syminfo section
> > > > >
> > > > > https://docs.oracle.com/cd/E26502_01/html/E26507/chapter7-17.html
> > > > >
> > > > > Can you extend it to do what you want?
> > > > >  
> > > >
> > > > It looks like there is only some minimal support for the Syminfo section in
> > > > Binutils. The support is only there so that readelf can dump information about
> > > > the Syminfo section from an object file.
> > > >
> > > > There's no implementation to enable the creation of a Syminfo section with
> > > > user-specified flags for certain symbols, nor are there any hooks into any
> > > > other Binutils programs, so I can't see any advantage to try and leverage that
> > > > existing functionality.  
> > >
> > > syminfo is a very useful ELF extension.  My question is if binutils supports
> > > syminfo, can it be extended to meet your need?
> > >
> > >  
> >
> > Well, a syminfo entry has a field to store flags which describes the extra
> > information about the symbol, similar to the "kind" field of a symbol
> > meta-information entry.
> >
> > typedef struct {
> >         Elf64_Half      si_boundto;
> >         Elf64_Half      si_flags;
> > } Elf64_Syminfo;
> >
> > Meanwhile, syminfo augments the si_flags value by using si_boundto to point
> > to an index in the .dynamic section. This is similar to how the "value" field of
> > a symbol meta-information entry augments its "kind" field, but this
> > functionality must work in static executables which do not have a .dynamic
> > section.
> >
> > So one way to extend syminfo to meet the general need of this proposed symbol
> > meta-information functionality then we would need a new field to store the
> > value associated with the "si_flags".
> >
> > typedef struct {
> >         Elf64_Half      si_boundto;
> >         Elf64_Half      si_flags;
> >         Elf64_Addr      si_value;
> > } Elf64_Syminfo_new;  
> 
> You should start a discussion to extend ELF symbol info at
> 
> https://groups.google.com/forum/#!forum/generic-abi

Thanks for the suggestion. I'll work with TI to get something posted there soon.

Can you clarify whether you still think this would work best as an extension to
Solaris Syminfo?

To be honest I have a problem with that given that Syminfo is a target specific
extension to ELF (which just happens to be implemented in a generic way in
Readelf), and we want to add symbol meta-information as a generic extension
to ELF.

There's no advantage to building upon Syminfo in Binutils given the
implementation is limited to readelf dumping the Syminfo table.

If Syminfo was already part of the gABI I would be on board with working from
that to add the new functionality.

I guess the other option is to take Syminfo, extend it with the new
functionality we want, then propose it as generic ELF extension. I wonder if
there would be legal issues doing all that without any input from Oracle.

Do they "own" the spec? Would I be able to propose their spec with some
extensions to ELF gABI? (These are somewhat rhetorical questions...)

Regards,
Jozef
> 
> > But there are more caveats given that I've worked from a spec which has been
> > followed for an implementation in Clang/LLVM, so if in Binutils we base the
> > symbol meta-information functionality off Solaris syminfo we lose compatibility
> > with this other tool.
> >
> > Even though adding the above si_value field would meet the *general* need of
> > the new implementation, the implementation details between the syminfo and
> > symbol meta-information are very different.
> >
> > I do understand the desire not to fragment Binutils with multiple
> > implementations of similar functionality.
> >
> > If this will be a sticking point for upstreaming then I will need to consider
> > some other options. A key aim of the upstreaming effort of this functioniality
> > is that the GNU and Clang/LLVM implementations are compatible.
> >
> > Thanks,
> > Jozef  
> 
> 
> 

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [RFC] Symbol meta-information ELF extension
  2020-02-18 18:55           ` Jozef Lawrynowicz
@ 2020-02-18 21:43             ` H.J. Lu
  0 siblings, 0 replies; 8+ messages in thread
From: H.J. Lu @ 2020-02-18 21:43 UTC (permalink / raw)
  To: Jozef Lawrynowicz; +Cc: binutils

On Tue, Feb 18, 2020 at 10:54 AM Jozef Lawrynowicz
<jozef.l@mittosystems.com> wrote:
>
> On Tue, 18 Feb 2020 06:50:58 -0800
> "H.J. Lu" <hjl.tools@gmail.com> wrote:
>
> > On Tue, Feb 18, 2020 at 6:38 AM Jozef Lawrynowicz
> > <jozef.l@mittosystems.com> wrote:
> > >
> > > On Tue, 18 Feb 2020 05:58:34 -0800
> > > "H.J. Lu" <hjl.tools@gmail.com> wrote:
> > >
> > > > On Tue, Feb 18, 2020 at 5:47 AM Jozef Lawrynowicz
> > > > <jozef.l@mittosystems.com> wrote:
> > > > >
> > > > > On Tue, 18 Feb 2020 04:58:02 -0800
> > > > > "H.J. Lu" <hjl.tools@gmail.com> wrote:
> > > > >
> > > > > > On Tue, Feb 18, 2020 at 2:26 AM Jozef Lawrynowicz
> > > > > > <jozef.l@mittosystems.com> wrote:
> > > > > > >
> > > > > > > Hi,
> > > > > > >
> > > > > > > I've been working with Texas Instruments to develop an ELF extension which
> > > > > > > allows additional information about symbols ("symbol meta-information") to
> > > > > > > be stored in ELF files, in a section called .symtab_meta.
> > > > > > >
> > > > > > > The aim of symbol meta-information is to provide a extensible format for
> > > > > > > propagating additional information about symbols from the source code through to
> > > > > > > the link stage.
> > > > > > >
> > > > > > > I hope I can get some feedback on this proposal, and its current implementation,
> > > > > > > from the upstream community so I can make any adjustments required for the
> > > > > > > eventual upstreaming of this feature.
> > > > > >
> > > > > > Have you looked at Solaris syminfo section
> > > > > >
> > > > > > https://docs.oracle.com/cd/E26502_01/html/E26507/chapter7-17.html
> > > > > >
> > > > > > Can you extend it to do what you want?
> > > > > >
> > > > >
> > > > > It looks like there is only some minimal support for the Syminfo section in
> > > > > Binutils. The support is only there so that readelf can dump information about
> > > > > the Syminfo section from an object file.
> > > > >
> > > > > There's no implementation to enable the creation of a Syminfo section with
> > > > > user-specified flags for certain symbols, nor are there any hooks into any
> > > > > other Binutils programs, so I can't see any advantage to try and leverage that
> > > > > existing functionality.
> > > >
> > > > syminfo is a very useful ELF extension.  My question is if binutils supports
> > > > syminfo, can it be extended to meet your need?
> > > >
> > > >
> > >
> > > Well, a syminfo entry has a field to store flags which describes the extra
> > > information about the symbol, similar to the "kind" field of a symbol
> > > meta-information entry.
> > >
> > > typedef struct {
> > >         Elf64_Half      si_boundto;
> > >         Elf64_Half      si_flags;
> > > } Elf64_Syminfo;
> > >
> > > Meanwhile, syminfo augments the si_flags value by using si_boundto to point
> > > to an index in the .dynamic section. This is similar to how the "value" field of
> > > a symbol meta-information entry augments its "kind" field, but this
> > > functionality must work in static executables which do not have a .dynamic
> > > section.
> > >
> > > So one way to extend syminfo to meet the general need of this proposed symbol
> > > meta-information functionality then we would need a new field to store the
> > > value associated with the "si_flags".
> > >
> > > typedef struct {
> > >         Elf64_Half      si_boundto;
> > >         Elf64_Half      si_flags;
> > >         Elf64_Addr      si_value;
> > > } Elf64_Syminfo_new;
> >
> > You should start a discussion to extend ELF symbol info at
> >
> > https://groups.google.com/forum/#!forum/generic-abi
>
> Thanks for the suggestion. I'll work with TI to get something posted there soon.
>
> Can you clarify whether you still think this would work best as an extension to
> Solaris Syminfo?
>
> To be honest I have a problem with that given that Syminfo is a target specific
> extension to ELF (which just happens to be implemented in a generic way in
> Readelf), and we want to add symbol meta-information as a generic extension
> to ELF.
>
> There's no advantage to building upon Syminfo in Binutils given the
> implementation is limited to readelf dumping the Syminfo table.
>
> If Syminfo was already part of the gABI I would be on board with working from
> that to add the new functionality.
>
> I guess the other option is to take Syminfo, extend it with the new
> functionality we want, then propose it as generic ELF extension. I wonder if
> there would be legal issues doing all that without any input from Oracle.
>
> Do they "own" the spec? Would I be able to propose their spec with some
> extensions to ELF gABI? (These are somewhat rhetorical questions...)

https://groups.google.com/forum/#!forum/generic-abi

is the place to discuss ELF gABI.


-- 
H.J.

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2020-02-18 21:43 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-02-18 10:25 [RFC] Symbol meta-information ELF extension Jozef Lawrynowicz
2020-02-18 12:58 ` H.J. Lu
2020-02-18 13:47   ` Jozef Lawrynowicz
2020-02-18 13:59     ` H.J. Lu
2020-02-18 14:39       ` Jozef Lawrynowicz
2020-02-18 14:51         ` H.J. Lu
2020-02-18 18:55           ` Jozef Lawrynowicz
2020-02-18 21:43             ` H.J. Lu

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).