1/** 2 * Copyright (c) 2021-2022 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16#ifndef LIBPANDAFILE_HELPERS_H 17#define LIBPANDAFILE_HELPERS_H 18 19#include "macros.h" 20#include "utils/bit_helpers.h" 21#include "utils/leb128.h" 22#include "utils/logger.h" 23#include "utils/span.h" 24 25#include <cstdint> 26#include <exception> 27#include <limits> 28#include <optional> 29 30namespace panda::panda_file::helpers { 31 32constexpr size_t UINT_BYTE2_SHIFT = 8U; 33constexpr size_t UINT_BYTE3_SHIFT = 16U; 34constexpr size_t UINT_BYTE4_SHIFT = 24U; 35constexpr const char *INVALID_SPAN_OFFSET = "Invalid span offset"; 36 37class FileAccessException : public std::exception { 38public: 39 explicit FileAccessException(const char *msg) : msg_(msg) {} 40 explicit FileAccessException(const std::string_view &msg) : msg_(msg) {} 41 const char *what() const noexcept override 42 { 43 return msg_.c_str(); 44 } 45 46private: 47 std::string msg_; 48}; 49 50#ifndef SUPPORT_KNOWN_EXCEPTION 51#define THROW_IF(cond, msg) \ 52 if (UNLIKELY(cond)) { \ 53 LOG(FATAL, PANDAFILE) << (msg); \ 54 } 55#else 56#define THROW_IF(cond, msg) \ 57 if (UNLIKELY(cond)) { \ 58 throw helpers::FileAccessException(msg); \ 59 } 60#endif 61 62template <size_t width> 63inline auto Read(Span<const uint8_t> *sp) 64{ 65 constexpr size_t BYTE_WIDTH = std::numeric_limits<uint8_t>::digits; 66 constexpr size_t BITWIDTH = BYTE_WIDTH * width; 67 using unsigned_type = panda::helpers::TypeHelperT<BITWIDTH, false>; 68 69 unsigned_type result = 0; 70 THROW_IF(sp->Size() < width, INVALID_SPAN_OFFSET); 71 72 for (size_t i = 0; i < width; i++) { 73 unsigned_type tmp = static_cast<unsigned_type>((*sp)[i]) << (i * BYTE_WIDTH); 74 result |= tmp; 75 } 76 *sp = sp->SubSpan(width); 77 return result; 78} 79 80template <> 81inline auto Read<sizeof(uint16_t)>(Span<const uint8_t> *sp) 82{ 83 uint16_t result = 0; 84 THROW_IF(sp->Size() < sizeof(uint16_t), INVALID_SPAN_OFFSET); 85 86 auto *p = sp->data(); 87 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) 88 result = *(p++); 89 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic, hicpp-signed-bitwise) 90 result |= static_cast<uint16_t>(*p) << UINT_BYTE2_SHIFT; 91 *sp = sp->SubSpan(sizeof(uint16_t)); 92 return result; 93} 94 95template <> 96inline auto Read<sizeof(uint32_t)>(Span<const uint8_t> *sp) 97{ 98 uint32_t result = 0; 99 THROW_IF(sp->Size() < sizeof(uint32_t), INVALID_SPAN_OFFSET); 100 101 auto *p = sp->data(); 102 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) 103 result = *(p++); 104 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) 105 result |= static_cast<uint32_t>(*(p++)) << UINT_BYTE2_SHIFT; 106 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) 107 result |= static_cast<uint32_t>(*(p++)) << UINT_BYTE3_SHIFT; 108 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) 109 result |= static_cast<uint32_t>(*p) << UINT_BYTE4_SHIFT; 110 *sp = sp->SubSpan(sizeof(uint32_t)); 111 return result; 112} 113 114template <size_t width> 115inline auto Read(Span<const uint8_t> sp) 116{ 117 return Read<width>(&sp); 118} 119 120inline uint32_t ReadULeb128(Span<const uint8_t> *sp) 121{ 122 THROW_IF(sp->Size() == 0U, INVALID_SPAN_OFFSET); 123 124 uint32_t result; 125 size_t n; 126 bool is_full; 127 std::tie(result, n, is_full) = leb128::DecodeUnsigned<uint32_t>(sp->data()); 128 THROW_IF(!is_full || sp->Size() < n, INVALID_SPAN_OFFSET); 129 *sp = sp->SubSpan(n); 130 return result; 131} 132 133inline void SkipULeb128(Span<const uint8_t> *sp) 134{ 135 if (sp->Size() > 0U && (*sp)[0U] <= leb128::PAYLOAD_MASK) { 136 *sp = sp->SubSpan(1U); 137 return; 138 } 139 140 if (sp->Size() > 1U && (*sp)[1U] <= leb128::PAYLOAD_MASK) { 141 *sp = sp->SubSpan(2U); 142 return; 143 } 144 145 if (sp->Size() > 2U && (*sp)[2U] <= leb128::PAYLOAD_MASK) { 146 *sp = sp->SubSpan(3U); 147 return; 148 } 149 150 if (sp->Size() > 3U && (*sp)[3U] <= leb128::PAYLOAD_MASK) { 151 *sp = sp->SubSpan(4U); 152 } 153} 154 155inline int32_t ReadLeb128(Span<const uint8_t> *sp) 156{ 157 uint32_t result; 158 size_t n; 159 bool is_full; 160 std::tie(result, n, is_full) = leb128::DecodeSigned<int32_t>(sp->data()); 161 THROW_IF(!is_full || sp->Size() < n, INVALID_SPAN_OFFSET); 162 *sp = sp->SubSpan(n); 163 return result; 164} 165 166template <size_t alignment> 167inline const uint8_t *Align(const uint8_t *ptr) 168{ 169 auto intptr = reinterpret_cast<uintptr_t>(ptr); 170 uintptr_t aligned = (intptr - 1U + alignment) & -alignment; 171 return reinterpret_cast<const uint8_t *>(aligned); 172} 173 174template <size_t alignment, class T> 175inline T Align(T n) 176{ 177 return (n - 1U + alignment) & -alignment; 178} 179 180template <class T, class E> 181inline std::optional<T> GetOptionalTaggedValue(Span<const uint8_t> sp, E tag, Span<const uint8_t> *next) 182{ 183 // NB! This is a workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80635 184 // which fails Release builds for GCC 8 and 9. 185 std::optional<T> novalue = {}; 186 if (UNLIKELY(sp.Size() == 0U)) { 187 return novalue; 188 } 189 190 if (sp[0] == static_cast<uint8_t>(tag)) { 191 sp = sp.SubSpan(1); 192 T value = static_cast<T>(Read<sizeof(T)>(&sp)); 193 *next = sp; 194 return value; 195 } 196 *next = sp; 197 198 return novalue; 199} 200 201template <class T, class E, class Callback> 202inline void EnumerateTaggedValues(Span<const uint8_t> sp, E tag, Callback cb, Span<const uint8_t> *next) 203{ 204 while (sp.Size() > 0U && sp[0] == static_cast<uint8_t>(tag)) { 205 sp = sp.SubSpan(1); 206 T value(Read<sizeof(T)>(&sp)); 207 cb(value); 208 } 209 210 if (next == nullptr) { 211 return; 212 } 213 214 *next = sp; 215} 216 217template <class T, class E, class Callback> 218inline bool EnumerateTaggedValuesWithEarlyStop(Span<const uint8_t> sp, E tag, Callback cb) 219{ 220 while (sp.Size() > 0U && sp[0] == static_cast<uint8_t>(tag)) { 221 sp = sp.SubSpan(1); 222 T value(Read<sizeof(T)>(&sp)); 223 if (cb(value)) { 224 return true; 225 } 226 } 227 228 return false; 229} 230 231} // namespace panda::panda_file::helpers 232 233#endif // LIBPANDAFILE_HELPERS_H 234