blob: ed68bdb9f840d7def1edbb49d9d9b1ff38084cbc [file] [log] [blame]
Brian Silverman86497922018-02-10 19:28:39 -05001/* Unaligned memory access functionality.
2 Copyright (C) 2000-2014 Red Hat, Inc.
3 This file is part of elfutils.
4 Written by Ulrich Drepper <drepper@redhat.com>, 2001.
5
6 This file is free software; you can redistribute it and/or modify
7 it under the terms of either
8
9 * the GNU Lesser General Public License as published by the Free
10 Software Foundation; either version 3 of the License, or (at
11 your option) any later version
12
13 or
14
15 * the GNU General Public License as published by the Free
16 Software Foundation; either version 2 of the License, or (at
17 your option) any later version
18
19 or both in parallel, as here.
20
21 elfutils is distributed in the hope that it will be useful, but
22 WITHOUT ANY WARRANTY; without even the implied warranty of
23 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24 General Public License for more details.
25
26 You should have received copies of the GNU General Public License and
27 the GNU Lesser General Public License along with this program. If
28 not, see <http://www.gnu.org/licenses/>. */
29
30#ifndef _MEMORY_ACCESS_H
31#define _MEMORY_ACCESS_H 1
32
33#include <byteswap.h>
34#include <limits.h>
35#include <stdint.h>
36
37
38/* Number decoding macros. See 7.6 Variable Length Data. */
39
40#define len_leb128(var) ((8 * sizeof (var) + 6) / 7)
41
42static inline size_t
43__libdw_max_len_leb128 (const size_t type_len,
44 const unsigned char *addr, const unsigned char *end)
45{
46 const size_t pointer_len = likely (addr < end) ? end - addr : 0;
47 return likely (type_len <= pointer_len) ? type_len : pointer_len;
48}
49
50static inline size_t
51__libdw_max_len_uleb128 (const unsigned char *addr, const unsigned char *end)
52{
53 const size_t type_len = len_leb128 (uint64_t);
54 return __libdw_max_len_leb128 (type_len, addr, end);
55}
56
57static inline size_t
58__libdw_max_len_sleb128 (const unsigned char *addr, const unsigned char *end)
59{
60 /* Subtract one step, so we don't shift into sign bit. */
61 const size_t type_len = len_leb128 (int64_t) - 1;
62 return __libdw_max_len_leb128 (type_len, addr, end);
63}
64
65#define get_uleb128_step(var, addr, nth) \
66 do { \
67 unsigned char __b = *(addr)++; \
68 (var) |= (typeof (var)) (__b & 0x7f) << ((nth) * 7); \
69 if (likely ((__b & 0x80) == 0)) \
70 return (var); \
71 } while (0)
72
73static inline uint64_t
74__libdw_get_uleb128 (const unsigned char **addrp, const unsigned char *end)
75{
76 uint64_t acc = 0;
77
78 /* Unroll the first step to help the compiler optimize
79 for the common single-byte case. */
80 get_uleb128_step (acc, *addrp, 0);
81
82 const size_t max = __libdw_max_len_uleb128 (*addrp - 1, end);
83 for (size_t i = 1; i < max; ++i)
84 get_uleb128_step (acc, *addrp, i);
85 /* Other implementations set VALUE to UINT_MAX in this
86 case. So we better do this as well. */
87 return UINT64_MAX;
88}
89
90static inline uint64_t
91__libdw_get_uleb128_unchecked (const unsigned char **addrp)
92{
93 uint64_t acc = 0;
94
95 /* Unroll the first step to help the compiler optimize
96 for the common single-byte case. */
97 get_uleb128_step (acc, *addrp, 0);
98
99 const size_t max = len_leb128 (uint64_t);
100 for (size_t i = 1; i < max; ++i)
101 get_uleb128_step (acc, *addrp, i);
102 /* Other implementations set VALUE to UINT_MAX in this
103 case. So we better do this as well. */
104 return UINT64_MAX;
105}
106
107/* Note, addr needs to me smaller than end. */
108#define get_uleb128(var, addr, end) ((var) = __libdw_get_uleb128 (&(addr), end))
109#define get_uleb128_unchecked(var, addr) ((var) = __libdw_get_uleb128_unchecked (&(addr)))
110
111/* The signed case is similar, but we sign-extend the result. */
112
113#define get_sleb128_step(var, addr, nth) \
114 do { \
115 unsigned char __b = *(addr)++; \
116 if (likely ((__b & 0x80) == 0)) \
117 { \
118 struct { signed int i:7; } __s = { .i = __b }; \
119 (var) |= (typeof (var)) __s.i * ((typeof (var)) 1 << ((nth) * 7)); \
120 return (var); \
121 } \
122 (var) |= (typeof (var)) (__b & 0x7f) << ((nth) * 7); \
123 } while (0)
124
125static inline int64_t
126__libdw_get_sleb128 (const unsigned char **addrp, const unsigned char *end)
127{
128 int64_t acc = 0;
129
130 /* Unroll the first step to help the compiler optimize
131 for the common single-byte case. */
132 get_sleb128_step (acc, *addrp, 0);
133
134 const size_t max = __libdw_max_len_sleb128 (*addrp - 1, end);
135 for (size_t i = 1; i < max; ++i)
136 get_sleb128_step (acc, *addrp, i);
137 /* Other implementations set VALUE to INT_MAX in this
138 case. So we better do this as well. */
139 return INT64_MAX;
140}
141
142#define get_sleb128(var, addr, end) ((var) = __libdw_get_sleb128 (&(addr), end))
143
144
145/* We use simple memory access functions in case the hardware allows it.
146 The caller has to make sure we don't have alias problems. */
147#if ALLOW_UNALIGNED
148
149# define read_2ubyte_unaligned(Dbg, Addr) \
150 (unlikely ((Dbg)->other_byte_order) \
151 ? bswap_16 (*((const uint16_t *) (Addr))) \
152 : *((const uint16_t *) (Addr)))
153# define read_2sbyte_unaligned(Dbg, Addr) \
154 (unlikely ((Dbg)->other_byte_order) \
155 ? (int16_t) bswap_16 (*((const int16_t *) (Addr))) \
156 : *((const int16_t *) (Addr)))
157
158# define read_4ubyte_unaligned_noncvt(Addr) \
159 *((const uint32_t *) (Addr))
160# define read_4ubyte_unaligned(Dbg, Addr) \
161 (unlikely ((Dbg)->other_byte_order) \
162 ? bswap_32 (*((const uint32_t *) (Addr))) \
163 : *((const uint32_t *) (Addr)))
164# define read_4sbyte_unaligned(Dbg, Addr) \
165 (unlikely ((Dbg)->other_byte_order) \
166 ? (int32_t) bswap_32 (*((const int32_t *) (Addr))) \
167 : *((const int32_t *) (Addr)))
168
169# define read_8ubyte_unaligned_noncvt(Addr) \
170 *((const uint64_t *) (Addr))
171# define read_8ubyte_unaligned(Dbg, Addr) \
172 (unlikely ((Dbg)->other_byte_order) \
173 ? bswap_64 (*((const uint64_t *) (Addr))) \
174 : *((const uint64_t *) (Addr)))
175# define read_8sbyte_unaligned(Dbg, Addr) \
176 (unlikely ((Dbg)->other_byte_order) \
177 ? (int64_t) bswap_64 (*((const int64_t *) (Addr))) \
178 : *((const int64_t *) (Addr)))
179
180#else
181
182union unaligned
183 {
184 void *p;
185 uint16_t u2;
186 uint32_t u4;
187 uint64_t u8;
188 int16_t s2;
189 int32_t s4;
190 int64_t s8;
191 } attribute_packed;
192
193# define read_2ubyte_unaligned(Dbg, Addr) \
194 read_2ubyte_unaligned_1 ((Dbg)->other_byte_order, (Addr))
195# define read_2sbyte_unaligned(Dbg, Addr) \
196 read_2sbyte_unaligned_1 ((Dbg)->other_byte_order, (Addr))
197# define read_4ubyte_unaligned(Dbg, Addr) \
198 read_4ubyte_unaligned_1 ((Dbg)->other_byte_order, (Addr))
199# define read_4sbyte_unaligned(Dbg, Addr) \
200 read_4sbyte_unaligned_1 ((Dbg)->other_byte_order, (Addr))
201# define read_8ubyte_unaligned(Dbg, Addr) \
202 read_8ubyte_unaligned_1 ((Dbg)->other_byte_order, (Addr))
203# define read_8sbyte_unaligned(Dbg, Addr) \
204 read_8sbyte_unaligned_1 ((Dbg)->other_byte_order, (Addr))
205
206static inline uint16_t
207read_2ubyte_unaligned_1 (bool other_byte_order, const void *p)
208{
209 const union unaligned *up = p;
210 if (unlikely (other_byte_order))
211 return bswap_16 (up->u2);
212 return up->u2;
213}
214static inline int16_t
215read_2sbyte_unaligned_1 (bool other_byte_order, const void *p)
216{
217 const union unaligned *up = p;
218 if (unlikely (other_byte_order))
219 return (int16_t) bswap_16 (up->u2);
220 return up->s2;
221}
222
223static inline uint32_t
224read_4ubyte_unaligned_noncvt (const void *p)
225{
226 const union unaligned *up = p;
227 return up->u4;
228}
229static inline uint32_t
230read_4ubyte_unaligned_1 (bool other_byte_order, const void *p)
231{
232 const union unaligned *up = p;
233 if (unlikely (other_byte_order))
234 return bswap_32 (up->u4);
235 return up->u4;
236}
237static inline int32_t
238read_4sbyte_unaligned_1 (bool other_byte_order, const void *p)
239{
240 const union unaligned *up = p;
241 if (unlikely (other_byte_order))
242 return (int32_t) bswap_32 (up->u4);
243 return up->s4;
244}
245
246static inline uint64_t
247read_8ubyte_unaligned_noncvt (const void *p)
248{
249 const union unaligned *up = p;
250 return up->u8;
251}
252static inline uint64_t
253read_8ubyte_unaligned_1 (bool other_byte_order, const void *p)
254{
255 const union unaligned *up = p;
256 if (unlikely (other_byte_order))
257 return bswap_64 (up->u8);
258 return up->u8;
259}
260static inline int64_t
261read_8sbyte_unaligned_1 (bool other_byte_order, const void *p)
262{
263 const union unaligned *up = p;
264 if (unlikely (other_byte_order))
265 return (int64_t) bswap_64 (up->u8);
266 return up->s8;
267}
268
269#endif /* allow unaligned */
270
271
272#define read_2ubyte_unaligned_inc(Dbg, Addr) \
273 ({ uint16_t t_ = read_2ubyte_unaligned (Dbg, Addr); \
274 Addr = (__typeof (Addr)) (((uintptr_t) (Addr)) + 2); \
275 t_; })
276#define read_2sbyte_unaligned_inc(Dbg, Addr) \
277 ({ int16_t t_ = read_2sbyte_unaligned (Dbg, Addr); \
278 Addr = (__typeof (Addr)) (((uintptr_t) (Addr)) + 2); \
279 t_; })
280
281#define read_4ubyte_unaligned_inc(Dbg, Addr) \
282 ({ uint32_t t_ = read_4ubyte_unaligned (Dbg, Addr); \
283 Addr = (__typeof (Addr)) (((uintptr_t) (Addr)) + 4); \
284 t_; })
285#define read_4sbyte_unaligned_inc(Dbg, Addr) \
286 ({ int32_t t_ = read_4sbyte_unaligned (Dbg, Addr); \
287 Addr = (__typeof (Addr)) (((uintptr_t) (Addr)) + 4); \
288 t_; })
289
290#define read_8ubyte_unaligned_inc(Dbg, Addr) \
291 ({ uint64_t t_ = read_8ubyte_unaligned (Dbg, Addr); \
292 Addr = (__typeof (Addr)) (((uintptr_t) (Addr)) + 8); \
293 t_; })
294#define read_8sbyte_unaligned_inc(Dbg, Addr) \
295 ({ int64_t t_ = read_8sbyte_unaligned (Dbg, Addr); \
296 Addr = (__typeof (Addr)) (((uintptr_t) (Addr)) + 8); \
297 t_; })
298
299
300#define read_addr_unaligned_inc(Nbytes, Dbg, Addr) \
301 (assert ((Nbytes) == 4 || (Nbytes) == 8), \
302 ((Nbytes) == 4 ? read_4ubyte_unaligned_inc (Dbg, Addr) \
303 : read_8ubyte_unaligned_inc (Dbg, Addr)))
304
305#endif /* memory-access.h */