tests: Add read_unaligned testcase

Run tests/read_unaligned 1 on a big endian and little endian machine
to generate the le_mem and be_mem arrays. The one byte variants are
kind of impossible to get wrong, but including them makes sure the
other variants are not naturally aligned in memory.

Signed-off-by: Mark Wielaard <mark@klomp.org>
diff --git a/tests/ChangeLog b/tests/ChangeLog
index 91aeada..d0149ba 100644
--- a/tests/ChangeLog
+++ b/tests/ChangeLog
@@ -1,3 +1,9 @@
+2020-10-25  Mark Wielaard  <mark@klomp.org>
+
+	* read_unaligned.c: New test.
+	* Makefile.am (check_PROGRAMS, TESTS): Add read_unaligned.
+	(read_unaligned_LDADD): New variable.
+
 2020-10-28  Tom Tromey  <tom@tromey.com>
 
 	PR26773
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 1b51ab8..84f6e88 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -63,7 +63,7 @@
 		  all-dwarf-ranges unit-info next_cfi \
 		  elfcopy addsections xlate_notes elfrdwrnop \
 		  dwelf_elf_e_machine_string \
-		  getphdrnum leb128
+		  getphdrnum leb128 read_unaligned
 
 asm_TESTS = asm-tst1 asm-tst2 asm-tst3 asm-tst4 asm-tst5 \
 	    asm-tst6 asm-tst7 asm-tst8 asm-tst9
@@ -186,7 +186,7 @@
 	run-disasm-riscv64.sh \
 	run-pt_gnu_prop-tests.sh \
 	run-getphdrnum.sh run-test-includes.sh \
-	leb128
+	leb128 read_unaligned
 
 if !BIARCH
 export ELFUTILS_DISABLE_BIARCH = 1
@@ -696,6 +696,7 @@
 dwelf_elf_e_machine_string_LDADD = $(libelf) $(libdw)
 getphdrnum_LDADD = $(libelf) $(libdw)
 leb128_LDADD = $(libelf) $(libdw)
+read_unaligned_LDADD = $(libelf) $(libdw)
 
 # We want to test the libelf header against the system elf.h header.
 # Don't include any -I CPPFLAGS. Except when we install our own elf.h.
diff --git a/tests/read_unaligned.c b/tests/read_unaligned.c
new file mode 100644
index 0000000..15e0c00
--- /dev/null
+++ b/tests/read_unaligned.c
@@ -0,0 +1,564 @@
+/* Test program for read_[type]_unaligned.
+   Copyright (C) 2020, Red Hat Inc.
+   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 <assert.h>
+#include <endian.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include "../libdw/libdwP.h"
+#include "../libdw/memory-access.h"
+
+union u8
+{
+  uint8_t v;
+  unsigned char c[1];
+};
+
+union s8
+{
+  int8_t v;
+  unsigned char c[1];
+};
+
+union u16
+{
+  uint16_t v;
+  unsigned char c[2];
+};
+
+union s16
+{
+  int16_t v;
+  unsigned char c[2];
+};
+
+union u24
+{
+  uint32_t v:24;
+  unsigned char c[3];
+} __attribute__((packed));
+
+union u32
+{
+  uint32_t v;
+  unsigned char c[4];
+};
+
+union s32
+{
+  int32_t v;
+  unsigned char c[4];
+};
+
+union u64
+{
+  uint64_t v;
+  unsigned char c[8];
+};
+
+union s64
+{
+  uint64_t v;
+  unsigned char c[8];
+};
+
+uint8_t u8_nums[] =
+  {
+   0,
+   1,
+   UINT8_MAX / 2 - 1,
+   UINT8_MAX / 2,
+   UINT8_MAX / 2 + 1,
+   UINT8_MAX,
+   UINT8_MAX -1
+  };
+
+int8_t s8_nums[] =
+  {
+   INT8_MIN,
+   INT8_MIN + 1,
+   -1,
+   0,
+   1,
+   INT8_MAX,
+   INT8_MAX - 1
+  };
+
+uint16_t u16_nums[] =
+  {
+   0,
+   1,
+   UINT16_MAX / 2 - 1,
+   UINT16_MAX / 2,
+   UINT16_MAX / 2 + 1,
+   UINT16_MAX,
+   UINT16_MAX -1
+  };
+
+int16_t s16_nums[] =
+  {
+   INT16_MIN,
+   INT16_MIN + 1,
+   -1,
+   0,
+   1,
+   INT16_MAX,
+   INT16_MAX - 1
+  };
+
+#define UINT24_MAX 0xffffff
+
+uint32_t u24_nums[] =
+  {
+   0,
+   1,
+   UINT24_MAX / 2 - 1,
+   UINT24_MAX / 2,
+   UINT24_MAX / 2 + 1,
+   UINT24_MAX,
+   UINT24_MAX -1
+  };
+
+uint32_t u32_nums[] =
+  {
+   0,
+   1,
+   UINT32_MAX / 2 - 1,
+   UINT32_MAX / 2,
+   UINT32_MAX / 2 + 1,
+   UINT32_MAX,
+   UINT32_MAX -1
+  };
+
+int32_t s32_nums[] =
+  {
+   INT32_MIN,
+   INT32_MIN + 1,
+   -1,
+   0,
+   1,
+   INT32_MAX,
+   INT32_MAX - 1
+  };
+
+uint64_t u64_nums[] =
+  {
+   0,
+   1,
+   UINT64_MAX / 2 - 1,
+   UINT64_MAX / 2,
+   UINT64_MAX / 2 + 1,
+   UINT64_MAX,
+   UINT64_MAX -1
+  };
+
+int64_t s64_nums[] =
+  {
+   INT64_MIN,
+   INT64_MIN + 1,
+   -1,
+   0,
+   1,
+   INT64_MAX,
+   INT64_MAX - 1
+  };
+
+static unsigned char le_mem[] =
+  {
+    /* u8 */
+    0x00,
+    0x01,
+    0x7e,
+    0x7f,
+    0x80,
+    0xff,
+    0xfe,
+    /* s8 */
+    0x80,
+    0x81,
+    0xff,
+    0x00,
+    0x01,
+    0x7f,
+    0x7e,
+    /* u16 */
+    0x00, 0x00,
+    0x01, 0x00,
+    0xfe, 0x7f,
+    0xff, 0x7f,
+    0x00, 0x80,
+    0xff, 0xff,
+    0xfe, 0xff,
+    /* s16 */
+    0x00, 0x80,
+    0x01, 0x80,
+    0xff, 0xff,
+    0x00, 0x00,
+    0x01, 0x00,
+    0xff, 0x7f,
+    0xfe, 0x7f,
+    /* u24 */
+    0x00, 0x00, 0x00,
+    0x01, 0x00, 0x00,
+    0xfe, 0xff, 0x7f,
+    0xff, 0xff, 0x7f,
+    0x00, 0x00, 0x80,
+    0xff, 0xff, 0xff,
+    0xfe, 0xff, 0xff,
+    /* u32 */
+    0x00, 0x00, 0x00, 0x00,
+    0x01, 0x00, 0x00, 0x00,
+    0xfe, 0xff, 0xff, 0x7f,
+    0xff, 0xff, 0xff, 0x7f,
+    0x00, 0x00, 0x00, 0x80,
+    0xff, 0xff, 0xff, 0xff,
+    0xfe, 0xff, 0xff, 0xff,
+    /* s32 */
+    0x00, 0x00, 0x00, 0x80,
+    0x01, 0x00, 0x00, 0x80,
+    0xff, 0xff, 0xff, 0xff,
+    0x00, 0x00, 0x00, 0x00,
+    0x01, 0x00, 0x00, 0x00,
+    0xff, 0xff, 0xff, 0x7f,
+    0xfe, 0xff, 0xff, 0x7f,
+    /* u64 */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    /* s64 */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+    0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f,
+    0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f,
+  };
+
+static unsigned char be_mem[] =
+  {
+    /* u8 */
+    0x00,
+    0x01,
+    0x7e,
+    0x7f,
+    0x80,
+    0xff,
+    0xfe,
+    /* s8 */
+    0x80,
+    0x81,
+    0xff,
+    0x00,
+    0x01,
+    0x7f,
+    0x7e,
+    /* u16 */
+    0x00, 0x00,
+    0x00, 0x01,
+    0x7f, 0xfe,
+    0x7f, 0xff,
+    0x80, 0x00,
+    0xff, 0xff,
+    0xff, 0xfe,
+    /* s16 */
+    0x80, 0x00,
+    0x80, 0x01,
+    0xff, 0xff,
+    0x00, 0x00,
+    0x00, 0x01,
+    0x7f, 0xff,
+    0x7f, 0xfe,
+    /* u24 */
+    0x00, 0x00, 0x00,
+    0x00, 0x00, 0x01,
+    0x7f, 0xff, 0xfe,
+    0x7f, 0xff, 0xff,
+    0x80, 0x00, 0x00,
+    0xff, 0xff, 0xff,
+    0xff, 0xff, 0xfe,
+    /* u32 */
+    0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x01,
+    0x7f, 0xff, 0xff, 0xfe,
+    0x7f, 0xff, 0xff, 0xff,
+    0x80, 0x00, 0x00, 0x00,
+    0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xfe,
+    /* s32 */
+    0x80, 0x00, 0x00, 0x00,
+    0x80, 0x00, 0x00, 0x01,
+    0xff, 0xff, 0xff, 0xff,
+    0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x01,
+    0x7f, 0xff, 0xff, 0xff,
+    0x7f, 0xff, 0xff, 0xfe,
+    /* u64 */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+    0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe,
+    0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe,
+    /* s64 */
+    0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+    0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe,
+  };
+
+int
+main (int argc, char **argv __attribute__((unused)))
+{
+  /* No arguments means check, otherwise Write out the memory array.  */
+  bool write = false;
+  if (argc > 1)
+    write = true;
+
+  bool is_le = (BYTE_ORDER == LITTLE_ENDIAN);
+
+  if (write)
+    {
+      if (is_le)
+	printf ("static unsigned char le_mem[] =\n");
+      else
+	printf ("static unsigned char be_mem[] =\n");
+      printf ("  {\n");
+    }
+
+  Dwarf dbg_le = { .other_byte_order = !is_le };
+  Dwarf dbg_be = { .other_byte_order = is_le };
+
+  unsigned char *p_le = le_mem;
+  unsigned char *p_be = be_mem;
+
+  union u8 u8;
+  if (write)
+    printf ("    /* u8 */\n");
+  for (size_t i = 0; i < sizeof (u8_nums) / sizeof (u8); i++)
+    {
+      if (write)
+	{
+	  u8.v = u8_nums[i];
+	  printf ("    0x%02" PRIx8 ",\n", u8.c[0]);
+	}
+      else
+	{
+	  uint8_t v = *p_le++;
+	  assert (v == u8_nums[i]);
+	  v = *p_be++;
+	  assert (v == u8_nums[i]);
+	}
+    }
+
+  union s8 s8;
+  if (write)
+    printf ("    /* s8 */\n");
+  for (size_t i = 0; i < sizeof (s8_nums) / sizeof (s8); i++)
+    {
+      if (write)
+	{
+	  s8.v = s8_nums[i];
+	  printf ("    0x%02" PRIx8 ",\n", s8.c[0]);
+	}
+      else
+	{
+	  int8_t v = *p_le++;
+	  assert (v == s8_nums[i]);
+	  v = *p_be++;
+	  assert (v == s8_nums[i]);
+	}
+    }
+
+  union u16 u16;
+  if (write)
+    printf ("    /* u16 */\n");
+  for (size_t i = 0; i < sizeof (u16_nums) / sizeof (u16); i++)
+    {
+      if (write)
+	{
+	  u16.v = u16_nums[i];
+	  printf ("    0x%02" PRIx8 ", ", u16.c[0]);
+	  printf ("0x%02" PRIx8 ",\n", u16.c[1]);
+	}
+      else
+	{
+	  uint16_t v = read_2ubyte_unaligned_inc (&dbg_le, p_le);
+	  assert (v == u16_nums[i]);
+	  v = read_2ubyte_unaligned_inc (&dbg_be, p_be);
+	  assert (v == u16_nums[i]);
+	}
+    }
+
+  union s16 s16;
+  if (write)
+    printf ("    /* s16 */\n");
+  for (size_t i = 0; i < sizeof (s16_nums) / sizeof (s16); i++)
+    {
+      if (write)
+	{
+	  s16.v = s16_nums[i];
+	  printf ("    0x%02" PRIx8 ", ", s16.c[0]);
+	  printf ("0x%02" PRIx8 ",\n", s16.c[1]);
+	}
+      else
+	{
+	  int16_t v = read_2sbyte_unaligned_inc (&dbg_le, p_le);
+	  assert (v == s16_nums[i]);
+	  v = read_2sbyte_unaligned_inc (&dbg_be, p_be);
+	  assert (v == s16_nums[i]);
+	}
+    }
+
+  union u24 u24;
+  if (write)
+    printf ("    /* u24 */\n");
+  for (size_t i = 0; i < sizeof (u24_nums) / sizeof (uint32_t); i++)
+    {
+      if (write)
+	{
+	  u24.v = u24_nums[i];
+	  printf ("    0x%02" PRIx8 ", ", u24.c[0]);
+	  printf ("0x%02" PRIx8 ", ", u24.c[1]);
+	  printf ("0x%02" PRIx8 ",\n", u24.c[2]);
+	}
+      else
+	{
+	  uint32_t v = read_3ubyte_unaligned_inc (&dbg_le, p_le);
+	  assert (v == u24_nums[i]);
+	  v = read_3ubyte_unaligned_inc (&dbg_be, p_be);
+	  assert (v == u24_nums[i]);
+	}
+    }
+
+  union u32 u32;
+  if (write)
+    printf ("    /* u32 */\n");
+  for (size_t i = 0; i < sizeof (u32_nums) / sizeof (u32); i++)
+    {
+      if (write)
+	{
+	  u32.v = u32_nums[i];
+	  printf ("    0x%02" PRIx8 ", ", u32.c[0]);
+	  printf ("0x%02" PRIx8 ", ", u32.c[1]);
+	  printf ("0x%02" PRIx8 ", ", u32.c[2]);
+	  printf ("0x%02" PRIx8 ",\n", u32.c[3]);
+	}
+      else
+	{
+	  uint32_t v = read_4ubyte_unaligned_inc (&dbg_le, p_le);
+	  assert (v == u32_nums[i]);
+	  v = read_4ubyte_unaligned_inc (&dbg_be, p_be);
+	  assert (v == u32_nums[i]);
+	}
+    }
+
+  union s32 s32;
+  if (write)
+    printf ("    /* s32 */\n");
+  for (size_t i = 0; i < sizeof (s32_nums) / sizeof (s32); i++)
+    {
+      if (write)
+	{
+	  s32.v = s32_nums[i];
+	  printf ("    0x%02" PRIx8 ", ", s32.c[0]);
+	  printf ("0x%02" PRIx8 ", ", s32.c[1]);
+	  printf ("0x%02" PRIx8 ", ", s32.c[2]);
+	  printf ("0x%02" PRIx8 ",\n", s32.c[3]);
+	}
+      else
+	{
+	  int32_t v = read_4sbyte_unaligned_inc (&dbg_le, p_le);
+	  assert (v == s32_nums[i]);
+	  v = read_4sbyte_unaligned_inc (&dbg_be, p_be);
+	  assert (v == s32_nums[i]);
+	}
+    }
+
+  union u64 u64;
+  if (write)
+    printf ("    /* u64 */\n");
+  for (size_t i = 0; i < sizeof (u64_nums) / sizeof (u64); i++)
+    {
+      if (write)
+	{
+	  u64.v = u64_nums[i];
+	  printf ("    0x%02" PRIx8 ", ", u64.c[0]);
+	  printf ("0x%02" PRIx8 ", ", u64.c[1]);
+	  printf ("0x%02" PRIx8 ", ", u64.c[2]);
+	  printf ("0x%02" PRIx8 ", ", u64.c[3]);
+	  printf ("0x%02" PRIx8 ", ", u64.c[4]);
+	  printf ("0x%02" PRIx8 ", ", u64.c[5]);
+	  printf ("0x%02" PRIx8 ", ", u64.c[6]);
+	  printf ("0x%02" PRIx8 ",\n", u64.c[7]);
+	}
+      else
+	{
+	  uint64_t v = read_8ubyte_unaligned_inc (&dbg_le, p_le);
+	  assert (v == u64_nums[i]);
+	  v = read_8ubyte_unaligned_inc (&dbg_be, p_be);
+	  assert (v == u64_nums[i]);
+	}
+    }
+
+  union s64 s64;
+  if (write)
+    printf ("    /* s64 */\n");
+  for (size_t i = 0; i < sizeof (s64_nums) / sizeof (s64); i++)
+    {
+      if (write)
+	{
+	  s64.v = s64_nums[i];
+	  printf ("    0x%02" PRIx8 ", ", s64.c[0]);
+	  printf ("0x%02" PRIx8 ", ", s64.c[1]);
+	  printf ("0x%02" PRIx8 ", ", s64.c[2]);
+	  printf ("0x%02" PRIx8 ", ", s64.c[3]);
+	  printf ("0x%02" PRIx8 ", ", s64.c[4]);
+	  printf ("0x%02" PRIx8 ", ", s64.c[5]);
+	  printf ("0x%02" PRIx8 ", ", s64.c[6]);
+	  printf ("0x%02" PRIx8 ",\n", s64.c[7]);
+	}
+      else
+	{
+	  int64_t v = read_8sbyte_unaligned_inc (&dbg_le, p_le);
+	  assert (v == s64_nums[i]);
+	  v = read_8sbyte_unaligned_inc (&dbg_be, p_be);
+	  assert (v == s64_nums[i]);
+	}
+    }
+
+  if (write)
+    printf ("  };\n");
+  else
+    {
+      assert (p_le == le_mem + sizeof (le_mem));
+      assert (p_be == be_mem + sizeof (be_mem));
+    }
+
+  return 0;
+}