[PATCHv2] libelf, readelf, elflint: Add RELR support

Message ID 20230824233355.2034723-1-mark@klomp.org
State Committed
Headers
Series [PATCHv2] libelf, readelf, elflint: Add RELR support |

Commit Message

Mark Wielaard Aug. 24, 2023, 11:33 p.m. UTC
  Handle RELR as defined here:
https://groups.google.com/g/generic-abi/c/bX460iggiKg/m/YT2RrjpMAwAJ

Introduce new ELF_T_RELR Elf_Type and handle it for SHT_RELR.  Check
various properties in elflint.  Print RELR relocations in
readelf. Just the entries with -U.  Just the addresses with -N. And
addresses plus symbol/offsets by default.

Also add a test to check that gelf.h works with the system elf.h.

	* libebl/eblsectiontypename.c (ebl_section_type_name): Add RELR
	to knownstype.
	* libelf/elf32_updatenull.c (updatenull_wrlock): Handle
	sh_entsize for SHT_RELR.
	* libelf/gelf.h (GElf_Relr): New typedef for Elf64_Relr.
	* libelf/gelf_fsize.c (__libelf_type_sizes): Add ELF_T_RELR.
	* libelf/gelf_xlate.c (__elf_xfctstom): Likewise.
	* libelf/gelf_xlate.h: Add RELR as FUNDAMENTAL.
	* libelf/libelf.h (Elf_Type): Add ELF_T_RELR. Add RELR
	defines/typedefs if undefined in system elf.h.
	* libelf/libelfP.h: Define ELF32_FSZ_RELR and ELF64_FSZ_RELR.
	* src/elflint.c (check_reloc_shdr): Check she_entsize for
	ELF_T_RELR.
	(check_relr): New function.
	(check_dynamic): Handle DT_RELR.
	(special_sections): Add SHT_RELR.
	(check_sections): Call check_relr.
	* src/readelf.c (print_relocs): Also accept a Dwfl_Module.
	(handle_relocs_relr): New function.
	(print_dwarf_addr): Make static and declare early.
	(process_elf_file): Pass dwflmod to print_relocs.
	(handle_dynamic): Handle DT_RELRSZ and DTRELRENT.
	* system-elf-gelf-test.c: New test.
	* Makefile.am (TESTS): Add system-elf-gelf-test.
	(check_PROGRAMS): Likewise.
	(system_elf_gelf_test_CPPFLAGS): New variable.
	(system_elf_gelf_test_LDADD): Likewise.

https://sourceware.org/bugzilla/show_bug.cgi?id=28495

Signed-off-by: Mark Wielaard <mark@klomp.org>
---

v2
- Fix GElf_Relr typedef name
- Add RELR typedefs/defines if missing from system elf.h
- Add a system-elf-gelf-test

 libebl/eblsectiontypename.c  |   3 +-
 libelf/elf32_updatenull.c    |   3 +
 libelf/gelf.h                |   3 +
 libelf/gelf_fsize.c          |   3 +-
 libelf/gelf_xlate.c          |   3 +-
 libelf/gelf_xlate.h          |   1 +
 libelf/libelf.h              |  14 ++++
 libelf/libelfP.h             |   2 +
 src/elflint.c                |  51 ++++++++++++--
 src/readelf.c                | 125 +++++++++++++++++++++++++++++++++--
 tests/Makefile.am            |   9 ++-
 tests/system-elf-gelf-test.c |  36 ++++++++++
 12 files changed, 239 insertions(+), 14 deletions(-)
 create mode 100644 tests/system-elf-gelf-test.c
  

Comments

Mark Wielaard Aug. 29, 2023, 10:55 a.m. UTC | #1
Hi,

On Fri, 2023-08-25 at 01:33 +0200, Mark Wielaard wrote:
> Handle RELR as defined here:
> https://groups.google.com/g/generic-abi/c/bX460iggiKg/m/YT2RrjpMAwAJ
> 
> Introduce new ELF_T_RELR Elf_Type and handle it for SHT_RELR.  Check
> various properties in elflint.  Print RELR relocations in
> readelf. Just the entries with -U.  Just the addresses with -N. And
> addresses plus symbol/offsets by default.
> 
> Also add a test to check that gelf.h works with the system elf.h.
> 
> 	* libebl/eblsectiontypename.c (ebl_section_type_name): Add RELR
> 	to knownstype.
> 	* libelf/elf32_updatenull.c (updatenull_wrlock): Handle
> 	sh_entsize for SHT_RELR.
> 	* libelf/gelf.h (GElf_Relr): New typedef for Elf64_Relr.
> 	* libelf/gelf_fsize.c (__libelf_type_sizes): Add ELF_T_RELR.
> 	* libelf/gelf_xlate.c (__elf_xfctstom): Likewise.
> 	* libelf/gelf_xlate.h: Add RELR as FUNDAMENTAL.
> 	* libelf/libelf.h (Elf_Type): Add ELF_T_RELR. Add RELR
> 	defines/typedefs if undefined in system elf.h.
> 	* libelf/libelfP.h: Define ELF32_FSZ_RELR and ELF64_FSZ_RELR.
> 	* src/elflint.c (check_reloc_shdr): Check she_entsize for
> 	ELF_T_RELR.
> 	(check_relr): New function.
> 	(check_dynamic): Handle DT_RELR.
> 	(special_sections): Add SHT_RELR.
> 	(check_sections): Call check_relr.
> 	* src/readelf.c (print_relocs): Also accept a Dwfl_Module.
> 	(handle_relocs_relr): New function.
> 	(print_dwarf_addr): Make static and declare early.
> 	(process_elf_file): Pass dwflmod to print_relocs.
> 	(handle_dynamic): Handle DT_RELRSZ and DTRELRENT.
> 	* system-elf-gelf-test.c: New test.
> 	* Makefile.am (TESTS): Add system-elf-gelf-test.
> 	(check_PROGRAMS): Likewise.
> 	(system_elf_gelf_test_CPPFLAGS): New variable.
> 	(system_elf_gelf_test_LDADD): Likewise.
> 
> https://sourceware.org/bugzilla/show_bug.cgi?id=28495
> 
> Signed-off-by: Mark Wielaard <mark@klomp.org>
> ---
> 
> v2
> - Fix GElf_Relr typedef name
> - Add RELR typedefs/defines if missing from system elf.h
> - Add a system-elf-gelf-test

I pushed this variant. The same, minus the new testcase has also been
backported to Fedora rawhide.

Cheers,

Mark
  

Patch

diff --git a/libebl/eblsectiontypename.c b/libebl/eblsectiontypename.c
index 2008b95a..ade25d4a 100644
--- a/libebl/eblsectiontypename.c
+++ b/libebl/eblsectiontypename.c
@@ -61,7 +61,8 @@  ebl_section_type_name (Ebl *ebl, int section, char *buf, size_t len)
 	  KNOWNSTYPE (FINI_ARRAY),
 	  KNOWNSTYPE (PREINIT_ARRAY),
 	  KNOWNSTYPE (GROUP),
-	  KNOWNSTYPE (SYMTAB_SHNDX)
+	  KNOWNSTYPE (SYMTAB_SHNDX),
+	  KNOWNSTYPE (RELR)
 	};
 
       /* Handle standard names.  */
diff --git a/libelf/elf32_updatenull.c b/libelf/elf32_updatenull.c
index 6c06e5e4..c5d26b00 100644
--- a/libelf/elf32_updatenull.c
+++ b/libelf/elf32_updatenull.c
@@ -256,6 +256,9 @@  __elfw2(LIBELFBITS,updatenull_wrlock) (Elf *elf, int *change_bop, size_t shnum)
 		case SHT_SUNW_syminfo:
 		  sh_entsize = elf_typesize (LIBELFBITS, ELF_T_SYMINFO, 1);
 		  break;
+		case SHT_RELR:
+		  sh_entsize = elf_typesize (LIBELFBITS, ELF_T_RELR, 1);
+		  break;
 		default:
 		  break;
 		}
diff --git a/libelf/gelf.h b/libelf/gelf.h
index 7a3c87aa..1847021e 100644
--- a/libelf/gelf.h
+++ b/libelf/gelf.h
@@ -82,6 +82,9 @@  typedef Elf64_Rel GElf_Rel;
 /* Relocation table entry with addend (in section of type SHT_RELA).  */
 typedef Elf64_Rela GElf_Rela;
 
+/* Relative relocation entry (in section of type SHT_RELR).  */
+typedef Elf64_Relr GElf_Relr;
+
 /* Program segment header.  */
 typedef Elf64_Phdr GElf_Phdr;
 
diff --git a/libelf/gelf_fsize.c b/libelf/gelf_fsize.c
index 493d7916..63bcbae5 100644
--- a/libelf/gelf_fsize.c
+++ b/libelf/gelf_fsize.c
@@ -69,7 +69,8 @@  const size_t __libelf_type_sizes[ELFCLASSNUM - 1][ELF_T_NUM] =
       [ELF_T_LIB]	= sizeof (ElfW2(LIBELFBITS, Ext_Lib)),		      \
       [ELF_T_AUXV]	= sizeof (ElfW2(LIBELFBITS, Ext_auxv_t)),	      \
       [ELF_T_CHDR]	= sizeof (ElfW2(LIBELFBITS, Ext_Chdr)),		      \
-      [ELF_T_GNUHASH]	= ELFW2(LIBELFBITS, FSZ_WORD)
+      [ELF_T_GNUHASH]	= ELFW2(LIBELFBITS, FSZ_WORD),			      \
+      [ELF_T_RELR]	= ELFW2(LIBELFBITS, FSZ_RELR)
       TYPE_SIZES (32)
     },
     [ELFCLASS64 - 1] = {
diff --git a/libelf/gelf_xlate.c b/libelf/gelf_xlate.c
index d8ad0634..749da1a1 100644
--- a/libelf/gelf_xlate.c
+++ b/libelf/gelf_xlate.c
@@ -204,7 +204,8 @@  const xfct_t __elf_xfctstom[ELFCLASSNUM - 1][ELF_T_NUM] =
 	[ELF_T_MOVE]	= ElfW2(Bits, cvt_Move),			      \
 	[ELF_T_LIB]	= ElfW2(Bits, cvt_Lib),				      \
 	[ELF_T_AUXV]	= ElfW2(Bits, cvt_auxv_t),			      \
-	[ELF_T_CHDR]	= ElfW2(Bits, cvt_chdr)
+	[ELF_T_CHDR]	= ElfW2(Bits, cvt_chdr),			      \
+	[ELF_T_RELR]	= ElfW2(Bits, cvt_Relr)
         define_xfcts (32),
 	[ELF_T_GNUHASH] = Elf32_cvt_Word
       },
diff --git a/libelf/gelf_xlate.h b/libelf/gelf_xlate.h
index 3c0e4bf6..d5511c34 100644
--- a/libelf/gelf_xlate.h
+++ b/libelf/gelf_xlate.h
@@ -36,6 +36,7 @@  FUNDAMENTAL (WORD, Word, LIBELFBITS);
 FUNDAMENTAL (SWORD, Sword, LIBELFBITS);
 FUNDAMENTAL (XWORD, Xword, LIBELFBITS);
 FUNDAMENTAL (SXWORD, Sxword, LIBELFBITS);
+FUNDAMENTAL (RELR, Relr, LIBELFBITS);
 
 /* The structured types.  */
 TYPE (Ehdr, LIBELFBITS)
diff --git a/libelf/libelf.h b/libelf/libelf.h
index 2374a48a..458a1fad 100644
--- a/libelf/libelf.h
+++ b/libelf/libelf.h
@@ -69,6 +69,19 @@ 
  #define ELFCOMPRESS_ZSTD       2          /* Zstandard algorithm.  */
 #endif
 
+#ifndef SHT_RELR
+ /* So RELR defines/typedefs can be used even with an old system elf.h.  */
+ #define SHT_RELR       19      /* RELR relative relocations */
+
+ /* RELR relocation table entry */
+ typedef Elf32_Word     Elf32_Relr;
+ typedef Elf64_Xword    Elf64_Relr;
+
+ #define DT_RELRSZ      35      /* Total size of RELR relative relocations */
+ #define DT_RELR        36      /* Address of RELR relative relocations */
+ #define DT_RELRENT     37      /* Size of one RELR relative relocaction */
+#endif
+
 #if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 3)
 # define __nonnull_attribute__(...) __attribute__ ((__nonnull__ (__VA_ARGS__)))
 # define __deprecated_attribute__ __attribute__ ((__deprecated__))
@@ -124,6 +137,7 @@  typedef enum
   ELF_T_CHDR,			/* Compressed, Elf32_Chdr, Elf64_Chdr, ... */
   ELF_T_NHDR8,			/* Special GNU Properties note.  Same as Nhdr,
 				   except padding.  */
+  ELF_T_RELR,			/* Relative relocation entry.  */
   /* Keep this the last entry.  */
   ELF_T_NUM
 } Elf_Type;
diff --git a/libelf/libelfP.h b/libelf/libelfP.h
index d3c241e5..96476064 100644
--- a/libelf/libelfP.h
+++ b/libelf/libelfP.h
@@ -63,6 +63,7 @@ 
 #define ELF32_FSZ_SWORD  4
 #define ELF32_FSZ_XWORD  8
 #define ELF32_FSZ_SXWORD 8
+#define ELF32_FSZ_RELR   4
 
 /* Same for 64 bits objects.  */
 #define ELF64_FSZ_ADDR   8
@@ -72,6 +73,7 @@ 
 #define ELF64_FSZ_SWORD  4
 #define ELF64_FSZ_XWORD  8
 #define ELF64_FSZ_SXWORD 8
+#define ELF64_FSZ_RELR   8
 
 
 /* This is an extension of the ELF_F_* enumeration.  The values here are
diff --git a/src/elflint.c b/src/elflint.c
index dd42dcb4..864de710 100644
--- a/src/elflint.c
+++ b/src/elflint.c
@@ -1,5 +1,6 @@ 
 /* Pedantic checking of ELF files compliance with gABI/psABI spec.
    Copyright (C) 2001-2015, 2017, 2018 Red Hat, Inc.
+   Copyright (C) 2023 Mark J. Wielaard <mark@klomp.org>
    This file is part of elfutils.
    Written by Ulrich Drepper <drepper@redhat.com>, 2001.
 
@@ -1291,10 +1292,20 @@  section [%2d] '%s': no relocations for merge-able string sections possible\n"),
 
   size_t sh_entsize = gelf_fsize (ebl->elf, reltype, 1, EV_CURRENT);
   if (shdr->sh_entsize != sh_entsize)
-    ERROR (_(reltype == ELF_T_RELA ? "\
-section [%2d] '%s': section entry size does not match ElfXX_Rela\n" : "\
-section [%2d] '%s': section entry size does not match ElfXX_Rel\n"),
-	   idx, section_name (ebl, idx));
+    {
+      if (reltype == ELF_T_RELA)
+	ERROR ("\
+section [%2d] '%s': section entry size does not match ElfXX_Rela\n",
+	       idx, section_name (ebl, idx));
+      else if (reltype == ELF_T_REL)
+	ERROR ("\
+section [%2d] '%s': section entry size does not match ElfXX_Rel\n",
+	       idx, section_name (ebl, idx));
+      else
+	ERROR ("\
+section [%2d] '%s': section entry size does not match ElfXX_Relr\n",
+	       idx, section_name (ebl, idx));
+    }
 
   /* In preparation of checking whether relocations are text
      relocations or not we need to determine whether the file is
@@ -1590,6 +1601,32 @@  section [%2d] '%s': cannot get relocation %zu: %s\n"),
     }
 }
 
+static void
+check_relr (Ebl *ebl, GElf_Ehdr *ehdr, GElf_Shdr *shdr, int idx)
+{
+  Elf_Data *data = elf_getdata (elf_getscn (ebl->elf, idx), NULL);
+  if (data == NULL)
+    {
+      ERROR (_("section [%2d] '%s': cannot get section data\n"),
+	     idx, section_name (ebl, idx));
+      return;
+    }
+
+  /* Check the fields of the section header.  */
+  GElf_Shdr destshdr_mem;
+  GElf_Shdr *destshdr = NULL;
+  struct loaded_segment *loaded = NULL;
+  check_reloc_shdr (ebl, ehdr, shdr, idx, ELF_T_RELR, &destshdr,
+		    &destshdr_mem, &loaded);
+
+  /* Just throw them away.  */
+  while (loaded != NULL)
+    {
+      struct loaded_segment *old = loaded;
+      loaded = loaded->next;
+      free (old);
+    }
+}
 
 /* Number of dynamic sections.  */
 static int ndynamic;
@@ -1780,6 +1817,7 @@  section [%2d] '%s': entry %zu: pointer does not match address of section [%2d] '
 	case DT_PLTGOT:
 	case DT_REL:
 	case DT_RELA:
+	case DT_RELR:
 	case DT_SYMBOLIC:
 	case DT_SYMTAB:
 	case DT_VERDEF:
@@ -3660,6 +3698,7 @@  static const struct
     { ".plt", 5, SHT_PROGBITS, unused, 0, 0 }, // XXX more tests
     { ".preinit_array", 15, SHT_PREINIT_ARRAY, exact, SHF_ALLOC | SHF_WRITE, 0 },
     { ".rela", 5, SHT_RELA, atleast, 0, SHF_ALLOC | SHF_INFO_LINK }, // XXX more tests
+    { ".relr", 5, SHT_RELR, atleast, 0, SHF_ALLOC }, // XXX more tests
     { ".rel", 4, SHT_REL, atleast, 0, SHF_ALLOC | SHF_INFO_LINK }, // XXX more tests
     { ".rodata", 8, SHT_PROGBITS, atleast, SHF_ALLOC, SHF_MERGE | SHF_STRINGS },
     { ".rodata1", 9, SHT_PROGBITS, atleast, SHF_ALLOC, SHF_MERGE | SHF_STRINGS },
@@ -4182,6 +4221,10 @@  section [%2zu] '%s': relocatable files cannot have dynamic symbol tables\n"),
 	  check_rel (ebl, ehdr, shdr, cnt);
 	  break;
 
+	case SHT_RELR:
+	  check_relr (ebl, ehdr, shdr, cnt);
+	  break;
+
 	case SHT_DYNAMIC:
 	  check_dynamic (ebl, ehdr, shdr, cnt);
 	  break;
diff --git a/src/readelf.c b/src/readelf.c
index eda0ce09..db31ad09 100644
--- a/src/readelf.c
+++ b/src/readelf.c
@@ -1,5 +1,6 @@ 
 /* Print information from ELF file in human-readable form.
    Copyright (C) 1999-2018 Red Hat, Inc.
+   Copyright (C) 2023 Mark J. Wielaard <mark@klomp.org>
    This file is part of elfutils.
 
    This file is free software; you can redistribute it and/or modify
@@ -301,11 +302,13 @@  static void print_shdr (Ebl *ebl, GElf_Ehdr *ehdr);
 static void print_phdr (Ebl *ebl, GElf_Ehdr *ehdr);
 static void print_scngrp (Ebl *ebl);
 static void print_dynamic (Ebl *ebl);
-static void print_relocs (Ebl *ebl, GElf_Ehdr *ehdr);
+static void print_relocs (Ebl *ebl, Dwfl_Module *mod, GElf_Ehdr *ehdr);
 static void handle_relocs_rel (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn,
 			       GElf_Shdr *shdr);
 static void handle_relocs_rela (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn,
 				GElf_Shdr *shdr);
+static void handle_relocs_relr (Ebl *ebl, Dwfl_Module *mod, Elf_Scn *scn,
+				GElf_Shdr *shdr);
 static bool print_symtab (Ebl *ebl, int type);
 static bool handle_symtab (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr);
 static bool handle_dynamic_symtab (Ebl *ebl);
@@ -336,6 +339,8 @@  static void dump_data (Ebl *ebl);
 static void dump_strings (Ebl *ebl);
 static void print_strings (Ebl *ebl);
 static void dump_archive_index (Elf *, const char *);
+static void print_dwarf_addr (Dwfl_Module *dwflmod, int address_size,
+			      Dwarf_Addr address, Dwarf_Addr raw);
 
 enum dyn_idx
 {
@@ -1052,7 +1057,7 @@  process_elf_file (Dwfl_Module *dwflmod, int fd)
   if (print_dynamic_table)
     print_dynamic (ebl);
   if (print_relocations)
-    print_relocs (pure_ebl, ehdr);
+    print_relocs (pure_ebl, dwflmod, ehdr);
   if (print_histogram)
     handle_hash (ebl);
   if (print_symbol_table || print_dynsym_table)
@@ -1971,9 +1976,11 @@  handle_dynamic (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr, GElf_Phdr *phdr)
 	case DT_RELASZ:
 	case DT_STRSZ:
 	case DT_RELSZ:
+	case DT_RELRSZ:
 	case DT_RELAENT:
 	case DT_SYMENT:
 	case DT_RELENT:
+	case DT_RELRENT:
 	case DT_PLTPADSZ:
 	case DT_MOVEENT:
 	case DT_MOVESZ:
@@ -2049,7 +2056,7 @@  print_dynamic (Ebl *ebl)
 
 /* Print relocations.  */
 static void
-print_relocs (Ebl *ebl, GElf_Ehdr *ehdr)
+print_relocs (Ebl *ebl, Dwfl_Module *mod, GElf_Ehdr *ehdr)
 {
   /* Find all relocation sections and handle them.  */
   Elf_Scn *scn = NULL;
@@ -2066,6 +2073,8 @@  print_relocs (Ebl *ebl, GElf_Ehdr *ehdr)
 	    handle_relocs_rel (ebl, ehdr, scn, shdr);
 	  else if (shdr->sh_type == SHT_RELA)
 	    handle_relocs_rela (ebl, ehdr, scn, shdr);
+	  else if (shdr->sh_type == SHT_RELR)
+	    handle_relocs_relr (ebl, mod, scn, shdr);
 	}
     }
 }
@@ -2454,6 +2463,114 @@  handle_relocs_rela (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, GElf_Shdr *shdr)
     }
 }
 
+/* Handle a relocation section.  */
+static void
+handle_relocs_relr (Ebl *ebl, Dwfl_Module *mod, Elf_Scn *scn, GElf_Shdr *shdr)
+{
+  int class = gelf_getclass (ebl->elf);
+  size_t sh_entsize = gelf_fsize (ebl->elf, ELF_T_RELR, 1, EV_CURRENT);
+  int nentries = shdr->sh_size / sh_entsize;
+
+  /* Get the data of the section.  */
+  Elf_Data *data = elf_getdata (scn, NULL);
+  if (data == NULL)
+    return;
+
+  /* Get the section header string table index.  */
+  size_t shstrndx;
+  if (unlikely (elf_getshdrstrndx (ebl->elf, &shstrndx) < 0))
+    error_exit (0, _("cannot get section header string table index"));
+
+  /* A .relr.dyn section does not refer to a specific section.  */
+  printf (ngettext ("\
+\nRelocation section [%2u] '%s' at offset %#0" PRIx64 " contains %d entry:\n",
+		    "\
+\nRelocation section [%2u] '%s' at offset %#0" PRIx64 " contains %d entries:\n",
+		    nentries),
+	  (unsigned int) elf_ndxscn (scn),
+	  elf_strptr (ebl->elf, shstrndx, shdr->sh_name),
+	  shdr->sh_offset,
+	  nentries);
+
+  if (class == ELFCLASS32)
+    {
+      uint32_t base = 0;
+      for (int cnt = 0; cnt < nentries; ++cnt)
+	{
+	  Elf32_Word *words = data->d_buf;
+	  Elf32_Word entry = words[cnt];
+
+	  /* Just the raw entries?  */
+	  if (print_unresolved_addresses)
+            printf ("  %#010" PRIx32 "%s\n", entry,
+                    (entry & 1) == 0 ? " *" : "");
+	  else
+	    {
+	      /* A real address, also sets base.  */
+	      if ((entry & 1) == 0)
+		{
+		  printf ("  ");
+		  print_dwarf_addr (mod, 4, entry, entry);
+		  printf (" *\n");
+
+		  base = entry + 4;
+		}
+	      else
+		{
+		  /* Untangle address from base and bits.  */
+		  uint32_t addr;
+		  for (addr = base; (entry >>= 1) != 0; addr += 4)
+		    if ((entry & 1) != 0)
+		      {
+			printf ("  ");
+			print_dwarf_addr (mod, 4, addr, addr);
+			printf ("\n");
+		      }
+		  base += 4 * (4 * 8 - 1);
+		}
+	    }
+	}
+    }
+  else
+    {
+      uint64_t base = 0;
+      for (int cnt = 0; cnt < nentries; ++cnt)
+	{
+	  Elf64_Xword *xwords = data->d_buf;
+	  Elf64_Xword entry = xwords[cnt];
+
+	  /* Just the raw entries?  */
+	  if (print_unresolved_addresses)
+	    printf ("  %#018" PRIx64 "%s\n", entry,
+		    (entry & 1) == 0 ? " *" : "");
+	  else
+	    {
+	      /* A real address, also sets base.  */
+	      if ((entry & 1) == 0)
+		{
+		  printf ("  ");
+		  print_dwarf_addr (mod, 8, entry, entry);
+		  printf (" *\n");
+
+		  base = entry + 8;
+		}
+	      else
+		{
+		  /* Untangle address from base and bits.  */
+		  uint64_t addr;
+		  for (addr = base; (entry >>= 1) != 0; addr += 8)
+		    if ((entry & 1) != 0)
+		      {
+			printf ("  ");
+			print_dwarf_addr (mod, 8, addr, addr);
+			printf ("\n");
+		      }
+		  base += 8 * (8 * 8 - 1);
+		}
+	    }
+	}
+    }
+}
 
 /* Print the program header.  Return true if a symtab is printed,
    false otherwise.  */
@@ -4107,7 +4224,7 @@  get_debug_elf_data (Dwarf *dbg, Ebl *ebl, int idx, Elf_Scn *scn)
   return elf_getdata (scn, NULL);
 }
 
-void
+static void
 print_dwarf_addr (Dwfl_Module *dwflmod,
 		  int address_size, Dwarf_Addr address, Dwarf_Addr raw)
 {
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 0c77f658..49069ccf 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -61,7 +61,7 @@  check_PROGRAMS = arextract arsymtest newfile saridx scnnames sectiondump \
 		  elfcopy addsections xlate_notes elfrdwrnop \
 		  dwelf_elf_e_machine_string \
 		  getphdrnum leb128 read_unaligned \
-		  msg_tst system-elf-libelf-test \
+		  msg_tst system-elf-libelf-test system-elf-gelf-test \
 		  nvidia_extended_linemap_libdw \
 		  $(asm_TESTS)
 
@@ -207,7 +207,7 @@  TESTS = run-arextract.sh run-arsymtest.sh run-ar.sh newfile test-nlist \
 	run-pt_gnu_prop-tests.sh \
 	run-getphdrnum.sh run-test-includes.sh \
 	leb128 read_unaligned \
-	msg_tst system-elf-libelf-test \
+	msg_tst system-elf-libelf-test system-elf-gelf-test \
 	$(asm_TESTS) run-disasm-bpf.sh run-low_high_pc-dw-form-indirect.sh \
 	run-nvidia-extended-linemap-libdw.sh run-nvidia-extended-linemap-readelf.sh \
 	run-readelf-dw-form-indirect.sh run-strip-largealign.sh \
@@ -805,14 +805,17 @@  leb128_LDADD = $(libelf) $(libdw)
 read_unaligned_LDADD = $(libelf) $(libdw)
 nvidia_extended_linemap_libdw_LDADD = $(libelf) $(libdw)
 
-# We want to test the libelf header against the system elf.h header.
+# We want to test the libelf headers against the system elf.h header.
 # Don't include any -I CPPFLAGS. Except when we install our own elf.h.
 if !INSTALL_ELFH
 system_elf_libelf_test_CPPFLAGS =
+system_elf_gelf_test_CPPFLAGS =
 else
 system_elf_libelf_test_CPPFLAGS = -I$(top_srcdir)/libelf
+system_elf_gelf_test_CPPFLAGS = -I$(top_srcdir)/libelf
 endif
 system_elf_libelf_test_LDADD = $(libelf)
+system_elf_gelf_test_LDADD = $(libelf)
 
 # A lock file used to make sure only one test dumps core at a time
 CLEANFILES += core-dump-backtrace.lock
diff --git a/tests/system-elf-gelf-test.c b/tests/system-elf-gelf-test.c
new file mode 100644
index 00000000..5a0e4e76
--- /dev/null
+++ b/tests/system-elf-gelf-test.c
@@ -0,0 +1,36 @@ 
+/* Explicit test compiling with system elf.h header plus libelf/gelf headers.
+
+   Copyright (C) Mark J. Wielaard <mark@klomp.org>
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include <elf.h>
+#include <stddef.h>
+#include "../libelf/libelf.h"
+#include "../libelf/gelf.h"
+
+int
+main (void)
+{
+  /* Trivial test, this is really a compile test anyway.  */
+  if (elf_version (EV_CURRENT) == EV_NONE)
+    return -1;
+
+  /* This will obviously fail. It is just to check that gelf_getclass
+     available (both at compile time and runtime).  */
+  int cls = gelf_getclass (NULL);
+
+  return cls == ELFCLASSNONE ? 0 : -1;
+}