Blame libdw/memory-access.h

Packit 032894
/* Unaligned memory access functionality.
Packit 032894
   Copyright (C) 2000-2014, 2018 Red Hat, Inc.
Packit 032894
   This file is part of elfutils.
Packit 032894
Packit 032894
   This file is free software; you can redistribute it and/or modify
Packit 032894
   it under the terms of either
Packit 032894
Packit 032894
     * the GNU Lesser General Public License as published by the Free
Packit 032894
       Software Foundation; either version 3 of the License, or (at
Packit 032894
       your option) any later version
Packit 032894
Packit 032894
   or
Packit 032894
Packit 032894
     * the GNU General Public License as published by the Free
Packit 032894
       Software Foundation; either version 2 of the License, or (at
Packit 032894
       your option) any later version
Packit 032894
Packit 032894
   or both in parallel, as here.
Packit 032894
Packit 032894
   elfutils is distributed in the hope that it will be useful, but
Packit 032894
   WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 032894
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 032894
   General Public License for more details.
Packit 032894
Packit 032894
   You should have received copies of the GNU General Public License and
Packit 032894
   the GNU Lesser General Public License along with this program.  If
Packit 032894
   not, see <http://www.gnu.org/licenses/>.  */
Packit 032894
Packit 032894
#ifndef _MEMORY_ACCESS_H
Packit 032894
#define _MEMORY_ACCESS_H 1
Packit 032894
Packit 032894
#include <byteswap.h>
Packit 032894
#include <endian.h>
Packit 032894
#include <limits.h>
Packit 032894
#include <stdint.h>
Packit 032894
Packit 032894
Packit 032894
/* Number decoding macros.  See 7.6 Variable Length Data.  */
Packit 032894
Packit 032894
#define len_leb128(var) ((8 * sizeof (var) + 6) / 7)
Packit 032894
Packit 032894
static inline size_t
Packit 032894
__libdw_max_len_leb128 (const size_t type_len,
Packit 032894
			const unsigned char *addr, const unsigned char *end)
Packit 032894
{
Packit 032894
  const size_t pointer_len = likely (addr < end) ? end - addr : 0;
Packit 032894
  return likely (type_len <= pointer_len) ? type_len : pointer_len;
Packit 032894
}
Packit 032894
Packit 032894
static inline size_t
Packit 032894
__libdw_max_len_uleb128 (const unsigned char *addr, const unsigned char *end)
Packit 032894
{
Packit 032894
  const size_t type_len = len_leb128 (uint64_t);
Packit 032894
  return __libdw_max_len_leb128 (type_len, addr, end);
Packit 032894
}
Packit 032894
Packit 032894
static inline size_t
Packit 032894
__libdw_max_len_sleb128 (const unsigned char *addr, const unsigned char *end)
Packit 032894
{
Packit 032894
  /* Subtract one step, so we don't shift into sign bit.  */
Packit 032894
  const size_t type_len = len_leb128 (int64_t) - 1;
Packit 032894
  return __libdw_max_len_leb128 (type_len, addr, end);
Packit 032894
}
Packit 032894
Packit 032894
#define get_uleb128_step(var, addr, nth)				      \
Packit 032894
  do {									      \
Packit 032894
    unsigned char __b = *(addr)++;					      \
Packit 032894
    (var) |= (typeof (var)) (__b & 0x7f) << ((nth) * 7);		      \
Packit 032894
    if (likely ((__b & 0x80) == 0))					      \
Packit 032894
      return (var);							      \
Packit 032894
  } while (0)
Packit 032894
Packit 032894
static inline uint64_t
Packit 032894
__libdw_get_uleb128 (const unsigned char **addrp, const unsigned char *end)
Packit 032894
{
Packit 032894
  uint64_t acc = 0;
Packit 032894
Packit 032894
  /* Unroll the first step to help the compiler optimize
Packit 032894
     for the common single-byte case.  */
Packit 032894
  get_uleb128_step (acc, *addrp, 0);
Packit 032894
Packit 032894
  const size_t max = __libdw_max_len_uleb128 (*addrp - 1, end);
Packit 032894
  for (size_t i = 1; i < max; ++i)
Packit 032894
    get_uleb128_step (acc, *addrp, i);
Packit 032894
  /* Other implementations set VALUE to UINT_MAX in this
Packit 032894
     case.  So we better do this as well.  */
Packit 032894
  return UINT64_MAX;
Packit 032894
}
Packit 032894
Packit 032894
static inline uint64_t
Packit 032894
__libdw_get_uleb128_unchecked (const unsigned char **addrp)
Packit 032894
{
Packit 032894
  uint64_t acc = 0;
Packit 032894
Packit 032894
  /* Unroll the first step to help the compiler optimize
Packit 032894
     for the common single-byte case.  */
Packit 032894
  get_uleb128_step (acc, *addrp, 0);
Packit 032894
Packit 032894
  const size_t max = len_leb128 (uint64_t);
Packit 032894
  for (size_t i = 1; i < max; ++i)
Packit 032894
    get_uleb128_step (acc, *addrp, i);
Packit 032894
  /* Other implementations set VALUE to UINT_MAX in this
Packit 032894
     case.  So we better do this as well.  */
Packit 032894
  return UINT64_MAX;
Packit 032894
}
Packit 032894
Packit 032894
/* Note, addr needs to me smaller than end. */
Packit 032894
#define get_uleb128(var, addr, end) ((var) = __libdw_get_uleb128 (&(addr), end))
Packit 032894
#define get_uleb128_unchecked(var, addr) ((var) = __libdw_get_uleb128_unchecked (&(addr)))
Packit 032894
Packit 032894
/* The signed case is similar, but we sign-extend the result.  */
Packit 032894
Packit 032894
#define get_sleb128_step(var, addr, nth)				      \
Packit 032894
  do {									      \
Packit 032894
    unsigned char __b = *(addr)++;					      \
Packit 032894
    if (likely ((__b & 0x80) == 0))					      \
Packit 032894
      {									      \
Packit 032894
	struct { signed int i:7; } __s = { .i = __b };			      \
Packit 032894
	(var) |= (typeof (var)) __s.i * ((typeof (var)) 1 << ((nth) * 7));    \
Packit 032894
	return (var);							      \
Packit 032894
      }									      \
Packit 032894
    (var) |= (typeof (var)) (__b & 0x7f) << ((nth) * 7);		      \
Packit 032894
  } while (0)
Packit 032894
Packit 032894
static inline int64_t
Packit 032894
__libdw_get_sleb128 (const unsigned char **addrp, const unsigned char *end)
Packit 032894
{
Packit 032894
  int64_t acc = 0;
Packit 032894
Packit 032894
  /* Unroll the first step to help the compiler optimize
Packit 032894
     for the common single-byte case.  */
Packit 032894
  get_sleb128_step (acc, *addrp, 0);
Packit 032894
Packit 032894
  const size_t max = __libdw_max_len_sleb128 (*addrp - 1, end);
Packit 032894
  for (size_t i = 1; i < max; ++i)
Packit 032894
    get_sleb128_step (acc, *addrp, i);
Packit 032894
  /* Other implementations set VALUE to INT_MAX in this
Packit 032894
     case.  So we better do this as well.  */
Packit 032894
  return INT64_MAX;
Packit 032894
}
Packit 032894
Packit 032894
static inline int64_t
Packit 032894
__libdw_get_sleb128_unchecked (const unsigned char **addrp)
Packit 032894
{
Packit 032894
  int64_t acc = 0;
Packit 032894
Packit 032894
  /* Unroll the first step to help the compiler optimize
Packit 032894
     for the common single-byte case.  */
Packit 032894
  get_sleb128_step (acc, *addrp, 0);
Packit 032894
Packit 032894
  /* Subtract one step, so we don't shift into sign bit.  */
Packit 032894
  const size_t max = len_leb128 (int64_t) - 1;
Packit 032894
  for (size_t i = 1; i < max; ++i)
Packit 032894
    get_sleb128_step (acc, *addrp, i);
Packit 032894
  /* Other implementations set VALUE to INT_MAX in this
Packit 032894
     case.  So we better do this as well.  */
Packit 032894
  return INT64_MAX;
Packit 032894
}
Packit 032894
Packit 032894
#define get_sleb128(var, addr, end) ((var) = __libdw_get_sleb128 (&(addr), end))
Packit 032894
#define get_sleb128_unchecked(var, addr) ((var) = __libdw_get_sleb128_unchecked (&(addr)))
Packit 032894
Packit 032894
Packit 032894
/* We use simple memory access functions in case the hardware allows it.
Packit 032894
   The caller has to make sure we don't have alias problems.  */
Packit 032894
#if ALLOW_UNALIGNED
Packit 032894
Packit 032894
# define read_2ubyte_unaligned(Dbg, Addr) \
Packit 032894
  (unlikely ((Dbg)->other_byte_order)					      \
Packit 032894
   ? bswap_16 (*((const uint16_t *) (Addr)))				      \
Packit 032894
   : *((const uint16_t *) (Addr)))
Packit 032894
# define read_2sbyte_unaligned(Dbg, Addr) \
Packit 032894
  (unlikely ((Dbg)->other_byte_order)					      \
Packit 032894
   ? (int16_t) bswap_16 (*((const int16_t *) (Addr)))			      \
Packit 032894
   : *((const int16_t *) (Addr)))
Packit 032894
Packit 032894
# define read_4ubyte_unaligned_noncvt(Addr) \
Packit 032894
   *((const uint32_t *) (Addr))
Packit 032894
# define read_4ubyte_unaligned(Dbg, Addr) \
Packit 032894
  (unlikely ((Dbg)->other_byte_order)					      \
Packit 032894
   ? bswap_32 (*((const uint32_t *) (Addr)))				      \
Packit 032894
   : *((const uint32_t *) (Addr)))
Packit 032894
# define read_4sbyte_unaligned(Dbg, Addr) \
Packit 032894
  (unlikely ((Dbg)->other_byte_order)					      \
Packit 032894
   ? (int32_t) bswap_32 (*((const int32_t *) (Addr)))			      \
Packit 032894
   : *((const int32_t *) (Addr)))
Packit 032894
Packit 032894
# define read_8ubyte_unaligned_noncvt(Addr) \
Packit 032894
   *((const uint64_t *) (Addr))
Packit 032894
# define read_8ubyte_unaligned(Dbg, Addr) \
Packit 032894
  (unlikely ((Dbg)->other_byte_order)					      \
Packit 032894
   ? bswap_64 (*((const uint64_t *) (Addr)))				      \
Packit 032894
   : *((const uint64_t *) (Addr)))
Packit 032894
# define read_8sbyte_unaligned(Dbg, Addr) \
Packit 032894
  (unlikely ((Dbg)->other_byte_order)					      \
Packit 032894
   ? (int64_t) bswap_64 (*((const int64_t *) (Addr)))			      \
Packit 032894
   : *((const int64_t *) (Addr)))
Packit 032894
Packit 032894
#else
Packit 032894
Packit 032894
union unaligned
Packit 032894
  {
Packit 032894
    void *p;
Packit 032894
    uint16_t u2;
Packit 032894
    uint32_t u4;
Packit 032894
    uint64_t u8;
Packit 032894
    int16_t s2;
Packit 032894
    int32_t s4;
Packit 032894
    int64_t s8;
Packit 032894
  } attribute_packed;
Packit 032894
Packit 032894
# define read_2ubyte_unaligned(Dbg, Addr) \
Packit 032894
  read_2ubyte_unaligned_1 ((Dbg)->other_byte_order, (Addr))
Packit 032894
# define read_2sbyte_unaligned(Dbg, Addr) \
Packit 032894
  read_2sbyte_unaligned_1 ((Dbg)->other_byte_order, (Addr))
Packit 032894
# define read_4ubyte_unaligned(Dbg, Addr) \
Packit 032894
  read_4ubyte_unaligned_1 ((Dbg)->other_byte_order, (Addr))
Packit 032894
# define read_4sbyte_unaligned(Dbg, Addr) \
Packit 032894
  read_4sbyte_unaligned_1 ((Dbg)->other_byte_order, (Addr))
Packit 032894
# define read_8ubyte_unaligned(Dbg, Addr) \
Packit 032894
  read_8ubyte_unaligned_1 ((Dbg)->other_byte_order, (Addr))
Packit 032894
# define read_8sbyte_unaligned(Dbg, Addr) \
Packit 032894
  read_8sbyte_unaligned_1 ((Dbg)->other_byte_order, (Addr))
Packit 032894
Packit 032894
static inline uint16_t
Packit 032894
read_2ubyte_unaligned_1 (bool other_byte_order, const void *p)
Packit 032894
{
Packit 032894
  const union unaligned *up = p;
Packit 032894
  if (unlikely (other_byte_order))
Packit 032894
    return bswap_16 (up->u2);
Packit 032894
  return up->u2;
Packit 032894
}
Packit 032894
static inline int16_t
Packit 032894
read_2sbyte_unaligned_1 (bool other_byte_order, const void *p)
Packit 032894
{
Packit 032894
  const union unaligned *up = p;
Packit 032894
  if (unlikely (other_byte_order))
Packit 032894
    return (int16_t) bswap_16 (up->u2);
Packit 032894
  return up->s2;
Packit 032894
}
Packit 032894
Packit 032894
static inline uint32_t
Packit 032894
read_4ubyte_unaligned_noncvt (const void *p)
Packit 032894
{
Packit 032894
  const union unaligned *up = p;
Packit 032894
  return up->u4;
Packit 032894
}
Packit 032894
static inline uint32_t
Packit 032894
read_4ubyte_unaligned_1 (bool other_byte_order, const void *p)
Packit 032894
{
Packit 032894
  const union unaligned *up = p;
Packit 032894
  if (unlikely (other_byte_order))
Packit 032894
    return bswap_32 (up->u4);
Packit 032894
  return up->u4;
Packit 032894
}
Packit 032894
static inline int32_t
Packit 032894
read_4sbyte_unaligned_1 (bool other_byte_order, const void *p)
Packit 032894
{
Packit 032894
  const union unaligned *up = p;
Packit 032894
  if (unlikely (other_byte_order))
Packit 032894
    return (int32_t) bswap_32 (up->u4);
Packit 032894
  return up->s4;
Packit 032894
}
Packit 032894
Packit 032894
static inline uint64_t
Packit 032894
read_8ubyte_unaligned_noncvt (const void *p)
Packit 032894
{
Packit 032894
  const union unaligned *up = p;
Packit 032894
  return up->u8;
Packit 032894
}
Packit 032894
static inline uint64_t
Packit 032894
read_8ubyte_unaligned_1 (bool other_byte_order, const void *p)
Packit 032894
{
Packit 032894
  const union unaligned *up = p;
Packit 032894
  if (unlikely (other_byte_order))
Packit 032894
    return bswap_64 (up->u8);
Packit 032894
  return up->u8;
Packit 032894
}
Packit 032894
static inline int64_t
Packit 032894
read_8sbyte_unaligned_1 (bool other_byte_order, const void *p)
Packit 032894
{
Packit 032894
  const union unaligned *up = p;
Packit 032894
  if (unlikely (other_byte_order))
Packit 032894
    return (int64_t) bswap_64 (up->u8);
Packit 032894
  return up->s8;
Packit 032894
}
Packit 032894
Packit 032894
#endif	/* allow unaligned */
Packit 032894
Packit 032894
Packit 032894
#define read_2ubyte_unaligned_inc(Dbg, Addr) \
Packit 032894
  ({ uint16_t t_ = read_2ubyte_unaligned (Dbg, Addr);			      \
Packit 032894
     Addr = (__typeof (Addr)) (((uintptr_t) (Addr)) + 2);		      \
Packit 032894
     t_; })
Packit 032894
#define read_2sbyte_unaligned_inc(Dbg, Addr) \
Packit 032894
  ({ int16_t t_ = read_2sbyte_unaligned (Dbg, Addr);			      \
Packit 032894
     Addr = (__typeof (Addr)) (((uintptr_t) (Addr)) + 2);		      \
Packit 032894
     t_; })
Packit 032894
Packit 032894
#define read_4ubyte_unaligned_inc(Dbg, Addr) \
Packit 032894
  ({ uint32_t t_ = read_4ubyte_unaligned (Dbg, Addr);			      \
Packit 032894
     Addr = (__typeof (Addr)) (((uintptr_t) (Addr)) + 4);		      \
Packit 032894
     t_; })
Packit 032894
#define read_4sbyte_unaligned_inc(Dbg, Addr) \
Packit 032894
  ({ int32_t t_ = read_4sbyte_unaligned (Dbg, Addr);			      \
Packit 032894
     Addr = (__typeof (Addr)) (((uintptr_t) (Addr)) + 4);		      \
Packit 032894
     t_; })
Packit 032894
Packit 032894
#define read_8ubyte_unaligned_inc(Dbg, Addr) \
Packit 032894
  ({ uint64_t t_ = read_8ubyte_unaligned (Dbg, Addr);			      \
Packit 032894
     Addr = (__typeof (Addr)) (((uintptr_t) (Addr)) + 8);		      \
Packit 032894
     t_; })
Packit 032894
#define read_8sbyte_unaligned_inc(Dbg, Addr) \
Packit 032894
  ({ int64_t t_ = read_8sbyte_unaligned (Dbg, Addr);			      \
Packit 032894
     Addr = (__typeof (Addr)) (((uintptr_t) (Addr)) + 8);		      \
Packit 032894
     t_; })
Packit 032894
Packit 032894
/* 3ubyte reads are only used for DW_FORM_addrx3 and DW_FORM_strx3.
Packit 032894
   And are probably very rare.  They are not optimized.  They are
Packit 032894
   handled as if reading a 4byte value with the first (for big endian)
Packit 032894
   or last (for little endian) byte zero.  */
Packit 032894
Packit 032894
static inline int
Packit 032894
file_byte_order (bool other_byte_order)
Packit 032894
{
Packit 032894
#if __BYTE_ORDER == __LITTLE_ENDIAN
Packit 032894
  return other_byte_order ? __BIG_ENDIAN : __LITTLE_ENDIAN;
Packit 032894
#else
Packit 032894
  return other_byte_order ? __LITTLE_ENDIAN : __BIG_ENDIAN;
Packit 032894
#endif
Packit 032894
}
Packit 032894
Packit 032894
static inline uint32_t
Packit 032894
read_3ubyte_unaligned (Dwarf *dbg, const unsigned char *p)
Packit 032894
{
Packit 032894
  union
Packit 032894
  {
Packit 032894
    uint32_t u4;
Packit 032894
    unsigned char c[4];
Packit 032894
  } d;
Packit 032894
  bool other_byte_order = dbg->other_byte_order;
Packit 032894
Packit 032894
  if (file_byte_order (other_byte_order) == __BIG_ENDIAN)
Packit 032894
    {
Packit 032894
      d.c[0] = 0x00;
Packit 032894
      d.c[1] = p[0];
Packit 032894
      d.c[2] = p[1];
Packit 032894
      d.c[3] = p[2];
Packit 032894
    }
Packit 032894
  else
Packit 032894
    {
Packit 032894
      d.c[0] = p[0];
Packit 032894
      d.c[1] = p[1];
Packit 032894
      d.c[2] = p[2];
Packit 032894
      d.c[3] = 0x00;
Packit 032894
    }
Packit 032894
Packit 032894
  if (other_byte_order)
Packit 032894
    return bswap_32 (d.u4);
Packit 032894
  else
Packit 032894
    return d.u4;
Packit 032894
}
Packit 032894
Packit 032894
Packit 032894
#define read_3ubyte_unaligned_inc(Dbg, Addr) \
Packit 032894
  ({ uint32_t t_ = read_2ubyte_unaligned (Dbg, Addr);			      \
Packit 032894
     Addr = (__typeof (Addr)) (((uintptr_t) (Addr)) + 3);		      \
Packit 032894
     t_; })
Packit 032894
Packit 032894
#define read_addr_unaligned_inc(Nbytes, Dbg, Addr)			\
Packit 032894
  (assert ((Nbytes) == 4 || (Nbytes) == 8),				\
Packit 032894
    ((Nbytes) == 4 ? read_4ubyte_unaligned_inc (Dbg, Addr)		\
Packit 032894
     : read_8ubyte_unaligned_inc (Dbg, Addr)))
Packit 032894
Packit 032894
#endif	/* memory-access.h */