Line data Source code
1 : /* 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 :
42 : static inline size_t
43 : __libdw_max_len_leb128 (const size_t type_len,
44 : const unsigned char *addr, const unsigned char *end)
45 : {
46 460945 : const size_t pointer_len = likely (addr < end) ? end - addr : 0;
47 460945 : return likely (type_len <= pointer_len) ? type_len : pointer_len;
48 : }
49 :
50 : static inline size_t
51 : __libdw_max_len_uleb128 (const unsigned char *addr, const unsigned char *end)
52 : {
53 352627 : const size_t type_len = len_leb128 (uint64_t);
54 352627 : return __libdw_max_len_leb128 (type_len, addr, end);
55 : }
56 :
57 : static 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 108318 : const size_t type_len = len_leb128 (int64_t) - 1;
62 108318 : 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 :
73 : static inline uint64_t
74 168922976 : __libdw_get_uleb128 (const unsigned char **addrp, const unsigned char *end)
75 : {
76 168922976 : uint64_t acc = 0;
77 :
78 : /* Unroll the first step to help the compiler optimize
79 : for the common single-byte case. */
80 168922976 : get_uleb128_step (acc, *addrp, 0);
81 :
82 705254 : const size_t max = __libdw_max_len_uleb128 (*addrp - 1, end);
83 384675 : for (size_t i = 1; i < max; ++i)
84 384675 : 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 :
90 : /* Note, addr needs to me smaller than end. */
91 : #define get_uleb128(var, addr, end) ((var) = __libdw_get_uleb128 (&(addr), end))
92 :
93 : /* The signed case is similar, but we sign-extend the result. */
94 :
95 : #define get_sleb128_step(var, addr, nth) \
96 : do { \
97 : unsigned char __b = *(addr)++; \
98 : if (likely ((__b & 0x80) == 0)) \
99 : { \
100 : struct { signed int i:7; } __s = { .i = __b }; \
101 : (var) |= (typeof (var)) __s.i * ((typeof (var)) 1 << ((nth) * 7)); \
102 : return (var); \
103 : } \
104 : (var) |= (typeof (var)) (__b & 0x7f) << ((nth) * 7); \
105 : } while (0)
106 :
107 : static inline int64_t
108 271345 : __libdw_get_sleb128 (const unsigned char **addrp, const unsigned char *end)
109 : {
110 271345 : int64_t acc = 0;
111 :
112 : /* Unroll the first step to help the compiler optimize
113 : for the common single-byte case. */
114 271345 : get_sleb128_step (acc, *addrp, 0);
115 :
116 216636 : const size_t max = __libdw_max_len_sleb128 (*addrp - 1, end);
117 111894 : for (size_t i = 1; i < max; ++i)
118 111877 : get_sleb128_step (acc, *addrp, i);
119 : /* Other implementations set VALUE to INT_MAX in this
120 : case. So we better do this as well. */
121 : return INT64_MAX;
122 : }
123 :
124 : #define get_sleb128(var, addr, end) ((var) = __libdw_get_sleb128 (&(addr), end))
125 :
126 :
127 : /* We use simple memory access functions in case the hardware allows it.
128 : The caller has to make sure we don't have alias problems. */
129 : #if ALLOW_UNALIGNED
130 :
131 : # define read_2ubyte_unaligned(Dbg, Addr) \
132 : (unlikely ((Dbg)->other_byte_order) \
133 : ? bswap_16 (*((const uint16_t *) (Addr))) \
134 : : *((const uint16_t *) (Addr)))
135 : # define read_2sbyte_unaligned(Dbg, Addr) \
136 : (unlikely ((Dbg)->other_byte_order) \
137 : ? (int16_t) bswap_16 (*((const int16_t *) (Addr))) \
138 : : *((const int16_t *) (Addr)))
139 :
140 : # define read_4ubyte_unaligned_noncvt(Addr) \
141 : *((const uint32_t *) (Addr))
142 : # define read_4ubyte_unaligned(Dbg, Addr) \
143 : (unlikely ((Dbg)->other_byte_order) \
144 : ? bswap_32 (*((const uint32_t *) (Addr))) \
145 : : *((const uint32_t *) (Addr)))
146 : # define read_4sbyte_unaligned(Dbg, Addr) \
147 : (unlikely ((Dbg)->other_byte_order) \
148 : ? (int32_t) bswap_32 (*((const int32_t *) (Addr))) \
149 : : *((const int32_t *) (Addr)))
150 :
151 : # define read_8ubyte_unaligned_noncvt(Addr) \
152 : *((const uint64_t *) (Addr))
153 : # define read_8ubyte_unaligned(Dbg, Addr) \
154 : (unlikely ((Dbg)->other_byte_order) \
155 : ? bswap_64 (*((const uint64_t *) (Addr))) \
156 : : *((const uint64_t *) (Addr)))
157 : # define read_8sbyte_unaligned(Dbg, Addr) \
158 : (unlikely ((Dbg)->other_byte_order) \
159 : ? (int64_t) bswap_64 (*((const int64_t *) (Addr))) \
160 : : *((const int64_t *) (Addr)))
161 :
162 : #else
163 :
164 : union unaligned
165 : {
166 : void *p;
167 : uint16_t u2;
168 : uint32_t u4;
169 : uint64_t u8;
170 : int16_t s2;
171 : int32_t s4;
172 : int64_t s8;
173 : } __attribute__ ((packed));
174 :
175 : # define read_2ubyte_unaligned(Dbg, Addr) \
176 : read_2ubyte_unaligned_1 ((Dbg)->other_byte_order, (Addr))
177 : # define read_2sbyte_unaligned(Dbg, Addr) \
178 : read_2sbyte_unaligned_1 ((Dbg)->other_byte_order, (Addr))
179 : # define read_4ubyte_unaligned(Dbg, Addr) \
180 : read_4ubyte_unaligned_1 ((Dbg)->other_byte_order, (Addr))
181 : # define read_4sbyte_unaligned(Dbg, Addr) \
182 : read_4sbyte_unaligned_1 ((Dbg)->other_byte_order, (Addr))
183 : # define read_8ubyte_unaligned(Dbg, Addr) \
184 : read_8ubyte_unaligned_1 ((Dbg)->other_byte_order, (Addr))
185 : # define read_8sbyte_unaligned(Dbg, Addr) \
186 : read_8sbyte_unaligned_1 ((Dbg)->other_byte_order, (Addr))
187 :
188 : static inline uint16_t
189 : read_2ubyte_unaligned_1 (bool other_byte_order, const void *p)
190 : {
191 : const union unaligned *up = p;
192 : if (unlikely (other_byte_order))
193 : return bswap_16 (up->u2);
194 : return up->u2;
195 : }
196 : static inline int16_t
197 : read_2sbyte_unaligned_1 (bool other_byte_order, const void *p)
198 : {
199 : const union unaligned *up = p;
200 : if (unlikely (other_byte_order))
201 : return (int16_t) bswap_16 (up->u2);
202 : return up->s2;
203 : }
204 :
205 : static inline uint32_t
206 : read_4ubyte_unaligned_noncvt (const void *p)
207 : {
208 : const union unaligned *up = p;
209 : return up->u4;
210 : }
211 : static inline uint32_t
212 : read_4ubyte_unaligned_1 (bool other_byte_order, const void *p)
213 : {
214 : const union unaligned *up = p;
215 : if (unlikely (other_byte_order))
216 : return bswap_32 (up->u4);
217 : return up->u4;
218 : }
219 : static inline int32_t
220 : read_4sbyte_unaligned_1 (bool other_byte_order, const void *p)
221 : {
222 : const union unaligned *up = p;
223 : if (unlikely (other_byte_order))
224 : return (int32_t) bswap_32 (up->u4);
225 : return up->s4;
226 : }
227 :
228 : static inline uint64_t
229 : read_8ubyte_unaligned_noncvt (const void *p)
230 : {
231 : const union unaligned *up = p;
232 : return up->u8;
233 : }
234 : static inline uint64_t
235 : read_8ubyte_unaligned_1 (bool other_byte_order, const void *p)
236 : {
237 : const union unaligned *up = p;
238 : if (unlikely (other_byte_order))
239 : return bswap_64 (up->u8);
240 : return up->u8;
241 : }
242 : static inline int64_t
243 : read_8sbyte_unaligned_1 (bool other_byte_order, const void *p)
244 : {
245 : const union unaligned *up = p;
246 : if (unlikely (other_byte_order))
247 : return (int64_t) bswap_64 (up->u8);
248 : return up->s8;
249 : }
250 :
251 : #endif /* allow unaligned */
252 :
253 :
254 : #define read_2ubyte_unaligned_inc(Dbg, Addr) \
255 : ({ uint16_t t_ = read_2ubyte_unaligned (Dbg, Addr); \
256 : Addr = (__typeof (Addr)) (((uintptr_t) (Addr)) + 2); \
257 : t_; })
258 : #define read_2sbyte_unaligned_inc(Dbg, Addr) \
259 : ({ int16_t t_ = read_2sbyte_unaligned (Dbg, Addr); \
260 : Addr = (__typeof (Addr)) (((uintptr_t) (Addr)) + 2); \
261 : t_; })
262 :
263 : #define read_4ubyte_unaligned_inc(Dbg, Addr) \
264 : ({ uint32_t t_ = read_4ubyte_unaligned (Dbg, Addr); \
265 : Addr = (__typeof (Addr)) (((uintptr_t) (Addr)) + 4); \
266 : t_; })
267 : #define read_4sbyte_unaligned_inc(Dbg, Addr) \
268 : ({ int32_t t_ = read_4sbyte_unaligned (Dbg, Addr); \
269 : Addr = (__typeof (Addr)) (((uintptr_t) (Addr)) + 4); \
270 : t_; })
271 :
272 : #define read_8ubyte_unaligned_inc(Dbg, Addr) \
273 : ({ uint64_t t_ = read_8ubyte_unaligned (Dbg, Addr); \
274 : Addr = (__typeof (Addr)) (((uintptr_t) (Addr)) + 8); \
275 : t_; })
276 : #define read_8sbyte_unaligned_inc(Dbg, Addr) \
277 : ({ int64_t t_ = read_8sbyte_unaligned (Dbg, Addr); \
278 : Addr = (__typeof (Addr)) (((uintptr_t) (Addr)) + 8); \
279 : t_; })
280 :
281 :
282 : #define read_addr_unaligned_inc(Nbytes, Dbg, Addr) \
283 : (assert ((Nbytes) == 4 || (Nbytes) == 8), \
284 : ((Nbytes) == 4 ? read_4ubyte_unaligned_inc (Dbg, Addr) \
285 : : read_8ubyte_unaligned_inc (Dbg, Addr)))
286 :
287 : #endif /* memory-access.h */
|