/* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ART_LIBDEXFILE_DEX_HIDDEN_API_ACCESS_FLAGS_H_ #define ART_LIBDEXFILE_DEX_HIDDEN_API_ACCESS_FLAGS_H_ #include "base/bit_utils.h" #include "base/macros.h" #include "dex/modifiers.h" namespace art { /* This class is used for encoding and decoding access flags of class members * from the boot class path. These access flags might contain additional two bits * of information on whether the given class member should be hidden from apps * and under what circumstances. * * The encoding is different inside DexFile, where we are concerned with size, * and at runtime where we want to optimize for speed of access. The class * provides helper functions to decode/encode both of them. * * Encoding in DexFile * =================== * * First bit is encoded as inversion of visibility flags (public/private/protected). * At most one can be set for any given class member. If two or three are set, * this is interpreted as the first bit being set and actual visibility flags * being the complement of the encoded flags. * * Second bit is either encoded as bit 5 for fields and non-native methods, where * it carries no other meaning. If a method is native (bit 8 set), bit 9 is used. * * Bits were selected so that they never increase the length of unsigned LEB-128 * encoding of the access flags. * * Encoding at runtime * =================== * * Two bits are set aside in the uint32_t access flags in the intrinsics ordinal * space (thus intrinsics need to be special-cased). These are two consecutive * bits and they are directly used to store the integer value of the ApiList * enum values. * */ class HiddenApiAccessFlags { public: enum ApiList { kWhitelist = 0, kLightGreylist, kDarkGreylist, kBlacklist, }; static ALWAYS_INLINE ApiList DecodeFromDex(uint32_t dex_access_flags) { DexHiddenAccessFlags flags(dex_access_flags); uint32_t int_value = (flags.IsFirstBitSet() ? 1 : 0) + (flags.IsSecondBitSet() ? 2 : 0); return static_cast(int_value); } static ALWAYS_INLINE uint32_t RemoveFromDex(uint32_t dex_access_flags) { DexHiddenAccessFlags flags(dex_access_flags); flags.SetFirstBit(false); flags.SetSecondBit(false); return flags.GetEncoding(); } static ALWAYS_INLINE uint32_t EncodeForDex(uint32_t dex_access_flags, ApiList value) { DexHiddenAccessFlags flags(RemoveFromDex(dex_access_flags)); uint32_t int_value = static_cast(value); flags.SetFirstBit((int_value & 1) != 0); flags.SetSecondBit((int_value & 2) != 0); return flags.GetEncoding(); } static ALWAYS_INLINE ApiList DecodeFromRuntime(uint32_t runtime_access_flags) { // This is used in the fast path, only DCHECK here. DCHECK_EQ(runtime_access_flags & kAccIntrinsic, 0u); uint32_t int_value = (runtime_access_flags & kAccHiddenApiBits) >> kAccFlagsShift; return static_cast(int_value); } static ALWAYS_INLINE uint32_t EncodeForRuntime(uint32_t runtime_access_flags, ApiList value) { CHECK_EQ(runtime_access_flags & kAccIntrinsic, 0u); uint32_t hidden_api_flags = static_cast(value) << kAccFlagsShift; CHECK_EQ(hidden_api_flags & ~kAccHiddenApiBits, 0u); runtime_access_flags &= ~kAccHiddenApiBits; return runtime_access_flags | hidden_api_flags; } private: static const int kAccFlagsShift = CTZ(kAccHiddenApiBits); static_assert(IsPowerOfTwo((kAccHiddenApiBits >> kAccFlagsShift) + 1), "kAccHiddenApiBits are not continuous"); struct DexHiddenAccessFlags { explicit DexHiddenAccessFlags(uint32_t access_flags) : access_flags_(access_flags) {} ALWAYS_INLINE uint32_t GetSecondFlag() { return ((access_flags_ & kAccNative) != 0) ? kAccDexHiddenBitNative : kAccDexHiddenBit; } ALWAYS_INLINE bool IsFirstBitSet() { static_assert(IsPowerOfTwo(0u), "Following statement checks if *at most* one bit is set"); return !IsPowerOfTwo(access_flags_ & kAccVisibilityFlags); } ALWAYS_INLINE void SetFirstBit(bool value) { if (IsFirstBitSet() != value) { access_flags_ ^= kAccVisibilityFlags; } } ALWAYS_INLINE bool IsSecondBitSet() { return (access_flags_ & GetSecondFlag()) != 0; } ALWAYS_INLINE void SetSecondBit(bool value) { if (value) { access_flags_ |= GetSecondFlag(); } else { access_flags_ &= ~GetSecondFlag(); } } ALWAYS_INLINE uint32_t GetEncoding() const { return access_flags_; } uint32_t access_flags_; }; }; inline std::ostream& operator<<(std::ostream& os, HiddenApiAccessFlags::ApiList value) { switch (value) { case HiddenApiAccessFlags::kWhitelist: os << "whitelist"; break; case HiddenApiAccessFlags::kLightGreylist: os << "light greylist"; break; case HiddenApiAccessFlags::kDarkGreylist: os << "dark greylist"; break; case HiddenApiAccessFlags::kBlacklist: os << "blacklist"; break; } return os; } } // namespace art #endif // ART_LIBDEXFILE_DEX_HIDDEN_API_ACCESS_FLAGS_H_