1// elfcpp_swap.h -- Handle swapping for elfcpp -*- C++ -*- 2 3// Copyright (C) 2006-2024 Free Software Foundation, Inc. 4// Written by Ian Lance Taylor <iant@google.com>. 5 6// This file is part of elfcpp. 7 8// This program is free software; you can redistribute it and/or 9// modify it under the terms of the GNU Library General Public License 10// as published by the Free Software Foundation; either version 2, or 11// (at your option) any later version. 12 13// In addition to the permissions in the GNU Library General Public 14// License, the Free Software Foundation gives you unlimited 15// permission to link the compiled version of this file into 16// combinations with other programs, and to distribute those 17// combinations without any restriction coming from the use of this 18// file. (The Library Public License restrictions do apply in other 19// respects; for example, they cover modification of the file, and 20/// distribution when not linked into a combined executable.) 21 22// This program is distributed in the hope that it will be useful, but 23// WITHOUT ANY WARRANTY; without even the implied warranty of 24// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 25// Library General Public License for more details. 26 27// You should have received a copy of the GNU Library General Public 28// License along with this program; if not, write to the Free Software 29// Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 30// 02110-1301, USA. 31 32// This header file defines basic template classes to efficiently swap 33// numbers between host form and target form. When the host and 34// target have the same endianness, these turn into no-ops. 35 36#ifndef ELFCPP_SWAP_H 37#define ELFCPP_SWAP_H 38 39#include <stdint.h> 40 41// We need an autoconf-generated config.h file for endianness and 42// swapping. We check two macros: WORDS_BIGENDIAN and 43// HAVE_BYTESWAP_H. 44 45#include "config.h" 46 47#ifdef HAVE_BYTESWAP_H 48#include <byteswap.h> 49#endif // defined(HAVE_BYTESWAP_H) 50 51// Provide our own versions of the byteswap functions. 52#if !HAVE_DECL_BSWAP_16 53static inline uint16_t 54bswap_16(uint16_t v) 55{ 56 return ((v >> 8) & 0xff) | ((v & 0xff) << 8); 57} 58#endif // !HAVE_DECL_BSWAP16 59 60#if !HAVE_DECL_BSWAP_32 61static inline uint32_t 62bswap_32(uint32_t v) 63{ 64 return ( ((v & 0xff000000) >> 24) 65 | ((v & 0x00ff0000) >> 8) 66 | ((v & 0x0000ff00) << 8) 67 | ((v & 0x000000ff) << 24)); 68} 69#endif // !HAVE_DECL_BSWAP32 70 71#if !HAVE_DECL_BSWAP_64 72static inline uint64_t 73bswap_64(uint64_t v) 74{ 75 return ( ((v & 0xff00000000000000ULL) >> 56) 76 | ((v & 0x00ff000000000000ULL) >> 40) 77 | ((v & 0x0000ff0000000000ULL) >> 24) 78 | ((v & 0x000000ff00000000ULL) >> 8) 79 | ((v & 0x00000000ff000000ULL) << 8) 80 | ((v & 0x0000000000ff0000ULL) << 24) 81 | ((v & 0x000000000000ff00ULL) << 40) 82 | ((v & 0x00000000000000ffULL) << 56)); 83} 84#endif // !HAVE_DECL_BSWAP64 85 86// gcc 4.3 and later provides __builtin_bswap32 and __builtin_bswap64. 87 88#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) 89#undef bswap_32 90#define bswap_32 __builtin_bswap32 91#undef bswap_64 92#define bswap_64 __builtin_bswap64 93#endif 94 95namespace elfcpp 96{ 97 98// Endian simply indicates whether the host is big endian or not. 99 100struct Endian 101{ 102 public: 103 // Used for template specializations. 104 static const bool host_big_endian = 105#ifdef WORDS_BIGENDIAN 106 true 107#else 108 false 109#endif 110 ; 111}; 112 113// Valtype_base is a template based on size (8, 16, 32, 64) which 114// defines the type Valtype as the unsigned integer, and 115// Signed_valtype as the signed integer, of the specified size. 116 117template<int size> 118struct Valtype_base; 119 120template<> 121struct Valtype_base<8> 122{ 123 typedef uint8_t Valtype; 124 typedef int8_t Signed_valtype; 125}; 126 127template<> 128struct Valtype_base<16> 129{ 130 typedef uint16_t Valtype; 131 typedef int16_t Signed_valtype; 132}; 133 134template<> 135struct Valtype_base<32> 136{ 137 typedef uint32_t Valtype; 138 typedef int32_t Signed_valtype; 139}; 140 141template<> 142struct Valtype_base<64> 143{ 144 typedef uint64_t Valtype; 145 typedef int64_t Signed_valtype; 146}; 147 148// Convert_endian is a template based on size and on whether the host 149// and target have the same endianness. It defines the type Valtype 150// as Valtype_base does, and also defines a function convert_host 151// which takes an argument of type Valtype and returns the same value, 152// but swapped if the host and target have different endianness. 153 154template<int size, bool same_endian> 155struct Convert_endian; 156 157template<int size> 158struct Convert_endian<size, true> 159{ 160 typedef typename Valtype_base<size>::Valtype Valtype; 161 162 static inline Valtype 163 convert_host(Valtype v) 164 { return v; } 165}; 166 167template<> 168struct Convert_endian<8, false> 169{ 170 typedef Valtype_base<8>::Valtype Valtype; 171 172 static inline Valtype 173 convert_host(Valtype v) 174 { return v; } 175}; 176 177template<> 178struct Convert_endian<16, false> 179{ 180 typedef Valtype_base<16>::Valtype Valtype; 181 182 static inline Valtype 183 convert_host(Valtype v) 184 { return bswap_16(v); } 185}; 186 187template<> 188struct Convert_endian<32, false> 189{ 190 typedef Valtype_base<32>::Valtype Valtype; 191 192 static inline Valtype 193 convert_host(Valtype v) 194 { return bswap_32(v); } 195}; 196 197template<> 198struct Convert_endian<64, false> 199{ 200 typedef Valtype_base<64>::Valtype Valtype; 201 202 static inline Valtype 203 convert_host(Valtype v) 204 { return bswap_64(v); } 205}; 206 207// Convert is a template based on size and on whether the target is 208// big endian. It defines Valtype and convert_host like 209// Convert_endian. That is, it is just like Convert_endian except in 210// the meaning of the second template parameter. 211 212template<int size, bool big_endian> 213struct Convert 214{ 215 typedef typename Valtype_base<size>::Valtype Valtype; 216 217 static inline Valtype 218 convert_host(Valtype v) 219 { 220 return Convert_endian<size, big_endian == Endian::host_big_endian> 221 ::convert_host(v); 222 } 223}; 224 225// Swap is a template based on size and on whether the target is big 226// endian. It defines the type Valtype and the functions readval and 227// writeval. The functions read and write values of the appropriate 228// size out of buffers, swapping them if necessary. readval and 229// writeval are overloaded to take pointers to the appropriate type or 230// pointers to unsigned char. 231 232template<int size, bool big_endian> 233struct Swap 234{ 235 typedef typename Valtype_base<size>::Valtype Valtype; 236 237 static inline Valtype 238 readval(const Valtype* wv) 239 { return Convert<size, big_endian>::convert_host(*wv); } 240 241 static inline void 242 writeval(Valtype* wv, Valtype v) 243 { *wv = Convert<size, big_endian>::convert_host(v); } 244 245 static inline Valtype 246 readval(const unsigned char* wv) 247 { return readval(reinterpret_cast<const Valtype*>(wv)); } 248 249 static inline void 250 writeval(unsigned char* wv, Valtype v) 251 { writeval(reinterpret_cast<Valtype*>(wv), v); } 252}; 253 254// We need to specialize the 8-bit version of Swap to avoid 255// conflicting overloads, since both versions of readval and writeval 256// will have the same type parameters. 257 258template<bool big_endian> 259struct Swap<8, big_endian> 260{ 261 typedef typename Valtype_base<8>::Valtype Valtype; 262 263 static inline Valtype 264 readval(const Valtype* wv) 265 { return *wv; } 266 267 static inline void 268 writeval(Valtype* wv, Valtype v) 269 { *wv = v; } 270}; 271 272// Swap_unaligned is a template based on size and on whether the 273// target is big endian. It defines the type Valtype and the 274// functions readval and writeval. The functions read and write 275// values of the appropriate size out of buffers which may be 276// misaligned. 277 278template<int size, bool big_endian> 279struct Swap_unaligned; 280 281template<bool big_endian> 282struct Swap_unaligned<8, big_endian> 283{ 284 typedef typename Valtype_base<8>::Valtype Valtype; 285 286 static inline Valtype 287 readval(const unsigned char* wv) 288 { return *wv; } 289 290 static inline void 291 writeval(unsigned char* wv, Valtype v) 292 { *wv = v; } 293}; 294 295template<> 296struct Swap_unaligned<16, false> 297{ 298 typedef Valtype_base<16>::Valtype Valtype; 299 300 static inline Valtype 301 readval(const unsigned char* wv) 302 { 303 return (wv[1] << 8) | wv[0]; 304 } 305 306 static inline void 307 writeval(unsigned char* wv, Valtype v) 308 { 309 wv[1] = v >> 8; 310 wv[0] = v; 311 } 312}; 313 314template<> 315struct Swap_unaligned<16, true> 316{ 317 typedef Valtype_base<16>::Valtype Valtype; 318 319 static inline Valtype 320 readval(const unsigned char* wv) 321 { 322 return (wv[0] << 8) | wv[1]; 323 } 324 325 static inline void 326 writeval(unsigned char* wv, Valtype v) 327 { 328 wv[0] = v >> 8; 329 wv[1] = v; 330 } 331}; 332 333template<> 334struct Swap_unaligned<32, false> 335{ 336 typedef Valtype_base<32>::Valtype Valtype; 337 338 static inline Valtype 339 readval(const unsigned char* wv) 340 { 341 return (wv[3] << 24) | (wv[2] << 16) | (wv[1] << 8) | wv[0]; 342 } 343 344 static inline void 345 writeval(unsigned char* wv, Valtype v) 346 { 347 wv[3] = v >> 24; 348 wv[2] = v >> 16; 349 wv[1] = v >> 8; 350 wv[0] = v; 351 } 352}; 353 354template<> 355struct Swap_unaligned<32, true> 356{ 357 typedef Valtype_base<32>::Valtype Valtype; 358 359 static inline Valtype 360 readval(const unsigned char* wv) 361 { 362 return (wv[0] << 24) | (wv[1] << 16) | (wv[2] << 8) | wv[3]; 363 } 364 365 static inline void 366 writeval(unsigned char* wv, Valtype v) 367 { 368 wv[0] = v >> 24; 369 wv[1] = v >> 16; 370 wv[2] = v >> 8; 371 wv[3] = v; 372 } 373}; 374 375template<> 376struct Swap_unaligned<64, false> 377{ 378 typedef Valtype_base<64>::Valtype Valtype; 379 380 static inline Valtype 381 readval(const unsigned char* wv) 382 { 383 return ((static_cast<Valtype>(wv[7]) << 56) 384 | (static_cast<Valtype>(wv[6]) << 48) 385 | (static_cast<Valtype>(wv[5]) << 40) 386 | (static_cast<Valtype>(wv[4]) << 32) 387 | (static_cast<Valtype>(wv[3]) << 24) 388 | (static_cast<Valtype>(wv[2]) << 16) 389 | (static_cast<Valtype>(wv[1]) << 8) 390 | static_cast<Valtype>(wv[0])); 391 } 392 393 static inline void 394 writeval(unsigned char* wv, Valtype v) 395 { 396 wv[7] = v >> 56; 397 wv[6] = v >> 48; 398 wv[5] = v >> 40; 399 wv[4] = v >> 32; 400 wv[3] = v >> 24; 401 wv[2] = v >> 16; 402 wv[1] = v >> 8; 403 wv[0] = v; 404 } 405}; 406 407template<> 408struct Swap_unaligned<64, true> 409{ 410 typedef Valtype_base<64>::Valtype Valtype; 411 412 static inline Valtype 413 readval(const unsigned char* wv) 414 { 415 return ((static_cast<Valtype>(wv[0]) << 56) 416 | (static_cast<Valtype>(wv[1]) << 48) 417 | (static_cast<Valtype>(wv[2]) << 40) 418 | (static_cast<Valtype>(wv[3]) << 32) 419 | (static_cast<Valtype>(wv[4]) << 24) 420 | (static_cast<Valtype>(wv[5]) << 16) 421 | (static_cast<Valtype>(wv[6]) << 8) 422 | static_cast<Valtype>(wv[7])); 423 } 424 425 static inline void 426 writeval(unsigned char* wv, Valtype v) 427 { 428 wv[0] = v >> 56; 429 wv[1] = v >> 48; 430 wv[2] = v >> 40; 431 wv[3] = v >> 32; 432 wv[4] = v >> 24; 433 wv[5] = v >> 16; 434 wv[6] = v >> 8; 435 wv[7] = v; 436 } 437}; 438 439// Swap_aligned32 is a template based on size and on whether the 440// target is big endian. It defines the type Valtype and the 441// functions readval and writeval. The functions read and write 442// values of the appropriate size out of buffers which may not be 443// 64-bit aligned, but are 32-bit aligned. 444 445template<int size, bool big_endian> 446struct Swap_aligned32 447{ 448 typedef typename Valtype_base<size>::Valtype Valtype; 449 450 static inline Valtype 451 readval(const unsigned char* wv) 452 { return Swap<size, big_endian>::readval( 453 reinterpret_cast<const Valtype*>(wv)); } 454 455 static inline void 456 writeval(unsigned char* wv, Valtype v) 457 { Swap<size, big_endian>::writeval(reinterpret_cast<Valtype*>(wv), v); } 458}; 459 460template<> 461struct Swap_aligned32<64, true> 462{ 463 typedef Valtype_base<64>::Valtype Valtype; 464 465 static inline Valtype 466 readval(const unsigned char* wv) 467 { 468 return ((static_cast<Valtype>(Swap<32, true>::readval(wv)) << 32) 469 | static_cast<Valtype>(Swap<32, true>::readval(wv + 4))); 470 } 471 472 static inline void 473 writeval(unsigned char* wv, Valtype v) 474 { 475 typedef Valtype_base<32>::Valtype Valtype32; 476 477 Swap<32, true>::writeval(wv, static_cast<Valtype32>(v >> 32)); 478 Swap<32, true>::writeval(wv + 4, static_cast<Valtype32>(v)); 479 } 480}; 481 482template<> 483struct Swap_aligned32<64, false> 484{ 485 typedef Valtype_base<64>::Valtype Valtype; 486 487 static inline Valtype 488 readval(const unsigned char* wv) 489 { 490 return ((static_cast<Valtype>(Swap<32, false>::readval(wv + 4)) << 32) 491 | static_cast<Valtype>(Swap<32, false>::readval(wv))); 492 } 493 494 static inline void 495 writeval(unsigned char* wv, Valtype v) 496 { 497 typedef Valtype_base<32>::Valtype Valtype32; 498 499 Swap<32, false>::writeval(wv + 4, static_cast<Valtype32>(v >> 32)); 500 Swap<32, false>::writeval(wv, static_cast<Valtype32>(v)); 501 } 502}; 503 504} // End namespace elfcpp. 505 506#endif // !defined(ELFCPP_SWAP_H) 507