1.Compile with cmake 2.Support for fixing codeItem 3.Optimisation de l'espace de stockagepull/16/head
parent
095ad946e2
commit
27660d7c67
@ -0,0 +1,66 @@ |
|||||||
|
# For more information about using CMake with Android Studio, read the |
||||||
|
# documentation: https://d.android.com/studio/projects/add-native-code.html |
||||||
|
|
||||||
|
# Sets the minimum version of CMake required to build the native library. |
||||||
|
|
||||||
|
cmake_minimum_required(VERSION 3.4.1) |
||||||
|
|
||||||
|
# Creates and names a library, sets it as either STATIC |
||||||
|
# or SHARED, and provides the relative paths to its source code. |
||||||
|
# You can define multiple libraries, and CMake builds them for you. |
||||||
|
# Gradle automatically packages shared libraries with your APK. |
||||||
|
include_directories( dex ) |
||||||
|
include_directories( base ) |
||||||
|
include_directories( ./ ) |
||||||
|
|
||||||
|
aux_source_directory(./ SRC7) |
||||||
|
aux_source_directory(xhook SRC6) |
||||||
|
aux_source_directory(ziparchive SRC1) |
||||||
|
aux_source_directory(dex SRC2) |
||||||
|
aux_source_directory(android-base SRC3) |
||||||
|
aux_source_directory(utils SRC4) |
||||||
|
aux_source_directory(hook SRC5) |
||||||
|
aux_source_directory(jnihook SRC6) |
||||||
|
add_compile_options(-w) |
||||||
|
add_library( # Sets the name of the library. |
||||||
|
blackdex |
||||||
|
|
||||||
|
# Sets the library as a shared library. |
||||||
|
SHARED |
||||||
|
|
||||||
|
# Provides a relative path to your source file(s). |
||||||
|
VmCore.cpp |
||||||
|
${SRC3} |
||||||
|
${SRC1} |
||||||
|
${SRC2} |
||||||
|
${SRC4} |
||||||
|
${SRC5} |
||||||
|
${SRC6} |
||||||
|
${SRC7} |
||||||
|
) |
||||||
|
|
||||||
|
# Searches for a specified prebuilt library and stores the path as a |
||||||
|
# variable. Because CMake includes system libraries in the search path by |
||||||
|
# default, you only need to specify the name of the public NDK library |
||||||
|
# you want to add. CMake verifies that the library exists before |
||||||
|
# completing its build. |
||||||
|
|
||||||
|
find_library( # Sets the name of the path variable. |
||||||
|
log-lib |
||||||
|
|
||||||
|
# Specifies the name of the NDK library that |
||||||
|
# you want CMake to locate. |
||||||
|
log ) |
||||||
|
|
||||||
|
# Specifies libraries CMake should link to your target library. You |
||||||
|
# can link multiple libraries, such as libraries you define in this |
||||||
|
# build script, prebuilt third-party libraries, or system libraries. |
||||||
|
|
||||||
|
target_link_libraries( # Specifies the target library. |
||||||
|
blackdex |
||||||
|
|
||||||
|
# Links the target library to the log library |
||||||
|
# included in the NDK. |
||||||
|
${log-lib} |
||||||
|
z |
||||||
|
) |
@ -0,0 +1,174 @@ |
|||||||
|
//
|
||||||
|
// Created by Milk on 2021/5/16.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "DexDump.h" |
||||||
|
#include "utils/HexDump.h" |
||||||
|
#include "utils/Log.h" |
||||||
|
#include "VmCore.h" |
||||||
|
#include "utils/PointerCheck.h" |
||||||
|
#include "jnihook/Art.h" |
||||||
|
#include "jnihook/ArtM.h" |
||||||
|
|
||||||
|
#include <cstdio> |
||||||
|
#include <fcntl.h> |
||||||
|
#include <cstring> |
||||||
|
#include <cstdlib> |
||||||
|
#include <map> |
||||||
|
#include "unistd.h" |
||||||
|
|
||||||
|
#include "dex/dex_file.h" |
||||||
|
#include "dex/dex_file_loader.h" |
||||||
|
#include "jnihook/JniHook.h" |
||||||
|
#include "xhook/xhook.h" |
||||||
|
|
||||||
|
using namespace std; |
||||||
|
static int beginOffset = -2; |
||||||
|
|
||||||
|
HOOK_JNI(int, kill, pid_t __pid, int __signal) { |
||||||
|
ALOGE("hooked so kill"); |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
HOOK_JNI(int, killpg, int __pgrp, int __signal) { |
||||||
|
ALOGE("hooked so killpg"); |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
void init(JNIEnv *env) { |
||||||
|
const char *soName = ".*\\.so$"; |
||||||
|
xhook_register(soName, "kill", (void *) new_kill, |
||||||
|
(void **) (&orig_kill)); |
||||||
|
xhook_register(soName, "killpg", (void *) new_killpg, |
||||||
|
(void **) (&orig_killpg)); |
||||||
|
xhook_refresh(0); |
||||||
|
|
||||||
|
jlongArray emptyCookie = VmCore::loadEmptyDex(env); |
||||||
|
jsize arrSize = env->GetArrayLength(emptyCookie); |
||||||
|
if (env->ExceptionCheck() == JNI_TRUE) { |
||||||
|
return; |
||||||
|
} |
||||||
|
jlong *long_data = env->GetLongArrayElements(emptyCookie, nullptr); |
||||||
|
|
||||||
|
for (int i = 0; i < arrSize; ++i) { |
||||||
|
jlong cookie = long_data[i]; |
||||||
|
if (cookie == 0) { |
||||||
|
continue; |
||||||
|
} |
||||||
|
auto dex = reinterpret_cast<char *>(cookie); |
||||||
|
for (int ii = 0; ii < 10; ++ii) { |
||||||
|
auto value = *(size_t *) (dex + ii * sizeof(size_t)); |
||||||
|
if (value == 1872) { |
||||||
|
beginOffset = ii - 1; |
||||||
|
// auto dexBegin = *(size_t *) (dex + beginOffset * sizeof(size_t));
|
||||||
|
// HexDump(reinterpret_cast<char *>(dexBegin), 10, 0);
|
||||||
|
env->ReleaseLongArrayElements(emptyCookie, long_data, 0); |
||||||
|
return; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
env->ReleaseLongArrayElements(emptyCookie, long_data, 0); |
||||||
|
beginOffset = -1; |
||||||
|
} |
||||||
|
|
||||||
|
void fixCodeItem(JNIEnv *env, const art_lkchan::DexFile *dex_file_, size_t begin) { |
||||||
|
for (size_t classdef_ctr = 0;classdef_ctr < dex_file_->NumClassDefs(); ++classdef_ctr) { |
||||||
|
const art_lkchan::DexFile::ClassDef &cd = dex_file_->GetClassDef(classdef_ctr); |
||||||
|
const uint8_t *class_data = dex_file_->GetClassData(cd); |
||||||
|
auto &classTypeId = dex_file_->GetTypeId(cd.class_idx_); |
||||||
|
std::string class_name = dex_file_->GetTypeDescriptor(classTypeId); |
||||||
|
|
||||||
|
if (class_data != nullptr) { |
||||||
|
art_lkchan::ClassDataItemIterator cdit(*dex_file_, class_data); |
||||||
|
cdit.SkipAllFields(); |
||||||
|
while (cdit.HasNextMethod()) { |
||||||
|
if (cdit.GetMemberIndex() > dex_file_->NumMethodIds()) |
||||||
|
continue; |
||||||
|
const art_lkchan::DexFile::MethodId &method_id_item = dex_file_->GetMethodId( |
||||||
|
cdit.GetMemberIndex()); |
||||||
|
auto method_name = dex_file_->GetMethodName(method_id_item); |
||||||
|
auto method_signature = dex_file_->GetMethodSignature( |
||||||
|
method_id_item).ToString().c_str(); |
||||||
|
auto java_method = VmCore::findMethod(env, class_name.c_str(), method_name, |
||||||
|
method_signature); |
||||||
|
if (java_method) { |
||||||
|
auto artMethod = ArtM::GetArtMethod(env, java_method); |
||||||
|
const art_lkchan::DexFile::CodeItem *orig_code_item = cdit.GetMethodCodeItem(); |
||||||
|
if (cdit.GetMethodCodeItemOffset() && orig_code_item) { |
||||||
|
auto codeItemSize = dex_file_->GetCodeItemSize(*orig_code_item); |
||||||
|
auto new_code_item = |
||||||
|
begin + ArtM::GetArtMethodDexCodeItemOffset(artMethod); |
||||||
|
memcpy((void *) orig_code_item, |
||||||
|
(void *) new_code_item, |
||||||
|
codeItemSize); |
||||||
|
} |
||||||
|
} else { |
||||||
|
env->ExceptionClear(); |
||||||
|
} |
||||||
|
cdit.Next(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void DexDump::dumpDex(JNIEnv *env, jlong cookie, jstring dir, jboolean fix) { |
||||||
|
if (beginOffset == -2) { |
||||||
|
init(env); |
||||||
|
} |
||||||
|
if (beginOffset == -1) { |
||||||
|
ALOGD("dumpDex not support!"); |
||||||
|
return; |
||||||
|
} |
||||||
|
char magic[8] = {0x64, 0x65, 0x78, 0x0a, 0x30, 0x33, 0x35, 0x00}; |
||||||
|
auto base = reinterpret_cast<char *>(cookie); |
||||||
|
auto begin = *(size_t *) (base + beginOffset * sizeof(size_t)); |
||||||
|
if (!PointerCheck::check(reinterpret_cast<void *>(begin))) { |
||||||
|
return; |
||||||
|
} |
||||||
|
auto dirC = env->GetStringUTFChars(dir, 0); |
||||||
|
|
||||||
|
auto dexSizeOffset = ((unsigned long) begin) + 0x20; |
||||||
|
int size = *(size_t *) dexSizeOffset; |
||||||
|
|
||||||
|
void *buffer = malloc(size); |
||||||
|
if (buffer) { |
||||||
|
memcpy(buffer, reinterpret_cast<const void *>(begin), size); |
||||||
|
// fix magic
|
||||||
|
memcpy(buffer, magic, sizeof(magic)); |
||||||
|
|
||||||
|
const bool kVerifyChecksum = false; |
||||||
|
const bool kVerify = true; |
||||||
|
const art_lkchan::DexFileLoader dex_file_loader; |
||||||
|
std::string error_msg; |
||||||
|
std::vector<std::unique_ptr<const art_lkchan::DexFile>> dex_files; |
||||||
|
if (!dex_file_loader.OpenAll(reinterpret_cast<const uint8_t *>(buffer), |
||||||
|
size, |
||||||
|
"", |
||||||
|
kVerify, |
||||||
|
kVerifyChecksum, |
||||||
|
&error_msg, |
||||||
|
&dex_files)) { |
||||||
|
// Display returned error message to user. Note that this error behavior
|
||||||
|
// differs from the error messages shown by the original Dalvik dexdump.
|
||||||
|
ALOGE("Open dex error %s", error_msg.c_str()); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
if (fix) { |
||||||
|
fixCodeItem(env, dex_files[0].get(), begin); |
||||||
|
} |
||||||
|
char path[1024]; |
||||||
|
sprintf(path, "%s/dex_%d.dex", dirC, size); |
||||||
|
auto fd = open(path, O_CREAT | O_WRONLY, 0600); |
||||||
|
ssize_t w = write(fd, buffer, size); |
||||||
|
fsync(fd); |
||||||
|
if (w > 0) { |
||||||
|
ALOGE("dump dex ======> %s", path); |
||||||
|
} else { |
||||||
|
remove(path); |
||||||
|
} |
||||||
|
close(fd); |
||||||
|
free(buffer); |
||||||
|
env->ReleaseStringUTFChars(dir, dirC); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,345 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2015 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. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include "android-base/file.h" |
||||||
|
|
||||||
|
#include <errno.h> |
||||||
|
#include <fcntl.h> |
||||||
|
#include <libgen.h> |
||||||
|
#include <sys/stat.h> |
||||||
|
#include <sys/types.h> |
||||||
|
#include <unistd.h> |
||||||
|
|
||||||
|
#include <memory> |
||||||
|
#include <mutex> |
||||||
|
#include <string> |
||||||
|
#include <vector> |
||||||
|
|
||||||
|
#include "android-base/logging.h" |
||||||
|
#include "android-base/macros.h" // For TEMP_FAILURE_RETRY on Darwin. |
||||||
|
#include "android-base/unique_fd.h" |
||||||
|
#include "android-base/utf8.h" |
||||||
|
|
||||||
|
#if defined(__APPLE__) |
||||||
|
#include <mach-o/dyld.h> |
||||||
|
#endif |
||||||
|
#if defined(_WIN32) |
||||||
|
#include <windows.h> |
||||||
|
#define O_CLOEXEC O_NOINHERIT |
||||||
|
#define O_NOFOLLOW 0 |
||||||
|
#endif |
||||||
|
|
||||||
|
namespace android_lkchan { |
||||||
|
namespace base { |
||||||
|
|
||||||
|
// Versions of standard library APIs that support UTF-8 strings.
|
||||||
|
using namespace android_lkchan::base::utf8; |
||||||
|
|
||||||
|
bool ReadFdToString(int fd, std::string* content) { |
||||||
|
content->clear(); |
||||||
|
|
||||||
|
// Although original we had small files in mind, this code gets used for
|
||||||
|
// very large files too, where the std::string growth heuristics might not
|
||||||
|
// be suitable. https://code.google.com/p/android/issues/detail?id=258500.
|
||||||
|
struct stat sb; |
||||||
|
if (fstat(fd, &sb) != -1 && sb.st_size > 0) { |
||||||
|
content->reserve(sb.st_size); |
||||||
|
} |
||||||
|
|
||||||
|
char buf[BUFSIZ]; |
||||||
|
ssize_t n; |
||||||
|
while ((n = TEMP_FAILURE_RETRY(read(fd, &buf[0], sizeof(buf)))) > 0) { |
||||||
|
content->append(buf, n); |
||||||
|
} |
||||||
|
return (n == 0) ? true : false; |
||||||
|
} |
||||||
|
|
||||||
|
bool ReadFileToString(const std::string& path, std::string* content, bool follow_symlinks) { |
||||||
|
content->clear(); |
||||||
|
|
||||||
|
int flags = O_RDONLY | O_CLOEXEC | O_BINARY | (follow_symlinks ? 0 : O_NOFOLLOW); |
||||||
|
android_lkchan::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), flags))); |
||||||
|
if (fd == -1) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
return ReadFdToString(fd, content); |
||||||
|
} |
||||||
|
|
||||||
|
bool WriteStringToFd(const std::string& content, int fd) { |
||||||
|
const char* p = content.data(); |
||||||
|
size_t left = content.size(); |
||||||
|
while (left > 0) { |
||||||
|
ssize_t n = TEMP_FAILURE_RETRY(write(fd, p, left)); |
||||||
|
if (n == -1) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
p += n; |
||||||
|
left -= n; |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
static bool CleanUpAfterFailedWrite(const std::string& path) { |
||||||
|
// Something went wrong. Let's not leave a corrupt file lying around.
|
||||||
|
int saved_errno = errno; |
||||||
|
unlink(path.c_str()); |
||||||
|
errno = saved_errno; |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
#if !defined(_WIN32) |
||||||
|
bool WriteStringToFile(const std::string& content, const std::string& path, |
||||||
|
mode_t mode, uid_t owner, gid_t group, |
||||||
|
bool follow_symlinks) { |
||||||
|
int flags = O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_BINARY | |
||||||
|
(follow_symlinks ? 0 : O_NOFOLLOW); |
||||||
|
android_lkchan::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), flags, mode))); |
||||||
|
if (fd == -1) { |
||||||
|
PLOG(ERROR) << "android_lkchan::WriteStringToFile open failed"; |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
// We do an explicit fchmod here because we assume that the caller really
|
||||||
|
// meant what they said and doesn't want the umask-influenced mode.
|
||||||
|
if (fchmod(fd, mode) == -1) { |
||||||
|
PLOG(ERROR) << "android_lkchan::WriteStringToFile fchmod failed"; |
||||||
|
return CleanUpAfterFailedWrite(path); |
||||||
|
} |
||||||
|
if (fchown(fd, owner, group) == -1) { |
||||||
|
PLOG(ERROR) << "android_lkchan::WriteStringToFile fchown failed"; |
||||||
|
return CleanUpAfterFailedWrite(path); |
||||||
|
} |
||||||
|
if (!WriteStringToFd(content, fd)) { |
||||||
|
PLOG(ERROR) << "android_lkchan::WriteStringToFile write failed"; |
||||||
|
return CleanUpAfterFailedWrite(path); |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
#endif |
||||||
|
|
||||||
|
bool WriteStringToFile(const std::string& content, const std::string& path, |
||||||
|
bool follow_symlinks) { |
||||||
|
int flags = O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_BINARY | |
||||||
|
(follow_symlinks ? 0 : O_NOFOLLOW); |
||||||
|
android_lkchan::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), flags, 0666))); |
||||||
|
if (fd == -1) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
return WriteStringToFd(content, fd) || CleanUpAfterFailedWrite(path); |
||||||
|
} |
||||||
|
|
||||||
|
bool ReadFully(int fd, void* data, size_t byte_count) { |
||||||
|
uint8_t* p = reinterpret_cast<uint8_t*>(data); |
||||||
|
size_t remaining = byte_count; |
||||||
|
while (remaining > 0) { |
||||||
|
ssize_t n = TEMP_FAILURE_RETRY(read(fd, p, remaining)); |
||||||
|
if (n <= 0) return false; |
||||||
|
p += n; |
||||||
|
remaining -= n; |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
#if defined(_WIN32) |
||||||
|
// Windows implementation of pread. Note that this DOES move the file descriptors read position,
|
||||||
|
// but it does so atomically.
|
||||||
|
static ssize_t pread(int fd, void* data, size_t byte_count, off64_t offset) { |
||||||
|
DWORD bytes_read; |
||||||
|
OVERLAPPED overlapped; |
||||||
|
memset(&overlapped, 0, sizeof(OVERLAPPED)); |
||||||
|
overlapped.Offset = static_cast<DWORD>(offset); |
||||||
|
overlapped.OffsetHigh = static_cast<DWORD>(offset >> 32); |
||||||
|
if (!ReadFile(reinterpret_cast<HANDLE>(_get_osfhandle(fd)), data, static_cast<DWORD>(byte_count), |
||||||
|
&bytes_read, &overlapped)) { |
||||||
|
// In case someone tries to read errno (since this is masquerading as a POSIX call)
|
||||||
|
errno = EIO; |
||||||
|
return -1; |
||||||
|
} |
||||||
|
return static_cast<ssize_t>(bytes_read); |
||||||
|
} |
||||||
|
#endif |
||||||
|
|
||||||
|
bool ReadFullyAtOffset(int fd, void* data, size_t byte_count, off64_t offset) { |
||||||
|
uint8_t* p = reinterpret_cast<uint8_t*>(data); |
||||||
|
while (byte_count > 0) { |
||||||
|
ssize_t n = TEMP_FAILURE_RETRY(pread(fd, p, byte_count, offset)); |
||||||
|
if (n <= 0) return false; |
||||||
|
p += n; |
||||||
|
byte_count -= n; |
||||||
|
offset += n; |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
bool WriteFully(int fd, const void* data, size_t byte_count) { |
||||||
|
const uint8_t* p = reinterpret_cast<const uint8_t*>(data); |
||||||
|
size_t remaining = byte_count; |
||||||
|
while (remaining > 0) { |
||||||
|
ssize_t n = TEMP_FAILURE_RETRY(write(fd, p, remaining)); |
||||||
|
if (n == -1) return false; |
||||||
|
p += n; |
||||||
|
remaining -= n; |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
bool RemoveFileIfExists(const std::string& path, std::string* err) { |
||||||
|
struct stat st; |
||||||
|
#if defined(_WIN32) |
||||||
|
//TODO: Windows version can't handle symbol link correctly.
|
||||||
|
int result = stat(path.c_str(), &st); |
||||||
|
bool file_type_removable = (result == 0 && S_ISREG(st.st_mode)); |
||||||
|
#else |
||||||
|
int result = lstat(path.c_str(), &st); |
||||||
|
bool file_type_removable = (result == 0 && (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode))); |
||||||
|
#endif |
||||||
|
if (result == 0) { |
||||||
|
if (!file_type_removable) { |
||||||
|
if (err != nullptr) { |
||||||
|
*err = "is not a regular or symbol link file"; |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
if (unlink(path.c_str()) == -1) { |
||||||
|
if (err != nullptr) { |
||||||
|
*err = strerror(errno); |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
#if !defined(_WIN32) |
||||||
|
bool Readlink(const std::string& path, std::string* result) { |
||||||
|
result->clear(); |
||||||
|
|
||||||
|
// Most Linux file systems (ext2 and ext4, say) limit symbolic links to
|
||||||
|
// 4095 bytes. Since we'll copy out into the string anyway, it doesn't
|
||||||
|
// waste memory to just start there. We add 1 so that we can recognize
|
||||||
|
// whether it actually fit (rather than being truncated to 4095).
|
||||||
|
std::vector<char> buf(4095 + 1); |
||||||
|
while (true) { |
||||||
|
ssize_t size = readlink(path.c_str(), &buf[0], buf.size()); |
||||||
|
// Unrecoverable error?
|
||||||
|
if (size == -1) return false; |
||||||
|
// It fit! (If size == buf.size(), it may have been truncated.)
|
||||||
|
if (static_cast<size_t>(size) < buf.size()) { |
||||||
|
result->assign(&buf[0], size); |
||||||
|
return true; |
||||||
|
} |
||||||
|
// Double our buffer and try again.
|
||||||
|
buf.resize(buf.size() * 2); |
||||||
|
} |
||||||
|
} |
||||||
|
#endif |
||||||
|
|
||||||
|
#if !defined(_WIN32) |
||||||
|
bool Realpath(const std::string& path, std::string* result) { |
||||||
|
result->clear(); |
||||||
|
|
||||||
|
char* realpath_buf = realpath(path.c_str(), nullptr); |
||||||
|
if (realpath_buf == nullptr) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
result->assign(realpath_buf); |
||||||
|
free(realpath_buf); |
||||||
|
return true; |
||||||
|
} |
||||||
|
#endif |
||||||
|
|
||||||
|
std::string GetExecutablePath() { |
||||||
|
#if defined(__linux__) |
||||||
|
std::string path; |
||||||
|
android_lkchan::base::Readlink("/proc/self/exe", &path); |
||||||
|
return path; |
||||||
|
#elif defined(__APPLE__) |
||||||
|
char path[PATH_MAX + 1]; |
||||||
|
uint32_t path_len = sizeof(path); |
||||||
|
int rc = _NSGetExecutablePath(path, &path_len); |
||||||
|
if (rc < 0) { |
||||||
|
std::unique_ptr<char> path_buf(new char[path_len]); |
||||||
|
_NSGetExecutablePath(path_buf.get(), &path_len); |
||||||
|
return path_buf.get(); |
||||||
|
} |
||||||
|
return path; |
||||||
|
#elif defined(_WIN32) |
||||||
|
char path[PATH_MAX + 1]; |
||||||
|
DWORD result = GetModuleFileName(NULL, path, sizeof(path) - 1); |
||||||
|
if (result == 0 || result == sizeof(path) - 1) return ""; |
||||||
|
path[PATH_MAX - 1] = 0; |
||||||
|
return path; |
||||||
|
#else |
||||||
|
#error unknown OS |
||||||
|
#endif |
||||||
|
} |
||||||
|
|
||||||
|
std::string GetExecutableDirectory() { |
||||||
|
return Dirname(GetExecutablePath()); |
||||||
|
} |
||||||
|
|
||||||
|
std::string Basename(const std::string& path) { |
||||||
|
// Copy path because basename may modify the string passed in.
|
||||||
|
std::string result(path); |
||||||
|
|
||||||
|
#if !defined(__BIONIC__) |
||||||
|
// Use lock because basename() may write to a process global and return a
|
||||||
|
// pointer to that. Note that this locking strategy only works if all other
|
||||||
|
// callers to basename in the process also grab this same lock, but its
|
||||||
|
// better than nothing. Bionic's basename returns a thread-local buffer.
|
||||||
|
static std::mutex& basename_lock = *new std::mutex(); |
||||||
|
std::lock_guard<std::mutex> lock(basename_lock); |
||||||
|
#endif |
||||||
|
|
||||||
|
// Note that if std::string uses copy-on-write strings, &str[0] will cause
|
||||||
|
// the copy to be made, so there is no chance of us accidentally writing to
|
||||||
|
// the storage for 'path'.
|
||||||
|
char* name = basename(&result[0]); |
||||||
|
|
||||||
|
// In case basename returned a pointer to a process global, copy that string
|
||||||
|
// before leaving the lock.
|
||||||
|
result.assign(name); |
||||||
|
|
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
std::string Dirname(const std::string& path) { |
||||||
|
// Copy path because dirname may modify the string passed in.
|
||||||
|
std::string result(path); |
||||||
|
|
||||||
|
#if !defined(__BIONIC__) |
||||||
|
// Use lock because dirname() may write to a process global and return a
|
||||||
|
// pointer to that. Note that this locking strategy only works if all other
|
||||||
|
// callers to dirname in the process also grab this same lock, but its
|
||||||
|
// better than nothing. Bionic's dirname returns a thread-local buffer.
|
||||||
|
static std::mutex& dirname_lock = *new std::mutex(); |
||||||
|
std::lock_guard<std::mutex> lock(dirname_lock); |
||||||
|
#endif |
||||||
|
|
||||||
|
// Note that if std::string uses copy-on-write strings, &str[0] will cause
|
||||||
|
// the copy to be made, so there is no chance of us accidentally writing to
|
||||||
|
// the storage for 'path'.
|
||||||
|
char* parent = dirname(&result[0]); |
||||||
|
|
||||||
|
// In case dirname returned a pointer to a process global, copy that string
|
||||||
|
// before leaving the lock.
|
||||||
|
result.assign(parent); |
||||||
|
|
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace base
|
||||||
|
} // namespace android_lkchan
|
@ -0,0 +1,82 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2015 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 ANDROID_BASE_FILE_H |
||||||
|
#define ANDROID_BASE_FILE_H |
||||||
|
|
||||||
|
#include <sys/stat.h> |
||||||
|
#include <sys/types.h> |
||||||
|
#include <string> |
||||||
|
|
||||||
|
#if !defined(_WIN32) && !defined(O_BINARY) |
||||||
|
#define O_BINARY 0 |
||||||
|
#endif |
||||||
|
|
||||||
|
#if defined(__APPLE__) |
||||||
|
/* Mac OS has always had a 64-bit off_t, so it doesn't have off64_t. */ |
||||||
|
typedef off_t off64_t; |
||||||
|
#endif |
||||||
|
|
||||||
|
namespace android_lkchan { |
||||||
|
namespace base { |
||||||
|
|
||||||
|
bool ReadFdToString(int fd, std::string* content); |
||||||
|
bool ReadFileToString(const std::string& path, std::string* content, |
||||||
|
bool follow_symlinks = false); |
||||||
|
|
||||||
|
bool WriteStringToFile(const std::string& content, const std::string& path, |
||||||
|
bool follow_symlinks = false); |
||||||
|
bool WriteStringToFd(const std::string& content, int fd); |
||||||
|
|
||||||
|
#if !defined(_WIN32) |
||||||
|
bool WriteStringToFile(const std::string& content, const std::string& path, |
||||||
|
mode_t mode, uid_t owner, gid_t group, |
||||||
|
bool follow_symlinks = false); |
||||||
|
#endif |
||||||
|
|
||||||
|
bool ReadFully(int fd, void* data, size_t byte_count); |
||||||
|
|
||||||
|
// Reads `byte_count` bytes from the file descriptor at the specified offset.
|
||||||
|
// Returns false if there was an IO error or EOF was reached before reading `byte_count` bytes.
|
||||||
|
//
|
||||||
|
// NOTE: On Linux/Mac, this function wraps pread, which provides atomic read support without
|
||||||
|
// modifying the read pointer of the file descriptor. On Windows, however, the read pointer does
|
||||||
|
// get modified. This means that ReadFullyAtOffset can be used concurrently with other calls to the
|
||||||
|
// same function, but concurrently seeking or reading incrementally can lead to unexpected
|
||||||
|
// behavior.
|
||||||
|
bool ReadFullyAtOffset(int fd, void* data, size_t byte_count, off64_t offset); |
||||||
|
|
||||||
|
bool WriteFully(int fd, const void* data, size_t byte_count); |
||||||
|
|
||||||
|
bool RemoveFileIfExists(const std::string& path, std::string* err = nullptr); |
||||||
|
|
||||||
|
#if !defined(_WIN32) |
||||||
|
bool Realpath(const std::string& path, std::string* result); |
||||||
|
bool Readlink(const std::string& path, std::string* result); |
||||||
|
#endif |
||||||
|
|
||||||
|
std::string GetExecutablePath(); |
||||||
|
std::string GetExecutableDirectory(); |
||||||
|
|
||||||
|
// Like the regular basename and dirname, but thread-safe on all
|
||||||
|
// platforms and capable of correctly handling exotic Windows paths.
|
||||||
|
std::string Basename(const std::string& path); |
||||||
|
std::string Dirname(const std::string& path); |
||||||
|
|
||||||
|
} // namespace base
|
||||||
|
} // namespace android_lkchan
|
||||||
|
|
||||||
|
#endif // ANDROID_BASE_FILE_H
|
@ -0,0 +1,503 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2015 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. |
||||||
|
*/ |
||||||
|
|
||||||
|
#if defined(_WIN32) |
||||||
|
#include <windows.h> |
||||||
|
#endif |
||||||
|
|
||||||
|
#include "android-base/logging.h" |
||||||
|
|
||||||
|
#include <fcntl.h> |
||||||
|
#include <libgen.h> |
||||||
|
#include <time.h> |
||||||
|
|
||||||
|
// For getprogname(3) or program_invocation_short_name.
|
||||||
|
#if defined(__ANDROID__) || defined(__APPLE__) |
||||||
|
#include <stdlib.h> |
||||||
|
#elif defined(__GLIBC__) |
||||||
|
#include <errno.h> |
||||||
|
#endif |
||||||
|
|
||||||
|
#if defined(__linux__) |
||||||
|
#include <sys/uio.h> |
||||||
|
#endif |
||||||
|
|
||||||
|
#include <iostream> |
||||||
|
#include <limits> |
||||||
|
#include <mutex> |
||||||
|
#include <sstream> |
||||||
|
#include <string> |
||||||
|
#include <utility> |
||||||
|
#include <vector> |
||||||
|
|
||||||
|
// Headers for LogMessage::LogLine.
|
||||||
|
#ifdef __ANDROID__ |
||||||
|
#include <android/log.h> |
||||||
|
#include <android/set_abort_message.h> |
||||||
|
#else |
||||||
|
#include <sys/types.h> |
||||||
|
#include <unistd.h> |
||||||
|
#endif |
||||||
|
|
||||||
|
#include <android-base/macros.h> |
||||||
|
#include <android-base/strings.h> |
||||||
|
|
||||||
|
// For gettid.
|
||||||
|
#if defined(__APPLE__) |
||||||
|
#include "AvailabilityMacros.h" // For MAC_OS_X_VERSION_MAX_ALLOWED |
||||||
|
#include <stdint.h> |
||||||
|
#include <stdlib.h> |
||||||
|
#include <sys/syscall.h> |
||||||
|
#include <sys/time.h> |
||||||
|
#include <unistd.h> |
||||||
|
#elif defined(__linux__) && !defined(__ANDROID__) |
||||||
|
#include <syscall.h> |
||||||
|
#include <unistd.h> |
||||||
|
#elif defined(_WIN32) |
||||||
|
#include <windows.h> |
||||||
|
#endif |
||||||
|
|
||||||
|
#if defined(_WIN32) |
||||||
|
typedef uint32_t thread_id; |
||||||
|
#else |
||||||
|
typedef pid_t thread_id; |
||||||
|
#endif |
||||||
|
|
||||||
|
static thread_id GetThreadId() { |
||||||
|
#if defined(__BIONIC__) |
||||||
|
return gettid(); |
||||||
|
#elif defined(__APPLE__) |
||||||
|
uint64_t tid; |
||||||
|
pthread_threadid_np(NULL, &tid); |
||||||
|
return tid; |
||||||
|
#elif defined(__linux__) |
||||||
|
return syscall(__NR_gettid); |
||||||
|
#elif defined(_WIN32) |
||||||
|
return GetCurrentThreadId(); |
||||||
|
#endif |
||||||
|
} |
||||||
|
|
||||||
|
namespace { |
||||||
|
#if defined(__GLIBC__) |
||||||
|
const char* getprogname() { |
||||||
|
return program_invocation_short_name; |
||||||
|
} |
||||||
|
#elif defined(_WIN32) |
||||||
|
const char* getprogname() { |
||||||
|
static bool first = true; |
||||||
|
static char progname[MAX_PATH] = {}; |
||||||
|
|
||||||
|
if (first) { |
||||||
|
CHAR longname[MAX_PATH]; |
||||||
|
DWORD nchars = GetModuleFileNameA(nullptr, longname, arraysize(longname)); |
||||||
|
if ((nchars >= arraysize(longname)) || (nchars == 0)) { |
||||||
|
// String truncation or some other error.
|
||||||
|
strcpy(progname, "<unknown>"); |
||||||
|
} else { |
||||||
|
strcpy(progname, basename(longname)); |
||||||
|
} |
||||||
|
first = false; |
||||||
|
} |
||||||
|
|
||||||
|
return progname; |
||||||
|
} |
||||||
|
#endif |
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace android_lkchan { |
||||||
|
namespace base { |
||||||
|
|
||||||
|
static std::mutex& LoggingLock() { |
||||||
|
static auto& logging_lock = *new std::mutex(); |
||||||
|
return logging_lock; |
||||||
|
} |
||||||
|
|
||||||
|
static LogFunction& Logger() { |
||||||
|
#ifdef __ANDROID__ |
||||||
|
static auto& logger = *new LogFunction(LogdLogger()); |
||||||
|
#else |
||||||
|
static auto& logger = *new LogFunction(StderrLogger); |
||||||
|
#endif |
||||||
|
return logger; |
||||||
|
} |
||||||
|
|
||||||
|
static AbortFunction& Aborter() { |
||||||
|
static auto& aborter = *new AbortFunction(DefaultAborter); |
||||||
|
return aborter; |
||||||
|
} |
||||||
|
|
||||||
|
static std::recursive_mutex& TagLock() { |
||||||
|
static auto& tag_lock = *new std::recursive_mutex(); |
||||||
|
return tag_lock; |
||||||
|
} |
||||||
|
static std::string* gDefaultTag; |
||||||
|
std::string GetDefaultTag() { |
||||||
|
std::lock_guard<std::recursive_mutex> lock(TagLock()); |
||||||
|
if (gDefaultTag == nullptr) { |
||||||
|
return ""; |
||||||
|
} |
||||||
|
return *gDefaultTag; |
||||||
|
} |
||||||
|
void SetDefaultTag(const std::string& tag) { |
||||||
|
std::lock_guard<std::recursive_mutex> lock(TagLock()); |
||||||
|
if (gDefaultTag != nullptr) { |
||||||
|
delete gDefaultTag; |
||||||
|
gDefaultTag = nullptr; |
||||||
|
} |
||||||
|
if (!tag.empty()) { |
||||||
|
gDefaultTag = new std::string(tag); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
static bool gInitialized = false; |
||||||
|
static LogSeverity gMinimumLogSeverity = INFO; |
||||||
|
|
||||||
|
#if defined(__linux__) |
||||||
|
void KernelLogger(android_lkchan::base::LogId, android_lkchan::base::LogSeverity severity, |
||||||
|
const char* tag, const char*, unsigned int, const char* msg) { |
||||||
|
// clang-format off
|
||||||
|
static constexpr int kLogSeverityToKernelLogLevel[] = { |
||||||
|
[android_lkchan::base::VERBOSE] = 7, // KERN_DEBUG (there is no verbose kernel log
|
||||||
|
// level)
|
||||||
|
[android_lkchan::base::DEBUG] = 7, // KERN_DEBUG
|
||||||
|
[android_lkchan::base::INFO] = 6, // KERN_INFO
|
||||||
|
[android_lkchan::base::WARNING] = 4, // KERN_WARNING
|
||||||
|
[android_lkchan::base::ERROR] = 3, // KERN_ERROR
|
||||||
|
[android_lkchan::base::FATAL_WITHOUT_ABORT] = 2, // KERN_CRIT
|
||||||
|
[android_lkchan::base::FATAL] = 2, // KERN_CRIT
|
||||||
|
}; |
||||||
|
// clang-format on
|
||||||
|
static_assert(arraysize(kLogSeverityToKernelLogLevel) == android_lkchan::base::FATAL + 1, |
||||||
|
"Mismatch in size of kLogSeverityToKernelLogLevel and values in LogSeverity"); |
||||||
|
|
||||||
|
static int klog_fd = TEMP_FAILURE_RETRY(open("/dev/kmsg", O_WRONLY | O_CLOEXEC)); |
||||||
|
if (klog_fd == -1) return; |
||||||
|
|
||||||
|
int level = kLogSeverityToKernelLogLevel[severity]; |
||||||
|
|
||||||
|
// The kernel's printk buffer is only 1024 bytes.
|
||||||
|
// TODO: should we automatically break up long lines into multiple lines?
|
||||||
|
// Or we could log but with something like "..." at the end?
|
||||||
|
char buf[1024]; |
||||||
|
size_t size = snprintf(buf, sizeof(buf), "<%d>%s: %s\n", level, tag, msg); |
||||||
|
if (size > sizeof(buf)) { |
||||||
|
size = snprintf(buf, sizeof(buf), "<%d>%s: %zu-byte message too long for printk\n", |
||||||
|
level, tag, size); |
||||||
|
} |
||||||
|
|
||||||
|
iovec iov[1]; |
||||||
|
iov[0].iov_base = buf; |
||||||
|
iov[0].iov_len = size; |
||||||
|
TEMP_FAILURE_RETRY(writev(klog_fd, iov, 1)); |
||||||
|
} |
||||||
|
#endif |
||||||
|
|
||||||
|
void StderrLogger(LogId, LogSeverity severity, const char* tag, const char* file, unsigned int line, |
||||||
|
const char* message) { |
||||||
|
struct tm now; |
||||||
|
time_t t = time(nullptr); |
||||||
|
|
||||||
|
#if defined(_WIN32) |
||||||
|
localtime_s(&now, &t); |
||||||
|
#else |
||||||
|
localtime_r(&t, &now); |
||||||
|
#endif |
||||||
|
|
||||||
|
char timestamp[32]; |
||||||
|
strftime(timestamp, sizeof(timestamp), "%m-%d %H:%M:%S", &now); |
||||||
|
|
||||||
|
static const char log_characters[] = "VDIWEFF"; |
||||||
|
static_assert(arraysize(log_characters) - 1 == FATAL + 1, |
||||||
|
"Mismatch in size of log_characters and values in LogSeverity"); |
||||||
|
char severity_char = log_characters[severity]; |
||||||
|
fprintf(stderr, "%s %c %s %5d %5d %s:%u] %s\n", tag ? tag : "nullptr", severity_char, timestamp, |
||||||
|
getpid(), GetThreadId(), file, line, message); |
||||||
|
} |
||||||
|
|
||||||
|
void DefaultAborter(const char* abort_message) { |
||||||
|
#ifdef __ANDROID__ |
||||||
|
android_set_abort_message(abort_message); |
||||||
|
#else |
||||||
|
UNUSED(abort_message); |
||||||
|
#endif |
||||||
|
abort(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
#ifdef __ANDROID__ |
||||||
|
LogdLogger::LogdLogger(LogId default_log_id) : default_log_id_(default_log_id) { |
||||||
|
} |
||||||
|
|
||||||
|
void LogdLogger::operator()(LogId id, LogSeverity severity, const char* tag, |
||||||
|
const char* file, unsigned int line, |
||||||
|
const char* message) { |
||||||
|
static constexpr android_LogPriority kLogSeverityToAndroidLogPriority[] = { |
||||||
|
ANDROID_LOG_VERBOSE, ANDROID_LOG_DEBUG, ANDROID_LOG_INFO, |
||||||
|
ANDROID_LOG_WARN, ANDROID_LOG_ERROR, ANDROID_LOG_FATAL, |
||||||
|
ANDROID_LOG_FATAL, |
||||||
|
}; |
||||||
|
static_assert(arraysize(kLogSeverityToAndroidLogPriority) == FATAL + 1, |
||||||
|
"Mismatch in size of kLogSeverityToAndroidLogPriority and values in LogSeverity"); |
||||||
|
|
||||||
|
int priority = kLogSeverityToAndroidLogPriority[severity]; |
||||||
|
if (id == DEFAULT) { |
||||||
|
id = default_log_id_; |
||||||
|
} |
||||||
|
|
||||||
|
static constexpr log_id kLogIdToAndroidLogId[] = { |
||||||
|
LOG_ID_MAX, LOG_ID_MAIN, LOG_ID_SYSTEM, |
||||||
|
}; |
||||||
|
static_assert(arraysize(kLogIdToAndroidLogId) == SYSTEM + 1, |
||||||
|
"Mismatch in size of kLogIdToAndroidLogId and values in LogId"); |
||||||
|
log_id lg_id = kLogIdToAndroidLogId[id]; |
||||||
|
|
||||||
|
if (priority == ANDROID_LOG_FATAL) { |
||||||
|
__android_log_buf_print(lg_id, priority, tag, "%s:%u] %s", file, line, |
||||||
|
message); |
||||||
|
} else { |
||||||
|
__android_log_buf_print(lg_id, priority, tag, "%s", message); |
||||||
|
} |
||||||
|
} |
||||||
|
#endif |
||||||
|
|
||||||
|
void InitLogging(char* argv[], LogFunction&& logger, AbortFunction&& aborter) { |
||||||
|
SetLogger(std::forward<LogFunction>(logger)); |
||||||
|
SetAborter(std::forward<AbortFunction>(aborter)); |
||||||
|
|
||||||
|
if (gInitialized) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
gInitialized = true; |
||||||
|
|
||||||
|
// Stash the command line for later use. We can use /proc/self/cmdline on
|
||||||
|
// Linux to recover this, but we don't have that luxury on the Mac/Windows,
|
||||||
|
// and there are a couple of argv[0] variants that are commonly used.
|
||||||
|
if (argv != nullptr) { |
||||||
|
SetDefaultTag(basename(argv[0])); |
||||||
|
} |
||||||
|
|
||||||
|
const char* tags = getenv("ANDROID_LOG_TAGS"); |
||||||
|
if (tags == nullptr) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
std::vector<std::string> specs = Split(tags, " "); |
||||||
|
for (size_t i = 0; i < specs.size(); ++i) { |
||||||
|
// "tag-pattern:[vdiwefs]"
|
||||||
|
std::string spec(specs[i]); |
||||||
|
if (spec.size() == 3 && StartsWith(spec, "*:")) { |
||||||
|
switch (spec[2]) { |
||||||
|
case 'v': |
||||||
|
gMinimumLogSeverity = VERBOSE; |
||||||
|
continue; |
||||||
|
case 'd': |
||||||
|
gMinimumLogSeverity = DEBUG; |
||||||
|
continue; |
||||||
|
case 'i': |
||||||
|
gMinimumLogSeverity = INFO; |
||||||
|
continue; |
||||||
|
case 'w': |
||||||
|
gMinimumLogSeverity = WARNING; |
||||||
|
continue; |
||||||
|
case 'e': |
||||||
|
gMinimumLogSeverity = ERROR; |
||||||
|
continue; |
||||||
|
case 'f': |
||||||
|
gMinimumLogSeverity = FATAL_WITHOUT_ABORT; |
||||||
|
continue; |
||||||
|
// liblog will even suppress FATAL if you say 's' for silent, but that's
|
||||||
|
// crazy!
|
||||||
|
case 's': |
||||||
|
gMinimumLogSeverity = FATAL_WITHOUT_ABORT; |
||||||
|
continue; |
||||||
|
} |
||||||
|
} |
||||||
|
LOG(FATAL) << "unsupported '" << spec << "' in ANDROID_LOG_TAGS (" << tags |
||||||
|
<< ")"; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void SetLogger(LogFunction&& logger) { |
||||||
|
std::lock_guard<std::mutex> lock(LoggingLock()); |
||||||
|
Logger() = std::move(logger); |
||||||
|
} |
||||||
|
|
||||||
|
void SetAborter(AbortFunction&& aborter) { |
||||||
|
std::lock_guard<std::mutex> lock(LoggingLock()); |
||||||
|
Aborter() = std::move(aborter); |
||||||
|
} |
||||||
|
|
||||||
|
static const char* GetFileBasename(const char* file) { |
||||||
|
// We can't use basename(3) even on Unix because the Mac doesn't
|
||||||
|
// have a non-modifying basename.
|
||||||
|
const char* last_slash = strrchr(file, '/'); |
||||||
|
if (last_slash != nullptr) { |
||||||
|
return last_slash + 1; |
||||||
|
} |
||||||
|
#if defined(_WIN32) |
||||||
|
const char* last_backslash = strrchr(file, '\\'); |
||||||
|
if (last_backslash != nullptr) { |
||||||
|
return last_backslash + 1; |
||||||
|
} |
||||||
|
#endif |
||||||
|
return file; |
||||||
|
} |
||||||
|
|
||||||
|
// This indirection greatly reduces the stack impact of having lots of
|
||||||
|
// checks/logging in a function.
|
||||||
|
class LogMessageData { |
||||||
|
public: |
||||||
|
LogMessageData(const char* file, unsigned int line, LogId id, LogSeverity severity, |
||||||
|
const char* tag, int error) |
||||||
|
: file_(GetFileBasename(file)), |
||||||
|
line_number_(line), |
||||||
|
id_(id), |
||||||
|
severity_(severity), |
||||||
|
tag_(tag), |
||||||
|
error_(error) {} |
||||||
|
|
||||||
|
const char* GetFile() const { |
||||||
|
return file_; |
||||||
|
} |
||||||
|
|
||||||
|
unsigned int GetLineNumber() const { |
||||||
|
return line_number_; |
||||||
|
} |
||||||
|
|
||||||
|
LogSeverity GetSeverity() const { |
||||||
|
return severity_; |
||||||
|
} |
||||||
|
|
||||||
|
const char* GetTag() const { return tag_; } |
||||||
|
|
||||||
|
LogId GetId() const { |
||||||
|
return id_; |
||||||
|
} |
||||||
|
|
||||||
|
int GetError() const { |
||||||
|
return error_; |
||||||
|
} |
||||||
|
|
||||||
|
std::ostream& GetBuffer() { |
||||||
|
return buffer_; |
||||||
|
} |
||||||
|
|
||||||
|
std::string ToString() const { |
||||||
|
return buffer_.str(); |
||||||
|
} |
||||||
|
|
||||||
|
private: |
||||||
|
std::ostringstream buffer_; |
||||||
|
const char* const file_; |
||||||
|
const unsigned int line_number_; |
||||||
|
const LogId id_; |
||||||
|
const LogSeverity severity_; |
||||||
|
const char* const tag_; |
||||||
|
const int error_; |
||||||
|
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(LogMessageData); |
||||||
|
}; |
||||||
|
|
||||||
|
LogMessage::LogMessage(const char* file, unsigned int line, LogId id, LogSeverity severity, |
||||||
|
const char* tag, int error) |
||||||
|
: data_(new LogMessageData(file, line, id, severity, tag, error)) {} |
||||||
|
|
||||||
|
LogMessage::LogMessage(const char* file, unsigned int line, LogId id, LogSeverity severity, |
||||||
|
int error) |
||||||
|
: LogMessage(file, line, id, severity, nullptr, error) {} |
||||||
|
|
||||||
|
LogMessage::~LogMessage() { |
||||||
|
// Check severity again. This is duplicate work wrt/ LOG macros, but not LOG_STREAM.
|
||||||
|
if (!WOULD_LOG(data_->GetSeverity())) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
// Finish constructing the message.
|
||||||
|
if (data_->GetError() != -1) { |
||||||
|
data_->GetBuffer() << ": " << strerror(data_->GetError()); |
||||||
|
} |
||||||
|
std::string msg(data_->ToString()); |
||||||
|
|
||||||
|
{ |
||||||
|
// Do the actual logging with the lock held.
|
||||||
|
std::lock_guard<std::mutex> lock(LoggingLock()); |
||||||
|
if (msg.find('\n') == std::string::npos) { |
||||||
|
LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetId(), data_->GetSeverity(), |
||||||
|
data_->GetTag(), msg.c_str()); |
||||||
|
} else { |
||||||
|
msg += '\n'; |
||||||
|
size_t i = 0; |
||||||
|
while (i < msg.size()) { |
||||||
|
size_t nl = msg.find('\n', i); |
||||||
|
msg[nl] = '\0'; |
||||||
|
LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetId(), data_->GetSeverity(), |
||||||
|
data_->GetTag(), &msg[i]); |
||||||
|
// Undo the zero-termination so we can give the complete message to the aborter.
|
||||||
|
msg[nl] = '\n'; |
||||||
|
i = nl + 1; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Abort if necessary.
|
||||||
|
if (data_->GetSeverity() == FATAL) { |
||||||
|
Aborter()(msg.c_str()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
std::ostream& LogMessage::stream() { |
||||||
|
return data_->GetBuffer(); |
||||||
|
} |
||||||
|
|
||||||
|
void LogMessage::LogLine(const char* file, unsigned int line, LogId id, LogSeverity severity, |
||||||
|
const char* tag, const char* message) { |
||||||
|
if (tag == nullptr) { |
||||||
|
std::lock_guard<std::recursive_mutex> lock(TagLock()); |
||||||
|
if (gDefaultTag == nullptr) { |
||||||
|
gDefaultTag = new std::string(getprogname()); |
||||||
|
} |
||||||
|
Logger()(id, severity, gDefaultTag->c_str(), file, line, message); |
||||||
|
} else { |
||||||
|
Logger()(id, severity, tag, file, line, message); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void LogMessage::LogLine(const char* file, unsigned int line, LogId id, LogSeverity severity, |
||||||
|
const char* message) { |
||||||
|
LogLine(file, line, id, severity, nullptr, message); |
||||||
|
} |
||||||
|
|
||||||
|
LogSeverity GetMinimumLogSeverity() { |
||||||
|
return gMinimumLogSeverity; |
||||||
|
} |
||||||
|
|
||||||
|
LogSeverity SetMinimumLogSeverity(LogSeverity new_severity) { |
||||||
|
LogSeverity old_severity = gMinimumLogSeverity; |
||||||
|
gMinimumLogSeverity = new_severity; |
||||||
|
return old_severity; |
||||||
|
} |
||||||
|
|
||||||
|
ScopedLogSeverity::ScopedLogSeverity(LogSeverity new_severity) { |
||||||
|
old_ = SetMinimumLogSeverity(new_severity); |
||||||
|
} |
||||||
|
|
||||||
|
ScopedLogSeverity::~ScopedLogSeverity() { |
||||||
|
SetMinimumLogSeverity(old_); |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace base
|
||||||
|
} // namespace android_lkchan
|
@ -0,0 +1,501 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2015 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 ANDROID_BASE_LOGGING_H |
||||||
|
#define ANDROID_BASE_LOGGING_H |
||||||
|
|
||||||
|
//
|
||||||
|
// Google-style C++ logging.
|
||||||
|
//
|
||||||
|
|
||||||
|
// This header provides a C++ stream interface to logging.
|
||||||
|
//
|
||||||
|
// To log:
|
||||||
|
//
|
||||||
|
// LOG(INFO) << "Some text; " << some_value;
|
||||||
|
//
|
||||||
|
// Replace `INFO` with any severity from `enum LogSeverity`.
|
||||||
|
//
|
||||||
|
// To log the result of a failed function and include the string
|
||||||
|
// representation of `errno` at the end:
|
||||||
|
//
|
||||||
|
// PLOG(ERROR) << "Write failed";
|
||||||
|
//
|
||||||
|
// The output will be something like `Write failed: I/O error`.
|
||||||
|
// Remember this as 'P' as in perror(3).
|
||||||
|
//
|
||||||
|
// To output your own types, simply implement operator<< as normal.
|
||||||
|
//
|
||||||
|
// By default, output goes to logcat on Android and stderr on the host.
|
||||||
|
// A process can use `SetLogger` to decide where all logging goes.
|
||||||
|
// Implementations are provided for logcat, stderr, and dmesg.
|
||||||
|
//
|
||||||
|
// By default, the process' name is used as the log tag.
|
||||||
|
// Code can choose a specific log tag by defining LOG_TAG
|
||||||
|
// before including this header.
|
||||||
|
|
||||||
|
// This header also provides assertions:
|
||||||
|
//
|
||||||
|
// CHECK(must_be_true);
|
||||||
|
// CHECK_EQ(a, b) << z_is_interesting_too;
|
||||||
|
|
||||||
|
// NOTE: For Windows, you must include logging.h after windows.h to allow the
|
||||||
|
// following code to suppress the evil ERROR macro:
|
||||||
|
#ifdef _WIN32 |
||||||
|
// windows.h includes wingdi.h which defines an evil macro ERROR.
|
||||||
|
#ifdef ERROR |
||||||
|
#undef ERROR |
||||||
|
#endif |
||||||
|
#endif |
||||||
|
|
||||||
|
#include <functional> |
||||||
|
#include <memory> |
||||||
|
#include <ostream> |
||||||
|
|
||||||
|
#include "android-base/macros.h" |
||||||
|
|
||||||
|
// Note: DO NOT USE DIRECTLY. Use LOG_TAG instead.
|
||||||
|
#ifdef _LOG_TAG_INTERNAL |
||||||
|
#error "_LOG_TAG_INTERNAL must not be defined" |
||||||
|
#endif |
||||||
|
#ifdef LOG_TAG |
||||||
|
#define _LOG_TAG_INTERNAL LOG_TAG |
||||||
|
#else |
||||||
|
#define _LOG_TAG_INTERNAL nullptr |
||||||
|
#endif |
||||||
|
|
||||||
|
namespace android_lkchan { |
||||||
|
namespace base { |
||||||
|
|
||||||
|
enum LogSeverity { |
||||||
|
VERBOSE, |
||||||
|
DEBUG, |
||||||
|
INFO, |
||||||
|
WARNING, |
||||||
|
ERROR, |
||||||
|
FATAL_WITHOUT_ABORT, |
||||||
|
FATAL, |
||||||
|
}; |
||||||
|
|
||||||
|
enum LogId { |
||||||
|
DEFAULT, |
||||||
|
MAIN, |
||||||
|
SYSTEM, |
||||||
|
}; |
||||||
|
|
||||||
|
using LogFunction = std::function<void(LogId, LogSeverity, const char*, const char*, |
||||||
|
unsigned int, const char*)>; |
||||||
|
using AbortFunction = std::function<void(const char*)>; |
||||||
|
|
||||||
|
void KernelLogger(LogId, LogSeverity, const char*, const char*, unsigned int, const char*); |
||||||
|
void StderrLogger(LogId, LogSeverity, const char*, const char*, unsigned int, const char*); |
||||||
|
|
||||||
|
void DefaultAborter(const char* abort_message); |
||||||
|
|
||||||
|
std::string GetDefaultTag(); |
||||||
|
void SetDefaultTag(const std::string& tag); |
||||||
|
|
||||||
|
#ifdef __ANDROID__ |
||||||
|
// We expose this even though it is the default because a user that wants to
|
||||||
|
// override the default log buffer will have to construct this themselves.
|
||||||
|
class LogdLogger { |
||||||
|
public: |
||||||
|
explicit LogdLogger(LogId default_log_id = android_lkchan::base::MAIN); |
||||||
|
|
||||||
|
void operator()(LogId, LogSeverity, const char* tag, const char* file, |
||||||
|
unsigned int line, const char* message); |
||||||
|
|
||||||
|
private: |
||||||
|
LogId default_log_id_; |
||||||
|
}; |
||||||
|
#endif |
||||||
|
|
||||||
|
// Configure logging based on ANDROID_LOG_TAGS environment variable.
|
||||||
|
// We need to parse a string that looks like
|
||||||
|
//
|
||||||
|
// *:v jdwp:d dalvikvm:d dalvikvm-gc:i dalvikvmi:i
|
||||||
|
//
|
||||||
|
// The tag (or '*' for the global level) comes first, followed by a colon and a
|
||||||
|
// letter indicating the minimum priority level we're expected to log. This can
|
||||||
|
// be used to reveal or conceal logs with specific tags.
|
||||||
|
#ifdef __ANDROID__ |
||||||
|
#define INIT_LOGGING_DEFAULT_LOGGER LogdLogger() |
||||||
|
#else |
||||||
|
#define INIT_LOGGING_DEFAULT_LOGGER StderrLogger |
||||||
|
#endif |
||||||
|
void InitLogging(char* argv[], |
||||||
|
LogFunction&& logger = INIT_LOGGING_DEFAULT_LOGGER, |
||||||
|
AbortFunction&& aborter = DefaultAborter); |
||||||
|
#undef INIT_LOGGING_DEFAULT_LOGGER |
||||||
|
|
||||||
|
// Replace the current logger.
|
||||||
|
void SetLogger(LogFunction&& logger); |
||||||
|
|
||||||
|
// Replace the current aborter.
|
||||||
|
void SetAborter(AbortFunction&& aborter); |
||||||
|
|
||||||
|
class ErrnoRestorer { |
||||||
|
public: |
||||||
|
ErrnoRestorer() |
||||||
|
: saved_errno_(errno) { |
||||||
|
} |
||||||
|
|
||||||
|
~ErrnoRestorer() { |
||||||
|
errno = saved_errno_; |
||||||
|
} |
||||||
|
|
||||||
|
// Allow this object to be used as part of && operation.
|
||||||
|
operator bool() const { |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
private: |
||||||
|
const int saved_errno_; |
||||||
|
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(ErrnoRestorer); |
||||||
|
}; |
||||||
|
|
||||||
|
// A helper macro that produces an expression that accepts both a qualified name and an
|
||||||
|
// unqualified name for a LogSeverity, and returns a LogSeverity value.
|
||||||
|
// Note: DO NOT USE DIRECTLY. This is an implementation detail.
|
||||||
|
#define SEVERITY_LAMBDA(severity) ([&]() { \ |
||||||
|
using ::android_lkchan::base::VERBOSE; \
|
||||||
|
using ::android_lkchan::base::DEBUG; \
|
||||||
|
using ::android_lkchan::base::INFO; \
|
||||||
|
using ::android_lkchan::base::WARNING; \
|
||||||
|
using ::android_lkchan::base::ERROR; \
|
||||||
|
using ::android_lkchan::base::FATAL_WITHOUT_ABORT; \
|
||||||
|
using ::android_lkchan::base::FATAL; \
|
||||||
|
return (severity); }()) |
||||||
|
|
||||||
|
#ifdef __clang_analyzer__ |
||||||
|
// Clang's static analyzer does not see the conditional statement inside
|
||||||
|
// LogMessage's destructor that will abort on FATAL severity.
|
||||||
|
#define ABORT_AFTER_LOG_FATAL for (;; abort()) |
||||||
|
|
||||||
|
struct LogAbortAfterFullExpr { |
||||||
|
~LogAbortAfterFullExpr() __attribute__((noreturn)) { abort(); } |
||||||
|
explicit operator bool() const { return false; } |
||||||
|
}; |
||||||
|
// Provides an expression that evaluates to the truthiness of `x`, automatically
|
||||||
|
// aborting if `c` is true.
|
||||||
|
#define ABORT_AFTER_LOG_EXPR_IF(c, x) (((c) && ::android_lkchan::base::LogAbortAfterFullExpr()) || (x)) |
||||||
|
// Note to the static analyzer that we always execute FATAL logs in practice.
|
||||||
|
#define MUST_LOG_MESSAGE(severity) (SEVERITY_LAMBDA(severity) == ::android_lkchan::base::FATAL) |
||||||
|
#else |
||||||
|
#define ABORT_AFTER_LOG_FATAL |
||||||
|
#define ABORT_AFTER_LOG_EXPR_IF(c, x) (x) |
||||||
|
#define MUST_LOG_MESSAGE(severity) false |
||||||
|
#endif |
||||||
|
#define ABORT_AFTER_LOG_FATAL_EXPR(x) ABORT_AFTER_LOG_EXPR_IF(true, x) |
||||||
|
|
||||||
|
// Defines whether the given severity will be logged or silently swallowed.
|
||||||
|
#define WOULD_LOG(severity) \ |
||||||
|
(UNLIKELY((SEVERITY_LAMBDA(severity)) >= ::android_lkchan::base::GetMinimumLogSeverity()) || \
|
||||||
|
MUST_LOG_MESSAGE(severity)) |
||||||
|
|
||||||
|
// Get an ostream that can be used for logging at the given severity and to the default
|
||||||
|
// destination.
|
||||||
|
//
|
||||||
|
// Notes:
|
||||||
|
// 1) This will not check whether the severity is high enough. One should use WOULD_LOG to filter
|
||||||
|
// usage manually.
|
||||||
|
// 2) This does not save and restore errno.
|
||||||
|
#define LOG_STREAM(severity) LOG_STREAM_TO(DEFAULT, severity) |
||||||
|
|
||||||
|
// Get an ostream that can be used for logging at the given severity and to the
|
||||||
|
// given destination. The same notes as for LOG_STREAM apply.
|
||||||
|
#define LOG_STREAM_TO(dest, severity) \ |
||||||
|
::android_lkchan::base::LogMessage(__FILE__, __LINE__, ::android_lkchan::base::dest, \
|
||||||
|
SEVERITY_LAMBDA(severity), _LOG_TAG_INTERNAL, -1) \
|
||||||
|
.stream() |
||||||
|
|
||||||
|
// Logs a message to logcat on Android otherwise to stderr. If the severity is
|
||||||
|
// FATAL it also causes an abort. For example:
|
||||||
|
//
|
||||||
|
// LOG(FATAL) << "We didn't expect to reach here";
|
||||||
|
#define LOG(severity) LOG_TO(DEFAULT, severity) |
||||||
|
|
||||||
|
// Checks if we want to log something, and sets up appropriate RAII objects if
|
||||||
|
// so.
|
||||||
|
// Note: DO NOT USE DIRECTLY. This is an implementation detail.
|
||||||
|
#define LOGGING_PREAMBLE(severity) \ |
||||||
|
(WOULD_LOG(severity) && \
|
||||||
|
ABORT_AFTER_LOG_EXPR_IF((SEVERITY_LAMBDA(severity)) == ::android_lkchan::base::FATAL, true) && \
|
||||||
|
::android_lkchan::base::ErrnoRestorer()) |
||||||
|
|
||||||
|
// Logs a message to logcat with the specified log ID on Android otherwise to
|
||||||
|
// stderr. If the severity is FATAL it also causes an abort.
|
||||||
|
// Use an expression here so we can support the << operator following the macro,
|
||||||
|
// like "LOG(DEBUG) << xxx;".
|
||||||
|
#define LOG_TO(dest, severity) LOGGING_PREAMBLE(severity) && LOG_STREAM_TO(dest, severity) |
||||||
|
|
||||||
|
// A variant of LOG that also logs the current errno value. To be used when
|
||||||
|
// library calls fail.
|
||||||
|
#define PLOG(severity) PLOG_TO(DEFAULT, severity) |
||||||
|
|
||||||
|
// Behaves like PLOG, but logs to the specified log ID.
|
||||||
|
#define PLOG_TO(dest, severity) \ |
||||||
|
LOGGING_PREAMBLE(severity) && \
|
||||||
|
::android_lkchan::base::LogMessage(__FILE__, __LINE__, ::android_lkchan::base::dest, \
|
||||||
|
SEVERITY_LAMBDA(severity), _LOG_TAG_INTERNAL, errno) \
|
||||||
|
.stream() |
||||||
|
|
||||||
|
// Marker that code is yet to be implemented.
|
||||||
|
#define UNIMPLEMENTED(level) \ |
||||||
|
LOG(level) << __PRETTY_FUNCTION__ << " unimplemented " |
||||||
|
|
||||||
|
// Check whether condition x holds and LOG(FATAL) if not. The value of the
|
||||||
|
// expression x is only evaluated once. Extra logging can be appended using <<
|
||||||
|
// after. For example:
|
||||||
|
//
|
||||||
|
// CHECK(false == true) results in a log message of
|
||||||
|
// "Check failed: false == true".
|
||||||
|
#define CHECK(x) \ |
||||||
|
LIKELY((x)) || ABORT_AFTER_LOG_FATAL_EXPR(false) || \
|
||||||
|
::android_lkchan::base::LogMessage(__FILE__, __LINE__, ::android_lkchan::base::DEFAULT, \
|
||||||
|
::android_lkchan::base::FATAL, _LOG_TAG_INTERNAL, -1) \
|
||||||
|
.stream() \
|
||||||
|
<< "Check failed: " #x << " " |
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
// Helper for CHECK_xx(x,y) macros.
|
||||||
|
#define CHECK_OP(LHS, RHS, OP) \ |
||||||
|
for (auto _values = ::android_lkchan::base::MakeEagerEvaluator(LHS, RHS); \
|
||||||
|
UNLIKELY(!(_values.lhs OP _values.rhs)); \
|
||||||
|
/* empty */) \
|
||||||
|
ABORT_AFTER_LOG_FATAL \
|
||||||
|
::android_lkchan::base::LogMessage(__FILE__, __LINE__, ::android_lkchan::base::DEFAULT, \
|
||||||
|
::android_lkchan::base::FATAL, _LOG_TAG_INTERNAL, -1) \
|
||||||
|
.stream() \
|
||||||
|
<< "Check failed: " << #LHS << " " << #OP << " " << #RHS << " (" #LHS "=" << _values.lhs \
|
||||||
|
<< ", " #RHS "=" << _values.rhs << ") " |
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
// Check whether a condition holds between x and y, LOG(FATAL) if not. The value
|
||||||
|
// of the expressions x and y is evaluated once. Extra logging can be appended
|
||||||
|
// using << after. For example:
|
||||||
|
//
|
||||||
|
// CHECK_NE(0 == 1, false) results in
|
||||||
|
// "Check failed: false != false (0==1=false, false=false) ".
|
||||||
|
#define CHECK_EQ(x, y) CHECK_OP(x, y, == ) |
||||||
|
#define CHECK_NE(x, y) CHECK_OP(x, y, != ) |
||||||
|
#define CHECK_LE(x, y) CHECK_OP(x, y, <= ) |
||||||
|
#define CHECK_LT(x, y) CHECK_OP(x, y, < ) |
||||||
|
#define CHECK_GE(x, y) CHECK_OP(x, y, >= ) |
||||||
|
#define CHECK_GT(x, y) CHECK_OP(x, y, > ) |
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
// Helper for CHECK_STRxx(s1,s2) macros.
|
||||||
|
#define CHECK_STROP(s1, s2, sense) \ |
||||||
|
while (UNLIKELY((strcmp(s1, s2) == 0) != (sense))) \
|
||||||
|
ABORT_AFTER_LOG_FATAL \
|
||||||
|
::android_lkchan::base::LogMessage(__FILE__, __LINE__, ::android_lkchan::base::DEFAULT, \
|
||||||
|
::android_lkchan::base::FATAL, _LOG_TAG_INTERNAL, -1) \
|
||||||
|
.stream() \
|
||||||
|
<< "Check failed: " << "\"" << (s1) << "\"" \
|
||||||
|
<< ((sense) ? " == " : " != ") << "\"" << (s2) << "\"" |
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
// Check for string (const char*) equality between s1 and s2, LOG(FATAL) if not.
|
||||||
|
#define CHECK_STREQ(s1, s2) CHECK_STROP(s1, s2, true) |
||||||
|
#define CHECK_STRNE(s1, s2) CHECK_STROP(s1, s2, false) |
||||||
|
|
||||||
|
// Perform the pthread function call(args), LOG(FATAL) on error.
|
||||||
|
#define CHECK_PTHREAD_CALL(call, args, what) \ |
||||||
|
do { \
|
||||||
|
int rc = call args; \
|
||||||
|
if (rc != 0) { \
|
||||||
|
errno = rc; \
|
||||||
|
ABORT_AFTER_LOG_FATAL \
|
||||||
|
PLOG(FATAL) << #call << " failed for " << (what); \
|
||||||
|
} \
|
||||||
|
} while (false) |
||||||
|
|
||||||
|
// CHECK that can be used in a constexpr function. For example:
|
||||||
|
//
|
||||||
|
// constexpr int half(int n) {
|
||||||
|
// return
|
||||||
|
// DCHECK_CONSTEXPR(n >= 0, , 0)
|
||||||
|
// CHECK_CONSTEXPR((n & 1) == 0),
|
||||||
|
// << "Extra debugging output: n = " << n, 0)
|
||||||
|
// n / 2;
|
||||||
|
// }
|
||||||
|
#define CHECK_CONSTEXPR(x, out, dummy) \ |
||||||
|
(UNLIKELY(!(x))) \
|
||||||
|
? (LOG(FATAL) << "Check failed: " << #x out, dummy) \
|
||||||
|
: |
||||||
|
|
||||||
|
// DCHECKs are debug variants of CHECKs only enabled in debug builds. Generally
|
||||||
|
// CHECK should be used unless profiling identifies a CHECK as being in
|
||||||
|
// performance critical code.
|
||||||
|
#if defined(NDEBUG) && !defined(__clang_analyzer__) |
||||||
|
static constexpr bool kEnableDChecks = false; |
||||||
|
#else |
||||||
|
static constexpr bool kEnableDChecks = true; |
||||||
|
#endif |
||||||
|
|
||||||
|
#define DCHECK(x) \ |
||||||
|
if (::android_lkchan::base::kEnableDChecks) CHECK(x) |
||||||
|
#define DCHECK_EQ(x, y) \ |
||||||
|
if (::android_lkchan::base::kEnableDChecks) CHECK_EQ(x, y) |
||||||
|
#define DCHECK_NE(x, y) \ |
||||||
|
if (::android_lkchan::base::kEnableDChecks) CHECK_NE(x, y) |
||||||
|
#define DCHECK_LE(x, y) \ |
||||||
|
if (::android_lkchan::base::kEnableDChecks) CHECK_LE(x, y) |
||||||
|
#define DCHECK_LT(x, y) \ |
||||||
|
if (::android_lkchan::base::kEnableDChecks) CHECK_LT(x, y) |
||||||
|
#define DCHECK_GE(x, y) \ |
||||||
|
if (::android_lkchan::base::kEnableDChecks) CHECK_GE(x, y) |
||||||
|
#define DCHECK_GT(x, y) \ |
||||||
|
if (::android_lkchan::base::kEnableDChecks) CHECK_GT(x, y) |
||||||
|
#define DCHECK_STREQ(s1, s2) \ |
||||||
|
if (::android_lkchan::base::kEnableDChecks) CHECK_STREQ(s1, s2) |
||||||
|
#define DCHECK_STRNE(s1, s2) \ |
||||||
|
if (::android_lkchan::base::kEnableDChecks) CHECK_STRNE(s1, s2) |
||||||
|
#if defined(NDEBUG) && !defined(__clang_analyzer__) |
||||||
|
#define DCHECK_CONSTEXPR(x, out, dummy) |
||||||
|
#else |
||||||
|
#define DCHECK_CONSTEXPR(x, out, dummy) CHECK_CONSTEXPR(x, out, dummy) |
||||||
|
#endif |
||||||
|
|
||||||
|
// Temporary class created to evaluate the LHS and RHS, used with
|
||||||
|
// MakeEagerEvaluator to infer the types of LHS and RHS.
|
||||||
|
template <typename LHS, typename RHS> |
||||||
|
struct EagerEvaluator { |
||||||
|
constexpr EagerEvaluator(LHS l, RHS r) : lhs(l), rhs(r) { |
||||||
|
} |
||||||
|
LHS lhs; |
||||||
|
RHS rhs; |
||||||
|
}; |
||||||
|
|
||||||
|
// Helper function for CHECK_xx.
|
||||||
|
template <typename LHS, typename RHS> |
||||||
|
constexpr EagerEvaluator<LHS, RHS> MakeEagerEvaluator(LHS lhs, RHS rhs) { |
||||||
|
return EagerEvaluator<LHS, RHS>(lhs, rhs); |
||||||
|
} |
||||||
|
|
||||||
|
// Explicitly instantiate EagerEvalue for pointers so that char*s aren't treated
|
||||||
|
// as strings. To compare strings use CHECK_STREQ and CHECK_STRNE. We rely on
|
||||||
|
// signed/unsigned warnings to protect you against combinations not explicitly
|
||||||
|
// listed below.
|
||||||
|
#define EAGER_PTR_EVALUATOR(T1, T2) \ |
||||||
|
template <> \
|
||||||
|
struct EagerEvaluator<T1, T2> { \
|
||||||
|
EagerEvaluator(T1 l, T2 r) \
|
||||||
|
: lhs(reinterpret_cast<const void*>(l)), \
|
||||||
|
rhs(reinterpret_cast<const void*>(r)) { \
|
||||||
|
} \
|
||||||
|
const void* lhs; \
|
||||||
|
const void* rhs; \
|
||||||
|
} |
||||||
|
EAGER_PTR_EVALUATOR(const char*, const char*); |
||||||
|
EAGER_PTR_EVALUATOR(const char*, char*); |
||||||
|
EAGER_PTR_EVALUATOR(char*, const char*); |
||||||
|
EAGER_PTR_EVALUATOR(char*, char*); |
||||||
|
EAGER_PTR_EVALUATOR(const unsigned char*, const unsigned char*); |
||||||
|
EAGER_PTR_EVALUATOR(const unsigned char*, unsigned char*); |
||||||
|
EAGER_PTR_EVALUATOR(unsigned char*, const unsigned char*); |
||||||
|
EAGER_PTR_EVALUATOR(unsigned char*, unsigned char*); |
||||||
|
EAGER_PTR_EVALUATOR(const signed char*, const signed char*); |
||||||
|
EAGER_PTR_EVALUATOR(const signed char*, signed char*); |
||||||
|
EAGER_PTR_EVALUATOR(signed char*, const signed char*); |
||||||
|
EAGER_PTR_EVALUATOR(signed char*, signed char*); |
||||||
|
|
||||||
|
// Data for the log message, not stored in LogMessage to avoid increasing the
|
||||||
|
// stack size.
|
||||||
|
class LogMessageData; |
||||||
|
|
||||||
|
// A LogMessage is a temporarily scoped object used by LOG and the unlikely part
|
||||||
|
// of a CHECK. The destructor will abort if the severity is FATAL.
|
||||||
|
class LogMessage { |
||||||
|
public: |
||||||
|
LogMessage(const char* file, unsigned int line, LogId id, LogSeverity severity, const char* tag, |
||||||
|
int error); |
||||||
|
|
||||||
|
~LogMessage(); |
||||||
|
|
||||||
|
// Returns the stream associated with the message, the LogMessage performs
|
||||||
|
// output when it goes out of scope.
|
||||||
|
std::ostream& stream(); |
||||||
|
|
||||||
|
// The routine that performs the actual logging.
|
||||||
|
static void LogLine(const char* file, unsigned int line, LogId id, LogSeverity severity, |
||||||
|
const char* tag, const char* msg); |
||||||
|
|
||||||
|
private: |
||||||
|
const std::unique_ptr<LogMessageData> data_; |
||||||
|
|
||||||
|
// TODO(b/35361699): remove these symbols once all prebuilds stop using it.
|
||||||
|
LogMessage(const char* file, unsigned int line, LogId id, LogSeverity severity, int error); |
||||||
|
static void LogLine(const char* file, unsigned int line, LogId id, LogSeverity severity, |
||||||
|
const char* msg); |
||||||
|
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(LogMessage); |
||||||
|
}; |
||||||
|
|
||||||
|
// Get the minimum severity level for logging.
|
||||||
|
LogSeverity GetMinimumLogSeverity(); |
||||||
|
|
||||||
|
// Set the minimum severity level for logging, returning the old severity.
|
||||||
|
LogSeverity SetMinimumLogSeverity(LogSeverity new_severity); |
||||||
|
|
||||||
|
// Allows to temporarily change the minimum severity level for logging.
|
||||||
|
class ScopedLogSeverity { |
||||||
|
public: |
||||||
|
explicit ScopedLogSeverity(LogSeverity level); |
||||||
|
~ScopedLogSeverity(); |
||||||
|
|
||||||
|
private: |
||||||
|
LogSeverity old_; |
||||||
|
}; |
||||||
|
|
||||||
|
} // namespace base
|
||||||
|
} // namespace android_lkchan
|
||||||
|
|
||||||
|
namespace std { |
||||||
|
|
||||||
|
// Emit a warning of ostream<< with std::string*. The intention was most likely to print *string.
|
||||||
|
//
|
||||||
|
// Note: for this to work, we need to have this in a namespace.
|
||||||
|
// Note: lots of ifdef magic to make this work with Clang (platform) vs GCC (windows tools)
|
||||||
|
// Note: using diagnose_if(true) under Clang and nothing under GCC/mingw as there is no common
|
||||||
|
// attribute support.
|
||||||
|
// Note: using a pragma because "-Wgcc-compat" (included in "-Weverything") complains about
|
||||||
|
// diagnose_if.
|
||||||
|
// Note: to print the pointer, use "<< static_cast<const void*>(string_pointer)" instead.
|
||||||
|
// Note: a not-recommended alternative is to let Clang ignore the warning by adding
|
||||||
|
// -Wno-user-defined-warnings to CPPFLAGS.
|
||||||
|
#ifdef __clang__ |
||||||
|
#pragma clang diagnostic push |
||||||
|
#pragma clang diagnostic ignored "-Wgcc-compat" |
||||||
|
#define OSTREAM_STRING_POINTER_USAGE_WARNING \ |
||||||
|
__attribute__((diagnose_if(true, "Unexpected logging of string pointer", "warning"))) |
||||||
|
#else |
||||||
|
#define OSTREAM_STRING_POINTER_USAGE_WARNING /* empty */ |
||||||
|
#endif |
||||||
|
inline std::ostream& operator<<(std::ostream& stream, const std::string* string_pointer) |
||||||
|
OSTREAM_STRING_POINTER_USAGE_WARNING { |
||||||
|
return stream << static_cast<const void*>(string_pointer); |
||||||
|
} |
||||||
|
#ifdef __clang__ |
||||||
|
#pragma clang diagnostic pop |
||||||
|
#endif |
||||||
|
#undef OSTREAM_STRING_POINTER_USAGE_WARNING |
||||||
|
|
||||||
|
} // namespace std
|
||||||
|
|
||||||
|
#endif // ANDROID_BASE_LOGGING_H
|
@ -0,0 +1,197 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2015 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 ANDROID_BASE_MACROS_H |
||||||
|
#define ANDROID_BASE_MACROS_H |
||||||
|
|
||||||
|
#include <stddef.h> // for size_t |
||||||
|
#include <unistd.h> // for TEMP_FAILURE_RETRY |
||||||
|
|
||||||
|
// bionic and glibc both have TEMP_FAILURE_RETRY, but eg Mac OS' libc doesn't.
|
||||||
|
#ifndef TEMP_FAILURE_RETRY |
||||||
|
#define TEMP_FAILURE_RETRY(exp) \ |
||||||
|
({ \
|
||||||
|
decltype(exp) _rc; \
|
||||||
|
do { \
|
||||||
|
_rc = (exp); \
|
||||||
|
} while (_rc == -1 && errno == EINTR); \
|
||||||
|
_rc; \
|
||||||
|
}) |
||||||
|
#endif |
||||||
|
|
||||||
|
// A macro to disallow the copy constructor and operator= functions
|
||||||
|
// This must be placed in the private: declarations for a class.
|
||||||
|
//
|
||||||
|
// For disallowing only assign or copy, delete the relevant operator or
|
||||||
|
// constructor, for example:
|
||||||
|
// void operator=(const TypeName&) = delete;
|
||||||
|
// Note, that most uses of DISALLOW_ASSIGN and DISALLOW_COPY are broken
|
||||||
|
// semantically, one should either use disallow both or neither. Try to
|
||||||
|
// avoid these in new code.
|
||||||
|
#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ |
||||||
|
TypeName(const TypeName&) = delete; \
|
||||||
|
void operator=(const TypeName&) = delete |
||||||
|
|
||||||
|
// A macro to disallow all the implicit constructors, namely the
|
||||||
|
// default constructor, copy constructor and operator= functions.
|
||||||
|
//
|
||||||
|
// This should be used in the private: declarations for a class
|
||||||
|
// that wants to prevent anyone from instantiating it. This is
|
||||||
|
// especially useful for classes containing only static methods.
|
||||||
|
#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \ |
||||||
|
TypeName() = delete; \
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(TypeName) |
||||||
|
|
||||||
|
// The arraysize(arr) macro returns the # of elements in an array arr.
|
||||||
|
// The expression is a compile-time constant, and therefore can be
|
||||||
|
// used in defining new arrays, for example. If you use arraysize on
|
||||||
|
// a pointer by mistake, you will get a compile-time error.
|
||||||
|
//
|
||||||
|
// One caveat is that arraysize() doesn't accept any array of an
|
||||||
|
// anonymous type or a type defined inside a function. In these rare
|
||||||
|
// cases, you have to use the unsafe ARRAYSIZE_UNSAFE() macro below. This is
|
||||||
|
// due to a limitation in C++'s template system. The limitation might
|
||||||
|
// eventually be removed, but it hasn't happened yet.
|
||||||
|
|
||||||
|
// This template function declaration is used in defining arraysize.
|
||||||
|
// Note that the function doesn't need an implementation, as we only
|
||||||
|
// use its type.
|
||||||
|
template <typename T, size_t N> |
||||||
|
char(&ArraySizeHelper(T(&array)[N]))[N]; // NOLINT(readability/casting)
|
||||||
|
|
||||||
|
#define arraysize(array) (sizeof(ArraySizeHelper(array))) |
||||||
|
|
||||||
|
// ARRAYSIZE_UNSAFE performs essentially the same calculation as arraysize,
|
||||||
|
// but can be used on anonymous types or types defined inside
|
||||||
|
// functions. It's less safe than arraysize as it accepts some
|
||||||
|
// (although not all) pointers. Therefore, you should use arraysize
|
||||||
|
// whenever possible.
|
||||||
|
//
|
||||||
|
// The expression ARRAYSIZE_UNSAFE(a) is a compile-time constant of type
|
||||||
|
// size_t.
|
||||||
|
//
|
||||||
|
// ARRAYSIZE_UNSAFE catches a few type errors. If you see a compiler error
|
||||||
|
//
|
||||||
|
// "warning: division by zero in ..."
|
||||||
|
//
|
||||||
|
// when using ARRAYSIZE_UNSAFE, you are (wrongfully) giving it a pointer.
|
||||||
|
// You should only use ARRAYSIZE_UNSAFE on statically allocated arrays.
|
||||||
|
//
|
||||||
|
// The following comments are on the implementation details, and can
|
||||||
|
// be ignored by the users.
|
||||||
|
//
|
||||||
|
// ARRAYSIZE_UNSAFE(arr) works by inspecting sizeof(arr) (the # of bytes in
|
||||||
|
// the array) and sizeof(*(arr)) (the # of bytes in one array
|
||||||
|
// element). If the former is divisible by the latter, perhaps arr is
|
||||||
|
// indeed an array, in which case the division result is the # of
|
||||||
|
// elements in the array. Otherwise, arr cannot possibly be an array,
|
||||||
|
// and we generate a compiler error to prevent the code from
|
||||||
|
// compiling.
|
||||||
|
//
|
||||||
|
// Since the size of bool is implementation-defined, we need to cast
|
||||||
|
// !(sizeof(a) & sizeof(*(a))) to size_t in order to ensure the final
|
||||||
|
// result has type size_t.
|
||||||
|
//
|
||||||
|
// This macro is not perfect as it wrongfully accepts certain
|
||||||
|
// pointers, namely where the pointer size is divisible by the pointee
|
||||||
|
// size. Since all our code has to go through a 32-bit compiler,
|
||||||
|
// where a pointer is 4 bytes, this means all pointers to a type whose
|
||||||
|
// size is 3 or greater than 4 will be (righteously) rejected.
|
||||||
|
#define ARRAYSIZE_UNSAFE(a) \ |
||||||
|
((sizeof(a) / sizeof(*(a))) / \
|
||||||
|
static_cast<size_t>(!(sizeof(a) % sizeof(*(a))))) |
||||||
|
|
||||||
|
// Changing this definition will cause you a lot of pain. A majority of
|
||||||
|
// vendor code defines LIKELY and UNLIKELY this way, and includes
|
||||||
|
// this header through an indirect path.
|
||||||
|
#define LIKELY( exp ) (__builtin_expect( (exp) != 0, true )) |
||||||
|
#define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false )) |
||||||
|
|
||||||
|
#define WARN_UNUSED __attribute__((warn_unused_result)) |
||||||
|
|
||||||
|
// A deprecated function to call to create a false use of the parameter, for
|
||||||
|
// example:
|
||||||
|
// int foo(int x) { UNUSED(x); return 10; }
|
||||||
|
// to avoid compiler warnings. Going forward we prefer ATTRIBUTE_UNUSED.
|
||||||
|
template <typename... T> |
||||||
|
void UNUSED(const T&...) { |
||||||
|
} |
||||||
|
|
||||||
|
// An attribute to place on a parameter to a function, for example:
|
||||||
|
// int foo(int x ATTRIBUTE_UNUSED) { return 10; }
|
||||||
|
// to avoid compiler warnings.
|
||||||
|
#define ATTRIBUTE_UNUSED __attribute__((__unused__)) |
||||||
|
|
||||||
|
// The FALLTHROUGH_INTENDED macro can be used to annotate implicit fall-through
|
||||||
|
// between switch labels:
|
||||||
|
// switch (x) {
|
||||||
|
// case 40:
|
||||||
|
// case 41:
|
||||||
|
// if (truth_is_out_there) {
|
||||||
|
// ++x;
|
||||||
|
// FALLTHROUGH_INTENDED; // Use instead of/along with annotations in
|
||||||
|
// // comments.
|
||||||
|
// } else {
|
||||||
|
// return x;
|
||||||
|
// }
|
||||||
|
// case 42:
|
||||||
|
// ...
|
||||||
|
//
|
||||||
|
// As shown in the example above, the FALLTHROUGH_INTENDED macro should be
|
||||||
|
// followed by a semicolon. It is designed to mimic control-flow statements
|
||||||
|
// like 'break;', so it can be placed in most places where 'break;' can, but
|
||||||
|
// only if there are no statements on the execution path between it and the
|
||||||
|
// next switch label.
|
||||||
|
//
|
||||||
|
// When compiled with clang, the FALLTHROUGH_INTENDED macro is expanded to
|
||||||
|
// [[clang::fallthrough]] attribute, which is analysed when performing switch
|
||||||
|
// labels fall-through diagnostic ('-Wimplicit-fallthrough'). See clang
|
||||||
|
// documentation on language extensions for details:
|
||||||
|
// http://clang.llvm.org/docs/LanguageExtensions.html#clang__fallthrough
|
||||||
|
//
|
||||||
|
// When used with unsupported compilers, the FALLTHROUGH_INTENDED macro has no
|
||||||
|
// effect on diagnostics.
|
||||||
|
//
|
||||||
|
// In either case this macro has no effect on runtime behavior and performance
|
||||||
|
// of code.
|
||||||
|
#if defined(__clang__) && defined(__has_warning) |
||||||
|
#if __has_feature(cxx_attributes) && __has_warning("-Wimplicit-fallthrough") |
||||||
|
#define FALLTHROUGH_INTENDED [[clang::fallthrough]] // NOLINT
|
||||||
|
#endif |
||||||
|
#endif |
||||||
|
|
||||||
|
#ifndef FALLTHROUGH_INTENDED |
||||||
|
#define FALLTHROUGH_INTENDED \ |
||||||
|
do { \
|
||||||
|
} while (0) |
||||||
|
#endif |
||||||
|
|
||||||
|
// Current ABI string
|
||||||
|
#if defined(__arm__) |
||||||
|
#define ABI_STRING "arm" |
||||||
|
#elif defined(__aarch64__) |
||||||
|
#define ABI_STRING "arm64" |
||||||
|
#elif defined(__i386__) |
||||||
|
#define ABI_STRING "x86" |
||||||
|
#elif defined(__x86_64__) |
||||||
|
#define ABI_STRING "x86_64" |
||||||
|
#elif defined(__mips__) && !defined(__LP64__) |
||||||
|
#define ABI_STRING "mips" |
||||||
|
#elif defined(__mips__) && defined(__LP64__) |
||||||
|
#define ABI_STRING "mips64" |
||||||
|
#endif |
||||||
|
|
||||||
|
#endif // ANDROID_BASE_MACROS_H
|
@ -0,0 +1,41 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2015 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 ANDROID_BASE_MEMORY_H |
||||||
|
#define ANDROID_BASE_MEMORY_H |
||||||
|
|
||||||
|
namespace android_lkchan { |
||||||
|
namespace base { |
||||||
|
|
||||||
|
// Use memcpy for access to unaligned data on targets with alignment
|
||||||
|
// restrictions. The compiler will generate appropriate code to access these
|
||||||
|
// structures without generating alignment exceptions.
|
||||||
|
template <typename T> |
||||||
|
static inline T get_unaligned(const void* address) { |
||||||
|
T result; |
||||||
|
memcpy(&result, address, sizeof(T)); |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
template <typename T> |
||||||
|
static inline void put_unaligned(void* address, T v) { |
||||||
|
memcpy(address, &v, sizeof(T)); |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace base
|
||||||
|
} // namespace android_lkchan
|
||||||
|
|
||||||
|
#endif // ANDROID_BASE_MEMORY_H
|
@ -0,0 +1,85 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011 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. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include "android-base/stringprintf.h" |
||||||
|
|
||||||
|
#include <stdio.h> |
||||||
|
|
||||||
|
#include <string> |
||||||
|
|
||||||
|
namespace android_lkchan { |
||||||
|
namespace base { |
||||||
|
|
||||||
|
void StringAppendV(std::string* dst, const char* format, va_list ap) { |
||||||
|
// First try with a small fixed size buffer
|
||||||
|
char space[1024]; |
||||||
|
|
||||||
|
// It's possible for methods that use a va_list to invalidate
|
||||||
|
// the data in it upon use. The fix is to make a copy
|
||||||
|
// of the structure before using it and use that copy instead.
|
||||||
|
va_list backup_ap; |
||||||
|
va_copy(backup_ap, ap); |
||||||
|
int result = vsnprintf(space, sizeof(space), format, backup_ap); |
||||||
|
va_end(backup_ap); |
||||||
|
|
||||||
|
if (result < static_cast<int>(sizeof(space))) { |
||||||
|
if (result >= 0) { |
||||||
|
// Normal case -- everything fit.
|
||||||
|
dst->append(space, result); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
if (result < 0) { |
||||||
|
// Just an error.
|
||||||
|
return; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Increase the buffer size to the size requested by vsnprintf,
|
||||||
|
// plus one for the closing \0.
|
||||||
|
int length = result + 1; |
||||||
|
char* buf = new char[length]; |
||||||
|
|
||||||
|
// Restore the va_list before we use it again
|
||||||
|
va_copy(backup_ap, ap); |
||||||
|
result = vsnprintf(buf, length, format, backup_ap); |
||||||
|
va_end(backup_ap); |
||||||
|
|
||||||
|
if (result >= 0 && result < length) { |
||||||
|
// It fit
|
||||||
|
dst->append(buf, result); |
||||||
|
} |
||||||
|
delete[] buf; |
||||||
|
} |
||||||
|
|
||||||
|
std::string StringPrintf(const char* fmt, ...) { |
||||||
|
va_list ap; |
||||||
|
va_start(ap, fmt); |
||||||
|
std::string result; |
||||||
|
StringAppendV(&result, fmt, ap); |
||||||
|
va_end(ap); |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
void StringAppendF(std::string* dst, const char* format, ...) { |
||||||
|
va_list ap; |
||||||
|
va_start(ap, format); |
||||||
|
StringAppendV(dst, format, ap); |
||||||
|
va_end(ap); |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace base
|
||||||
|
} // namespace android_lkchan
|
@ -0,0 +1,56 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011 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 ANDROID_BASE_STRINGPRINTF_H |
||||||
|
#define ANDROID_BASE_STRINGPRINTF_H |
||||||
|
|
||||||
|
#include <stdarg.h> |
||||||
|
#include <string> |
||||||
|
|
||||||
|
namespace android_lkchan { |
||||||
|
namespace base { |
||||||
|
|
||||||
|
// These printf-like functions are implemented in terms of vsnprintf, so they
|
||||||
|
// use the same attribute for compile-time format string checking. On Windows,
|
||||||
|
// if the mingw version of vsnprintf is used, use `gnu_printf' which allows z
|
||||||
|
// in %zd and PRIu64 (and related) to be recognized by the compile-time
|
||||||
|
// checking.
|
||||||
|
#define ANDROID_BASE_FORMAT_ARCHETYPE __printf__ |
||||||
|
#ifdef __USE_MINGW_ANSI_STDIO |
||||||
|
#if __USE_MINGW_ANSI_STDIO |
||||||
|
#undef ANDROID_BASE_FORMAT_ARCHETYPE |
||||||
|
#define ANDROID_BASE_FORMAT_ARCHETYPE gnu_printf |
||||||
|
#endif |
||||||
|
#endif |
||||||
|
|
||||||
|
// Returns a string corresponding to printf-like formatting of the arguments.
|
||||||
|
std::string StringPrintf(const char* fmt, ...) |
||||||
|
__attribute__((__format__(ANDROID_BASE_FORMAT_ARCHETYPE, 1, 2))); |
||||||
|
|
||||||
|
// Appends a printf-like formatting of the arguments to 'dst'.
|
||||||
|
void StringAppendF(std::string* dst, const char* fmt, ...) |
||||||
|
__attribute__((__format__(ANDROID_BASE_FORMAT_ARCHETYPE, 2, 3))); |
||||||
|
|
||||||
|
// Appends a printf-like formatting of the arguments to 'dst'.
|
||||||
|
void StringAppendV(std::string* dst, const char* format, va_list ap) |
||||||
|
__attribute__((__format__(ANDROID_BASE_FORMAT_ARCHETYPE, 2, 0))); |
||||||
|
|
||||||
|
#undef ANDROID_BASE_FORMAT_ARCHETYPE |
||||||
|
|
||||||
|
} // namespace base
|
||||||
|
} // namespace android_lkchan
|
||||||
|
|
||||||
|
#endif // ANDROID_BASE_STRINGPRINTF_H
|
@ -0,0 +1,137 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2015 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. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include "android-base/strings.h" |
||||||
|
|
||||||
|
#include <stdlib.h> |
||||||
|
#include <string.h> |
||||||
|
|
||||||
|
#include <string> |
||||||
|
#include <vector> |
||||||
|
|
||||||
|
namespace android_lkchan { |
||||||
|
namespace base { |
||||||
|
|
||||||
|
#define CHECK_NE(a, b) \ |
||||||
|
if ((a) == (b)) abort(); |
||||||
|
|
||||||
|
std::vector<std::string> Split(const std::string& s, |
||||||
|
const std::string& delimiters) { |
||||||
|
CHECK_NE(delimiters.size(), 0U); |
||||||
|
|
||||||
|
std::vector<std::string> result; |
||||||
|
|
||||||
|
size_t base = 0; |
||||||
|
size_t found; |
||||||
|
while (true) { |
||||||
|
found = s.find_first_of(delimiters, base); |
||||||
|
result.push_back(s.substr(base, found - base)); |
||||||
|
if (found == s.npos) break; |
||||||
|
base = found + 1; |
||||||
|
} |
||||||
|
|
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
std::string Trim(const std::string& s) { |
||||||
|
std::string result; |
||||||
|
|
||||||
|
if (s.size() == 0) { |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
size_t start_index = 0; |
||||||
|
size_t end_index = s.size() - 1; |
||||||
|
|
||||||
|
// Skip initial whitespace.
|
||||||
|
while (start_index < s.size()) { |
||||||
|
if (!isspace(s[start_index])) { |
||||||
|
break; |
||||||
|
} |
||||||
|
start_index++; |
||||||
|
} |
||||||
|
|
||||||
|
// Skip terminating whitespace.
|
||||||
|
while (end_index >= start_index) { |
||||||
|
if (!isspace(s[end_index])) { |
||||||
|
break; |
||||||
|
} |
||||||
|
end_index--; |
||||||
|
} |
||||||
|
|
||||||
|
// All spaces, no beef.
|
||||||
|
if (end_index < start_index) { |
||||||
|
return ""; |
||||||
|
} |
||||||
|
// Start_index is the first non-space, end_index is the last one.
|
||||||
|
return s.substr(start_index, end_index - start_index + 1); |
||||||
|
} |
||||||
|
|
||||||
|
// These cases are probably the norm, so we mark them extern in the header to
|
||||||
|
// aid compile time and binary size.
|
||||||
|
template std::string Join(const std::vector<std::string>&, char); |
||||||
|
template std::string Join(const std::vector<const char*>&, char); |
||||||
|
template std::string Join(const std::vector<std::string>&, const std::string&); |
||||||
|
template std::string Join(const std::vector<const char*>&, const std::string&); |
||||||
|
|
||||||
|
bool StartsWith(const std::string& s, const char* prefix) { |
||||||
|
return strncmp(s.c_str(), prefix, strlen(prefix)) == 0; |
||||||
|
} |
||||||
|
|
||||||
|
bool StartsWith(const std::string& s, const std::string& prefix) { |
||||||
|
return strncmp(s.c_str(), prefix.c_str(), prefix.size()) == 0; |
||||||
|
} |
||||||
|
|
||||||
|
bool StartsWithIgnoreCase(const std::string& s, const char* prefix) { |
||||||
|
return strncasecmp(s.c_str(), prefix, strlen(prefix)) == 0; |
||||||
|
} |
||||||
|
|
||||||
|
bool StartsWithIgnoreCase(const std::string& s, const std::string& prefix) { |
||||||
|
return strncasecmp(s.c_str(), prefix.c_str(), prefix.size()) == 0; |
||||||
|
} |
||||||
|
|
||||||
|
static bool EndsWith(const std::string& s, const char* suffix, size_t suffix_length, |
||||||
|
bool case_sensitive) { |
||||||
|
size_t string_length = s.size(); |
||||||
|
if (suffix_length > string_length) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
size_t offset = string_length - suffix_length; |
||||||
|
return (case_sensitive ? strncmp : strncasecmp)(s.c_str() + offset, suffix, suffix_length) == 0; |
||||||
|
} |
||||||
|
|
||||||
|
bool EndsWith(const std::string& s, const char* suffix) { |
||||||
|
return EndsWith(s, suffix, strlen(suffix), true); |
||||||
|
} |
||||||
|
|
||||||
|
bool EndsWith(const std::string& s, const std::string& suffix) { |
||||||
|
return EndsWith(s, suffix.c_str(), suffix.size(), true); |
||||||
|
} |
||||||
|
|
||||||
|
bool EndsWithIgnoreCase(const std::string& s, const char* suffix) { |
||||||
|
return EndsWith(s, suffix, strlen(suffix), false); |
||||||
|
} |
||||||
|
|
||||||
|
bool EndsWithIgnoreCase(const std::string& s, const std::string& suffix) { |
||||||
|
return EndsWith(s, suffix.c_str(), suffix.size(), false); |
||||||
|
} |
||||||
|
|
||||||
|
bool EqualsIgnoreCase(const std::string& lhs, const std::string& rhs) { |
||||||
|
return strcasecmp(lhs.c_str(), rhs.c_str()) == 0; |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace base
|
||||||
|
} // namespace android_lkchan
|
@ -0,0 +1,79 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2015 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 ANDROID_BASE_STRINGS_H |
||||||
|
#define ANDROID_BASE_STRINGS_H |
||||||
|
|
||||||
|
#include <sstream> |
||||||
|
#include <string> |
||||||
|
#include <vector> |
||||||
|
|
||||||
|
namespace android_lkchan { |
||||||
|
namespace base { |
||||||
|
|
||||||
|
// Splits a string into a vector of strings.
|
||||||
|
//
|
||||||
|
// The string is split at each occurrence of a character in delimiters.
|
||||||
|
//
|
||||||
|
// The empty string is not a valid delimiter list.
|
||||||
|
std::vector<std::string> Split(const std::string& s, |
||||||
|
const std::string& delimiters); |
||||||
|
|
||||||
|
// Trims whitespace off both ends of the given string.
|
||||||
|
std::string Trim(const std::string& s); |
||||||
|
|
||||||
|
// Joins a container of things into a single string, using the given separator.
|
||||||
|
template <typename ContainerT, typename SeparatorT> |
||||||
|
std::string Join(const ContainerT& things, SeparatorT separator) { |
||||||
|
if (things.empty()) { |
||||||
|
return ""; |
||||||
|
} |
||||||
|
|
||||||
|
std::ostringstream result; |
||||||
|
result << *things.begin(); |
||||||
|
for (auto it = std::next(things.begin()); it != things.end(); ++it) { |
||||||
|
result << separator << *it; |
||||||
|
} |
||||||
|
return result.str(); |
||||||
|
} |
||||||
|
|
||||||
|
// We instantiate the common cases in strings.cpp.
|
||||||
|
extern template std::string Join(const std::vector<std::string>&, char); |
||||||
|
extern template std::string Join(const std::vector<const char*>&, char); |
||||||
|
extern template std::string Join(const std::vector<std::string>&, const std::string&); |
||||||
|
extern template std::string Join(const std::vector<const char*>&, const std::string&); |
||||||
|
|
||||||
|
// Tests whether 's' starts with 'prefix'.
|
||||||
|
// TODO: string_view
|
||||||
|
bool StartsWith(const std::string& s, const char* prefix); |
||||||
|
bool StartsWithIgnoreCase(const std::string& s, const char* prefix); |
||||||
|
bool StartsWith(const std::string& s, const std::string& prefix); |
||||||
|
bool StartsWithIgnoreCase(const std::string& s, const std::string& prefix); |
||||||
|
|
||||||
|
// Tests whether 's' ends with 'suffix'.
|
||||||
|
// TODO: string_view
|
||||||
|
bool EndsWith(const std::string& s, const char* suffix); |
||||||
|
bool EndsWithIgnoreCase(const std::string& s, const char* suffix); |
||||||
|
bool EndsWith(const std::string& s, const std::string& suffix); |
||||||
|
bool EndsWithIgnoreCase(const std::string& s, const std::string& suffix); |
||||||
|
|
||||||
|
// Tests whether 'lhs' equals 'rhs', ignoring case.
|
||||||
|
bool EqualsIgnoreCase(const std::string& lhs, const std::string& rhs); |
||||||
|
|
||||||
|
} // namespace base
|
||||||
|
} // namespace android_lkchan
|
||||||
|
|
||||||
|
#endif // ANDROID_BASE_STRINGS_H
|
@ -0,0 +1,113 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 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 ANDROID_BASE_THREAD_ANNOTATIONS_H |
||||||
|
#define ANDROID_BASE_THREAD_ANNOTATIONS_H |
||||||
|
|
||||||
|
#if defined(__SUPPORT_TS_ANNOTATION__) || defined(__clang__) |
||||||
|
#define THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x)) |
||||||
|
#else |
||||||
|
#define THREAD_ANNOTATION_ATTRIBUTE__(x) // no-op
|
||||||
|
#endif |
||||||
|
|
||||||
|
#define CAPABILITY(x) \ |
||||||
|
THREAD_ANNOTATION_ATTRIBUTE__(capability(x)) |
||||||
|
|
||||||
|
#define SCOPED_CAPABILITY \ |
||||||
|
THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable) |
||||||
|
|
||||||
|
#define SHARED_CAPABILITY(...) \ |
||||||
|
THREAD_ANNOTATION_ATTRIBUTE__(shared_capability(__VA_ARGS__)) |
||||||
|
|
||||||
|
#define GUARDED_BY(x) \ |
||||||
|
THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x)) |
||||||
|
|
||||||
|
#define PT_GUARDED_BY(x) \ |
||||||
|
THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_by(x)) |
||||||
|
|
||||||
|
#define EXCLUSIVE_LOCKS_REQUIRED(...) \ |
||||||
|
THREAD_ANNOTATION_ATTRIBUTE__(exclusive_locks_required(__VA_ARGS__)) |
||||||
|
|
||||||
|
#define SHARED_LOCKS_REQUIRED(...) \ |
||||||
|
THREAD_ANNOTATION_ATTRIBUTE__(shared_locks_required(__VA_ARGS__)) |
||||||
|
|
||||||
|
#define ACQUIRED_BEFORE(...) \ |
||||||
|
THREAD_ANNOTATION_ATTRIBUTE__(acquired_before(__VA_ARGS__)) |
||||||
|
|
||||||
|
#define ACQUIRED_AFTER(...) \ |
||||||
|
THREAD_ANNOTATION_ATTRIBUTE__(acquired_after(__VA_ARGS__)) |
||||||
|
|
||||||
|
#define REQUIRES(...) \ |
||||||
|
THREAD_ANNOTATION_ATTRIBUTE__(requires_capability(__VA_ARGS__)) |
||||||
|
|
||||||
|
#define REQUIRES_SHARED(...) \ |
||||||
|
THREAD_ANNOTATION_ATTRIBUTE__(requires_shared_capability(__VA_ARGS__)) |
||||||
|
|
||||||
|
#define ACQUIRE(...) \ |
||||||
|
THREAD_ANNOTATION_ATTRIBUTE__(acquire_capability(__VA_ARGS__)) |
||||||
|
|
||||||
|
#define ACQUIRE_SHARED(...) \ |
||||||
|
THREAD_ANNOTATION_ATTRIBUTE__(acquire_shared_capability(__VA_ARGS__)) |
||||||
|
|
||||||
|
#define RELEASE(...) \ |
||||||
|
THREAD_ANNOTATION_ATTRIBUTE__(release_capability(__VA_ARGS__)) |
||||||
|
|
||||||
|
#define RELEASE_SHARED(...) \ |
||||||
|
THREAD_ANNOTATION_ATTRIBUTE__(release_shared_capability(__VA_ARGS__)) |
||||||
|
|
||||||
|
#define TRY_ACQUIRE(...) \ |
||||||
|
THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_capability(__VA_ARGS__)) |
||||||
|
|
||||||
|
#define TRY_ACQUIRE_SHARED(...) \ |
||||||
|
THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_shared_capability(__VA_ARGS__)) |
||||||
|
|
||||||
|
#define EXCLUDES(...) \ |
||||||
|
THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(__VA_ARGS__)) |
||||||
|
|
||||||
|
#define ASSERT_CAPABILITY(x) \ |
||||||
|
THREAD_ANNOTATION_ATTRIBUTE__(assert_capability(x)) |
||||||
|
|
||||||
|
#define ASSERT_SHARED_CAPABILITY(x) \ |
||||||
|
THREAD_ANNOTATION_ATTRIBUTE__(assert_shared_capability(x)) |
||||||
|
|
||||||
|
#define RETURN_CAPABILITY(x) \ |
||||||
|
THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x)) |
||||||
|
|
||||||
|
#define EXCLUSIVE_LOCK_FUNCTION(...) \ |
||||||
|
THREAD_ANNOTATION_ATTRIBUTE__(exclusive_lock_function(__VA_ARGS__)) |
||||||
|
|
||||||
|
#define EXCLUSIVE_TRYLOCK_FUNCTION(...) \ |
||||||
|
THREAD_ANNOTATION_ATTRIBUTE__(exclusive_trylock_function(__VA_ARGS__)) |
||||||
|
|
||||||
|
#define SHARED_LOCK_FUNCTION(...) \ |
||||||
|
THREAD_ANNOTATION_ATTRIBUTE__(shared_lock_function(__VA_ARGS__)) |
||||||
|
|
||||||
|
#define SHARED_TRYLOCK_FUNCTION(...) \ |
||||||
|
THREAD_ANNOTATION_ATTRIBUTE__(shared_trylock_function(__VA_ARGS__)) |
||||||
|
|
||||||
|
#define UNLOCK_FUNCTION(...) \ |
||||||
|
THREAD_ANNOTATION_ATTRIBUTE__(unlock_function(__VA_ARGS__)) |
||||||
|
|
||||||
|
#define SCOPED_LOCKABLE \ |
||||||
|
THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable) |
||||||
|
|
||||||
|
#define LOCK_RETURNED(x) \ |
||||||
|
THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x)) |
||||||
|
|
||||||
|
#define NO_THREAD_SAFETY_ANALYSIS \ |
||||||
|
THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis) |
||||||
|
|
||||||
|
#endif // ANDROID_BASE_THREAD_ANNOTATIONS_H
|
@ -0,0 +1,154 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2015 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 ANDROID_BASE_UNIQUE_FD_H |
||||||
|
#define ANDROID_BASE_UNIQUE_FD_H |
||||||
|
|
||||||
|
#include <fcntl.h> |
||||||
|
|
||||||
|
#if !defined(_WIN32) |
||||||
|
#include <sys/socket.h> |
||||||
|
#endif |
||||||
|
|
||||||
|
#include <sys/types.h> |
||||||
|
#include <unistd.h> |
||||||
|
|
||||||
|
// DO NOT INCLUDE OTHER LIBBASE HEADERS!
|
||||||
|
// This file gets used in libbinder, and libbinder is used everywhere.
|
||||||
|
// Including other headers from libbase frequently results in inclusion of
|
||||||
|
// android-base/macros.h, which causes macro collisions.
|
||||||
|
|
||||||
|
// Container for a file descriptor that automatically closes the descriptor as
|
||||||
|
// it goes out of scope.
|
||||||
|
//
|
||||||
|
// unique_fd ufd(open("/some/path", "r"));
|
||||||
|
// if (ufd.get() == -1) return error;
|
||||||
|
//
|
||||||
|
// // Do something useful, possibly including 'return'.
|
||||||
|
//
|
||||||
|
// return 0; // Descriptor is closed for you.
|
||||||
|
//
|
||||||
|
// unique_fd is also known as ScopedFd/ScopedFD/scoped_fd; mentioned here to help
|
||||||
|
// you find this class if you're searching for one of those names.
|
||||||
|
namespace android_lkchan { |
||||||
|
namespace base { |
||||||
|
|
||||||
|
struct DefaultCloser { |
||||||
|
static void Close(int fd) { |
||||||
|
// Even if close(2) fails with EINTR, the fd will have been closed.
|
||||||
|
// Using TEMP_FAILURE_RETRY will either lead to EBADF or closing someone
|
||||||
|
// else's fd.
|
||||||
|
// http://lkml.indiana.edu/hypermail/linux/kernel/0509.1/0877.html
|
||||||
|
::close(fd); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
template <typename Closer> |
||||||
|
class unique_fd_impl final { |
||||||
|
public: |
||||||
|
unique_fd_impl() : value_(-1) {} |
||||||
|
|
||||||
|
explicit unique_fd_impl(int value) : value_(value) {} |
||||||
|
~unique_fd_impl() { reset(); } |
||||||
|
|
||||||
|
unique_fd_impl(unique_fd_impl&& other) : value_(other.release()) {} |
||||||
|
unique_fd_impl& operator=(unique_fd_impl&& s) { |
||||||
|
reset(s.release()); |
||||||
|
return *this; |
||||||
|
} |
||||||
|
|
||||||
|
void reset(int new_value = -1) { |
||||||
|
if (value_ != -1) { |
||||||
|
Closer::Close(value_); |
||||||
|
} |
||||||
|
value_ = new_value; |
||||||
|
} |
||||||
|
|
||||||
|
int get() const { return value_; } |
||||||
|
operator int() const { return get(); } |
||||||
|
|
||||||
|
int release() __attribute__((warn_unused_result)) { |
||||||
|
int ret = value_; |
||||||
|
value_ = -1; |
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
private: |
||||||
|
int value_; |
||||||
|
|
||||||
|
unique_fd_impl(const unique_fd_impl&); |
||||||
|
void operator=(const unique_fd_impl&); |
||||||
|
}; |
||||||
|
|
||||||
|
using unique_fd = unique_fd_impl<DefaultCloser>; |
||||||
|
|
||||||
|
#if !defined(_WIN32) |
||||||
|
|
||||||
|
// Inline functions, so that they can be used header-only.
|
||||||
|
inline bool Pipe(unique_fd* read, unique_fd* write) { |
||||||
|
int pipefd[2]; |
||||||
|
|
||||||
|
#if defined(__linux__) |
||||||
|
if (pipe2(pipefd, O_CLOEXEC) != 0) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
#else // defined(__APPLE__)
|
||||||
|
if (pipe(pipefd) != 0) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
if (fcntl(pipefd[0], F_SETFD, FD_CLOEXEC) != 0 || fcntl(pipefd[1], F_SETFD, FD_CLOEXEC) != 0) { |
||||||
|
close(pipefd[0]); |
||||||
|
close(pipefd[1]); |
||||||
|
return false; |
||||||
|
} |
||||||
|
#endif |
||||||
|
|
||||||
|
read->reset(pipefd[0]); |
||||||
|
write->reset(pipefd[1]); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
inline bool Socketpair(int domain, int type, int protocol, unique_fd* left, unique_fd* right) { |
||||||
|
int sockfd[2]; |
||||||
|
if (socketpair(domain, type, protocol, sockfd) != 0) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
left->reset(sockfd[0]); |
||||||
|
right->reset(sockfd[1]); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
inline bool Socketpair(int type, unique_fd* left, unique_fd* right) { |
||||||
|
return Socketpair(AF_UNIX, type, 0, left, right); |
||||||
|
} |
||||||
|
|
||||||
|
#endif // !defined(_WIN32)
|
||||||
|
|
||||||
|
} // namespace base
|
||||||
|
} // namespace android_lkchan
|
||||||
|
|
||||||
|
template <typename T> |
||||||
|
int close(const android_lkchan::base::unique_fd_impl<T>&) |
||||||
|
#if defined(__clang__) |
||||||
|
__attribute__((__unavailable__( |
||||||
|
#else |
||||||
|
__attribute__((__error__( |
||||||
|
#endif |
||||||
|
"close called on unique_fd" |
||||||
|
))); |
||||||
|
|
||||||
|
#endif // ANDROID_BASE_UNIQUE_FD_H
|
@ -0,0 +1,106 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2015 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 ANDROID_BASE_UTF8_H |
||||||
|
#define ANDROID_BASE_UTF8_H |
||||||
|
|
||||||
|
#ifdef _WIN32 |
||||||
|
#include <string> |
||||||
|
#else |
||||||
|
// Bring in prototypes for standard APIs so that we can import them into the utf8 namespace.
|
||||||
|
#include <fcntl.h> // open |
||||||
|
#include <stdio.h> // fopen |
||||||
|
#include <sys/stat.h> // mkdir |
||||||
|
#include <unistd.h> // unlink |
||||||
|
#endif |
||||||
|
|
||||||
|
namespace android_lkchan { |
||||||
|
namespace base { |
||||||
|
|
||||||
|
// Only available on Windows because this is only needed on Windows.
|
||||||
|
#ifdef _WIN32 |
||||||
|
// Convert size number of UTF-16 wchar_t's to UTF-8. Returns whether the
|
||||||
|
// conversion was done successfully.
|
||||||
|
bool WideToUTF8(const wchar_t* utf16, const size_t size, std::string* utf8); |
||||||
|
|
||||||
|
// Convert a NULL-terminated string of UTF-16 characters to UTF-8. Returns
|
||||||
|
// whether the conversion was done successfully.
|
||||||
|
bool WideToUTF8(const wchar_t* utf16, std::string* utf8); |
||||||
|
|
||||||
|
// Convert a UTF-16 std::wstring (including any embedded NULL characters) to
|
||||||
|
// UTF-8. Returns whether the conversion was done successfully.
|
||||||
|
bool WideToUTF8(const std::wstring& utf16, std::string* utf8); |
||||||
|
|
||||||
|
// Convert size number of UTF-8 char's to UTF-16. Returns whether the conversion
|
||||||
|
// was done successfully.
|
||||||
|
bool UTF8ToWide(const char* utf8, const size_t size, std::wstring* utf16); |
||||||
|
|
||||||
|
// Convert a NULL-terminated string of UTF-8 characters to UTF-16. Returns
|
||||||
|
// whether the conversion was done successfully.
|
||||||
|
bool UTF8ToWide(const char* utf8, std::wstring* utf16); |
||||||
|
|
||||||
|
// Convert a UTF-8 std::string (including any embedded NULL characters) to
|
||||||
|
// UTF-16. Returns whether the conversion was done successfully.
|
||||||
|
bool UTF8ToWide(const std::string& utf8, std::wstring* utf16); |
||||||
|
|
||||||
|
// Convert a file system path, represented as a NULL-terminated string of
|
||||||
|
// UTF-8 characters, to a UTF-16 string representing the same file system
|
||||||
|
// path using the Windows extended-lengh path representation.
|
||||||
|
//
|
||||||
|
// See https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx#MAXPATH:
|
||||||
|
// ```The Windows API has many functions that also have Unicode versions to
|
||||||
|
// permit an extended-length path for a maximum total path length of 32,767
|
||||||
|
// characters. To specify an extended-length path, use the "\\?\" prefix.
|
||||||
|
// For example, "\\?\D:\very long path".```
|
||||||
|
//
|
||||||
|
// Returns whether the conversion was done successfully.
|
||||||
|
bool UTF8PathToWindowsLongPath(const char* utf8, std::wstring* utf16); |
||||||
|
#endif |
||||||
|
|
||||||
|
// The functions in the utf8 namespace take UTF-8 strings. For Windows, these
|
||||||
|
// are wrappers, for non-Windows these just expose existing APIs. To call these
|
||||||
|
// functions, use:
|
||||||
|
//
|
||||||
|
// // anonymous namespace to avoid conflict with existing open(), unlink(), etc.
|
||||||
|
// namespace {
|
||||||
|
// // Import functions into anonymous namespace.
|
||||||
|
// using namespace android_lkchan::base::utf8;
|
||||||
|
//
|
||||||
|
// void SomeFunction(const char* name) {
|
||||||
|
// int fd = open(name, ...); // Calls android_lkchan::base::utf8::open().
|
||||||
|
// ...
|
||||||
|
// unlink(name); // Calls android_lkchan::base::utf8::unlink().
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
namespace utf8 { |
||||||
|
|
||||||
|
#ifdef _WIN32 |
||||||
|
FILE* fopen(const char* name, const char* mode); |
||||||
|
int mkdir(const char* name, mode_t mode); |
||||||
|
int open(const char* name, int flags, ...); |
||||||
|
int unlink(const char* name); |
||||||
|
#else |
||||||
|
using ::fopen; |
||||||
|
using ::mkdir; |
||||||
|
using ::open; |
||||||
|
using ::unlink; |
||||||
|
#endif |
||||||
|
|
||||||
|
} // namespace utf8
|
||||||
|
} // namespace base
|
||||||
|
} // namespace android_lkchan
|
||||||
|
|
||||||
|
#endif // ANDROID_BASE_UTF8_H
|
@ -0,0 +1,504 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2015 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_LIBARTBASE_BASE_BIT_UTILS_H_ |
||||||
|
#define ART_LIBARTBASE_BASE_BIT_UTILS_H_ |
||||||
|
|
||||||
|
#include <limits> |
||||||
|
#include <type_traits> |
||||||
|
|
||||||
|
#include "android-base/logging.h" |
||||||
|
|
||||||
|
#include "base/stl_util_identity.h" |
||||||
|
|
||||||
|
namespace art_lkchan { |
||||||
|
|
||||||
|
// Like sizeof, but count how many bits a type takes. Pass type explicitly.
|
||||||
|
template <typename T> |
||||||
|
constexpr size_t BitSizeOf() { |
||||||
|
static_assert(std::is_integral<T>::value, "T must be integral"); |
||||||
|
using unsigned_type = typename std::make_unsigned<T>::type; |
||||||
|
static_assert(sizeof(T) == sizeof(unsigned_type), "Unexpected type size mismatch!"); |
||||||
|
static_assert(std::numeric_limits<unsigned_type>::radix == 2, "Unexpected radix!"); |
||||||
|
return std::numeric_limits<unsigned_type>::digits; |
||||||
|
} |
||||||
|
|
||||||
|
// Like sizeof, but count how many bits a type takes. Infers type from parameter.
|
||||||
|
template <typename T> |
||||||
|
constexpr size_t BitSizeOf(T /*x*/) { |
||||||
|
return BitSizeOf<T>(); |
||||||
|
} |
||||||
|
|
||||||
|
template<typename T> |
||||||
|
constexpr int CLZ(T x) { |
||||||
|
static_assert(std::is_integral<T>::value, "T must be integral"); |
||||||
|
static_assert(std::is_unsigned<T>::value, "T must be unsigned"); |
||||||
|
static_assert(std::numeric_limits<T>::radix == 2, "Unexpected radix!"); |
||||||
|
static_assert(sizeof(T) == sizeof(uint64_t) || sizeof(T) <= sizeof(uint32_t), |
||||||
|
"Unsupported sizeof(T)"); |
||||||
|
DCHECK_NE(x, 0u); |
||||||
|
constexpr bool is_64_bit = (sizeof(T) == sizeof(uint64_t)); |
||||||
|
constexpr size_t adjustment = |
||||||
|
is_64_bit ? 0u : std::numeric_limits<uint32_t>::digits - std::numeric_limits<T>::digits; |
||||||
|
return is_64_bit ? __builtin_clzll(x) : __builtin_clz(x) - adjustment; |
||||||
|
} |
||||||
|
|
||||||
|
// Similar to CLZ except that on zero input it returns bitwidth and supports signed integers.
|
||||||
|
template<typename T> |
||||||
|
constexpr int JAVASTYLE_CLZ(T x) { |
||||||
|
static_assert(std::is_integral<T>::value, "T must be integral"); |
||||||
|
using unsigned_type = typename std::make_unsigned<T>::type; |
||||||
|
return (x == 0) ? BitSizeOf<T>() : CLZ(static_cast<unsigned_type>(x)); |
||||||
|
} |
||||||
|
|
||||||
|
template<typename T> |
||||||
|
constexpr int CTZ(T x) { |
||||||
|
static_assert(std::is_integral<T>::value, "T must be integral"); |
||||||
|
// It is not unreasonable to ask for trailing zeros in a negative number. As such, do not check
|
||||||
|
// that T is an unsigned type.
|
||||||
|
static_assert(sizeof(T) == sizeof(uint64_t) || sizeof(T) <= sizeof(uint32_t), |
||||||
|
"Unsupported sizeof(T)"); |
||||||
|
DCHECK_NE(x, static_cast<T>(0)); |
||||||
|
return (sizeof(T) == sizeof(uint64_t)) ? __builtin_ctzll(x) : __builtin_ctz(x); |
||||||
|
} |
||||||
|
|
||||||
|
// Similar to CTZ except that on zero input it returns bitwidth and supports signed integers.
|
||||||
|
template<typename T> |
||||||
|
constexpr int JAVASTYLE_CTZ(T x) { |
||||||
|
static_assert(std::is_integral<T>::value, "T must be integral"); |
||||||
|
using unsigned_type = typename std::make_unsigned<T>::type; |
||||||
|
return (x == 0) ? BitSizeOf<T>() : CTZ(static_cast<unsigned_type>(x)); |
||||||
|
} |
||||||
|
|
||||||
|
// Return the number of 1-bits in `x`.
|
||||||
|
template<typename T> |
||||||
|
constexpr int POPCOUNT(T x) { |
||||||
|
return (sizeof(T) == sizeof(uint32_t)) ? __builtin_popcount(x) : __builtin_popcountll(x); |
||||||
|
} |
||||||
|
|
||||||
|
// Swap bytes.
|
||||||
|
template<typename T> |
||||||
|
constexpr T BSWAP(T x) { |
||||||
|
if (sizeof(T) == sizeof(uint16_t)) { |
||||||
|
return __builtin_bswap16(x); |
||||||
|
} else if (sizeof(T) == sizeof(uint32_t)) { |
||||||
|
return __builtin_bswap32(x); |
||||||
|
} else { |
||||||
|
return __builtin_bswap64(x); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Find the bit position of the most significant bit (0-based), or -1 if there were no bits set.
|
||||||
|
template <typename T> |
||||||
|
constexpr ssize_t MostSignificantBit(T value) { |
||||||
|
static_assert(std::is_integral<T>::value, "T must be integral"); |
||||||
|
static_assert(std::is_unsigned<T>::value, "T must be unsigned"); |
||||||
|
static_assert(std::numeric_limits<T>::radix == 2, "Unexpected radix!"); |
||||||
|
return (value == 0) ? -1 : std::numeric_limits<T>::digits - 1 - CLZ(value); |
||||||
|
} |
||||||
|
|
||||||
|
// Find the bit position of the least significant bit (0-based), or -1 if there were no bits set.
|
||||||
|
template <typename T> |
||||||
|
constexpr ssize_t LeastSignificantBit(T value) { |
||||||
|
static_assert(std::is_integral<T>::value, "T must be integral"); |
||||||
|
static_assert(std::is_unsigned<T>::value, "T must be unsigned"); |
||||||
|
return (value == 0) ? -1 : CTZ(value); |
||||||
|
} |
||||||
|
|
||||||
|
// How many bits (minimally) does it take to store the constant 'value'? i.e. 1 for 1, 3 for 5, etc.
|
||||||
|
template <typename T> |
||||||
|
constexpr size_t MinimumBitsToStore(T value) { |
||||||
|
return static_cast<size_t>(MostSignificantBit(value) + 1); |
||||||
|
} |
||||||
|
|
||||||
|
template <typename T> |
||||||
|
constexpr T RoundUpToPowerOfTwo(T x) { |
||||||
|
static_assert(std::is_integral<T>::value, "T must be integral"); |
||||||
|
static_assert(std::is_unsigned<T>::value, "T must be unsigned"); |
||||||
|
// NOTE: Undefined if x > (1 << (std::numeric_limits<T>::digits - 1)).
|
||||||
|
return (x < 2u) ? x : static_cast<T>(1u) << (std::numeric_limits<T>::digits - CLZ(x - 1u)); |
||||||
|
} |
||||||
|
|
||||||
|
// Return highest possible N - a power of two - such that val >= N.
|
||||||
|
template <typename T> |
||||||
|
constexpr T TruncToPowerOfTwo(T val) { |
||||||
|
static_assert(std::is_integral<T>::value, "T must be integral"); |
||||||
|
static_assert(std::is_unsigned<T>::value, "T must be unsigned"); |
||||||
|
return (val != 0) ? static_cast<T>(1u) << (BitSizeOf<T>() - CLZ(val) - 1u) : 0; |
||||||
|
} |
||||||
|
|
||||||
|
template<typename T> |
||||||
|
constexpr bool IsPowerOfTwo(T x) { |
||||||
|
static_assert(std::is_integral<T>::value, "T must be integral"); |
||||||
|
// TODO: assert unsigned. There is currently many uses with signed values.
|
||||||
|
return (x & (x - 1)) == 0; |
||||||
|
} |
||||||
|
|
||||||
|
template<typename T> |
||||||
|
constexpr int WhichPowerOf2(T x) { |
||||||
|
static_assert(std::is_integral<T>::value, "T must be integral"); |
||||||
|
// TODO: assert unsigned. There is currently many uses with signed values.
|
||||||
|
DCHECK((x != 0) && IsPowerOfTwo(x)); |
||||||
|
return CTZ(x); |
||||||
|
} |
||||||
|
|
||||||
|
// For rounding integers.
|
||||||
|
// Note: Omit the `n` from T type deduction, deduce only from the `x` argument.
|
||||||
|
template<typename T> |
||||||
|
constexpr T RoundDown(T x, typename Identity<T>::type n) WARN_UNUSED; |
||||||
|
|
||||||
|
template<typename T> |
||||||
|
constexpr T RoundDown(T x, typename Identity<T>::type n) { |
||||||
|
DCHECK(IsPowerOfTwo(n)); |
||||||
|
return (x & -n); |
||||||
|
} |
||||||
|
|
||||||
|
template<typename T> |
||||||
|
constexpr T RoundUp(T x, typename std::remove_reference<T>::type n) WARN_UNUSED; |
||||||
|
|
||||||
|
template<typename T> |
||||||
|
constexpr T RoundUp(T x, typename std::remove_reference<T>::type n) { |
||||||
|
return RoundDown(x + n - 1, n); |
||||||
|
} |
||||||
|
|
||||||
|
// For aligning pointers.
|
||||||
|
template<typename T> |
||||||
|
inline T* AlignDown(T* x, uintptr_t n) WARN_UNUSED; |
||||||
|
|
||||||
|
template<typename T> |
||||||
|
inline T* AlignDown(T* x, uintptr_t n) { |
||||||
|
return reinterpret_cast<T*>(RoundDown(reinterpret_cast<uintptr_t>(x), n)); |
||||||
|
} |
||||||
|
|
||||||
|
template<typename T> |
||||||
|
inline T* AlignUp(T* x, uintptr_t n) WARN_UNUSED; |
||||||
|
|
||||||
|
template<typename T> |
||||||
|
inline T* AlignUp(T* x, uintptr_t n) { |
||||||
|
return reinterpret_cast<T*>(RoundUp(reinterpret_cast<uintptr_t>(x), n)); |
||||||
|
} |
||||||
|
|
||||||
|
template<int n, typename T> |
||||||
|
constexpr bool IsAligned(T x) { |
||||||
|
static_assert((n & (n - 1)) == 0, "n is not a power of two"); |
||||||
|
return (x & (n - 1)) == 0; |
||||||
|
} |
||||||
|
|
||||||
|
template<int n, typename T> |
||||||
|
inline bool IsAligned(T* x) { |
||||||
|
return IsAligned<n>(reinterpret_cast<const uintptr_t>(x)); |
||||||
|
} |
||||||
|
|
||||||
|
template<typename T> |
||||||
|
inline bool IsAlignedParam(T x, int n) { |
||||||
|
return (x & (n - 1)) == 0; |
||||||
|
} |
||||||
|
|
||||||
|
template<typename T> |
||||||
|
inline bool IsAlignedParam(T* x, int n) { |
||||||
|
return IsAlignedParam(reinterpret_cast<const uintptr_t>(x), n); |
||||||
|
} |
||||||
|
|
||||||
|
#define CHECK_ALIGNED(value, alignment) \ |
||||||
|
CHECK(::art_lkchan::IsAligned<alignment>(value)) << reinterpret_cast<const void*>(value) |
||||||
|
|
||||||
|
#define DCHECK_ALIGNED(value, alignment) \ |
||||||
|
DCHECK(::art_lkchan::IsAligned<alignment>(value)) << reinterpret_cast<const void*>(value) |
||||||
|
|
||||||
|
#define CHECK_ALIGNED_PARAM(value, alignment) \ |
||||||
|
CHECK(::art_lkchan::IsAlignedParam(value, alignment)) << reinterpret_cast<const void*>(value) |
||||||
|
|
||||||
|
#define DCHECK_ALIGNED_PARAM(value, alignment) \ |
||||||
|
DCHECK(::art_lkchan::IsAlignedParam(value, alignment)) << reinterpret_cast<const void*>(value) |
||||||
|
|
||||||
|
inline uint16_t Low16Bits(uint32_t value) { |
||||||
|
return static_cast<uint16_t>(value); |
||||||
|
} |
||||||
|
|
||||||
|
inline uint16_t High16Bits(uint32_t value) { |
||||||
|
return static_cast<uint16_t>(value >> 16); |
||||||
|
} |
||||||
|
|
||||||
|
inline uint32_t Low32Bits(uint64_t value) { |
||||||
|
return static_cast<uint32_t>(value); |
||||||
|
} |
||||||
|
|
||||||
|
inline uint32_t High32Bits(uint64_t value) { |
||||||
|
return static_cast<uint32_t>(value >> 32); |
||||||
|
} |
||||||
|
|
||||||
|
// Check whether an N-bit two's-complement representation can hold value.
|
||||||
|
template <typename T> |
||||||
|
inline bool IsInt(size_t N, T value) { |
||||||
|
if (N == BitSizeOf<T>()) { |
||||||
|
return true; |
||||||
|
} else { |
||||||
|
CHECK_LT(0u, N); |
||||||
|
CHECK_LT(N, BitSizeOf<T>()); |
||||||
|
T limit = static_cast<T>(1) << (N - 1u); |
||||||
|
return (-limit <= value) && (value < limit); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
template <typename T> |
||||||
|
constexpr T GetIntLimit(size_t bits) { |
||||||
|
DCHECK_NE(bits, 0u); |
||||||
|
DCHECK_LT(bits, BitSizeOf<T>()); |
||||||
|
return static_cast<T>(1) << (bits - 1); |
||||||
|
} |
||||||
|
|
||||||
|
template <size_t kBits, typename T> |
||||||
|
constexpr bool IsInt(T value) { |
||||||
|
static_assert(kBits > 0, "kBits cannot be zero."); |
||||||
|
static_assert(kBits <= BitSizeOf<T>(), "kBits must be <= max."); |
||||||
|
static_assert(std::is_signed<T>::value, "Needs a signed type."); |
||||||
|
// Corner case for "use all bits." Can't use the limits, as they would overflow, but it is
|
||||||
|
// trivially true.
|
||||||
|
return (kBits == BitSizeOf<T>()) ? |
||||||
|
true : |
||||||
|
(-GetIntLimit<T>(kBits) <= value) && (value < GetIntLimit<T>(kBits)); |
||||||
|
} |
||||||
|
|
||||||
|
template <size_t kBits, typename T> |
||||||
|
constexpr bool IsUint(T value) { |
||||||
|
static_assert(kBits > 0, "kBits cannot be zero."); |
||||||
|
static_assert(kBits <= BitSizeOf<T>(), "kBits must be <= max."); |
||||||
|
static_assert(std::is_integral<T>::value, "Needs an integral type."); |
||||||
|
// Corner case for "use all bits." Can't use the limits, as they would overflow, but it is
|
||||||
|
// trivially true.
|
||||||
|
// NOTE: To avoid triggering assertion in GetIntLimit(kBits+1) if kBits+1==BitSizeOf<T>(),
|
||||||
|
// use GetIntLimit(kBits)*2u. The unsigned arithmetic works well for us if it overflows.
|
||||||
|
using unsigned_type = typename std::make_unsigned<T>::type; |
||||||
|
return (0 <= value) && |
||||||
|
(kBits == BitSizeOf<T>() || |
||||||
|
(static_cast<unsigned_type>(value) <= GetIntLimit<unsigned_type>(kBits) * 2u - 1u)); |
||||||
|
} |
||||||
|
|
||||||
|
template <size_t kBits, typename T> |
||||||
|
constexpr bool IsAbsoluteUint(T value) { |
||||||
|
static_assert(kBits <= BitSizeOf<T>(), "kBits must be <= max."); |
||||||
|
static_assert(std::is_integral<T>::value, "Needs an integral type."); |
||||||
|
using unsigned_type = typename std::make_unsigned<T>::type; |
||||||
|
return (kBits == BitSizeOf<T>()) |
||||||
|
? true |
||||||
|
: IsUint<kBits>(value < 0 |
||||||
|
? static_cast<unsigned_type>(-1 - value) + 1u // Avoid overflow.
|
||||||
|
: static_cast<unsigned_type>(value)); |
||||||
|
} |
||||||
|
|
||||||
|
// Generate maximum/minimum values for signed/unsigned n-bit integers
|
||||||
|
template <typename T> |
||||||
|
constexpr T MaxInt(size_t bits) { |
||||||
|
DCHECK(std::is_unsigned<T>::value || bits > 0u) << "bits cannot be zero for signed."; |
||||||
|
DCHECK_LE(bits, BitSizeOf<T>()); |
||||||
|
using unsigned_type = typename std::make_unsigned<T>::type; |
||||||
|
return bits == BitSizeOf<T>() |
||||||
|
? std::numeric_limits<T>::max() |
||||||
|
: std::is_signed<T>::value |
||||||
|
? ((bits == 1u) ? 0 : static_cast<T>(MaxInt<unsigned_type>(bits - 1))) |
||||||
|
: static_cast<T>(UINT64_C(1) << bits) - static_cast<T>(1); |
||||||
|
} |
||||||
|
|
||||||
|
template <typename T> |
||||||
|
constexpr T MinInt(size_t bits) { |
||||||
|
DCHECK(std::is_unsigned<T>::value || bits > 0) << "bits cannot be zero for signed."; |
||||||
|
DCHECK_LE(bits, BitSizeOf<T>()); |
||||||
|
return bits == BitSizeOf<T>() |
||||||
|
? std::numeric_limits<T>::min() |
||||||
|
: std::is_signed<T>::value |
||||||
|
? ((bits == 1u) ? -1 : static_cast<T>(-1) - MaxInt<T>(bits)) |
||||||
|
: static_cast<T>(0); |
||||||
|
} |
||||||
|
|
||||||
|
// Returns value with bit set in lowest one-bit position or 0 if 0. (java.lang.X.lowestOneBit).
|
||||||
|
template <typename kind> |
||||||
|
inline static kind LowestOneBitValue(kind opnd) { |
||||||
|
// Hacker's Delight, Section 2-1
|
||||||
|
return opnd & -opnd; |
||||||
|
} |
||||||
|
|
||||||
|
// Returns value with bit set in hightest one-bit position or 0 if 0. (java.lang.X.highestOneBit).
|
||||||
|
template <typename T> |
||||||
|
inline static T HighestOneBitValue(T opnd) { |
||||||
|
using unsigned_type = typename std::make_unsigned<T>::type; |
||||||
|
T res; |
||||||
|
if (opnd == 0) { |
||||||
|
res = 0; |
||||||
|
} else { |
||||||
|
int bit_position = BitSizeOf<T>() - (CLZ(static_cast<unsigned_type>(opnd)) + 1); |
||||||
|
res = static_cast<T>(UINT64_C(1) << bit_position); |
||||||
|
} |
||||||
|
return res; |
||||||
|
} |
||||||
|
|
||||||
|
// Rotate bits.
|
||||||
|
template <typename T, bool left> |
||||||
|
inline static T Rot(T opnd, int distance) { |
||||||
|
int mask = BitSizeOf<T>() - 1; |
||||||
|
int unsigned_right_shift = left ? (-distance & mask) : (distance & mask); |
||||||
|
int signed_left_shift = left ? (distance & mask) : (-distance & mask); |
||||||
|
using unsigned_type = typename std::make_unsigned<T>::type; |
||||||
|
return (static_cast<unsigned_type>(opnd) >> unsigned_right_shift) | (opnd << signed_left_shift); |
||||||
|
} |
||||||
|
|
||||||
|
// TUNING: use rbit for arm/arm64
|
||||||
|
inline static uint32_t ReverseBits32(uint32_t opnd) { |
||||||
|
// Hacker's Delight 7-1
|
||||||
|
opnd = ((opnd >> 1) & 0x55555555) | ((opnd & 0x55555555) << 1); |
||||||
|
opnd = ((opnd >> 2) & 0x33333333) | ((opnd & 0x33333333) << 2); |
||||||
|
opnd = ((opnd >> 4) & 0x0F0F0F0F) | ((opnd & 0x0F0F0F0F) << 4); |
||||||
|
opnd = ((opnd >> 8) & 0x00FF00FF) | ((opnd & 0x00FF00FF) << 8); |
||||||
|
opnd = ((opnd >> 16)) | ((opnd) << 16); |
||||||
|
return opnd; |
||||||
|
} |
||||||
|
|
||||||
|
// TUNING: use rbit for arm/arm64
|
||||||
|
inline static uint64_t ReverseBits64(uint64_t opnd) { |
||||||
|
// Hacker's Delight 7-1
|
||||||
|
opnd = (opnd & 0x5555555555555555L) << 1 | ((opnd >> 1) & 0x5555555555555555L); |
||||||
|
opnd = (opnd & 0x3333333333333333L) << 2 | ((opnd >> 2) & 0x3333333333333333L); |
||||||
|
opnd = (opnd & 0x0f0f0f0f0f0f0f0fL) << 4 | ((opnd >> 4) & 0x0f0f0f0f0f0f0f0fL); |
||||||
|
opnd = (opnd & 0x00ff00ff00ff00ffL) << 8 | ((opnd >> 8) & 0x00ff00ff00ff00ffL); |
||||||
|
opnd = (opnd << 48) | ((opnd & 0xffff0000L) << 16) | ((opnd >> 16) & 0xffff0000L) | (opnd >> 48); |
||||||
|
return opnd; |
||||||
|
} |
||||||
|
|
||||||
|
// Create a mask for the least significant "bits"
|
||||||
|
// The returned value is always unsigned to prevent undefined behavior for bitwise ops.
|
||||||
|
//
|
||||||
|
// Given 'bits',
|
||||||
|
// Returns:
|
||||||
|
// <--- bits --->
|
||||||
|
// +-----------------+------------+
|
||||||
|
// | 0 ............0 | 1.....1 |
|
||||||
|
// +-----------------+------------+
|
||||||
|
// msb lsb
|
||||||
|
template <typename T = size_t> |
||||||
|
inline static constexpr std::make_unsigned_t<T> MaskLeastSignificant(size_t bits) { |
||||||
|
DCHECK_GE(BitSizeOf<T>(), bits) << "Bits out of range for type T"; |
||||||
|
using unsigned_T = std::make_unsigned_t<T>; |
||||||
|
if (bits >= BitSizeOf<T>()) { |
||||||
|
return std::numeric_limits<unsigned_T>::max(); |
||||||
|
} else { |
||||||
|
auto kOne = static_cast<unsigned_T>(1); // Do not truncate for T>size_t.
|
||||||
|
return static_cast<unsigned_T>((kOne << bits) - kOne); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Clears the bitfield starting at the least significant bit "lsb" with a bitwidth of 'width'.
|
||||||
|
// (Equivalent of ARM BFC instruction).
|
||||||
|
//
|
||||||
|
// Given:
|
||||||
|
// <-- width -->
|
||||||
|
// +--------+------------+--------+
|
||||||
|
// | ABC... | bitfield | XYZ... +
|
||||||
|
// +--------+------------+--------+
|
||||||
|
// lsb 0
|
||||||
|
// Returns:
|
||||||
|
// <-- width -->
|
||||||
|
// +--------+------------+--------+
|
||||||
|
// | ABC... | 0........0 | XYZ... +
|
||||||
|
// +--------+------------+--------+
|
||||||
|
// lsb 0
|
||||||
|
template <typename T> |
||||||
|
inline static constexpr T BitFieldClear(T value, size_t lsb, size_t width) { |
||||||
|
DCHECK_GE(BitSizeOf(value), lsb + width) << "Bit field out of range for value"; |
||||||
|
const auto val = static_cast<std::make_unsigned_t<T>>(value); |
||||||
|
const auto mask = MaskLeastSignificant<T>(width); |
||||||
|
|
||||||
|
return static_cast<T>(val & ~(mask << lsb)); |
||||||
|
} |
||||||
|
|
||||||
|
// Inserts the contents of 'data' into bitfield of 'value' starting
|
||||||
|
// at the least significant bit "lsb" with a bitwidth of 'width'.
|
||||||
|
// Note: data must be within range of [MinInt(width), MaxInt(width)].
|
||||||
|
// (Equivalent of ARM BFI instruction).
|
||||||
|
//
|
||||||
|
// Given (data):
|
||||||
|
// <-- width -->
|
||||||
|
// +--------+------------+--------+
|
||||||
|
// | ABC... | bitfield | XYZ... +
|
||||||
|
// +--------+------------+--------+
|
||||||
|
// lsb 0
|
||||||
|
// Returns:
|
||||||
|
// <-- width -->
|
||||||
|
// +--------+------------+--------+
|
||||||
|
// | ABC... | 0...data | XYZ... +
|
||||||
|
// +--------+------------+--------+
|
||||||
|
// lsb 0
|
||||||
|
|
||||||
|
template <typename T, typename T2> |
||||||
|
inline static constexpr T BitFieldInsert(T value, T2 data, size_t lsb, size_t width) { |
||||||
|
DCHECK_GE(BitSizeOf(value), lsb + width) << "Bit field out of range for value"; |
||||||
|
if (width != 0u) { |
||||||
|
DCHECK_GE(MaxInt<T2>(width), data) << "Data out of range [too large] for bitwidth"; |
||||||
|
DCHECK_LE(MinInt<T2>(width), data) << "Data out of range [too small] for bitwidth"; |
||||||
|
} else { |
||||||
|
DCHECK_EQ(static_cast<T2>(0), data) << "Data out of range [nonzero] for bitwidth 0"; |
||||||
|
} |
||||||
|
const auto data_mask = MaskLeastSignificant<T2>(width); |
||||||
|
const auto value_cleared = BitFieldClear(value, lsb, width); |
||||||
|
|
||||||
|
return static_cast<T>(value_cleared | ((data & data_mask) << lsb)); |
||||||
|
} |
||||||
|
|
||||||
|
// Extracts the bitfield starting at the least significant bit "lsb" with a bitwidth of 'width'.
|
||||||
|
// Signed types are sign-extended during extraction. (Equivalent of ARM UBFX/SBFX instruction).
|
||||||
|
//
|
||||||
|
// Given:
|
||||||
|
// <-- width -->
|
||||||
|
// +--------+-------------+-------+
|
||||||
|
// | | bitfield | +
|
||||||
|
// +--------+-------------+-------+
|
||||||
|
// lsb 0
|
||||||
|
// (Unsigned) Returns:
|
||||||
|
// <-- width -->
|
||||||
|
// +----------------+-------------+
|
||||||
|
// | 0... 0 | bitfield |
|
||||||
|
// +----------------+-------------+
|
||||||
|
// 0
|
||||||
|
// (Signed) Returns:
|
||||||
|
// <-- width -->
|
||||||
|
// +----------------+-------------+
|
||||||
|
// | S... S | bitfield |
|
||||||
|
// +----------------+-------------+
|
||||||
|
// 0
|
||||||
|
// where S is the highest bit in 'bitfield'.
|
||||||
|
template <typename T> |
||||||
|
inline static constexpr T BitFieldExtract(T value, size_t lsb, size_t width) { |
||||||
|
DCHECK_GE(BitSizeOf(value), lsb + width) << "Bit field out of range for value"; |
||||||
|
const auto val = static_cast<std::make_unsigned_t<T>>(value); |
||||||
|
|
||||||
|
const T bitfield_unsigned = |
||||||
|
static_cast<T>((val >> lsb) & MaskLeastSignificant<T>(width)); |
||||||
|
if (std::is_signed<T>::value) { |
||||||
|
// Perform sign extension
|
||||||
|
if (width == 0) { // Avoid underflow.
|
||||||
|
return static_cast<T>(0); |
||||||
|
} else if (bitfield_unsigned & (1 << (width - 1))) { // Detect if sign bit was set.
|
||||||
|
// MSB <width> LSB
|
||||||
|
// 0b11111...100...000000
|
||||||
|
const auto ones_negmask = ~MaskLeastSignificant<T>(width); |
||||||
|
return static_cast<T>(bitfield_unsigned | ones_negmask); |
||||||
|
} |
||||||
|
} |
||||||
|
// Skip sign extension.
|
||||||
|
return bitfield_unsigned; |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace art_lkchan
|
||||||
|
|
||||||
|
#endif // ART_LIBARTBASE_BASE_BIT_UTILS_H_
|
@ -0,0 +1,170 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011 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_LIBARTBASE_BASE_CASTS_H_ |
||||||
|
#define ART_LIBARTBASE_BASE_CASTS_H_ |
||||||
|
|
||||||
|
#include <assert.h> |
||||||
|
#include <stdint.h> |
||||||
|
#include <string.h> |
||||||
|
|
||||||
|
#include <limits> |
||||||
|
#include <type_traits> |
||||||
|
|
||||||
|
#include <android-base/logging.h> |
||||||
|
|
||||||
|
#include "stl_util_identity.h" |
||||||
|
|
||||||
|
namespace art_lkchan { |
||||||
|
|
||||||
|
// Use implicit_cast as a safe version of static_cast or const_cast
|
||||||
|
// for upcasting in the type hierarchy (i.e. casting a pointer to Foo
|
||||||
|
// to a pointer to SuperclassOfFoo or casting a pointer to Foo to
|
||||||
|
// a const pointer to Foo).
|
||||||
|
// When you use implicit_cast, the compiler checks that the cast is safe.
|
||||||
|
// Such explicit implicit_casts are necessary in surprisingly many
|
||||||
|
// situations where C++ demands an exact type match instead of an
|
||||||
|
// argument type convertible to a target type.
|
||||||
|
//
|
||||||
|
// The From type can be inferred, so the preferred syntax for using
|
||||||
|
// implicit_cast is the same as for static_cast etc.:
|
||||||
|
//
|
||||||
|
// implicit_cast<ToType>(expr)
|
||||||
|
//
|
||||||
|
// implicit_cast would have been part of the C++ standard library,
|
||||||
|
// but the proposal was submitted too late. It will probably make
|
||||||
|
// its way into the language in the future.
|
||||||
|
template<typename To, typename From> |
||||||
|
inline To implicit_cast(From const &f) { |
||||||
|
return f; |
||||||
|
} |
||||||
|
|
||||||
|
// When you upcast (that is, cast a pointer from type Foo to type
|
||||||
|
// SuperclassOfFoo), it's fine to use implicit_cast<>, since upcasts
|
||||||
|
// always succeed. When you downcast (that is, cast a pointer from
|
||||||
|
// type Foo to type SubclassOfFoo), static_cast<> isn't safe, because
|
||||||
|
// how do you know the pointer is really of type SubclassOfFoo? It
|
||||||
|
// could be a bare Foo, or of type DifferentSubclassOfFoo. Thus,
|
||||||
|
// when you downcast, you should use this macro. In debug mode, we
|
||||||
|
// use dynamic_cast<> to double-check the downcast is legal (we die
|
||||||
|
// if it's not). In normal mode, we do the efficient static_cast<>
|
||||||
|
// instead. Thus, it's important to test in debug mode to make sure
|
||||||
|
// the cast is legal!
|
||||||
|
// This is the only place in the code we should use dynamic_cast<>.
|
||||||
|
// In particular, you SHOULDN'T be using dynamic_cast<> in order to
|
||||||
|
// do RTTI (eg code like this:
|
||||||
|
// if (dynamic_cast<Subclass1>(foo)) HandleASubclass1Object(foo);
|
||||||
|
// if (dynamic_cast<Subclass2>(foo)) HandleASubclass2Object(foo);
|
||||||
|
// You should design the code some other way not to need this.
|
||||||
|
|
||||||
|
template<typename To, typename From> // use like this: down_cast<T*>(foo);
|
||||||
|
inline To down_cast(From* f) { // so we only accept pointers
|
||||||
|
static_assert(std::is_base_of<From, typename std::remove_pointer<To>::type>::value, |
||||||
|
"down_cast unsafe as To is not a subtype of From"); |
||||||
|
|
||||||
|
return static_cast<To>(f); |
||||||
|
} |
||||||
|
|
||||||
|
template<typename To, typename From> // use like this: down_cast<T&>(foo);
|
||||||
|
inline To down_cast(From& f) { // so we only accept references
|
||||||
|
static_assert(std::is_base_of<From, typename std::remove_reference<To>::type>::value, |
||||||
|
"down_cast unsafe as To is not a subtype of From"); |
||||||
|
|
||||||
|
return static_cast<To>(f); |
||||||
|
} |
||||||
|
|
||||||
|
template <class Dest, class Source> |
||||||
|
inline Dest bit_cast(const Source& source) { |
||||||
|
// Compile time assertion: sizeof(Dest) == sizeof(Source)
|
||||||
|
// A compile error here means your Dest and Source have different sizes.
|
||||||
|
static_assert(sizeof(Dest) == sizeof(Source), "sizes should be equal"); |
||||||
|
Dest dest; |
||||||
|
memcpy(&dest, &source, sizeof(dest)); |
||||||
|
return dest; |
||||||
|
} |
||||||
|
|
||||||
|
// A version of static_cast that DCHECKs that the value can be precisely represented
|
||||||
|
// when converting to Dest.
|
||||||
|
template <typename Dest, typename Source> |
||||||
|
constexpr Dest dchecked_integral_cast(Source source) { |
||||||
|
DCHECK( |
||||||
|
// Check that the value is within the lower limit of Dest.
|
||||||
|
(static_cast<intmax_t>(std::numeric_limits<Dest>::min()) <= |
||||||
|
static_cast<intmax_t>(std::numeric_limits<Source>::min()) || |
||||||
|
source >= static_cast<Source>(std::numeric_limits<Dest>::min())) && |
||||||
|
// Check that the value is within the upper limit of Dest.
|
||||||
|
(static_cast<uintmax_t>(std::numeric_limits<Dest>::max()) >= |
||||||
|
static_cast<uintmax_t>(std::numeric_limits<Source>::max()) || |
||||||
|
source <= static_cast<Source>(std::numeric_limits<Dest>::max()))) |
||||||
|
<< "dchecked_integral_cast failed for " << source |
||||||
|
<< " (would be " << static_cast<Dest>(source) << ")"; |
||||||
|
|
||||||
|
return static_cast<Dest>(source); |
||||||
|
} |
||||||
|
|
||||||
|
// A version of dchecked_integral_cast casting between an integral type and an enum type.
|
||||||
|
// When casting to an enum type, the cast does not check if the value corresponds to an enumerator.
|
||||||
|
// When casting from an enum type, the target type can be omitted and the enum's underlying type
|
||||||
|
// shall be used.
|
||||||
|
|
||||||
|
template <typename Dest, typename Source> |
||||||
|
constexpr |
||||||
|
typename std::enable_if<!std::is_enum<Source>::value, Dest>::type |
||||||
|
enum_cast(Source value) { |
||||||
|
return static_cast<Dest>( |
||||||
|
dchecked_integral_cast<typename std::underlying_type<Dest>::type>(value)); |
||||||
|
} |
||||||
|
|
||||||
|
template <typename Dest = void, typename Source> |
||||||
|
constexpr |
||||||
|
typename std::enable_if<std::is_enum<Source>::value, |
||||||
|
typename std::conditional<std::is_same<Dest, void>::value, |
||||||
|
std::underlying_type<Source>, |
||||||
|
Identity<Dest>>::type>::type::type |
||||||
|
enum_cast(Source value) { |
||||||
|
using return_type = typename std::conditional<std::is_same<Dest, void>::value, |
||||||
|
std::underlying_type<Source>, |
||||||
|
Identity<Dest>>::type::type; |
||||||
|
return dchecked_integral_cast<return_type>( |
||||||
|
static_cast<typename std::underlying_type<Source>::type>(value)); |
||||||
|
} |
||||||
|
|
||||||
|
// A version of reinterpret_cast<>() between pointers and int64_t/uint64_t
|
||||||
|
// that goes through uintptr_t to avoid treating the pointer as "signed."
|
||||||
|
|
||||||
|
template <typename Dest, typename Source> |
||||||
|
inline Dest reinterpret_cast64(Source source) { |
||||||
|
// This is the overload for casting from int64_t/uint64_t to a pointer.
|
||||||
|
static_assert(std::is_same<Source, int64_t>::value || std::is_same<Source, uint64_t>::value, |
||||||
|
"Source must be int64_t or uint64_t."); |
||||||
|
static_assert(std::is_pointer<Dest>::value, "Dest must be a pointer."); |
||||||
|
// Check that we don't lose any non-0 bits here.
|
||||||
|
DCHECK_EQ(static_cast<Source>(static_cast<uintptr_t>(source)), source); |
||||||
|
return reinterpret_cast<Dest>(static_cast<uintptr_t>(source)); |
||||||
|
} |
||||||
|
|
||||||
|
template <typename Dest, typename Source> |
||||||
|
inline Dest reinterpret_cast64(Source* ptr) { |
||||||
|
// This is the overload for casting from a pointer to int64_t/uint64_t.
|
||||||
|
static_assert(std::is_same<Dest, int64_t>::value || std::is_same<Dest, uint64_t>::value, |
||||||
|
"Dest must be int64_t or uint64_t."); |
||||||
|
static_assert(sizeof(uintptr_t) <= sizeof(Dest), "Expecting at most 64-bit pointers."); |
||||||
|
return static_cast<Dest>(reinterpret_cast<uintptr_t>(ptr)); |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace art_lkchan
|
||||||
|
|
||||||
|
#endif // ART_LIBARTBASE_BASE_CASTS_H_
|
@ -0,0 +1,45 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 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_LIBARTBASE_BASE_ENUMS_H_ |
||||||
|
#define ART_LIBARTBASE_BASE_ENUMS_H_ |
||||||
|
|
||||||
|
#include <cstddef> |
||||||
|
#include <ostream> |
||||||
|
|
||||||
|
namespace art_lkchan { |
||||||
|
|
||||||
|
enum class PointerSize : size_t { |
||||||
|
k32 = 4, |
||||||
|
k64 = 8 |
||||||
|
}; |
||||||
|
|
||||||
|
inline std::ostream& operator<<(std::ostream& os, const PointerSize& rhs) { |
||||||
|
switch (rhs) { |
||||||
|
case PointerSize::k32: os << "k32"; break; |
||||||
|
case PointerSize::k64: os << "k64"; break; |
||||||
|
default: os << "PointerSize[" << static_cast<int>(rhs) << "]"; break; |
||||||
|
} |
||||||
|
return os; |
||||||
|
} |
||||||
|
|
||||||
|
static constexpr PointerSize kRuntimePointerSize = sizeof(void*) == 8U |
||||||
|
? PointerSize::k64 |
||||||
|
: PointerSize::k32; |
||||||
|
|
||||||
|
} // namespace art_lkchan
|
||||||
|
|
||||||
|
#endif // ART_LIBARTBASE_BASE_ENUMS_H_
|
@ -0,0 +1,139 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011 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_LIBARTBASE_BASE_GLOBALS_H_ |
||||||
|
#define ART_LIBARTBASE_BASE_GLOBALS_H_ |
||||||
|
|
||||||
|
#include <stddef.h> |
||||||
|
#include <stdint.h> |
||||||
|
|
||||||
|
namespace art_lkchan { |
||||||
|
|
||||||
|
static constexpr size_t KB = 1024; |
||||||
|
static constexpr size_t MB = KB * KB; |
||||||
|
static constexpr size_t GB = KB * KB * KB; |
||||||
|
|
||||||
|
// Runtime sizes.
|
||||||
|
static constexpr size_t kBitsPerByte = 8; |
||||||
|
static constexpr size_t kBitsPerByteLog2 = 3; |
||||||
|
static constexpr int kBitsPerIntPtrT = sizeof(intptr_t) * kBitsPerByte; |
||||||
|
|
||||||
|
// Required stack alignment
|
||||||
|
static constexpr size_t kStackAlignment = 16; |
||||||
|
|
||||||
|
// System page size. We check this against sysconf(_SC_PAGE_SIZE) at runtime, but use a simple
|
||||||
|
// compile-time constant so the compiler can generate better code.
|
||||||
|
static constexpr int kPageSize = 4096; |
||||||
|
|
||||||
|
// Returns whether the given memory offset can be used for generating
|
||||||
|
// an implicit null check.
|
||||||
|
static inline bool CanDoImplicitNullCheckOn(uintptr_t offset) { |
||||||
|
return offset < kPageSize; |
||||||
|
} |
||||||
|
|
||||||
|
// Required object alignment
|
||||||
|
static constexpr size_t kObjectAlignmentShift = 3; |
||||||
|
static constexpr size_t kObjectAlignment = 1u << kObjectAlignmentShift; |
||||||
|
static constexpr size_t kLargeObjectAlignment = kPageSize; |
||||||
|
|
||||||
|
// Clion, clang analyzer, etc can falsely believe that "if (kIsDebugBuild)" always
|
||||||
|
// returns the same value. By wrapping into a call to another constexpr function, we force it
|
||||||
|
// to realize that is not actually always evaluating to the same value.
|
||||||
|
static constexpr bool GlobalsReturnSelf(bool self) { return self; } |
||||||
|
|
||||||
|
// Whether or not this is a debug build. Useful in conditionals where NDEBUG isn't.
|
||||||
|
// TODO: Use only __clang_analyzer__ here. b/64455231
|
||||||
|
#if defined(NDEBUG) && !defined(__CLION_IDE__) |
||||||
|
static constexpr bool kIsDebugBuild = GlobalsReturnSelf(false); |
||||||
|
#else |
||||||
|
static constexpr bool kIsDebugBuild = GlobalsReturnSelf(true); |
||||||
|
#endif |
||||||
|
|
||||||
|
#if defined(ART_PGO_INSTRUMENTATION) |
||||||
|
static constexpr bool kIsPGOInstrumentation = true; |
||||||
|
#else |
||||||
|
static constexpr bool kIsPGOInstrumentation = false; |
||||||
|
#endif |
||||||
|
|
||||||
|
// ART_TARGET - Defined for target builds of ART.
|
||||||
|
// ART_TARGET_LINUX - Defined for target Linux builds of ART.
|
||||||
|
// ART_TARGET_ANDROID - Defined for target Android builds of ART.
|
||||||
|
// Note: Either ART_TARGET_LINUX or ART_TARGET_ANDROID need to be set when ART_TARGET is set.
|
||||||
|
// Note: When ART_TARGET_LINUX is defined mem_map.h will not be using Ashmem for memory mappings
|
||||||
|
// (usually only available on Android kernels).
|
||||||
|
#if defined(ART_TARGET) |
||||||
|
// Useful in conditionals where ART_TARGET isn't.
|
||||||
|
static constexpr bool kIsTargetBuild = true; |
||||||
|
# if defined(ART_TARGET_LINUX) |
||||||
|
static constexpr bool kIsTargetLinux = true; |
||||||
|
# elif defined(ART_TARGET_ANDROID) |
||||||
|
static constexpr bool kIsTargetLinux = false; |
||||||
|
# else |
||||||
|
# error "Either ART_TARGET_LINUX or ART_TARGET_ANDROID needs to be defined for target builds." |
||||||
|
# endif |
||||||
|
#else |
||||||
|
static constexpr bool kIsTargetBuild = false; |
||||||
|
# if defined(ART_TARGET_LINUX) |
||||||
|
# error "ART_TARGET_LINUX defined for host build." |
||||||
|
# elif defined(ART_TARGET_ANDROID) |
||||||
|
# error "ART_TARGET_ANDROID defined for host build." |
||||||
|
# else |
||||||
|
static constexpr bool kIsTargetLinux = false; |
||||||
|
# endif |
||||||
|
#endif |
||||||
|
|
||||||
|
// Additional statically-linked ART binaries (dex2oats, oatdumps, etc.) are
|
||||||
|
// always available on the host
|
||||||
|
#if !defined(ART_TARGET) |
||||||
|
static constexpr bool kHostStaticBuildEnabled = true; |
||||||
|
#else |
||||||
|
static constexpr bool kHostStaticBuildEnabled = false; |
||||||
|
#endif |
||||||
|
|
||||||
|
// Garbage collector constants.
|
||||||
|
static constexpr bool kMovingCollector = true; |
||||||
|
static constexpr bool kMarkCompactSupport = false && kMovingCollector; |
||||||
|
// True if we allow moving classes.
|
||||||
|
static constexpr bool kMovingClasses = !kMarkCompactSupport; |
||||||
|
|
||||||
|
// If true, enable the tlab allocator by default.
|
||||||
|
#ifdef ART_USE_TLAB |
||||||
|
static constexpr bool kUseTlab = true; |
||||||
|
#else |
||||||
|
static constexpr bool kUseTlab = false; |
||||||
|
#endif |
||||||
|
|
||||||
|
// Kinds of tracing clocks.
|
||||||
|
enum class TraceClockSource { |
||||||
|
kThreadCpu, |
||||||
|
kWall, |
||||||
|
kDual, // Both wall and thread CPU clocks.
|
||||||
|
}; |
||||||
|
|
||||||
|
#if defined(__linux__) |
||||||
|
static constexpr TraceClockSource kDefaultTraceClockSource = TraceClockSource::kDual; |
||||||
|
#else |
||||||
|
static constexpr TraceClockSource kDefaultTraceClockSource = TraceClockSource::kWall; |
||||||
|
#endif |
||||||
|
|
||||||
|
static constexpr bool kDefaultMustRelocate = true; |
||||||
|
|
||||||
|
// Size of a heap reference.
|
||||||
|
static constexpr size_t kHeapReferenceSize = sizeof(uint32_t); |
||||||
|
|
||||||
|
} // namespace art_lkchan
|
||||||
|
|
||||||
|
#endif // ART_LIBARTBASE_BASE_GLOBALS_H_
|
@ -0,0 +1,74 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2014 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_LIBARTBASE_BASE_HASH_MAP_H_ |
||||||
|
#define ART_LIBARTBASE_BASE_HASH_MAP_H_ |
||||||
|
|
||||||
|
#include <utility> |
||||||
|
|
||||||
|
#include "hash_set.h" |
||||||
|
|
||||||
|
namespace art_lkchan { |
||||||
|
|
||||||
|
template <typename Fn> |
||||||
|
class HashMapWrapper { |
||||||
|
public: |
||||||
|
// Hash function.
|
||||||
|
template <class Key, class Value> |
||||||
|
size_t operator()(const std::pair<Key, Value>& pair) const { |
||||||
|
return fn_(pair.first); |
||||||
|
} |
||||||
|
template <class Key> |
||||||
|
size_t operator()(const Key& key) const { |
||||||
|
return fn_(key); |
||||||
|
} |
||||||
|
template <class Key, class Value> |
||||||
|
bool operator()(const std::pair<Key, Value>& a, const std::pair<Key, Value>& b) const { |
||||||
|
return fn_(a.first, b.first); |
||||||
|
} |
||||||
|
template <class Key, class Value, class Element> |
||||||
|
bool operator()(const std::pair<Key, Value>& a, const Element& element) const { |
||||||
|
return fn_(a.first, element); |
||||||
|
} |
||||||
|
|
||||||
|
private: |
||||||
|
Fn fn_; |
||||||
|
}; |
||||||
|
|
||||||
|
template <class Key, class Value, class EmptyFn, |
||||||
|
class HashFn = std::hash<Key>, class Pred = std::equal_to<Key>, |
||||||
|
class Alloc = std::allocator<std::pair<Key, Value>>> |
||||||
|
class HashMap : public HashSet<std::pair<Key, Value>, |
||||||
|
EmptyFn, |
||||||
|
HashMapWrapper<HashFn>, |
||||||
|
HashMapWrapper<Pred>, |
||||||
|
Alloc> { |
||||||
|
private: |
||||||
|
using Base = HashSet<std::pair<Key, Value>, |
||||||
|
EmptyFn, |
||||||
|
HashMapWrapper<HashFn>, |
||||||
|
HashMapWrapper<Pred>, |
||||||
|
Alloc>; |
||||||
|
|
||||||
|
public: |
||||||
|
HashMap() : Base() { } |
||||||
|
explicit HashMap(const Alloc& alloc) |
||||||
|
: Base(alloc) { } |
||||||
|
}; |
||||||
|
|
||||||
|
} // namespace art_lkchan
|
||||||
|
|
||||||
|
#endif // ART_LIBARTBASE_BASE_HASH_MAP_H_
|
@ -0,0 +1,693 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2014 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_LIBARTBASE_BASE_HASH_SET_H_ |
||||||
|
#define ART_LIBARTBASE_BASE_HASH_SET_H_ |
||||||
|
|
||||||
|
#include <stdint.h> |
||||||
|
|
||||||
|
#include <functional> |
||||||
|
#include <iterator> |
||||||
|
#include <memory> |
||||||
|
#include <type_traits> |
||||||
|
#include <utility> |
||||||
|
|
||||||
|
#include <android-base/logging.h> |
||||||
|
|
||||||
|
#include "bit_utils.h" |
||||||
|
#include "macros.h" |
||||||
|
|
||||||
|
namespace art_lkchan { |
||||||
|
|
||||||
|
// Returns true if an item is empty.
|
||||||
|
template <class T> |
||||||
|
class DefaultEmptyFn { |
||||||
|
public: |
||||||
|
void MakeEmpty(T& item) const { |
||||||
|
item = T(); |
||||||
|
} |
||||||
|
bool IsEmpty(const T& item) const { |
||||||
|
return item == T(); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
template <class T> |
||||||
|
class DefaultEmptyFn<T*> { |
||||||
|
public: |
||||||
|
void MakeEmpty(T*& item) const { |
||||||
|
item = nullptr; |
||||||
|
} |
||||||
|
bool IsEmpty(T* const& item) const { |
||||||
|
return item == nullptr; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
// Low memory version of a hash set, uses less memory than std::unordered_set since elements aren't
|
||||||
|
// boxed. Uses linear probing to resolve collisions.
|
||||||
|
// EmptyFn needs to implement two functions MakeEmpty(T& item) and IsEmpty(const T& item).
|
||||||
|
// TODO: We could get rid of this requirement by using a bitmap, though maybe this would be slower
|
||||||
|
// and more complicated.
|
||||||
|
template <class T, class EmptyFn = DefaultEmptyFn<T>, class HashFn = std::hash<T>, |
||||||
|
class Pred = std::equal_to<T>, class Alloc = std::allocator<T>> |
||||||
|
class HashSet { |
||||||
|
template <class Elem, class HashSetType> |
||||||
|
class BaseIterator : std::iterator<std::forward_iterator_tag, Elem> { |
||||||
|
public: |
||||||
|
BaseIterator(const BaseIterator&) = default; |
||||||
|
BaseIterator(BaseIterator&&) = default; |
||||||
|
BaseIterator(HashSetType* hash_set, size_t index) : index_(index), hash_set_(hash_set) { |
||||||
|
} |
||||||
|
BaseIterator& operator=(const BaseIterator&) = default; |
||||||
|
BaseIterator& operator=(BaseIterator&&) = default; |
||||||
|
|
||||||
|
bool operator==(const BaseIterator& other) const { |
||||||
|
return hash_set_ == other.hash_set_ && this->index_ == other.index_; |
||||||
|
} |
||||||
|
|
||||||
|
bool operator!=(const BaseIterator& other) const { |
||||||
|
return !(*this == other); |
||||||
|
} |
||||||
|
|
||||||
|
BaseIterator operator++() { // Value after modification.
|
||||||
|
this->index_ = this->NextNonEmptySlot(this->index_, hash_set_); |
||||||
|
return *this; |
||||||
|
} |
||||||
|
|
||||||
|
BaseIterator operator++(int) { |
||||||
|
BaseIterator temp = *this; |
||||||
|
this->index_ = this->NextNonEmptySlot(this->index_, hash_set_); |
||||||
|
return temp; |
||||||
|
} |
||||||
|
|
||||||
|
Elem& operator*() const { |
||||||
|
DCHECK(!hash_set_->IsFreeSlot(this->index_)); |
||||||
|
return hash_set_->ElementForIndex(this->index_); |
||||||
|
} |
||||||
|
|
||||||
|
Elem* operator->() const { |
||||||
|
return &**this; |
||||||
|
} |
||||||
|
|
||||||
|
// TODO: Operator -- --(int) (and use std::bidirectional_iterator_tag)
|
||||||
|
|
||||||
|
private: |
||||||
|
size_t index_; |
||||||
|
HashSetType* hash_set_; |
||||||
|
|
||||||
|
size_t NextNonEmptySlot(size_t index, const HashSet* hash_set) const { |
||||||
|
const size_t num_buckets = hash_set->NumBuckets(); |
||||||
|
DCHECK_LT(index, num_buckets); |
||||||
|
do { |
||||||
|
++index; |
||||||
|
} while (index < num_buckets && hash_set->IsFreeSlot(index)); |
||||||
|
return index; |
||||||
|
} |
||||||
|
|
||||||
|
friend class HashSet; |
||||||
|
}; |
||||||
|
|
||||||
|
public: |
||||||
|
using value_type = T; |
||||||
|
using allocator_type = Alloc; |
||||||
|
using reference = T&; |
||||||
|
using const_reference = const T&; |
||||||
|
using pointer = T*; |
||||||
|
using const_pointer = const T*; |
||||||
|
using iterator = BaseIterator<T, HashSet>; |
||||||
|
using const_iterator = BaseIterator<const T, const HashSet>; |
||||||
|
using size_type = size_t; |
||||||
|
using difference_type = ptrdiff_t; |
||||||
|
|
||||||
|
static constexpr double kDefaultMinLoadFactor = 0.4; |
||||||
|
static constexpr double kDefaultMaxLoadFactor = 0.7; |
||||||
|
static constexpr size_t kMinBuckets = 1000; |
||||||
|
|
||||||
|
// If we don't own the data, this will create a new array which owns the data.
|
||||||
|
void Clear() { |
||||||
|
DeallocateStorage(); |
||||||
|
num_elements_ = 0; |
||||||
|
elements_until_expand_ = 0; |
||||||
|
} |
||||||
|
|
||||||
|
HashSet() : HashSet(kDefaultMinLoadFactor, kDefaultMaxLoadFactor) {} |
||||||
|
|
||||||
|
HashSet(double min_load_factor, double max_load_factor) noexcept |
||||||
|
: num_elements_(0u), |
||||||
|
num_buckets_(0u), |
||||||
|
elements_until_expand_(0u), |
||||||
|
owns_data_(false), |
||||||
|
data_(nullptr), |
||||||
|
min_load_factor_(min_load_factor), |
||||||
|
max_load_factor_(max_load_factor) { |
||||||
|
DCHECK_GT(min_load_factor, 0.0); |
||||||
|
DCHECK_LT(max_load_factor, 1.0); |
||||||
|
} |
||||||
|
|
||||||
|
explicit HashSet(const allocator_type& alloc) noexcept |
||||||
|
: allocfn_(alloc), |
||||||
|
hashfn_(), |
||||||
|
emptyfn_(), |
||||||
|
pred_(), |
||||||
|
num_elements_(0u), |
||||||
|
num_buckets_(0u), |
||||||
|
elements_until_expand_(0u), |
||||||
|
owns_data_(false), |
||||||
|
data_(nullptr), |
||||||
|
min_load_factor_(kDefaultMinLoadFactor), |
||||||
|
max_load_factor_(kDefaultMaxLoadFactor) { |
||||||
|
} |
||||||
|
|
||||||
|
HashSet(const HashSet& other) noexcept |
||||||
|
: allocfn_(other.allocfn_), |
||||||
|
hashfn_(other.hashfn_), |
||||||
|
emptyfn_(other.emptyfn_), |
||||||
|
pred_(other.pred_), |
||||||
|
num_elements_(other.num_elements_), |
||||||
|
num_buckets_(0), |
||||||
|
elements_until_expand_(other.elements_until_expand_), |
||||||
|
owns_data_(false), |
||||||
|
data_(nullptr), |
||||||
|
min_load_factor_(other.min_load_factor_), |
||||||
|
max_load_factor_(other.max_load_factor_) { |
||||||
|
AllocateStorage(other.NumBuckets()); |
||||||
|
for (size_t i = 0; i < num_buckets_; ++i) { |
||||||
|
ElementForIndex(i) = other.data_[i]; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// noexcept required so that the move constructor is used instead of copy constructor.
|
||||||
|
// b/27860101
|
||||||
|
HashSet(HashSet&& other) noexcept |
||||||
|
: allocfn_(std::move(other.allocfn_)), |
||||||
|
hashfn_(std::move(other.hashfn_)), |
||||||
|
emptyfn_(std::move(other.emptyfn_)), |
||||||
|
pred_(std::move(other.pred_)), |
||||||
|
num_elements_(other.num_elements_), |
||||||
|
num_buckets_(other.num_buckets_), |
||||||
|
elements_until_expand_(other.elements_until_expand_), |
||||||
|
owns_data_(other.owns_data_), |
||||||
|
data_(other.data_), |
||||||
|
min_load_factor_(other.min_load_factor_), |
||||||
|
max_load_factor_(other.max_load_factor_) { |
||||||
|
other.num_elements_ = 0u; |
||||||
|
other.num_buckets_ = 0u; |
||||||
|
other.elements_until_expand_ = 0u; |
||||||
|
other.owns_data_ = false; |
||||||
|
other.data_ = nullptr; |
||||||
|
} |
||||||
|
|
||||||
|
// Construct from existing data.
|
||||||
|
// Read from a block of memory, if make_copy_of_data is false, then data_ points to within the
|
||||||
|
// passed in ptr_.
|
||||||
|
HashSet(const uint8_t* ptr, bool make_copy_of_data, size_t* read_count) noexcept { |
||||||
|
uint64_t temp; |
||||||
|
size_t offset = 0; |
||||||
|
offset = ReadFromBytes(ptr, offset, &temp); |
||||||
|
num_elements_ = static_cast<uint64_t>(temp); |
||||||
|
offset = ReadFromBytes(ptr, offset, &temp); |
||||||
|
num_buckets_ = static_cast<uint64_t>(temp); |
||||||
|
CHECK_LE(num_elements_, num_buckets_); |
||||||
|
offset = ReadFromBytes(ptr, offset, &temp); |
||||||
|
elements_until_expand_ = static_cast<uint64_t>(temp); |
||||||
|
offset = ReadFromBytes(ptr, offset, &min_load_factor_); |
||||||
|
offset = ReadFromBytes(ptr, offset, &max_load_factor_); |
||||||
|
if (!make_copy_of_data) { |
||||||
|
owns_data_ = false; |
||||||
|
data_ = const_cast<T*>(reinterpret_cast<const T*>(ptr + offset)); |
||||||
|
offset += sizeof(*data_) * num_buckets_; |
||||||
|
} else { |
||||||
|
AllocateStorage(num_buckets_); |
||||||
|
// Write elements, not that this may not be safe for cross compilation if the elements are
|
||||||
|
// pointer sized.
|
||||||
|
for (size_t i = 0; i < num_buckets_; ++i) { |
||||||
|
offset = ReadFromBytes(ptr, offset, &data_[i]); |
||||||
|
} |
||||||
|
} |
||||||
|
// Caller responsible for aligning.
|
||||||
|
*read_count = offset; |
||||||
|
} |
||||||
|
|
||||||
|
// Returns how large the table is after being written. If target is null, then no writing happens
|
||||||
|
// but the size is still returned. Target must be 8 byte aligned.
|
||||||
|
size_t WriteToMemory(uint8_t* ptr) const { |
||||||
|
size_t offset = 0; |
||||||
|
offset = WriteToBytes(ptr, offset, static_cast<uint64_t>(num_elements_)); |
||||||
|
offset = WriteToBytes(ptr, offset, static_cast<uint64_t>(num_buckets_)); |
||||||
|
offset = WriteToBytes(ptr, offset, static_cast<uint64_t>(elements_until_expand_)); |
||||||
|
offset = WriteToBytes(ptr, offset, min_load_factor_); |
||||||
|
offset = WriteToBytes(ptr, offset, max_load_factor_); |
||||||
|
// Write elements, not that this may not be safe for cross compilation if the elements are
|
||||||
|
// pointer sized.
|
||||||
|
for (size_t i = 0; i < num_buckets_; ++i) { |
||||||
|
offset = WriteToBytes(ptr, offset, data_[i]); |
||||||
|
} |
||||||
|
// Caller responsible for aligning.
|
||||||
|
return offset; |
||||||
|
} |
||||||
|
|
||||||
|
~HashSet() { |
||||||
|
DeallocateStorage(); |
||||||
|
} |
||||||
|
|
||||||
|
HashSet& operator=(HashSet&& other) noexcept { |
||||||
|
HashSet(std::move(other)).swap(*this); // NOLINT [runtime/explicit] [5]
|
||||||
|
return *this; |
||||||
|
} |
||||||
|
|
||||||
|
HashSet& operator=(const HashSet& other) noexcept { |
||||||
|
HashSet(other).swap(*this); // NOLINT(runtime/explicit) - a case of lint gone mad.
|
||||||
|
return *this; |
||||||
|
} |
||||||
|
|
||||||
|
// Lower case for c++11 for each.
|
||||||
|
iterator begin() { |
||||||
|
iterator ret(this, 0); |
||||||
|
if (num_buckets_ != 0 && IsFreeSlot(ret.index_)) { |
||||||
|
++ret; // Skip all the empty slots.
|
||||||
|
} |
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
// Lower case for c++11 for each. const version.
|
||||||
|
const_iterator begin() const { |
||||||
|
const_iterator ret(this, 0); |
||||||
|
if (num_buckets_ != 0 && IsFreeSlot(ret.index_)) { |
||||||
|
++ret; // Skip all the empty slots.
|
||||||
|
} |
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
// Lower case for c++11 for each.
|
||||||
|
iterator end() { |
||||||
|
return iterator(this, NumBuckets()); |
||||||
|
} |
||||||
|
|
||||||
|
// Lower case for c++11 for each. const version.
|
||||||
|
const_iterator end() const { |
||||||
|
return const_iterator(this, NumBuckets()); |
||||||
|
} |
||||||
|
|
||||||
|
bool Empty() const { |
||||||
|
return Size() == 0; |
||||||
|
} |
||||||
|
|
||||||
|
// Return true if the hash set has ownership of the underlying data.
|
||||||
|
bool OwnsData() const { |
||||||
|
return owns_data_; |
||||||
|
} |
||||||
|
|
||||||
|
// Erase algorithm:
|
||||||
|
// Make an empty slot where the iterator is pointing.
|
||||||
|
// Scan forwards until we hit another empty slot.
|
||||||
|
// If an element in between doesn't rehash to the range from the current empty slot to the
|
||||||
|
// iterator. It must be before the empty slot, in that case we can move it to the empty slot
|
||||||
|
// and set the empty slot to be the location we just moved from.
|
||||||
|
// Relies on maintaining the invariant that there's no empty slots from the 'ideal' index of an
|
||||||
|
// element to its actual location/index.
|
||||||
|
iterator Erase(iterator it) { |
||||||
|
// empty_index is the index that will become empty.
|
||||||
|
size_t empty_index = it.index_; |
||||||
|
DCHECK(!IsFreeSlot(empty_index)); |
||||||
|
size_t next_index = empty_index; |
||||||
|
bool filled = false; // True if we filled the empty index.
|
||||||
|
while (true) { |
||||||
|
next_index = NextIndex(next_index); |
||||||
|
T& next_element = ElementForIndex(next_index); |
||||||
|
// If the next element is empty, we are done. Make sure to clear the current empty index.
|
||||||
|
if (emptyfn_.IsEmpty(next_element)) { |
||||||
|
emptyfn_.MakeEmpty(ElementForIndex(empty_index)); |
||||||
|
break; |
||||||
|
} |
||||||
|
// Otherwise try to see if the next element can fill the current empty index.
|
||||||
|
const size_t next_hash = hashfn_(next_element); |
||||||
|
// Calculate the ideal index, if it is within empty_index + 1 to next_index then there is
|
||||||
|
// nothing we can do.
|
||||||
|
size_t next_ideal_index = IndexForHash(next_hash); |
||||||
|
// Loop around if needed for our check.
|
||||||
|
size_t unwrapped_next_index = next_index; |
||||||
|
if (unwrapped_next_index < empty_index) { |
||||||
|
unwrapped_next_index += NumBuckets(); |
||||||
|
} |
||||||
|
// Loop around if needed for our check.
|
||||||
|
size_t unwrapped_next_ideal_index = next_ideal_index; |
||||||
|
if (unwrapped_next_ideal_index < empty_index) { |
||||||
|
unwrapped_next_ideal_index += NumBuckets(); |
||||||
|
} |
||||||
|
if (unwrapped_next_ideal_index <= empty_index || |
||||||
|
unwrapped_next_ideal_index > unwrapped_next_index) { |
||||||
|
// If the target index isn't within our current range it must have been probed from before
|
||||||
|
// the empty index.
|
||||||
|
ElementForIndex(empty_index) = std::move(next_element); |
||||||
|
filled = true; // TODO: Optimize
|
||||||
|
empty_index = next_index; |
||||||
|
} |
||||||
|
} |
||||||
|
--num_elements_; |
||||||
|
// If we didn't fill the slot then we need go to the next non free slot.
|
||||||
|
if (!filled) { |
||||||
|
++it; |
||||||
|
} |
||||||
|
return it; |
||||||
|
} |
||||||
|
|
||||||
|
// Find an element, returns end() if not found.
|
||||||
|
// Allows custom key (K) types, example of when this is useful:
|
||||||
|
// Set of Class* sorted by name, want to find a class with a name but can't allocate a dummy
|
||||||
|
// object in the heap for performance solution.
|
||||||
|
template <typename K> |
||||||
|
iterator Find(const K& key) { |
||||||
|
return FindWithHash(key, hashfn_(key)); |
||||||
|
} |
||||||
|
|
||||||
|
template <typename K> |
||||||
|
const_iterator Find(const K& key) const { |
||||||
|
return FindWithHash(key, hashfn_(key)); |
||||||
|
} |
||||||
|
|
||||||
|
template <typename K> |
||||||
|
iterator FindWithHash(const K& key, size_t hash) { |
||||||
|
return iterator(this, FindIndex(key, hash)); |
||||||
|
} |
||||||
|
|
||||||
|
template <typename K> |
||||||
|
const_iterator FindWithHash(const K& key, size_t hash) const { |
||||||
|
return const_iterator(this, FindIndex(key, hash)); |
||||||
|
} |
||||||
|
|
||||||
|
// Insert an element, allows duplicates.
|
||||||
|
template <typename U, typename = typename std::enable_if<std::is_convertible<U, T>::value>::type> |
||||||
|
void Insert(U&& element) { |
||||||
|
InsertWithHash(std::forward<U>(element), hashfn_(element)); |
||||||
|
} |
||||||
|
|
||||||
|
template <typename U, typename = typename std::enable_if<std::is_convertible<U, T>::value>::type> |
||||||
|
void InsertWithHash(U&& element, size_t hash) { |
||||||
|
DCHECK_EQ(hash, hashfn_(element)); |
||||||
|
if (num_elements_ >= elements_until_expand_) { |
||||||
|
Expand(); |
||||||
|
DCHECK_LT(num_elements_, elements_until_expand_); |
||||||
|
} |
||||||
|
const size_t index = FirstAvailableSlot(IndexForHash(hash)); |
||||||
|
data_[index] = std::forward<U>(element); |
||||||
|
++num_elements_; |
||||||
|
} |
||||||
|
|
||||||
|
size_t Size() const { |
||||||
|
return num_elements_; |
||||||
|
} |
||||||
|
|
||||||
|
void swap(HashSet& other) { |
||||||
|
// Use argument-dependent lookup with fall-back to std::swap() for function objects.
|
||||||
|
using std::swap; |
||||||
|
swap(allocfn_, other.allocfn_); |
||||||
|
swap(hashfn_, other.hashfn_); |
||||||
|
swap(emptyfn_, other.emptyfn_); |
||||||
|
swap(pred_, other.pred_); |
||||||
|
std::swap(data_, other.data_); |
||||||
|
std::swap(num_buckets_, other.num_buckets_); |
||||||
|
std::swap(num_elements_, other.num_elements_); |
||||||
|
std::swap(elements_until_expand_, other.elements_until_expand_); |
||||||
|
std::swap(min_load_factor_, other.min_load_factor_); |
||||||
|
std::swap(max_load_factor_, other.max_load_factor_); |
||||||
|
std::swap(owns_data_, other.owns_data_); |
||||||
|
} |
||||||
|
|
||||||
|
allocator_type get_allocator() const { |
||||||
|
return allocfn_; |
||||||
|
} |
||||||
|
|
||||||
|
void ShrinkToMaximumLoad() { |
||||||
|
Resize(Size() / max_load_factor_); |
||||||
|
} |
||||||
|
|
||||||
|
// Reserve enough room to insert until Size() == num_elements without requiring to grow the hash
|
||||||
|
// set. No-op if the hash set is already large enough to do this.
|
||||||
|
void Reserve(size_t num_elements) { |
||||||
|
size_t num_buckets = num_elements / max_load_factor_; |
||||||
|
// Deal with rounding errors. Add one for rounding.
|
||||||
|
while (static_cast<size_t>(num_buckets * max_load_factor_) <= num_elements + 1u) { |
||||||
|
++num_buckets; |
||||||
|
} |
||||||
|
if (num_buckets > NumBuckets()) { |
||||||
|
Resize(num_buckets); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// To distance that inserted elements were probed. Used for measuring how good hash functions
|
||||||
|
// are.
|
||||||
|
size_t TotalProbeDistance() const { |
||||||
|
size_t total = 0; |
||||||
|
for (size_t i = 0; i < NumBuckets(); ++i) { |
||||||
|
const T& element = ElementForIndex(i); |
||||||
|
if (!emptyfn_.IsEmpty(element)) { |
||||||
|
size_t ideal_location = IndexForHash(hashfn_(element)); |
||||||
|
if (ideal_location > i) { |
||||||
|
total += i + NumBuckets() - ideal_location; |
||||||
|
} else { |
||||||
|
total += i - ideal_location; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return total; |
||||||
|
} |
||||||
|
|
||||||
|
// Calculate the current load factor and return it.
|
||||||
|
double CalculateLoadFactor() const { |
||||||
|
return static_cast<double>(Size()) / static_cast<double>(NumBuckets()); |
||||||
|
} |
||||||
|
|
||||||
|
// Make sure that everything reinserts in the right spot. Returns the number of errors.
|
||||||
|
size_t Verify() NO_THREAD_SAFETY_ANALYSIS { |
||||||
|
size_t errors = 0; |
||||||
|
for (size_t i = 0; i < num_buckets_; ++i) { |
||||||
|
T& element = data_[i]; |
||||||
|
if (!emptyfn_.IsEmpty(element)) { |
||||||
|
T temp; |
||||||
|
emptyfn_.MakeEmpty(temp); |
||||||
|
std::swap(temp, element); |
||||||
|
size_t first_slot = FirstAvailableSlot(IndexForHash(hashfn_(temp))); |
||||||
|
if (i != first_slot) { |
||||||
|
LOG(ERROR) << "Element " << i << " should be in slot " << first_slot; |
||||||
|
++errors; |
||||||
|
} |
||||||
|
std::swap(temp, element); |
||||||
|
} |
||||||
|
} |
||||||
|
return errors; |
||||||
|
} |
||||||
|
|
||||||
|
double GetMinLoadFactor() const { |
||||||
|
return min_load_factor_; |
||||||
|
} |
||||||
|
|
||||||
|
double GetMaxLoadFactor() const { |
||||||
|
return max_load_factor_; |
||||||
|
} |
||||||
|
|
||||||
|
// Change the load factor of the hash set. If the current load factor is greater than the max
|
||||||
|
// specified, then we resize the hash table storage.
|
||||||
|
void SetLoadFactor(double min_load_factor, double max_load_factor) { |
||||||
|
DCHECK_LT(min_load_factor, max_load_factor); |
||||||
|
DCHECK_GT(min_load_factor, 0.0); |
||||||
|
DCHECK_LT(max_load_factor, 1.0); |
||||||
|
min_load_factor_ = min_load_factor; |
||||||
|
max_load_factor_ = max_load_factor; |
||||||
|
elements_until_expand_ = NumBuckets() * max_load_factor_; |
||||||
|
// If the current load factor isn't in the range, then resize to the mean of the minimum and
|
||||||
|
// maximum load factor.
|
||||||
|
const double load_factor = CalculateLoadFactor(); |
||||||
|
if (load_factor > max_load_factor_) { |
||||||
|
Resize(Size() / ((min_load_factor_ + max_load_factor_) * 0.5)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// The hash set expands when Size() reaches ElementsUntilExpand().
|
||||||
|
size_t ElementsUntilExpand() const { |
||||||
|
return elements_until_expand_; |
||||||
|
} |
||||||
|
|
||||||
|
size_t NumBuckets() const { |
||||||
|
return num_buckets_; |
||||||
|
} |
||||||
|
|
||||||
|
private: |
||||||
|
T& ElementForIndex(size_t index) { |
||||||
|
DCHECK_LT(index, NumBuckets()); |
||||||
|
DCHECK(data_ != nullptr); |
||||||
|
return data_[index]; |
||||||
|
} |
||||||
|
|
||||||
|
const T& ElementForIndex(size_t index) const { |
||||||
|
DCHECK_LT(index, NumBuckets()); |
||||||
|
DCHECK(data_ != nullptr); |
||||||
|
return data_[index]; |
||||||
|
} |
||||||
|
|
||||||
|
size_t IndexForHash(size_t hash) const { |
||||||
|
// Protect against undefined behavior (division by zero).
|
||||||
|
if (UNLIKELY(num_buckets_ == 0)) { |
||||||
|
return 0; |
||||||
|
} |
||||||
|
return hash % num_buckets_; |
||||||
|
} |
||||||
|
|
||||||
|
size_t NextIndex(size_t index) const { |
||||||
|
if (UNLIKELY(++index >= num_buckets_)) { |
||||||
|
DCHECK_EQ(index, NumBuckets()); |
||||||
|
return 0; |
||||||
|
} |
||||||
|
return index; |
||||||
|
} |
||||||
|
|
||||||
|
// Find the hash table slot for an element, or return NumBuckets() if not found.
|
||||||
|
// This value for not found is important so that iterator(this, FindIndex(...)) == end().
|
||||||
|
template <typename K> |
||||||
|
size_t FindIndex(const K& element, size_t hash) const { |
||||||
|
// Guard against failing to get an element for a non-existing index.
|
||||||
|
if (UNLIKELY(NumBuckets() == 0)) { |
||||||
|
return 0; |
||||||
|
} |
||||||
|
DCHECK_EQ(hashfn_(element), hash); |
||||||
|
size_t index = IndexForHash(hash); |
||||||
|
while (true) { |
||||||
|
const T& slot = ElementForIndex(index); |
||||||
|
if (emptyfn_.IsEmpty(slot)) { |
||||||
|
return NumBuckets(); |
||||||
|
} |
||||||
|
if (pred_(slot, element)) { |
||||||
|
return index; |
||||||
|
} |
||||||
|
index = NextIndex(index); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
bool IsFreeSlot(size_t index) const { |
||||||
|
return emptyfn_.IsEmpty(ElementForIndex(index)); |
||||||
|
} |
||||||
|
|
||||||
|
// Allocate a number of buckets.
|
||||||
|
void AllocateStorage(size_t num_buckets) { |
||||||
|
num_buckets_ = num_buckets; |
||||||
|
data_ = allocfn_.allocate(num_buckets_); |
||||||
|
owns_data_ = true; |
||||||
|
for (size_t i = 0; i < num_buckets_; ++i) { |
||||||
|
allocfn_.construct(allocfn_.address(data_[i])); |
||||||
|
emptyfn_.MakeEmpty(data_[i]); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void DeallocateStorage() { |
||||||
|
if (owns_data_) { |
||||||
|
for (size_t i = 0; i < NumBuckets(); ++i) { |
||||||
|
allocfn_.destroy(allocfn_.address(data_[i])); |
||||||
|
} |
||||||
|
if (data_ != nullptr) { |
||||||
|
allocfn_.deallocate(data_, NumBuckets()); |
||||||
|
} |
||||||
|
owns_data_ = false; |
||||||
|
} |
||||||
|
data_ = nullptr; |
||||||
|
num_buckets_ = 0; |
||||||
|
} |
||||||
|
|
||||||
|
// Expand the set based on the load factors.
|
||||||
|
void Expand() { |
||||||
|
size_t min_index = static_cast<size_t>(Size() / min_load_factor_); |
||||||
|
// Resize based on the minimum load factor.
|
||||||
|
Resize(min_index); |
||||||
|
} |
||||||
|
|
||||||
|
// Expand / shrink the table to the new specified size.
|
||||||
|
void Resize(size_t new_size) { |
||||||
|
if (new_size < kMinBuckets) { |
||||||
|
new_size = kMinBuckets; |
||||||
|
} |
||||||
|
DCHECK_GE(new_size, Size()); |
||||||
|
T* const old_data = data_; |
||||||
|
size_t old_num_buckets = num_buckets_; |
||||||
|
// Reinsert all of the old elements.
|
||||||
|
const bool owned_data = owns_data_; |
||||||
|
AllocateStorage(new_size); |
||||||
|
for (size_t i = 0; i < old_num_buckets; ++i) { |
||||||
|
T& element = old_data[i]; |
||||||
|
if (!emptyfn_.IsEmpty(element)) { |
||||||
|
data_[FirstAvailableSlot(IndexForHash(hashfn_(element)))] = std::move(element); |
||||||
|
} |
||||||
|
if (owned_data) { |
||||||
|
allocfn_.destroy(allocfn_.address(element)); |
||||||
|
} |
||||||
|
} |
||||||
|
if (owned_data) { |
||||||
|
allocfn_.deallocate(old_data, old_num_buckets); |
||||||
|
} |
||||||
|
|
||||||
|
// When we hit elements_until_expand_, we are at the max load factor and must expand again.
|
||||||
|
elements_until_expand_ = NumBuckets() * max_load_factor_; |
||||||
|
} |
||||||
|
|
||||||
|
ALWAYS_INLINE size_t FirstAvailableSlot(size_t index) const { |
||||||
|
DCHECK_LT(index, NumBuckets()); // Don't try to get a slot out of range.
|
||||||
|
size_t non_empty_count = 0; |
||||||
|
while (!emptyfn_.IsEmpty(data_[index])) { |
||||||
|
index = NextIndex(index); |
||||||
|
non_empty_count++; |
||||||
|
DCHECK_LE(non_empty_count, NumBuckets()); // Don't loop forever.
|
||||||
|
} |
||||||
|
return index; |
||||||
|
} |
||||||
|
|
||||||
|
// Return new offset.
|
||||||
|
template <typename Elem> |
||||||
|
static size_t WriteToBytes(uint8_t* ptr, size_t offset, Elem n) { |
||||||
|
DCHECK_ALIGNED(ptr + offset, sizeof(n)); |
||||||
|
if (ptr != nullptr) { |
||||||
|
*reinterpret_cast<Elem*>(ptr + offset) = n; |
||||||
|
} |
||||||
|
return offset + sizeof(n); |
||||||
|
} |
||||||
|
|
||||||
|
template <typename Elem> |
||||||
|
static size_t ReadFromBytes(const uint8_t* ptr, size_t offset, Elem* out) { |
||||||
|
DCHECK(ptr != nullptr); |
||||||
|
DCHECK_ALIGNED(ptr + offset, sizeof(*out)); |
||||||
|
*out = *reinterpret_cast<const Elem*>(ptr + offset); |
||||||
|
return offset + sizeof(*out); |
||||||
|
} |
||||||
|
|
||||||
|
Alloc allocfn_; // Allocator function.
|
||||||
|
HashFn hashfn_; // Hashing function.
|
||||||
|
EmptyFn emptyfn_; // IsEmpty/SetEmpty function.
|
||||||
|
Pred pred_; // Equals function.
|
||||||
|
size_t num_elements_; // Number of inserted elements.
|
||||||
|
size_t num_buckets_; // Number of hash table buckets.
|
||||||
|
size_t elements_until_expand_; // Maximum number of elements until we expand the table.
|
||||||
|
bool owns_data_; // If we own data_ and are responsible for freeing it.
|
||||||
|
T* data_; // Backing storage.
|
||||||
|
double min_load_factor_; |
||||||
|
double max_load_factor_; |
||||||
|
|
||||||
|
ART_FRIEND_TEST(InternTableTest, CrossHash); |
||||||
|
}; |
||||||
|
|
||||||
|
template <class T, class EmptyFn, class HashFn, class Pred, class Alloc> |
||||||
|
void swap(HashSet<T, EmptyFn, HashFn, Pred, Alloc>& lhs, |
||||||
|
HashSet<T, EmptyFn, HashFn, Pred, Alloc>& rhs) { |
||||||
|
lhs.swap(rhs); |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace art_lkchan
|
||||||
|
|
||||||
|
#endif // ART_LIBARTBASE_BASE_HASH_SET_H_
|
@ -0,0 +1,70 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2015 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_LIBARTBASE_BASE_ITERATION_RANGE_H_ |
||||||
|
#define ART_LIBARTBASE_BASE_ITERATION_RANGE_H_ |
||||||
|
|
||||||
|
#include <iterator> |
||||||
|
|
||||||
|
namespace art_lkchan { |
||||||
|
|
||||||
|
// Helper class that acts as a container for range-based loops, given an iteration
|
||||||
|
// range [first, last) defined by two iterators.
|
||||||
|
template <typename Iter> |
||||||
|
class IterationRange { |
||||||
|
public: |
||||||
|
typedef Iter iterator; |
||||||
|
typedef typename std::iterator_traits<Iter>::difference_type difference_type; |
||||||
|
typedef typename std::iterator_traits<Iter>::value_type value_type; |
||||||
|
typedef typename std::iterator_traits<Iter>::pointer pointer; |
||||||
|
typedef typename std::iterator_traits<Iter>::reference reference; |
||||||
|
|
||||||
|
IterationRange(iterator first, iterator last) : first_(first), last_(last) { } |
||||||
|
|
||||||
|
iterator begin() const { return first_; } |
||||||
|
iterator end() const { return last_; } |
||||||
|
iterator cbegin() const { return first_; } |
||||||
|
iterator cend() const { return last_; } |
||||||
|
|
||||||
|
private: |
||||||
|
const iterator first_; |
||||||
|
const iterator last_; |
||||||
|
}; |
||||||
|
|
||||||
|
template <typename Iter> |
||||||
|
inline IterationRange<Iter> MakeIterationRange(const Iter& begin_it, const Iter& end_it) { |
||||||
|
return IterationRange<Iter>(begin_it, end_it); |
||||||
|
} |
||||||
|
|
||||||
|
template <typename Iter> |
||||||
|
inline IterationRange<Iter> MakeEmptyIterationRange(const Iter& it) { |
||||||
|
return IterationRange<Iter>(it, it); |
||||||
|
} |
||||||
|
|
||||||
|
template <typename Container> |
||||||
|
inline auto ReverseRange(Container&& c) { |
||||||
|
typedef typename std::reverse_iterator<decltype(c.begin())> riter; |
||||||
|
return MakeIterationRange(riter(c.end()), riter(c.begin())); |
||||||
|
} |
||||||
|
|
||||||
|
template <typename T, size_t size> |
||||||
|
inline auto ReverseRange(T (&array)[size]) { |
||||||
|
return ReverseRange(MakeIterationRange<T*>(array, array+size)); |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace art_lkchan
|
||||||
|
|
||||||
|
#endif // ART_LIBARTBASE_BASE_ITERATION_RANGE_H_
|
@ -0,0 +1,377 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011 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_LIBARTBASE_BASE_LEB128_H_ |
||||||
|
#define ART_LIBARTBASE_BASE_LEB128_H_ |
||||||
|
|
||||||
|
#include <vector> |
||||||
|
|
||||||
|
#include <android-base/logging.h> |
||||||
|
|
||||||
|
#include "base/bit_utils.h" |
||||||
|
#include "base/globals.h" |
||||||
|
#include "base/macros.h" |
||||||
|
|
||||||
|
namespace art_lkchan { |
||||||
|
|
||||||
|
// Reads an unsigned LEB128 value, updating the given pointer to point
|
||||||
|
// just past the end of the read value. This function tolerates
|
||||||
|
// non-zero high-order bits in the fifth encoded byte.
|
||||||
|
static inline uint32_t DecodeUnsignedLeb128(const uint8_t** data) { |
||||||
|
const uint8_t* ptr = *data; |
||||||
|
int result = *(ptr++); |
||||||
|
if (UNLIKELY(result > 0x7f)) { |
||||||
|
int cur = *(ptr++); |
||||||
|
result = (result & 0x7f) | ((cur & 0x7f) << 7); |
||||||
|
if (cur > 0x7f) { |
||||||
|
cur = *(ptr++); |
||||||
|
result |= (cur & 0x7f) << 14; |
||||||
|
if (cur > 0x7f) { |
||||||
|
cur = *(ptr++); |
||||||
|
result |= (cur & 0x7f) << 21; |
||||||
|
if (cur > 0x7f) { |
||||||
|
// Note: We don't check to see if cur is out of range here,
|
||||||
|
// meaning we tolerate garbage in the four high-order bits.
|
||||||
|
cur = *(ptr++); |
||||||
|
result |= cur << 28; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
*data = ptr; |
||||||
|
return static_cast<uint32_t>(result); |
||||||
|
} |
||||||
|
|
||||||
|
static inline uint32_t DecodeUnsignedLeb128WithoutMovingCursor(const uint8_t* data) { |
||||||
|
return DecodeUnsignedLeb128(&data); |
||||||
|
} |
||||||
|
|
||||||
|
static inline bool DecodeUnsignedLeb128Checked(const uint8_t** data, |
||||||
|
const void* end, |
||||||
|
uint32_t* out) { |
||||||
|
const uint8_t* ptr = *data; |
||||||
|
if (ptr >= end) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
int result = *(ptr++); |
||||||
|
if (UNLIKELY(result > 0x7f)) { |
||||||
|
if (ptr >= end) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
int cur = *(ptr++); |
||||||
|
result = (result & 0x7f) | ((cur & 0x7f) << 7); |
||||||
|
if (cur > 0x7f) { |
||||||
|
if (ptr >= end) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
cur = *(ptr++); |
||||||
|
result |= (cur & 0x7f) << 14; |
||||||
|
if (cur > 0x7f) { |
||||||
|
if (ptr >= end) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
cur = *(ptr++); |
||||||
|
result |= (cur & 0x7f) << 21; |
||||||
|
if (cur > 0x7f) { |
||||||
|
if (ptr >= end) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
// Note: We don't check to see if cur is out of range here,
|
||||||
|
// meaning we tolerate garbage in the four high-order bits.
|
||||||
|
cur = *(ptr++); |
||||||
|
result |= cur << 28; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
*data = ptr; |
||||||
|
*out = static_cast<uint32_t>(result); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
// Reads an unsigned LEB128 + 1 value. updating the given pointer to point
|
||||||
|
// just past the end of the read value. This function tolerates
|
||||||
|
// non-zero high-order bits in the fifth encoded byte.
|
||||||
|
// It is possible for this function to return -1.
|
||||||
|
static inline int32_t DecodeUnsignedLeb128P1(const uint8_t** data) { |
||||||
|
return DecodeUnsignedLeb128(data) - 1; |
||||||
|
} |
||||||
|
|
||||||
|
// Reads a signed LEB128 value, updating the given pointer to point
|
||||||
|
// just past the end of the read value. This function tolerates
|
||||||
|
// non-zero high-order bits in the fifth encoded byte.
|
||||||
|
static inline int32_t DecodeSignedLeb128(const uint8_t** data) { |
||||||
|
const uint8_t* ptr = *data; |
||||||
|
int32_t result = *(ptr++); |
||||||
|
if (result <= 0x7f) { |
||||||
|
result = (result << 25) >> 25; |
||||||
|
} else { |
||||||
|
int cur = *(ptr++); |
||||||
|
result = (result & 0x7f) | ((cur & 0x7f) << 7); |
||||||
|
if (cur <= 0x7f) { |
||||||
|
result = (result << 18) >> 18; |
||||||
|
} else { |
||||||
|
cur = *(ptr++); |
||||||
|
result |= (cur & 0x7f) << 14; |
||||||
|
if (cur <= 0x7f) { |
||||||
|
result = (result << 11) >> 11; |
||||||
|
} else { |
||||||
|
cur = *(ptr++); |
||||||
|
result |= (cur & 0x7f) << 21; |
||||||
|
if (cur <= 0x7f) { |
||||||
|
result = (result << 4) >> 4; |
||||||
|
} else { |
||||||
|
// Note: We don't check to see if cur is out of range here,
|
||||||
|
// meaning we tolerate garbage in the four high-order bits.
|
||||||
|
cur = *(ptr++); |
||||||
|
result |= cur << 28; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
*data = ptr; |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
static inline bool DecodeSignedLeb128Checked(const uint8_t** data, |
||||||
|
const void* end, |
||||||
|
int32_t* out) { |
||||||
|
const uint8_t* ptr = *data; |
||||||
|
if (ptr >= end) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
int32_t result = *(ptr++); |
||||||
|
if (result <= 0x7f) { |
||||||
|
result = (result << 25) >> 25; |
||||||
|
} else { |
||||||
|
if (ptr >= end) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
int cur = *(ptr++); |
||||||
|
result = (result & 0x7f) | ((cur & 0x7f) << 7); |
||||||
|
if (cur <= 0x7f) { |
||||||
|
result = (result << 18) >> 18; |
||||||
|
} else { |
||||||
|
if (ptr >= end) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
cur = *(ptr++); |
||||||
|
result |= (cur & 0x7f) << 14; |
||||||
|
if (cur <= 0x7f) { |
||||||
|
result = (result << 11) >> 11; |
||||||
|
} else { |
||||||
|
if (ptr >= end) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
cur = *(ptr++); |
||||||
|
result |= (cur & 0x7f) << 21; |
||||||
|
if (cur <= 0x7f) { |
||||||
|
result = (result << 4) >> 4; |
||||||
|
} else { |
||||||
|
if (ptr >= end) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
// Note: We don't check to see if cur is out of range here,
|
||||||
|
// meaning we tolerate garbage in the four high-order bits.
|
||||||
|
cur = *(ptr++); |
||||||
|
result |= cur << 28; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
*data = ptr; |
||||||
|
*out = static_cast<uint32_t>(result); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
// Returns the number of bytes needed to encode the value in unsigned LEB128.
|
||||||
|
static inline uint32_t UnsignedLeb128Size(uint32_t data) { |
||||||
|
// bits_to_encode = (data != 0) ? 32 - CLZ(x) : 1 // 32 - CLZ(data | 1)
|
||||||
|
// bytes = ceil(bits_to_encode / 7.0); // (6 + bits_to_encode) / 7
|
||||||
|
uint32_t x = 6 + 32 - CLZ(data | 1U); |
||||||
|
// Division by 7 is done by (x * 37) >> 8 where 37 = ceil(256 / 7).
|
||||||
|
// This works for 0 <= x < 256 / (7 * 37 - 256), i.e. 0 <= x <= 85.
|
||||||
|
return (x * 37) >> 8; |
||||||
|
} |
||||||
|
|
||||||
|
static inline bool IsLeb128Terminator(const uint8_t* ptr) { |
||||||
|
return *ptr <= 0x7f; |
||||||
|
} |
||||||
|
|
||||||
|
// Returns the first byte of a Leb128 value assuming that:
|
||||||
|
// (1) `end_ptr` points to the first byte after the Leb128 value, and
|
||||||
|
// (2) there is another Leb128 value before this one.
|
||||||
|
template <typename T> |
||||||
|
static inline T* ReverseSearchUnsignedLeb128(T* end_ptr) { |
||||||
|
static_assert(std::is_same<typename std::remove_const<T>::type, uint8_t>::value, |
||||||
|
"T must be a uint8_t"); |
||||||
|
T* ptr = end_ptr; |
||||||
|
|
||||||
|
// Move one byte back, check that this is the terminating byte.
|
||||||
|
ptr--; |
||||||
|
DCHECK(IsLeb128Terminator(ptr)); |
||||||
|
|
||||||
|
// Keep moving back while the previous byte is not a terminating byte.
|
||||||
|
// Fail after reading five bytes in case there isn't another Leb128 value
|
||||||
|
// before this one.
|
||||||
|
while (!IsLeb128Terminator(ptr - 1)) { |
||||||
|
ptr--; |
||||||
|
DCHECK_LE(static_cast<ptrdiff_t>(end_ptr - ptr), 5); |
||||||
|
} |
||||||
|
|
||||||
|
return ptr; |
||||||
|
} |
||||||
|
|
||||||
|
// Returns the number of bytes needed to encode the value in unsigned LEB128.
|
||||||
|
static inline uint32_t SignedLeb128Size(int32_t data) { |
||||||
|
// Like UnsignedLeb128Size(), but we need one bit beyond the highest bit that differs from sign.
|
||||||
|
data = data ^ (data >> 31); |
||||||
|
uint32_t x = 1 /* we need to encode the sign bit */ + 6 + 32 - CLZ(data | 1U); |
||||||
|
return (x * 37) >> 8; |
||||||
|
} |
||||||
|
|
||||||
|
static inline uint8_t* EncodeUnsignedLeb128(uint8_t* dest, uint32_t value) { |
||||||
|
uint8_t out = value & 0x7f; |
||||||
|
value >>= 7; |
||||||
|
while (value != 0) { |
||||||
|
*dest++ = out | 0x80; |
||||||
|
out = value & 0x7f; |
||||||
|
value >>= 7; |
||||||
|
} |
||||||
|
*dest++ = out; |
||||||
|
return dest; |
||||||
|
} |
||||||
|
|
||||||
|
template <typename Vector> |
||||||
|
static inline void EncodeUnsignedLeb128(Vector* dest, uint32_t value) { |
||||||
|
static_assert(std::is_same<typename Vector::value_type, uint8_t>::value, "Invalid value type"); |
||||||
|
uint8_t out = value & 0x7f; |
||||||
|
value >>= 7; |
||||||
|
while (value != 0) { |
||||||
|
dest->push_back(out | 0x80); |
||||||
|
out = value & 0x7f; |
||||||
|
value >>= 7; |
||||||
|
} |
||||||
|
dest->push_back(out); |
||||||
|
} |
||||||
|
|
||||||
|
// Overwrite encoded Leb128 with a new value. The new value must be less than
|
||||||
|
// or equal to the old value to ensure that it fits the allocated space.
|
||||||
|
static inline void UpdateUnsignedLeb128(uint8_t* dest, uint32_t value) { |
||||||
|
const uint8_t* old_end = dest; |
||||||
|
uint32_t old_value = DecodeUnsignedLeb128(&old_end); |
||||||
|
DCHECK_LE(UnsignedLeb128Size(value), UnsignedLeb128Size(old_value)); |
||||||
|
for (uint8_t* end = EncodeUnsignedLeb128(dest, value); end < old_end; end++) { |
||||||
|
// Use longer encoding than necessary to fill the allocated space.
|
||||||
|
end[-1] |= 0x80; |
||||||
|
end[0] = 0; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
static inline uint8_t* EncodeSignedLeb128(uint8_t* dest, int32_t value) { |
||||||
|
uint32_t extra_bits = static_cast<uint32_t>(value ^ (value >> 31)) >> 6; |
||||||
|
uint8_t out = value & 0x7f; |
||||||
|
while (extra_bits != 0u) { |
||||||
|
*dest++ = out | 0x80; |
||||||
|
value >>= 7; |
||||||
|
out = value & 0x7f; |
||||||
|
extra_bits >>= 7; |
||||||
|
} |
||||||
|
*dest++ = out; |
||||||
|
return dest; |
||||||
|
} |
||||||
|
|
||||||
|
template<typename Vector> |
||||||
|
static inline void EncodeSignedLeb128(Vector* dest, int32_t value) { |
||||||
|
static_assert(std::is_same<typename Vector::value_type, uint8_t>::value, "Invalid value type"); |
||||||
|
uint32_t extra_bits = static_cast<uint32_t>(value ^ (value >> 31)) >> 6; |
||||||
|
uint8_t out = value & 0x7f; |
||||||
|
while (extra_bits != 0u) { |
||||||
|
dest->push_back(out | 0x80); |
||||||
|
value >>= 7; |
||||||
|
out = value & 0x7f; |
||||||
|
extra_bits >>= 7; |
||||||
|
} |
||||||
|
dest->push_back(out); |
||||||
|
} |
||||||
|
|
||||||
|
// An encoder that pushes int32_t/uint32_t data onto the given std::vector.
|
||||||
|
template <typename Vector = std::vector<uint8_t>> |
||||||
|
class Leb128Encoder { |
||||||
|
static_assert(std::is_same<typename Vector::value_type, uint8_t>::value, "Invalid value type"); |
||||||
|
|
||||||
|
public: |
||||||
|
explicit Leb128Encoder(Vector* data) : data_(data) { |
||||||
|
DCHECK(data != nullptr); |
||||||
|
} |
||||||
|
|
||||||
|
void Reserve(uint32_t size) { |
||||||
|
data_->reserve(size); |
||||||
|
} |
||||||
|
|
||||||
|
void PushBackUnsigned(uint32_t value) { |
||||||
|
EncodeUnsignedLeb128(data_, value); |
||||||
|
} |
||||||
|
|
||||||
|
template<typename It> |
||||||
|
void InsertBackUnsigned(It cur, It end) { |
||||||
|
for (; cur != end; ++cur) { |
||||||
|
PushBackUnsigned(*cur); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void PushBackSigned(int32_t value) { |
||||||
|
EncodeSignedLeb128(data_, value); |
||||||
|
} |
||||||
|
|
||||||
|
template<typename It> |
||||||
|
void InsertBackSigned(It cur, It end) { |
||||||
|
for (; cur != end; ++cur) { |
||||||
|
PushBackSigned(*cur); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
const Vector& GetData() const { |
||||||
|
return *data_; |
||||||
|
} |
||||||
|
|
||||||
|
protected: |
||||||
|
Vector* const data_; |
||||||
|
|
||||||
|
private: |
||||||
|
DISALLOW_COPY_AND_ASSIGN(Leb128Encoder); |
||||||
|
}; |
||||||
|
|
||||||
|
// An encoder with an API similar to vector<uint32_t> where the data is captured in ULEB128 format.
|
||||||
|
template <typename Vector = std::vector<uint8_t>> |
||||||
|
class Leb128EncodingVector FINAL : private Vector, |
||||||
|
public Leb128Encoder<Vector> { |
||||||
|
static_assert(std::is_same<typename Vector::value_type, uint8_t>::value, "Invalid value type"); |
||||||
|
|
||||||
|
public: |
||||||
|
Leb128EncodingVector() : Leb128Encoder<Vector>(this) { } |
||||||
|
|
||||||
|
explicit Leb128EncodingVector(const typename Vector::allocator_type& alloc) |
||||||
|
: Vector(alloc), |
||||||
|
Leb128Encoder<Vector>(this) { } |
||||||
|
|
||||||
|
private: |
||||||
|
DISALLOW_COPY_AND_ASSIGN(Leb128EncodingVector); |
||||||
|
}; |
||||||
|
|
||||||
|
} // namespace art_lkchan
|
||||||
|
|
||||||
|
#endif // ART_LIBARTBASE_BASE_LEB128_H_
|
@ -0,0 +1,101 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2010 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_LIBARTBASE_BASE_MACROS_H_ |
||||||
|
#define ART_LIBARTBASE_BASE_MACROS_H_ |
||||||
|
|
||||||
|
#include <stddef.h> // for size_t |
||||||
|
#include <unistd.h> // for TEMP_FAILURE_RETRY |
||||||
|
|
||||||
|
#include "android-base/macros.h" |
||||||
|
#include "android-base/thread_annotations.h" |
||||||
|
|
||||||
|
#define OVERRIDE override |
||||||
|
#define FINAL final |
||||||
|
|
||||||
|
// Declare a friend relationship in a class with a test. Used rather that FRIEND_TEST to avoid
|
||||||
|
// globally importing gtest/gtest.h into the main ART header files.
|
||||||
|
#define ART_FRIEND_TEST(test_set_name, individual_test)\ |
||||||
|
friend class test_set_name##_##individual_test##_Test |
||||||
|
|
||||||
|
// Declare a friend relationship in a class with a typed test.
|
||||||
|
#define ART_FRIEND_TYPED_TEST(test_set_name, individual_test)\ |
||||||
|
template<typename T> ART_FRIEND_TEST(test_set_name, individual_test) |
||||||
|
|
||||||
|
// A macro to disallow new and delete operators for a class. It goes in the private: declarations.
|
||||||
|
// NOTE: Providing placement new (and matching delete) for constructing container elements.
|
||||||
|
#define DISALLOW_ALLOCATION() \ |
||||||
|
public: \
|
||||||
|
NO_RETURN ALWAYS_INLINE void operator delete(void*, size_t) { UNREACHABLE(); } \
|
||||||
|
ALWAYS_INLINE void* operator new(size_t, void* ptr) noexcept { return ptr; } \
|
||||||
|
ALWAYS_INLINE void operator delete(void*, void*) noexcept { } \
|
||||||
|
private: \
|
||||||
|
void* operator new(size_t) = delete // NOLINT
|
||||||
|
|
||||||
|
#define SIZEOF_MEMBER(t, f) sizeof((reinterpret_cast<t*>(4096))->f) // NOLINT
|
||||||
|
|
||||||
|
#define OFFSETOF_MEMBER(t, f) \ |
||||||
|
(reinterpret_cast<uintptr_t>(&reinterpret_cast<t*>(16)->f) - static_cast<uintptr_t>(16u)) // NOLINT
|
||||||
|
|
||||||
|
#define OFFSETOF_MEMBERPTR(t, f) \ |
||||||
|
(reinterpret_cast<uintptr_t>(&(reinterpret_cast<t*>(16)->*f)) - static_cast<uintptr_t>(16)) // NOLINT
|
||||||
|
|
||||||
|
#define PACKED(x) __attribute__ ((__aligned__(x), __packed__)) |
||||||
|
|
||||||
|
// Stringify the argument.
|
||||||
|
#define QUOTE(x) #x |
||||||
|
#define STRINGIFY(x) QUOTE(x) |
||||||
|
|
||||||
|
// Append tokens after evaluating.
|
||||||
|
#define APPEND_TOKENS_AFTER_EVAL_2(a, b) a ## b |
||||||
|
#define APPEND_TOKENS_AFTER_EVAL(a, b) APPEND_TOKENS_AFTER_EVAL_2(a, b) |
||||||
|
|
||||||
|
#ifndef NDEBUG |
||||||
|
#define ALWAYS_INLINE |
||||||
|
#else |
||||||
|
#define ALWAYS_INLINE __attribute__ ((always_inline)) |
||||||
|
#endif |
||||||
|
|
||||||
|
// clang doesn't like attributes on lambda functions. It would be nice to say:
|
||||||
|
// #define ALWAYS_INLINE_LAMBDA ALWAYS_INLINE
|
||||||
|
#define ALWAYS_INLINE_LAMBDA |
||||||
|
|
||||||
|
#define NO_INLINE __attribute__ ((noinline)) |
||||||
|
|
||||||
|
#if defined (__APPLE__) |
||||||
|
#define HOT_ATTR |
||||||
|
#define COLD_ATTR |
||||||
|
#else |
||||||
|
#define HOT_ATTR __attribute__ ((hot)) |
||||||
|
#define COLD_ATTR __attribute__ ((cold)) |
||||||
|
#endif |
||||||
|
|
||||||
|
#define PURE __attribute__ ((__pure__)) |
||||||
|
|
||||||
|
// Define that a position within code is unreachable, for example:
|
||||||
|
// int foo () { LOG(FATAL) << "Don't call me"; UNREACHABLE(); }
|
||||||
|
// without the UNREACHABLE a return statement would be necessary.
|
||||||
|
#define UNREACHABLE __builtin_unreachable |
||||||
|
|
||||||
|
// Add the C++11 noreturn attribute.
|
||||||
|
#define NO_RETURN [[ noreturn ]] // NOLINT[whitespace/braces] [5]
|
||||||
|
|
||||||
|
// Annotalysis thread-safety analysis support. Things that are not in base.
|
||||||
|
|
||||||
|
#define LOCKABLE CAPABILITY("mutex") |
||||||
|
#define SHARED_LOCKABLE SHARED_CAPABILITY("mutex") |
||||||
|
|
||||||
|
#endif // ART_LIBARTBASE_BASE_MACROS_H_
|
@ -0,0 +1,41 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2015 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 ANDROID_BASE_MEMORY_H |
||||||
|
#define ANDROID_BASE_MEMORY_H |
||||||
|
|
||||||
|
namespace android_lkchan { |
||||||
|
namespace base { |
||||||
|
|
||||||
|
// Use memcpy for access to unaligned data on targets with alignment
|
||||||
|
// restrictions. The compiler will generate appropriate code to access these
|
||||||
|
// structures without generating alignment exceptions.
|
||||||
|
template <typename T> |
||||||
|
static inline T get_unaligned(const void* address) { |
||||||
|
T result; |
||||||
|
memcpy(&result, address, sizeof(T)); |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
template <typename T> |
||||||
|
static inline void put_unaligned(void* address, T v) { |
||||||
|
memcpy(address, &v, sizeof(T)); |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace base
|
||||||
|
} // namespace android_lkchan
|
||||||
|
|
||||||
|
#endif // ANDROID_BASE_MEMORY_H
|
@ -0,0 +1,74 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2015 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_LIBARTBASE_BASE_MEMORY_TOOL_H_ |
||||||
|
#define ART_LIBARTBASE_BASE_MEMORY_TOOL_H_ |
||||||
|
|
||||||
|
#include <stddef.h> |
||||||
|
|
||||||
|
#if !defined(__has_feature) |
||||||
|
#define __has_feature(x) 0 |
||||||
|
#endif |
||||||
|
|
||||||
|
#if __has_feature(address_sanitizer) |
||||||
|
|
||||||
|
#include <sanitizer/asan_interface.h> |
||||||
|
#define ADDRESS_SANITIZER |
||||||
|
|
||||||
|
#ifdef ART_ENABLE_ADDRESS_SANITIZER |
||||||
|
#define MEMORY_TOOL_MAKE_NOACCESS(p, s) __asan_poison_memory_region(p, s) |
||||||
|
#define MEMORY_TOOL_MAKE_UNDEFINED(p, s) __asan_unpoison_memory_region(p, s) |
||||||
|
#define MEMORY_TOOL_MAKE_DEFINED(p, s) __asan_unpoison_memory_region(p, s) |
||||||
|
constexpr bool kMemoryToolIsAvailable = true; |
||||||
|
#else |
||||||
|
#define MEMORY_TOOL_MAKE_NOACCESS(p, s) do { (void)(p); (void)(s); } while (0) |
||||||
|
#define MEMORY_TOOL_MAKE_UNDEFINED(p, s) do { (void)(p); (void)(s); } while (0) |
||||||
|
#define MEMORY_TOOL_MAKE_DEFINED(p, s) do { (void)(p); (void)(s); } while (0) |
||||||
|
constexpr bool kMemoryToolIsAvailable = false; |
||||||
|
#endif |
||||||
|
|
||||||
|
extern "C" void __asan_handle_no_return(); |
||||||
|
|
||||||
|
#define ATTRIBUTE_NO_SANITIZE_ADDRESS __attribute__((no_sanitize_address)) |
||||||
|
#define MEMORY_TOOL_HANDLE_NO_RETURN __asan_handle_no_return() |
||||||
|
#define RUNNING_ON_MEMORY_TOOL 1U |
||||||
|
constexpr bool kMemoryToolIsValgrind = false; |
||||||
|
constexpr bool kMemoryToolDetectsLeaks = true; |
||||||
|
constexpr bool kMemoryToolAddsRedzones = true; |
||||||
|
constexpr size_t kMemoryToolStackGuardSizeScale = 2; |
||||||
|
|
||||||
|
#else |
||||||
|
|
||||||
|
/*
|
||||||
|
* chensenhua |
||||||
|
#include <memcheck/memcheck.h> |
||||||
|
#include <valgrind.h> |
||||||
|
#define MEMORY_TOOL_MAKE_NOACCESS(p, s) VALGRIND_MAKE_MEM_NOACCESS(p, s) |
||||||
|
#define MEMORY_TOOL_MAKE_UNDEFINED(p, s) VALGRIND_MAKE_MEM_UNDEFINED(p, s) |
||||||
|
#define MEMORY_TOOL_MAKE_DEFINED(p, s) VALGRIND_MAKE_MEM_DEFINED(p, s) |
||||||
|
#define ATTRIBUTE_NO_SANITIZE_ADDRESS |
||||||
|
#define MEMORY_TOOL_HANDLE_NO_RETURN do { } while (0) |
||||||
|
#define RUNNING_ON_MEMORY_TOOL RUNNING_ON_VALGRIND |
||||||
|
constexpr bool kMemoryToolIsAvailable = true; |
||||||
|
constexpr bool kMemoryToolIsValgrind = true; |
||||||
|
constexpr bool kMemoryToolDetectsLeaks = true; |
||||||
|
constexpr bool kMemoryToolAddsRedzones = true; |
||||||
|
constexpr size_t kMemoryToolStackGuardSizeScale = 1; |
||||||
|
*/ |
||||||
|
|
||||||
|
#endif |
||||||
|
|
||||||
|
#endif // ART_LIBARTBASE_BASE_MEMORY_TOOL_H_
|
@ -0,0 +1,180 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2012 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_LIBARTBASE_BASE_SAFE_MAP_H_ |
||||||
|
#define ART_LIBARTBASE_BASE_SAFE_MAP_H_ |
||||||
|
|
||||||
|
#include <map> |
||||||
|
#include <memory> |
||||||
|
#include <type_traits> |
||||||
|
|
||||||
|
#include <android-base/logging.h> |
||||||
|
|
||||||
|
namespace art_lkchan { |
||||||
|
|
||||||
|
// Equivalent to std::map, but without operator[] and its bug-prone semantics (in particular,
|
||||||
|
// the implicit insertion of a default-constructed value on failed lookups).
|
||||||
|
template <typename K, typename V, typename Comparator = std::less<K>, |
||||||
|
typename Allocator = std::allocator<std::pair<const K, V>>> |
||||||
|
class SafeMap { |
||||||
|
private: |
||||||
|
typedef SafeMap<K, V, Comparator, Allocator> Self; |
||||||
|
|
||||||
|
public: |
||||||
|
typedef typename ::std::map<K, V, Comparator, Allocator>::key_compare key_compare; |
||||||
|
typedef typename ::std::map<K, V, Comparator, Allocator>::value_compare value_compare; |
||||||
|
typedef typename ::std::map<K, V, Comparator, Allocator>::allocator_type allocator_type; |
||||||
|
typedef typename ::std::map<K, V, Comparator, Allocator>::iterator iterator; |
||||||
|
typedef typename ::std::map<K, V, Comparator, Allocator>::const_iterator const_iterator; |
||||||
|
typedef typename ::std::map<K, V, Comparator, Allocator>::size_type size_type; |
||||||
|
typedef typename ::std::map<K, V, Comparator, Allocator>::key_type key_type; |
||||||
|
typedef typename ::std::map<K, V, Comparator, Allocator>::value_type value_type; |
||||||
|
|
||||||
|
SafeMap() = default; |
||||||
|
SafeMap(const SafeMap&) = default; |
||||||
|
SafeMap(SafeMap&&) = default; |
||||||
|
explicit SafeMap(const key_compare& cmp, const allocator_type& allocator = allocator_type()) |
||||||
|
: map_(cmp, allocator) { |
||||||
|
} |
||||||
|
|
||||||
|
Self& operator=(const Self& rhs) { |
||||||
|
map_ = rhs.map_; |
||||||
|
return *this; |
||||||
|
} |
||||||
|
|
||||||
|
allocator_type get_allocator() const { return map_.get_allocator(); } |
||||||
|
key_compare key_comp() const { return map_.key_comp(); } |
||||||
|
value_compare value_comp() const { return map_.value_comp(); } |
||||||
|
|
||||||
|
iterator begin() { return map_.begin(); } |
||||||
|
const_iterator begin() const { return map_.begin(); } |
||||||
|
iterator end() { return map_.end(); } |
||||||
|
const_iterator end() const { return map_.end(); } |
||||||
|
|
||||||
|
bool empty() const { return map_.empty(); } |
||||||
|
size_type size() const { return map_.size(); } |
||||||
|
|
||||||
|
void swap(Self& other) { map_.swap(other.map_); } |
||||||
|
void clear() { map_.clear(); } |
||||||
|
iterator erase(iterator it) { return map_.erase(it); } |
||||||
|
size_type erase(const K& k) { return map_.erase(k); } |
||||||
|
|
||||||
|
iterator find(const K& k) { return map_.find(k); } |
||||||
|
const_iterator find(const K& k) const { return map_.find(k); } |
||||||
|
|
||||||
|
iterator lower_bound(const K& k) { return map_.lower_bound(k); } |
||||||
|
const_iterator lower_bound(const K& k) const { return map_.lower_bound(k); } |
||||||
|
|
||||||
|
iterator upper_bound(const K& k) { return map_.upper_bound(k); } |
||||||
|
const_iterator upper_bound(const K& k) const { return map_.upper_bound(k); } |
||||||
|
|
||||||
|
size_type count(const K& k) const { return map_.count(k); } |
||||||
|
|
||||||
|
// Note that unlike std::map's operator[], this doesn't return a reference to the value.
|
||||||
|
V Get(const K& k) const { |
||||||
|
const_iterator it = map_.find(k); |
||||||
|
DCHECK(it != map_.end()); |
||||||
|
return it->second; |
||||||
|
} |
||||||
|
|
||||||
|
// Used to insert a new mapping.
|
||||||
|
iterator Put(const K& k, const V& v) { |
||||||
|
std::pair<iterator, bool> result = map_.emplace(k, v); |
||||||
|
DCHECK(result.second); // Check we didn't accidentally overwrite an existing value.
|
||||||
|
return result.first; |
||||||
|
} |
||||||
|
iterator Put(const K& k, V&& v) { |
||||||
|
std::pair<iterator, bool> result = map_.emplace(k, std::move(v)); |
||||||
|
DCHECK(result.second); // Check we didn't accidentally overwrite an existing value.
|
||||||
|
return result.first; |
||||||
|
} |
||||||
|
|
||||||
|
// Used to insert a new mapping at a known position for better performance.
|
||||||
|
iterator PutBefore(const_iterator pos, const K& k, const V& v) { |
||||||
|
// Check that we're using the correct position and the key is not in the map.
|
||||||
|
DCHECK(pos == map_.end() || map_.key_comp()(k, pos->first)); |
||||||
|
DCHECK(pos == map_.begin() || map_.key_comp()((--const_iterator(pos))->first, k)); |
||||||
|
return map_.emplace_hint(pos, k, v); |
||||||
|
} |
||||||
|
iterator PutBefore(const_iterator pos, const K& k, V&& v) { |
||||||
|
// Check that we're using the correct position and the key is not in the map.
|
||||||
|
DCHECK(pos == map_.end() || map_.key_comp()(k, pos->first)); |
||||||
|
DCHECK(pos == map_.begin() || map_.key_comp()((--const_iterator(pos))->first, k)); |
||||||
|
return map_.emplace_hint(pos, k, std::move(v)); |
||||||
|
} |
||||||
|
|
||||||
|
// Used to insert a new mapping or overwrite an existing mapping. Note that if the value type
|
||||||
|
// of this container is a pointer, any overwritten pointer will be lost and if this container
|
||||||
|
// was the owner, you have a leak. Returns iterator pointing to the new or overwritten entry.
|
||||||
|
iterator Overwrite(const K& k, const V& v) { |
||||||
|
std::pair<iterator, bool> result = map_.insert(std::make_pair(k, v)); |
||||||
|
if (!result.second) { |
||||||
|
// Already there - update the value for the existing key
|
||||||
|
result.first->second = v; |
||||||
|
} |
||||||
|
return result.first; |
||||||
|
} |
||||||
|
|
||||||
|
template <typename CreateFn> |
||||||
|
V GetOrCreate(const K& k, CreateFn create) { |
||||||
|
static_assert(std::is_same<V, typename std::result_of<CreateFn()>::type>::value, |
||||||
|
"Argument `create` should return a value of type V."); |
||||||
|
auto lb = lower_bound(k); |
||||||
|
if (lb != end() && !key_comp()(k, lb->first)) { |
||||||
|
return lb->second; |
||||||
|
} |
||||||
|
auto it = PutBefore(lb, k, create()); |
||||||
|
return it->second; |
||||||
|
} |
||||||
|
|
||||||
|
iterator FindOrAdd(const K& k, const V& v) { |
||||||
|
iterator it = find(k); |
||||||
|
return it == end() ? Put(k, v) : it; |
||||||
|
} |
||||||
|
|
||||||
|
iterator FindOrAdd(const K& k) { |
||||||
|
iterator it = find(k); |
||||||
|
return it == end() ? Put(k, V()) : it; |
||||||
|
} |
||||||
|
|
||||||
|
bool Equals(const Self& rhs) const { |
||||||
|
return map_ == rhs.map_; |
||||||
|
} |
||||||
|
|
||||||
|
template <class... Args> |
||||||
|
std::pair<iterator, bool> emplace(Args&&... args) { |
||||||
|
return map_.emplace(std::forward<Args>(args)...); |
||||||
|
} |
||||||
|
|
||||||
|
private: |
||||||
|
::std::map<K, V, Comparator, Allocator> map_; |
||||||
|
}; |
||||||
|
|
||||||
|
template <typename K, typename V, typename Comparator, typename Allocator> |
||||||
|
bool operator==(const SafeMap<K, V, Comparator, Allocator>& lhs, |
||||||
|
const SafeMap<K, V, Comparator, Allocator>& rhs) { |
||||||
|
return lhs.Equals(rhs); |
||||||
|
} |
||||||
|
|
||||||
|
template <typename K, typename V, typename Comparator, typename Allocator> |
||||||
|
bool operator!=(const SafeMap<K, V, Comparator, Allocator>& lhs, |
||||||
|
const SafeMap<K, V, Comparator, Allocator>& rhs) { |
||||||
|
return !(lhs == rhs); |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace art_lkchan
|
||||||
|
|
||||||
|
#endif // ART_LIBARTBASE_BASE_SAFE_MAP_H_
|
@ -0,0 +1,158 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011 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_LIBARTBASE_BASE_STL_UTIL_H_ |
||||||
|
#define ART_LIBARTBASE_BASE_STL_UTIL_H_ |
||||||
|
|
||||||
|
#include <algorithm> |
||||||
|
#include <set> |
||||||
|
#include <sstream> |
||||||
|
|
||||||
|
#include <android-base/logging.h> |
||||||
|
|
||||||
|
namespace art_lkchan { |
||||||
|
|
||||||
|
// STLDeleteContainerPointers()
|
||||||
|
// For a range within a container of pointers, calls delete
|
||||||
|
// (non-array version) on these pointers.
|
||||||
|
// NOTE: for these three functions, we could just implement a DeleteObject
|
||||||
|
// functor and then call for_each() on the range and functor, but this
|
||||||
|
// requires us to pull in all of algorithm.h, which seems expensive.
|
||||||
|
// For hash_[multi]set, it is important that this deletes behind the iterator
|
||||||
|
// because the hash_set may call the hash function on the iterator when it is
|
||||||
|
// advanced, which could result in the hash function trying to deference a
|
||||||
|
// stale pointer.
|
||||||
|
template <class ForwardIterator> |
||||||
|
void STLDeleteContainerPointers(ForwardIterator begin, |
||||||
|
ForwardIterator end) { |
||||||
|
while (begin != end) { |
||||||
|
ForwardIterator temp = begin; |
||||||
|
++begin; |
||||||
|
delete *temp; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// STLDeleteElements() deletes all the elements in an STL container and clears
|
||||||
|
// the container. This function is suitable for use with a vector, set,
|
||||||
|
// hash_set, or any other STL container which defines sensible begin(), end(),
|
||||||
|
// and clear() methods.
|
||||||
|
//
|
||||||
|
// If container is null, this function is a no-op.
|
||||||
|
//
|
||||||
|
// As an alternative to calling STLDeleteElements() directly, consider
|
||||||
|
// using a container of std::unique_ptr, which ensures that your container's
|
||||||
|
// elements are deleted when the container goes out of scope.
|
||||||
|
template <class T> |
||||||
|
void STLDeleteElements(T *container) { |
||||||
|
if (container != nullptr) { |
||||||
|
STLDeleteContainerPointers(container->begin(), container->end()); |
||||||
|
container->clear(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Given an STL container consisting of (key, value) pairs, STLDeleteValues
|
||||||
|
// deletes all the "value" components and clears the container. Does nothing
|
||||||
|
// in the case it's given a null pointer.
|
||||||
|
template <class T> |
||||||
|
void STLDeleteValues(T *v) { |
||||||
|
if (v != nullptr) { |
||||||
|
for (typename T::iterator i = v->begin(); i != v->end(); ++i) { |
||||||
|
delete i->second; |
||||||
|
} |
||||||
|
v->clear(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Deleter using free() for use with std::unique_ptr<>. See also UniqueCPtr<> below.
|
||||||
|
struct FreeDelete { |
||||||
|
// NOTE: Deleting a const object is valid but free() takes a non-const pointer.
|
||||||
|
void operator()(const void* ptr) const { |
||||||
|
free(const_cast<void*>(ptr)); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
// Alias for std::unique_ptr<> that uses the C function free() to delete objects.
|
||||||
|
template <typename T> |
||||||
|
using UniqueCPtr = std::unique_ptr<T, FreeDelete>; |
||||||
|
|
||||||
|
// Find index of the first element with the specified value known to be in the container.
|
||||||
|
template <typename Container, typename T> |
||||||
|
size_t IndexOfElement(const Container& container, const T& value) { |
||||||
|
auto it = std::find(container.begin(), container.end(), value); |
||||||
|
DCHECK(it != container.end()); // Must exist.
|
||||||
|
return std::distance(container.begin(), it); |
||||||
|
} |
||||||
|
|
||||||
|
// Remove the first element with the specified value known to be in the container.
|
||||||
|
template <typename Container, typename T> |
||||||
|
void RemoveElement(Container& container, const T& value) { |
||||||
|
auto it = std::find(container.begin(), container.end(), value); |
||||||
|
DCHECK(it != container.end()); // Must exist.
|
||||||
|
container.erase(it); |
||||||
|
} |
||||||
|
|
||||||
|
// Replace the first element with the specified old_value known to be in the container.
|
||||||
|
template <typename Container, typename T> |
||||||
|
void ReplaceElement(Container& container, const T& old_value, const T& new_value) { |
||||||
|
auto it = std::find(container.begin(), container.end(), old_value); |
||||||
|
DCHECK(it != container.end()); // Must exist.
|
||||||
|
*it = new_value; |
||||||
|
} |
||||||
|
|
||||||
|
// Search for an element with the specified value and return true if it was found, false otherwise.
|
||||||
|
template <typename Container, typename T> |
||||||
|
bool ContainsElement(const Container& container, const T& value, size_t start_pos = 0u) { |
||||||
|
DCHECK_LE(start_pos, container.size()); |
||||||
|
auto start = container.begin(); |
||||||
|
std::advance(start, start_pos); |
||||||
|
auto it = std::find(start, container.end(), value); |
||||||
|
return it != container.end(); |
||||||
|
} |
||||||
|
|
||||||
|
// 32-bit FNV-1a hash function suitable for std::unordered_map.
|
||||||
|
// It can be used with any container which works with range-based for loop.
|
||||||
|
// See http://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function
|
||||||
|
template <typename Vector> |
||||||
|
struct FNVHash { |
||||||
|
size_t operator()(const Vector& vector) const { |
||||||
|
uint32_t hash = 2166136261u; |
||||||
|
for (const auto& value : vector) { |
||||||
|
hash = (hash ^ value) * 16777619u; |
||||||
|
} |
||||||
|
return hash; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
// Merge `other` entries into `to_update`.
|
||||||
|
template <typename T> |
||||||
|
static inline void MergeSets(std::set<T>& to_update, const std::set<T>& other) { |
||||||
|
to_update.insert(other.begin(), other.end()); |
||||||
|
} |
||||||
|
|
||||||
|
// Returns a copy of the passed vector that doesn't memory-own its entries.
|
||||||
|
template <typename T> |
||||||
|
static inline std::vector<T*> MakeNonOwningPointerVector(const std::vector<std::unique_ptr<T>>& src) { |
||||||
|
std::vector<T*> result; |
||||||
|
result.reserve(src.size()); |
||||||
|
for (const std::unique_ptr<T>& t : src) { |
||||||
|
result.push_back(t.get()); |
||||||
|
} |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace art_lkchan
|
||||||
|
|
||||||
|
#endif // ART_LIBARTBASE_BASE_STL_UTIL_H_
|
@ -0,0 +1,41 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011 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_LIBARTBASE_BASE_STL_UTIL_IDENTITY_H_ |
||||||
|
#define ART_LIBARTBASE_BASE_STL_UTIL_IDENTITY_H_ |
||||||
|
|
||||||
|
namespace art_lkchan { |
||||||
|
|
||||||
|
// Use to suppress type deduction for a function argument.
|
||||||
|
// See std::identity<> for more background:
|
||||||
|
// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1856.html#20.2.2 - move/forward helpers
|
||||||
|
//
|
||||||
|
// e.g. "template <typename X> void bar(identity<X>::type foo);
|
||||||
|
// bar(5); // compilation error
|
||||||
|
// bar<int>(5); // ok
|
||||||
|
// or "template <typename T> void foo(T* x, typename Identity<T*>::type y);
|
||||||
|
// Base b;
|
||||||
|
// Derived d;
|
||||||
|
// foo(&b, &d); // Use implicit Derived* -> Base* conversion.
|
||||||
|
// If T was deduced from both &b and &d, there would be a mismatch, i.e. deduction failure.
|
||||||
|
template <typename T> |
||||||
|
struct Identity { |
||||||
|
using type = T; |
||||||
|
}; |
||||||
|
|
||||||
|
} // namespace art_lkchan
|
||||||
|
|
||||||
|
#endif // ART_LIBARTBASE_BASE_STL_UTIL_IDENTITY_H_
|
@ -0,0 +1,289 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2010 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_LIBARTBASE_BASE_STRINGPIECE_H_ |
||||||
|
#define ART_LIBARTBASE_BASE_STRINGPIECE_H_ |
||||||
|
|
||||||
|
#include <string.h> |
||||||
|
#include <string> |
||||||
|
|
||||||
|
#include <android-base/logging.h> |
||||||
|
|
||||||
|
namespace art_lkchan { |
||||||
|
|
||||||
|
// A string-like object that points to a sized piece of memory.
|
||||||
|
//
|
||||||
|
// Functions or methods may use const StringPiece& parameters to accept either
|
||||||
|
// a "const char*" or a "string" value that will be implicitly converted to
|
||||||
|
// a StringPiece. The implicit conversion means that it is often appropriate
|
||||||
|
// to include this .h file in other files rather than forward-declaring
|
||||||
|
// StringPiece as would be appropriate for most other Google classes.
|
||||||
|
class StringPiece { |
||||||
|
public: |
||||||
|
// standard STL container boilerplate
|
||||||
|
typedef char value_type; |
||||||
|
typedef const char* pointer; |
||||||
|
typedef const char& reference; |
||||||
|
typedef const char& const_reference; |
||||||
|
typedef size_t size_type; |
||||||
|
typedef ptrdiff_t difference_type; |
||||||
|
static constexpr size_type npos = size_type(-1); |
||||||
|
typedef const char* const_iterator; |
||||||
|
typedef const char* iterator; |
||||||
|
typedef std::reverse_iterator<const_iterator> const_reverse_iterator; |
||||||
|
typedef std::reverse_iterator<iterator> reverse_iterator; |
||||||
|
|
||||||
|
// We provide non-explicit singleton constructors so users can pass
|
||||||
|
// in a "const char*" or a "string" wherever a "StringPiece" is
|
||||||
|
// expected.
|
||||||
|
StringPiece() : ptr_(nullptr), length_(0) { } |
||||||
|
StringPiece(const char* str) // NOLINT implicit constructor desired
|
||||||
|
: ptr_(str), length_((str == nullptr) ? 0 : strlen(str)) { } |
||||||
|
StringPiece(const std::string& str) // NOLINT implicit constructor desired
|
||||||
|
: ptr_(str.data()), length_(str.size()) { } |
||||||
|
StringPiece(const char* offset, size_t len) : ptr_(offset), length_(len) { } |
||||||
|
|
||||||
|
// data() may return a pointer to a buffer with embedded NULs, and the
|
||||||
|
// returned buffer may or may not be null terminated. Therefore it is
|
||||||
|
// typically a mistake to pass data() to a routine that expects a NUL
|
||||||
|
// terminated string.
|
||||||
|
const char* data() const { return ptr_; } |
||||||
|
size_type size() const { return length_; } |
||||||
|
size_type length() const { return length_; } |
||||||
|
bool empty() const { return length_ == 0; } |
||||||
|
|
||||||
|
void clear() { |
||||||
|
ptr_ = nullptr; |
||||||
|
length_ = 0; |
||||||
|
} |
||||||
|
void set(const char* data_in, size_type len) { |
||||||
|
ptr_ = data_in; |
||||||
|
length_ = len; |
||||||
|
} |
||||||
|
void set(const char* str) { |
||||||
|
ptr_ = str; |
||||||
|
if (str != nullptr) { |
||||||
|
length_ = strlen(str); |
||||||
|
} else { |
||||||
|
length_ = 0; |
||||||
|
} |
||||||
|
} |
||||||
|
void set(const void* data_in, size_type len) { |
||||||
|
ptr_ = reinterpret_cast<const char*>(data_in); |
||||||
|
length_ = len; |
||||||
|
} |
||||||
|
|
||||||
|
char operator[](size_type i) const { |
||||||
|
DCHECK_LT(i, length_); |
||||||
|
return ptr_[i]; |
||||||
|
} |
||||||
|
|
||||||
|
void remove_prefix(size_type n) { |
||||||
|
ptr_ += n; |
||||||
|
length_ -= n; |
||||||
|
} |
||||||
|
|
||||||
|
void remove_suffix(size_type n) { |
||||||
|
length_ -= n; |
||||||
|
} |
||||||
|
|
||||||
|
int compare(const StringPiece& x) const { |
||||||
|
int r = memcmp(ptr_, x.ptr_, std::min(length_, x.length_)); |
||||||
|
if (r == 0) { |
||||||
|
if (length_ < x.length_) r = -1; |
||||||
|
else if (length_ > x.length_) r = +1; |
||||||
|
} |
||||||
|
return r; |
||||||
|
} |
||||||
|
|
||||||
|
std::string as_string() const { |
||||||
|
return std::string(data(), size()); |
||||||
|
} |
||||||
|
// We also define ToString() here, since many other string-like
|
||||||
|
// interfaces name the routine that converts to a C++ string
|
||||||
|
// "ToString", and it's confusing to have the method that does that
|
||||||
|
// for a StringPiece be called "as_string()". We also leave the
|
||||||
|
// "as_string()" method defined here for existing code.
|
||||||
|
std::string ToString() const { |
||||||
|
return std::string(data(), size()); |
||||||
|
} |
||||||
|
|
||||||
|
void CopyToString(std::string* target) const { |
||||||
|
target->assign(ptr_, length_); |
||||||
|
} |
||||||
|
|
||||||
|
void AppendToString(std::string* target) const; |
||||||
|
|
||||||
|
// Does "this" start with "x"
|
||||||
|
bool starts_with(const StringPiece& x) const { |
||||||
|
return ((length_ >= x.length_) && |
||||||
|
(memcmp(ptr_, x.ptr_, x.length_) == 0)); |
||||||
|
} |
||||||
|
|
||||||
|
// Does "this" end with "x"
|
||||||
|
bool ends_with(const StringPiece& x) const { |
||||||
|
return ((length_ >= x.length_) && |
||||||
|
(memcmp(ptr_ + (length_-x.length_), x.ptr_, x.length_) == 0)); |
||||||
|
} |
||||||
|
|
||||||
|
iterator begin() const { return ptr_; } |
||||||
|
iterator end() const { return ptr_ + length_; } |
||||||
|
const_reverse_iterator rbegin() const { |
||||||
|
return const_reverse_iterator(ptr_ + length_); |
||||||
|
} |
||||||
|
const_reverse_iterator rend() const { |
||||||
|
return const_reverse_iterator(ptr_); |
||||||
|
} |
||||||
|
|
||||||
|
size_type copy(char* buf, size_type n, size_type pos = 0) const { |
||||||
|
size_type ret = std::min(length_ - pos, n); |
||||||
|
memcpy(buf, ptr_ + pos, ret); |
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
size_type find(const StringPiece& s, size_type pos = 0) const { |
||||||
|
if (length_ == 0 || pos > static_cast<size_type>(length_)) { |
||||||
|
return npos; |
||||||
|
} |
||||||
|
const char* result = std::search(ptr_ + pos, ptr_ + length_, s.ptr_, s.ptr_ + s.length_); |
||||||
|
const size_type xpos = result - ptr_; |
||||||
|
return xpos + s.length_ <= length_ ? xpos : npos; |
||||||
|
} |
||||||
|
|
||||||
|
size_type find(char c, size_type pos = 0) const { |
||||||
|
if (length_ == 0 || pos >= length_) { |
||||||
|
return npos; |
||||||
|
} |
||||||
|
const char* result = std::find(ptr_ + pos, ptr_ + length_, c); |
||||||
|
return result != ptr_ + length_ ? result - ptr_ : npos; |
||||||
|
} |
||||||
|
|
||||||
|
size_type rfind(const StringPiece& s, size_type pos = npos) const { |
||||||
|
if (length_ < s.length_) return npos; |
||||||
|
const size_t ulen = length_; |
||||||
|
if (s.length_ == 0) return std::min(ulen, pos); |
||||||
|
|
||||||
|
const char* last = ptr_ + std::min(ulen - s.length_, pos) + s.length_; |
||||||
|
const char* result = std::find_end(ptr_, last, s.ptr_, s.ptr_ + s.length_); |
||||||
|
return result != last ? result - ptr_ : npos; |
||||||
|
} |
||||||
|
|
||||||
|
size_type rfind(char c, size_type pos = npos) const { |
||||||
|
if (length_ == 0) return npos; |
||||||
|
for (int i = std::min(pos, static_cast<size_type>(length_ - 1)); |
||||||
|
i >= 0; --i) { |
||||||
|
if (ptr_[i] == c) { |
||||||
|
return i; |
||||||
|
} |
||||||
|
} |
||||||
|
return npos; |
||||||
|
} |
||||||
|
|
||||||
|
StringPiece substr(size_type pos, size_type n = npos) const { |
||||||
|
if (pos > static_cast<size_type>(length_)) pos = length_; |
||||||
|
if (n > length_ - pos) n = length_ - pos; |
||||||
|
return StringPiece(ptr_ + pos, n); |
||||||
|
} |
||||||
|
|
||||||
|
int Compare(const StringPiece& rhs) const { |
||||||
|
const int r = memcmp(data(), rhs.data(), std::min(size(), rhs.size())); |
||||||
|
if (r != 0) { |
||||||
|
return r; |
||||||
|
} |
||||||
|
if (size() < rhs.size()) { |
||||||
|
return -1; |
||||||
|
} else if (size() > rhs.size()) { |
||||||
|
return 1; |
||||||
|
} |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
private: |
||||||
|
// Pointer to char data, not necessarily zero terminated.
|
||||||
|
const char* ptr_; |
||||||
|
// Length of data.
|
||||||
|
size_type length_; |
||||||
|
}; |
||||||
|
|
||||||
|
// This large function is defined inline so that in a fairly common case where
|
||||||
|
// one of the arguments is a literal, the compiler can elide a lot of the
|
||||||
|
// following comparisons.
|
||||||
|
inline bool operator==(const StringPiece& x, const StringPiece& y) { |
||||||
|
StringPiece::size_type len = x.size(); |
||||||
|
if (len != y.size()) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
const char* p1 = x.data(); |
||||||
|
const char* p2 = y.data(); |
||||||
|
if (p1 == p2) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
if (len == 0) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
// Test last byte in case strings share large common prefix
|
||||||
|
if (p1[len-1] != p2[len-1]) return false; |
||||||
|
if (len == 1) return true; |
||||||
|
|
||||||
|
// At this point we can, but don't have to, ignore the last byte. We use
|
||||||
|
// this observation to fold the odd-length case into the even-length case.
|
||||||
|
len &= ~1; |
||||||
|
|
||||||
|
return memcmp(p1, p2, len) == 0; |
||||||
|
} |
||||||
|
|
||||||
|
inline bool operator==(const StringPiece& x, const char* y) { |
||||||
|
if (y == nullptr) { |
||||||
|
return x.size() == 0; |
||||||
|
} else { |
||||||
|
return strncmp(x.data(), y, x.size()) == 0 && y[x.size()] == '\0'; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
inline bool operator!=(const StringPiece& x, const StringPiece& y) { |
||||||
|
return !(x == y); |
||||||
|
} |
||||||
|
|
||||||
|
inline bool operator!=(const StringPiece& x, const char* y) { |
||||||
|
return !(x == y); |
||||||
|
} |
||||||
|
|
||||||
|
inline bool operator<(const StringPiece& x, const StringPiece& y) { |
||||||
|
return x.Compare(y) < 0; |
||||||
|
} |
||||||
|
|
||||||
|
inline bool operator>(const StringPiece& x, const StringPiece& y) { |
||||||
|
return y < x; |
||||||
|
} |
||||||
|
|
||||||
|
inline bool operator<=(const StringPiece& x, const StringPiece& y) { |
||||||
|
return !(x > y); |
||||||
|
} |
||||||
|
|
||||||
|
inline bool operator>=(const StringPiece& x, const StringPiece& y) { |
||||||
|
return !(x < y); |
||||||
|
} |
||||||
|
|
||||||
|
inline std::ostream& operator<<(std::ostream& o, const StringPiece& piece) { |
||||||
|
o.write(piece.data(), piece.size()); |
||||||
|
return o; |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace art_lkchan
|
||||||
|
|
||||||
|
#endif // ART_LIBARTBASE_BASE_STRINGPIECE_H_
|
@ -0,0 +1,31 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2014 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_LIBARTBASE_BASE_VALUE_OBJECT_H_ |
||||||
|
#define ART_LIBARTBASE_BASE_VALUE_OBJECT_H_ |
||||||
|
|
||||||
|
#include "base/macros.h" |
||||||
|
|
||||||
|
namespace art_lkchan { |
||||||
|
|
||||||
|
class ValueObject { |
||||||
|
private: |
||||||
|
DISALLOW_ALLOCATION(); |
||||||
|
}; |
||||||
|
|
||||||
|
} // namespace art_lkchan
|
||||||
|
|
||||||
|
#endif // ART_LIBARTBASE_BASE_VALUE_OBJECT_H_
|
@ -0,0 +1,100 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011 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_BASE64_TEST_UTIL_H_ |
||||||
|
#define ART_LIBDEXFILE_DEX_BASE64_TEST_UTIL_H_ |
||||||
|
|
||||||
|
#include <stdint.h> |
||||||
|
#include <stdlib.h> |
||||||
|
|
||||||
|
#include <memory> |
||||||
|
#include <vector> |
||||||
|
|
||||||
|
#include <android-base/logging.h> |
||||||
|
|
||||||
|
namespace art_lkchan { |
||||||
|
|
||||||
|
static inline uint8_t* DecodeBase64(const char* src, size_t* dst_size) { |
||||||
|
static const uint8_t kBase64Map[256] = { |
||||||
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, |
||||||
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, |
||||||
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, |
||||||
|
255, 255, 255, 255, 255, 255, 255, 62, 255, 255, 255, 63, |
||||||
|
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, |
||||||
|
255, 254, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, |
||||||
|
7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, |
||||||
|
19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 255, |
||||||
|
255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, |
||||||
|
37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, |
||||||
|
49, 50, 51, 255, 255, 255, 255, 255, 255, 255, 255, 255, |
||||||
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, |
||||||
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, |
||||||
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, |
||||||
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, |
||||||
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, |
||||||
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, |
||||||
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, |
||||||
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, |
||||||
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, |
||||||
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, |
||||||
|
255, 255, 255, 255 |
||||||
|
}; |
||||||
|
|
||||||
|
CHECK(dst_size != nullptr); |
||||||
|
std::vector<uint8_t> tmp; |
||||||
|
uint32_t t = 0, y = 0; |
||||||
|
int g = 3; |
||||||
|
for (size_t i = 0; src[i] != '\0'; ++i) { |
||||||
|
uint8_t c = kBase64Map[src[i] & 0xFF]; |
||||||
|
if (c == 255) continue; |
||||||
|
// the final = symbols are read and used to trim the remaining bytes
|
||||||
|
if (c == 254) { |
||||||
|
c = 0; |
||||||
|
// prevent g < 0 which would potentially allow an overflow later
|
||||||
|
if (--g < 0) { |
||||||
|
*dst_size = 0; |
||||||
|
return nullptr; |
||||||
|
} |
||||||
|
} else if (g != 3) { |
||||||
|
// we only allow = to be at the end
|
||||||
|
*dst_size = 0; |
||||||
|
return nullptr; |
||||||
|
} |
||||||
|
t = (t << 6) | c; |
||||||
|
if (++y == 4) { |
||||||
|
tmp.push_back((t >> 16) & 255); |
||||||
|
if (g > 1) { |
||||||
|
tmp.push_back((t >> 8) & 255); |
||||||
|
} |
||||||
|
if (g > 2) { |
||||||
|
tmp.push_back(t & 255); |
||||||
|
} |
||||||
|
y = t = 0; |
||||||
|
} |
||||||
|
} |
||||||
|
if (y != 0) { |
||||||
|
*dst_size = 0; |
||||||
|
return nullptr; |
||||||
|
} |
||||||
|
std::unique_ptr<uint8_t[]> dst(new uint8_t[tmp.size()]); |
||||||
|
*dst_size = tmp.size(); |
||||||
|
std::copy(tmp.begin(), tmp.end(), dst.get()); |
||||||
|
return dst.release(); |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace art_lkchan
|
||||||
|
|
||||||
|
#endif // ART_LIBDEXFILE_DEX_BASE64_TEST_UTIL_H_
|
@ -0,0 +1,147 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 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_BYTECODE_UTILS_H_ |
||||||
|
#define ART_LIBDEXFILE_DEX_BYTECODE_UTILS_H_ |
||||||
|
|
||||||
|
#include "base/value_object.h" |
||||||
|
#include "dex/dex_file-inl.h" |
||||||
|
#include "dex/dex_file.h" |
||||||
|
#include "dex/dex_instruction-inl.h" |
||||||
|
|
||||||
|
namespace art_lkchan { |
||||||
|
|
||||||
|
class DexSwitchTable : public ValueObject { |
||||||
|
public: |
||||||
|
DexSwitchTable(const Instruction& instruction, uint32_t dex_pc) |
||||||
|
: instruction_(instruction), |
||||||
|
dex_pc_(dex_pc), |
||||||
|
sparse_(instruction.Opcode() == Instruction::SPARSE_SWITCH) { |
||||||
|
int32_t table_offset = instruction.VRegB_31t(); |
||||||
|
const uint16_t* table = reinterpret_cast<const uint16_t*>(&instruction) + table_offset; |
||||||
|
DCHECK_EQ(table[0], sparse_ ? static_cast<uint16_t>(Instruction::kSparseSwitchSignature) |
||||||
|
: static_cast<uint16_t>(Instruction::kPackedSwitchSignature)); |
||||||
|
num_entries_ = table[1]; |
||||||
|
values_ = reinterpret_cast<const int32_t*>(&table[2]); |
||||||
|
} |
||||||
|
|
||||||
|
uint16_t GetNumEntries() const { |
||||||
|
return num_entries_; |
||||||
|
} |
||||||
|
|
||||||
|
void CheckIndex(size_t index) const { |
||||||
|
if (sparse_) { |
||||||
|
// In a sparse table, we have num_entries_ keys and num_entries_ values, in that order.
|
||||||
|
DCHECK_LT(index, 2 * static_cast<size_t>(num_entries_)); |
||||||
|
} else { |
||||||
|
// In a packed table, we have the starting key and num_entries_ values.
|
||||||
|
DCHECK_LT(index, 1 + static_cast<size_t>(num_entries_)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
int32_t GetEntryAt(size_t index) const { |
||||||
|
CheckIndex(index); |
||||||
|
return values_[index]; |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t GetDexPcForIndex(size_t index) const { |
||||||
|
CheckIndex(index); |
||||||
|
return dex_pc_ + |
||||||
|
(reinterpret_cast<const int16_t*>(values_ + index) - |
||||||
|
reinterpret_cast<const int16_t*>(&instruction_)); |
||||||
|
} |
||||||
|
|
||||||
|
// Index of the first value in the table.
|
||||||
|
size_t GetFirstValueIndex() const { |
||||||
|
if (sparse_) { |
||||||
|
// In a sparse table, we have num_entries_ keys and num_entries_ values, in that order.
|
||||||
|
return num_entries_; |
||||||
|
} else { |
||||||
|
// In a packed table, we have the starting key and num_entries_ values.
|
||||||
|
return 1; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
bool IsSparse() const { return sparse_; } |
||||||
|
|
||||||
|
bool ShouldBuildDecisionTree() { |
||||||
|
return IsSparse() || GetNumEntries() <= kSmallSwitchThreshold; |
||||||
|
} |
||||||
|
|
||||||
|
private: |
||||||
|
const Instruction& instruction_; |
||||||
|
const uint32_t dex_pc_; |
||||||
|
|
||||||
|
// Whether this is a sparse-switch table (or a packed-switch one).
|
||||||
|
const bool sparse_; |
||||||
|
|
||||||
|
// This can't be const as it needs to be computed off of the given instruction, and complicated
|
||||||
|
// expressions in the initializer list seemed very ugly.
|
||||||
|
uint16_t num_entries_; |
||||||
|
|
||||||
|
const int32_t* values_; |
||||||
|
|
||||||
|
// The number of entries in a packed switch before we use a jump table or specified
|
||||||
|
// compare/jump series.
|
||||||
|
static constexpr uint16_t kSmallSwitchThreshold = 3; |
||||||
|
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(DexSwitchTable); |
||||||
|
}; |
||||||
|
|
||||||
|
class DexSwitchTableIterator { |
||||||
|
public: |
||||||
|
explicit DexSwitchTableIterator(const DexSwitchTable& table) |
||||||
|
: table_(table), |
||||||
|
num_entries_(static_cast<size_t>(table_.GetNumEntries())), |
||||||
|
first_target_offset_(table_.GetFirstValueIndex()), |
||||||
|
index_(0u) {} |
||||||
|
|
||||||
|
bool Done() const { return index_ >= num_entries_; } |
||||||
|
bool IsLast() const { return index_ == num_entries_ - 1; } |
||||||
|
|
||||||
|
void Advance() { |
||||||
|
DCHECK(!Done()); |
||||||
|
index_++; |
||||||
|
} |
||||||
|
|
||||||
|
int32_t CurrentKey() const { |
||||||
|
return table_.IsSparse() ? table_.GetEntryAt(index_) : table_.GetEntryAt(0) + index_; |
||||||
|
} |
||||||
|
|
||||||
|
int32_t CurrentTargetOffset() const { |
||||||
|
return table_.GetEntryAt(index_ + first_target_offset_); |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t GetDexPcForCurrentIndex() const { return table_.GetDexPcForIndex(index_); } |
||||||
|
|
||||||
|
private: |
||||||
|
const DexSwitchTable& table_; |
||||||
|
const size_t num_entries_; |
||||||
|
const size_t first_target_offset_; |
||||||
|
|
||||||
|
size_t index_; |
||||||
|
}; |
||||||
|
|
||||||
|
inline bool IsThrowingDexInstruction(const Instruction& instruction) { |
||||||
|
// Special-case MONITOR_EXIT which is a throwing instruction but the verifier
|
||||||
|
// guarantees that it will never throw. This is necessary to avoid rejecting
|
||||||
|
// 'synchronized' blocks/methods.
|
||||||
|
return instruction.IsThrow() && instruction.Opcode() != Instruction::MONITOR_EXIT; |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace art_lkchan
|
||||||
|
|
||||||
|
#endif // ART_LIBDEXFILE_DEX_BYTECODE_UTILS_H_
|
@ -0,0 +1,42 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2013 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_CLASS_REFERENCE_H_ |
||||||
|
#define ART_LIBDEXFILE_DEX_CLASS_REFERENCE_H_ |
||||||
|
|
||||||
|
#include <stdint.h> |
||||||
|
#include <utility> |
||||||
|
|
||||||
|
#include "dex/dex_file_reference.h" |
||||||
|
|
||||||
|
namespace art_lkchan { |
||||||
|
|
||||||
|
class DexFile; |
||||||
|
|
||||||
|
// A class is uniquely located by its DexFile and the class_defs_ table index into that DexFile
|
||||||
|
class ClassReference : public DexFileReference { |
||||||
|
public: |
||||||
|
ClassReference(const DexFile* file, uint32_t class_def_idx) |
||||||
|
: DexFileReference(file, class_def_idx) {} |
||||||
|
|
||||||
|
uint32_t ClassDefIdx() const { |
||||||
|
return index; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
} // namespace art_lkchan
|
||||||
|
|
||||||
|
#endif // ART_LIBDEXFILE_DEX_CLASS_REFERENCE_H_
|
@ -0,0 +1,204 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2017 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_CODE_ITEM_ACCESSORS_INL_H_ |
||||||
|
#define ART_LIBDEXFILE_DEX_CODE_ITEM_ACCESSORS_INL_H_ |
||||||
|
|
||||||
|
#include "code_item_accessors.h" |
||||||
|
|
||||||
|
#include "compact_dex_file.h" |
||||||
|
#include "dex_file-inl.h" |
||||||
|
#include "standard_dex_file.h" |
||||||
|
|
||||||
|
// The no ART version is used by binaries that don't include the whole runtime.
|
||||||
|
namespace art_lkchan { |
||||||
|
|
||||||
|
inline void CodeItemInstructionAccessor::Init(uint32_t insns_size_in_code_units, |
||||||
|
const uint16_t* insns) { |
||||||
|
insns_size_in_code_units_ = insns_size_in_code_units; |
||||||
|
insns_ = insns; |
||||||
|
} |
||||||
|
|
||||||
|
inline void CodeItemInstructionAccessor::Init(const CompactDexFile::CodeItem& code_item) { |
||||||
|
uint32_t insns_size_in_code_units; |
||||||
|
code_item.DecodeFields</*kDecodeOnlyInstructionCount*/ true>( |
||||||
|
&insns_size_in_code_units, |
||||||
|
/*registers_size*/ nullptr, |
||||||
|
/*ins_size*/ nullptr, |
||||||
|
/*outs_size*/ nullptr, |
||||||
|
/*tries_size*/ nullptr); |
||||||
|
Init(insns_size_in_code_units, code_item.insns_); |
||||||
|
} |
||||||
|
|
||||||
|
inline void CodeItemInstructionAccessor::Init(const StandardDexFile::CodeItem& code_item) { |
||||||
|
Init(code_item.insns_size_in_code_units_, code_item.insns_); |
||||||
|
} |
||||||
|
|
||||||
|
inline void CodeItemInstructionAccessor::Init(const DexFile& dex_file, |
||||||
|
const DexFile::CodeItem* code_item) { |
||||||
|
if (code_item != nullptr) { |
||||||
|
DCHECK(dex_file.IsInDataSection(code_item)); |
||||||
|
if (dex_file.IsCompactDexFile()) { |
||||||
|
Init(down_cast<const CompactDexFile::CodeItem&>(*code_item)); |
||||||
|
} else { |
||||||
|
DCHECK(dex_file.IsStandardDexFile()); |
||||||
|
Init(down_cast<const StandardDexFile::CodeItem&>(*code_item)); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
inline CodeItemInstructionAccessor::CodeItemInstructionAccessor( |
||||||
|
const DexFile& dex_file, |
||||||
|
const DexFile::CodeItem* code_item) { |
||||||
|
Init(dex_file, code_item); |
||||||
|
} |
||||||
|
|
||||||
|
inline DexInstructionIterator CodeItemInstructionAccessor::begin() const { |
||||||
|
return DexInstructionIterator(insns_, 0u); |
||||||
|
} |
||||||
|
|
||||||
|
inline DexInstructionIterator CodeItemInstructionAccessor::end() const { |
||||||
|
return DexInstructionIterator(insns_, insns_size_in_code_units_); |
||||||
|
} |
||||||
|
|
||||||
|
inline IterationRange<DexInstructionIterator> CodeItemInstructionAccessor::InstructionsFrom( |
||||||
|
uint32_t start_dex_pc) const { |
||||||
|
DCHECK_LT(start_dex_pc, InsnsSizeInCodeUnits()); |
||||||
|
return { |
||||||
|
DexInstructionIterator(insns_, start_dex_pc), |
||||||
|
DexInstructionIterator(insns_, insns_size_in_code_units_) }; |
||||||
|
} |
||||||
|
|
||||||
|
inline void CodeItemDataAccessor::Init(const CompactDexFile::CodeItem& code_item) { |
||||||
|
uint32_t insns_size_in_code_units; |
||||||
|
code_item.DecodeFields</*kDecodeOnlyInstructionCount*/ false>(&insns_size_in_code_units, |
||||||
|
®isters_size_, |
||||||
|
&ins_size_, |
||||||
|
&outs_size_, |
||||||
|
&tries_size_); |
||||||
|
CodeItemInstructionAccessor::Init(insns_size_in_code_units, code_item.insns_); |
||||||
|
} |
||||||
|
|
||||||
|
inline void CodeItemDataAccessor::Init(const StandardDexFile::CodeItem& code_item) { |
||||||
|
CodeItemInstructionAccessor::Init(code_item); |
||||||
|
registers_size_ = code_item.registers_size_; |
||||||
|
ins_size_ = code_item.ins_size_; |
||||||
|
outs_size_ = code_item.outs_size_; |
||||||
|
tries_size_ = code_item.tries_size_; |
||||||
|
} |
||||||
|
|
||||||
|
inline void CodeItemDataAccessor::Init(const DexFile& dex_file, |
||||||
|
const DexFile::CodeItem* code_item) { |
||||||
|
if (code_item != nullptr) { |
||||||
|
if (dex_file.IsCompactDexFile()) { |
||||||
|
CodeItemDataAccessor::Init(down_cast<const CompactDexFile::CodeItem&>(*code_item)); |
||||||
|
} else { |
||||||
|
DCHECK(dex_file.IsStandardDexFile()); |
||||||
|
CodeItemDataAccessor::Init(down_cast<const StandardDexFile::CodeItem&>(*code_item)); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
inline CodeItemDataAccessor::CodeItemDataAccessor(const DexFile& dex_file, |
||||||
|
const DexFile::CodeItem* code_item) { |
||||||
|
Init(dex_file, code_item); |
||||||
|
} |
||||||
|
|
||||||
|
inline IterationRange<const DexFile::TryItem*> CodeItemDataAccessor::TryItems() const { |
||||||
|
const DexFile::TryItem* try_items = DexFile::GetTryItems(end(), 0u); |
||||||
|
return { |
||||||
|
try_items, |
||||||
|
try_items + TriesSize() }; |
||||||
|
} |
||||||
|
|
||||||
|
inline const uint8_t* CodeItemDataAccessor::GetCatchHandlerData(size_t offset) const { |
||||||
|
return DexFile::GetCatchHandlerData(end(), TriesSize(), offset); |
||||||
|
} |
||||||
|
|
||||||
|
inline const DexFile::TryItem* CodeItemDataAccessor::FindTryItem(uint32_t try_dex_pc) const { |
||||||
|
IterationRange<const DexFile::TryItem*> try_items(TryItems()); |
||||||
|
int32_t index = DexFile::FindTryItem(try_items.begin(), |
||||||
|
try_items.end() - try_items.begin(), |
||||||
|
try_dex_pc); |
||||||
|
return index != -1 ? &try_items.begin()[index] : nullptr; |
||||||
|
} |
||||||
|
|
||||||
|
inline const void* CodeItemDataAccessor::CodeItemDataEnd() const { |
||||||
|
const uint8_t* handler_data = GetCatchHandlerData(); |
||||||
|
|
||||||
|
if (TriesSize() == 0 || handler_data == nullptr) { |
||||||
|
return &end().Inst(); |
||||||
|
} |
||||||
|
// Get the start of the handler data.
|
||||||
|
const uint32_t handlers_size = DecodeUnsignedLeb128(&handler_data); |
||||||
|
// Manually read each handler.
|
||||||
|
for (uint32_t i = 0; i < handlers_size; ++i) { |
||||||
|
int32_t uleb128_count = DecodeSignedLeb128(&handler_data) * 2; |
||||||
|
if (uleb128_count <= 0) { |
||||||
|
uleb128_count = -uleb128_count + 1; |
||||||
|
} |
||||||
|
for (int32_t j = 0; j < uleb128_count; ++j) { |
||||||
|
DecodeUnsignedLeb128(&handler_data); |
||||||
|
} |
||||||
|
} |
||||||
|
return reinterpret_cast<const void*>(handler_data); |
||||||
|
} |
||||||
|
|
||||||
|
inline void CodeItemDebugInfoAccessor::Init(const DexFile& dex_file, |
||||||
|
const DexFile::CodeItem* code_item, |
||||||
|
uint32_t dex_method_index) { |
||||||
|
if (code_item == nullptr) { |
||||||
|
return; |
||||||
|
} |
||||||
|
dex_file_ = &dex_file; |
||||||
|
if (dex_file.IsCompactDexFile()) { |
||||||
|
Init(down_cast<const CompactDexFile::CodeItem&>(*code_item), dex_method_index); |
||||||
|
} else { |
||||||
|
DCHECK(dex_file.IsStandardDexFile()); |
||||||
|
Init(down_cast<const StandardDexFile::CodeItem&>(*code_item)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
inline void CodeItemDebugInfoAccessor::Init(const CompactDexFile::CodeItem& code_item, |
||||||
|
uint32_t dex_method_index) { |
||||||
|
debug_info_offset_ = down_cast<const CompactDexFile*>(dex_file_)->GetDebugInfoOffset( |
||||||
|
dex_method_index); |
||||||
|
CodeItemDataAccessor::Init(code_item); |
||||||
|
} |
||||||
|
|
||||||
|
inline void CodeItemDebugInfoAccessor::Init(const StandardDexFile::CodeItem& code_item) { |
||||||
|
debug_info_offset_ = code_item.debug_info_off_; |
||||||
|
CodeItemDataAccessor::Init(code_item); |
||||||
|
} |
||||||
|
|
||||||
|
template<typename NewLocalCallback> |
||||||
|
inline bool CodeItemDebugInfoAccessor::DecodeDebugLocalInfo(bool is_static, |
||||||
|
uint32_t method_idx, |
||||||
|
NewLocalCallback new_local, |
||||||
|
void* context) const { |
||||||
|
return dex_file_->DecodeDebugLocalInfo(RegistersSize(), |
||||||
|
InsSize(), |
||||||
|
InsnsSizeInCodeUnits(), |
||||||
|
DebugInfoOffset(), |
||||||
|
is_static, |
||||||
|
method_idx, |
||||||
|
new_local, |
||||||
|
context); |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace art_lkchan
|
||||||
|
|
||||||
|
#endif // ART_LIBDEXFILE_DEX_CODE_ITEM_ACCESSORS_INL_H_
|
@ -0,0 +1,166 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2017 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. |
||||||
|
*/ |
||||||
|
|
||||||
|
// TODO: Dex helpers have ART specific APIs, we may want to refactor these for use in dexdump.
|
||||||
|
|
||||||
|
#ifndef ART_LIBDEXFILE_DEX_CODE_ITEM_ACCESSORS_H_ |
||||||
|
#define ART_LIBDEXFILE_DEX_CODE_ITEM_ACCESSORS_H_ |
||||||
|
|
||||||
|
#include "compact_dex_file.h" |
||||||
|
#include "dex_file.h" |
||||||
|
#include "dex_instruction_iterator.h" |
||||||
|
#include "standard_dex_file.h" |
||||||
|
|
||||||
|
namespace art_lkchan { |
||||||
|
|
||||||
|
class ArtMethod; |
||||||
|
|
||||||
|
// Abstracts accesses to the instruction fields of code items for CompactDexFile and
|
||||||
|
// StandardDexFile.
|
||||||
|
class CodeItemInstructionAccessor { |
||||||
|
public: |
||||||
|
ALWAYS_INLINE CodeItemInstructionAccessor(const DexFile& dex_file, |
||||||
|
const DexFile::CodeItem* code_item); |
||||||
|
|
||||||
|
ALWAYS_INLINE explicit CodeItemInstructionAccessor(ArtMethod* method); |
||||||
|
|
||||||
|
ALWAYS_INLINE DexInstructionIterator begin() const; |
||||||
|
|
||||||
|
ALWAYS_INLINE DexInstructionIterator end() const; |
||||||
|
|
||||||
|
IterationRange<DexInstructionIterator> InstructionsFrom(uint32_t start_dex_pc) const; |
||||||
|
|
||||||
|
uint32_t InsnsSizeInCodeUnits() const { |
||||||
|
return insns_size_in_code_units_; |
||||||
|
} |
||||||
|
|
||||||
|
const uint16_t* Insns() const { |
||||||
|
return insns_; |
||||||
|
} |
||||||
|
|
||||||
|
// Return the instruction for a dex pc.
|
||||||
|
const Instruction& InstructionAt(uint32_t dex_pc) const { |
||||||
|
DCHECK_LT(dex_pc, InsnsSizeInCodeUnits()); |
||||||
|
return *Instruction::At(insns_ + dex_pc); |
||||||
|
} |
||||||
|
|
||||||
|
// Return true if the accessor has a code item.
|
||||||
|
bool HasCodeItem() const { |
||||||
|
return Insns() != nullptr; |
||||||
|
} |
||||||
|
|
||||||
|
protected: |
||||||
|
CodeItemInstructionAccessor() = default; |
||||||
|
|
||||||
|
ALWAYS_INLINE void Init(uint32_t insns_size_in_code_units, const uint16_t* insns); |
||||||
|
ALWAYS_INLINE void Init(const CompactDexFile::CodeItem& code_item); |
||||||
|
ALWAYS_INLINE void Init(const StandardDexFile::CodeItem& code_item); |
||||||
|
ALWAYS_INLINE void Init(const DexFile& dex_file, const DexFile::CodeItem* code_item); |
||||||
|
|
||||||
|
private: |
||||||
|
// size of the insns array, in 2 byte code units. 0 if there is no code item.
|
||||||
|
uint32_t insns_size_in_code_units_ = 0; |
||||||
|
|
||||||
|
// Pointer to the instructions, null if there is no code item.
|
||||||
|
const uint16_t* insns_ = 0; |
||||||
|
}; |
||||||
|
|
||||||
|
// Abstracts accesses to code item fields other than debug info for CompactDexFile and
|
||||||
|
// StandardDexFile.
|
||||||
|
class CodeItemDataAccessor : public CodeItemInstructionAccessor { |
||||||
|
public: |
||||||
|
ALWAYS_INLINE CodeItemDataAccessor(const DexFile& dex_file, const DexFile::CodeItem* code_item); |
||||||
|
|
||||||
|
uint16_t RegistersSize() const { |
||||||
|
return registers_size_; |
||||||
|
} |
||||||
|
|
||||||
|
uint16_t InsSize() const { |
||||||
|
return ins_size_; |
||||||
|
} |
||||||
|
|
||||||
|
uint16_t OutsSize() const { |
||||||
|
return outs_size_; |
||||||
|
} |
||||||
|
|
||||||
|
uint16_t TriesSize() const { |
||||||
|
return tries_size_; |
||||||
|
} |
||||||
|
|
||||||
|
IterationRange<const DexFile::TryItem*> TryItems() const; |
||||||
|
|
||||||
|
const uint8_t* GetCatchHandlerData(size_t offset = 0) const; |
||||||
|
|
||||||
|
const DexFile::TryItem* FindTryItem(uint32_t try_dex_pc) const; |
||||||
|
|
||||||
|
inline const void* CodeItemDataEnd() const; |
||||||
|
|
||||||
|
protected: |
||||||
|
CodeItemDataAccessor() = default; |
||||||
|
|
||||||
|
ALWAYS_INLINE void Init(const CompactDexFile::CodeItem& code_item); |
||||||
|
ALWAYS_INLINE void Init(const StandardDexFile::CodeItem& code_item); |
||||||
|
ALWAYS_INLINE void Init(const DexFile& dex_file, const DexFile::CodeItem* code_item); |
||||||
|
|
||||||
|
private: |
||||||
|
// Fields mirrored from the dex/cdex code item.
|
||||||
|
uint16_t registers_size_; |
||||||
|
uint16_t ins_size_; |
||||||
|
uint16_t outs_size_; |
||||||
|
uint16_t tries_size_; |
||||||
|
}; |
||||||
|
|
||||||
|
// Abstract accesses to code item data including debug info offset. More heavy weight than the other
|
||||||
|
// helpers.
|
||||||
|
class CodeItemDebugInfoAccessor : public CodeItemDataAccessor { |
||||||
|
public: |
||||||
|
CodeItemDebugInfoAccessor() = default; |
||||||
|
|
||||||
|
// Initialize with an existing offset.
|
||||||
|
ALWAYS_INLINE CodeItemDebugInfoAccessor(const DexFile& dex_file, |
||||||
|
const DexFile::CodeItem* code_item, |
||||||
|
uint32_t dex_method_index) { |
||||||
|
Init(dex_file, code_item, dex_method_index); |
||||||
|
} |
||||||
|
|
||||||
|
ALWAYS_INLINE void Init(const DexFile& dex_file, |
||||||
|
const DexFile::CodeItem* code_item, |
||||||
|
uint32_t dex_method_index); |
||||||
|
|
||||||
|
ALWAYS_INLINE explicit CodeItemDebugInfoAccessor(ArtMethod* method); |
||||||
|
|
||||||
|
uint32_t DebugInfoOffset() const { |
||||||
|
return debug_info_offset_; |
||||||
|
} |
||||||
|
|
||||||
|
template<typename NewLocalCallback> |
||||||
|
bool DecodeDebugLocalInfo(bool is_static, |
||||||
|
uint32_t method_idx, |
||||||
|
NewLocalCallback new_local, |
||||||
|
void* context) const; |
||||||
|
|
||||||
|
protected: |
||||||
|
ALWAYS_INLINE void Init(const CompactDexFile::CodeItem& code_item, uint32_t dex_method_index); |
||||||
|
ALWAYS_INLINE void Init(const StandardDexFile::CodeItem& code_item); |
||||||
|
|
||||||
|
private: |
||||||
|
const DexFile* dex_file_ = nullptr; |
||||||
|
uint32_t debug_info_offset_ = 0u; |
||||||
|
}; |
||||||
|
|
||||||
|
} // namespace art_lkchan
|
||||||
|
|
||||||
|
#endif // ART_LIBDEXFILE_DEX_CODE_ITEM_ACCESSORS_H_
|
@ -0,0 +1,108 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2017 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. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include "compact_dex_file.h" |
||||||
|
|
||||||
|
#include "base/leb128.h" |
||||||
|
#include "code_item_accessors-inl.h" |
||||||
|
#include "dex_file-inl.h" |
||||||
|
|
||||||
|
namespace art_lkchan { |
||||||
|
|
||||||
|
constexpr uint8_t CompactDexFile::kDexMagic[kDexMagicSize]; |
||||||
|
constexpr uint8_t CompactDexFile::kDexMagicVersion[]; |
||||||
|
|
||||||
|
void CompactDexFile::WriteMagic(uint8_t* magic) { |
||||||
|
std::copy_n(kDexMagic, kDexMagicSize, magic); |
||||||
|
} |
||||||
|
|
||||||
|
void CompactDexFile::WriteCurrentVersion(uint8_t* magic) { |
||||||
|
std::copy_n(kDexMagicVersion, kDexVersionLen, magic + kDexMagicSize); |
||||||
|
} |
||||||
|
|
||||||
|
bool CompactDexFile::IsMagicValid(const uint8_t* magic) { |
||||||
|
return (memcmp(magic, kDexMagic, sizeof(kDexMagic)) == 0); |
||||||
|
} |
||||||
|
|
||||||
|
bool CompactDexFile::IsVersionValid(const uint8_t* magic) { |
||||||
|
const uint8_t* version = &magic[sizeof(kDexMagic)]; |
||||||
|
return memcmp(version, kDexMagicVersion, kDexVersionLen) == 0; |
||||||
|
} |
||||||
|
|
||||||
|
bool CompactDexFile::IsMagicValid() const { |
||||||
|
return IsMagicValid(header_->magic_); |
||||||
|
} |
||||||
|
|
||||||
|
bool CompactDexFile::IsVersionValid() const { |
||||||
|
return IsVersionValid(header_->magic_); |
||||||
|
} |
||||||
|
|
||||||
|
bool CompactDexFile::SupportsDefaultMethods() const { |
||||||
|
return (GetHeader().GetFeatureFlags() & |
||||||
|
static_cast<uint32_t>(FeatureFlags::kDefaultMethods)) != 0; |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t CompactDexFile::GetCodeItemSize(const DexFile::CodeItem& item) const { |
||||||
|
DCHECK(IsInDataSection(&item)); |
||||||
|
return reinterpret_cast<uintptr_t>(CodeItemDataAccessor(*this, &item).CodeItemDataEnd()) - |
||||||
|
reinterpret_cast<uintptr_t>(&item); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
uint32_t CompactDexFile::CalculateChecksum(const uint8_t* base_begin, |
||||||
|
size_t base_size, |
||||||
|
const uint8_t* data_begin, |
||||||
|
size_t data_size) { |
||||||
|
Header temp_header(*Header::At(base_begin)); |
||||||
|
// Zero out fields that are not included in the sum.
|
||||||
|
temp_header.checksum_ = 0u; |
||||||
|
temp_header.data_off_ = 0u; |
||||||
|
temp_header.data_size_ = 0u; |
||||||
|
uint32_t checksum = ChecksumMemoryRange(reinterpret_cast<const uint8_t*>(&temp_header), |
||||||
|
sizeof(temp_header)); |
||||||
|
// Exclude the header since we already computed it's checksum.
|
||||||
|
checksum = (checksum * 31) ^ ChecksumMemoryRange(base_begin + sizeof(temp_header), |
||||||
|
base_size - sizeof(temp_header)); |
||||||
|
checksum = (checksum * 31) ^ ChecksumMemoryRange(data_begin, data_size); |
||||||
|
return checksum; |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t CompactDexFile::CalculateChecksum() const { |
||||||
|
return CalculateChecksum(Begin(), Size(), DataBegin(), DataSize()); |
||||||
|
} |
||||||
|
|
||||||
|
CompactDexFile::CompactDexFile(const uint8_t* base, |
||||||
|
size_t size, |
||||||
|
const uint8_t* data_begin, |
||||||
|
size_t data_size, |
||||||
|
const std::string& location, |
||||||
|
uint32_t location_checksum, |
||||||
|
const OatDexFile* oat_dex_file, |
||||||
|
std::unique_ptr<DexFileContainer> container) |
||||||
|
: DexFile(base, |
||||||
|
size, |
||||||
|
data_begin, |
||||||
|
data_size, |
||||||
|
location, |
||||||
|
location_checksum, |
||||||
|
oat_dex_file, |
||||||
|
std::move(container), |
||||||
|
/*is_compact_dex*/ true), |
||||||
|
debug_info_offsets_(DataBegin() + GetHeader().debug_info_offsets_pos_, |
||||||
|
GetHeader().debug_info_base_, |
||||||
|
GetHeader().debug_info_offsets_table_offset_) {} |
||||||
|
|
||||||
|
} // namespace art_lkchan
|
@ -0,0 +1,305 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2017 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_COMPACT_DEX_FILE_H_ |
||||||
|
#define ART_LIBDEXFILE_DEX_COMPACT_DEX_FILE_H_ |
||||||
|
|
||||||
|
#include "base/casts.h" |
||||||
|
#include "dex_file.h" |
||||||
|
#include "dex/compact_offset_table.h" |
||||||
|
|
||||||
|
namespace art_lkchan { |
||||||
|
|
||||||
|
// CompactDex is a currently ART internal dex file format that aims to reduce storage/RAM usage.
|
||||||
|
class CompactDexFile : public DexFile { |
||||||
|
public: |
||||||
|
static constexpr uint8_t kDexMagic[kDexMagicSize] = { 'c', 'd', 'e', 'x' }; |
||||||
|
static constexpr uint8_t kDexMagicVersion[] = {'0', '0', '1', '\0'}; |
||||||
|
|
||||||
|
enum class FeatureFlags : uint32_t { |
||||||
|
kDefaultMethods = 0x1, |
||||||
|
}; |
||||||
|
|
||||||
|
class Header : public DexFile::Header { |
||||||
|
public: |
||||||
|
static const Header* At(const void* at) { |
||||||
|
return reinterpret_cast<const Header*>(at); |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t GetFeatureFlags() const { |
||||||
|
return feature_flags_; |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t GetDataOffset() const { |
||||||
|
return data_off_; |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t GetDataSize() const { |
||||||
|
return data_size_; |
||||||
|
} |
||||||
|
|
||||||
|
// Range of the shared data section owned by the dex file. Owned in this context refers to data
|
||||||
|
// for this DEX that was not deduplicated to another DEX.
|
||||||
|
uint32_t OwnedDataBegin() const { |
||||||
|
return owned_data_begin_; |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t OwnedDataEnd() const { |
||||||
|
return owned_data_end_; |
||||||
|
} |
||||||
|
|
||||||
|
private: |
||||||
|
uint32_t feature_flags_ = 0u; |
||||||
|
|
||||||
|
// Position in the compact dex file for the debug info table data starts.
|
||||||
|
uint32_t debug_info_offsets_pos_ = 0u; |
||||||
|
|
||||||
|
// Offset into the debug info table data where the lookup table is.
|
||||||
|
uint32_t debug_info_offsets_table_offset_ = 0u; |
||||||
|
|
||||||
|
// Base offset of where debug info starts in the dex file.
|
||||||
|
uint32_t debug_info_base_ = 0u; |
||||||
|
|
||||||
|
// Range of the shared data section owned by the dex file.
|
||||||
|
uint32_t owned_data_begin_ = 0u; |
||||||
|
uint32_t owned_data_end_ = 0u; |
||||||
|
|
||||||
|
friend class CompactDexFile; |
||||||
|
friend class CompactDexWriter; |
||||||
|
}; |
||||||
|
|
||||||
|
// Like the standard code item except without a debug info offset. Each code item may have a
|
||||||
|
// preheader to encode large methods. In 99% of cases, the preheader is not used. This enables
|
||||||
|
// smaller size with a good fast path case in the accessors.
|
||||||
|
struct CodeItem : public DexFile::CodeItem { |
||||||
|
static constexpr size_t kAlignment = sizeof(uint16_t); |
||||||
|
// Max preheader size in uint16_ts.
|
||||||
|
static constexpr size_t kMaxPreHeaderSize = 6; |
||||||
|
|
||||||
|
private: |
||||||
|
CodeItem() = default; |
||||||
|
|
||||||
|
static constexpr size_t kRegistersSizeShift = 12; |
||||||
|
static constexpr size_t kInsSizeShift = 8; |
||||||
|
static constexpr size_t kOutsSizeShift = 4; |
||||||
|
static constexpr size_t kTriesSizeSizeShift = 0; |
||||||
|
static constexpr uint16_t kFlagPreHeaderRegisterSize = 0x1 << 0; |
||||||
|
static constexpr uint16_t kFlagPreHeaderInsSize = 0x1 << 1; |
||||||
|
static constexpr uint16_t kFlagPreHeaderOutsSize = 0x1 << 2; |
||||||
|
static constexpr uint16_t kFlagPreHeaderTriesSize = 0x1 << 3; |
||||||
|
static constexpr uint16_t kFlagPreHeaderInsnsSize = 0x1 << 4; |
||||||
|
static constexpr size_t kInsnsSizeShift = 5; |
||||||
|
static constexpr size_t kInsnsSizeBits = sizeof(uint16_t) * kBitsPerByte - kInsnsSizeShift; |
||||||
|
|
||||||
|
// Combined preheader flags for fast testing if we need to go slow path.
|
||||||
|
static constexpr uint16_t kFlagPreHeaderCombined = |
||||||
|
kFlagPreHeaderRegisterSize | |
||||||
|
kFlagPreHeaderInsSize | |
||||||
|
kFlagPreHeaderOutsSize | |
||||||
|
kFlagPreHeaderTriesSize | |
||||||
|
kFlagPreHeaderInsnsSize; |
||||||
|
|
||||||
|
// Create a code item and associated preheader if required based on field values.
|
||||||
|
// Returns the start of the preheader. The preheader buffer must be at least as large as
|
||||||
|
// kMaxPreHeaderSize;
|
||||||
|
uint16_t* Create(uint16_t registers_size, |
||||||
|
uint16_t ins_size, |
||||||
|
uint16_t outs_size, |
||||||
|
uint16_t tries_size, |
||||||
|
uint32_t insns_size_in_code_units, |
||||||
|
uint16_t* out_preheader) { |
||||||
|
// Dex verification ensures that registers size > ins_size, so we can subtract the registers
|
||||||
|
// size accordingly to reduce how often we need to use the preheader.
|
||||||
|
DCHECK_GE(registers_size, ins_size); |
||||||
|
registers_size -= ins_size; |
||||||
|
fields_ = (registers_size & 0xF) << kRegistersSizeShift; |
||||||
|
fields_ |= (ins_size & 0xF) << kInsSizeShift; |
||||||
|
fields_ |= (outs_size & 0xF) << kOutsSizeShift; |
||||||
|
fields_ |= (tries_size & 0xF) << kTriesSizeSizeShift; |
||||||
|
registers_size &= ~0xF; |
||||||
|
ins_size &= ~0xF; |
||||||
|
outs_size &= ~0xF; |
||||||
|
tries_size &= ~0xF; |
||||||
|
insns_count_and_flags_ = 0; |
||||||
|
const size_t masked_count = insns_size_in_code_units & ((1 << kInsnsSizeBits) - 1); |
||||||
|
insns_count_and_flags_ |= masked_count << kInsnsSizeShift; |
||||||
|
insns_size_in_code_units -= masked_count; |
||||||
|
|
||||||
|
// Since the preheader case is rare (1% of code items), use a suboptimally large but fast
|
||||||
|
// decoding format.
|
||||||
|
if (insns_size_in_code_units != 0) { |
||||||
|
insns_count_and_flags_ |= kFlagPreHeaderInsnsSize; |
||||||
|
--out_preheader; |
||||||
|
*out_preheader = static_cast<uint16_t>(insns_size_in_code_units); |
||||||
|
--out_preheader; |
||||||
|
*out_preheader = static_cast<uint16_t>(insns_size_in_code_units >> 16); |
||||||
|
} |
||||||
|
auto preheader_encode = [&](uint16_t size, uint16_t flag) { |
||||||
|
if (size != 0) { |
||||||
|
insns_count_and_flags_ |= flag; |
||||||
|
--out_preheader; |
||||||
|
*out_preheader = size; |
||||||
|
} |
||||||
|
}; |
||||||
|
preheader_encode(registers_size, kFlagPreHeaderRegisterSize); |
||||||
|
preheader_encode(ins_size, kFlagPreHeaderInsSize); |
||||||
|
preheader_encode(outs_size, kFlagPreHeaderOutsSize); |
||||||
|
preheader_encode(tries_size, kFlagPreHeaderTriesSize); |
||||||
|
return out_preheader; |
||||||
|
} |
||||||
|
|
||||||
|
ALWAYS_INLINE bool HasPreHeader(uint16_t flag) const { |
||||||
|
return (insns_count_and_flags_ & flag) != 0; |
||||||
|
} |
||||||
|
|
||||||
|
// Return true if the code item has any preheaders.
|
||||||
|
ALWAYS_INLINE static bool HasAnyPreHeader(uint16_t insns_count_and_flags) { |
||||||
|
return (insns_count_and_flags & kFlagPreHeaderCombined) != 0; |
||||||
|
} |
||||||
|
|
||||||
|
ALWAYS_INLINE uint16_t* GetPreHeader() { |
||||||
|
return reinterpret_cast<uint16_t*>(this); |
||||||
|
} |
||||||
|
|
||||||
|
ALWAYS_INLINE const uint16_t* GetPreHeader() const { |
||||||
|
return reinterpret_cast<const uint16_t*>(this); |
||||||
|
} |
||||||
|
|
||||||
|
// Decode fields and read the preheader if necessary. If kDecodeOnlyInstructionCount is
|
||||||
|
// specified then only the instruction count is decoded.
|
||||||
|
template <bool kDecodeOnlyInstructionCount> |
||||||
|
ALWAYS_INLINE void DecodeFields(uint32_t* insns_count, |
||||||
|
uint16_t* registers_size, |
||||||
|
uint16_t* ins_size, |
||||||
|
uint16_t* outs_size, |
||||||
|
uint16_t* tries_size) const { |
||||||
|
*insns_count = insns_count_and_flags_ >> kInsnsSizeShift; |
||||||
|
if (!kDecodeOnlyInstructionCount) { |
||||||
|
const uint16_t fields = fields_; |
||||||
|
*registers_size = (fields >> kRegistersSizeShift) & 0xF; |
||||||
|
*ins_size = (fields >> kInsSizeShift) & 0xF; |
||||||
|
*outs_size = (fields >> kOutsSizeShift) & 0xF; |
||||||
|
*tries_size = (fields >> kTriesSizeSizeShift) & 0xF; |
||||||
|
} |
||||||
|
if (UNLIKELY(HasAnyPreHeader(insns_count_and_flags_))) { |
||||||
|
const uint16_t* preheader = GetPreHeader(); |
||||||
|
if (HasPreHeader(kFlagPreHeaderInsnsSize)) { |
||||||
|
--preheader; |
||||||
|
*insns_count += static_cast<uint32_t>(*preheader); |
||||||
|
--preheader; |
||||||
|
*insns_count += static_cast<uint32_t>(*preheader) << 16; |
||||||
|
} |
||||||
|
if (!kDecodeOnlyInstructionCount) { |
||||||
|
if (HasPreHeader(kFlagPreHeaderRegisterSize)) { |
||||||
|
--preheader; |
||||||
|
*registers_size += preheader[0]; |
||||||
|
} |
||||||
|
if (HasPreHeader(kFlagPreHeaderInsSize)) { |
||||||
|
--preheader; |
||||||
|
*ins_size += preheader[0]; |
||||||
|
} |
||||||
|
if (HasPreHeader(kFlagPreHeaderOutsSize)) { |
||||||
|
--preheader; |
||||||
|
*outs_size += preheader[0]; |
||||||
|
} |
||||||
|
if (HasPreHeader(kFlagPreHeaderTriesSize)) { |
||||||
|
--preheader; |
||||||
|
*tries_size += preheader[0]; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
if (!kDecodeOnlyInstructionCount) { |
||||||
|
*registers_size += *ins_size; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Packed code item data, 4 bits each: [registers_size, ins_size, outs_size, tries_size]
|
||||||
|
uint16_t fields_; |
||||||
|
|
||||||
|
// 5 bits for if either of the fields required preheader extension, 11 bits for the number of
|
||||||
|
// instruction code units.
|
||||||
|
uint16_t insns_count_and_flags_; |
||||||
|
|
||||||
|
uint16_t insns_[1]; // actual array of bytecode.
|
||||||
|
|
||||||
|
ART_FRIEND_TEST(CodeItemAccessorsTest, TestDexInstructionsAccessor); |
||||||
|
ART_FRIEND_TEST(CompactDexFileTest, CodeItemFields); |
||||||
|
friend class CodeItemDataAccessor; |
||||||
|
friend class CodeItemDebugInfoAccessor; |
||||||
|
friend class CodeItemInstructionAccessor; |
||||||
|
friend class CompactDexFile; |
||||||
|
friend class CompactDexWriter; |
||||||
|
DISALLOW_COPY_AND_ASSIGN(CodeItem); |
||||||
|
}; |
||||||
|
|
||||||
|
// Write the compact dex specific magic.
|
||||||
|
static void WriteMagic(uint8_t* magic); |
||||||
|
|
||||||
|
// Write the current version, note that the input is the address of the magic.
|
||||||
|
static void WriteCurrentVersion(uint8_t* magic); |
||||||
|
|
||||||
|
// Returns true if the byte string points to the magic value.
|
||||||
|
static bool IsMagicValid(const uint8_t* magic); |
||||||
|
virtual bool IsMagicValid() const OVERRIDE; |
||||||
|
|
||||||
|
// Returns true if the byte string after the magic is the correct value.
|
||||||
|
static bool IsVersionValid(const uint8_t* magic); |
||||||
|
virtual bool IsVersionValid() const OVERRIDE; |
||||||
|
|
||||||
|
// TODO This is completely a guess. We really need to do better. b/72402467
|
||||||
|
// We ask for 64 megabytes which should be big enough for any realistic dex file.
|
||||||
|
virtual size_t GetDequickenedSize() const OVERRIDE { |
||||||
|
return 64 * MB; |
||||||
|
} |
||||||
|
|
||||||
|
const Header& GetHeader() const { |
||||||
|
return down_cast<const Header&>(DexFile::GetHeader()); |
||||||
|
} |
||||||
|
|
||||||
|
virtual bool SupportsDefaultMethods() const OVERRIDE; |
||||||
|
|
||||||
|
uint32_t GetCodeItemSize(const DexFile::CodeItem& item) const OVERRIDE; |
||||||
|
|
||||||
|
uint32_t GetDebugInfoOffset(uint32_t dex_method_index) const { |
||||||
|
return debug_info_offsets_.GetOffset(dex_method_index); |
||||||
|
} |
||||||
|
|
||||||
|
static uint32_t CalculateChecksum(const uint8_t* base_begin, |
||||||
|
size_t base_size, |
||||||
|
const uint8_t* data_begin, |
||||||
|
size_t data_size); |
||||||
|
virtual uint32_t CalculateChecksum() const OVERRIDE; |
||||||
|
|
||||||
|
private: |
||||||
|
CompactDexFile(const uint8_t* base, |
||||||
|
size_t size, |
||||||
|
const uint8_t* data_begin, |
||||||
|
size_t data_size, |
||||||
|
const std::string& location, |
||||||
|
uint32_t location_checksum, |
||||||
|
const OatDexFile* oat_dex_file, |
||||||
|
std::unique_ptr<DexFileContainer> container); |
||||||
|
|
||||||
|
CompactOffsetTable::Accessor debug_info_offsets_; |
||||||
|
|
||||||
|
friend class DexFile; |
||||||
|
friend class DexFileLoader; |
||||||
|
DISALLOW_COPY_AND_ASSIGN(CompactDexFile); |
||||||
|
}; |
||||||
|
|
||||||
|
} // namespace art_lkchan
|
||||||
|
|
||||||
|
#endif // ART_LIBDEXFILE_DEX_COMPACT_DEX_FILE_H_
|
@ -0,0 +1,49 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2017 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_COMPACT_DEX_LEVEL_H_ |
||||||
|
#define ART_LIBDEXFILE_DEX_COMPACT_DEX_LEVEL_H_ |
||||||
|
|
||||||
|
#include <string> |
||||||
|
|
||||||
|
#include "dex_file.h" |
||||||
|
|
||||||
|
namespace art_lkchan { |
||||||
|
|
||||||
|
// Optimization level for compact dex generation.
|
||||||
|
enum class CompactDexLevel { |
||||||
|
// Level none means not generated.
|
||||||
|
kCompactDexLevelNone, |
||||||
|
// Level fast means optimizations that don't take many resources to perform.
|
||||||
|
kCompactDexLevelFast, |
||||||
|
}; |
||||||
|
|
||||||
|
#ifndef ART_DEFAULT_COMPACT_DEX_LEVEL |
||||||
|
#error ART_DEFAULT_COMPACT_DEX_LEVEL not specified. |
||||||
|
#else |
||||||
|
#define ART_DEFAULT_COMPACT_DEX_LEVEL_VALUE_fast CompactDexLevel::kCompactDexLevelFast |
||||||
|
#define ART_DEFAULT_COMPACT_DEX_LEVEL_VALUE_none CompactDexLevel::kCompactDexLevelNone |
||||||
|
|
||||||
|
#define ART_DEFAULT_COMPACT_DEX_LEVEL_DEFAULT APPEND_TOKENS_AFTER_EVAL( \ |
||||||
|
ART_DEFAULT_COMPACT_DEX_LEVEL_VALUE_, \
|
||||||
|
ART_DEFAULT_COMPACT_DEX_LEVEL) |
||||||
|
|
||||||
|
static constexpr CompactDexLevel kDefaultCompactDexLevel = ART_DEFAULT_COMPACT_DEX_LEVEL_DEFAULT; |
||||||
|
#endif |
||||||
|
|
||||||
|
} // namespace art_lkchan
|
||||||
|
|
||||||
|
#endif // ART_LIBDEXFILE_DEX_COMPACT_DEX_LEVEL_H_
|
@ -0,0 +1,37 @@ |
|||||||
|
/*
|
||||||
|
* 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_COMPACT_DEX_UTILS_H_ |
||||||
|
#define ART_LIBDEXFILE_DEX_COMPACT_DEX_UTILS_H_ |
||||||
|
|
||||||
|
#include <vector> |
||||||
|
|
||||||
|
#include "base/bit_utils.h" |
||||||
|
|
||||||
|
namespace art_lkchan { |
||||||
|
|
||||||
|
// Add padding to the end of the array until the size is aligned.
|
||||||
|
template <typename T, template<typename> class Allocator> |
||||||
|
static inline void AlignmentPadVector(std::vector<T, Allocator<T>>* dest, |
||||||
|
size_t alignment) { |
||||||
|
while (!IsAlignedParam(dest->size(), alignment)) { |
||||||
|
dest->push_back(T()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace art_lkchan
|
||||||
|
|
||||||
|
#endif // ART_LIBDEXFILE_DEX_COMPACT_DEX_UTILS_H_
|
@ -0,0 +1,133 @@ |
|||||||
|
/*
|
||||||
|
* 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. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include "compact_offset_table.h" |
||||||
|
|
||||||
|
#include "compact_dex_utils.h" |
||||||
|
#include "base/leb128.h" |
||||||
|
|
||||||
|
namespace art_lkchan { |
||||||
|
|
||||||
|
constexpr size_t CompactOffsetTable::kElementsPerIndex; |
||||||
|
|
||||||
|
CompactOffsetTable::Accessor::Accessor(const uint8_t* data_begin, |
||||||
|
uint32_t minimum_offset, |
||||||
|
uint32_t table_offset) |
||||||
|
: table_(reinterpret_cast<const uint32_t*>(data_begin + table_offset)), |
||||||
|
minimum_offset_(minimum_offset), |
||||||
|
data_begin_(data_begin) {} |
||||||
|
|
||||||
|
CompactOffsetTable::Accessor::Accessor(const uint8_t* data_begin) |
||||||
|
: Accessor(data_begin + 2 * sizeof(uint32_t), |
||||||
|
reinterpret_cast<const uint32_t*>(data_begin)[0], |
||||||
|
reinterpret_cast<const uint32_t*>(data_begin)[1]) {} |
||||||
|
|
||||||
|
uint32_t CompactOffsetTable::Accessor::GetOffset(uint32_t index) const { |
||||||
|
const uint32_t offset = table_[index / kElementsPerIndex]; |
||||||
|
const size_t bit_index = index % kElementsPerIndex; |
||||||
|
|
||||||
|
const uint8_t* block = data_begin_ + offset; |
||||||
|
uint16_t bit_mask = *block; |
||||||
|
++block; |
||||||
|
bit_mask = (bit_mask << kBitsPerByte) | *block; |
||||||
|
++block; |
||||||
|
if ((bit_mask & (1 << bit_index)) == 0) { |
||||||
|
// Bit is not set means the offset is 0.
|
||||||
|
return 0u; |
||||||
|
} |
||||||
|
// Trim off the bits above the index we want and count how many bits are set. This is how many
|
||||||
|
// lebs we need to decode.
|
||||||
|
size_t count = POPCOUNT(static_cast<uintptr_t>(bit_mask) << (kBitsPerIntPtrT - 1 - bit_index)); |
||||||
|
DCHECK_GT(count, 0u); |
||||||
|
uint32_t current_offset = minimum_offset_; |
||||||
|
do { |
||||||
|
current_offset += DecodeUnsignedLeb128(&block); |
||||||
|
--count; |
||||||
|
} while (count > 0); |
||||||
|
return current_offset; |
||||||
|
} |
||||||
|
|
||||||
|
void CompactOffsetTable::Build(const std::vector<uint32_t>& offsets, |
||||||
|
std::vector<uint8_t>* out_data) { |
||||||
|
static constexpr size_t kNumOffsets = 2; |
||||||
|
uint32_t out_offsets[kNumOffsets] = {}; |
||||||
|
CompactOffsetTable::Build(offsets, out_data, &out_offsets[0], &out_offsets[1]); |
||||||
|
// Write the offsets at the start of the debug info.
|
||||||
|
out_data->insert(out_data->begin(), |
||||||
|
reinterpret_cast<const uint8_t*>(&out_offsets[0]), |
||||||
|
reinterpret_cast<const uint8_t*>(&out_offsets[kNumOffsets])); |
||||||
|
} |
||||||
|
|
||||||
|
void CompactOffsetTable::Build(const std::vector<uint32_t>& offsets, |
||||||
|
std::vector<uint8_t>* out_data, |
||||||
|
uint32_t* out_min_offset, |
||||||
|
uint32_t* out_table_offset) { |
||||||
|
DCHECK(out_data != nullptr); |
||||||
|
DCHECK(out_data->empty()); |
||||||
|
// Calculate the base offset and return it.
|
||||||
|
*out_min_offset = std::numeric_limits<uint32_t>::max(); |
||||||
|
for (const uint32_t offset : offsets) { |
||||||
|
if (offset != 0u) { |
||||||
|
*out_min_offset = std::min(*out_min_offset, offset); |
||||||
|
} |
||||||
|
} |
||||||
|
// Write the leb blocks and store the important offsets (each kElementsPerIndex elements).
|
||||||
|
size_t block_start = 0; |
||||||
|
|
||||||
|
std::vector<uint32_t> offset_table; |
||||||
|
|
||||||
|
// Write data first then the table.
|
||||||
|
while (block_start < offsets.size()) { |
||||||
|
// Write the offset of the block for each block.
|
||||||
|
offset_table.push_back(out_data->size()); |
||||||
|
|
||||||
|
// Block size of up to kElementsPerIndex
|
||||||
|
const size_t block_size = std::min(offsets.size() - block_start, kElementsPerIndex); |
||||||
|
|
||||||
|
// Calculate bit mask since need to write that first.
|
||||||
|
uint16_t bit_mask = 0u; |
||||||
|
for (size_t i = 0; i < block_size; ++i) { |
||||||
|
if (offsets[block_start + i] != 0u) { |
||||||
|
bit_mask |= 1 << i; |
||||||
|
} |
||||||
|
} |
||||||
|
// Write bit mask.
|
||||||
|
out_data->push_back(static_cast<uint8_t>(bit_mask >> kBitsPerByte)); |
||||||
|
out_data->push_back(static_cast<uint8_t>(bit_mask)); |
||||||
|
|
||||||
|
// Write offsets relative to the previous offset.
|
||||||
|
uint32_t prev_offset = *out_min_offset; |
||||||
|
for (size_t i = 0; i < block_size; ++i) { |
||||||
|
const uint32_t offset = offsets[block_start + i]; |
||||||
|
if (offset != 0u) { |
||||||
|
uint32_t delta = offset - prev_offset; |
||||||
|
EncodeUnsignedLeb128(out_data, delta); |
||||||
|
prev_offset = offset; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
block_start += block_size; |
||||||
|
} |
||||||
|
|
||||||
|
// Write the offset table.
|
||||||
|
AlignmentPadVector(out_data, alignof(uint32_t)); |
||||||
|
*out_table_offset = out_data->size(); |
||||||
|
out_data->insert(out_data->end(), |
||||||
|
reinterpret_cast<const uint8_t*>(&offset_table[0]), |
||||||
|
reinterpret_cast<const uint8_t*>(&offset_table[0] + offset_table.size())); |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace art_lkchan
|
@ -0,0 +1,69 @@ |
|||||||
|
/*
|
||||||
|
* 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_COMPACT_OFFSET_TABLE_H_ |
||||||
|
#define ART_LIBDEXFILE_DEX_COMPACT_OFFSET_TABLE_H_ |
||||||
|
|
||||||
|
#include <cstdint> |
||||||
|
#include <vector> |
||||||
|
|
||||||
|
namespace art_lkchan { |
||||||
|
|
||||||
|
// Compact offset table that aims to minimize size while still providing reasonable speed (10-20ns
|
||||||
|
// average time per lookup on host).
|
||||||
|
class CompactOffsetTable { |
||||||
|
public: |
||||||
|
// This value is coupled with the leb chunk bitmask. That logic must also be adjusted when the
|
||||||
|
// integer is modified.
|
||||||
|
static constexpr size_t kElementsPerIndex = 16; |
||||||
|
|
||||||
|
// Leb block format:
|
||||||
|
// [uint16_t] 16 bit mask for what indexes actually have a non zero offset for the chunk.
|
||||||
|
// [lebs] Up to 16 lebs encoded using leb128, one leb bit. The leb specifies how the offset
|
||||||
|
// changes compared to the previous index.
|
||||||
|
|
||||||
|
class Accessor { |
||||||
|
public: |
||||||
|
// Read the minimum and table offsets from the data pointer.
|
||||||
|
explicit Accessor(const uint8_t* data_begin); |
||||||
|
|
||||||
|
Accessor(const uint8_t* data_begin, uint32_t minimum_offset, uint32_t table_offset); |
||||||
|
|
||||||
|
// Return the offset for the index.
|
||||||
|
uint32_t GetOffset(uint32_t index) const; |
||||||
|
|
||||||
|
private: |
||||||
|
const uint32_t* const table_; |
||||||
|
const uint32_t minimum_offset_; |
||||||
|
const uint8_t* const data_begin_; |
||||||
|
}; |
||||||
|
|
||||||
|
// Version that also serializes the min offset and table offset.
|
||||||
|
static void Build(const std::vector<uint32_t>& offsets, std::vector<uint8_t>* out_data); |
||||||
|
|
||||||
|
// Returned offsets are all relative to out_min_offset.
|
||||||
|
static void Build(const std::vector<uint32_t>& offsets, |
||||||
|
std::vector<uint8_t>* out_data, |
||||||
|
uint32_t* out_min_offset, |
||||||
|
uint32_t* out_table_offset); |
||||||
|
|
||||||
|
// 32 bit aligned for the offset table.
|
||||||
|
static constexpr size_t kAlignment = sizeof(uint32_t); |
||||||
|
}; |
||||||
|
|
||||||
|
} // namespace art_lkchan
|
||||||
|
|
||||||
|
#endif // ART_LIBDEXFILE_DEX_COMPACT_OFFSET_TABLE_H_
|
@ -0,0 +1,426 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011 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. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include "descriptors_names.h" |
||||||
|
|
||||||
|
#include "android-base/stringprintf.h" |
||||||
|
#include "android-base/strings.h" |
||||||
|
|
||||||
|
#include "dex/utf-inl.h" |
||||||
|
|
||||||
|
namespace art_lkchan { |
||||||
|
|
||||||
|
using android_lkchan::base::StringAppendF; |
||||||
|
using android_lkchan::base::StringPrintf; |
||||||
|
|
||||||
|
void AppendPrettyDescriptor(const char* descriptor, std::string* result) { |
||||||
|
// Count the number of '['s to get the dimensionality.
|
||||||
|
const char* c = descriptor; |
||||||
|
size_t dim = 0; |
||||||
|
while (*c == '[') { |
||||||
|
dim++; |
||||||
|
c++; |
||||||
|
} |
||||||
|
|
||||||
|
// Reference or primitive?
|
||||||
|
if (*c == 'L') { |
||||||
|
// "[[La/b/C;" -> "a.b.C[][]".
|
||||||
|
c++; // Skip the 'L'.
|
||||||
|
} else { |
||||||
|
// "[[B" -> "byte[][]".
|
||||||
|
// To make life easier, we make primitives look like unqualified
|
||||||
|
// reference types.
|
||||||
|
switch (*c) { |
||||||
|
case 'B': c = "byte;"; break; |
||||||
|
case 'C': c = "char;"; break; |
||||||
|
case 'D': c = "double;"; break; |
||||||
|
case 'F': c = "float;"; break; |
||||||
|
case 'I': c = "int;"; break; |
||||||
|
case 'J': c = "long;"; break; |
||||||
|
case 'S': c = "short;"; break; |
||||||
|
case 'Z': c = "boolean;"; break; |
||||||
|
case 'V': c = "void;"; break; // Used when decoding return types.
|
||||||
|
default: result->append(descriptor); return; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// At this point, 'c' is a string of the form "fully/qualified/Type;"
|
||||||
|
// or "primitive;". Rewrite the type with '.' instead of '/':
|
||||||
|
const char* p = c; |
||||||
|
while (*p != ';') { |
||||||
|
char ch = *p++; |
||||||
|
if (ch == '/') { |
||||||
|
ch = '.'; |
||||||
|
} |
||||||
|
result->push_back(ch); |
||||||
|
} |
||||||
|
// ...and replace the semicolon with 'dim' "[]" pairs:
|
||||||
|
for (size_t i = 0; i < dim; ++i) { |
||||||
|
result->append("[]"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
std::string PrettyDescriptor(const char* descriptor) { |
||||||
|
std::string result; |
||||||
|
AppendPrettyDescriptor(descriptor, &result); |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
std::string GetJniShortName(const std::string& class_descriptor, const std::string& method) { |
||||||
|
// Remove the leading 'L' and trailing ';'...
|
||||||
|
std::string class_name(class_descriptor); |
||||||
|
CHECK_EQ(class_name[0], 'L') << class_name; |
||||||
|
CHECK_EQ(class_name[class_name.size() - 1], ';') << class_name; |
||||||
|
class_name.erase(0, 1); |
||||||
|
class_name.erase(class_name.size() - 1, 1); |
||||||
|
|
||||||
|
std::string short_name; |
||||||
|
short_name += "Java_"; |
||||||
|
short_name += MangleForJni(class_name); |
||||||
|
short_name += "_"; |
||||||
|
short_name += MangleForJni(method); |
||||||
|
return short_name; |
||||||
|
} |
||||||
|
|
||||||
|
// See http://java.sun.com/j2se/1.5.0/docs/guide/jni/spec/design.html#wp615 for the full rules.
|
||||||
|
std::string MangleForJni(const std::string& s) { |
||||||
|
std::string result; |
||||||
|
size_t char_count = CountModifiedUtf8Chars(s.c_str()); |
||||||
|
const char* cp = &s[0]; |
||||||
|
for (size_t i = 0; i < char_count; ++i) { |
||||||
|
uint32_t ch = GetUtf16FromUtf8(&cp); |
||||||
|
if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9')) { |
||||||
|
result.push_back(ch); |
||||||
|
} else if (ch == '.' || ch == '/') { |
||||||
|
result += "_"; |
||||||
|
} else if (ch == '_') { |
||||||
|
result += "_1"; |
||||||
|
} else if (ch == ';') { |
||||||
|
result += "_2"; |
||||||
|
} else if (ch == '[') { |
||||||
|
result += "_3"; |
||||||
|
} else { |
||||||
|
const uint16_t leading = GetLeadingUtf16Char(ch); |
||||||
|
const uint32_t trailing = GetTrailingUtf16Char(ch); |
||||||
|
|
||||||
|
StringAppendF(&result, "_0%04x", leading); |
||||||
|
if (trailing != 0) { |
||||||
|
StringAppendF(&result, "_0%04x", trailing); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
std::string DotToDescriptor(const char* class_name) { |
||||||
|
std::string descriptor(class_name); |
||||||
|
std::replace(descriptor.begin(), descriptor.end(), '.', '/'); |
||||||
|
if (descriptor.length() > 0 && descriptor[0] != '[') { |
||||||
|
descriptor = "L" + descriptor + ";"; |
||||||
|
} |
||||||
|
return descriptor; |
||||||
|
} |
||||||
|
|
||||||
|
std::string DescriptorToDot(const char* descriptor) { |
||||||
|
size_t length = strlen(descriptor); |
||||||
|
if (length > 1) { |
||||||
|
if (descriptor[0] == 'L' && descriptor[length - 1] == ';') { |
||||||
|
// Descriptors have the leading 'L' and trailing ';' stripped.
|
||||||
|
std::string result(descriptor + 1, length - 2); |
||||||
|
std::replace(result.begin(), result.end(), '/', '.'); |
||||||
|
return result; |
||||||
|
} else { |
||||||
|
// For arrays the 'L' and ';' remain intact.
|
||||||
|
std::string result(descriptor); |
||||||
|
std::replace(result.begin(), result.end(), '/', '.'); |
||||||
|
return result; |
||||||
|
} |
||||||
|
} |
||||||
|
// Do nothing for non-class/array descriptors.
|
||||||
|
return descriptor; |
||||||
|
} |
||||||
|
|
||||||
|
std::string DescriptorToName(const char* descriptor) { |
||||||
|
size_t length = strlen(descriptor); |
||||||
|
if (descriptor[0] == 'L' && descriptor[length - 1] == ';') { |
||||||
|
std::string result(descriptor + 1, length - 2); |
||||||
|
return result; |
||||||
|
} |
||||||
|
return descriptor; |
||||||
|
} |
||||||
|
|
||||||
|
// Helper for IsValidPartOfMemberNameUtf8(), a bit vector indicating valid low ascii.
|
||||||
|
static uint32_t DEX_MEMBER_VALID_LOW_ASCII[4] = { |
||||||
|
0x00000000, // 00..1f low control characters; nothing valid
|
||||||
|
0x03ff2010, // 20..3f digits and symbols; valid: '0'..'9', '$', '-'
|
||||||
|
0x87fffffe, // 40..5f uppercase etc.; valid: 'A'..'Z', '_'
|
||||||
|
0x07fffffe // 60..7f lowercase etc.; valid: 'a'..'z'
|
||||||
|
}; |
||||||
|
|
||||||
|
// Helper for IsValidPartOfMemberNameUtf8(); do not call directly.
|
||||||
|
static bool IsValidPartOfMemberNameUtf8Slow(const char** pUtf8Ptr) { |
||||||
|
/*
|
||||||
|
* It's a multibyte encoded character. Decode it and analyze. We |
||||||
|
* accept anything that isn't (a) an improperly encoded low value, |
||||||
|
* (b) an improper surrogate pair, (c) an encoded '\0', (d) a high |
||||||
|
* control character, or (e) a high space, layout, or special |
||||||
|
* character (U+00a0, U+2000..U+200f, U+2028..U+202f, |
||||||
|
* U+fff0..U+ffff). This is all specified in the dex format |
||||||
|
* document. |
||||||
|
*/ |
||||||
|
|
||||||
|
const uint32_t pair = GetUtf16FromUtf8(pUtf8Ptr); |
||||||
|
const uint16_t leading = GetLeadingUtf16Char(pair); |
||||||
|
|
||||||
|
// We have a surrogate pair resulting from a valid 4 byte UTF sequence.
|
||||||
|
// No further checks are necessary because 4 byte sequences span code
|
||||||
|
// points [U+10000, U+1FFFFF], which are valid codepoints in a dex
|
||||||
|
// identifier. Furthermore, GetUtf16FromUtf8 guarantees that each of
|
||||||
|
// the surrogate halves are valid and well formed in this instance.
|
||||||
|
if (GetTrailingUtf16Char(pair) != 0) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
// We've encountered a one, two or three byte UTF-8 sequence. The
|
||||||
|
// three byte UTF-8 sequence could be one half of a surrogate pair.
|
||||||
|
switch (leading >> 8) { |
||||||
|
case 0x00: |
||||||
|
// It's only valid if it's above the ISO-8859-1 high space (0xa0).
|
||||||
|
return (leading > 0x00a0); |
||||||
|
case 0xd8: |
||||||
|
case 0xd9: |
||||||
|
case 0xda: |
||||||
|
case 0xdb: |
||||||
|
{ |
||||||
|
// We found a three byte sequence encoding one half of a surrogate.
|
||||||
|
// Look for the other half.
|
||||||
|
const uint32_t pair2 = GetUtf16FromUtf8(pUtf8Ptr); |
||||||
|
const uint16_t trailing = GetLeadingUtf16Char(pair2); |
||||||
|
|
||||||
|
return (GetTrailingUtf16Char(pair2) == 0) && (0xdc00 <= trailing && trailing <= 0xdfff); |
||||||
|
} |
||||||
|
case 0xdc: |
||||||
|
case 0xdd: |
||||||
|
case 0xde: |
||||||
|
case 0xdf: |
||||||
|
// It's a trailing surrogate, which is not valid at this point.
|
||||||
|
return false; |
||||||
|
case 0x20: |
||||||
|
case 0xff: |
||||||
|
// It's in the range that has spaces, controls, and specials.
|
||||||
|
switch (leading & 0xfff8) { |
||||||
|
case 0x2000: |
||||||
|
case 0x2008: |
||||||
|
case 0x2028: |
||||||
|
case 0xfff0: |
||||||
|
case 0xfff8: |
||||||
|
return false; |
||||||
|
} |
||||||
|
return true; |
||||||
|
default: |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
UNREACHABLE(); |
||||||
|
} |
||||||
|
|
||||||
|
/* Return whether the pointed-at modified-UTF-8 encoded character is
|
||||||
|
* valid as part of a member name, updating the pointer to point past |
||||||
|
* the consumed character. This will consume two encoded UTF-16 code |
||||||
|
* points if the character is encoded as a surrogate pair. Also, if |
||||||
|
* this function returns false, then the given pointer may only have |
||||||
|
* been partially advanced. |
||||||
|
*/ |
||||||
|
static bool IsValidPartOfMemberNameUtf8(const char** pUtf8Ptr) { |
||||||
|
uint8_t c = (uint8_t) **pUtf8Ptr; |
||||||
|
if (LIKELY(c <= 0x7f)) { |
||||||
|
// It's low-ascii, so check the table.
|
||||||
|
uint32_t wordIdx = c >> 5; |
||||||
|
uint32_t bitIdx = c & 0x1f; |
||||||
|
(*pUtf8Ptr)++; |
||||||
|
return (DEX_MEMBER_VALID_LOW_ASCII[wordIdx] & (1 << bitIdx)) != 0; |
||||||
|
} |
||||||
|
|
||||||
|
// It's a multibyte encoded character. Call a non-inline function
|
||||||
|
// for the heavy lifting.
|
||||||
|
return IsValidPartOfMemberNameUtf8Slow(pUtf8Ptr); |
||||||
|
} |
||||||
|
|
||||||
|
bool IsValidMemberName(const char* s) { |
||||||
|
bool angle_name = false; |
||||||
|
|
||||||
|
switch (*s) { |
||||||
|
case '\0': |
||||||
|
// The empty string is not a valid name.
|
||||||
|
return false; |
||||||
|
case '<': |
||||||
|
angle_name = true; |
||||||
|
s++; |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
while (true) { |
||||||
|
switch (*s) { |
||||||
|
case '\0': |
||||||
|
return !angle_name; |
||||||
|
case '>': |
||||||
|
return angle_name && s[1] == '\0'; |
||||||
|
} |
||||||
|
|
||||||
|
if (!IsValidPartOfMemberNameUtf8(&s)) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
enum ClassNameType { kName, kDescriptor }; |
||||||
|
template<ClassNameType kType, char kSeparator> |
||||||
|
static bool IsValidClassName(const char* s) { |
||||||
|
int arrayCount = 0; |
||||||
|
while (*s == '[') { |
||||||
|
arrayCount++; |
||||||
|
s++; |
||||||
|
} |
||||||
|
|
||||||
|
if (arrayCount > 255) { |
||||||
|
// Arrays may have no more than 255 dimensions.
|
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
ClassNameType type = kType; |
||||||
|
if (type != kDescriptor && arrayCount != 0) { |
||||||
|
/*
|
||||||
|
* If we're looking at an array of some sort, then it doesn't |
||||||
|
* matter if what is being asked for is a class name; the |
||||||
|
* format looks the same as a type descriptor in that case, so |
||||||
|
* treat it as such. |
||||||
|
*/ |
||||||
|
type = kDescriptor; |
||||||
|
} |
||||||
|
|
||||||
|
if (type == kDescriptor) { |
||||||
|
/*
|
||||||
|
* We are looking for a descriptor. Either validate it as a |
||||||
|
* single-character primitive type, or continue on to check the |
||||||
|
* embedded class name (bracketed by "L" and ";"). |
||||||
|
*/ |
||||||
|
switch (*(s++)) { |
||||||
|
case 'B': |
||||||
|
case 'C': |
||||||
|
case 'D': |
||||||
|
case 'F': |
||||||
|
case 'I': |
||||||
|
case 'J': |
||||||
|
case 'S': |
||||||
|
case 'Z': |
||||||
|
// These are all single-character descriptors for primitive types.
|
||||||
|
return (*s == '\0'); |
||||||
|
case 'V': |
||||||
|
// Non-array void is valid, but you can't have an array of void.
|
||||||
|
return (arrayCount == 0) && (*s == '\0'); |
||||||
|
case 'L': |
||||||
|
// Class name: Break out and continue below.
|
||||||
|
break; |
||||||
|
default: |
||||||
|
// Oddball descriptor character.
|
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* We just consumed the 'L' that introduces a class name as part |
||||||
|
* of a type descriptor, or we are looking for an unadorned class
|
||||||
|
* name. |
||||||
|
*/ |
||||||
|
|
||||||
|
bool sepOrFirst = true; // first character or just encountered a separator.
|
||||||
|
for (;;) { |
||||||
|
uint8_t c = (uint8_t) *s; |
||||||
|
switch (c) { |
||||||
|
case '\0': |
||||||
|
/*
|
||||||
|
* Premature end for a type descriptor, but valid for |
||||||
|
* a class name as long as we haven't encountered an |
||||||
|
* empty component (including the degenerate case of |
||||||
|
* the empty string ""). |
||||||
|
*/ |
||||||
|
return (type == kName) && !sepOrFirst; |
||||||
|
case ';': |
||||||
|
/*
|
||||||
|
* Invalid character for a class name, but the |
||||||
|
* legitimate end of a type descriptor. In the latter |
||||||
|
* case, make sure that this is the end of the string |
||||||
|
* and that it doesn't end with an empty component |
||||||
|
* (including the degenerate case of "L;"). |
||||||
|
*/ |
||||||
|
return (type == kDescriptor) && !sepOrFirst && (s[1] == '\0'); |
||||||
|
case '/': |
||||||
|
case '.': |
||||||
|
if (c != kSeparator) { |
||||||
|
// The wrong separator character.
|
||||||
|
return false; |
||||||
|
} |
||||||
|
if (sepOrFirst) { |
||||||
|
// Separator at start or two separators in a row.
|
||||||
|
return false; |
||||||
|
} |
||||||
|
sepOrFirst = true; |
||||||
|
s++; |
||||||
|
break; |
||||||
|
default: |
||||||
|
if (!IsValidPartOfMemberNameUtf8(&s)) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
sepOrFirst = false; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
bool IsValidBinaryClassName(const char* s) { |
||||||
|
return IsValidClassName<kName, '.'>(s); |
||||||
|
} |
||||||
|
|
||||||
|
bool IsValidJniClassName(const char* s) { |
||||||
|
return IsValidClassName<kName, '/'>(s); |
||||||
|
} |
||||||
|
|
||||||
|
bool IsValidDescriptor(const char* s) { |
||||||
|
return IsValidClassName<kDescriptor, '/'>(s); |
||||||
|
} |
||||||
|
|
||||||
|
void Split(const std::string& s, char separator, std::vector<std::string>* result) { |
||||||
|
const char* p = s.data(); |
||||||
|
const char* end = p + s.size(); |
||||||
|
while (p != end) { |
||||||
|
if (*p == separator) { |
||||||
|
++p; |
||||||
|
} else { |
||||||
|
const char* start = p; |
||||||
|
while (++p != end && *p != separator) { |
||||||
|
// Skip to the next occurrence of the separator.
|
||||||
|
} |
||||||
|
result->push_back(std::string(start, p - start)); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
std::string PrettyDescriptor(Primitive::Type type) { |
||||||
|
return PrettyDescriptor(Primitive::Descriptor(type)); |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace art_lkchan
|
@ -0,0 +1,63 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011 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_DESCRIPTORS_NAMES_H_ |
||||||
|
#define ART_LIBDEXFILE_DEX_DESCRIPTORS_NAMES_H_ |
||||||
|
|
||||||
|
#include <string> |
||||||
|
|
||||||
|
#include "dex/primitive.h" |
||||||
|
|
||||||
|
namespace art_lkchan { |
||||||
|
|
||||||
|
// Used to implement PrettyClass, PrettyField, PrettyMethod, and PrettyTypeOf,
|
||||||
|
// one of which is probably more useful to you.
|
||||||
|
// Returns a human-readable equivalent of 'descriptor'. So "I" would be "int",
|
||||||
|
// "[[I" would be "int[][]", "[Ljava/lang/String;" would be
|
||||||
|
// "java.lang.String[]", and so forth.
|
||||||
|
void AppendPrettyDescriptor(const char* descriptor, std::string* result); |
||||||
|
std::string PrettyDescriptor(const char* descriptor); |
||||||
|
std::string PrettyDescriptor(Primitive::Type type); |
||||||
|
|
||||||
|
// Performs JNI name mangling as described in section 11.3 "Linking Native Methods"
|
||||||
|
// of the JNI spec.
|
||||||
|
std::string MangleForJni(const std::string& s); |
||||||
|
|
||||||
|
std::string GetJniShortName(const std::string& class_name, const std::string& method_name); |
||||||
|
|
||||||
|
// Turn "java.lang.String" into "Ljava/lang/String;".
|
||||||
|
std::string DotToDescriptor(const char* class_name); |
||||||
|
|
||||||
|
// Turn "Ljava/lang/String;" into "java.lang.String" using the conventions of
|
||||||
|
// java.lang.Class.getName().
|
||||||
|
std::string DescriptorToDot(const char* descriptor); |
||||||
|
|
||||||
|
// Turn "Ljava/lang/String;" into "java/lang/String" using the opposite conventions of
|
||||||
|
// java.lang.Class.getName().
|
||||||
|
std::string DescriptorToName(const char* descriptor); |
||||||
|
|
||||||
|
// Tests for whether 's' is a valid class name in the three common forms:
|
||||||
|
bool IsValidBinaryClassName(const char* s); // "java.lang.String"
|
||||||
|
bool IsValidJniClassName(const char* s); // "java/lang/String"
|
||||||
|
bool IsValidDescriptor(const char* s); // "Ljava/lang/String;"
|
||||||
|
|
||||||
|
// Returns whether the given string is a valid field or method name,
|
||||||
|
// additionally allowing names that begin with '<' and end with '>'.
|
||||||
|
bool IsValidMemberName(const char* s); |
||||||
|
|
||||||
|
} // namespace art_lkchan
|
||||||
|
|
||||||
|
#endif // ART_LIBDEXFILE_DEX_DESCRIPTORS_NAMES_H_
|
@ -0,0 +1,93 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 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_DEX_CACHE_RESOLVED_CLASSES_H_ |
||||||
|
#define ART_LIBDEXFILE_DEX_DEX_CACHE_RESOLVED_CLASSES_H_ |
||||||
|
|
||||||
|
#include <string> |
||||||
|
#include <unordered_set> |
||||||
|
#include <vector> |
||||||
|
|
||||||
|
#include "dex/dex_file_types.h" |
||||||
|
|
||||||
|
namespace art_lkchan { |
||||||
|
|
||||||
|
// Data structure for passing around which classes belonging to a dex cache / dex file are resolved.
|
||||||
|
class DexCacheResolvedClasses { |
||||||
|
public: |
||||||
|
DexCacheResolvedClasses(const std::string& dex_location, |
||||||
|
const std::string& base_location, |
||||||
|
uint32_t location_checksum, |
||||||
|
uint32_t num_method_ids) |
||||||
|
: dex_location_(dex_location), |
||||||
|
base_location_(base_location), |
||||||
|
location_checksum_(location_checksum), |
||||||
|
num_method_ids_(num_method_ids) {} |
||||||
|
|
||||||
|
// Only compare the key elements, ignore the resolved classes.
|
||||||
|
int Compare(const DexCacheResolvedClasses& other) const { |
||||||
|
if (location_checksum_ != other.location_checksum_) { |
||||||
|
return static_cast<int>(location_checksum_ - other.location_checksum_); |
||||||
|
} |
||||||
|
// Don't need to compare base_location_ since dex_location_ has more info.
|
||||||
|
return dex_location_.compare(other.dex_location_); |
||||||
|
} |
||||||
|
|
||||||
|
bool AddClass(dex::TypeIndex index) const { |
||||||
|
return classes_.insert(index).second; |
||||||
|
} |
||||||
|
|
||||||
|
template <class InputIt> |
||||||
|
void AddClasses(InputIt begin, InputIt end) const { |
||||||
|
classes_.insert(begin, end); |
||||||
|
} |
||||||
|
|
||||||
|
const std::string& GetDexLocation() const { |
||||||
|
return dex_location_; |
||||||
|
} |
||||||
|
|
||||||
|
const std::string& GetBaseLocation() const { |
||||||
|
return base_location_; |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t GetLocationChecksum() const { |
||||||
|
return location_checksum_; |
||||||
|
} |
||||||
|
|
||||||
|
const std::unordered_set<dex::TypeIndex>& GetClasses() const { |
||||||
|
return classes_; |
||||||
|
} |
||||||
|
|
||||||
|
size_t NumMethodIds() const { |
||||||
|
return num_method_ids_; |
||||||
|
} |
||||||
|
|
||||||
|
private: |
||||||
|
const std::string dex_location_; |
||||||
|
const std::string base_location_; |
||||||
|
const uint32_t location_checksum_; |
||||||
|
const uint32_t num_method_ids_; |
||||||
|
// Array of resolved class def indexes.
|
||||||
|
mutable std::unordered_set<dex::TypeIndex> classes_; |
||||||
|
}; |
||||||
|
|
||||||
|
inline bool operator<(const DexCacheResolvedClasses& a, const DexCacheResolvedClasses& b) { |
||||||
|
return a.Compare(b) < 0; |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace art_lkchan
|
||||||
|
|
||||||
|
#endif // ART_LIBDEXFILE_DEX_DEX_CACHE_RESOLVED_CLASSES_H_
|
@ -0,0 +1,532 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011 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_DEX_FILE_INL_H_ |
||||||
|
#define ART_LIBDEXFILE_DEX_DEX_FILE_INL_H_ |
||||||
|
|
||||||
|
#include "base/casts.h" |
||||||
|
#include "base/leb128.h" |
||||||
|
#include "base/stringpiece.h" |
||||||
|
#include "compact_dex_file.h" |
||||||
|
#include "dex_file.h" |
||||||
|
#include "invoke_type.h" |
||||||
|
#include "standard_dex_file.h" |
||||||
|
|
||||||
|
namespace art_lkchan { |
||||||
|
|
||||||
|
inline int32_t DexFile::GetStringLength(const StringId& string_id) const { |
||||||
|
const uint8_t* ptr = DataBegin() + string_id.string_data_off_; |
||||||
|
return DecodeUnsignedLeb128(&ptr); |
||||||
|
} |
||||||
|
|
||||||
|
inline const char* DexFile::GetStringDataAndUtf16Length(const StringId& string_id, |
||||||
|
uint32_t* utf16_length) const { |
||||||
|
DCHECK(utf16_length != nullptr) << GetLocation(); |
||||||
|
const uint8_t* ptr = DataBegin() + string_id.string_data_off_; |
||||||
|
*utf16_length = DecodeUnsignedLeb128(&ptr); |
||||||
|
return reinterpret_cast<const char*>(ptr); |
||||||
|
} |
||||||
|
|
||||||
|
inline const char* DexFile::GetStringData(const StringId& string_id) const { |
||||||
|
uint32_t ignored; |
||||||
|
return GetStringDataAndUtf16Length(string_id, &ignored); |
||||||
|
} |
||||||
|
|
||||||
|
inline const char* DexFile::StringDataAndUtf16LengthByIdx(dex::StringIndex idx, |
||||||
|
uint32_t* utf16_length) const { |
||||||
|
if (!idx.IsValid()) { |
||||||
|
*utf16_length = 0; |
||||||
|
return nullptr; |
||||||
|
} |
||||||
|
const StringId& string_id = GetStringId(idx); |
||||||
|
return GetStringDataAndUtf16Length(string_id, utf16_length); |
||||||
|
} |
||||||
|
|
||||||
|
inline const char* DexFile::StringDataByIdx(dex::StringIndex idx) const { |
||||||
|
uint32_t unicode_length; |
||||||
|
return StringDataAndUtf16LengthByIdx(idx, &unicode_length); |
||||||
|
} |
||||||
|
|
||||||
|
inline const char* DexFile::StringByTypeIdx(dex::TypeIndex idx, uint32_t* unicode_length) const { |
||||||
|
if (!idx.IsValid()) { |
||||||
|
return nullptr; |
||||||
|
} |
||||||
|
const TypeId& type_id = GetTypeId(idx); |
||||||
|
return StringDataAndUtf16LengthByIdx(type_id.descriptor_idx_, unicode_length); |
||||||
|
} |
||||||
|
|
||||||
|
inline const char* DexFile::StringByTypeIdx(dex::TypeIndex idx) const { |
||||||
|
if (!idx.IsValid()) { |
||||||
|
return nullptr; |
||||||
|
} |
||||||
|
const TypeId& type_id = GetTypeId(idx); |
||||||
|
return StringDataByIdx(type_id.descriptor_idx_); |
||||||
|
} |
||||||
|
|
||||||
|
inline const char* DexFile::GetTypeDescriptor(const TypeId& type_id) const { |
||||||
|
return StringDataByIdx(type_id.descriptor_idx_); |
||||||
|
} |
||||||
|
|
||||||
|
inline const char* DexFile::GetFieldTypeDescriptor(const FieldId& field_id) const { |
||||||
|
const DexFile::TypeId& type_id = GetTypeId(field_id.type_idx_); |
||||||
|
return GetTypeDescriptor(type_id); |
||||||
|
} |
||||||
|
|
||||||
|
inline const char* DexFile::GetFieldName(const FieldId& field_id) const { |
||||||
|
return StringDataByIdx(field_id.name_idx_); |
||||||
|
} |
||||||
|
|
||||||
|
inline const char* DexFile::GetMethodDeclaringClassDescriptor(const MethodId& method_id) const { |
||||||
|
const DexFile::TypeId& type_id = GetTypeId(method_id.class_idx_); |
||||||
|
return GetTypeDescriptor(type_id); |
||||||
|
} |
||||||
|
|
||||||
|
inline const Signature DexFile::GetMethodSignature(const MethodId& method_id) const { |
||||||
|
return Signature(this, GetProtoId(method_id.proto_idx_)); |
||||||
|
} |
||||||
|
|
||||||
|
inline const Signature DexFile::GetProtoSignature(const ProtoId& proto_id) const { |
||||||
|
return Signature(this, proto_id); |
||||||
|
} |
||||||
|
|
||||||
|
inline const char* DexFile::GetMethodName(const MethodId& method_id) const { |
||||||
|
return StringDataByIdx(method_id.name_idx_); |
||||||
|
} |
||||||
|
|
||||||
|
inline const char* DexFile::GetMethodShorty(uint32_t idx) const { |
||||||
|
return StringDataByIdx(GetProtoId(GetMethodId(idx).proto_idx_).shorty_idx_); |
||||||
|
} |
||||||
|
|
||||||
|
inline const char* DexFile::GetMethodShorty(const MethodId& method_id) const { |
||||||
|
return StringDataByIdx(GetProtoId(method_id.proto_idx_).shorty_idx_); |
||||||
|
} |
||||||
|
|
||||||
|
inline const char* DexFile::GetMethodShorty(const MethodId& method_id, uint32_t* length) const { |
||||||
|
// Using the UTF16 length is safe here as shorties are guaranteed to be ASCII characters.
|
||||||
|
return StringDataAndUtf16LengthByIdx(GetProtoId(method_id.proto_idx_).shorty_idx_, length); |
||||||
|
} |
||||||
|
|
||||||
|
inline const char* DexFile::GetClassDescriptor(const ClassDef& class_def) const { |
||||||
|
return StringByTypeIdx(class_def.class_idx_); |
||||||
|
} |
||||||
|
|
||||||
|
inline const char* DexFile::GetReturnTypeDescriptor(const ProtoId& proto_id) const { |
||||||
|
return StringByTypeIdx(proto_id.return_type_idx_); |
||||||
|
} |
||||||
|
|
||||||
|
inline const char* DexFile::GetShorty(uint32_t proto_idx) const { |
||||||
|
const ProtoId& proto_id = GetProtoId(proto_idx); |
||||||
|
return StringDataByIdx(proto_id.shorty_idx_); |
||||||
|
} |
||||||
|
|
||||||
|
inline const DexFile::TryItem* DexFile::GetTryItems(const DexInstructionIterator& code_item_end, |
||||||
|
uint32_t offset) { |
||||||
|
return reinterpret_cast<const TryItem*> |
||||||
|
(RoundUp(reinterpret_cast<uintptr_t>(&code_item_end.Inst()), TryItem::kAlignment)) + offset; |
||||||
|
} |
||||||
|
|
||||||
|
static inline bool DexFileStringEquals(const DexFile* df1, dex::StringIndex sidx1, |
||||||
|
const DexFile* df2, dex::StringIndex sidx2) { |
||||||
|
uint32_t s1_len; // Note: utf16 length != mutf8 length.
|
||||||
|
const char* s1_data = df1->StringDataAndUtf16LengthByIdx(sidx1, &s1_len); |
||||||
|
uint32_t s2_len; |
||||||
|
const char* s2_data = df2->StringDataAndUtf16LengthByIdx(sidx2, &s2_len); |
||||||
|
return (s1_len == s2_len) && (strcmp(s1_data, s2_data) == 0); |
||||||
|
} |
||||||
|
|
||||||
|
inline bool Signature::operator==(const Signature& rhs) const { |
||||||
|
if (dex_file_ == nullptr) { |
||||||
|
return rhs.dex_file_ == nullptr; |
||||||
|
} |
||||||
|
if (rhs.dex_file_ == nullptr) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
if (dex_file_ == rhs.dex_file_) { |
||||||
|
return proto_id_ == rhs.proto_id_; |
||||||
|
} |
||||||
|
uint32_t lhs_shorty_len; // For a shorty utf16 length == mutf8 length.
|
||||||
|
const char* lhs_shorty_data = dex_file_->StringDataAndUtf16LengthByIdx(proto_id_->shorty_idx_, |
||||||
|
&lhs_shorty_len); |
||||||
|
StringPiece lhs_shorty(lhs_shorty_data, lhs_shorty_len); |
||||||
|
{ |
||||||
|
uint32_t rhs_shorty_len; |
||||||
|
const char* rhs_shorty_data = |
||||||
|
rhs.dex_file_->StringDataAndUtf16LengthByIdx(rhs.proto_id_->shorty_idx_, |
||||||
|
&rhs_shorty_len); |
||||||
|
StringPiece rhs_shorty(rhs_shorty_data, rhs_shorty_len); |
||||||
|
if (lhs_shorty != rhs_shorty) { |
||||||
|
return false; // Shorty mismatch.
|
||||||
|
} |
||||||
|
} |
||||||
|
if (lhs_shorty[0] == 'L') { |
||||||
|
const DexFile::TypeId& return_type_id = dex_file_->GetTypeId(proto_id_->return_type_idx_); |
||||||
|
const DexFile::TypeId& rhs_return_type_id = |
||||||
|
rhs.dex_file_->GetTypeId(rhs.proto_id_->return_type_idx_); |
||||||
|
if (!DexFileStringEquals(dex_file_, return_type_id.descriptor_idx_, |
||||||
|
rhs.dex_file_, rhs_return_type_id.descriptor_idx_)) { |
||||||
|
return false; // Return type mismatch.
|
||||||
|
} |
||||||
|
} |
||||||
|
if (lhs_shorty.find('L', 1) != StringPiece::npos) { |
||||||
|
const DexFile::TypeList* params = dex_file_->GetProtoParameters(*proto_id_); |
||||||
|
const DexFile::TypeList* rhs_params = rhs.dex_file_->GetProtoParameters(*rhs.proto_id_); |
||||||
|
// We found a reference parameter in the matching shorty, so both lists must be non-empty.
|
||||||
|
DCHECK(params != nullptr); |
||||||
|
DCHECK(rhs_params != nullptr); |
||||||
|
uint32_t params_size = params->Size(); |
||||||
|
DCHECK_EQ(params_size, rhs_params->Size()); // Parameter list size must match.
|
||||||
|
for (uint32_t i = 0; i < params_size; ++i) { |
||||||
|
const DexFile::TypeId& param_id = dex_file_->GetTypeId(params->GetTypeItem(i).type_idx_); |
||||||
|
const DexFile::TypeId& rhs_param_id = |
||||||
|
rhs.dex_file_->GetTypeId(rhs_params->GetTypeItem(i).type_idx_); |
||||||
|
if (!DexFileStringEquals(dex_file_, param_id.descriptor_idx_, |
||||||
|
rhs.dex_file_, rhs_param_id.descriptor_idx_)) { |
||||||
|
return false; // Parameter type mismatch.
|
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
inline |
||||||
|
InvokeType ClassDataItemIterator::GetMethodInvokeType(const DexFile::ClassDef& class_def) const { |
||||||
|
if (HasNextDirectMethod()) { |
||||||
|
if ((GetRawMemberAccessFlags() & kAccStatic) != 0) { |
||||||
|
return kStatic; |
||||||
|
} else { |
||||||
|
return kDirect; |
||||||
|
} |
||||||
|
} else { |
||||||
|
DCHECK_EQ(GetRawMemberAccessFlags() & kAccStatic, 0U); |
||||||
|
if ((class_def.access_flags_ & kAccInterface) != 0) { |
||||||
|
return kInterface; |
||||||
|
} else if ((GetRawMemberAccessFlags() & kAccConstructor) != 0) { |
||||||
|
return kSuper; |
||||||
|
} else { |
||||||
|
return kVirtual; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
template<typename NewLocalCallback, typename IndexToStringData, typename TypeIndexToStringData> |
||||||
|
bool DexFile::DecodeDebugLocalInfo(const uint8_t* stream, |
||||||
|
const std::string& location, |
||||||
|
const char* declaring_class_descriptor, |
||||||
|
const std::vector<const char*>& arg_descriptors, |
||||||
|
const std::string& method_name, |
||||||
|
bool is_static, |
||||||
|
uint16_t registers_size, |
||||||
|
uint16_t ins_size, |
||||||
|
uint16_t insns_size_in_code_units, |
||||||
|
IndexToStringData index_to_string_data, |
||||||
|
TypeIndexToStringData type_index_to_string_data, |
||||||
|
NewLocalCallback new_local_callback, |
||||||
|
void* context) { |
||||||
|
if (stream == nullptr) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
std::vector<LocalInfo> local_in_reg(registers_size); |
||||||
|
|
||||||
|
uint16_t arg_reg = registers_size - ins_size; |
||||||
|
if (!is_static) { |
||||||
|
const char* descriptor = declaring_class_descriptor; |
||||||
|
local_in_reg[arg_reg].name_ = "this"; |
||||||
|
local_in_reg[arg_reg].descriptor_ = descriptor; |
||||||
|
local_in_reg[arg_reg].signature_ = nullptr; |
||||||
|
local_in_reg[arg_reg].start_address_ = 0; |
||||||
|
local_in_reg[arg_reg].reg_ = arg_reg; |
||||||
|
local_in_reg[arg_reg].is_live_ = true; |
||||||
|
arg_reg++; |
||||||
|
} |
||||||
|
|
||||||
|
DecodeUnsignedLeb128(&stream); // Line.
|
||||||
|
uint32_t parameters_size = DecodeUnsignedLeb128(&stream); |
||||||
|
uint32_t i; |
||||||
|
if (parameters_size != arg_descriptors.size()) { |
||||||
|
LOG(ERROR) << "invalid stream - problem with parameter iterator in " << location |
||||||
|
<< " for method " << method_name; |
||||||
|
return false; |
||||||
|
} |
||||||
|
for (i = 0; i < parameters_size && i < arg_descriptors.size(); ++i) { |
||||||
|
if (arg_reg >= registers_size) { |
||||||
|
LOG(ERROR) << "invalid stream - arg reg >= reg size (" << arg_reg |
||||||
|
<< " >= " << registers_size << ") in " << location; |
||||||
|
return false; |
||||||
|
} |
||||||
|
uint32_t name_idx = DecodeUnsignedLeb128P1(&stream); |
||||||
|
const char* descriptor = arg_descriptors[i]; |
||||||
|
local_in_reg[arg_reg].name_ = index_to_string_data(name_idx); |
||||||
|
local_in_reg[arg_reg].descriptor_ = descriptor; |
||||||
|
local_in_reg[arg_reg].signature_ = nullptr; |
||||||
|
local_in_reg[arg_reg].start_address_ = 0; |
||||||
|
local_in_reg[arg_reg].reg_ = arg_reg; |
||||||
|
local_in_reg[arg_reg].is_live_ = true; |
||||||
|
switch (*descriptor) { |
||||||
|
case 'D': |
||||||
|
case 'J': |
||||||
|
arg_reg += 2; |
||||||
|
break; |
||||||
|
default: |
||||||
|
arg_reg += 1; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t address = 0; |
||||||
|
for (;;) { |
||||||
|
uint8_t opcode = *stream++; |
||||||
|
switch (opcode) { |
||||||
|
case DBG_END_SEQUENCE: |
||||||
|
// Emit all variables which are still alive at the end of the method.
|
||||||
|
for (uint16_t reg = 0; reg < registers_size; reg++) { |
||||||
|
if (local_in_reg[reg].is_live_) { |
||||||
|
local_in_reg[reg].end_address_ = insns_size_in_code_units; |
||||||
|
new_local_callback(context, local_in_reg[reg]); |
||||||
|
} |
||||||
|
} |
||||||
|
return true; |
||||||
|
case DBG_ADVANCE_PC: |
||||||
|
address += DecodeUnsignedLeb128(&stream); |
||||||
|
break; |
||||||
|
case DBG_ADVANCE_LINE: |
||||||
|
DecodeSignedLeb128(&stream); // Line.
|
||||||
|
break; |
||||||
|
case DBG_START_LOCAL: |
||||||
|
case DBG_START_LOCAL_EXTENDED: { |
||||||
|
uint16_t reg = DecodeUnsignedLeb128(&stream); |
||||||
|
if (reg >= registers_size) { |
||||||
|
LOG(ERROR) << "invalid stream - reg >= reg size (" << reg << " >= " |
||||||
|
<< registers_size << ") in " << location; |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t name_idx = DecodeUnsignedLeb128P1(&stream); |
||||||
|
uint16_t descriptor_idx = DecodeUnsignedLeb128P1(&stream); |
||||||
|
uint32_t signature_idx = dex::kDexNoIndex; |
||||||
|
if (opcode == DBG_START_LOCAL_EXTENDED) { |
||||||
|
signature_idx = DecodeUnsignedLeb128P1(&stream); |
||||||
|
} |
||||||
|
|
||||||
|
// Emit what was previously there, if anything
|
||||||
|
if (local_in_reg[reg].is_live_) { |
||||||
|
local_in_reg[reg].end_address_ = address; |
||||||
|
new_local_callback(context, local_in_reg[reg]); |
||||||
|
} |
||||||
|
|
||||||
|
local_in_reg[reg].name_ = index_to_string_data(name_idx); |
||||||
|
local_in_reg[reg].descriptor_ = type_index_to_string_data(descriptor_idx);; |
||||||
|
local_in_reg[reg].signature_ = index_to_string_data(signature_idx); |
||||||
|
local_in_reg[reg].start_address_ = address; |
||||||
|
local_in_reg[reg].reg_ = reg; |
||||||
|
local_in_reg[reg].is_live_ = true; |
||||||
|
break; |
||||||
|
} |
||||||
|
case DBG_END_LOCAL: { |
||||||
|
uint16_t reg = DecodeUnsignedLeb128(&stream); |
||||||
|
if (reg >= registers_size) { |
||||||
|
LOG(ERROR) << "invalid stream - reg >= reg size (" << reg << " >= " |
||||||
|
<< registers_size << ") in " << location; |
||||||
|
return false; |
||||||
|
} |
||||||
|
// If the register is live, close it properly. Otherwise, closing an already
|
||||||
|
// closed register is sloppy, but harmless if no further action is taken.
|
||||||
|
if (local_in_reg[reg].is_live_) { |
||||||
|
local_in_reg[reg].end_address_ = address; |
||||||
|
new_local_callback(context, local_in_reg[reg]); |
||||||
|
local_in_reg[reg].is_live_ = false; |
||||||
|
} |
||||||
|
break; |
||||||
|
} |
||||||
|
case DBG_RESTART_LOCAL: { |
||||||
|
uint16_t reg = DecodeUnsignedLeb128(&stream); |
||||||
|
if (reg >= registers_size) { |
||||||
|
LOG(ERROR) << "invalid stream - reg >= reg size (" << reg << " >= " |
||||||
|
<< registers_size << ") in " << location; |
||||||
|
return false; |
||||||
|
} |
||||||
|
// If the register is live, the "restart" is superfluous,
|
||||||
|
// and we don't want to mess with the existing start address.
|
||||||
|
if (!local_in_reg[reg].is_live_) { |
||||||
|
local_in_reg[reg].start_address_ = address; |
||||||
|
local_in_reg[reg].is_live_ = true; |
||||||
|
} |
||||||
|
break; |
||||||
|
} |
||||||
|
case DBG_SET_PROLOGUE_END: |
||||||
|
case DBG_SET_EPILOGUE_BEGIN: |
||||||
|
break; |
||||||
|
case DBG_SET_FILE: |
||||||
|
DecodeUnsignedLeb128P1(&stream); // name.
|
||||||
|
break; |
||||||
|
default: |
||||||
|
address += (opcode - DBG_FIRST_SPECIAL) / DBG_LINE_RANGE; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
template<typename NewLocalCallback> |
||||||
|
bool DexFile::DecodeDebugLocalInfo(uint32_t registers_size, |
||||||
|
uint32_t ins_size, |
||||||
|
uint32_t insns_size_in_code_units, |
||||||
|
uint32_t debug_info_offset, |
||||||
|
bool is_static, |
||||||
|
uint32_t method_idx, |
||||||
|
NewLocalCallback new_local_callback, |
||||||
|
void* context) const { |
||||||
|
const uint8_t* const stream = GetDebugInfoStream(debug_info_offset); |
||||||
|
if (stream == nullptr) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
std::vector<const char*> arg_descriptors; |
||||||
|
DexFileParameterIterator it(*this, GetMethodPrototype(GetMethodId(method_idx))); |
||||||
|
for (; it.HasNext(); it.Next()) { |
||||||
|
arg_descriptors.push_back(it.GetDescriptor()); |
||||||
|
} |
||||||
|
return DecodeDebugLocalInfo(stream, |
||||||
|
GetLocation(), |
||||||
|
GetMethodDeclaringClassDescriptor(GetMethodId(method_idx)), |
||||||
|
arg_descriptors, |
||||||
|
this->PrettyMethod(method_idx), |
||||||
|
is_static, |
||||||
|
registers_size, |
||||||
|
ins_size, |
||||||
|
insns_size_in_code_units, |
||||||
|
[this](uint32_t idx) { |
||||||
|
return StringDataByIdx(dex::StringIndex(idx)); |
||||||
|
}, |
||||||
|
[this](uint32_t idx) { |
||||||
|
return StringByTypeIdx(dex::TypeIndex( |
||||||
|
dchecked_integral_cast<uint16_t>(idx))); |
||||||
|
}, |
||||||
|
new_local_callback, |
||||||
|
context); |
||||||
|
} |
||||||
|
|
||||||
|
template<typename DexDebugNewPosition, typename IndexToStringData> |
||||||
|
bool DexFile::DecodeDebugPositionInfo(const uint8_t* stream, |
||||||
|
IndexToStringData index_to_string_data, |
||||||
|
DexDebugNewPosition position_functor, |
||||||
|
void* context) { |
||||||
|
if (stream == nullptr) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
PositionInfo entry = PositionInfo(); |
||||||
|
entry.line_ = DecodeUnsignedLeb128(&stream); |
||||||
|
uint32_t parameters_size = DecodeUnsignedLeb128(&stream); |
||||||
|
for (uint32_t i = 0; i < parameters_size; ++i) { |
||||||
|
DecodeUnsignedLeb128P1(&stream); // Parameter name.
|
||||||
|
} |
||||||
|
|
||||||
|
for (;;) { |
||||||
|
uint8_t opcode = *stream++; |
||||||
|
switch (opcode) { |
||||||
|
case DBG_END_SEQUENCE: |
||||||
|
return true; // end of stream.
|
||||||
|
case DBG_ADVANCE_PC: |
||||||
|
entry.address_ += DecodeUnsignedLeb128(&stream); |
||||||
|
break; |
||||||
|
case DBG_ADVANCE_LINE: |
||||||
|
entry.line_ += DecodeSignedLeb128(&stream); |
||||||
|
break; |
||||||
|
case DBG_START_LOCAL: |
||||||
|
DecodeUnsignedLeb128(&stream); // reg.
|
||||||
|
DecodeUnsignedLeb128P1(&stream); // name.
|
||||||
|
DecodeUnsignedLeb128P1(&stream); // descriptor.
|
||||||
|
break; |
||||||
|
case DBG_START_LOCAL_EXTENDED: |
||||||
|
DecodeUnsignedLeb128(&stream); // reg.
|
||||||
|
DecodeUnsignedLeb128P1(&stream); // name.
|
||||||
|
DecodeUnsignedLeb128P1(&stream); // descriptor.
|
||||||
|
DecodeUnsignedLeb128P1(&stream); // signature.
|
||||||
|
break; |
||||||
|
case DBG_END_LOCAL: |
||||||
|
case DBG_RESTART_LOCAL: |
||||||
|
DecodeUnsignedLeb128(&stream); // reg.
|
||||||
|
break; |
||||||
|
case DBG_SET_PROLOGUE_END: |
||||||
|
entry.prologue_end_ = true; |
||||||
|
break; |
||||||
|
case DBG_SET_EPILOGUE_BEGIN: |
||||||
|
entry.epilogue_begin_ = true; |
||||||
|
break; |
||||||
|
case DBG_SET_FILE: { |
||||||
|
uint32_t name_idx = DecodeUnsignedLeb128P1(&stream); |
||||||
|
entry.source_file_ = index_to_string_data(name_idx); |
||||||
|
break; |
||||||
|
} |
||||||
|
default: { |
||||||
|
int adjopcode = opcode - DBG_FIRST_SPECIAL; |
||||||
|
entry.address_ += adjopcode / DBG_LINE_RANGE; |
||||||
|
entry.line_ += DBG_LINE_BASE + (adjopcode % DBG_LINE_RANGE); |
||||||
|
if (position_functor(context, entry)) { |
||||||
|
return true; // early exit.
|
||||||
|
} |
||||||
|
entry.prologue_end_ = false; |
||||||
|
entry.epilogue_begin_ = false; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
template<typename DexDebugNewPosition> |
||||||
|
bool DexFile::DecodeDebugPositionInfo(uint32_t debug_info_offset, |
||||||
|
DexDebugNewPosition position_functor, |
||||||
|
void* context) const { |
||||||
|
return DecodeDebugPositionInfo(GetDebugInfoStream(debug_info_offset), |
||||||
|
[this](uint32_t idx) { |
||||||
|
return StringDataByIdx(dex::StringIndex(idx)); |
||||||
|
}, |
||||||
|
position_functor, |
||||||
|
context); |
||||||
|
} |
||||||
|
|
||||||
|
inline const CompactDexFile* DexFile::AsCompactDexFile() const { |
||||||
|
DCHECK(IsCompactDexFile()); |
||||||
|
return down_cast<const CompactDexFile*>(this); |
||||||
|
} |
||||||
|
|
||||||
|
inline const StandardDexFile* DexFile::AsStandardDexFile() const { |
||||||
|
DCHECK(IsStandardDexFile()); |
||||||
|
return down_cast<const StandardDexFile*>(this); |
||||||
|
} |
||||||
|
|
||||||
|
// Get the base of the encoded data for the given DexCode.
|
||||||
|
inline const uint8_t* DexFile::GetCatchHandlerData(const DexInstructionIterator& code_item_end, |
||||||
|
uint32_t tries_size, |
||||||
|
uint32_t offset) { |
||||||
|
const uint8_t* handler_data = |
||||||
|
reinterpret_cast<const uint8_t*>(GetTryItems(code_item_end, tries_size)); |
||||||
|
return handler_data + offset; |
||||||
|
} |
||||||
|
|
||||||
|
template <typename Visitor> |
||||||
|
inline void DexFile::ClassDef::VisitMethods(const DexFile* dex_file, const Visitor& visitor) const { |
||||||
|
const uint8_t* class_data = dex_file->GetClassData(*this); |
||||||
|
if (class_data != nullptr) { |
||||||
|
ClassDataItemIterator it(*dex_file, class_data); |
||||||
|
it.SkipAllFields(); |
||||||
|
for (; it.HasNext(); it.Next()) { |
||||||
|
visitor(it); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace art_lkchan
|
||||||
|
|
||||||
|
#endif // ART_LIBDEXFILE_DEX_DEX_FILE_INL_H_
|
@ -0,0 +1,817 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011 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. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include "dex_file.h" |
||||||
|
|
||||||
|
#include <limits.h> |
||||||
|
#include <stdio.h> |
||||||
|
#include <stdlib.h> |
||||||
|
#include <string.h> |
||||||
|
#include <zlib.h> |
||||||
|
|
||||||
|
#include <memory> |
||||||
|
#include <sstream> |
||||||
|
#include <type_traits> |
||||||
|
|
||||||
|
#include "android-base/stringprintf.h" |
||||||
|
|
||||||
|
#include "base/enums.h" |
||||||
|
#include "base/leb128.h" |
||||||
|
#include "base/stl_util.h" |
||||||
|
#include "descriptors_names.h" |
||||||
|
#include "dex_file-inl.h" |
||||||
|
#include "standard_dex_file.h" |
||||||
|
#include "utf-inl.h" |
||||||
|
|
||||||
|
namespace art_lkchan { |
||||||
|
|
||||||
|
using android_lkchan::base::StringPrintf; |
||||||
|
|
||||||
|
static_assert(sizeof(dex::StringIndex) == sizeof(uint32_t), "StringIndex size is wrong"); |
||||||
|
static_assert(std::is_trivially_copyable<dex::StringIndex>::value, "StringIndex not trivial"); |
||||||
|
static_assert(sizeof(dex::TypeIndex) == sizeof(uint16_t), "TypeIndex size is wrong"); |
||||||
|
static_assert(std::is_trivially_copyable<dex::TypeIndex>::value, "TypeIndex not trivial"); |
||||||
|
|
||||||
|
void DexFile::UnHideAccessFlags(ClassDataItemIterator& class_it) { |
||||||
|
uint8_t* data = const_cast<uint8_t*>(class_it.DataPointer()); |
||||||
|
uint32_t new_flag = class_it.GetMemberAccessFlags(); |
||||||
|
bool is_method = class_it.IsAtMethod(); |
||||||
|
// Go back 1 uleb to start.
|
||||||
|
data = ReverseSearchUnsignedLeb128(data); |
||||||
|
if (is_method) { |
||||||
|
// Methods have another uleb field before the access flags
|
||||||
|
data = ReverseSearchUnsignedLeb128(data); |
||||||
|
} |
||||||
|
DCHECK_EQ(HiddenApiAccessFlags::RemoveFromDex(DecodeUnsignedLeb128WithoutMovingCursor(data)), |
||||||
|
new_flag); |
||||||
|
UpdateUnsignedLeb128(data, new_flag); |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t DexFile::CalculateChecksum() const { |
||||||
|
return CalculateChecksum(Begin(), Size()); |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t DexFile::CalculateChecksum(const uint8_t* begin, size_t size) { |
||||||
|
const uint32_t non_sum_bytes = OFFSETOF_MEMBER(DexFile::Header, signature_); |
||||||
|
return ChecksumMemoryRange(begin + non_sum_bytes, size - non_sum_bytes); |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t DexFile::ChecksumMemoryRange(const uint8_t* begin, size_t size) { |
||||||
|
return adler32(adler32(0L, Z_NULL, 0), begin, size); |
||||||
|
} |
||||||
|
|
||||||
|
int DexFile::GetPermissions() const { |
||||||
|
CHECK(container_.get() != nullptr); |
||||||
|
return container_->GetPermissions(); |
||||||
|
} |
||||||
|
|
||||||
|
bool DexFile::IsReadOnly() const { |
||||||
|
CHECK(container_.get() != nullptr); |
||||||
|
return container_->IsReadOnly(); |
||||||
|
} |
||||||
|
|
||||||
|
bool DexFile::EnableWrite() const { |
||||||
|
CHECK(container_.get() != nullptr); |
||||||
|
return container_->EnableWrite(); |
||||||
|
} |
||||||
|
|
||||||
|
bool DexFile::DisableWrite() const { |
||||||
|
CHECK(container_.get() != nullptr); |
||||||
|
return container_->DisableWrite(); |
||||||
|
} |
||||||
|
|
||||||
|
DexFile::DexFile(const uint8_t* base, |
||||||
|
size_t size, |
||||||
|
const uint8_t* data_begin, |
||||||
|
size_t data_size, |
||||||
|
const std::string& location, |
||||||
|
uint32_t location_checksum, |
||||||
|
const OatDexFile* oat_dex_file, |
||||||
|
std::unique_ptr<DexFileContainer> container, |
||||||
|
bool is_compact_dex) |
||||||
|
: begin_(base), |
||||||
|
size_(size), |
||||||
|
data_begin_(data_begin), |
||||||
|
data_size_(data_size), |
||||||
|
location_(location), |
||||||
|
location_checksum_(location_checksum), |
||||||
|
header_(reinterpret_cast<const Header*>(base)), |
||||||
|
string_ids_(reinterpret_cast<const StringId*>(base + header_->string_ids_off_)), |
||||||
|
type_ids_(reinterpret_cast<const TypeId*>(base + header_->type_ids_off_)), |
||||||
|
field_ids_(reinterpret_cast<const FieldId*>(base + header_->field_ids_off_)), |
||||||
|
method_ids_(reinterpret_cast<const MethodId*>(base + header_->method_ids_off_)), |
||||||
|
proto_ids_(reinterpret_cast<const ProtoId*>(base + header_->proto_ids_off_)), |
||||||
|
class_defs_(reinterpret_cast<const ClassDef*>(base + header_->class_defs_off_)), |
||||||
|
method_handles_(nullptr), |
||||||
|
num_method_handles_(0), |
||||||
|
call_site_ids_(nullptr), |
||||||
|
num_call_site_ids_(0), |
||||||
|
oat_dex_file_(oat_dex_file), |
||||||
|
container_(std::move(container)), |
||||||
|
is_compact_dex_(is_compact_dex), |
||||||
|
is_platform_dex_(false) { |
||||||
|
CHECK(begin_ != nullptr) << GetLocation(); |
||||||
|
CHECK_GT(size_, 0U) << GetLocation(); |
||||||
|
// Check base (=header) alignment.
|
||||||
|
// Must be 4-byte aligned to avoid undefined behavior when accessing
|
||||||
|
// any of the sections via a pointer.
|
||||||
|
CHECK_ALIGNED(begin_, alignof(Header)); |
||||||
|
|
||||||
|
InitializeSectionsFromMapList(); |
||||||
|
} |
||||||
|
|
||||||
|
DexFile::~DexFile() { |
||||||
|
// We don't call DeleteGlobalRef on dex_object_ because we're only called by DestroyJavaVM, and
|
||||||
|
// that's only called after DetachCurrentThread, which means there's no JNIEnv. We could
|
||||||
|
// re-attach, but cleaning up these global references is not obviously useful. It's not as if
|
||||||
|
// the global reference table is otherwise empty!
|
||||||
|
} |
||||||
|
|
||||||
|
bool DexFile::Init(std::string* error_msg) { |
||||||
|
if (!CheckMagicAndVersion(error_msg)) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
bool DexFile::CheckMagicAndVersion(std::string* error_msg) const { |
||||||
|
if (!IsMagicValid()) { |
||||||
|
std::ostringstream oss; |
||||||
|
oss << "Unrecognized magic number in " << GetLocation() << ":" |
||||||
|
<< " " << header_->magic_[0] |
||||||
|
<< " " << header_->magic_[1] |
||||||
|
<< " " << header_->magic_[2] |
||||||
|
<< " " << header_->magic_[3]; |
||||||
|
*error_msg = oss.str(); |
||||||
|
return false; |
||||||
|
} |
||||||
|
if (!IsVersionValid()) { |
||||||
|
std::ostringstream oss; |
||||||
|
oss << "Unrecognized version number in " << GetLocation() << ":" |
||||||
|
<< " " << header_->magic_[4] |
||||||
|
<< " " << header_->magic_[5] |
||||||
|
<< " " << header_->magic_[6] |
||||||
|
<< " " << header_->magic_[7]; |
||||||
|
*error_msg = oss.str(); |
||||||
|
return false; |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
void DexFile::InitializeSectionsFromMapList() { |
||||||
|
const MapList* map_list = reinterpret_cast<const MapList*>(DataBegin() + header_->map_off_); |
||||||
|
if (header_->map_off_ == 0 || header_->map_off_ > DataSize()) { |
||||||
|
// Bad offset. The dex file verifier runs after this method and will reject the file.
|
||||||
|
return; |
||||||
|
} |
||||||
|
const size_t count = map_list->size_; |
||||||
|
|
||||||
|
size_t map_limit = header_->map_off_ + count * sizeof(MapItem); |
||||||
|
if (header_->map_off_ >= map_limit || map_limit > DataSize()) { |
||||||
|
// Overflow or out out of bounds. The dex file verifier runs after
|
||||||
|
// this method and will reject the file as it is malformed.
|
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
for (size_t i = 0; i < count; ++i) { |
||||||
|
const MapItem& map_item = map_list->list_[i]; |
||||||
|
if (map_item.type_ == kDexTypeMethodHandleItem) { |
||||||
|
method_handles_ = reinterpret_cast<const MethodHandleItem*>(Begin() + map_item.offset_); |
||||||
|
num_method_handles_ = map_item.size_; |
||||||
|
} else if (map_item.type_ == kDexTypeCallSiteIdItem) { |
||||||
|
call_site_ids_ = reinterpret_cast<const CallSiteIdItem*>(Begin() + map_item.offset_); |
||||||
|
num_call_site_ids_ = map_item.size_; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t DexFile::Header::GetVersion() const { |
||||||
|
const char* version = reinterpret_cast<const char*>(&magic_[kDexMagicSize]); |
||||||
|
return atoi(version); |
||||||
|
} |
||||||
|
|
||||||
|
const DexFile::ClassDef* DexFile::FindClassDef(dex::TypeIndex type_idx) const { |
||||||
|
size_t num_class_defs = NumClassDefs(); |
||||||
|
// Fast path for rare no class defs case.
|
||||||
|
if (num_class_defs == 0) { |
||||||
|
return nullptr; |
||||||
|
} |
||||||
|
for (size_t i = 0; i < num_class_defs; ++i) { |
||||||
|
const ClassDef& class_def = GetClassDef(i); |
||||||
|
if (class_def.class_idx_ == type_idx) { |
||||||
|
return &class_def; |
||||||
|
} |
||||||
|
} |
||||||
|
return nullptr; |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t DexFile::FindCodeItemOffset(const DexFile::ClassDef& class_def, |
||||||
|
uint32_t method_idx) const { |
||||||
|
const uint8_t* class_data = GetClassData(class_def); |
||||||
|
CHECK(class_data != nullptr); |
||||||
|
ClassDataItemIterator it(*this, class_data); |
||||||
|
it.SkipAllFields(); |
||||||
|
while (it.HasNextDirectMethod()) { |
||||||
|
if (it.GetMemberIndex() == method_idx) { |
||||||
|
return it.GetMethodCodeItemOffset(); |
||||||
|
} |
||||||
|
it.Next(); |
||||||
|
} |
||||||
|
while (it.HasNextVirtualMethod()) { |
||||||
|
if (it.GetMemberIndex() == method_idx) { |
||||||
|
return it.GetMethodCodeItemOffset(); |
||||||
|
} |
||||||
|
it.Next(); |
||||||
|
} |
||||||
|
LOG(FATAL) << "Unable to find method " << method_idx; |
||||||
|
UNREACHABLE(); |
||||||
|
} |
||||||
|
|
||||||
|
const DexFile::FieldId* DexFile::FindFieldId(const DexFile::TypeId& declaring_klass, |
||||||
|
const DexFile::StringId& name, |
||||||
|
const DexFile::TypeId& type) const { |
||||||
|
// Binary search MethodIds knowing that they are sorted by class_idx, name_idx then proto_idx
|
||||||
|
const dex::TypeIndex class_idx = GetIndexForTypeId(declaring_klass); |
||||||
|
const dex::StringIndex name_idx = GetIndexForStringId(name); |
||||||
|
const dex::TypeIndex type_idx = GetIndexForTypeId(type); |
||||||
|
int32_t lo = 0; |
||||||
|
int32_t hi = NumFieldIds() - 1; |
||||||
|
while (hi >= lo) { |
||||||
|
int32_t mid = (hi + lo) / 2; |
||||||
|
const DexFile::FieldId& field = GetFieldId(mid); |
||||||
|
if (class_idx > field.class_idx_) { |
||||||
|
lo = mid + 1; |
||||||
|
} else if (class_idx < field.class_idx_) { |
||||||
|
hi = mid - 1; |
||||||
|
} else { |
||||||
|
if (name_idx > field.name_idx_) { |
||||||
|
lo = mid + 1; |
||||||
|
} else if (name_idx < field.name_idx_) { |
||||||
|
hi = mid - 1; |
||||||
|
} else { |
||||||
|
if (type_idx > field.type_idx_) { |
||||||
|
lo = mid + 1; |
||||||
|
} else if (type_idx < field.type_idx_) { |
||||||
|
hi = mid - 1; |
||||||
|
} else { |
||||||
|
return &field; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return nullptr; |
||||||
|
} |
||||||
|
|
||||||
|
const DexFile::MethodId* DexFile::FindMethodId(const DexFile::TypeId& declaring_klass, |
||||||
|
const DexFile::StringId& name, |
||||||
|
const DexFile::ProtoId& signature) const { |
||||||
|
// Binary search MethodIds knowing that they are sorted by class_idx, name_idx then proto_idx
|
||||||
|
const dex::TypeIndex class_idx = GetIndexForTypeId(declaring_klass); |
||||||
|
const dex::StringIndex name_idx = GetIndexForStringId(name); |
||||||
|
const uint16_t proto_idx = GetIndexForProtoId(signature); |
||||||
|
int32_t lo = 0; |
||||||
|
int32_t hi = NumMethodIds() - 1; |
||||||
|
while (hi >= lo) { |
||||||
|
int32_t mid = (hi + lo) / 2; |
||||||
|
const DexFile::MethodId& method = GetMethodId(mid); |
||||||
|
if (class_idx > method.class_idx_) { |
||||||
|
lo = mid + 1; |
||||||
|
} else if (class_idx < method.class_idx_) { |
||||||
|
hi = mid - 1; |
||||||
|
} else { |
||||||
|
if (name_idx > method.name_idx_) { |
||||||
|
lo = mid + 1; |
||||||
|
} else if (name_idx < method.name_idx_) { |
||||||
|
hi = mid - 1; |
||||||
|
} else { |
||||||
|
if (proto_idx > method.proto_idx_) { |
||||||
|
lo = mid + 1; |
||||||
|
} else if (proto_idx < method.proto_idx_) { |
||||||
|
hi = mid - 1; |
||||||
|
} else { |
||||||
|
return &method; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return nullptr; |
||||||
|
} |
||||||
|
|
||||||
|
const DexFile::StringId* DexFile::FindStringId(const char* string) const { |
||||||
|
int32_t lo = 0; |
||||||
|
int32_t hi = NumStringIds() - 1; |
||||||
|
while (hi >= lo) { |
||||||
|
int32_t mid = (hi + lo) / 2; |
||||||
|
const DexFile::StringId& str_id = GetStringId(dex::StringIndex(mid)); |
||||||
|
const char* str = GetStringData(str_id); |
||||||
|
int compare = CompareModifiedUtf8ToModifiedUtf8AsUtf16CodePointValues(string, str); |
||||||
|
if (compare > 0) { |
||||||
|
lo = mid + 1; |
||||||
|
} else if (compare < 0) { |
||||||
|
hi = mid - 1; |
||||||
|
} else { |
||||||
|
return &str_id; |
||||||
|
} |
||||||
|
} |
||||||
|
return nullptr; |
||||||
|
} |
||||||
|
|
||||||
|
const DexFile::TypeId* DexFile::FindTypeId(const char* string) const { |
||||||
|
int32_t lo = 0; |
||||||
|
int32_t hi = NumTypeIds() - 1; |
||||||
|
while (hi >= lo) { |
||||||
|
int32_t mid = (hi + lo) / 2; |
||||||
|
const TypeId& type_id = GetTypeId(dex::TypeIndex(mid)); |
||||||
|
const DexFile::StringId& str_id = GetStringId(type_id.descriptor_idx_); |
||||||
|
const char* str = GetStringData(str_id); |
||||||
|
int compare = CompareModifiedUtf8ToModifiedUtf8AsUtf16CodePointValues(string, str); |
||||||
|
if (compare > 0) { |
||||||
|
lo = mid + 1; |
||||||
|
} else if (compare < 0) { |
||||||
|
hi = mid - 1; |
||||||
|
} else { |
||||||
|
return &type_id; |
||||||
|
} |
||||||
|
} |
||||||
|
return nullptr; |
||||||
|
} |
||||||
|
|
||||||
|
const DexFile::StringId* DexFile::FindStringId(const uint16_t* string, size_t length) const { |
||||||
|
int32_t lo = 0; |
||||||
|
int32_t hi = NumStringIds() - 1; |
||||||
|
while (hi >= lo) { |
||||||
|
int32_t mid = (hi + lo) / 2; |
||||||
|
const DexFile::StringId& str_id = GetStringId(dex::StringIndex(mid)); |
||||||
|
const char* str = GetStringData(str_id); |
||||||
|
int compare = CompareModifiedUtf8ToUtf16AsCodePointValues(str, string, length); |
||||||
|
if (compare > 0) { |
||||||
|
lo = mid + 1; |
||||||
|
} else if (compare < 0) { |
||||||
|
hi = mid - 1; |
||||||
|
} else { |
||||||
|
return &str_id; |
||||||
|
} |
||||||
|
} |
||||||
|
return nullptr; |
||||||
|
} |
||||||
|
|
||||||
|
const DexFile::TypeId* DexFile::FindTypeId(dex::StringIndex string_idx) const { |
||||||
|
int32_t lo = 0; |
||||||
|
int32_t hi = NumTypeIds() - 1; |
||||||
|
while (hi >= lo) { |
||||||
|
int32_t mid = (hi + lo) / 2; |
||||||
|
const TypeId& type_id = GetTypeId(dex::TypeIndex(mid)); |
||||||
|
if (string_idx > type_id.descriptor_idx_) { |
||||||
|
lo = mid + 1; |
||||||
|
} else if (string_idx < type_id.descriptor_idx_) { |
||||||
|
hi = mid - 1; |
||||||
|
} else { |
||||||
|
return &type_id; |
||||||
|
} |
||||||
|
} |
||||||
|
return nullptr; |
||||||
|
} |
||||||
|
|
||||||
|
const DexFile::ProtoId* DexFile::FindProtoId(dex::TypeIndex return_type_idx, |
||||||
|
const dex::TypeIndex* signature_type_idxs, |
||||||
|
uint32_t signature_length) const { |
||||||
|
int32_t lo = 0; |
||||||
|
int32_t hi = NumProtoIds() - 1; |
||||||
|
while (hi >= lo) { |
||||||
|
int32_t mid = (hi + lo) / 2; |
||||||
|
const DexFile::ProtoId& proto = GetProtoId(mid); |
||||||
|
int compare = return_type_idx.index_ - proto.return_type_idx_.index_; |
||||||
|
if (compare == 0) { |
||||||
|
DexFileParameterIterator it(*this, proto); |
||||||
|
size_t i = 0; |
||||||
|
while (it.HasNext() && i < signature_length && compare == 0) { |
||||||
|
compare = signature_type_idxs[i].index_ - it.GetTypeIdx().index_; |
||||||
|
it.Next(); |
||||||
|
i++; |
||||||
|
} |
||||||
|
if (compare == 0) { |
||||||
|
if (it.HasNext()) { |
||||||
|
compare = -1; |
||||||
|
} else if (i < signature_length) { |
||||||
|
compare = 1; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
if (compare > 0) { |
||||||
|
lo = mid + 1; |
||||||
|
} else if (compare < 0) { |
||||||
|
hi = mid - 1; |
||||||
|
} else { |
||||||
|
return &proto; |
||||||
|
} |
||||||
|
} |
||||||
|
return nullptr; |
||||||
|
} |
||||||
|
|
||||||
|
// Given a signature place the type ids into the given vector
|
||||||
|
bool DexFile::CreateTypeList(const StringPiece& signature, |
||||||
|
dex::TypeIndex* return_type_idx, |
||||||
|
std::vector<dex::TypeIndex>* param_type_idxs) const { |
||||||
|
if (signature[0] != '(') { |
||||||
|
return false; |
||||||
|
} |
||||||
|
size_t offset = 1; |
||||||
|
size_t end = signature.size(); |
||||||
|
bool process_return = false; |
||||||
|
while (offset < end) { |
||||||
|
size_t start_offset = offset; |
||||||
|
char c = signature[offset]; |
||||||
|
offset++; |
||||||
|
if (c == ')') { |
||||||
|
process_return = true; |
||||||
|
continue; |
||||||
|
} |
||||||
|
while (c == '[') { // process array prefix
|
||||||
|
if (offset >= end) { // expect some descriptor following [
|
||||||
|
return false; |
||||||
|
} |
||||||
|
c = signature[offset]; |
||||||
|
offset++; |
||||||
|
} |
||||||
|
if (c == 'L') { // process type descriptors
|
||||||
|
do { |
||||||
|
if (offset >= end) { // unexpected early termination of descriptor
|
||||||
|
return false; |
||||||
|
} |
||||||
|
c = signature[offset]; |
||||||
|
offset++; |
||||||
|
} while (c != ';'); |
||||||
|
} |
||||||
|
// TODO: avoid creating a std::string just to get a 0-terminated char array
|
||||||
|
std::string descriptor(signature.data() + start_offset, offset - start_offset); |
||||||
|
const DexFile::TypeId* type_id = FindTypeId(descriptor.c_str()); |
||||||
|
if (type_id == nullptr) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
dex::TypeIndex type_idx = GetIndexForTypeId(*type_id); |
||||||
|
if (!process_return) { |
||||||
|
param_type_idxs->push_back(type_idx); |
||||||
|
} else { |
||||||
|
*return_type_idx = type_idx; |
||||||
|
return offset == end; // return true if the signature had reached a sensible end
|
||||||
|
} |
||||||
|
} |
||||||
|
return false; // failed to correctly parse return type
|
||||||
|
} |
||||||
|
|
||||||
|
const Signature DexFile::CreateSignature(const StringPiece& signature) const { |
||||||
|
dex::TypeIndex return_type_idx; |
||||||
|
std::vector<dex::TypeIndex> param_type_indices; |
||||||
|
bool success = CreateTypeList(signature, &return_type_idx, ¶m_type_indices); |
||||||
|
if (!success) { |
||||||
|
return Signature::NoSignature(); |
||||||
|
} |
||||||
|
const ProtoId* proto_id = FindProtoId(return_type_idx, param_type_indices); |
||||||
|
if (proto_id == nullptr) { |
||||||
|
return Signature::NoSignature(); |
||||||
|
} |
||||||
|
return Signature(this, *proto_id); |
||||||
|
} |
||||||
|
|
||||||
|
int32_t DexFile::FindTryItem(const TryItem* try_items, uint32_t tries_size, uint32_t address) { |
||||||
|
uint32_t min = 0; |
||||||
|
uint32_t max = tries_size; |
||||||
|
while (min < max) { |
||||||
|
const uint32_t mid = (min + max) / 2; |
||||||
|
|
||||||
|
const art_lkchan::DexFile::TryItem& ti = try_items[mid]; |
||||||
|
const uint32_t start = ti.start_addr_; |
||||||
|
const uint32_t end = start + ti.insn_count_; |
||||||
|
|
||||||
|
if (address < start) { |
||||||
|
max = mid; |
||||||
|
} else if (address >= end) { |
||||||
|
min = mid + 1; |
||||||
|
} else { // We have a winner!
|
||||||
|
return mid; |
||||||
|
} |
||||||
|
} |
||||||
|
// No match.
|
||||||
|
return -1; |
||||||
|
} |
||||||
|
|
||||||
|
bool DexFile::LineNumForPcCb(void* raw_context, const PositionInfo& entry) { |
||||||
|
LineNumFromPcContext* context = reinterpret_cast<LineNumFromPcContext*>(raw_context); |
||||||
|
|
||||||
|
// We know that this callback will be called in
|
||||||
|
// ascending address order, so keep going until we find
|
||||||
|
// a match or we've just gone past it.
|
||||||
|
if (entry.address_ > context->address_) { |
||||||
|
// The line number from the previous positions callback
|
||||||
|
// wil be the final result.
|
||||||
|
return true; |
||||||
|
} else { |
||||||
|
context->line_num_ = entry.line_; |
||||||
|
return entry.address_ == context->address_; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Read a signed integer. "zwidth" is the zero-based byte count.
|
||||||
|
int32_t DexFile::ReadSignedInt(const uint8_t* ptr, int zwidth) { |
||||||
|
int32_t val = 0; |
||||||
|
for (int i = zwidth; i >= 0; --i) { |
||||||
|
val = ((uint32_t)val >> 8) | (((int32_t)*ptr++) << 24); |
||||||
|
} |
||||||
|
val >>= (3 - zwidth) * 8; |
||||||
|
return val; |
||||||
|
} |
||||||
|
|
||||||
|
// Read an unsigned integer. "zwidth" is the zero-based byte count,
|
||||||
|
// "fill_on_right" indicates which side we want to zero-fill from.
|
||||||
|
uint32_t DexFile::ReadUnsignedInt(const uint8_t* ptr, int zwidth, bool fill_on_right) { |
||||||
|
uint32_t val = 0; |
||||||
|
for (int i = zwidth; i >= 0; --i) { |
||||||
|
val = (val >> 8) | (((uint32_t)*ptr++) << 24); |
||||||
|
} |
||||||
|
if (!fill_on_right) { |
||||||
|
val >>= (3 - zwidth) * 8; |
||||||
|
} |
||||||
|
return val; |
||||||
|
} |
||||||
|
|
||||||
|
// Read a signed long. "zwidth" is the zero-based byte count.
|
||||||
|
int64_t DexFile::ReadSignedLong(const uint8_t* ptr, int zwidth) { |
||||||
|
int64_t val = 0; |
||||||
|
for (int i = zwidth; i >= 0; --i) { |
||||||
|
val = ((uint64_t)val >> 8) | (((int64_t)*ptr++) << 56); |
||||||
|
} |
||||||
|
val >>= (7 - zwidth) * 8; |
||||||
|
return val; |
||||||
|
} |
||||||
|
|
||||||
|
// Read an unsigned long. "zwidth" is the zero-based byte count,
|
||||||
|
// "fill_on_right" indicates which side we want to zero-fill from.
|
||||||
|
uint64_t DexFile::ReadUnsignedLong(const uint8_t* ptr, int zwidth, bool fill_on_right) { |
||||||
|
uint64_t val = 0; |
||||||
|
for (int i = zwidth; i >= 0; --i) { |
||||||
|
val = (val >> 8) | (((uint64_t)*ptr++) << 56); |
||||||
|
} |
||||||
|
if (!fill_on_right) { |
||||||
|
val >>= (7 - zwidth) * 8; |
||||||
|
} |
||||||
|
return val; |
||||||
|
} |
||||||
|
|
||||||
|
std::string DexFile::PrettyMethod(uint32_t method_idx, bool with_signature) const { |
||||||
|
if (method_idx >= NumMethodIds()) { |
||||||
|
return StringPrintf("<<invalid-method-idx-%d>>", method_idx); |
||||||
|
} |
||||||
|
const DexFile::MethodId& method_id = GetMethodId(method_idx); |
||||||
|
std::string result; |
||||||
|
const DexFile::ProtoId* proto_id = with_signature ? &GetProtoId(method_id.proto_idx_) : nullptr; |
||||||
|
if (with_signature) { |
||||||
|
AppendPrettyDescriptor(StringByTypeIdx(proto_id->return_type_idx_), &result); |
||||||
|
result += ' '; |
||||||
|
} |
||||||
|
AppendPrettyDescriptor(GetMethodDeclaringClassDescriptor(method_id), &result); |
||||||
|
result += '.'; |
||||||
|
result += GetMethodName(method_id); |
||||||
|
if (with_signature) { |
||||||
|
result += '('; |
||||||
|
const DexFile::TypeList* params = GetProtoParameters(*proto_id); |
||||||
|
if (params != nullptr) { |
||||||
|
const char* separator = ""; |
||||||
|
for (uint32_t i = 0u, size = params->Size(); i != size; ++i) { |
||||||
|
result += separator; |
||||||
|
separator = ", "; |
||||||
|
AppendPrettyDescriptor(StringByTypeIdx(params->GetTypeItem(i).type_idx_), &result); |
||||||
|
} |
||||||
|
} |
||||||
|
result += ')'; |
||||||
|
} |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
std::string DexFile::PrettyField(uint32_t field_idx, bool with_type) const { |
||||||
|
if (field_idx >= NumFieldIds()) { |
||||||
|
return StringPrintf("<<invalid-field-idx-%d>>", field_idx); |
||||||
|
} |
||||||
|
const DexFile::FieldId& field_id = GetFieldId(field_idx); |
||||||
|
std::string result; |
||||||
|
if (with_type) { |
||||||
|
result += GetFieldTypeDescriptor(field_id); |
||||||
|
result += ' '; |
||||||
|
} |
||||||
|
AppendPrettyDescriptor(GetFieldDeclaringClassDescriptor(field_id), &result); |
||||||
|
result += '.'; |
||||||
|
result += GetFieldName(field_id); |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
std::string DexFile::PrettyType(dex::TypeIndex type_idx) const { |
||||||
|
if (type_idx.index_ >= NumTypeIds()) { |
||||||
|
return StringPrintf("<<invalid-type-idx-%d>>", type_idx.index_); |
||||||
|
} |
||||||
|
const DexFile::TypeId& type_id = GetTypeId(type_idx); |
||||||
|
return PrettyDescriptor(GetTypeDescriptor(type_id)); |
||||||
|
} |
||||||
|
|
||||||
|
// Checks that visibility is as expected. Includes special behavior for M and
|
||||||
|
// before to allow runtime and build visibility when expecting runtime.
|
||||||
|
std::ostream& operator<<(std::ostream& os, const DexFile& dex_file) { |
||||||
|
os << StringPrintf("[DexFile: %s dex-checksum=%08x location-checksum=%08x %p-%p]", |
||||||
|
dex_file.GetLocation().c_str(), |
||||||
|
dex_file.GetHeader().checksum_, dex_file.GetLocationChecksum(), |
||||||
|
dex_file.Begin(), dex_file.Begin() + dex_file.Size()); |
||||||
|
return os; |
||||||
|
} |
||||||
|
|
||||||
|
std::string Signature::ToString() const { |
||||||
|
if (dex_file_ == nullptr) { |
||||||
|
CHECK(proto_id_ == nullptr); |
||||||
|
return "<no signature>"; |
||||||
|
} |
||||||
|
const DexFile::TypeList* params = dex_file_->GetProtoParameters(*proto_id_); |
||||||
|
std::string result; |
||||||
|
if (params == nullptr) { |
||||||
|
result += "()"; |
||||||
|
} else { |
||||||
|
result += "("; |
||||||
|
for (uint32_t i = 0; i < params->Size(); ++i) { |
||||||
|
result += dex_file_->StringByTypeIdx(params->GetTypeItem(i).type_idx_); |
||||||
|
} |
||||||
|
result += ")"; |
||||||
|
} |
||||||
|
result += dex_file_->StringByTypeIdx(proto_id_->return_type_idx_); |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t Signature::GetNumberOfParameters() const { |
||||||
|
const DexFile::TypeList* params = dex_file_->GetProtoParameters(*proto_id_); |
||||||
|
return (params != nullptr) ? params->Size() : 0; |
||||||
|
} |
||||||
|
|
||||||
|
bool Signature::IsVoid() const { |
||||||
|
const char* return_type = dex_file_->GetReturnTypeDescriptor(*proto_id_); |
||||||
|
return strcmp(return_type, "V") == 0; |
||||||
|
} |
||||||
|
|
||||||
|
bool Signature::operator==(const StringPiece& rhs) const { |
||||||
|
if (dex_file_ == nullptr) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
StringPiece tail(rhs); |
||||||
|
if (!tail.starts_with("(")) { |
||||||
|
return false; // Invalid signature
|
||||||
|
} |
||||||
|
tail.remove_prefix(1); // "(";
|
||||||
|
const DexFile::TypeList* params = dex_file_->GetProtoParameters(*proto_id_); |
||||||
|
if (params != nullptr) { |
||||||
|
for (uint32_t i = 0; i < params->Size(); ++i) { |
||||||
|
StringPiece param(dex_file_->StringByTypeIdx(params->GetTypeItem(i).type_idx_)); |
||||||
|
if (!tail.starts_with(param)) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
tail.remove_prefix(param.length()); |
||||||
|
} |
||||||
|
} |
||||||
|
if (!tail.starts_with(")")) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
tail.remove_prefix(1); // ")";
|
||||||
|
return tail == dex_file_->StringByTypeIdx(proto_id_->return_type_idx_); |
||||||
|
} |
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& os, const Signature& sig) { |
||||||
|
return os << sig.ToString(); |
||||||
|
} |
||||||
|
std::ostream& operator<<(std::ostream& os, const EncodedStaticFieldValueIterator::ValueType& code) |
||||||
|
{ |
||||||
|
//chensenhua add
|
||||||
|
return os << EncodedStaticFieldValueIterator::ValueType(code); |
||||||
|
} |
||||||
|
//chensenhua end
|
||||||
|
|
||||||
|
// Decodes the header section from the class data bytes.
|
||||||
|
void ClassDataItemIterator::ReadClassDataHeader() { |
||||||
|
CHECK(ptr_pos_ != nullptr); |
||||||
|
header_.static_fields_size_ = DecodeUnsignedLeb128(&ptr_pos_); |
||||||
|
header_.instance_fields_size_ = DecodeUnsignedLeb128(&ptr_pos_); |
||||||
|
header_.direct_methods_size_ = DecodeUnsignedLeb128(&ptr_pos_); |
||||||
|
header_.virtual_methods_size_ = DecodeUnsignedLeb128(&ptr_pos_); |
||||||
|
} |
||||||
|
|
||||||
|
void ClassDataItemIterator::ReadClassDataField() { |
||||||
|
field_.field_idx_delta_ = DecodeUnsignedLeb128(&ptr_pos_); |
||||||
|
field_.access_flags_ = DecodeUnsignedLeb128(&ptr_pos_); |
||||||
|
// The user of the iterator is responsible for checking if there
|
||||||
|
// are unordered or duplicate indexes.
|
||||||
|
} |
||||||
|
|
||||||
|
void ClassDataItemIterator::ReadClassDataMethod() { |
||||||
|
method_.method_idx_delta_ = DecodeUnsignedLeb128(&ptr_pos_); |
||||||
|
method_.access_flags_ = DecodeUnsignedLeb128(&ptr_pos_); |
||||||
|
method_.code_off_ = DecodeUnsignedLeb128(&ptr_pos_); |
||||||
|
if (last_idx_ != 0 && method_.method_idx_delta_ == 0) { |
||||||
|
LOG(WARNING) << "Duplicate method in " << dex_file_.GetLocation(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
EncodedArrayValueIterator::EncodedArrayValueIterator(const DexFile& dex_file, |
||||||
|
const uint8_t* array_data) |
||||||
|
: dex_file_(dex_file), |
||||||
|
array_size_(), |
||||||
|
pos_(-1), |
||||||
|
ptr_(array_data), |
||||||
|
type_(kByte) { |
||||||
|
array_size_ = (ptr_ != nullptr) ? DecodeUnsignedLeb128(&ptr_) : 0; |
||||||
|
if (array_size_ > 0) { |
||||||
|
Next(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void EncodedArrayValueIterator::Next() { |
||||||
|
pos_++; |
||||||
|
if (pos_ >= array_size_) { |
||||||
|
return; |
||||||
|
} |
||||||
|
uint8_t value_type = *ptr_++; |
||||||
|
uint8_t value_arg = value_type >> kEncodedValueArgShift; |
||||||
|
size_t width = value_arg + 1; // assume and correct later
|
||||||
|
type_ = static_cast<ValueType>(value_type & kEncodedValueTypeMask); |
||||||
|
switch (type_) { |
||||||
|
case kBoolean: |
||||||
|
jval_.i = (value_arg != 0) ? 1 : 0; |
||||||
|
width = 0; |
||||||
|
break; |
||||||
|
case kByte: |
||||||
|
jval_.i = DexFile::ReadSignedInt(ptr_, value_arg); |
||||||
|
CHECK(IsInt<8>(jval_.i)); |
||||||
|
break; |
||||||
|
case kShort: |
||||||
|
jval_.i = DexFile::ReadSignedInt(ptr_, value_arg); |
||||||
|
CHECK(IsInt<16>(jval_.i)); |
||||||
|
break; |
||||||
|
case kChar: |
||||||
|
jval_.i = DexFile::ReadUnsignedInt(ptr_, value_arg, false); |
||||||
|
CHECK(IsUint<16>(jval_.i)); |
||||||
|
break; |
||||||
|
case kInt: |
||||||
|
jval_.i = DexFile::ReadSignedInt(ptr_, value_arg); |
||||||
|
break; |
||||||
|
case kLong: |
||||||
|
jval_.j = DexFile::ReadSignedLong(ptr_, value_arg); |
||||||
|
break; |
||||||
|
case kFloat: |
||||||
|
jval_.i = DexFile::ReadUnsignedInt(ptr_, value_arg, true); |
||||||
|
break; |
||||||
|
case kDouble: |
||||||
|
jval_.j = DexFile::ReadUnsignedLong(ptr_, value_arg, true); |
||||||
|
break; |
||||||
|
case kString: |
||||||
|
case kType: |
||||||
|
case kMethodType: |
||||||
|
case kMethodHandle: |
||||||
|
jval_.i = DexFile::ReadUnsignedInt(ptr_, value_arg, false); |
||||||
|
break; |
||||||
|
case kField: |
||||||
|
case kMethod: |
||||||
|
case kEnum: |
||||||
|
case kArray: |
||||||
|
case kAnnotation: |
||||||
|
UNIMPLEMENTED(FATAL) << ": type " << type_; |
||||||
|
UNREACHABLE(); |
||||||
|
case kNull: |
||||||
|
jval_.l = nullptr; |
||||||
|
width = 0; |
||||||
|
break; |
||||||
|
default: |
||||||
|
LOG(FATAL) << "Unreached"; |
||||||
|
UNREACHABLE(); |
||||||
|
} |
||||||
|
ptr_ += width; |
||||||
|
} |
||||||
|
|
||||||
|
namespace dex { |
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& os, const StringIndex& index) { |
||||||
|
os << "StringIndex[" << index.index_ << "]"; |
||||||
|
return os; |
||||||
|
} |
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& os, const TypeIndex& index) { |
||||||
|
os << "TypeIndex[" << index.index_ << "]"; |
||||||
|
return os; |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace dex
|
||||||
|
|
||||||
|
} // namespace art_lkchan
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,104 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2017 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. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include "dex_file_exception_helpers.h" |
||||||
|
|
||||||
|
#include "code_item_accessors-inl.h" |
||||||
|
|
||||||
|
namespace art_lkchan { |
||||||
|
|
||||||
|
CatchHandlerIterator::CatchHandlerIterator(const CodeItemDataAccessor& accessor, uint32_t address) { |
||||||
|
handler_.address_ = -1; |
||||||
|
int32_t offset = -1; |
||||||
|
|
||||||
|
// Short-circuit the overwhelmingly common cases.
|
||||||
|
switch (accessor.TriesSize()) { |
||||||
|
case 0: |
||||||
|
break; |
||||||
|
case 1: { |
||||||
|
const DexFile::TryItem* tries = accessor.TryItems().begin(); |
||||||
|
uint32_t start = tries->start_addr_; |
||||||
|
if (address >= start) { |
||||||
|
uint32_t end = start + tries->insn_count_; |
||||||
|
if (address < end) { |
||||||
|
offset = tries->handler_off_; |
||||||
|
} |
||||||
|
} |
||||||
|
break; |
||||||
|
} |
||||||
|
default: { |
||||||
|
const DexFile::TryItem* try_item = accessor.FindTryItem(address); |
||||||
|
offset = try_item != nullptr ? try_item->handler_off_ : -1; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
Init(accessor, offset); |
||||||
|
} |
||||||
|
|
||||||
|
CatchHandlerIterator::CatchHandlerIterator(const CodeItemDataAccessor& accessor, |
||||||
|
const DexFile::TryItem& try_item) { |
||||||
|
handler_.address_ = -1; |
||||||
|
Init(accessor, try_item.handler_off_); |
||||||
|
} |
||||||
|
|
||||||
|
void CatchHandlerIterator::Init(const CodeItemDataAccessor& accessor, int32_t offset) { |
||||||
|
if (offset >= 0) { |
||||||
|
Init(accessor.GetCatchHandlerData(offset)); |
||||||
|
} else { |
||||||
|
// Not found, initialize as empty
|
||||||
|
current_data_ = nullptr; |
||||||
|
remaining_count_ = -1; |
||||||
|
catch_all_ = false; |
||||||
|
DCHECK(!HasNext()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void CatchHandlerIterator::Init(const uint8_t* handler_data) { |
||||||
|
current_data_ = handler_data; |
||||||
|
remaining_count_ = DecodeSignedLeb128(¤t_data_); |
||||||
|
|
||||||
|
// If remaining_count_ is non-positive, then it is the negative of
|
||||||
|
// the number of catch types, and the catches are followed by a
|
||||||
|
// catch-all handler.
|
||||||
|
if (remaining_count_ <= 0) { |
||||||
|
catch_all_ = true; |
||||||
|
remaining_count_ = -remaining_count_; |
||||||
|
} else { |
||||||
|
catch_all_ = false; |
||||||
|
} |
||||||
|
Next(); |
||||||
|
} |
||||||
|
|
||||||
|
void CatchHandlerIterator::Next() { |
||||||
|
if (remaining_count_ > 0) { |
||||||
|
handler_.type_idx_ = dex::TypeIndex(DecodeUnsignedLeb128(¤t_data_)); |
||||||
|
handler_.address_ = DecodeUnsignedLeb128(¤t_data_); |
||||||
|
remaining_count_--; |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
if (catch_all_) { |
||||||
|
handler_.type_idx_ = dex::TypeIndex(DexFile::kDexNoIndex16); |
||||||
|
handler_.address_ = DecodeUnsignedLeb128(¤t_data_); |
||||||
|
catch_all_ = false; |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
// no more handler
|
||||||
|
remaining_count_ = -1; |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace art_lkchan
|
@ -0,0 +1,68 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2017 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_DEX_FILE_EXCEPTION_HELPERS_H_ |
||||||
|
#define ART_LIBDEXFILE_DEX_DEX_FILE_EXCEPTION_HELPERS_H_ |
||||||
|
|
||||||
|
#include "dex_file.h" |
||||||
|
|
||||||
|
namespace art_lkchan { |
||||||
|
|
||||||
|
class CodeItemDataAccessor; |
||||||
|
|
||||||
|
class CatchHandlerIterator { |
||||||
|
public: |
||||||
|
CatchHandlerIterator(const CodeItemDataAccessor& accessor, uint32_t address); |
||||||
|
|
||||||
|
CatchHandlerIterator(const CodeItemDataAccessor& accessor, const DexFile::TryItem& try_item); |
||||||
|
|
||||||
|
explicit CatchHandlerIterator(const uint8_t* handler_data) { |
||||||
|
Init(handler_data); |
||||||
|
} |
||||||
|
|
||||||
|
dex::TypeIndex GetHandlerTypeIndex() const { |
||||||
|
return handler_.type_idx_; |
||||||
|
} |
||||||
|
uint32_t GetHandlerAddress() const { |
||||||
|
return handler_.address_; |
||||||
|
} |
||||||
|
void Next(); |
||||||
|
bool HasNext() const { |
||||||
|
return remaining_count_ != -1 || catch_all_; |
||||||
|
} |
||||||
|
// End of this set of catch blocks, convenience method to locate next set of catch blocks
|
||||||
|
const uint8_t* EndDataPointer() const { |
||||||
|
CHECK(!HasNext()); |
||||||
|
return current_data_; |
||||||
|
} |
||||||
|
|
||||||
|
private: |
||||||
|
void Init(const CodeItemDataAccessor& accessor, int32_t offset); |
||||||
|
void Init(const uint8_t* handler_data); |
||||||
|
|
||||||
|
struct CatchHandlerItem { |
||||||
|
dex::TypeIndex type_idx_; // type index of the caught exception type
|
||||||
|
uint32_t address_; // handler address
|
||||||
|
} handler_; |
||||||
|
const uint8_t* current_data_; // the current handler in dex file.
|
||||||
|
int32_t remaining_count_; // number of handlers not read.
|
||||||
|
bool catch_all_; // is there a handler that will catch all exceptions in case
|
||||||
|
// that all typed handler does not match.
|
||||||
|
}; |
||||||
|
|
||||||
|
} // namespace art_lkchan
|
||||||
|
|
||||||
|
#endif // ART_LIBDEXFILE_DEX_DEX_FILE_EXCEPTION_HELPERS_H_
|
@ -0,0 +1,107 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2017 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. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include "dex_file_layout.h" |
||||||
|
|
||||||
|
#include <sys/mman.h> |
||||||
|
|
||||||
|
#include "dex_file.h" |
||||||
|
|
||||||
|
namespace art_lkchan { |
||||||
|
|
||||||
|
int DexLayoutSection::MadviseLargestPageAlignedRegion(const uint8_t* begin, |
||||||
|
const uint8_t* end, |
||||||
|
int advice) { |
||||||
|
DCHECK_LE(begin, end); |
||||||
|
begin = AlignUp(begin, kPageSize); |
||||||
|
end = AlignDown(end, kPageSize); |
||||||
|
if (begin < end) { |
||||||
|
// TODO: remove the direct dependency on madvise here.
|
||||||
|
int result = madvise(const_cast<uint8_t*>(begin), end - begin, advice); |
||||||
|
if (result != 0) { |
||||||
|
PLOG(WARNING) << "madvise failed " << result; |
||||||
|
} |
||||||
|
return result; |
||||||
|
} |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
void DexLayoutSection::Subsection::Madvise(const DexFile* dex_file, int advice) const { |
||||||
|
DCHECK(dex_file != nullptr); |
||||||
|
DCHECK_LT(start_offset_, dex_file->Size()); |
||||||
|
DCHECK_LE(end_offset_, dex_file->Size()); |
||||||
|
MadviseLargestPageAlignedRegion(dex_file->Begin() + start_offset_, |
||||||
|
dex_file->Begin() + end_offset_, |
||||||
|
advice); |
||||||
|
} |
||||||
|
|
||||||
|
void DexLayoutSections::Madvise(const DexFile* dex_file, MadviseState state) const { |
||||||
|
// The dex file is already defaulted to random access everywhere.
|
||||||
|
for (const DexLayoutSection& section : sections_) { |
||||||
|
switch (state) { |
||||||
|
case MadviseState::kMadviseStateAtLoad: { |
||||||
|
section.parts_[static_cast<size_t>(LayoutType::kLayoutTypeStartupOnly)].Madvise( |
||||||
|
dex_file, |
||||||
|
MADV_WILLNEED); |
||||||
|
section.parts_[static_cast<size_t>(LayoutType::kLayoutTypeHot)].Madvise( |
||||||
|
dex_file, |
||||||
|
MADV_WILLNEED); |
||||||
|
break; |
||||||
|
} |
||||||
|
case MadviseState::kMadviseStateFinishedLaunch: { |
||||||
|
section.parts_[static_cast<size_t>(LayoutType::kLayoutTypeStartupOnly)].Madvise( |
||||||
|
dex_file, |
||||||
|
MADV_DONTNEED); |
||||||
|
break; |
||||||
|
} |
||||||
|
case MadviseState::kMadviseStateFinishedTrim: { |
||||||
|
section.parts_[static_cast<size_t>(LayoutType::kLayoutTypeSometimesUsed)].Madvise( |
||||||
|
dex_file, |
||||||
|
MADV_DONTNEED); |
||||||
|
section.parts_[static_cast<size_t>(LayoutType::kLayoutTypeUsedOnce)].Madvise( |
||||||
|
dex_file, |
||||||
|
MADV_DONTNEED); |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& os, const DexLayoutSection& section) { |
||||||
|
for (size_t i = 0; i < static_cast<size_t>(LayoutType::kLayoutTypeCount); ++i) { |
||||||
|
const DexLayoutSection::Subsection& part = section.parts_[i]; |
||||||
|
os << static_cast<LayoutType>(i) << "(" |
||||||
|
<< part.start_offset_ << "-" << part.end_offset_ << ") "; |
||||||
|
} |
||||||
|
return os; |
||||||
|
} |
||||||
|
//chensenhua add
|
||||||
|
std::ostream& operator<<(std::ostream& os, const DexLayoutSections::SectionType& collector_type){ |
||||||
|
return os<<DexLayoutSections::SectionType (collector_type); |
||||||
|
} |
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& os, const LayoutType& collector_type){ |
||||||
|
return os << LayoutType(collector_type); |
||||||
|
} |
||||||
|
//chensenhua end
|
||||||
|
std::ostream& operator<<(std::ostream& os, const DexLayoutSections& sections) { |
||||||
|
for (size_t i = 0; i < static_cast<size_t>(DexLayoutSections::SectionType::kSectionCount); ++i) { |
||||||
|
os << static_cast<DexLayoutSections::SectionType>(i) << ":" << sections.sections_[i] << "\n"; |
||||||
|
} |
||||||
|
return os; |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace art_lkchan
|
@ -0,0 +1,127 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2017 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_DEX_FILE_LAYOUT_H_ |
||||||
|
#define ART_LIBDEXFILE_DEX_DEX_FILE_LAYOUT_H_ |
||||||
|
|
||||||
|
#include <algorithm> |
||||||
|
#include <cstdint> |
||||||
|
#include <iosfwd> |
||||||
|
|
||||||
|
#include <android-base/logging.h> |
||||||
|
|
||||||
|
namespace art_lkchan { |
||||||
|
|
||||||
|
class DexFile; |
||||||
|
|
||||||
|
enum class LayoutType : uint8_t { |
||||||
|
// Layout of things that are hot (commonly accessed), these should be pinned or madvised will
|
||||||
|
// need.
|
||||||
|
kLayoutTypeHot, |
||||||
|
// Layout of things that are randomly used. These should be advised to random access.
|
||||||
|
// Without layout, this is the default mode when loading a dex file.
|
||||||
|
kLayoutTypeSometimesUsed, |
||||||
|
// Layout of things that are only used during startup, these can be madvised after launch.
|
||||||
|
kLayoutTypeStartupOnly, |
||||||
|
// Layout of things that are needed probably only once (class initializers). These can be
|
||||||
|
// madvised during trim events.
|
||||||
|
kLayoutTypeUsedOnce, |
||||||
|
// Layout of things that are thought to be unused. These things should be advised to random
|
||||||
|
// access.
|
||||||
|
kLayoutTypeUnused, |
||||||
|
// Unused value, just the number of elements in the enum.
|
||||||
|
kLayoutTypeCount, |
||||||
|
}; |
||||||
|
std::ostream& operator<<(std::ostream& os, const LayoutType& collector_type); |
||||||
|
|
||||||
|
// Return the "best" layout option if the same item has multiple different layouts.
|
||||||
|
static inline LayoutType MergeLayoutType(LayoutType a, LayoutType b) { |
||||||
|
return std::min(a, b); |
||||||
|
} |
||||||
|
|
||||||
|
enum class MadviseState : uint8_t { |
||||||
|
// Madvise based on a file that was just loaded.
|
||||||
|
kMadviseStateAtLoad, |
||||||
|
// Madvise based after launch is finished.
|
||||||
|
kMadviseStateFinishedLaunch, |
||||||
|
// Trim by madvising code that is unlikely to be too important in the future.
|
||||||
|
kMadviseStateFinishedTrim, |
||||||
|
}; |
||||||
|
std::ostream& operator<<(std::ostream& os, const MadviseState& collector_type); |
||||||
|
|
||||||
|
// A dex layout section such as code items or strings. Each section is composed of subsections
|
||||||
|
// that are laid out adjacently to each other such as (hot, unused, startup, etc...).
|
||||||
|
class DexLayoutSection { |
||||||
|
public: |
||||||
|
// A subsection is a a continuous range of dex file that is all part of the same layout hint.
|
||||||
|
class Subsection { |
||||||
|
public: |
||||||
|
// Use uint32_t to handle 32/64 bit cross compilation.
|
||||||
|
uint32_t start_offset_ = 0u; |
||||||
|
uint32_t end_offset_ = 0u; |
||||||
|
|
||||||
|
bool Contains(uint32_t offset) const { |
||||||
|
return start_offset_ <= offset && offset < end_offset_; |
||||||
|
} |
||||||
|
|
||||||
|
bool Size() const { |
||||||
|
DCHECK_LE(start_offset_, end_offset_); |
||||||
|
return end_offset_ - start_offset_; |
||||||
|
} |
||||||
|
|
||||||
|
void CombineSection(uint32_t start_offset, uint32_t end_offset) { |
||||||
|
DCHECK_LE(start_offset, end_offset); |
||||||
|
if (start_offset_ == end_offset_) { |
||||||
|
start_offset_ = start_offset; |
||||||
|
end_offset_ = end_offset; |
||||||
|
} else { |
||||||
|
start_offset_ = std::min(start_offset_, start_offset); |
||||||
|
end_offset_ = std::max(end_offset_, end_offset); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void Madvise(const DexFile* dex_file, int advice) const; |
||||||
|
}; |
||||||
|
|
||||||
|
// Madvise the largest page-aligned region contained in [begin, end).
|
||||||
|
static int MadviseLargestPageAlignedRegion(const uint8_t* begin, const uint8_t* end, int advice); |
||||||
|
|
||||||
|
Subsection parts_[static_cast<size_t>(LayoutType::kLayoutTypeCount)]; |
||||||
|
}; |
||||||
|
|
||||||
|
// A set of dex layout sections, currently there is only one section for code and one for strings.
|
||||||
|
class DexLayoutSections { |
||||||
|
public: |
||||||
|
enum class SectionType : uint8_t { |
||||||
|
kSectionTypeCode, |
||||||
|
kSectionTypeStrings, |
||||||
|
kSectionCount, |
||||||
|
}; |
||||||
|
|
||||||
|
// Advise access about the dex file based on layout. The caller is expected to have already
|
||||||
|
// madvised to MADV_RANDOM.
|
||||||
|
void Madvise(const DexFile* dex_file, MadviseState state) const; |
||||||
|
|
||||||
|
DexLayoutSection sections_[static_cast<size_t>(SectionType::kSectionCount)]; |
||||||
|
}; |
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& os, const DexLayoutSections::SectionType& collector_type); |
||||||
|
std::ostream& operator<<(std::ostream& os, const DexLayoutSection& section); |
||||||
|
std::ostream& operator<<(std::ostream& os, const DexLayoutSections& sections); |
||||||
|
|
||||||
|
} // namespace art_lkchan
|
||||||
|
|
||||||
|
#endif // ART_LIBDEXFILE_DEX_DEX_FILE_LAYOUT_H_
|
@ -0,0 +1,508 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2017 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. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include "dex_file_loader.h" |
||||||
|
|
||||||
|
#include "android-base/stringprintf.h" |
||||||
|
|
||||||
|
#include "base/stl_util.h" |
||||||
|
#include "compact_dex_file.h" |
||||||
|
#include "dex_file.h" |
||||||
|
#include "dex_file_verifier.h" |
||||||
|
#include "standard_dex_file.h" |
||||||
|
#include "ziparchive/zip_archive.h" |
||||||
|
|
||||||
|
// system/core/zip_archive definitions.
|
||||||
|
struct ZipEntry; |
||||||
|
typedef void* ZipArchiveHandle; |
||||||
|
|
||||||
|
namespace art_lkchan { |
||||||
|
|
||||||
|
namespace { |
||||||
|
|
||||||
|
class VectorContainer : public DexFileContainer { |
||||||
|
public: |
||||||
|
explicit VectorContainer(std::vector<uint8_t>&& vector) : vector_(std::move(vector)) { } |
||||||
|
virtual ~VectorContainer() OVERRIDE { } |
||||||
|
|
||||||
|
int GetPermissions() OVERRIDE { |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
bool IsReadOnly() OVERRIDE { |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
bool EnableWrite() OVERRIDE { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
bool DisableWrite() OVERRIDE { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
private: |
||||||
|
std::vector<uint8_t> vector_; |
||||||
|
DISALLOW_COPY_AND_ASSIGN(VectorContainer); |
||||||
|
}; |
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
using android_lkchan::base::StringPrintf; |
||||||
|
|
||||||
|
class DexZipArchive; |
||||||
|
|
||||||
|
class DexZipEntry { |
||||||
|
public: |
||||||
|
// Extract this entry to memory.
|
||||||
|
// Returns null on failure and sets error_msg.
|
||||||
|
const std::vector<uint8_t> Extract(std::string* error_msg) { |
||||||
|
std::vector<uint8_t> map(GetUncompressedLength()); |
||||||
|
if (map.size() == 0) { |
||||||
|
DCHECK(!error_msg->empty()); |
||||||
|
return map; |
||||||
|
} |
||||||
|
const int32_t error = ExtractToMemory(handle_, zip_entry_, map.data(), map.size()); |
||||||
|
if (error) { |
||||||
|
*error_msg = std::string(ErrorCodeString(error)); |
||||||
|
} |
||||||
|
return map; |
||||||
|
} |
||||||
|
|
||||||
|
virtual ~DexZipEntry() { |
||||||
|
delete zip_entry_; |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t GetUncompressedLength() { |
||||||
|
return zip_entry_->uncompressed_length; |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t GetCrc32() { |
||||||
|
return zip_entry_->crc32; |
||||||
|
} |
||||||
|
|
||||||
|
private: |
||||||
|
DexZipEntry(ZipArchiveHandle handle, |
||||||
|
::ZipEntry* zip_entry, |
||||||
|
const std::string& entry_name) |
||||||
|
: handle_(handle), zip_entry_(zip_entry), entry_name_(entry_name) {} |
||||||
|
|
||||||
|
ZipArchiveHandle handle_; |
||||||
|
::ZipEntry* const zip_entry_; |
||||||
|
std::string const entry_name_; |
||||||
|
|
||||||
|
friend class DexZipArchive; |
||||||
|
DISALLOW_COPY_AND_ASSIGN(DexZipEntry); |
||||||
|
}; |
||||||
|
|
||||||
|
class DexZipArchive { |
||||||
|
public: |
||||||
|
// return new DexZipArchive instance on success, null on error.
|
||||||
|
static DexZipArchive* Open(const uint8_t* base, size_t size, std::string* error_msg) { |
||||||
|
ZipArchiveHandle handle; |
||||||
|
uint8_t* nonconst_base = const_cast<uint8_t*>(base); |
||||||
|
const int32_t error = OpenArchiveFromMemory(nonconst_base, size, "ZipArchiveMemory", &handle); |
||||||
|
if (error) { |
||||||
|
*error_msg = std::string(ErrorCodeString(error)); |
||||||
|
CloseArchive(handle); |
||||||
|
return nullptr; |
||||||
|
} |
||||||
|
return new DexZipArchive(handle); |
||||||
|
} |
||||||
|
|
||||||
|
DexZipEntry* Find(const char* name, std::string* error_msg) const { |
||||||
|
DCHECK(name != nullptr); |
||||||
|
// Resist the urge to delete the space. <: is a bigraph sequence.
|
||||||
|
std::unique_ptr< ::ZipEntry> zip_entry(new ::ZipEntry); |
||||||
|
const int32_t error = FindEntry(handle_, ZipString(name), zip_entry.get()); |
||||||
|
if (error) { |
||||||
|
*error_msg = std::string(ErrorCodeString(error)); |
||||||
|
return nullptr; |
||||||
|
} |
||||||
|
return new DexZipEntry(handle_, zip_entry.release(), name); |
||||||
|
} |
||||||
|
|
||||||
|
~DexZipArchive() { |
||||||
|
CloseArchive(handle_); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
private: |
||||||
|
explicit DexZipArchive(ZipArchiveHandle handle) : handle_(handle) {} |
||||||
|
ZipArchiveHandle handle_; |
||||||
|
|
||||||
|
friend class DexZipEntry; |
||||||
|
DISALLOW_COPY_AND_ASSIGN(DexZipArchive); |
||||||
|
}; |
||||||
|
|
||||||
|
static bool IsZipMagic(uint32_t magic) { |
||||||
|
return (('P' == ((magic >> 0) & 0xff)) && |
||||||
|
('K' == ((magic >> 8) & 0xff))); |
||||||
|
} |
||||||
|
|
||||||
|
bool DexFileLoader::IsMagicValid(uint32_t magic) { |
||||||
|
return IsMagicValid(reinterpret_cast<uint8_t*>(&magic)); |
||||||
|
} |
||||||
|
|
||||||
|
bool DexFileLoader::IsMagicValid(const uint8_t* magic) { |
||||||
|
return StandardDexFile::IsMagicValid(magic) || |
||||||
|
CompactDexFile::IsMagicValid(magic); |
||||||
|
} |
||||||
|
|
||||||
|
bool DexFileLoader::IsVersionAndMagicValid(const uint8_t* magic) { |
||||||
|
if (StandardDexFile::IsMagicValid(magic)) { |
||||||
|
return StandardDexFile::IsVersionValid(magic); |
||||||
|
} |
||||||
|
if (CompactDexFile::IsMagicValid(magic)) { |
||||||
|
return CompactDexFile::IsVersionValid(magic); |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
bool DexFileLoader::IsMultiDexLocation(const char* location) { |
||||||
|
return strrchr(location, kMultiDexSeparator) != nullptr; |
||||||
|
} |
||||||
|
|
||||||
|
std::string DexFileLoader::GetMultiDexClassesDexName(size_t index) { |
||||||
|
return (index == 0) ? "classes.dex" : StringPrintf("classes%zu.dex", index + 1); |
||||||
|
} |
||||||
|
|
||||||
|
std::string DexFileLoader::GetMultiDexLocation(size_t index, const char* dex_location) { |
||||||
|
return (index == 0) |
||||||
|
? dex_location |
||||||
|
: StringPrintf("%s%cclasses%zu.dex", dex_location, kMultiDexSeparator, index + 1); |
||||||
|
} |
||||||
|
|
||||||
|
std::string DexFileLoader::GetDexCanonicalLocation(const char* dex_location) { |
||||||
|
CHECK_NE(dex_location, static_cast<const char*>(nullptr)); |
||||||
|
std::string base_location = GetBaseLocation(dex_location); |
||||||
|
const char* suffix = dex_location + base_location.size(); |
||||||
|
DCHECK(suffix[0] == 0 || suffix[0] == kMultiDexSeparator); |
||||||
|
// Warning: Bionic implementation of realpath() allocates > 12KB on the stack.
|
||||||
|
// Do not run this code on a small stack, e.g. in signal handler.
|
||||||
|
UniqueCPtr<const char[]> path(realpath(base_location.c_str(), nullptr)); |
||||||
|
if (path != nullptr && path.get() != base_location) { |
||||||
|
return std::string(path.get()) + suffix; |
||||||
|
} else if (suffix[0] == 0) { |
||||||
|
return base_location; |
||||||
|
} else { |
||||||
|
return dex_location; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// All of the implementations here should be independent of the runtime.
|
||||||
|
// TODO: implement all the virtual methods.
|
||||||
|
|
||||||
|
bool DexFileLoader::GetMultiDexChecksums( |
||||||
|
const char* filename ATTRIBUTE_UNUSED, |
||||||
|
std::vector<uint32_t>* checksums ATTRIBUTE_UNUSED, |
||||||
|
std::string* error_msg, |
||||||
|
int zip_fd ATTRIBUTE_UNUSED, |
||||||
|
bool* zip_file_only_contains_uncompress_dex ATTRIBUTE_UNUSED) const { |
||||||
|
*error_msg = "UNIMPLEMENTED"; |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
std::unique_ptr<const DexFile> DexFileLoader::Open(const uint8_t* base, |
||||||
|
size_t size, |
||||||
|
const std::string& location, |
||||||
|
uint32_t location_checksum, |
||||||
|
const OatDexFile* oat_dex_file, |
||||||
|
bool verify, |
||||||
|
bool verify_checksum, |
||||||
|
std::string* error_msg) const { |
||||||
|
return OpenCommon(base, |
||||||
|
size, |
||||||
|
/*data_base*/ nullptr, |
||||||
|
/*data_size*/ 0, |
||||||
|
location, |
||||||
|
location_checksum, |
||||||
|
oat_dex_file, |
||||||
|
verify, |
||||||
|
verify_checksum, |
||||||
|
error_msg, |
||||||
|
/*container*/ nullptr, |
||||||
|
/*verify_result*/ nullptr); |
||||||
|
} |
||||||
|
|
||||||
|
std::unique_ptr<const DexFile> DexFileLoader::OpenWithDataSection( |
||||||
|
const uint8_t* base, |
||||||
|
size_t size, |
||||||
|
const uint8_t* data_base, |
||||||
|
size_t data_size, |
||||||
|
const std::string& location, |
||||||
|
uint32_t location_checksum, |
||||||
|
const OatDexFile* oat_dex_file, |
||||||
|
bool verify, |
||||||
|
bool verify_checksum, |
||||||
|
std::string* error_msg) const { |
||||||
|
return OpenCommon(base, |
||||||
|
size, |
||||||
|
data_base, |
||||||
|
data_size, |
||||||
|
location, |
||||||
|
location_checksum, |
||||||
|
oat_dex_file, |
||||||
|
verify, |
||||||
|
verify_checksum, |
||||||
|
error_msg, |
||||||
|
/*container*/ nullptr, |
||||||
|
/*verify_result*/ nullptr); |
||||||
|
} |
||||||
|
|
||||||
|
bool DexFileLoader::OpenAll( |
||||||
|
const uint8_t* base, |
||||||
|
size_t size, |
||||||
|
const std::string& location, |
||||||
|
bool verify, |
||||||
|
bool verify_checksum, |
||||||
|
std::string* error_msg, |
||||||
|
std::vector<std::unique_ptr<const DexFile>>* dex_files) const { |
||||||
|
DCHECK(dex_files != nullptr) << "DexFile::Open: out-param is nullptr"; |
||||||
|
uint32_t magic = *reinterpret_cast<const uint32_t*>(base); |
||||||
|
if (IsZipMagic(magic)) { |
||||||
|
std::unique_ptr<DexZipArchive> zip_archive(DexZipArchive::Open(base, size, error_msg)); |
||||||
|
if (zip_archive.get() == nullptr) { |
||||||
|
DCHECK(!error_msg->empty()); |
||||||
|
return false; |
||||||
|
} |
||||||
|
return OpenAllDexFilesFromZip(*zip_archive.get(), |
||||||
|
location, |
||||||
|
verify, |
||||||
|
verify_checksum, |
||||||
|
error_msg, |
||||||
|
dex_files); |
||||||
|
} |
||||||
|
if (IsMagicValid(magic)) { |
||||||
|
const DexFile::Header* dex_header = reinterpret_cast<const DexFile::Header*>(base); |
||||||
|
std::unique_ptr<const DexFile> dex_file(Open(base, |
||||||
|
size, |
||||||
|
location, |
||||||
|
dex_header->checksum_, |
||||||
|
/*oat_dex_file*/ nullptr, |
||||||
|
verify, |
||||||
|
verify_checksum, |
||||||
|
error_msg)); |
||||||
|
if (dex_file.get() != nullptr) { |
||||||
|
dex_files->push_back(std::move(dex_file)); |
||||||
|
return true; |
||||||
|
} else { |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
*error_msg = StringPrintf("Expected valid zip or dex file"); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
std::unique_ptr<DexFile> DexFileLoader::OpenCommon(const uint8_t* base, |
||||||
|
size_t size, |
||||||
|
const uint8_t* data_base, |
||||||
|
size_t data_size, |
||||||
|
const std::string& location, |
||||||
|
uint32_t location_checksum, |
||||||
|
const OatDexFile* oat_dex_file, |
||||||
|
bool verify, |
||||||
|
bool verify_checksum, |
||||||
|
std::string* error_msg, |
||||||
|
std::unique_ptr<DexFileContainer> container, |
||||||
|
VerifyResult* verify_result) { |
||||||
|
if (verify_result != nullptr) { |
||||||
|
*verify_result = VerifyResult::kVerifyNotAttempted; |
||||||
|
} |
||||||
|
std::unique_ptr<DexFile> dex_file; |
||||||
|
if (size >= sizeof(StandardDexFile::Header) && StandardDexFile::IsMagicValid(base)) { |
||||||
|
if (data_size != 0) { |
||||||
|
CHECK_EQ(base, data_base) << "Unsupported for standard dex"; |
||||||
|
} |
||||||
|
dex_file.reset(new StandardDexFile(base, |
||||||
|
size, |
||||||
|
location, |
||||||
|
location_checksum, |
||||||
|
oat_dex_file, |
||||||
|
std::move(container))); |
||||||
|
} else if (size >= sizeof(CompactDexFile::Header) && CompactDexFile::IsMagicValid(base)) { |
||||||
|
if (data_base == nullptr) { |
||||||
|
// TODO: Is there a clean way to support both an explicit data section and reading the one
|
||||||
|
// from the header.
|
||||||
|
CHECK_EQ(data_size, 0u); |
||||||
|
const CompactDexFile::Header* const header = CompactDexFile::Header::At(base); |
||||||
|
data_base = base + header->data_off_; |
||||||
|
data_size = header->data_size_; |
||||||
|
} |
||||||
|
dex_file.reset(new CompactDexFile(base, |
||||||
|
size, |
||||||
|
data_base, |
||||||
|
data_size, |
||||||
|
location, |
||||||
|
location_checksum, |
||||||
|
oat_dex_file, |
||||||
|
std::move(container))); |
||||||
|
// Disable verification for CompactDex input.
|
||||||
|
verify = false; |
||||||
|
} else { |
||||||
|
*error_msg = "Invalid or truncated dex file"; |
||||||
|
} |
||||||
|
if (dex_file == nullptr) { |
||||||
|
*error_msg = StringPrintf("Failed to open dex file '%s' from memory: %s", location.c_str(), |
||||||
|
error_msg->c_str()); |
||||||
|
return nullptr; |
||||||
|
} |
||||||
|
if (!dex_file->Init(error_msg)) { |
||||||
|
dex_file.reset(); |
||||||
|
return nullptr; |
||||||
|
} |
||||||
|
if (verify && !DexFileVerifier::Verify(dex_file.get(), |
||||||
|
dex_file->Begin(), |
||||||
|
dex_file->Size(), |
||||||
|
location.c_str(), |
||||||
|
verify_checksum, |
||||||
|
error_msg)) { |
||||||
|
if (verify_result != nullptr) { |
||||||
|
*verify_result = VerifyResult::kVerifyFailed; |
||||||
|
} |
||||||
|
return nullptr; |
||||||
|
} |
||||||
|
if (verify_result != nullptr) { |
||||||
|
*verify_result = VerifyResult::kVerifySucceeded; |
||||||
|
} |
||||||
|
return dex_file; |
||||||
|
} |
||||||
|
|
||||||
|
std::unique_ptr<const DexFile> DexFileLoader::OpenOneDexFileFromZip( |
||||||
|
const DexZipArchive& zip_archive, |
||||||
|
const char* entry_name, |
||||||
|
const std::string& location, |
||||||
|
bool verify, |
||||||
|
bool verify_checksum, |
||||||
|
std::string* error_msg, |
||||||
|
ZipOpenErrorCode* error_code) const { |
||||||
|
CHECK(!location.empty()); |
||||||
|
std::unique_ptr<DexZipEntry> zip_entry(zip_archive.Find(entry_name, error_msg)); |
||||||
|
if (zip_entry == nullptr) { |
||||||
|
*error_code = ZipOpenErrorCode::kEntryNotFound; |
||||||
|
return nullptr; |
||||||
|
} |
||||||
|
if (zip_entry->GetUncompressedLength() == 0) { |
||||||
|
*error_msg = StringPrintf("Dex file '%s' has zero length", location.c_str()); |
||||||
|
*error_code = ZipOpenErrorCode::kDexFileError; |
||||||
|
return nullptr; |
||||||
|
} |
||||||
|
|
||||||
|
std::vector<uint8_t> map(zip_entry->Extract(error_msg)); |
||||||
|
if (map.size() == 0) { |
||||||
|
*error_msg = StringPrintf("Failed to extract '%s' from '%s': %s", entry_name, location.c_str(), |
||||||
|
error_msg->c_str()); |
||||||
|
*error_code = ZipOpenErrorCode::kExtractToMemoryError; |
||||||
|
return nullptr; |
||||||
|
} |
||||||
|
VerifyResult verify_result; |
||||||
|
std::unique_ptr<const DexFile> dex_file = OpenCommon( |
||||||
|
map.data(), |
||||||
|
map.size(), |
||||||
|
/*data_base*/ nullptr, |
||||||
|
/*data_size*/ 0u, |
||||||
|
location, |
||||||
|
zip_entry->GetCrc32(), |
||||||
|
/*oat_dex_file*/ nullptr, |
||||||
|
verify, |
||||||
|
verify_checksum, |
||||||
|
error_msg, |
||||||
|
std::make_unique<VectorContainer>(std::move(map)), |
||||||
|
&verify_result); |
||||||
|
if (dex_file == nullptr) { |
||||||
|
if (verify_result == VerifyResult::kVerifyNotAttempted) { |
||||||
|
*error_code = ZipOpenErrorCode::kDexFileError; |
||||||
|
} else { |
||||||
|
*error_code = ZipOpenErrorCode::kVerifyError; |
||||||
|
} |
||||||
|
return nullptr; |
||||||
|
} |
||||||
|
if (verify_result != VerifyResult::kVerifySucceeded) { |
||||||
|
*error_code = ZipOpenErrorCode::kVerifyError; |
||||||
|
return nullptr; |
||||||
|
} |
||||||
|
*error_code = ZipOpenErrorCode::kNoError; |
||||||
|
return dex_file; |
||||||
|
} |
||||||
|
|
||||||
|
// Technically we do not have a limitation with respect to the number of dex files that can be in a
|
||||||
|
// multidex APK. However, it's bad practice, as each dex file requires its own tables for symbols
|
||||||
|
// (types, classes, methods, ...) and dex caches. So warn the user that we open a zip with what
|
||||||
|
// seems an excessive number.
|
||||||
|
static constexpr size_t kWarnOnManyDexFilesThreshold = 100; |
||||||
|
|
||||||
|
bool DexFileLoader::OpenAllDexFilesFromZip( |
||||||
|
const DexZipArchive& zip_archive, |
||||||
|
const std::string& location, |
||||||
|
bool verify, |
||||||
|
bool verify_checksum, |
||||||
|
std::string* error_msg, |
||||||
|
std::vector<std::unique_ptr<const DexFile>>* dex_files) const { |
||||||
|
DCHECK(dex_files != nullptr) << "DexFile::OpenFromZip: out-param is nullptr"; |
||||||
|
ZipOpenErrorCode error_code; |
||||||
|
std::unique_ptr<const DexFile> dex_file(OpenOneDexFileFromZip(zip_archive, |
||||||
|
kClassesDex, |
||||||
|
location, |
||||||
|
verify, |
||||||
|
verify_checksum, |
||||||
|
error_msg, |
||||||
|
&error_code)); |
||||||
|
if (dex_file.get() == nullptr) { |
||||||
|
return false; |
||||||
|
} else { |
||||||
|
// Had at least classes.dex.
|
||||||
|
dex_files->push_back(std::move(dex_file)); |
||||||
|
|
||||||
|
// Now try some more.
|
||||||
|
|
||||||
|
// We could try to avoid std::string allocations by working on a char array directly. As we
|
||||||
|
// do not expect a lot of iterations, this seems too involved and brittle.
|
||||||
|
|
||||||
|
for (size_t i = 1; ; ++i) { |
||||||
|
std::string name = GetMultiDexClassesDexName(i); |
||||||
|
std::string fake_location = GetMultiDexLocation(i, location.c_str()); |
||||||
|
std::unique_ptr<const DexFile> next_dex_file(OpenOneDexFileFromZip(zip_archive, |
||||||
|
name.c_str(), |
||||||
|
fake_location, |
||||||
|
verify, |
||||||
|
verify_checksum, |
||||||
|
error_msg, |
||||||
|
&error_code)); |
||||||
|
if (next_dex_file.get() == nullptr) { |
||||||
|
if (error_code != ZipOpenErrorCode::kEntryNotFound) { |
||||||
|
LOG(WARNING) << "Zip open failed: " << *error_msg; |
||||||
|
} |
||||||
|
break; |
||||||
|
} else { |
||||||
|
dex_files->push_back(std::move(next_dex_file)); |
||||||
|
} |
||||||
|
|
||||||
|
if (i == kWarnOnManyDexFilesThreshold) { |
||||||
|
LOG(WARNING) << location << " has in excess of " << kWarnOnManyDexFilesThreshold |
||||||
|
<< " dex files. Please consider coalescing and shrinking the number to " |
||||||
|
" avoid runtime overhead."; |
||||||
|
} |
||||||
|
|
||||||
|
if (i == std::numeric_limits<size_t>::max()) { |
||||||
|
LOG(ERROR) << "Overflow in number of dex files!"; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
} // namespace art_lkchan
|
@ -0,0 +1,199 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2017 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_DEX_FILE_LOADER_H_ |
||||||
|
#define ART_LIBDEXFILE_DEX_DEX_FILE_LOADER_H_ |
||||||
|
|
||||||
|
#include <cstdint> |
||||||
|
#include <memory> |
||||||
|
#include <string> |
||||||
|
#include <vector> |
||||||
|
|
||||||
|
namespace art_lkchan { |
||||||
|
|
||||||
|
class DexFile; |
||||||
|
class DexFileContainer; |
||||||
|
class MemMap; |
||||||
|
class OatDexFile; |
||||||
|
|
||||||
|
class DexZipArchive; |
||||||
|
|
||||||
|
// Class that is used to open dex files and deal with corresponding multidex and location logic.
|
||||||
|
class DexFileLoader { |
||||||
|
public: |
||||||
|
// name of the DexFile entry within a zip archive
|
||||||
|
static constexpr const char* kClassesDex = "classes.dex"; |
||||||
|
|
||||||
|
// The separator character in MultiDex locations.
|
||||||
|
static constexpr char kMultiDexSeparator = '!'; |
||||||
|
|
||||||
|
// Return true if the magic is valid for dex or cdex.
|
||||||
|
static bool IsMagicValid(uint32_t magic); |
||||||
|
static bool IsMagicValid(const uint8_t* magic); |
||||||
|
|
||||||
|
// Return true if the corresponding version and magic is valid.
|
||||||
|
static bool IsVersionAndMagicValid(const uint8_t* magic); |
||||||
|
|
||||||
|
// Check whether a location denotes a multidex dex file. This is a very simple check: returns
|
||||||
|
// whether the string contains the separator character.
|
||||||
|
static bool IsMultiDexLocation(const char* location); |
||||||
|
|
||||||
|
// Return the name of the index-th classes.dex in a multidex zip file. This is classes.dex for
|
||||||
|
// index == 0, and classes{index + 1}.dex else.
|
||||||
|
static std::string GetMultiDexClassesDexName(size_t index); |
||||||
|
|
||||||
|
// Return the (possibly synthetic) dex location for a multidex entry. This is dex_location for
|
||||||
|
// index == 0, and dex_location + multi-dex-separator + GetMultiDexClassesDexName(index) else.
|
||||||
|
static std::string GetMultiDexLocation(size_t index, const char* dex_location); |
||||||
|
|
||||||
|
// Returns the canonical form of the given dex location.
|
||||||
|
//
|
||||||
|
// There are different flavors of "dex locations" as follows:
|
||||||
|
// the file name of a dex file:
|
||||||
|
// The actual file path that the dex file has on disk.
|
||||||
|
// dex_location:
|
||||||
|
// This acts as a key for the class linker to know which dex file to load.
|
||||||
|
// It may correspond to either an old odex file or a particular dex file
|
||||||
|
// inside an oat file. In the first case it will also match the file name
|
||||||
|
// of the dex file. In the second case (oat) it will include the file name
|
||||||
|
// and possibly some multidex annotation to uniquely identify it.
|
||||||
|
// canonical_dex_location:
|
||||||
|
// the dex_location where its file name part has been made canonical.
|
||||||
|
static std::string GetDexCanonicalLocation(const char* dex_location); |
||||||
|
|
||||||
|
// For normal dex files, location and base location coincide. If a dex file is part of a multidex
|
||||||
|
// archive, the base location is the name of the originating jar/apk, stripped of any internal
|
||||||
|
// classes*.dex path.
|
||||||
|
static std::string GetBaseLocation(const char* location) { |
||||||
|
const char* pos = strrchr(location, kMultiDexSeparator); |
||||||
|
return (pos == nullptr) ? location : std::string(location, pos - location); |
||||||
|
} |
||||||
|
|
||||||
|
static std::string GetBaseLocation(const std::string& location) { |
||||||
|
return GetBaseLocation(location.c_str()); |
||||||
|
} |
||||||
|
|
||||||
|
// Returns the '!classes*.dex' part of the dex location. Returns an empty
|
||||||
|
// string if there is no multidex suffix for the given location.
|
||||||
|
// The kMultiDexSeparator is included in the returned suffix.
|
||||||
|
static std::string GetMultiDexSuffix(const std::string& location) { |
||||||
|
size_t pos = location.rfind(kMultiDexSeparator); |
||||||
|
return (pos == std::string::npos) ? std::string() : location.substr(pos); |
||||||
|
} |
||||||
|
|
||||||
|
virtual ~DexFileLoader() { } |
||||||
|
|
||||||
|
// Returns the checksums of a file for comparison with GetLocationChecksum().
|
||||||
|
// For .dex files, this is the single header checksum.
|
||||||
|
// For zip files, this is the zip entry CRC32 checksum for classes.dex and
|
||||||
|
// each additional multidex entry classes2.dex, classes3.dex, etc.
|
||||||
|
// If a valid zip_fd is provided the file content will be read directly from
|
||||||
|
// the descriptor and `filename` will be used as alias for error logging. If
|
||||||
|
// zip_fd is -1, the method will try to open the `filename` and read the
|
||||||
|
// content from it.
|
||||||
|
// Return true if the checksums could be found, false otherwise.
|
||||||
|
virtual bool GetMultiDexChecksums(const char* filename, |
||||||
|
std::vector<uint32_t>* checksums, |
||||||
|
std::string* error_msg, |
||||||
|
int zip_fd = -1, |
||||||
|
bool* zip_file_only_contains_uncompress_dex = nullptr) const; |
||||||
|
|
||||||
|
// Opens .dex file, backed by existing memory
|
||||||
|
virtual std::unique_ptr<const DexFile> Open(const uint8_t* base, |
||||||
|
size_t size, |
||||||
|
const std::string& location, |
||||||
|
uint32_t location_checksum, |
||||||
|
const OatDexFile* oat_dex_file, |
||||||
|
bool verify, |
||||||
|
bool verify_checksum, |
||||||
|
std::string* error_msg) const; |
||||||
|
|
||||||
|
// Open a dex file with a separate data section.
|
||||||
|
virtual std::unique_ptr<const DexFile> OpenWithDataSection( |
||||||
|
const uint8_t* base, |
||||||
|
size_t size, |
||||||
|
const uint8_t* data_base, |
||||||
|
size_t data_size, |
||||||
|
const std::string& location, |
||||||
|
uint32_t location_checksum, |
||||||
|
const OatDexFile* oat_dex_file, |
||||||
|
bool verify, |
||||||
|
bool verify_checksum, |
||||||
|
std::string* error_msg) const; |
||||||
|
|
||||||
|
|
||||||
|
// Opens all .dex files found in the memory map, guessing the container format based on file
|
||||||
|
// extension.
|
||||||
|
virtual bool OpenAll(const uint8_t* base, |
||||||
|
size_t size, |
||||||
|
const std::string& location, |
||||||
|
bool verify, |
||||||
|
bool verify_checksum, |
||||||
|
std::string* error_msg, |
||||||
|
std::vector<std::unique_ptr<const DexFile>>* dex_files) const; |
||||||
|
|
||||||
|
protected: |
||||||
|
enum class ZipOpenErrorCode { |
||||||
|
kNoError, |
||||||
|
kEntryNotFound, |
||||||
|
kExtractToMemoryError, |
||||||
|
kDexFileError, |
||||||
|
kMakeReadOnlyError, |
||||||
|
kVerifyError |
||||||
|
}; |
||||||
|
|
||||||
|
enum class VerifyResult { // private
|
||||||
|
kVerifyNotAttempted, |
||||||
|
kVerifySucceeded, |
||||||
|
kVerifyFailed |
||||||
|
}; |
||||||
|
|
||||||
|
static std::unique_ptr<DexFile> OpenCommon(const uint8_t* base, |
||||||
|
size_t size, |
||||||
|
const uint8_t* data_base, |
||||||
|
size_t data_size, |
||||||
|
const std::string& location, |
||||||
|
uint32_t location_checksum, |
||||||
|
const OatDexFile* oat_dex_file, |
||||||
|
bool verify, |
||||||
|
bool verify_checksum, |
||||||
|
std::string* error_msg, |
||||||
|
std::unique_ptr<DexFileContainer> container, |
||||||
|
VerifyResult* verify_result); |
||||||
|
|
||||||
|
private: |
||||||
|
// Open all classesXXX.dex files from a zip archive.
|
||||||
|
bool OpenAllDexFilesFromZip(const DexZipArchive& zip_archive, |
||||||
|
const std::string& location, |
||||||
|
bool verify, |
||||||
|
bool verify_checksum, |
||||||
|
std::string* error_msg, |
||||||
|
std::vector<std::unique_ptr<const DexFile>>* dex_files) const; |
||||||
|
|
||||||
|
// Opens .dex file from the entry_name in a zip archive. error_code is undefined when non-null
|
||||||
|
// return.
|
||||||
|
std::unique_ptr<const DexFile> OpenOneDexFileFromZip(const DexZipArchive& zip_archive, |
||||||
|
const char* entry_name, |
||||||
|
const std::string& location, |
||||||
|
bool verify, |
||||||
|
bool verify_checksum, |
||||||
|
std::string* error_msg, |
||||||
|
ZipOpenErrorCode* error_code) const; |
||||||
|
}; |
||||||
|
|
||||||
|
} // namespace art_lkchan
|
||||||
|
|
||||||
|
#endif // ART_LIBDEXFILE_DEX_DEX_FILE_LOADER_H_
|
@ -0,0 +1,52 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2017 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_DEX_FILE_REFERENCE_H_ |
||||||
|
#define ART_LIBDEXFILE_DEX_DEX_FILE_REFERENCE_H_ |
||||||
|
|
||||||
|
#include <cstdint> |
||||||
|
|
||||||
|
namespace art_lkchan { |
||||||
|
|
||||||
|
class DexFile; |
||||||
|
|
||||||
|
class DexFileReference { |
||||||
|
public: |
||||||
|
DexFileReference(const DexFile* file, uint32_t idx) : dex_file(file), index(idx) {} |
||||||
|
const DexFile* dex_file; |
||||||
|
uint32_t index; |
||||||
|
|
||||||
|
struct Comparator { |
||||||
|
bool operator()(const DexFileReference& a, const DexFileReference& b) const { |
||||||
|
if (a.dex_file != b.dex_file) { |
||||||
|
return a.dex_file < b.dex_file; |
||||||
|
} |
||||||
|
return a.index < b.index; |
||||||
|
} |
||||||
|
}; |
||||||
|
}; |
||||||
|
|
||||||
|
// Default comparators, compares the indicies, not the backing data.
|
||||||
|
inline bool operator<(const DexFileReference& a, const DexFileReference& b) { |
||||||
|
return DexFileReference::Comparator()(a, b); |
||||||
|
} |
||||||
|
inline bool operator==(const DexFileReference& a, const DexFileReference& b) { |
||||||
|
return a.dex_file == b.dex_file && a.index == b.index; |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace art_lkchan
|
||||||
|
|
||||||
|
#endif // ART_LIBDEXFILE_DEX_DEX_FILE_REFERENCE_H_
|
@ -0,0 +1,272 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2017 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. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include "dex_file_tracking_registrar.h" |
||||||
|
|
||||||
|
#include <deque> |
||||||
|
#include <tuple> |
||||||
|
|
||||||
|
#include <android-base/logging.h> |
||||||
|
|
||||||
|
// For dex tracking through poisoning. Note: Requires forcing sanitization. This is the reason for
|
||||||
|
// the ifdefs and early include.
|
||||||
|
#ifdef ART_DEX_FILE_ACCESS_TRACKING |
||||||
|
#ifndef ART_ENABLE_ADDRESS_SANITIZER |
||||||
|
#define ART_ENABLE_ADDRESS_SANITIZER |
||||||
|
#endif |
||||||
|
#endif |
||||||
|
#include "base/memory_tool.h" |
||||||
|
|
||||||
|
#include "code_item_accessors-inl.h" |
||||||
|
#include "dex_file-inl.h" |
||||||
|
|
||||||
|
namespace art_lkchan { |
||||||
|
namespace dex { |
||||||
|
namespace tracking { |
||||||
|
|
||||||
|
// If true, poison dex files to track accesses.
|
||||||
|
static constexpr bool kDexFileAccessTracking = |
||||||
|
#ifdef ART_DEX_FILE_ACCESS_TRACKING |
||||||
|
true; |
||||||
|
#else |
||||||
|
false; |
||||||
|
#endif |
||||||
|
|
||||||
|
// The following are configurations of poisoning certain sections of a Dex File.
|
||||||
|
// More will be added
|
||||||
|
enum DexTrackingType { |
||||||
|
// Poisons all of a Dex File when set.
|
||||||
|
kWholeDexTracking, |
||||||
|
// Poisons all Code Items of a Dex File when set.
|
||||||
|
kCodeItemTracking, |
||||||
|
// Poisons all subsections of a Code Item, except the Insns bytecode array
|
||||||
|
// section, when set for all Code Items in a Dex File.
|
||||||
|
kCodeItemNonInsnsTracking, |
||||||
|
// Poisons all subsections of a Code Item, except the Insns bytecode array
|
||||||
|
// section, when set for all Code Items in a Dex File.
|
||||||
|
// Additionally unpoisons the entire Code Item when method is a class
|
||||||
|
// initializer.
|
||||||
|
kCodeItemNonInsnsNoClinitTracking, |
||||||
|
// Poisons the size and offset information along with the first instruction.
|
||||||
|
// This is so that accessing multiple instructions while accessing a code item
|
||||||
|
// once will not trigger unnecessary accesses.
|
||||||
|
kCodeItemStartTracking, |
||||||
|
// Poisons all String Data Items of a Dex Files when set.
|
||||||
|
kStringDataItemTracking, |
||||||
|
// Poisons the first byte of the utf16_size value and the first byte of the
|
||||||
|
// data section for all String Data Items of a Dex File.
|
||||||
|
kStringDataItemStartTracking, |
||||||
|
// Poisons based on a custom tracking system which can be specified in
|
||||||
|
// SetDexSections
|
||||||
|
kCustomTracking, |
||||||
|
}; |
||||||
|
|
||||||
|
// Intended for local changes only.
|
||||||
|
// Represents the current configuration being run.
|
||||||
|
static constexpr DexTrackingType kCurrentTrackingSystem = kWholeDexTracking; |
||||||
|
|
||||||
|
// Intended for local changes only.
|
||||||
|
void DexFileTrackingRegistrar::SetDexSections() { |
||||||
|
if (kDexFileAccessTracking && dex_file_ != nullptr) { |
||||||
|
// Logs the Dex File's location and starting address if tracking is enabled
|
||||||
|
LOG(ERROR) << "RegisterDexFile: " << dex_file_->GetLocation() + " @ " << std::hex |
||||||
|
<< reinterpret_cast<uintptr_t>(dex_file_->Begin()); |
||||||
|
switch (kCurrentTrackingSystem) { |
||||||
|
case kWholeDexTracking: |
||||||
|
SetDexFileRegistration(true); |
||||||
|
break; |
||||||
|
case kCodeItemTracking: |
||||||
|
SetAllCodeItemRegistration(true); |
||||||
|
break; |
||||||
|
case kCodeItemNonInsnsTracking: |
||||||
|
SetAllCodeItemRegistration(true); |
||||||
|
SetAllInsnsRegistration(false); |
||||||
|
break; |
||||||
|
case kCodeItemNonInsnsNoClinitTracking: |
||||||
|
SetAllCodeItemRegistration(true); |
||||||
|
SetAllInsnsRegistration(false); |
||||||
|
SetCodeItemRegistration("<clinit>", false); |
||||||
|
break; |
||||||
|
case kCodeItemStartTracking: |
||||||
|
SetAllCodeItemStartRegistration(true); |
||||||
|
break; |
||||||
|
case kStringDataItemTracking: |
||||||
|
SetAllStringDataRegistration(true); |
||||||
|
break; |
||||||
|
case kStringDataItemStartTracking: |
||||||
|
SetAllStringDataStartRegistration(true); |
||||||
|
break; |
||||||
|
case kCustomTracking: |
||||||
|
// TODO: Add/remove additional calls here to (un)poison sections of
|
||||||
|
// dex_file_
|
||||||
|
break; |
||||||
|
default: |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void RegisterDexFile(const DexFile* dex_file) { |
||||||
|
DexFileTrackingRegistrar dex_tracking_registrar(dex_file); |
||||||
|
dex_tracking_registrar.SetDexSections(); |
||||||
|
dex_tracking_registrar.SetCurrentRanges(); |
||||||
|
} |
||||||
|
|
||||||
|
inline void SetRegistrationRange(const void* begin, size_t size, bool should_poison) { |
||||||
|
if (should_poison) { |
||||||
|
//chensenhua MEMORY_TOOL_MAKE_NOACCESS(begin, size);
|
||||||
|
} else { |
||||||
|
// Note: MEMORY_TOOL_MAKE_UNDEFINED has the same functionality with Address
|
||||||
|
// chensenhua Sanitizer. The difference has not been tested with Valgrind
|
||||||
|
//MEMORY_TOOL_MAKE_DEFINED(begin, size);
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void DexFileTrackingRegistrar::SetCurrentRanges() { |
||||||
|
// This also empties range_values_ to avoid redundant (un)poisoning upon
|
||||||
|
// subsequent calls.
|
||||||
|
while (!range_values_.empty()) { |
||||||
|
const std::tuple<const void*, size_t, bool>& current_range = range_values_.front(); |
||||||
|
SetRegistrationRange(std::get<0>(current_range), |
||||||
|
std::get<1>(current_range), |
||||||
|
std::get<2>(current_range)); |
||||||
|
range_values_.pop_front(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void DexFileTrackingRegistrar::SetDexFileRegistration(bool should_poison) { |
||||||
|
const void* dex_file_begin = reinterpret_cast<const void*>(dex_file_->Begin()); |
||||||
|
size_t dex_file_size = dex_file_->Size(); |
||||||
|
range_values_.push_back(std::make_tuple(dex_file_begin, dex_file_size, should_poison)); |
||||||
|
} |
||||||
|
|
||||||
|
void DexFileTrackingRegistrar::SetAllCodeItemRegistration(bool should_poison) { |
||||||
|
for (size_t classdef_ctr = 0; classdef_ctr < dex_file_->NumClassDefs(); ++classdef_ctr) { |
||||||
|
const DexFile::ClassDef& cd = dex_file_->GetClassDef(classdef_ctr); |
||||||
|
const uint8_t* class_data = dex_file_->GetClassData(cd); |
||||||
|
if (class_data != nullptr) { |
||||||
|
ClassDataItemIterator cdit(*dex_file_, class_data); |
||||||
|
cdit.SkipAllFields(); |
||||||
|
while (cdit.HasNextMethod()) { |
||||||
|
const DexFile::CodeItem* code_item = cdit.GetMethodCodeItem(); |
||||||
|
if (code_item != nullptr) { |
||||||
|
const void* code_item_begin = reinterpret_cast<const void*>(code_item); |
||||||
|
size_t code_item_size = dex_file_->GetCodeItemSize(*code_item); |
||||||
|
range_values_.push_back(std::make_tuple(code_item_begin, code_item_size, should_poison)); |
||||||
|
} |
||||||
|
cdit.Next(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void DexFileTrackingRegistrar::SetAllCodeItemStartRegistration(bool should_poison) { |
||||||
|
for (size_t classdef_ctr = 0; classdef_ctr < dex_file_->NumClassDefs(); ++classdef_ctr) { |
||||||
|
const DexFile::ClassDef& cd = dex_file_->GetClassDef(classdef_ctr); |
||||||
|
const uint8_t* class_data = dex_file_->GetClassData(cd); |
||||||
|
if (class_data != nullptr) { |
||||||
|
ClassDataItemIterator cdit(*dex_file_, class_data); |
||||||
|
cdit.SkipAllFields(); |
||||||
|
while (cdit.HasNextMethod()) { |
||||||
|
const DexFile::CodeItem* code_item = cdit.GetMethodCodeItem(); |
||||||
|
if (code_item != nullptr) { |
||||||
|
const void* code_item_begin = reinterpret_cast<const void*>(code_item); |
||||||
|
size_t code_item_start = reinterpret_cast<size_t>(code_item); |
||||||
|
CodeItemInstructionAccessor accessor(*dex_file_, code_item); |
||||||
|
size_t code_item_start_end = reinterpret_cast<size_t>(accessor.Insns()); |
||||||
|
size_t code_item_start_size = code_item_start_end - code_item_start; |
||||||
|
range_values_.push_back(std::make_tuple(code_item_begin, |
||||||
|
code_item_start_size, |
||||||
|
should_poison)); |
||||||
|
} |
||||||
|
cdit.Next(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void DexFileTrackingRegistrar::SetAllInsnsRegistration(bool should_poison) { |
||||||
|
for (size_t classdef_ctr = 0; classdef_ctr < dex_file_->NumClassDefs(); ++classdef_ctr) { |
||||||
|
const DexFile::ClassDef& cd = dex_file_->GetClassDef(classdef_ctr); |
||||||
|
const uint8_t* class_data = dex_file_->GetClassData(cd); |
||||||
|
if (class_data != nullptr) { |
||||||
|
ClassDataItemIterator cdit(*dex_file_, class_data); |
||||||
|
cdit.SkipAllFields(); |
||||||
|
while (cdit.HasNextMethod()) { |
||||||
|
const DexFile::CodeItem* code_item = cdit.GetMethodCodeItem(); |
||||||
|
if (code_item != nullptr) { |
||||||
|
CodeItemInstructionAccessor accessor(*dex_file_, code_item); |
||||||
|
const void* insns_begin = reinterpret_cast<const void*>(accessor.Insns()); |
||||||
|
// Member insns_size_in_code_units_ is in 2-byte units
|
||||||
|
size_t insns_size = accessor.InsnsSizeInCodeUnits() * 2; |
||||||
|
range_values_.push_back(std::make_tuple(insns_begin, insns_size, should_poison)); |
||||||
|
} |
||||||
|
cdit.Next(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void DexFileTrackingRegistrar::SetCodeItemRegistration(const char* class_name, bool should_poison) { |
||||||
|
for (size_t classdef_ctr = 0; classdef_ctr < dex_file_->NumClassDefs(); ++classdef_ctr) { |
||||||
|
const DexFile::ClassDef& cd = dex_file_->GetClassDef(classdef_ctr); |
||||||
|
const uint8_t* class_data = dex_file_->GetClassData(cd); |
||||||
|
if (class_data != nullptr) { |
||||||
|
ClassDataItemIterator cdit(*dex_file_, class_data); |
||||||
|
cdit.SkipAllFields(); |
||||||
|
while (cdit.HasNextMethod()) { |
||||||
|
const DexFile::MethodId& methodid_item = dex_file_->GetMethodId(cdit.GetMemberIndex()); |
||||||
|
const char * methodid_name = dex_file_->GetMethodName(methodid_item); |
||||||
|
const DexFile::CodeItem* code_item = cdit.GetMethodCodeItem(); |
||||||
|
if (code_item != nullptr && strcmp(methodid_name, class_name) == 0) { |
||||||
|
const void* code_item_begin = reinterpret_cast<const void*>(code_item); |
||||||
|
size_t code_item_size = dex_file_->GetCodeItemSize(*code_item); |
||||||
|
range_values_.push_back(std::make_tuple(code_item_begin, code_item_size, should_poison)); |
||||||
|
} |
||||||
|
cdit.Next(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void DexFileTrackingRegistrar::SetAllStringDataStartRegistration(bool should_poison) { |
||||||
|
for (size_t stringid_ctr = 0; stringid_ctr < dex_file_->NumStringIds(); ++stringid_ctr) { |
||||||
|
const DexFile::StringId & string_id = dex_file_->GetStringId(StringIndex(stringid_ctr)); |
||||||
|
const void* string_data_begin = reinterpret_cast<const void*>(dex_file_->Begin() + string_id.string_data_off_); |
||||||
|
// Data Section of String Data Item
|
||||||
|
const void* string_data_data_begin = reinterpret_cast<const void*>(dex_file_->GetStringData(string_id)); |
||||||
|
range_values_.push_back(std::make_tuple(string_data_begin, 1, should_poison)); |
||||||
|
range_values_.push_back(std::make_tuple(string_data_data_begin, 1, should_poison)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void DexFileTrackingRegistrar::SetAllStringDataRegistration(bool should_poison) { |
||||||
|
size_t map_offset = dex_file_->GetHeader().map_off_; |
||||||
|
auto map_list = reinterpret_cast<const DexFile::MapList*>(dex_file_->Begin() + map_offset); |
||||||
|
for (size_t map_ctr = 0; map_ctr < map_list->size_; ++map_ctr) { |
||||||
|
const DexFile::MapItem& map_item = map_list->list_[map_ctr]; |
||||||
|
if (map_item.type_ == DexFile::kDexTypeStringDataItem) { |
||||||
|
const DexFile::MapItem& next_map_item = map_list->list_[map_ctr + 1]; |
||||||
|
const void* string_data_begin = reinterpret_cast<const void*>(dex_file_->Begin() + map_item.offset_); |
||||||
|
size_t string_data_size = next_map_item.offset_ - map_item.offset_; |
||||||
|
range_values_.push_back(std::make_tuple(string_data_begin, string_data_size, should_poison)); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace tracking
|
||||||
|
} // namespace dex
|
||||||
|
} // namespace art_lkchan
|
@ -0,0 +1,81 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2017 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_DEX_FILE_TRACKING_REGISTRAR_H_ |
||||||
|
#define ART_LIBDEXFILE_DEX_DEX_FILE_TRACKING_REGISTRAR_H_ |
||||||
|
|
||||||
|
#include <deque> |
||||||
|
#include <tuple> |
||||||
|
|
||||||
|
#include "dex_file.h" |
||||||
|
|
||||||
|
namespace art_lkchan { |
||||||
|
namespace dex { |
||||||
|
namespace tracking { |
||||||
|
|
||||||
|
// Class for (un)poisoning various sections of Dex Files
|
||||||
|
//
|
||||||
|
// This class provides the means to log accesses only of sections whose
|
||||||
|
// accesses are needed. All accesses are displayed as stack traces in
|
||||||
|
// logcat.
|
||||||
|
class DexFileTrackingRegistrar { |
||||||
|
public: |
||||||
|
explicit DexFileTrackingRegistrar(const DexFile* const dex_file) |
||||||
|
: dex_file_(dex_file) { |
||||||
|
} |
||||||
|
|
||||||
|
// This function is where the functions below it are called to actually
|
||||||
|
// poison sections.
|
||||||
|
void SetDexSections(); |
||||||
|
|
||||||
|
// Uses data contained inside range_values_ to poison memory through the
|
||||||
|
// memory tool.
|
||||||
|
void SetCurrentRanges(); |
||||||
|
|
||||||
|
private: |
||||||
|
void SetDexFileRegistration(bool should_poison); |
||||||
|
|
||||||
|
// Set of functions concerning Code Items of dex_file_
|
||||||
|
void SetAllCodeItemRegistration(bool should_poison); |
||||||
|
// Sets the insns_ section of all code items.
|
||||||
|
void SetAllInsnsRegistration(bool should_poison); |
||||||
|
// This function finds the code item of a class based on class name.
|
||||||
|
void SetCodeItemRegistration(const char* class_name, bool should_poison); |
||||||
|
// Sets the size and offset information along with first instruction in insns_
|
||||||
|
// section of all code items.
|
||||||
|
void SetAllCodeItemStartRegistration(bool should_poison); |
||||||
|
|
||||||
|
// Set of functions concerning String Data Items of dex_file_
|
||||||
|
void SetAllStringDataRegistration(bool should_poison); |
||||||
|
// Sets the first byte of size value and data section of all string data
|
||||||
|
// items.
|
||||||
|
void SetAllStringDataStartRegistration(bool should_poison); |
||||||
|
|
||||||
|
// Contains tuples of all ranges of memory that need to be explicitly
|
||||||
|
// (un)poisoned by the memory tool.
|
||||||
|
std::deque<std::tuple<const void *, size_t, bool>> range_values_; |
||||||
|
|
||||||
|
const DexFile* const dex_file_; |
||||||
|
}; |
||||||
|
|
||||||
|
// This function is meant to called externally to use DexfileTrackingRegistrar
|
||||||
|
void RegisterDexFile(const DexFile* dex_file); |
||||||
|
|
||||||
|
} // namespace tracking
|
||||||
|
} // namespace dex
|
||||||
|
} // namespace art_lkchan
|
||||||
|
|
||||||
|
#endif // ART_LIBDEXFILE_DEX_DEX_FILE_TRACKING_REGISTRAR_H_
|
@ -0,0 +1,117 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 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_DEX_FILE_TYPES_H_ |
||||||
|
#define ART_LIBDEXFILE_DEX_DEX_FILE_TYPES_H_ |
||||||
|
|
||||||
|
#include <limits> |
||||||
|
#include <ostream> |
||||||
|
|
||||||
|
namespace art_lkchan { |
||||||
|
namespace dex { |
||||||
|
|
||||||
|
constexpr uint32_t kDexNoIndex = 0xFFFFFFFF; |
||||||
|
|
||||||
|
class StringIndex { |
||||||
|
public: |
||||||
|
uint32_t index_; |
||||||
|
|
||||||
|
constexpr StringIndex() : index_(std::numeric_limits<decltype(index_)>::max()) {} |
||||||
|
explicit constexpr StringIndex(uint32_t idx) : index_(idx) {} |
||||||
|
|
||||||
|
bool IsValid() const { |
||||||
|
return index_ != std::numeric_limits<decltype(index_)>::max(); |
||||||
|
} |
||||||
|
static StringIndex Invalid() { |
||||||
|
return StringIndex(std::numeric_limits<decltype(index_)>::max()); |
||||||
|
} |
||||||
|
|
||||||
|
bool operator==(const StringIndex& other) const { |
||||||
|
return index_ == other.index_; |
||||||
|
} |
||||||
|
bool operator!=(const StringIndex& other) const { |
||||||
|
return index_ != other.index_; |
||||||
|
} |
||||||
|
bool operator<(const StringIndex& other) const { |
||||||
|
return index_ < other.index_; |
||||||
|
} |
||||||
|
bool operator<=(const StringIndex& other) const { |
||||||
|
return index_ <= other.index_; |
||||||
|
} |
||||||
|
bool operator>(const StringIndex& other) const { |
||||||
|
return index_ > other.index_; |
||||||
|
} |
||||||
|
bool operator>=(const StringIndex& other) const { |
||||||
|
return index_ >= other.index_; |
||||||
|
} |
||||||
|
}; |
||||||
|
std::ostream& operator<<(std::ostream& os, const StringIndex& index); |
||||||
|
|
||||||
|
class TypeIndex { |
||||||
|
public: |
||||||
|
uint16_t index_; |
||||||
|
|
||||||
|
constexpr TypeIndex() : index_(std::numeric_limits<decltype(index_)>::max()) {} |
||||||
|
explicit constexpr TypeIndex(uint16_t idx) : index_(idx) {} |
||||||
|
|
||||||
|
bool IsValid() const { |
||||||
|
return index_ != std::numeric_limits<decltype(index_)>::max(); |
||||||
|
} |
||||||
|
static TypeIndex Invalid() { |
||||||
|
return TypeIndex(std::numeric_limits<decltype(index_)>::max()); |
||||||
|
} |
||||||
|
|
||||||
|
bool operator==(const TypeIndex& other) const { |
||||||
|
return index_ == other.index_; |
||||||
|
} |
||||||
|
bool operator!=(const TypeIndex& other) const { |
||||||
|
return index_ != other.index_; |
||||||
|
} |
||||||
|
bool operator<(const TypeIndex& other) const { |
||||||
|
return index_ < other.index_; |
||||||
|
} |
||||||
|
bool operator<=(const TypeIndex& other) const { |
||||||
|
return index_ <= other.index_; |
||||||
|
} |
||||||
|
bool operator>(const TypeIndex& other) const { |
||||||
|
return index_ > other.index_; |
||||||
|
} |
||||||
|
bool operator>=(const TypeIndex& other) const { |
||||||
|
return index_ >= other.index_; |
||||||
|
} |
||||||
|
}; |
||||||
|
std::ostream& operator<<(std::ostream& os, const TypeIndex& index); |
||||||
|
|
||||||
|
} // namespace dex
|
||||||
|
} // namespace art_lkchan
|
||||||
|
|
||||||
|
namespace std { |
||||||
|
|
||||||
|
template<> struct hash<art_lkchan::dex::StringIndex> { |
||||||
|
size_t operator()(const art_lkchan::dex::StringIndex& index) const { |
||||||
|
return hash<uint32_t>()(index.index_); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
template<> struct hash<art_lkchan::dex::TypeIndex> { |
||||||
|
size_t operator()(const art_lkchan::dex::TypeIndex& index) const { |
||||||
|
return hash<uint16_t>()(index.index_); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
} // namespace std
|
||||||
|
|
||||||
|
#endif // ART_LIBDEXFILE_DEX_DEX_FILE_TYPES_H_
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,246 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011 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_DEX_FILE_VERIFIER_H_ |
||||||
|
#define ART_LIBDEXFILE_DEX_DEX_FILE_VERIFIER_H_ |
||||||
|
|
||||||
|
#include <unordered_set> |
||||||
|
|
||||||
|
#include "base/hash_map.h" |
||||||
|
#include "base/safe_map.h" |
||||||
|
#include "dex_file.h" |
||||||
|
#include "dex_file_types.h" |
||||||
|
|
||||||
|
namespace art_lkchan { |
||||||
|
|
||||||
|
class DexFileVerifier { |
||||||
|
public: |
||||||
|
static bool Verify(const DexFile* dex_file, |
||||||
|
const uint8_t* begin, |
||||||
|
size_t size, |
||||||
|
const char* location, |
||||||
|
bool verify_checksum, |
||||||
|
std::string* error_msg); |
||||||
|
|
||||||
|
const std::string& FailureReason() const { |
||||||
|
return failure_reason_; |
||||||
|
} |
||||||
|
|
||||||
|
private: |
||||||
|
DexFileVerifier(const DexFile* dex_file, |
||||||
|
const uint8_t* begin, |
||||||
|
size_t size, |
||||||
|
const char* location, |
||||||
|
bool verify_checksum) |
||||||
|
: dex_file_(dex_file), |
||||||
|
begin_(begin), |
||||||
|
size_(size), |
||||||
|
location_(location), |
||||||
|
verify_checksum_(verify_checksum), |
||||||
|
header_(&dex_file->GetHeader()), |
||||||
|
ptr_(nullptr), |
||||||
|
previous_item_(nullptr) { |
||||||
|
} |
||||||
|
|
||||||
|
bool Verify(); |
||||||
|
|
||||||
|
bool CheckShortyDescriptorMatch(char shorty_char, const char* descriptor, bool is_return_type); |
||||||
|
bool CheckListSize(const void* start, size_t count, size_t element_size, const char* label); |
||||||
|
// Check a list. The head is assumed to be at *ptr, and elements to be of size element_size. If
|
||||||
|
// successful, the ptr will be moved forward the amount covered by the list.
|
||||||
|
bool CheckList(size_t element_size, const char* label, const uint8_t* *ptr); |
||||||
|
// Checks whether the offset is zero (when size is zero) or that the offset falls within the area
|
||||||
|
// claimed by the file.
|
||||||
|
bool CheckValidOffsetAndSize(uint32_t offset, uint32_t size, size_t alignment, const char* label); |
||||||
|
// Checks whether the size is less than the limit.
|
||||||
|
bool CheckSizeLimit(uint32_t size, uint32_t limit, const char* label); |
||||||
|
bool CheckIndex(uint32_t field, uint32_t limit, const char* label); |
||||||
|
|
||||||
|
bool CheckHeader(); |
||||||
|
bool CheckMap(); |
||||||
|
|
||||||
|
uint32_t ReadUnsignedLittleEndian(uint32_t size); |
||||||
|
bool CheckAndGetHandlerOffsets(const DexFile::CodeItem* code_item, |
||||||
|
uint32_t* handler_offsets, uint32_t handlers_size); |
||||||
|
bool CheckClassDataItemField(uint32_t idx, |
||||||
|
uint32_t access_flags, |
||||||
|
uint32_t class_access_flags, |
||||||
|
dex::TypeIndex class_type_index, |
||||||
|
bool expect_static); |
||||||
|
bool CheckClassDataItemMethod(uint32_t idx, |
||||||
|
uint32_t access_flags, |
||||||
|
uint32_t class_access_flags, |
||||||
|
dex::TypeIndex class_type_index, |
||||||
|
uint32_t code_offset, |
||||||
|
std::unordered_set<uint32_t>* direct_method_indexes, |
||||||
|
bool expect_direct); |
||||||
|
bool CheckOrderAndGetClassDef(bool is_field, |
||||||
|
const char* type_descr, |
||||||
|
uint32_t curr_index, |
||||||
|
uint32_t prev_index, |
||||||
|
bool* have_class, |
||||||
|
dex::TypeIndex* class_type_index, |
||||||
|
const DexFile::ClassDef** class_def); |
||||||
|
bool CheckStaticFieldTypes(const DexFile::ClassDef* class_def); |
||||||
|
|
||||||
|
bool CheckPadding(size_t offset, uint32_t aligned_offset, DexFile::MapItemType type); |
||||||
|
bool CheckEncodedValue(); |
||||||
|
bool CheckEncodedArray(); |
||||||
|
bool CheckEncodedAnnotation(); |
||||||
|
|
||||||
|
bool CheckIntraClassDataItem(); |
||||||
|
// Check all fields of the given type from the given iterator. Load the class data from the first
|
||||||
|
// field, if necessary (and return it), or use the given values.
|
||||||
|
template <bool kStatic> |
||||||
|
bool CheckIntraClassDataItemFields(ClassDataItemIterator* it, |
||||||
|
bool* have_class, |
||||||
|
dex::TypeIndex* class_type_index, |
||||||
|
const DexFile::ClassDef** class_def); |
||||||
|
// Check all methods of the given type from the given iterator. Load the class data from the first
|
||||||
|
// method, if necessary (and return it), or use the given values.
|
||||||
|
template <bool kDirect> |
||||||
|
bool CheckIntraClassDataItemMethods(ClassDataItemIterator* it, |
||||||
|
std::unordered_set<uint32_t>* direct_method_indexes, |
||||||
|
bool* have_class, |
||||||
|
dex::TypeIndex* class_type_index, |
||||||
|
const DexFile::ClassDef** class_def); |
||||||
|
|
||||||
|
bool CheckIntraCodeItem(); |
||||||
|
bool CheckIntraStringDataItem(); |
||||||
|
bool CheckIntraDebugInfoItem(); |
||||||
|
bool CheckIntraAnnotationItem(); |
||||||
|
bool CheckIntraAnnotationsDirectoryItem(); |
||||||
|
|
||||||
|
bool CheckIntraSectionIterate(size_t offset, uint32_t count, DexFile::MapItemType type); |
||||||
|
bool CheckIntraIdSection(size_t offset, uint32_t count, DexFile::MapItemType type); |
||||||
|
bool CheckIntraDataSection(size_t offset, uint32_t count, DexFile::MapItemType type); |
||||||
|
bool CheckIntraSection(); |
||||||
|
|
||||||
|
bool CheckOffsetToTypeMap(size_t offset, uint16_t type); |
||||||
|
|
||||||
|
// Note: as sometimes kDexNoIndex16, being 0xFFFF, is a valid return value, we need an
|
||||||
|
// additional out parameter to signal any errors loading an index.
|
||||||
|
dex::TypeIndex FindFirstClassDataDefiner(const uint8_t* ptr, bool* success); |
||||||
|
dex::TypeIndex FindFirstAnnotationsDirectoryDefiner(const uint8_t* ptr, bool* success); |
||||||
|
|
||||||
|
bool CheckInterStringIdItem(); |
||||||
|
bool CheckInterTypeIdItem(); |
||||||
|
bool CheckInterProtoIdItem(); |
||||||
|
bool CheckInterFieldIdItem(); |
||||||
|
bool CheckInterMethodIdItem(); |
||||||
|
bool CheckInterClassDefItem(); |
||||||
|
bool CheckInterCallSiteIdItem(); |
||||||
|
bool CheckInterMethodHandleItem(); |
||||||
|
bool CheckInterAnnotationSetRefList(); |
||||||
|
bool CheckInterAnnotationSetItem(); |
||||||
|
bool CheckInterClassDataItem(); |
||||||
|
bool CheckInterAnnotationsDirectoryItem(); |
||||||
|
|
||||||
|
bool CheckInterSectionIterate(size_t offset, uint32_t count, DexFile::MapItemType type); |
||||||
|
bool CheckInterSection(); |
||||||
|
|
||||||
|
// Load a string by (type) index. Checks whether the index is in bounds, printing the error if
|
||||||
|
// not. If there is an error, null is returned.
|
||||||
|
const char* CheckLoadStringByIdx(dex::StringIndex idx, const char* error_fmt); |
||||||
|
const char* CheckLoadStringByTypeIdx(dex::TypeIndex type_idx, const char* error_fmt); |
||||||
|
|
||||||
|
// Load a field/method/proto Id by index. Checks whether the index is in bounds, printing the
|
||||||
|
// error if not. If there is an error, null is returned.
|
||||||
|
const DexFile::FieldId* CheckLoadFieldId(uint32_t idx, const char* error_fmt); |
||||||
|
const DexFile::MethodId* CheckLoadMethodId(uint32_t idx, const char* error_fmt); |
||||||
|
const DexFile::ProtoId* CheckLoadProtoId(uint32_t idx, const char* error_fmt); |
||||||
|
|
||||||
|
void ErrorStringPrintf(const char* fmt, ...) |
||||||
|
__attribute__((__format__(__printf__, 2, 3))) COLD_ATTR; |
||||||
|
bool FailureReasonIsSet() const { return failure_reason_.size() != 0; } |
||||||
|
|
||||||
|
// Retrieve class index and class def from the given member. index is the member index, which is
|
||||||
|
// taken as either a field or a method index (as designated by is_field). The result, if the
|
||||||
|
// member and declaring class could be found, is stored in class_type_index and class_def.
|
||||||
|
// This is an expensive lookup, as we have to find the class def by type index, which is a
|
||||||
|
// linear search. The output values should thus be cached by the caller.
|
||||||
|
bool FindClassIndexAndDef(uint32_t index, |
||||||
|
bool is_field, |
||||||
|
dex::TypeIndex* class_type_index, |
||||||
|
const DexFile::ClassDef** output_class_def); |
||||||
|
|
||||||
|
// Check validity of the given access flags, interpreted for a field in the context of a class
|
||||||
|
// with the given second access flags.
|
||||||
|
bool CheckFieldAccessFlags(uint32_t idx, |
||||||
|
uint32_t field_access_flags, |
||||||
|
uint32_t class_access_flags, |
||||||
|
std::string* error_message); |
||||||
|
|
||||||
|
// Check validity of the given method and access flags, in the context of a class with the given
|
||||||
|
// second access flags.
|
||||||
|
bool CheckMethodAccessFlags(uint32_t method_index, |
||||||
|
uint32_t method_access_flags, |
||||||
|
uint32_t class_access_flags, |
||||||
|
uint32_t constructor_flags_by_name, |
||||||
|
bool has_code, |
||||||
|
bool expect_direct, |
||||||
|
std::string* error_message); |
||||||
|
|
||||||
|
// Check validity of given method if it's a constructor or class initializer.
|
||||||
|
bool CheckConstructorProperties(uint32_t method_index, uint32_t constructor_flags); |
||||||
|
|
||||||
|
const DexFile* const dex_file_; |
||||||
|
const uint8_t* const begin_; |
||||||
|
const size_t size_; |
||||||
|
const char* const location_; |
||||||
|
const bool verify_checksum_; |
||||||
|
const DexFile::Header* const header_; |
||||||
|
|
||||||
|
struct OffsetTypeMapEmptyFn { |
||||||
|
// Make a hash map slot empty by making the offset 0. Offset 0 is a valid dex file offset that
|
||||||
|
// is in the offset of the dex file header. However, we only store data section items in the
|
||||||
|
// map, and these are after the header.
|
||||||
|
void MakeEmpty(std::pair<uint32_t, uint16_t>& pair) const { |
||||||
|
pair.first = 0u; |
||||||
|
} |
||||||
|
// Check if a hash map slot is empty.
|
||||||
|
bool IsEmpty(const std::pair<uint32_t, uint16_t>& pair) const { |
||||||
|
return pair.first == 0; |
||||||
|
} |
||||||
|
}; |
||||||
|
struct OffsetTypeMapHashCompareFn { |
||||||
|
// Hash function for offset.
|
||||||
|
size_t operator()(const uint32_t key) const { |
||||||
|
return key; |
||||||
|
} |
||||||
|
// std::equal function for offset.
|
||||||
|
bool operator()(const uint32_t a, const uint32_t b) const { |
||||||
|
return a == b; |
||||||
|
} |
||||||
|
}; |
||||||
|
// Map from offset to dex file type, HashMap for performance reasons.
|
||||||
|
HashMap<uint32_t, |
||||||
|
uint16_t, |
||||||
|
OffsetTypeMapEmptyFn, |
||||||
|
OffsetTypeMapHashCompareFn, |
||||||
|
OffsetTypeMapHashCompareFn> offset_to_type_map_; |
||||||
|
const uint8_t* ptr_; |
||||||
|
const void* previous_item_; |
||||||
|
|
||||||
|
std::string failure_reason_; |
||||||
|
|
||||||
|
// Set of type ids for which there are ClassDef elements in the dex file.
|
||||||
|
std::unordered_set<decltype(DexFile::ClassDef::class_idx_)> defined_classes_; |
||||||
|
}; |
||||||
|
|
||||||
|
} // namespace art_lkchan
|
||||||
|
|
||||||
|
#endif // ART_LIBDEXFILE_DEX_DEX_FILE_VERIFIER_H_
|
@ -0,0 +1,558 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011 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_DEX_INSTRUCTION_INL_H_ |
||||||
|
#define ART_LIBDEXFILE_DEX_DEX_INSTRUCTION_INL_H_ |
||||||
|
|
||||||
|
#include "dex_instruction.h" |
||||||
|
|
||||||
|
namespace art_lkchan { |
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// VRegA
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
inline bool Instruction::HasVRegA() const { |
||||||
|
switch (FormatOf(Opcode())) { |
||||||
|
case k10t: return true; |
||||||
|
case k10x: return true; |
||||||
|
case k11n: return true; |
||||||
|
case k11x: return true; |
||||||
|
case k12x: return true; |
||||||
|
case k20t: return true; |
||||||
|
case k21c: return true; |
||||||
|
case k21h: return true; |
||||||
|
case k21s: return true; |
||||||
|
case k21t: return true; |
||||||
|
case k22b: return true; |
||||||
|
case k22c: return true; |
||||||
|
case k22s: return true; |
||||||
|
case k22t: return true; |
||||||
|
case k22x: return true; |
||||||
|
case k23x: return true; |
||||||
|
case k30t: return true; |
||||||
|
case k31c: return true; |
||||||
|
case k31i: return true; |
||||||
|
case k31t: return true; |
||||||
|
case k32x: return true; |
||||||
|
case k35c: return true; |
||||||
|
case k3rc: return true; |
||||||
|
case k45cc: return true; |
||||||
|
case k4rcc: return true; |
||||||
|
case k51l: return true; |
||||||
|
default: return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
inline int32_t Instruction::VRegA() const { |
||||||
|
switch (FormatOf(Opcode())) { |
||||||
|
case k10t: return VRegA_10t(); |
||||||
|
case k10x: return VRegA_10x(); |
||||||
|
case k11n: return VRegA_11n(); |
||||||
|
case k11x: return VRegA_11x(); |
||||||
|
case k12x: return VRegA_12x(); |
||||||
|
case k20t: return VRegA_20t(); |
||||||
|
case k21c: return VRegA_21c(); |
||||||
|
case k21h: return VRegA_21h(); |
||||||
|
case k21s: return VRegA_21s(); |
||||||
|
case k21t: return VRegA_21t(); |
||||||
|
case k22b: return VRegA_22b(); |
||||||
|
case k22c: return VRegA_22c(); |
||||||
|
case k22s: return VRegA_22s(); |
||||||
|
case k22t: return VRegA_22t(); |
||||||
|
case k22x: return VRegA_22x(); |
||||||
|
case k23x: return VRegA_23x(); |
||||||
|
case k30t: return VRegA_30t(); |
||||||
|
case k31c: return VRegA_31c(); |
||||||
|
case k31i: return VRegA_31i(); |
||||||
|
case k31t: return VRegA_31t(); |
||||||
|
case k32x: return VRegA_32x(); |
||||||
|
case k35c: return VRegA_35c(); |
||||||
|
case k3rc: return VRegA_3rc(); |
||||||
|
case k45cc: return VRegA_45cc(); |
||||||
|
case k4rcc: return VRegA_4rcc(); |
||||||
|
case k51l: return VRegA_51l(); |
||||||
|
default: |
||||||
|
LOG(FATAL) << "Tried to access vA of instruction " << Name() << " which has no A operand."; |
||||||
|
exit(EXIT_FAILURE); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
inline int8_t Instruction::VRegA_10t(uint16_t inst_data) const { |
||||||
|
DCHECK_EQ(FormatOf(Opcode()), k10t); |
||||||
|
return static_cast<int8_t>(InstAA(inst_data)); |
||||||
|
} |
||||||
|
|
||||||
|
inline uint8_t Instruction::VRegA_10x(uint16_t inst_data) const { |
||||||
|
DCHECK_EQ(FormatOf(Opcode()), k10x); |
||||||
|
return InstAA(inst_data); |
||||||
|
} |
||||||
|
|
||||||
|
inline uint4_t Instruction::VRegA_11n(uint16_t inst_data) const { |
||||||
|
DCHECK_EQ(FormatOf(Opcode()), k11n); |
||||||
|
return InstA(inst_data); |
||||||
|
} |
||||||
|
|
||||||
|
inline uint8_t Instruction::VRegA_11x(uint16_t inst_data) const { |
||||||
|
DCHECK_EQ(FormatOf(Opcode()), k11x); |
||||||
|
return InstAA(inst_data); |
||||||
|
} |
||||||
|
|
||||||
|
inline uint4_t Instruction::VRegA_12x(uint16_t inst_data) const { |
||||||
|
DCHECK_EQ(FormatOf(Opcode()), k12x); |
||||||
|
return InstA(inst_data); |
||||||
|
} |
||||||
|
|
||||||
|
inline int16_t Instruction::VRegA_20t() const { |
||||||
|
DCHECK_EQ(FormatOf(Opcode()), k20t); |
||||||
|
return static_cast<int16_t>(Fetch16(1)); |
||||||
|
} |
||||||
|
|
||||||
|
inline uint8_t Instruction::VRegA_21c(uint16_t inst_data) const { |
||||||
|
DCHECK_EQ(FormatOf(Opcode()), k21c); |
||||||
|
return InstAA(inst_data); |
||||||
|
} |
||||||
|
|
||||||
|
inline uint8_t Instruction::VRegA_21h(uint16_t inst_data) const { |
||||||
|
DCHECK_EQ(FormatOf(Opcode()), k21h); |
||||||
|
return InstAA(inst_data); |
||||||
|
} |
||||||
|
|
||||||
|
inline uint8_t Instruction::VRegA_21s(uint16_t inst_data) const { |
||||||
|
DCHECK_EQ(FormatOf(Opcode()), k21s); |
||||||
|
return InstAA(inst_data); |
||||||
|
} |
||||||
|
|
||||||
|
inline uint8_t Instruction::VRegA_21t(uint16_t inst_data) const { |
||||||
|
DCHECK_EQ(FormatOf(Opcode()), k21t); |
||||||
|
return InstAA(inst_data); |
||||||
|
} |
||||||
|
|
||||||
|
inline uint8_t Instruction::VRegA_22b(uint16_t inst_data) const { |
||||||
|
DCHECK_EQ(FormatOf(Opcode()), k22b); |
||||||
|
return InstAA(inst_data); |
||||||
|
} |
||||||
|
|
||||||
|
inline uint4_t Instruction::VRegA_22c(uint16_t inst_data) const { |
||||||
|
DCHECK_EQ(FormatOf(Opcode()), k22c); |
||||||
|
return InstA(inst_data); |
||||||
|
} |
||||||
|
|
||||||
|
inline uint4_t Instruction::VRegA_22s(uint16_t inst_data) const { |
||||||
|
DCHECK_EQ(FormatOf(Opcode()), k22s); |
||||||
|
return InstA(inst_data); |
||||||
|
} |
||||||
|
|
||||||
|
inline uint4_t Instruction::VRegA_22t(uint16_t inst_data) const { |
||||||
|
DCHECK_EQ(FormatOf(Opcode()), k22t); |
||||||
|
return InstA(inst_data); |
||||||
|
} |
||||||
|
|
||||||
|
inline uint8_t Instruction::VRegA_22x(uint16_t inst_data) const { |
||||||
|
DCHECK_EQ(FormatOf(Opcode()), k22x); |
||||||
|
return InstAA(inst_data); |
||||||
|
} |
||||||
|
|
||||||
|
inline uint8_t Instruction::VRegA_23x(uint16_t inst_data) const { |
||||||
|
DCHECK_EQ(FormatOf(Opcode()), k23x); |
||||||
|
return InstAA(inst_data); |
||||||
|
} |
||||||
|
|
||||||
|
inline int32_t Instruction::VRegA_30t() const { |
||||||
|
DCHECK_EQ(FormatOf(Opcode()), k30t); |
||||||
|
return static_cast<int32_t>(Fetch32(1)); |
||||||
|
} |
||||||
|
|
||||||
|
inline uint8_t Instruction::VRegA_31c(uint16_t inst_data) const { |
||||||
|
DCHECK_EQ(FormatOf(Opcode()), k31c); |
||||||
|
return InstAA(inst_data); |
||||||
|
} |
||||||
|
|
||||||
|
inline uint8_t Instruction::VRegA_31i(uint16_t inst_data) const { |
||||||
|
DCHECK_EQ(FormatOf(Opcode()), k31i); |
||||||
|
return InstAA(inst_data); |
||||||
|
} |
||||||
|
|
||||||
|
inline uint8_t Instruction::VRegA_31t(uint16_t inst_data) const { |
||||||
|
DCHECK_EQ(FormatOf(Opcode()), k31t); |
||||||
|
return InstAA(inst_data); |
||||||
|
} |
||||||
|
|
||||||
|
inline uint16_t Instruction::VRegA_32x() const { |
||||||
|
DCHECK_EQ(FormatOf(Opcode()), k32x); |
||||||
|
return Fetch16(1); |
||||||
|
} |
||||||
|
|
||||||
|
inline uint4_t Instruction::VRegA_35c(uint16_t inst_data) const { |
||||||
|
DCHECK_EQ(FormatOf(Opcode()), k35c); |
||||||
|
return InstB(inst_data); // This is labeled A in the spec.
|
||||||
|
} |
||||||
|
|
||||||
|
inline uint8_t Instruction::VRegA_3rc(uint16_t inst_data) const { |
||||||
|
DCHECK_EQ(FormatOf(Opcode()), k3rc); |
||||||
|
return InstAA(inst_data); |
||||||
|
} |
||||||
|
|
||||||
|
inline uint8_t Instruction::VRegA_51l(uint16_t inst_data) const { |
||||||
|
DCHECK_EQ(FormatOf(Opcode()), k51l); |
||||||
|
return InstAA(inst_data); |
||||||
|
} |
||||||
|
|
||||||
|
inline uint4_t Instruction::VRegA_45cc(uint16_t inst_data) const { |
||||||
|
DCHECK_EQ(FormatOf(Opcode()), k45cc); |
||||||
|
return InstB(inst_data); // This is labeled A in the spec.
|
||||||
|
} |
||||||
|
|
||||||
|
inline uint8_t Instruction::VRegA_4rcc(uint16_t inst_data) const { |
||||||
|
DCHECK_EQ(FormatOf(Opcode()), k4rcc); |
||||||
|
return InstAA(inst_data); |
||||||
|
} |
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// VRegB
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
inline bool Instruction::HasVRegB() const { |
||||||
|
switch (FormatOf(Opcode())) { |
||||||
|
case k11n: return true; |
||||||
|
case k12x: return true; |
||||||
|
case k21c: return true; |
||||||
|
case k21h: return true; |
||||||
|
case k21s: return true; |
||||||
|
case k21t: return true; |
||||||
|
case k22b: return true; |
||||||
|
case k22c: return true; |
||||||
|
case k22s: return true; |
||||||
|
case k22t: return true; |
||||||
|
case k22x: return true; |
||||||
|
case k23x: return true; |
||||||
|
case k31c: return true; |
||||||
|
case k31i: return true; |
||||||
|
case k31t: return true; |
||||||
|
case k32x: return true; |
||||||
|
case k35c: return true; |
||||||
|
case k3rc: return true; |
||||||
|
case k45cc: return true; |
||||||
|
case k4rcc: return true; |
||||||
|
case k51l: return true; |
||||||
|
default: return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
inline bool Instruction::HasWideVRegB() const { |
||||||
|
return FormatOf(Opcode()) == k51l; |
||||||
|
} |
||||||
|
|
||||||
|
inline int32_t Instruction::VRegB() const { |
||||||
|
switch (FormatOf(Opcode())) { |
||||||
|
case k11n: return VRegB_11n(); |
||||||
|
case k12x: return VRegB_12x(); |
||||||
|
case k21c: return VRegB_21c(); |
||||||
|
case k21h: return VRegB_21h(); |
||||||
|
case k21s: return VRegB_21s(); |
||||||
|
case k21t: return VRegB_21t(); |
||||||
|
case k22b: return VRegB_22b(); |
||||||
|
case k22c: return VRegB_22c(); |
||||||
|
case k22s: return VRegB_22s(); |
||||||
|
case k22t: return VRegB_22t(); |
||||||
|
case k22x: return VRegB_22x(); |
||||||
|
case k23x: return VRegB_23x(); |
||||||
|
case k31c: return VRegB_31c(); |
||||||
|
case k31i: return VRegB_31i(); |
||||||
|
case k31t: return VRegB_31t(); |
||||||
|
case k32x: return VRegB_32x(); |
||||||
|
case k35c: return VRegB_35c(); |
||||||
|
case k3rc: return VRegB_3rc(); |
||||||
|
case k45cc: return VRegB_45cc(); |
||||||
|
case k4rcc: return VRegB_4rcc(); |
||||||
|
case k51l: return VRegB_51l(); |
||||||
|
default: |
||||||
|
LOG(FATAL) << "Tried to access vB of instruction " << Name() << " which has no B operand."; |
||||||
|
exit(EXIT_FAILURE); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
inline uint64_t Instruction::WideVRegB() const { |
||||||
|
return VRegB_51l(); |
||||||
|
} |
||||||
|
|
||||||
|
inline int4_t Instruction::VRegB_11n(uint16_t inst_data) const { |
||||||
|
DCHECK_EQ(FormatOf(Opcode()), k11n); |
||||||
|
return static_cast<int4_t>((InstB(inst_data) << 28) >> 28); |
||||||
|
} |
||||||
|
|
||||||
|
inline uint4_t Instruction::VRegB_12x(uint16_t inst_data) const { |
||||||
|
DCHECK_EQ(FormatOf(Opcode()), k12x); |
||||||
|
return InstB(inst_data); |
||||||
|
} |
||||||
|
|
||||||
|
inline uint16_t Instruction::VRegB_21c() const { |
||||||
|
DCHECK_EQ(FormatOf(Opcode()), k21c); |
||||||
|
return Fetch16(1); |
||||||
|
} |
||||||
|
|
||||||
|
inline uint16_t Instruction::VRegB_21h() const { |
||||||
|
DCHECK_EQ(FormatOf(Opcode()), k21h); |
||||||
|
return Fetch16(1); |
||||||
|
} |
||||||
|
|
||||||
|
inline int16_t Instruction::VRegB_21s() const { |
||||||
|
DCHECK_EQ(FormatOf(Opcode()), k21s); |
||||||
|
return static_cast<int16_t>(Fetch16(1)); |
||||||
|
} |
||||||
|
|
||||||
|
inline int16_t Instruction::VRegB_21t() const { |
||||||
|
DCHECK_EQ(FormatOf(Opcode()), k21t); |
||||||
|
return static_cast<int16_t>(Fetch16(1)); |
||||||
|
} |
||||||
|
|
||||||
|
inline uint8_t Instruction::VRegB_22b() const { |
||||||
|
DCHECK_EQ(FormatOf(Opcode()), k22b); |
||||||
|
return static_cast<uint8_t>(Fetch16(1) & 0xff); |
||||||
|
} |
||||||
|
|
||||||
|
inline uint4_t Instruction::VRegB_22c(uint16_t inst_data) const { |
||||||
|
DCHECK_EQ(FormatOf(Opcode()), k22c); |
||||||
|
return InstB(inst_data); |
||||||
|
} |
||||||
|
|
||||||
|
inline uint4_t Instruction::VRegB_22s(uint16_t inst_data) const { |
||||||
|
DCHECK_EQ(FormatOf(Opcode()), k22s); |
||||||
|
return InstB(inst_data); |
||||||
|
} |
||||||
|
|
||||||
|
inline uint4_t Instruction::VRegB_22t(uint16_t inst_data) const { |
||||||
|
DCHECK_EQ(FormatOf(Opcode()), k22t); |
||||||
|
return InstB(inst_data); |
||||||
|
} |
||||||
|
|
||||||
|
inline uint16_t Instruction::VRegB_22x() const { |
||||||
|
DCHECK_EQ(FormatOf(Opcode()), k22x); |
||||||
|
return Fetch16(1); |
||||||
|
} |
||||||
|
|
||||||
|
inline uint8_t Instruction::VRegB_23x() const { |
||||||
|
DCHECK_EQ(FormatOf(Opcode()), k23x); |
||||||
|
return static_cast<uint8_t>(Fetch16(1) & 0xff); |
||||||
|
} |
||||||
|
|
||||||
|
inline uint32_t Instruction::VRegB_31c() const { |
||||||
|
DCHECK_EQ(FormatOf(Opcode()), k31c); |
||||||
|
return Fetch32(1); |
||||||
|
} |
||||||
|
|
||||||
|
inline int32_t Instruction::VRegB_31i() const { |
||||||
|
DCHECK_EQ(FormatOf(Opcode()), k31i); |
||||||
|
return static_cast<int32_t>(Fetch32(1)); |
||||||
|
} |
||||||
|
|
||||||
|
inline int32_t Instruction::VRegB_31t() const { |
||||||
|
DCHECK_EQ(FormatOf(Opcode()), k31t); |
||||||
|
return static_cast<int32_t>(Fetch32(1)); |
||||||
|
} |
||||||
|
|
||||||
|
inline uint16_t Instruction::VRegB_32x() const { |
||||||
|
DCHECK_EQ(FormatOf(Opcode()), k32x); |
||||||
|
return Fetch16(2); |
||||||
|
} |
||||||
|
|
||||||
|
inline uint16_t Instruction::VRegB_35c() const { |
||||||
|
DCHECK_EQ(FormatOf(Opcode()), k35c); |
||||||
|
return Fetch16(1); |
||||||
|
} |
||||||
|
|
||||||
|
inline uint16_t Instruction::VRegB_3rc() const { |
||||||
|
DCHECK_EQ(FormatOf(Opcode()), k3rc); |
||||||
|
return Fetch16(1); |
||||||
|
} |
||||||
|
|
||||||
|
inline uint16_t Instruction::VRegB_45cc() const { |
||||||
|
DCHECK_EQ(FormatOf(Opcode()), k45cc); |
||||||
|
return Fetch16(1); |
||||||
|
} |
||||||
|
|
||||||
|
inline uint16_t Instruction::VRegB_4rcc() const { |
||||||
|
DCHECK_EQ(FormatOf(Opcode()), k4rcc); |
||||||
|
return Fetch16(1); |
||||||
|
} |
||||||
|
|
||||||
|
inline uint64_t Instruction::VRegB_51l() const { |
||||||
|
DCHECK_EQ(FormatOf(Opcode()), k51l); |
||||||
|
uint64_t vB_wide = Fetch32(1) | ((uint64_t) Fetch32(3) << 32); |
||||||
|
return vB_wide; |
||||||
|
} |
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// VRegC
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
inline bool Instruction::HasVRegC() const { |
||||||
|
switch (FormatOf(Opcode())) { |
||||||
|
case k22b: return true; |
||||||
|
case k22c: return true; |
||||||
|
case k22s: return true; |
||||||
|
case k22t: return true; |
||||||
|
case k23x: return true; |
||||||
|
case k35c: return true; |
||||||
|
case k3rc: return true; |
||||||
|
case k45cc: return true; |
||||||
|
case k4rcc: return true; |
||||||
|
default: return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
inline int32_t Instruction::VRegC() const { |
||||||
|
switch (FormatOf(Opcode())) { |
||||||
|
case k22b: return VRegC_22b(); |
||||||
|
case k22c: return VRegC_22c(); |
||||||
|
case k22s: return VRegC_22s(); |
||||||
|
case k22t: return VRegC_22t(); |
||||||
|
case k23x: return VRegC_23x(); |
||||||
|
case k35c: return VRegC_35c(); |
||||||
|
case k3rc: return VRegC_3rc(); |
||||||
|
case k45cc: return VRegC_45cc(); |
||||||
|
case k4rcc: return VRegC_4rcc(); |
||||||
|
default: |
||||||
|
LOG(FATAL) << "Tried to access vC of instruction " << Name() << " which has no C operand."; |
||||||
|
exit(EXIT_FAILURE); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
inline int8_t Instruction::VRegC_22b() const { |
||||||
|
DCHECK_EQ(FormatOf(Opcode()), k22b); |
||||||
|
return static_cast<int8_t>(Fetch16(1) >> 8); |
||||||
|
} |
||||||
|
|
||||||
|
inline uint16_t Instruction::VRegC_22c() const { |
||||||
|
DCHECK_EQ(FormatOf(Opcode()), k22c); |
||||||
|
return Fetch16(1); |
||||||
|
} |
||||||
|
|
||||||
|
inline int16_t Instruction::VRegC_22s() const { |
||||||
|
DCHECK_EQ(FormatOf(Opcode()), k22s); |
||||||
|
return static_cast<int16_t>(Fetch16(1)); |
||||||
|
} |
||||||
|
|
||||||
|
inline int16_t Instruction::VRegC_22t() const { |
||||||
|
DCHECK_EQ(FormatOf(Opcode()), k22t); |
||||||
|
return static_cast<int16_t>(Fetch16(1)); |
||||||
|
} |
||||||
|
|
||||||
|
inline uint8_t Instruction::VRegC_23x() const { |
||||||
|
DCHECK_EQ(FormatOf(Opcode()), k23x); |
||||||
|
return static_cast<uint8_t>(Fetch16(1) >> 8); |
||||||
|
} |
||||||
|
|
||||||
|
inline uint4_t Instruction::VRegC_35c() const { |
||||||
|
DCHECK_EQ(FormatOf(Opcode()), k35c); |
||||||
|
return static_cast<uint4_t>(Fetch16(2) & 0x0f); |
||||||
|
} |
||||||
|
|
||||||
|
inline uint16_t Instruction::VRegC_3rc() const { |
||||||
|
DCHECK_EQ(FormatOf(Opcode()), k3rc); |
||||||
|
return Fetch16(2); |
||||||
|
} |
||||||
|
|
||||||
|
inline uint4_t Instruction::VRegC_45cc() const { |
||||||
|
DCHECK_EQ(FormatOf(Opcode()), k45cc); |
||||||
|
return static_cast<uint4_t>(Fetch16(2) & 0x0f); |
||||||
|
} |
||||||
|
|
||||||
|
inline uint16_t Instruction::VRegC_4rcc() const { |
||||||
|
DCHECK_EQ(FormatOf(Opcode()), k4rcc); |
||||||
|
return Fetch16(2); |
||||||
|
} |
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// VRegH
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
inline bool Instruction::HasVRegH() const { |
||||||
|
switch (FormatOf(Opcode())) { |
||||||
|
case k45cc: return true; |
||||||
|
case k4rcc: return true; |
||||||
|
default : return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
inline int32_t Instruction::VRegH() const { |
||||||
|
switch (FormatOf(Opcode())) { |
||||||
|
case k45cc: return VRegH_45cc(); |
||||||
|
case k4rcc: return VRegH_4rcc(); |
||||||
|
default : |
||||||
|
LOG(FATAL) << "Tried to access vH of instruction " << Name() << " which has no H operand."; |
||||||
|
exit(EXIT_FAILURE); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
inline uint16_t Instruction::VRegH_45cc() const { |
||||||
|
DCHECK_EQ(FormatOf(Opcode()), k45cc); |
||||||
|
return Fetch16(3); |
||||||
|
} |
||||||
|
|
||||||
|
inline uint16_t Instruction::VRegH_4rcc() const { |
||||||
|
DCHECK_EQ(FormatOf(Opcode()), k4rcc); |
||||||
|
return Fetch16(3); |
||||||
|
} |
||||||
|
|
||||||
|
inline bool Instruction::HasVarArgs() const { |
||||||
|
return (FormatOf(Opcode()) == k35c) || (FormatOf(Opcode()) == k45cc); |
||||||
|
} |
||||||
|
|
||||||
|
inline void Instruction::GetVarArgs(uint32_t arg[kMaxVarArgRegs], uint16_t inst_data) const { |
||||||
|
DCHECK(HasVarArgs()); |
||||||
|
|
||||||
|
/*
|
||||||
|
* Note that the fields mentioned in the spec don't appear in |
||||||
|
* their "usual" positions here compared to most formats. This |
||||||
|
* was done so that the field names for the argument count and |
||||||
|
* reference index match between this format and the corresponding |
||||||
|
* range formats (3rc and friends). |
||||||
|
* |
||||||
|
* Bottom line: The argument count is always in vA, and the |
||||||
|
* method constant (or equivalent) is always in vB. |
||||||
|
*/ |
||||||
|
uint16_t regList = Fetch16(2); |
||||||
|
uint4_t count = InstB(inst_data); // This is labeled A in the spec.
|
||||||
|
DCHECK_LE(count, 5U) << "Invalid arg count in 35c (" << count << ")"; |
||||||
|
|
||||||
|
/*
|
||||||
|
* Copy the argument registers into the arg[] array, and |
||||||
|
* also copy the first argument (if any) into vC. (The |
||||||
|
* DecodedInstruction structure doesn't have separate |
||||||
|
* fields for {vD, vE, vF, vG}, so there's no need to make |
||||||
|
* copies of those.) Note that cases 5..2 fall through. |
||||||
|
*/ |
||||||
|
switch (count) { |
||||||
|
case 5: |
||||||
|
arg[4] = InstA(inst_data); |
||||||
|
FALLTHROUGH_INTENDED; |
||||||
|
case 4: |
||||||
|
arg[3] = (regList >> 12) & 0x0f; |
||||||
|
FALLTHROUGH_INTENDED; |
||||||
|
case 3: |
||||||
|
arg[2] = (regList >> 8) & 0x0f; |
||||||
|
FALLTHROUGH_INTENDED; |
||||||
|
case 2: |
||||||
|
arg[1] = (regList >> 4) & 0x0f; |
||||||
|
FALLTHROUGH_INTENDED; |
||||||
|
case 1: |
||||||
|
arg[0] = regList & 0x0f; |
||||||
|
break; |
||||||
|
default: // case 0
|
||||||
|
break; // Valid, but no need to do anything.
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace art_lkchan
|
||||||
|
|
||||||
|
#endif // ART_LIBDEXFILE_DEX_DEX_INSTRUCTION_INL_H_
|
@ -0,0 +1,581 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011 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. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include "dex_instruction-inl.h" |
||||||
|
|
||||||
|
#include <inttypes.h> |
||||||
|
|
||||||
|
#include <iomanip> |
||||||
|
#include <sstream> |
||||||
|
|
||||||
|
#include "android-base/stringprintf.h" |
||||||
|
|
||||||
|
#include "dex_file-inl.h" |
||||||
|
#include "utf.h" |
||||||
|
|
||||||
|
namespace art_lkchan { |
||||||
|
|
||||||
|
using android_lkchan::base::StringPrintf; |
||||||
|
|
||||||
|
const char* const Instruction::kInstructionNames[] = { |
||||||
|
#define INSTRUCTION_NAME(o, c, pname, f, i, a, e, v) pname, |
||||||
|
#include "dex_instruction_list.h" |
||||||
|
DEX_INSTRUCTION_LIST(INSTRUCTION_NAME) |
||||||
|
#undef DEX_INSTRUCTION_LIST |
||||||
|
#undef INSTRUCTION_NAME |
||||||
|
}; |
||||||
|
|
||||||
|
static_assert(sizeof(Instruction::InstructionDescriptor) == 8u, "Unexpected descriptor size"); |
||||||
|
|
||||||
|
static constexpr int8_t InstructionSizeInCodeUnitsByOpcode(Instruction::Code opcode, |
||||||
|
Instruction::Format format) { |
||||||
|
if (opcode == Instruction::Code::NOP) { |
||||||
|
return -1; |
||||||
|
} else if ((format >= Instruction::Format::k10x) && (format <= Instruction::Format::k10t)) { |
||||||
|
return 1; |
||||||
|
} else if ((format >= Instruction::Format::k20t) && (format <= Instruction::Format::k22c)) { |
||||||
|
return 2; |
||||||
|
} else if ((format >= Instruction::Format::k32x) && (format <= Instruction::Format::k3rc)) { |
||||||
|
return 3; |
||||||
|
} else if ((format >= Instruction::Format::k45cc) && (format <= Instruction::Format::k4rcc)) { |
||||||
|
return 4; |
||||||
|
} else if (format == Instruction::Format::k51l) { |
||||||
|
return 5; |
||||||
|
} else { |
||||||
|
return -1; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Instruction::InstructionDescriptor const Instruction::kInstructionDescriptors[] = { |
||||||
|
#define INSTRUCTION_DESCR(opcode, c, p, format, index, flags, eflags, vflags) \ |
||||||
|
{ vflags, \
|
||||||
|
format, \
|
||||||
|
index, \
|
||||||
|
flags, \
|
||||||
|
InstructionSizeInCodeUnitsByOpcode((c), (format)), \
|
||||||
|
}, |
||||||
|
#include "dex_instruction_list.h" |
||||||
|
DEX_INSTRUCTION_LIST(INSTRUCTION_DESCR) |
||||||
|
#undef DEX_INSTRUCTION_LIST |
||||||
|
#undef INSTRUCTION_DESCR |
||||||
|
}; |
||||||
|
|
||||||
|
int32_t Instruction::GetTargetOffset() const { |
||||||
|
switch (FormatOf(Opcode())) { |
||||||
|
// Cases for conditional branches follow.
|
||||||
|
case k22t: return VRegC_22t(); |
||||||
|
case k21t: return VRegB_21t(); |
||||||
|
// Cases for unconditional branches follow.
|
||||||
|
case k10t: return VRegA_10t(); |
||||||
|
case k20t: return VRegA_20t(); |
||||||
|
case k30t: return VRegA_30t(); |
||||||
|
default: LOG(FATAL) << "Tried to access the branch offset of an instruction " << Name() << |
||||||
|
" which does not have a target operand."; |
||||||
|
} |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
bool Instruction::CanFlowThrough() const { |
||||||
|
const uint16_t* insns = reinterpret_cast<const uint16_t*>(this); |
||||||
|
uint16_t insn = *insns; |
||||||
|
Code opcode = static_cast<Code>(insn & 0xFF); |
||||||
|
return FlagsOf(opcode) & Instruction::kContinue; |
||||||
|
} |
||||||
|
|
||||||
|
size_t Instruction::SizeInCodeUnitsComplexOpcode() const { |
||||||
|
const uint16_t* insns = reinterpret_cast<const uint16_t*>(this); |
||||||
|
// Handle special NOP encoded variable length sequences.
|
||||||
|
switch (*insns) { |
||||||
|
case kPackedSwitchSignature: |
||||||
|
return (4 + insns[1] * 2); |
||||||
|
case kSparseSwitchSignature: |
||||||
|
return (2 + insns[1] * 4); |
||||||
|
case kArrayDataSignature: { |
||||||
|
uint16_t element_size = insns[1]; |
||||||
|
uint32_t length = insns[2] | (((uint32_t)insns[3]) << 16); |
||||||
|
// The plus 1 is to round up for odd size and width.
|
||||||
|
return (4 + (element_size * length + 1) / 2); |
||||||
|
} |
||||||
|
default: |
||||||
|
if ((*insns & 0xFF) == 0) { |
||||||
|
return 1; // NOP.
|
||||||
|
} else { |
||||||
|
LOG(FATAL) << "Unreachable: " << DumpString(nullptr); |
||||||
|
UNREACHABLE(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
size_t Instruction::CodeUnitsRequiredForSizeOfComplexOpcode() const { |
||||||
|
const uint16_t* insns = reinterpret_cast<const uint16_t*>(this); |
||||||
|
// Handle special NOP encoded variable length sequences.
|
||||||
|
switch (*insns) { |
||||||
|
case kPackedSwitchSignature: |
||||||
|
FALLTHROUGH_INTENDED; |
||||||
|
case kSparseSwitchSignature: |
||||||
|
return 2; |
||||||
|
case kArrayDataSignature: |
||||||
|
return 4; |
||||||
|
default: |
||||||
|
if ((*insns & 0xFF) == 0) { |
||||||
|
return 1; // NOP.
|
||||||
|
} else { |
||||||
|
LOG(FATAL) << "Unreachable: " << DumpString(nullptr); |
||||||
|
UNREACHABLE(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
std::string Instruction::DumpHex(size_t code_units) const { |
||||||
|
size_t inst_length = SizeInCodeUnits(); |
||||||
|
if (inst_length > code_units) { |
||||||
|
inst_length = code_units; |
||||||
|
} |
||||||
|
std::ostringstream os; |
||||||
|
const uint16_t* insn = reinterpret_cast<const uint16_t*>(this); |
||||||
|
for (size_t i = 0; i < inst_length; i++) { |
||||||
|
os << StringPrintf("0x%04x", insn[i]) << " "; |
||||||
|
} |
||||||
|
for (size_t i = inst_length; i < code_units; i++) { |
||||||
|
os << " "; |
||||||
|
} |
||||||
|
return os.str(); |
||||||
|
} |
||||||
|
|
||||||
|
std::string Instruction::DumpHexLE(size_t instr_code_units) const { |
||||||
|
size_t inst_length = SizeInCodeUnits(); |
||||||
|
if (inst_length > instr_code_units) { |
||||||
|
inst_length = instr_code_units; |
||||||
|
} |
||||||
|
std::ostringstream os; |
||||||
|
const uint16_t* insn = reinterpret_cast<const uint16_t*>(this); |
||||||
|
for (size_t i = 0; i < inst_length; i++) { |
||||||
|
os << StringPrintf("%02x%02x", static_cast<uint8_t>(insn[i] & 0x00FF), |
||||||
|
static_cast<uint8_t>((insn[i] & 0xFF00) >> 8)) << " "; |
||||||
|
} |
||||||
|
for (size_t i = inst_length; i < instr_code_units; i++) { |
||||||
|
os << " "; |
||||||
|
} |
||||||
|
return os.str(); |
||||||
|
} |
||||||
|
|
||||||
|
std::string Instruction::DumpString(const DexFile* file) const { |
||||||
|
std::ostringstream os; |
||||||
|
const char* opcode = kInstructionNames[Opcode()]; |
||||||
|
switch (FormatOf(Opcode())) { |
||||||
|
case k10x: os << opcode; break; |
||||||
|
case k12x: os << StringPrintf("%s v%d, v%d", opcode, VRegA_12x(), VRegB_12x()); break; |
||||||
|
case k11n: os << StringPrintf("%s v%d, #%+d", opcode, VRegA_11n(), VRegB_11n()); break; |
||||||
|
case k11x: os << StringPrintf("%s v%d", opcode, VRegA_11x()); break; |
||||||
|
case k10t: os << StringPrintf("%s %+d", opcode, VRegA_10t()); break; |
||||||
|
case k20t: os << StringPrintf("%s %+d", opcode, VRegA_20t()); break; |
||||||
|
case k22x: os << StringPrintf("%s v%d, v%d", opcode, VRegA_22x(), VRegB_22x()); break; |
||||||
|
case k21t: os << StringPrintf("%s v%d, %+d", opcode, VRegA_21t(), VRegB_21t()); break; |
||||||
|
case k21s: os << StringPrintf("%s v%d, #%+d", opcode, VRegA_21s(), VRegB_21s()); break; |
||||||
|
case k21h: { |
||||||
|
// op vAA, #+BBBB0000[00000000]
|
||||||
|
if (Opcode() == CONST_HIGH16) { |
||||||
|
uint32_t value = VRegB_21h() << 16; |
||||||
|
os << StringPrintf("%s v%d, #int %+d // 0x%x", opcode, VRegA_21h(), value, value); |
||||||
|
} else { |
||||||
|
uint64_t value = static_cast<uint64_t>(VRegB_21h()) << 48; |
||||||
|
os << StringPrintf("%s v%d, #long %+" PRId64 " // 0x%" PRIx64, opcode, VRegA_21h(), |
||||||
|
value, value); |
||||||
|
} |
||||||
|
} |
||||||
|
break; |
||||||
|
case k21c: { |
||||||
|
switch (Opcode()) { |
||||||
|
case CONST_STRING: |
||||||
|
if (file != nullptr) { |
||||||
|
uint32_t string_idx = VRegB_21c(); |
||||||
|
if (string_idx < file->NumStringIds()) { |
||||||
|
os << StringPrintf( |
||||||
|
"const-string v%d, %s // string@%d", |
||||||
|
VRegA_21c(), |
||||||
|
PrintableString(file->StringDataByIdx(dex::StringIndex(string_idx))).c_str(), |
||||||
|
string_idx); |
||||||
|
} else { |
||||||
|
os << StringPrintf("const-string v%d, <<invalid-string-idx-%d>> // string@%d", |
||||||
|
VRegA_21c(), |
||||||
|
string_idx, |
||||||
|
string_idx); |
||||||
|
} |
||||||
|
break; |
||||||
|
} |
||||||
|
FALLTHROUGH_INTENDED; |
||||||
|
case CHECK_CAST: |
||||||
|
case CONST_CLASS: |
||||||
|
case NEW_INSTANCE: |
||||||
|
if (file != nullptr) { |
||||||
|
dex::TypeIndex type_idx(VRegB_21c()); |
||||||
|
os << opcode << " v" << static_cast<int>(VRegA_21c()) << ", " |
||||||
|
<< file->PrettyType(type_idx) << " // type@" << type_idx; |
||||||
|
break; |
||||||
|
} |
||||||
|
FALLTHROUGH_INTENDED; |
||||||
|
case SGET: |
||||||
|
case SGET_WIDE: |
||||||
|
case SGET_OBJECT: |
||||||
|
case SGET_BOOLEAN: |
||||||
|
case SGET_BYTE: |
||||||
|
case SGET_CHAR: |
||||||
|
case SGET_SHORT: |
||||||
|
if (file != nullptr) { |
||||||
|
uint32_t field_idx = VRegB_21c(); |
||||||
|
os << opcode << " v" << static_cast<int>(VRegA_21c()) << ", " << file->PrettyField(field_idx, true) |
||||||
|
<< " // field@" << field_idx; |
||||||
|
break; |
||||||
|
} |
||||||
|
FALLTHROUGH_INTENDED; |
||||||
|
case SPUT: |
||||||
|
case SPUT_WIDE: |
||||||
|
case SPUT_OBJECT: |
||||||
|
case SPUT_BOOLEAN: |
||||||
|
case SPUT_BYTE: |
||||||
|
case SPUT_CHAR: |
||||||
|
case SPUT_SHORT: |
||||||
|
if (file != nullptr) { |
||||||
|
uint32_t field_idx = VRegB_21c(); |
||||||
|
os << opcode << " v" << static_cast<int>(VRegA_21c()) << ", " << file->PrettyField(field_idx, true) |
||||||
|
<< " // field@" << field_idx; |
||||||
|
break; |
||||||
|
} |
||||||
|
FALLTHROUGH_INTENDED; |
||||||
|
default: |
||||||
|
os << StringPrintf("%s v%d, thing@%d", opcode, VRegA_21c(), VRegB_21c()); |
||||||
|
break; |
||||||
|
} |
||||||
|
break; |
||||||
|
} |
||||||
|
case k23x: os << StringPrintf("%s v%d, v%d, v%d", opcode, VRegA_23x(), VRegB_23x(), VRegC_23x()); break; |
||||||
|
case k22b: os << StringPrintf("%s v%d, v%d, #%+d", opcode, VRegA_22b(), VRegB_22b(), VRegC_22b()); break; |
||||||
|
case k22t: os << StringPrintf("%s v%d, v%d, %+d", opcode, VRegA_22t(), VRegB_22t(), VRegC_22t()); break; |
||||||
|
case k22s: os << StringPrintf("%s v%d, v%d, #%+d", opcode, VRegA_22s(), VRegB_22s(), VRegC_22s()); break; |
||||||
|
case k22c: { |
||||||
|
switch (Opcode()) { |
||||||
|
case IGET: |
||||||
|
case IGET_WIDE: |
||||||
|
case IGET_OBJECT: |
||||||
|
case IGET_BOOLEAN: |
||||||
|
case IGET_BYTE: |
||||||
|
case IGET_CHAR: |
||||||
|
case IGET_SHORT: |
||||||
|
if (file != nullptr) { |
||||||
|
uint32_t field_idx = VRegC_22c(); |
||||||
|
os << opcode << " v" << static_cast<int>(VRegA_22c()) << ", v" << static_cast<int>(VRegB_22c()) << ", " |
||||||
|
<< file->PrettyField(field_idx, true) << " // field@" << field_idx; |
||||||
|
break; |
||||||
|
} |
||||||
|
FALLTHROUGH_INTENDED; |
||||||
|
case IGET_QUICK: |
||||||
|
case IGET_OBJECT_QUICK: |
||||||
|
if (file != nullptr) { |
||||||
|
uint32_t field_idx = VRegC_22c(); |
||||||
|
os << opcode << " v" << static_cast<int>(VRegA_22c()) << ", v" << static_cast<int>(VRegB_22c()) << ", " |
||||||
|
<< "// offset@" << field_idx; |
||||||
|
break; |
||||||
|
} |
||||||
|
FALLTHROUGH_INTENDED; |
||||||
|
case IPUT: |
||||||
|
case IPUT_WIDE: |
||||||
|
case IPUT_OBJECT: |
||||||
|
case IPUT_BOOLEAN: |
||||||
|
case IPUT_BYTE: |
||||||
|
case IPUT_CHAR: |
||||||
|
case IPUT_SHORT: |
||||||
|
if (file != nullptr) { |
||||||
|
uint32_t field_idx = VRegC_22c(); |
||||||
|
os << opcode << " v" << static_cast<int>(VRegA_22c()) << ", v" << static_cast<int>(VRegB_22c()) << ", " |
||||||
|
<< file->PrettyField(field_idx, true) << " // field@" << field_idx; |
||||||
|
break; |
||||||
|
} |
||||||
|
FALLTHROUGH_INTENDED; |
||||||
|
case IPUT_QUICK: |
||||||
|
case IPUT_OBJECT_QUICK: |
||||||
|
if (file != nullptr) { |
||||||
|
uint32_t field_idx = VRegC_22c(); |
||||||
|
os << opcode << " v" << static_cast<int>(VRegA_22c()) << ", v" << static_cast<int>(VRegB_22c()) << ", " |
||||||
|
<< "// offset@" << field_idx; |
||||||
|
break; |
||||||
|
} |
||||||
|
FALLTHROUGH_INTENDED; |
||||||
|
case INSTANCE_OF: |
||||||
|
if (file != nullptr) { |
||||||
|
dex::TypeIndex type_idx(VRegC_22c()); |
||||||
|
os << opcode << " v" << static_cast<int>(VRegA_22c()) << ", v" |
||||||
|
<< static_cast<int>(VRegB_22c()) << ", " << file->PrettyType(type_idx) |
||||||
|
<< " // type@" << type_idx.index_; |
||||||
|
break; |
||||||
|
} |
||||||
|
FALLTHROUGH_INTENDED; |
||||||
|
case NEW_ARRAY: |
||||||
|
if (file != nullptr) { |
||||||
|
dex::TypeIndex type_idx(VRegC_22c()); |
||||||
|
os << opcode << " v" << static_cast<int>(VRegA_22c()) << ", v" |
||||||
|
<< static_cast<int>(VRegB_22c()) << ", " << file->PrettyType(type_idx) |
||||||
|
<< " // type@" << type_idx.index_; |
||||||
|
break; |
||||||
|
} |
||||||
|
FALLTHROUGH_INTENDED; |
||||||
|
default: |
||||||
|
os << StringPrintf("%s v%d, v%d, thing@%d", opcode, VRegA_22c(), VRegB_22c(), VRegC_22c()); |
||||||
|
break; |
||||||
|
} |
||||||
|
break; |
||||||
|
} |
||||||
|
case k32x: os << StringPrintf("%s v%d, v%d", opcode, VRegA_32x(), VRegB_32x()); break; |
||||||
|
case k30t: os << StringPrintf("%s %+d", opcode, VRegA_30t()); break; |
||||||
|
case k31t: os << StringPrintf("%s v%d, %+d", opcode, VRegA_31t(), VRegB_31t()); break; |
||||||
|
case k31i: os << StringPrintf("%s v%d, #%+d", opcode, VRegA_31i(), VRegB_31i()); break; |
||||||
|
case k31c: |
||||||
|
if (Opcode() == CONST_STRING_JUMBO) { |
||||||
|
uint32_t string_idx = VRegB_31c(); |
||||||
|
if (file != nullptr) { |
||||||
|
if (string_idx < file->NumStringIds()) { |
||||||
|
os << StringPrintf( |
||||||
|
"%s v%d, %s // string@%d", |
||||||
|
opcode, |
||||||
|
VRegA_31c(), |
||||||
|
PrintableString(file->StringDataByIdx(dex::StringIndex(string_idx))).c_str(), |
||||||
|
string_idx); |
||||||
|
} else { |
||||||
|
os << StringPrintf("%s v%d, <<invalid-string-idx-%d>> // string@%d", |
||||||
|
opcode, |
||||||
|
VRegA_31c(), |
||||||
|
string_idx, |
||||||
|
string_idx); |
||||||
|
} |
||||||
|
} else { |
||||||
|
os << StringPrintf("%s v%d, string@%d", opcode, VRegA_31c(), string_idx); |
||||||
|
} |
||||||
|
} else { |
||||||
|
os << StringPrintf("%s v%d, thing@%d", opcode, VRegA_31c(), VRegB_31c()); break; |
||||||
|
} |
||||||
|
break; |
||||||
|
case k35c: { |
||||||
|
uint32_t arg[kMaxVarArgRegs]; |
||||||
|
GetVarArgs(arg); |
||||||
|
auto DumpArgs = [&](size_t count) { |
||||||
|
for (size_t i = 0; i < count; ++i) { |
||||||
|
if (i != 0) { |
||||||
|
os << ", "; |
||||||
|
} |
||||||
|
os << "v" << arg[i]; |
||||||
|
} |
||||||
|
}; |
||||||
|
switch (Opcode()) { |
||||||
|
case FILLED_NEW_ARRAY: |
||||||
|
{ |
||||||
|
os << opcode << " {"; |
||||||
|
DumpArgs(VRegA_35c()); |
||||||
|
os << "}, type@" << VRegB_35c(); |
||||||
|
} |
||||||
|
break; |
||||||
|
|
||||||
|
case INVOKE_VIRTUAL: |
||||||
|
case INVOKE_SUPER: |
||||||
|
case INVOKE_DIRECT: |
||||||
|
case INVOKE_STATIC: |
||||||
|
case INVOKE_INTERFACE: |
||||||
|
if (file != nullptr) { |
||||||
|
os << opcode << " {"; |
||||||
|
uint32_t method_idx = VRegB_35c(); |
||||||
|
DumpArgs(VRegA_35c()); |
||||||
|
os << "}, " << file->PrettyMethod(method_idx) << " // method@" << method_idx; |
||||||
|
break; |
||||||
|
} |
||||||
|
FALLTHROUGH_INTENDED; |
||||||
|
case INVOKE_VIRTUAL_QUICK: |
||||||
|
if (file != nullptr) { |
||||||
|
os << opcode << " {"; |
||||||
|
uint32_t method_idx = VRegB_35c(); |
||||||
|
DumpArgs(VRegA_35c()); |
||||||
|
os << "}, // vtable@" << method_idx; |
||||||
|
break; |
||||||
|
} |
||||||
|
FALLTHROUGH_INTENDED; |
||||||
|
case INVOKE_CUSTOM: |
||||||
|
if (file != nullptr) { |
||||||
|
os << opcode << " {"; |
||||||
|
uint32_t call_site_idx = VRegB_35c(); |
||||||
|
DumpArgs(VRegA_35c()); |
||||||
|
os << "}, // call_site@" << call_site_idx; |
||||||
|
break; |
||||||
|
} |
||||||
|
FALLTHROUGH_INTENDED; |
||||||
|
default: |
||||||
|
os << opcode << " {"; |
||||||
|
DumpArgs(VRegA_35c()); |
||||||
|
os << "}, thing@" << VRegB_35c(); |
||||||
|
break; |
||||||
|
} |
||||||
|
break; |
||||||
|
} |
||||||
|
case k3rc: { |
||||||
|
uint16_t first_reg = VRegC_3rc(); |
||||||
|
uint16_t last_reg = VRegC_3rc() + VRegA_3rc() - 1; |
||||||
|
switch (Opcode()) { |
||||||
|
case INVOKE_VIRTUAL_RANGE: |
||||||
|
case INVOKE_SUPER_RANGE: |
||||||
|
case INVOKE_DIRECT_RANGE: |
||||||
|
case INVOKE_STATIC_RANGE: |
||||||
|
case INVOKE_INTERFACE_RANGE: |
||||||
|
if (file != nullptr) { |
||||||
|
uint32_t method_idx = VRegB_3rc(); |
||||||
|
os << StringPrintf("%s, {v%d .. v%d}, ", opcode, first_reg, last_reg) |
||||||
|
<< file->PrettyMethod(method_idx) << " // method@" << method_idx; |
||||||
|
break; |
||||||
|
} |
||||||
|
FALLTHROUGH_INTENDED; |
||||||
|
case INVOKE_VIRTUAL_RANGE_QUICK: |
||||||
|
if (file != nullptr) { |
||||||
|
uint32_t method_idx = VRegB_3rc(); |
||||||
|
os << StringPrintf("%s, {v%d .. v%d}, ", opcode, first_reg, last_reg) |
||||||
|
<< "// vtable@" << method_idx; |
||||||
|
break; |
||||||
|
} |
||||||
|
FALLTHROUGH_INTENDED; |
||||||
|
case INVOKE_CUSTOM_RANGE: |
||||||
|
if (file != nullptr) { |
||||||
|
uint32_t call_site_idx = VRegB_3rc(); |
||||||
|
os << StringPrintf("%s, {v%d .. v%d}, ", opcode, first_reg, last_reg) |
||||||
|
<< "// call_site@" << call_site_idx; |
||||||
|
break; |
||||||
|
} |
||||||
|
FALLTHROUGH_INTENDED; |
||||||
|
default: |
||||||
|
os << StringPrintf("%s, {v%d .. v%d}, ", opcode, first_reg, last_reg) |
||||||
|
<< "thing@" << VRegB_3rc(); |
||||||
|
break; |
||||||
|
} |
||||||
|
break; |
||||||
|
} |
||||||
|
case k45cc: { |
||||||
|
uint32_t arg[kMaxVarArgRegs]; |
||||||
|
GetVarArgs(arg); |
||||||
|
uint32_t method_idx = VRegB_45cc(); |
||||||
|
uint32_t proto_idx = VRegH_45cc(); |
||||||
|
os << opcode << " {"; |
||||||
|
for (int i = 0; i < VRegA_45cc(); ++i) { |
||||||
|
if (i != 0) { |
||||||
|
os << ", "; |
||||||
|
} |
||||||
|
os << "v" << arg[i]; |
||||||
|
} |
||||||
|
os << "}"; |
||||||
|
if (file != nullptr) { |
||||||
|
os << ", " << file->PrettyMethod(method_idx) << ", " << file->GetShorty(proto_idx) |
||||||
|
<< " // "; |
||||||
|
} else { |
||||||
|
os << ", "; |
||||||
|
} |
||||||
|
os << "method@" << method_idx << ", proto@" << proto_idx; |
||||||
|
break; |
||||||
|
} |
||||||
|
case k4rcc: |
||||||
|
switch (Opcode()) { |
||||||
|
case INVOKE_POLYMORPHIC_RANGE: { |
||||||
|
if (file != nullptr) { |
||||||
|
uint32_t method_idx = VRegB_4rcc(); |
||||||
|
uint32_t proto_idx = VRegH_4rcc(); |
||||||
|
os << opcode << ", {v" << VRegC_4rcc() << " .. v" << (VRegC_4rcc() + VRegA_4rcc()) |
||||||
|
<< "}, " << file->PrettyMethod(method_idx) << ", " << file->GetShorty(proto_idx) |
||||||
|
<< " // method@" << method_idx << ", proto@" << proto_idx; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
FALLTHROUGH_INTENDED; |
||||||
|
default: { |
||||||
|
uint32_t method_idx = VRegB_4rcc(); |
||||||
|
uint32_t proto_idx = VRegH_4rcc(); |
||||||
|
os << opcode << ", {v" << VRegC_4rcc() << " .. v" << (VRegC_4rcc() + VRegA_4rcc()) |
||||||
|
<< "}, method@" << method_idx << ", proto@" << proto_idx; |
||||||
|
} |
||||||
|
} |
||||||
|
break; |
||||||
|
case k51l: os << StringPrintf("%s v%d, #%+" PRId64, opcode, VRegA_51l(), VRegB_51l()); break; |
||||||
|
} |
||||||
|
return os.str(); |
||||||
|
} |
||||||
|
|
||||||
|
// Add some checks that ensure the flags make sense. We need a subclass to be in the context of
|
||||||
|
// Instruction. Otherwise the flags from the instruction list don't work.
|
||||||
|
struct InstructionStaticAsserts : private Instruction { |
||||||
|
#define IMPLIES(a, b) (!(a) || (b)) |
||||||
|
|
||||||
|
#define VAR_ARGS_CHECK(o, c, pname, f, i, a, e, v) \ |
||||||
|
static_assert(IMPLIES((f) == k35c || (f) == k45cc, \
|
||||||
|
((v) & (kVerifyVarArg | kVerifyVarArgNonZero)) != 0), \
|
||||||
|
"Missing var-arg verification"); |
||||||
|
#include "dex_instruction_list.h" |
||||||
|
DEX_INSTRUCTION_LIST(VAR_ARGS_CHECK) |
||||||
|
#undef DEX_INSTRUCTION_LIST |
||||||
|
#undef VAR_ARGS_CHECK |
||||||
|
|
||||||
|
#define VAR_ARGS_RANGE_CHECK(o, c, pname, f, i, a, e, v) \ |
||||||
|
static_assert(IMPLIES((f) == k3rc || (f) == k4rcc, \
|
||||||
|
((v) & (kVerifyVarArgRange | kVerifyVarArgRangeNonZero)) != 0), \
|
||||||
|
"Missing var-arg verification"); |
||||||
|
#include "dex_instruction_list.h" |
||||||
|
DEX_INSTRUCTION_LIST(VAR_ARGS_RANGE_CHECK) |
||||||
|
#undef DEX_INSTRUCTION_LIST |
||||||
|
#undef VAR_ARGS_RANGE_CHECK |
||||||
|
|
||||||
|
#define EXPERIMENTAL_CHECK(o, c, pname, f, i, a, e, v) \ |
||||||
|
static_assert(kHaveExperimentalInstructions || (((a) & kExperimental) == 0), \
|
||||||
|
"Unexpected experimental instruction."); |
||||||
|
#include "dex_instruction_list.h" |
||||||
|
DEX_INSTRUCTION_LIST(EXPERIMENTAL_CHECK) |
||||||
|
#undef DEX_INSTRUCTION_LIST |
||||||
|
#undef EXPERIMENTAL_CHECK |
||||||
|
}; |
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& os, const Instruction::Code& code) { |
||||||
|
return os << Instruction::Name(code); |
||||||
|
} |
||||||
|
//chensenhua add
|
||||||
|
std::ostream &operator<<(std::ostream &os, const Instruction::Format &format){ |
||||||
|
return os << Instruction::Format (format); |
||||||
|
} |
||||||
|
|
||||||
|
std::ostream &operator<<(std::ostream &os, const Instruction::Flags &flags){ |
||||||
|
return os << Instruction::Flags (flags); |
||||||
|
} |
||||||
|
|
||||||
|
std::ostream &operator<<(std::ostream &os, const Instruction::VerifyFlag &vflags){ |
||||||
|
return os << Instruction::VerifyFlag(vflags); |
||||||
|
} |
||||||
|
//chensenhua add end
|
||||||
|
|
||||||
|
uint32_t RangeInstructionOperands::GetOperand(size_t operand_index) const { |
||||||
|
DCHECK_LT(operand_index, GetNumberOfOperands()); |
||||||
|
return first_operand_ + operand_index; |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t VarArgsInstructionOperands::GetOperand(size_t operand_index) const { |
||||||
|
DCHECK_LT(operand_index, GetNumberOfOperands()); |
||||||
|
return operands_[operand_index]; |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t NoReceiverInstructionOperands::GetOperand(size_t operand_index) const { |
||||||
|
DCHECK_LT(GetNumberOfOperands(), inner_->GetNumberOfOperands()); |
||||||
|
// The receiver is the first operand and since we're skipping it, we need to
|
||||||
|
// add 1 to the operand_index.
|
||||||
|
return inner_->GetOperand(operand_index + 1); |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace art_lkchan
|
@ -0,0 +1,757 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011 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_DEX_INSTRUCTION_H_ |
||||||
|
#define ART_LIBDEXFILE_DEX_DEX_INSTRUCTION_H_ |
||||||
|
|
||||||
|
#include <android-base/logging.h> |
||||||
|
|
||||||
|
#include "base/globals.h" |
||||||
|
#include "base/macros.h" |
||||||
|
|
||||||
|
typedef uint8_t uint4_t; |
||||||
|
typedef int8_t int4_t; |
||||||
|
|
||||||
|
namespace art_lkchan { |
||||||
|
|
||||||
|
class DexFile; |
||||||
|
|
||||||
|
enum { |
||||||
|
kNumPackedOpcodes = 0x100 |
||||||
|
}; |
||||||
|
|
||||||
|
class Instruction { |
||||||
|
public: |
||||||
|
// NOP-encoded switch-statement signatures.
|
||||||
|
enum Signatures { |
||||||
|
kPackedSwitchSignature = 0x0100, |
||||||
|
kSparseSwitchSignature = 0x0200, |
||||||
|
kArrayDataSignature = 0x0300, |
||||||
|
}; |
||||||
|
|
||||||
|
struct PACKED(4) PackedSwitchPayload { |
||||||
|
const uint16_t ident; |
||||||
|
const uint16_t case_count; |
||||||
|
const int32_t first_key; |
||||||
|
const int32_t targets[]; |
||||||
|
|
||||||
|
private: |
||||||
|
DISALLOW_COPY_AND_ASSIGN(PackedSwitchPayload); |
||||||
|
}; |
||||||
|
|
||||||
|
struct PACKED(4) SparseSwitchPayload { |
||||||
|
const uint16_t ident; |
||||||
|
const uint16_t case_count; |
||||||
|
const int32_t keys_and_targets[]; |
||||||
|
|
||||||
|
public: |
||||||
|
const int32_t* GetKeys() const { |
||||||
|
return keys_and_targets; |
||||||
|
} |
||||||
|
|
||||||
|
const int32_t* GetTargets() const { |
||||||
|
return keys_and_targets + case_count; |
||||||
|
} |
||||||
|
|
||||||
|
private: |
||||||
|
DISALLOW_COPY_AND_ASSIGN(SparseSwitchPayload); |
||||||
|
}; |
||||||
|
|
||||||
|
struct PACKED(4) ArrayDataPayload { |
||||||
|
const uint16_t ident; |
||||||
|
const uint16_t element_width; |
||||||
|
const uint32_t element_count; |
||||||
|
const uint8_t data[]; |
||||||
|
|
||||||
|
private: |
||||||
|
DISALLOW_COPY_AND_ASSIGN(ArrayDataPayload); |
||||||
|
}; |
||||||
|
|
||||||
|
enum Code { // private marker to avoid generate-operator-out.py from processing.
|
||||||
|
#define INSTRUCTION_ENUM(opcode, cname, p, f, i, a, e, v) cname = (opcode), |
||||||
|
#include "dex_instruction_list.h" |
||||||
|
DEX_INSTRUCTION_LIST(INSTRUCTION_ENUM) |
||||||
|
#undef DEX_INSTRUCTION_LIST |
||||||
|
#undef INSTRUCTION_ENUM |
||||||
|
RSUB_INT_LIT16 = RSUB_INT, |
||||||
|
}; |
||||||
|
|
||||||
|
enum Format : uint8_t { |
||||||
|
k10x, // op
|
||||||
|
k12x, // op vA, vB
|
||||||
|
k11n, // op vA, #+B
|
||||||
|
k11x, // op vAA
|
||||||
|
k10t, // op +AA
|
||||||
|
k20t, // op +AAAA
|
||||||
|
k22x, // op vAA, vBBBB
|
||||||
|
k21t, // op vAA, +BBBB
|
||||||
|
k21s, // op vAA, #+BBBB
|
||||||
|
k21h, // op vAA, #+BBBB00000[00000000]
|
||||||
|
k21c, // op vAA, thing@BBBB
|
||||||
|
k23x, // op vAA, vBB, vCC
|
||||||
|
k22b, // op vAA, vBB, #+CC
|
||||||
|
k22t, // op vA, vB, +CCCC
|
||||||
|
k22s, // op vA, vB, #+CCCC
|
||||||
|
k22c, // op vA, vB, thing@CCCC
|
||||||
|
k32x, // op vAAAA, vBBBB
|
||||||
|
k30t, // op +AAAAAAAA
|
||||||
|
k31t, // op vAA, +BBBBBBBB
|
||||||
|
k31i, // op vAA, #+BBBBBBBB
|
||||||
|
k31c, // op vAA, thing@BBBBBBBB
|
||||||
|
k35c, // op {vC, vD, vE, vF, vG}, thing@BBBB (B: count, A: vG)
|
||||||
|
k3rc, // op {vCCCC .. v(CCCC+AA-1)}, meth@BBBB
|
||||||
|
|
||||||
|
// op {vC, vD, vE, vF, vG}, meth@BBBB, proto@HHHH (A: count)
|
||||||
|
// format: AG op BBBB FEDC HHHH
|
||||||
|
k45cc, |
||||||
|
|
||||||
|
// op {VCCCC .. v(CCCC+AA-1)}, meth@BBBB, proto@HHHH (AA: count)
|
||||||
|
// format: AA op BBBB CCCC HHHH
|
||||||
|
k4rcc, // op {VCCCC .. v(CCCC+AA-1)}, meth@BBBB, proto@HHHH (AA: count)
|
||||||
|
|
||||||
|
k51l, // op vAA, #+BBBBBBBBBBBBBBBB
|
||||||
|
}; |
||||||
|
|
||||||
|
enum IndexType : uint8_t { |
||||||
|
kIndexUnknown = 0, |
||||||
|
kIndexNone, // has no index
|
||||||
|
kIndexTypeRef, // type reference index
|
||||||
|
kIndexStringRef, // string reference index
|
||||||
|
kIndexMethodRef, // method reference index
|
||||||
|
kIndexFieldRef, // field reference index
|
||||||
|
kIndexFieldOffset, // field offset (for static linked fields)
|
||||||
|
kIndexVtableOffset, // vtable offset (for static linked methods)
|
||||||
|
kIndexMethodAndProtoRef, // method and a proto reference index (for invoke-polymorphic)
|
||||||
|
kIndexCallSiteRef, // call site reference index
|
||||||
|
kIndexMethodHandleRef, // constant method handle reference index
|
||||||
|
kIndexProtoRef, // prototype reference index
|
||||||
|
}; |
||||||
|
|
||||||
|
enum Flags : uint8_t { |
||||||
|
kBranch = 0x01, // conditional or unconditional branch
|
||||||
|
kContinue = 0x02, // flow can continue to next statement
|
||||||
|
kSwitch = 0x04, // switch statement
|
||||||
|
kThrow = 0x08, // could cause an exception to be thrown
|
||||||
|
kReturn = 0x10, // returns, no additional statements
|
||||||
|
kInvoke = 0x20, // a flavor of invoke
|
||||||
|
kUnconditional = 0x40, // unconditional branch
|
||||||
|
kExperimental = 0x80, // is an experimental opcode
|
||||||
|
}; |
||||||
|
|
||||||
|
// Old flags. Keeping them around in case we might need them again some day.
|
||||||
|
enum ExtendedFlags : uint32_t { |
||||||
|
kAdd = 0x0000080, // addition
|
||||||
|
kSubtract = 0x0000100, // subtract
|
||||||
|
kMultiply = 0x0000200, // multiply
|
||||||
|
kDivide = 0x0000400, // division
|
||||||
|
kRemainder = 0x0000800, // remainder
|
||||||
|
kAnd = 0x0001000, // and
|
||||||
|
kOr = 0x0002000, // or
|
||||||
|
kXor = 0x0004000, // xor
|
||||||
|
kShl = 0x0008000, // shl
|
||||||
|
kShr = 0x0010000, // shr
|
||||||
|
kUshr = 0x0020000, // ushr
|
||||||
|
kCast = 0x0040000, // cast
|
||||||
|
kStore = 0x0080000, // store opcode
|
||||||
|
kLoad = 0x0100000, // load opcode
|
||||||
|
kClobber = 0x0200000, // clobbers memory in a big way (not just a write)
|
||||||
|
kRegCFieldOrConstant = 0x0400000, // is the third virtual register a field or literal constant (vC)
|
||||||
|
kRegBFieldOrConstant = 0x0800000, // is the second virtual register a field or literal constant (vB)
|
||||||
|
}; |
||||||
|
|
||||||
|
enum VerifyFlag : uint32_t { |
||||||
|
kVerifyNone = 0x0000000, |
||||||
|
kVerifyRegA = 0x0000001, |
||||||
|
kVerifyRegAWide = 0x0000002, |
||||||
|
kVerifyRegB = 0x0000004, |
||||||
|
kVerifyRegBField = 0x0000008, |
||||||
|
kVerifyRegBMethod = 0x0000010, |
||||||
|
kVerifyRegBNewInstance = 0x0000020, |
||||||
|
kVerifyRegBString = 0x0000040, |
||||||
|
kVerifyRegBType = 0x0000080, |
||||||
|
kVerifyRegBWide = 0x0000100, |
||||||
|
kVerifyRegC = 0x0000200, |
||||||
|
kVerifyRegCField = 0x0000400, |
||||||
|
kVerifyRegCNewArray = 0x0000800, |
||||||
|
kVerifyRegCType = 0x0001000, |
||||||
|
kVerifyRegCWide = 0x0002000, |
||||||
|
kVerifyArrayData = 0x0004000, |
||||||
|
kVerifyBranchTarget = 0x0008000, |
||||||
|
kVerifySwitchTargets = 0x0010000, |
||||||
|
kVerifyVarArg = 0x0020000, |
||||||
|
kVerifyVarArgNonZero = 0x0040000, |
||||||
|
kVerifyVarArgRange = 0x0080000, |
||||||
|
kVerifyVarArgRangeNonZero = 0x0100000, |
||||||
|
kVerifyRuntimeOnly = 0x0200000, |
||||||
|
kVerifyError = 0x0400000, |
||||||
|
kVerifyRegHPrototype = 0x0800000, |
||||||
|
kVerifyRegBCallSite = 0x1000000, |
||||||
|
kVerifyRegBMethodHandle = 0x2000000, |
||||||
|
kVerifyRegBPrototype = 0x4000000, |
||||||
|
}; |
||||||
|
|
||||||
|
// Collect the enums in a struct for better locality.
|
||||||
|
struct InstructionDescriptor { |
||||||
|
uint32_t verify_flags; // Set of VerifyFlag.
|
||||||
|
Format format; |
||||||
|
IndexType index_type; |
||||||
|
uint8_t flags; // Set of Flags.
|
||||||
|
int8_t size_in_code_units; |
||||||
|
}; |
||||||
|
|
||||||
|
static constexpr uint32_t kMaxVarArgRegs = 5; |
||||||
|
|
||||||
|
static constexpr bool kHaveExperimentalInstructions = false; |
||||||
|
|
||||||
|
// Returns the size (in 2 byte code units) of this instruction.
|
||||||
|
size_t SizeInCodeUnits() const { |
||||||
|
int8_t result = kInstructionDescriptors[Opcode()].size_in_code_units; |
||||||
|
if (UNLIKELY(result < 0)) { |
||||||
|
return SizeInCodeUnitsComplexOpcode(); |
||||||
|
} else { |
||||||
|
return static_cast<size_t>(result); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Code units required to calculate the size of the instruction.
|
||||||
|
size_t CodeUnitsRequiredForSizeComputation() const { |
||||||
|
const int8_t result = kInstructionDescriptors[Opcode()].size_in_code_units; |
||||||
|
return UNLIKELY(result < 0) ? CodeUnitsRequiredForSizeOfComplexOpcode() : 1; |
||||||
|
} |
||||||
|
|
||||||
|
// Reads an instruction out of the stream at the specified address.
|
||||||
|
static const Instruction* At(const uint16_t* code) { |
||||||
|
DCHECK(code != nullptr); |
||||||
|
return reinterpret_cast<const Instruction*>(code); |
||||||
|
} |
||||||
|
|
||||||
|
// Reads an instruction out of the stream from the current address plus an offset.
|
||||||
|
const Instruction* RelativeAt(int32_t offset) const WARN_UNUSED { |
||||||
|
return At(reinterpret_cast<const uint16_t*>(this) + offset); |
||||||
|
} |
||||||
|
|
||||||
|
// Returns a pointer to the next instruction in the stream.
|
||||||
|
const Instruction* Next() const { |
||||||
|
return RelativeAt(SizeInCodeUnits()); |
||||||
|
} |
||||||
|
|
||||||
|
// Returns a pointer to the instruction after this 1xx instruction in the stream.
|
||||||
|
const Instruction* Next_1xx() const { |
||||||
|
DCHECK(FormatOf(Opcode()) >= k10x && FormatOf(Opcode()) <= k10t); |
||||||
|
return RelativeAt(1); |
||||||
|
} |
||||||
|
|
||||||
|
// Returns a pointer to the instruction after this 2xx instruction in the stream.
|
||||||
|
const Instruction* Next_2xx() const { |
||||||
|
DCHECK(FormatOf(Opcode()) >= k20t && FormatOf(Opcode()) <= k22c); |
||||||
|
return RelativeAt(2); |
||||||
|
} |
||||||
|
|
||||||
|
// Returns a pointer to the instruction after this 3xx instruction in the stream.
|
||||||
|
const Instruction* Next_3xx() const { |
||||||
|
DCHECK(FormatOf(Opcode()) >= k32x && FormatOf(Opcode()) <= k3rc); |
||||||
|
return RelativeAt(3); |
||||||
|
} |
||||||
|
|
||||||
|
// Returns a pointer to the instruction after this 4xx instruction in the stream.
|
||||||
|
const Instruction* Next_4xx() const { |
||||||
|
DCHECK(FormatOf(Opcode()) >= k45cc && FormatOf(Opcode()) <= k4rcc); |
||||||
|
return RelativeAt(4); |
||||||
|
} |
||||||
|
|
||||||
|
// Returns a pointer to the instruction after this 51l instruction in the stream.
|
||||||
|
const Instruction* Next_51l() const { |
||||||
|
DCHECK(FormatOf(Opcode()) == k51l); |
||||||
|
return RelativeAt(5); |
||||||
|
} |
||||||
|
|
||||||
|
// Returns the name of this instruction's opcode.
|
||||||
|
const char* Name() const { |
||||||
|
return Instruction::Name(Opcode()); |
||||||
|
} |
||||||
|
|
||||||
|
// Returns the name of the given opcode.
|
||||||
|
static const char* Name(Code opcode) { |
||||||
|
return kInstructionNames[opcode]; |
||||||
|
} |
||||||
|
|
||||||
|
// VRegA
|
||||||
|
bool HasVRegA() const; |
||||||
|
ALWAYS_INLINE int32_t VRegA() const; |
||||||
|
|
||||||
|
int8_t VRegA_10t() const { |
||||||
|
return VRegA_10t(Fetch16(0)); |
||||||
|
} |
||||||
|
uint8_t VRegA_10x() const { |
||||||
|
return VRegA_10x(Fetch16(0)); |
||||||
|
} |
||||||
|
uint4_t VRegA_11n() const { |
||||||
|
return VRegA_11n(Fetch16(0)); |
||||||
|
} |
||||||
|
uint8_t VRegA_11x() const { |
||||||
|
return VRegA_11x(Fetch16(0)); |
||||||
|
} |
||||||
|
uint4_t VRegA_12x() const { |
||||||
|
return VRegA_12x(Fetch16(0)); |
||||||
|
} |
||||||
|
int16_t VRegA_20t() const; |
||||||
|
uint8_t VRegA_21c() const { |
||||||
|
return VRegA_21c(Fetch16(0)); |
||||||
|
} |
||||||
|
uint8_t VRegA_21h() const { |
||||||
|
return VRegA_21h(Fetch16(0)); |
||||||
|
} |
||||||
|
uint8_t VRegA_21s() const { |
||||||
|
return VRegA_21s(Fetch16(0)); |
||||||
|
} |
||||||
|
uint8_t VRegA_21t() const { |
||||||
|
return VRegA_21t(Fetch16(0)); |
||||||
|
} |
||||||
|
uint8_t VRegA_22b() const { |
||||||
|
return VRegA_22b(Fetch16(0)); |
||||||
|
} |
||||||
|
uint4_t VRegA_22c() const { |
||||||
|
return VRegA_22c(Fetch16(0)); |
||||||
|
} |
||||||
|
uint4_t VRegA_22s() const { |
||||||
|
return VRegA_22s(Fetch16(0)); |
||||||
|
} |
||||||
|
uint4_t VRegA_22t() const { |
||||||
|
return VRegA_22t(Fetch16(0)); |
||||||
|
} |
||||||
|
uint8_t VRegA_22x() const { |
||||||
|
return VRegA_22x(Fetch16(0)); |
||||||
|
} |
||||||
|
uint8_t VRegA_23x() const { |
||||||
|
return VRegA_23x(Fetch16(0)); |
||||||
|
} |
||||||
|
int32_t VRegA_30t() const; |
||||||
|
uint8_t VRegA_31c() const { |
||||||
|
return VRegA_31c(Fetch16(0)); |
||||||
|
} |
||||||
|
uint8_t VRegA_31i() const { |
||||||
|
return VRegA_31i(Fetch16(0)); |
||||||
|
} |
||||||
|
uint8_t VRegA_31t() const { |
||||||
|
return VRegA_31t(Fetch16(0)); |
||||||
|
} |
||||||
|
uint16_t VRegA_32x() const; |
||||||
|
uint4_t VRegA_35c() const { |
||||||
|
return VRegA_35c(Fetch16(0)); |
||||||
|
} |
||||||
|
uint8_t VRegA_3rc() const { |
||||||
|
return VRegA_3rc(Fetch16(0)); |
||||||
|
} |
||||||
|
uint8_t VRegA_51l() const { |
||||||
|
return VRegA_51l(Fetch16(0)); |
||||||
|
} |
||||||
|
uint4_t VRegA_45cc() const { |
||||||
|
return VRegA_45cc(Fetch16(0)); |
||||||
|
} |
||||||
|
uint8_t VRegA_4rcc() const { |
||||||
|
return VRegA_4rcc(Fetch16(0)); |
||||||
|
} |
||||||
|
|
||||||
|
// The following methods return the vA operand for various instruction formats. The "inst_data"
|
||||||
|
// parameter holds the first 16 bits of instruction which the returned value is decoded from.
|
||||||
|
int8_t VRegA_10t(uint16_t inst_data) const; |
||||||
|
uint8_t VRegA_10x(uint16_t inst_data) const; |
||||||
|
uint4_t VRegA_11n(uint16_t inst_data) const; |
||||||
|
uint8_t VRegA_11x(uint16_t inst_data) const; |
||||||
|
uint4_t VRegA_12x(uint16_t inst_data) const; |
||||||
|
uint8_t VRegA_21c(uint16_t inst_data) const; |
||||||
|
uint8_t VRegA_21h(uint16_t inst_data) const; |
||||||
|
uint8_t VRegA_21s(uint16_t inst_data) const; |
||||||
|
uint8_t VRegA_21t(uint16_t inst_data) const; |
||||||
|
uint8_t VRegA_22b(uint16_t inst_data) const; |
||||||
|
uint4_t VRegA_22c(uint16_t inst_data) const; |
||||||
|
uint4_t VRegA_22s(uint16_t inst_data) const; |
||||||
|
uint4_t VRegA_22t(uint16_t inst_data) const; |
||||||
|
uint8_t VRegA_22x(uint16_t inst_data) const; |
||||||
|
uint8_t VRegA_23x(uint16_t inst_data) const; |
||||||
|
uint8_t VRegA_31c(uint16_t inst_data) const; |
||||||
|
uint8_t VRegA_31i(uint16_t inst_data) const; |
||||||
|
uint8_t VRegA_31t(uint16_t inst_data) const; |
||||||
|
uint4_t VRegA_35c(uint16_t inst_data) const; |
||||||
|
uint8_t VRegA_3rc(uint16_t inst_data) const; |
||||||
|
uint8_t VRegA_51l(uint16_t inst_data) const; |
||||||
|
uint4_t VRegA_45cc(uint16_t inst_data) const; |
||||||
|
uint8_t VRegA_4rcc(uint16_t inst_data) const; |
||||||
|
|
||||||
|
// VRegB
|
||||||
|
bool HasVRegB() const; |
||||||
|
int32_t VRegB() const; |
||||||
|
|
||||||
|
bool HasWideVRegB() const; |
||||||
|
uint64_t WideVRegB() const; |
||||||
|
|
||||||
|
int4_t VRegB_11n() const { |
||||||
|
return VRegB_11n(Fetch16(0)); |
||||||
|
} |
||||||
|
uint4_t VRegB_12x() const { |
||||||
|
return VRegB_12x(Fetch16(0)); |
||||||
|
} |
||||||
|
uint16_t VRegB_21c() const; |
||||||
|
uint16_t VRegB_21h() const; |
||||||
|
int16_t VRegB_21s() const; |
||||||
|
int16_t VRegB_21t() const; |
||||||
|
uint8_t VRegB_22b() const; |
||||||
|
uint4_t VRegB_22c() const { |
||||||
|
return VRegB_22c(Fetch16(0)); |
||||||
|
} |
||||||
|
uint4_t VRegB_22s() const { |
||||||
|
return VRegB_22s(Fetch16(0)); |
||||||
|
} |
||||||
|
uint4_t VRegB_22t() const { |
||||||
|
return VRegB_22t(Fetch16(0)); |
||||||
|
} |
||||||
|
uint16_t VRegB_22x() const; |
||||||
|
uint8_t VRegB_23x() const; |
||||||
|
uint32_t VRegB_31c() const; |
||||||
|
int32_t VRegB_31i() const; |
||||||
|
int32_t VRegB_31t() const; |
||||||
|
uint16_t VRegB_32x() const; |
||||||
|
uint16_t VRegB_35c() const; |
||||||
|
uint16_t VRegB_3rc() const; |
||||||
|
uint64_t VRegB_51l() const; // vB_wide
|
||||||
|
uint16_t VRegB_45cc() const; |
||||||
|
uint16_t VRegB_4rcc() const; |
||||||
|
|
||||||
|
// The following methods return the vB operand for all instruction formats where it is encoded in
|
||||||
|
// the first 16 bits of instruction. The "inst_data" parameter holds these 16 bits. The returned
|
||||||
|
// value is decoded from it.
|
||||||
|
int4_t VRegB_11n(uint16_t inst_data) const; |
||||||
|
uint4_t VRegB_12x(uint16_t inst_data) const; |
||||||
|
uint4_t VRegB_22c(uint16_t inst_data) const; |
||||||
|
uint4_t VRegB_22s(uint16_t inst_data) const; |
||||||
|
uint4_t VRegB_22t(uint16_t inst_data) const; |
||||||
|
|
||||||
|
// VRegC
|
||||||
|
bool HasVRegC() const; |
||||||
|
int32_t VRegC() const; |
||||||
|
|
||||||
|
int8_t VRegC_22b() const; |
||||||
|
uint16_t VRegC_22c() const; |
||||||
|
int16_t VRegC_22s() const; |
||||||
|
int16_t VRegC_22t() const; |
||||||
|
uint8_t VRegC_23x() const; |
||||||
|
uint4_t VRegC_35c() const; |
||||||
|
uint16_t VRegC_3rc() const; |
||||||
|
uint4_t VRegC_45cc() const; |
||||||
|
uint16_t VRegC_4rcc() const; |
||||||
|
|
||||||
|
|
||||||
|
// VRegH
|
||||||
|
bool HasVRegH() const; |
||||||
|
int32_t VRegH() const; |
||||||
|
uint16_t VRegH_45cc() const; |
||||||
|
uint16_t VRegH_4rcc() const; |
||||||
|
|
||||||
|
// Fills the given array with the 'arg' array of the instruction.
|
||||||
|
bool HasVarArgs() const; |
||||||
|
void GetVarArgs(uint32_t args[kMaxVarArgRegs], uint16_t inst_data) const; |
||||||
|
void GetVarArgs(uint32_t args[kMaxVarArgRegs]) const { |
||||||
|
return GetVarArgs(args, Fetch16(0)); |
||||||
|
} |
||||||
|
|
||||||
|
// Returns the opcode field of the instruction. The given "inst_data" parameter must be the first
|
||||||
|
// 16 bits of instruction.
|
||||||
|
Code Opcode(uint16_t inst_data) const { |
||||||
|
DCHECK_EQ(inst_data, Fetch16(0)); |
||||||
|
return static_cast<Code>(inst_data & 0xFF); |
||||||
|
} |
||||||
|
|
||||||
|
// Returns the opcode field of the instruction from the first 16 bits of instruction.
|
||||||
|
Code Opcode() const { |
||||||
|
return Opcode(Fetch16(0)); |
||||||
|
} |
||||||
|
|
||||||
|
void SetOpcode(Code opcode) { |
||||||
|
DCHECK_LT(static_cast<uint16_t>(opcode), 256u); |
||||||
|
uint16_t* insns = reinterpret_cast<uint16_t*>(this); |
||||||
|
insns[0] = (insns[0] & 0xff00) | static_cast<uint16_t>(opcode); |
||||||
|
} |
||||||
|
|
||||||
|
void SetVRegA_10x(uint8_t val) { |
||||||
|
DCHECK(FormatOf(Opcode()) == k10x); |
||||||
|
uint16_t* insns = reinterpret_cast<uint16_t*>(this); |
||||||
|
insns[0] = (val << 8) | (insns[0] & 0x00ff); |
||||||
|
} |
||||||
|
|
||||||
|
void SetVRegB_3rc(uint16_t val) { |
||||||
|
DCHECK(FormatOf(Opcode()) == k3rc); |
||||||
|
uint16_t* insns = reinterpret_cast<uint16_t*>(this); |
||||||
|
insns[1] = val; |
||||||
|
} |
||||||
|
|
||||||
|
void SetVRegB_35c(uint16_t val) { |
||||||
|
DCHECK(FormatOf(Opcode()) == k35c); |
||||||
|
uint16_t* insns = reinterpret_cast<uint16_t*>(this); |
||||||
|
insns[1] = val; |
||||||
|
} |
||||||
|
|
||||||
|
void SetVRegC_22c(uint16_t val) { |
||||||
|
DCHECK(FormatOf(Opcode()) == k22c); |
||||||
|
uint16_t* insns = reinterpret_cast<uint16_t*>(this); |
||||||
|
insns[1] = val; |
||||||
|
} |
||||||
|
|
||||||
|
void SetVRegA_21c(uint8_t val) { |
||||||
|
DCHECK(FormatOf(Opcode()) == k21c); |
||||||
|
uint16_t* insns = reinterpret_cast<uint16_t*>(this); |
||||||
|
insns[0] = (val << 8) | (insns[0] & 0x00ff); |
||||||
|
} |
||||||
|
|
||||||
|
void SetVRegB_21c(uint16_t val) { |
||||||
|
DCHECK(FormatOf(Opcode()) == k21c); |
||||||
|
uint16_t* insns = reinterpret_cast<uint16_t*>(this); |
||||||
|
insns[1] = val; |
||||||
|
} |
||||||
|
|
||||||
|
// Returns the format of the given opcode.
|
||||||
|
static Format FormatOf(Code opcode) { |
||||||
|
return kInstructionDescriptors[opcode].format; |
||||||
|
} |
||||||
|
|
||||||
|
// Returns the index type of the given opcode.
|
||||||
|
static IndexType IndexTypeOf(Code opcode) { |
||||||
|
return kInstructionDescriptors[opcode].index_type; |
||||||
|
} |
||||||
|
|
||||||
|
// Returns the flags for the given opcode.
|
||||||
|
static uint8_t FlagsOf(Code opcode) { |
||||||
|
return kInstructionDescriptors[opcode].flags; |
||||||
|
} |
||||||
|
|
||||||
|
// Return the verify flags for the given opcode.
|
||||||
|
static uint32_t VerifyFlagsOf(Code opcode) { |
||||||
|
return kInstructionDescriptors[opcode].verify_flags; |
||||||
|
} |
||||||
|
|
||||||
|
// Returns true if this instruction is a branch.
|
||||||
|
bool IsBranch() const { |
||||||
|
return (kInstructionDescriptors[Opcode()].flags & kBranch) != 0; |
||||||
|
} |
||||||
|
|
||||||
|
// Returns true if this instruction is a unconditional branch.
|
||||||
|
bool IsUnconditional() const { |
||||||
|
return (kInstructionDescriptors[Opcode()].flags & kUnconditional) != 0; |
||||||
|
} |
||||||
|
|
||||||
|
// Returns the branch offset if this instruction is a branch.
|
||||||
|
int32_t GetTargetOffset() const; |
||||||
|
|
||||||
|
// Returns true if the instruction allows control flow to go to the following instruction.
|
||||||
|
bool CanFlowThrough() const; |
||||||
|
|
||||||
|
// Returns true if the instruction is a quickened instruction.
|
||||||
|
bool IsQuickened() const { |
||||||
|
return (kInstructionDescriptors[Opcode()].index_type == kIndexFieldOffset) || |
||||||
|
(kInstructionDescriptors[Opcode()].index_type == kIndexVtableOffset); |
||||||
|
} |
||||||
|
|
||||||
|
// Returns true if this instruction is a switch.
|
||||||
|
bool IsSwitch() const { |
||||||
|
return (kInstructionDescriptors[Opcode()].flags & kSwitch) != 0; |
||||||
|
} |
||||||
|
|
||||||
|
// Returns true if this instruction can throw.
|
||||||
|
bool IsThrow() const { |
||||||
|
return (kInstructionDescriptors[Opcode()].flags & kThrow) != 0; |
||||||
|
} |
||||||
|
|
||||||
|
// Determine if the instruction is any of 'return' instructions.
|
||||||
|
bool IsReturn() const { |
||||||
|
return (kInstructionDescriptors[Opcode()].flags & kReturn) != 0; |
||||||
|
} |
||||||
|
|
||||||
|
// Determine if this instruction ends execution of its basic block.
|
||||||
|
bool IsBasicBlockEnd() const { |
||||||
|
return IsBranch() || IsReturn() || Opcode() == THROW; |
||||||
|
} |
||||||
|
|
||||||
|
// Determine if this instruction is an invoke.
|
||||||
|
bool IsInvoke() const { |
||||||
|
return (kInstructionDescriptors[Opcode()].flags & kInvoke) != 0; |
||||||
|
} |
||||||
|
|
||||||
|
// Determine if this instruction is experimental.
|
||||||
|
bool IsExperimental() const { |
||||||
|
return (kInstructionDescriptors[Opcode()].flags & kExperimental) != 0; |
||||||
|
} |
||||||
|
|
||||||
|
int GetVerifyTypeArgumentA() const { |
||||||
|
return (kInstructionDescriptors[Opcode()].verify_flags & (kVerifyRegA | kVerifyRegAWide)); |
||||||
|
} |
||||||
|
|
||||||
|
int GetVerifyTypeArgumentB() const { |
||||||
|
return (kInstructionDescriptors[Opcode()].verify_flags & (kVerifyRegB | kVerifyRegBField | |
||||||
|
kVerifyRegBMethod | kVerifyRegBNewInstance | kVerifyRegBString | kVerifyRegBType | |
||||||
|
kVerifyRegBWide)); |
||||||
|
} |
||||||
|
|
||||||
|
int GetVerifyTypeArgumentC() const { |
||||||
|
return (kInstructionDescriptors[Opcode()].verify_flags & (kVerifyRegC | kVerifyRegCField | |
||||||
|
kVerifyRegCNewArray | kVerifyRegCType | kVerifyRegCWide)); |
||||||
|
} |
||||||
|
|
||||||
|
int GetVerifyTypeArgumentH() const { |
||||||
|
return (kInstructionDescriptors[Opcode()].verify_flags & kVerifyRegHPrototype); |
||||||
|
} |
||||||
|
|
||||||
|
int GetVerifyExtraFlags() const { |
||||||
|
return (kInstructionDescriptors[Opcode()].verify_flags & (kVerifyArrayData | |
||||||
|
kVerifyBranchTarget | kVerifySwitchTargets | kVerifyVarArg | kVerifyVarArgNonZero | |
||||||
|
kVerifyVarArgRange | kVerifyVarArgRangeNonZero | kVerifyError)); |
||||||
|
} |
||||||
|
|
||||||
|
bool GetVerifyIsRuntimeOnly() const { |
||||||
|
return (kInstructionDescriptors[Opcode()].verify_flags & kVerifyRuntimeOnly) != 0; |
||||||
|
} |
||||||
|
|
||||||
|
// Get the dex PC of this instruction as a offset in code units from the beginning of insns.
|
||||||
|
uint32_t GetDexPc(const uint16_t* insns) const { |
||||||
|
return (reinterpret_cast<const uint16_t*>(this) - insns); |
||||||
|
} |
||||||
|
|
||||||
|
// Dump decoded version of instruction
|
||||||
|
std::string DumpString(const DexFile*) const; |
||||||
|
|
||||||
|
// Dump code_units worth of this instruction, padding to code_units for shorter instructions
|
||||||
|
std::string DumpHex(size_t code_units) const; |
||||||
|
|
||||||
|
// Little-endian dump code_units worth of this instruction, padding to code_units for
|
||||||
|
// shorter instructions
|
||||||
|
std::string DumpHexLE(size_t instr_code_units) const; |
||||||
|
|
||||||
|
uint16_t Fetch16(size_t offset) const { |
||||||
|
const uint16_t* insns = reinterpret_cast<const uint16_t*>(this); |
||||||
|
return insns[offset]; |
||||||
|
} |
||||||
|
|
||||||
|
private: |
||||||
|
size_t SizeInCodeUnitsComplexOpcode() const; |
||||||
|
|
||||||
|
// Return how many code unit words are required to compute the size of the opcode.
|
||||||
|
size_t CodeUnitsRequiredForSizeOfComplexOpcode() const; |
||||||
|
|
||||||
|
uint32_t Fetch32(size_t offset) const { |
||||||
|
return (Fetch16(offset) | ((uint32_t) Fetch16(offset + 1) << 16)); |
||||||
|
} |
||||||
|
|
||||||
|
uint4_t InstA() const { |
||||||
|
return InstA(Fetch16(0)); |
||||||
|
} |
||||||
|
|
||||||
|
uint4_t InstB() const { |
||||||
|
return InstB(Fetch16(0)); |
||||||
|
} |
||||||
|
|
||||||
|
uint8_t InstAA() const { |
||||||
|
return InstAA(Fetch16(0)); |
||||||
|
} |
||||||
|
|
||||||
|
uint4_t InstA(uint16_t inst_data) const { |
||||||
|
DCHECK_EQ(inst_data, Fetch16(0)); |
||||||
|
return static_cast<uint4_t>((inst_data >> 8) & 0x0f); |
||||||
|
} |
||||||
|
|
||||||
|
uint4_t InstB(uint16_t inst_data) const { |
||||||
|
DCHECK_EQ(inst_data, Fetch16(0)); |
||||||
|
return static_cast<uint4_t>(inst_data >> 12); |
||||||
|
} |
||||||
|
|
||||||
|
uint8_t InstAA(uint16_t inst_data) const { |
||||||
|
DCHECK_EQ(inst_data, Fetch16(0)); |
||||||
|
return static_cast<uint8_t>(inst_data >> 8); |
||||||
|
} |
||||||
|
|
||||||
|
static const char* const kInstructionNames[]; |
||||||
|
|
||||||
|
static const InstructionDescriptor kInstructionDescriptors[]; |
||||||
|
|
||||||
|
DISALLOW_IMPLICIT_CONSTRUCTORS(Instruction); |
||||||
|
}; |
||||||
|
std::ostream& operator<<(std::ostream& os, const Instruction::Code& code); |
||||||
|
std::ostream& operator<<(std::ostream& os, const Instruction::Format& format); |
||||||
|
std::ostream& operator<<(std::ostream& os, const Instruction::Flags& flags); |
||||||
|
std::ostream& operator<<(std::ostream& os, const Instruction::VerifyFlag& vflags); |
||||||
|
|
||||||
|
// Base class for accessing instruction operands. Unifies operand
|
||||||
|
// access for instructions that have range and varargs forms
|
||||||
|
// (e.g. invoke-polymoprhic/range and invoke-polymorphic).
|
||||||
|
class InstructionOperands { |
||||||
|
public: |
||||||
|
explicit InstructionOperands(size_t num_operands) : num_operands_(num_operands) {} |
||||||
|
virtual ~InstructionOperands() {} |
||||||
|
virtual uint32_t GetOperand(size_t index) const = 0; |
||||||
|
size_t GetNumberOfOperands() const { return num_operands_; } |
||||||
|
|
||||||
|
private: |
||||||
|
const size_t num_operands_; |
||||||
|
|
||||||
|
DISALLOW_IMPLICIT_CONSTRUCTORS(InstructionOperands); |
||||||
|
}; |
||||||
|
|
||||||
|
// Class for accessing operands for instructions with a range format
|
||||||
|
// (e.g. 3rc and 4rcc).
|
||||||
|
class RangeInstructionOperands FINAL : public InstructionOperands { |
||||||
|
public: |
||||||
|
RangeInstructionOperands(uint32_t first_operand, size_t num_operands) |
||||||
|
: InstructionOperands(num_operands), first_operand_(first_operand) {} |
||||||
|
~RangeInstructionOperands() {} |
||||||
|
uint32_t GetOperand(size_t operand_index) const OVERRIDE; |
||||||
|
|
||||||
|
private: |
||||||
|
const uint32_t first_operand_; |
||||||
|
|
||||||
|
DISALLOW_IMPLICIT_CONSTRUCTORS(RangeInstructionOperands); |
||||||
|
}; |
||||||
|
|
||||||
|
// Class for accessing operands for instructions with a variable
|
||||||
|
// number of arguments format (e.g. 35c and 45cc).
|
||||||
|
class VarArgsInstructionOperands FINAL : public InstructionOperands { |
||||||
|
public: |
||||||
|
VarArgsInstructionOperands(const uint32_t (&operands)[Instruction::kMaxVarArgRegs], |
||||||
|
size_t num_operands) |
||||||
|
: InstructionOperands(num_operands), operands_(operands) {} |
||||||
|
~VarArgsInstructionOperands() {} |
||||||
|
uint32_t GetOperand(size_t operand_index) const OVERRIDE; |
||||||
|
|
||||||
|
private: |
||||||
|
const uint32_t (&operands_)[Instruction::kMaxVarArgRegs]; |
||||||
|
|
||||||
|
DISALLOW_IMPLICIT_CONSTRUCTORS(VarArgsInstructionOperands); |
||||||
|
}; |
||||||
|
|
||||||
|
// Class for accessing operands without the receiver by wrapping an
|
||||||
|
// existing InstructionOperands instance.
|
||||||
|
class NoReceiverInstructionOperands FINAL : public InstructionOperands { |
||||||
|
public: |
||||||
|
explicit NoReceiverInstructionOperands(const InstructionOperands* const inner) |
||||||
|
: InstructionOperands(inner->GetNumberOfOperands() - 1), inner_(inner) {} |
||||||
|
~NoReceiverInstructionOperands() {} |
||||||
|
uint32_t GetOperand(size_t operand_index) const OVERRIDE; |
||||||
|
|
||||||
|
private: |
||||||
|
const InstructionOperands* const inner_; |
||||||
|
|
||||||
|
DISALLOW_IMPLICIT_CONSTRUCTORS(NoReceiverInstructionOperands); |
||||||
|
}; |
||||||
|
|
||||||
|
} // namespace art_lkchan
|
||||||
|
|
||||||
|
#endif // ART_LIBDEXFILE_DEX_DEX_INSTRUCTION_H_
|
@ -0,0 +1,237 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2017 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_DEX_INSTRUCTION_ITERATOR_H_ |
||||||
|
#define ART_LIBDEXFILE_DEX_DEX_INSTRUCTION_ITERATOR_H_ |
||||||
|
|
||||||
|
#include <iterator> |
||||||
|
|
||||||
|
#include <android-base/logging.h> |
||||||
|
|
||||||
|
#include "base/macros.h" |
||||||
|
#include "dex_instruction.h" |
||||||
|
|
||||||
|
namespace art_lkchan { |
||||||
|
|
||||||
|
class DexInstructionPcPair { |
||||||
|
public: |
||||||
|
ALWAYS_INLINE const Instruction& Inst() const { |
||||||
|
return *Instruction::At(instructions_ + DexPc()); |
||||||
|
} |
||||||
|
|
||||||
|
ALWAYS_INLINE const Instruction* operator->() const { |
||||||
|
return &Inst(); |
||||||
|
} |
||||||
|
|
||||||
|
ALWAYS_INLINE uint32_t DexPc() const { |
||||||
|
return dex_pc_; |
||||||
|
} |
||||||
|
|
||||||
|
ALWAYS_INLINE const uint16_t* Instructions() const { |
||||||
|
return instructions_; |
||||||
|
} |
||||||
|
|
||||||
|
protected: |
||||||
|
explicit DexInstructionPcPair(const uint16_t* instructions, uint32_t dex_pc) |
||||||
|
: instructions_(instructions), dex_pc_(dex_pc) {} |
||||||
|
|
||||||
|
const uint16_t* instructions_ = nullptr; |
||||||
|
uint32_t dex_pc_ = 0; |
||||||
|
|
||||||
|
friend class DexInstructionIteratorBase; |
||||||
|
friend class DexInstructionIterator; |
||||||
|
friend class SafeDexInstructionIterator; |
||||||
|
}; |
||||||
|
|
||||||
|
// Base helper class to prevent duplicated comparators.
|
||||||
|
class DexInstructionIteratorBase : public |
||||||
|
std::iterator<std::forward_iterator_tag, DexInstructionPcPair> { |
||||||
|
public: |
||||||
|
using value_type = std::iterator<std::forward_iterator_tag, DexInstructionPcPair>::value_type; |
||||||
|
using difference_type = std::iterator<std::forward_iterator_tag, value_type>::difference_type; |
||||||
|
|
||||||
|
DexInstructionIteratorBase() = default; |
||||||
|
explicit DexInstructionIteratorBase(const Instruction* inst, uint32_t dex_pc) |
||||||
|
: data_(reinterpret_cast<const uint16_t*>(inst), dex_pc) {} |
||||||
|
|
||||||
|
const Instruction& Inst() const { |
||||||
|
return data_.Inst(); |
||||||
|
} |
||||||
|
|
||||||
|
// Return the dex pc for an iterator compared to the code item begin.
|
||||||
|
ALWAYS_INLINE uint32_t DexPc() const { |
||||||
|
return data_.DexPc(); |
||||||
|
} |
||||||
|
|
||||||
|
// Instructions from the start of the code item.
|
||||||
|
ALWAYS_INLINE const uint16_t* Instructions() const { |
||||||
|
return data_.Instructions(); |
||||||
|
} |
||||||
|
|
||||||
|
protected: |
||||||
|
DexInstructionPcPair data_; |
||||||
|
}; |
||||||
|
|
||||||
|
static ALWAYS_INLINE inline bool operator==(const DexInstructionIteratorBase& lhs, |
||||||
|
const DexInstructionIteratorBase& rhs) { |
||||||
|
DCHECK_EQ(lhs.Instructions(), rhs.Instructions()) << "Comparing different code items."; |
||||||
|
return lhs.DexPc() == rhs.DexPc(); |
||||||
|
} |
||||||
|
|
||||||
|
static inline bool operator!=(const DexInstructionIteratorBase& lhs, |
||||||
|
const DexInstructionIteratorBase& rhs) { |
||||||
|
return !(lhs == rhs); |
||||||
|
} |
||||||
|
|
||||||
|
static inline bool operator<(const DexInstructionIteratorBase& lhs, |
||||||
|
const DexInstructionIteratorBase& rhs) { |
||||||
|
DCHECK_EQ(lhs.Instructions(), rhs.Instructions()) << "Comparing different code items."; |
||||||
|
return lhs.DexPc() < rhs.DexPc(); |
||||||
|
} |
||||||
|
|
||||||
|
static inline bool operator>(const DexInstructionIteratorBase& lhs, |
||||||
|
const DexInstructionIteratorBase& rhs) { |
||||||
|
return rhs < lhs; |
||||||
|
} |
||||||
|
|
||||||
|
static inline bool operator<=(const DexInstructionIteratorBase& lhs, |
||||||
|
const DexInstructionIteratorBase& rhs) { |
||||||
|
return !(rhs < lhs); |
||||||
|
} |
||||||
|
|
||||||
|
static inline bool operator>=(const DexInstructionIteratorBase& lhs, |
||||||
|
const DexInstructionIteratorBase& rhs) { |
||||||
|
return !(lhs < rhs); |
||||||
|
} |
||||||
|
|
||||||
|
// A helper class for a code_item's instructions using range based for loop syntax.
|
||||||
|
class DexInstructionIterator : public DexInstructionIteratorBase { |
||||||
|
public: |
||||||
|
using DexInstructionIteratorBase::DexInstructionIteratorBase; |
||||||
|
|
||||||
|
explicit DexInstructionIterator(const uint16_t* inst, uint32_t dex_pc) |
||||||
|
: DexInstructionIteratorBase(Instruction::At(inst), dex_pc) {} |
||||||
|
|
||||||
|
explicit DexInstructionIterator(const DexInstructionPcPair& pair) |
||||||
|
: DexInstructionIterator(pair.Instructions(), pair.DexPc()) {} |
||||||
|
|
||||||
|
// Value after modification.
|
||||||
|
DexInstructionIterator& operator++() { |
||||||
|
data_.dex_pc_ += Inst().SizeInCodeUnits(); |
||||||
|
return *this; |
||||||
|
} |
||||||
|
|
||||||
|
// Value before modification.
|
||||||
|
DexInstructionIterator operator++(int) { |
||||||
|
DexInstructionIterator temp = *this; |
||||||
|
++*this; |
||||||
|
return temp; |
||||||
|
} |
||||||
|
|
||||||
|
const value_type& operator*() const { |
||||||
|
return data_; |
||||||
|
} |
||||||
|
|
||||||
|
const Instruction* operator->() const { |
||||||
|
return &data_.Inst(); |
||||||
|
} |
||||||
|
|
||||||
|
// Return the dex pc for the iterator.
|
||||||
|
ALWAYS_INLINE uint32_t DexPc() const { |
||||||
|
return data_.DexPc(); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
// A safe version of DexInstructionIterator that is guaranteed to not go past the end of the code
|
||||||
|
// item.
|
||||||
|
class SafeDexInstructionIterator : public DexInstructionIteratorBase { |
||||||
|
public: |
||||||
|
explicit SafeDexInstructionIterator(const DexInstructionIteratorBase& start, |
||||||
|
const DexInstructionIteratorBase& end) |
||||||
|
: DexInstructionIteratorBase(&start.Inst(), start.DexPc()) |
||||||
|
, num_code_units_(end.DexPc()) { |
||||||
|
DCHECK_EQ(start.Instructions(), end.Instructions()) |
||||||
|
<< "start and end must be in the same code item."; |
||||||
|
} |
||||||
|
|
||||||
|
// Value after modification, does not read past the end of the allowed region. May increment past
|
||||||
|
// the end of the code item though.
|
||||||
|
SafeDexInstructionIterator& operator++() { |
||||||
|
AssertValid(); |
||||||
|
const size_t size_code_units = Inst().CodeUnitsRequiredForSizeComputation(); |
||||||
|
const size_t available = NumCodeUnits() - DexPc(); |
||||||
|
if (UNLIKELY(size_code_units > available)) { |
||||||
|
error_state_ = true; |
||||||
|
return *this; |
||||||
|
} |
||||||
|
const size_t instruction_code_units = Inst().SizeInCodeUnits(); |
||||||
|
if (UNLIKELY(instruction_code_units > available)) { |
||||||
|
error_state_ = true; |
||||||
|
return *this; |
||||||
|
} |
||||||
|
data_.dex_pc_ += instruction_code_units; |
||||||
|
return *this; |
||||||
|
} |
||||||
|
|
||||||
|
// Value before modification.
|
||||||
|
SafeDexInstructionIterator operator++(int) { |
||||||
|
SafeDexInstructionIterator temp = *this; |
||||||
|
++*this; |
||||||
|
return temp; |
||||||
|
} |
||||||
|
|
||||||
|
const value_type& operator*() const { |
||||||
|
AssertValid(); |
||||||
|
return data_; |
||||||
|
} |
||||||
|
|
||||||
|
const Instruction* operator->() const { |
||||||
|
AssertValid(); |
||||||
|
return &data_.Inst(); |
||||||
|
} |
||||||
|
|
||||||
|
// Return the current instruction of the iterator.
|
||||||
|
ALWAYS_INLINE const Instruction& Inst() const { |
||||||
|
return data_.Inst(); |
||||||
|
} |
||||||
|
|
||||||
|
const uint16_t* Instructions() const { |
||||||
|
return data_.Instructions(); |
||||||
|
} |
||||||
|
|
||||||
|
// Returns true if the iterator is in an error state. This occurs when an instruction couldn't
|
||||||
|
// have its size computed without reading past the end iterator.
|
||||||
|
bool IsErrorState() const { |
||||||
|
return error_state_; |
||||||
|
} |
||||||
|
|
||||||
|
private: |
||||||
|
ALWAYS_INLINE void AssertValid() const { |
||||||
|
DCHECK(!IsErrorState()); |
||||||
|
DCHECK_LT(DexPc(), NumCodeUnits()); |
||||||
|
} |
||||||
|
|
||||||
|
ALWAYS_INLINE uint32_t NumCodeUnits() const { |
||||||
|
return num_code_units_; |
||||||
|
} |
||||||
|
|
||||||
|
const uint32_t num_code_units_ = 0; |
||||||
|
bool error_state_ = false; |
||||||
|
}; |
||||||
|
|
||||||
|
} // namespace art_lkchan
|
||||||
|
|
||||||
|
#endif // ART_LIBDEXFILE_DEX_DEX_INSTRUCTION_ITERATOR_H_
|
@ -0,0 +1,308 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011 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_DEX_INSTRUCTION_LIST_H_ |
||||||
|
#define ART_LIBDEXFILE_DEX_DEX_INSTRUCTION_LIST_H_ |
||||||
|
|
||||||
|
// V(opcode, instruction_code, name, format, index, flags, extended_flags, verifier_flags);
|
||||||
|
#define DEX_INSTRUCTION_LIST(V) \ |
||||||
|
V(0x00, NOP, "nop", k10x, kIndexNone, kContinue, 0, kVerifyNone) \
|
||||||
|
V(0x01, MOVE, "move", k12x, kIndexNone, kContinue, 0, kVerifyRegA | kVerifyRegB) \
|
||||||
|
V(0x02, MOVE_FROM16, "move/from16", k22x, kIndexNone, kContinue, 0, kVerifyRegA | kVerifyRegB) \
|
||||||
|
V(0x03, MOVE_16, "move/16", k32x, kIndexNone, kContinue, 0, kVerifyRegA | kVerifyRegB) \
|
||||||
|
V(0x04, MOVE_WIDE, "move-wide", k12x, kIndexNone, kContinue, 0, kVerifyRegAWide | kVerifyRegBWide) \
|
||||||
|
V(0x05, MOVE_WIDE_FROM16, "move-wide/from16", k22x, kIndexNone, kContinue, 0, kVerifyRegAWide | kVerifyRegBWide) \
|
||||||
|
V(0x06, MOVE_WIDE_16, "move-wide/16", k32x, kIndexNone, kContinue, 0, kVerifyRegAWide | kVerifyRegBWide) \
|
||||||
|
V(0x07, MOVE_OBJECT, "move-object", k12x, kIndexNone, kContinue, 0, kVerifyRegA | kVerifyRegB) \
|
||||||
|
V(0x08, MOVE_OBJECT_FROM16, "move-object/from16", k22x, kIndexNone, kContinue, 0, kVerifyRegA | kVerifyRegB) \
|
||||||
|
V(0x09, MOVE_OBJECT_16, "move-object/16", k32x, kIndexNone, kContinue, 0, kVerifyRegA | kVerifyRegB) \
|
||||||
|
V(0x0A, MOVE_RESULT, "move-result", k11x, kIndexNone, kContinue, 0, kVerifyRegA) \
|
||||||
|
V(0x0B, MOVE_RESULT_WIDE, "move-result-wide", k11x, kIndexNone, kContinue, 0, kVerifyRegAWide) \
|
||||||
|
V(0x0C, MOVE_RESULT_OBJECT, "move-result-object", k11x, kIndexNone, kContinue, 0, kVerifyRegA) \
|
||||||
|
V(0x0D, MOVE_EXCEPTION, "move-exception", k11x, kIndexNone, kContinue, 0, kVerifyRegA) \
|
||||||
|
V(0x0E, RETURN_VOID, "return-void", k10x, kIndexNone, kReturn, 0, kVerifyNone) \
|
||||||
|
V(0x0F, RETURN, "return", k11x, kIndexNone, kReturn, 0, kVerifyRegA) \
|
||||||
|
V(0x10, RETURN_WIDE, "return-wide", k11x, kIndexNone, kReturn, 0, kVerifyRegAWide) \
|
||||||
|
V(0x11, RETURN_OBJECT, "return-object", k11x, kIndexNone, kReturn, 0, kVerifyRegA) \
|
||||||
|
V(0x12, CONST_4, "const/4", k11n, kIndexNone, kContinue, kRegBFieldOrConstant, kVerifyRegA) \
|
||||||
|
V(0x13, CONST_16, "const/16", k21s, kIndexNone, kContinue, kRegBFieldOrConstant, kVerifyRegA) \
|
||||||
|
V(0x14, CONST, "const", k31i, kIndexNone, kContinue, kRegBFieldOrConstant, kVerifyRegA) \
|
||||||
|
V(0x15, CONST_HIGH16, "const/high16", k21h, kIndexNone, kContinue, kRegBFieldOrConstant, kVerifyRegA) \
|
||||||
|
V(0x16, CONST_WIDE_16, "const-wide/16", k21s, kIndexNone, kContinue, kRegBFieldOrConstant, kVerifyRegAWide) \
|
||||||
|
V(0x17, CONST_WIDE_32, "const-wide/32", k31i, kIndexNone, kContinue, kRegBFieldOrConstant, kVerifyRegAWide) \
|
||||||
|
V(0x18, CONST_WIDE, "const-wide", k51l, kIndexNone, kContinue, kRegBFieldOrConstant, kVerifyRegAWide) \
|
||||||
|
V(0x19, CONST_WIDE_HIGH16, "const-wide/high16", k21h, kIndexNone, kContinue, kRegBFieldOrConstant, kVerifyRegAWide) \
|
||||||
|
V(0x1A, CONST_STRING, "const-string", k21c, kIndexStringRef, kContinue | kThrow, 0, kVerifyRegA | kVerifyRegBString) \
|
||||||
|
V(0x1B, CONST_STRING_JUMBO, "const-string/jumbo", k31c, kIndexStringRef, kContinue | kThrow, 0, kVerifyRegA | kVerifyRegBString) \
|
||||||
|
V(0x1C, CONST_CLASS, "const-class", k21c, kIndexTypeRef, kContinue | kThrow, 0, kVerifyRegA | kVerifyRegBType) \
|
||||||
|
V(0x1D, MONITOR_ENTER, "monitor-enter", k11x, kIndexNone, kContinue | kThrow, kClobber, kVerifyRegA) \
|
||||||
|
V(0x1E, MONITOR_EXIT, "monitor-exit", k11x, kIndexNone, kContinue | kThrow, kClobber, kVerifyRegA) \
|
||||||
|
V(0x1F, CHECK_CAST, "check-cast", k21c, kIndexTypeRef, kContinue | kThrow, 0, kVerifyRegA | kVerifyRegBType) \
|
||||||
|
V(0x20, INSTANCE_OF, "instance-of", k22c, kIndexTypeRef, kContinue | kThrow, 0, kVerifyRegA | kVerifyRegB | kVerifyRegCType) \
|
||||||
|
V(0x21, ARRAY_LENGTH, "array-length", k12x, kIndexNone, kContinue | kThrow, 0, kVerifyRegA | kVerifyRegB) \
|
||||||
|
V(0x22, NEW_INSTANCE, "new-instance", k21c, kIndexTypeRef, kContinue | kThrow, kClobber, kVerifyRegA | kVerifyRegBNewInstance) \
|
||||||
|
V(0x23, NEW_ARRAY, "new-array", k22c, kIndexTypeRef, kContinue | kThrow, kClobber, kVerifyRegA | kVerifyRegB | kVerifyRegCNewArray) \
|
||||||
|
V(0x24, FILLED_NEW_ARRAY, "filled-new-array", k35c, kIndexTypeRef, kContinue | kThrow, kClobber, kVerifyRegBType | kVerifyVarArg) \
|
||||||
|
V(0x25, FILLED_NEW_ARRAY_RANGE, "filled-new-array/range", k3rc, kIndexTypeRef, kContinue | kThrow, kClobber, kVerifyRegBType | kVerifyVarArgRange) \
|
||||||
|
V(0x26, FILL_ARRAY_DATA, "fill-array-data", k31t, kIndexNone, kContinue | kThrow, kClobber, kVerifyRegA | kVerifyArrayData) \
|
||||||
|
V(0x27, THROW, "throw", k11x, kIndexNone, kThrow, 0, kVerifyRegA) \
|
||||||
|
V(0x28, GOTO, "goto", k10t, kIndexNone, kBranch | kUnconditional, 0, kVerifyBranchTarget) \
|
||||||
|
V(0x29, GOTO_16, "goto/16", k20t, kIndexNone, kBranch | kUnconditional, 0, kVerifyBranchTarget) \
|
||||||
|
V(0x2A, GOTO_32, "goto/32", k30t, kIndexNone, kBranch | kUnconditional, 0, kVerifyBranchTarget) \
|
||||||
|
V(0x2B, PACKED_SWITCH, "packed-switch", k31t, kIndexNone, kContinue | kSwitch, 0, kVerifyRegA | kVerifySwitchTargets) \
|
||||||
|
V(0x2C, SPARSE_SWITCH, "sparse-switch", k31t, kIndexNone, kContinue | kSwitch, 0, kVerifyRegA | kVerifySwitchTargets) \
|
||||||
|
V(0x2D, CMPL_FLOAT, "cmpl-float", k23x, kIndexNone, kContinue, 0, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
|
||||||
|
V(0x2E, CMPG_FLOAT, "cmpg-float", k23x, kIndexNone, kContinue, 0, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
|
||||||
|
V(0x2F, CMPL_DOUBLE, "cmpl-double", k23x, kIndexNone, kContinue, 0, kVerifyRegA | kVerifyRegBWide | kVerifyRegCWide) \
|
||||||
|
V(0x30, CMPG_DOUBLE, "cmpg-double", k23x, kIndexNone, kContinue, 0, kVerifyRegA | kVerifyRegBWide | kVerifyRegCWide) \
|
||||||
|
V(0x31, CMP_LONG, "cmp-long", k23x, kIndexNone, kContinue, 0, kVerifyRegA | kVerifyRegBWide | kVerifyRegCWide) \
|
||||||
|
V(0x32, IF_EQ, "if-eq", k22t, kIndexNone, kContinue | kBranch, 0, kVerifyRegA | kVerifyRegB | kVerifyBranchTarget) \
|
||||||
|
V(0x33, IF_NE, "if-ne", k22t, kIndexNone, kContinue | kBranch, 0, kVerifyRegA | kVerifyRegB | kVerifyBranchTarget) \
|
||||||
|
V(0x34, IF_LT, "if-lt", k22t, kIndexNone, kContinue | kBranch, 0, kVerifyRegA | kVerifyRegB | kVerifyBranchTarget) \
|
||||||
|
V(0x35, IF_GE, "if-ge", k22t, kIndexNone, kContinue | kBranch, 0, kVerifyRegA | kVerifyRegB | kVerifyBranchTarget) \
|
||||||
|
V(0x36, IF_GT, "if-gt", k22t, kIndexNone, kContinue | kBranch, 0, kVerifyRegA | kVerifyRegB | kVerifyBranchTarget) \
|
||||||
|
V(0x37, IF_LE, "if-le", k22t, kIndexNone, kContinue | kBranch, 0, kVerifyRegA | kVerifyRegB | kVerifyBranchTarget) \
|
||||||
|
V(0x38, IF_EQZ, "if-eqz", k21t, kIndexNone, kContinue | kBranch, 0, kVerifyRegA | kVerifyBranchTarget) \
|
||||||
|
V(0x39, IF_NEZ, "if-nez", k21t, kIndexNone, kContinue | kBranch, 0, kVerifyRegA | kVerifyBranchTarget) \
|
||||||
|
V(0x3A, IF_LTZ, "if-ltz", k21t, kIndexNone, kContinue | kBranch, 0, kVerifyRegA | kVerifyBranchTarget) \
|
||||||
|
V(0x3B, IF_GEZ, "if-gez", k21t, kIndexNone, kContinue | kBranch, 0, kVerifyRegA | kVerifyBranchTarget) \
|
||||||
|
V(0x3C, IF_GTZ, "if-gtz", k21t, kIndexNone, kContinue | kBranch, 0, kVerifyRegA | kVerifyBranchTarget) \
|
||||||
|
V(0x3D, IF_LEZ, "if-lez", k21t, kIndexNone, kContinue | kBranch, 0, kVerifyRegA | kVerifyBranchTarget) \
|
||||||
|
V(0x3E, UNUSED_3E, "unused-3e", k10x, kIndexUnknown, 0, 0, kVerifyError) \
|
||||||
|
V(0x3F, UNUSED_3F, "unused-3f", k10x, kIndexUnknown, 0, 0, kVerifyError) \
|
||||||
|
V(0x40, UNUSED_40, "unused-40", k10x, kIndexUnknown, 0, 0, kVerifyError) \
|
||||||
|
V(0x41, UNUSED_41, "unused-41", k10x, kIndexUnknown, 0, 0, kVerifyError) \
|
||||||
|
V(0x42, UNUSED_42, "unused-42", k10x, kIndexUnknown, 0, 0, kVerifyError) \
|
||||||
|
V(0x43, UNUSED_43, "unused-43", k10x, kIndexUnknown, 0, 0, kVerifyError) \
|
||||||
|
V(0x44, AGET, "aget", k23x, kIndexNone, kContinue | kThrow, kLoad, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
|
||||||
|
V(0x45, AGET_WIDE, "aget-wide", k23x, kIndexNone, kContinue | kThrow, kLoad, kVerifyRegAWide | kVerifyRegB | kVerifyRegC) \
|
||||||
|
V(0x46, AGET_OBJECT, "aget-object", k23x, kIndexNone, kContinue | kThrow, kLoad, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
|
||||||
|
V(0x47, AGET_BOOLEAN, "aget-boolean", k23x, kIndexNone, kContinue | kThrow, kLoad, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
|
||||||
|
V(0x48, AGET_BYTE, "aget-byte", k23x, kIndexNone, kContinue | kThrow, kLoad, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
|
||||||
|
V(0x49, AGET_CHAR, "aget-char", k23x, kIndexNone, kContinue | kThrow, kLoad, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
|
||||||
|
V(0x4A, AGET_SHORT, "aget-short", k23x, kIndexNone, kContinue | kThrow, kLoad, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
|
||||||
|
V(0x4B, APUT, "aput", k23x, kIndexNone, kContinue | kThrow, kStore, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
|
||||||
|
V(0x4C, APUT_WIDE, "aput-wide", k23x, kIndexNone, kContinue | kThrow, kStore, kVerifyRegAWide | kVerifyRegB | kVerifyRegC) \
|
||||||
|
V(0x4D, APUT_OBJECT, "aput-object", k23x, kIndexNone, kContinue | kThrow, kStore, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
|
||||||
|
V(0x4E, APUT_BOOLEAN, "aput-boolean", k23x, kIndexNone, kContinue | kThrow, kStore, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
|
||||||
|
V(0x4F, APUT_BYTE, "aput-byte", k23x, kIndexNone, kContinue | kThrow, kStore, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
|
||||||
|
V(0x50, APUT_CHAR, "aput-char", k23x, kIndexNone, kContinue | kThrow, kStore, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
|
||||||
|
V(0x51, APUT_SHORT, "aput-short", k23x, kIndexNone, kContinue | kThrow, kStore, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
|
||||||
|
V(0x52, IGET, "iget", k22c, kIndexFieldRef, kContinue | kThrow, kLoad | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRegCField) \
|
||||||
|
V(0x53, IGET_WIDE, "iget-wide", k22c, kIndexFieldRef, kContinue | kThrow, kLoad | kRegCFieldOrConstant, kVerifyRegAWide | kVerifyRegB | kVerifyRegCField) \
|
||||||
|
V(0x54, IGET_OBJECT, "iget-object", k22c, kIndexFieldRef, kContinue | kThrow, kLoad | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRegCField) \
|
||||||
|
V(0x55, IGET_BOOLEAN, "iget-boolean", k22c, kIndexFieldRef, kContinue | kThrow, kLoad | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRegCField) \
|
||||||
|
V(0x56, IGET_BYTE, "iget-byte", k22c, kIndexFieldRef, kContinue | kThrow, kLoad | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRegCField) \
|
||||||
|
V(0x57, IGET_CHAR, "iget-char", k22c, kIndexFieldRef, kContinue | kThrow, kLoad | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRegCField) \
|
||||||
|
V(0x58, IGET_SHORT, "iget-short", k22c, kIndexFieldRef, kContinue | kThrow, kLoad | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRegCField) \
|
||||||
|
V(0x59, IPUT, "iput", k22c, kIndexFieldRef, kContinue | kThrow, kStore | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRegCField) \
|
||||||
|
V(0x5A, IPUT_WIDE, "iput-wide", k22c, kIndexFieldRef, kContinue | kThrow, kStore | kRegCFieldOrConstant, kVerifyRegAWide | kVerifyRegB | kVerifyRegCField) \
|
||||||
|
V(0x5B, IPUT_OBJECT, "iput-object", k22c, kIndexFieldRef, kContinue | kThrow, kStore | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRegCField) \
|
||||||
|
V(0x5C, IPUT_BOOLEAN, "iput-boolean", k22c, kIndexFieldRef, kContinue | kThrow, kStore | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRegCField) \
|
||||||
|
V(0x5D, IPUT_BYTE, "iput-byte", k22c, kIndexFieldRef, kContinue | kThrow, kStore | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRegCField) \
|
||||||
|
V(0x5E, IPUT_CHAR, "iput-char", k22c, kIndexFieldRef, kContinue | kThrow, kStore | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRegCField) \
|
||||||
|
V(0x5F, IPUT_SHORT, "iput-short", k22c, kIndexFieldRef, kContinue | kThrow, kStore | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRegCField) \
|
||||||
|
V(0x60, SGET, "sget", k21c, kIndexFieldRef, kContinue | kThrow, kLoad | kRegBFieldOrConstant, kVerifyRegA | kVerifyRegBField) \
|
||||||
|
V(0x61, SGET_WIDE, "sget-wide", k21c, kIndexFieldRef, kContinue | kThrow, kLoad | kRegBFieldOrConstant, kVerifyRegAWide | kVerifyRegBField) \
|
||||||
|
V(0x62, SGET_OBJECT, "sget-object", k21c, kIndexFieldRef, kContinue | kThrow, kLoad | kRegBFieldOrConstant, kVerifyRegA | kVerifyRegBField) \
|
||||||
|
V(0x63, SGET_BOOLEAN, "sget-boolean", k21c, kIndexFieldRef, kContinue | kThrow, kLoad | kRegBFieldOrConstant, kVerifyRegA | kVerifyRegBField) \
|
||||||
|
V(0x64, SGET_BYTE, "sget-byte", k21c, kIndexFieldRef, kContinue | kThrow, kLoad | kRegBFieldOrConstant, kVerifyRegA | kVerifyRegBField) \
|
||||||
|
V(0x65, SGET_CHAR, "sget-char", k21c, kIndexFieldRef, kContinue | kThrow, kLoad | kRegBFieldOrConstant, kVerifyRegA | kVerifyRegBField) \
|
||||||
|
V(0x66, SGET_SHORT, "sget-short", k21c, kIndexFieldRef, kContinue | kThrow, kLoad | kRegBFieldOrConstant, kVerifyRegA | kVerifyRegBField) \
|
||||||
|
V(0x67, SPUT, "sput", k21c, kIndexFieldRef, kContinue | kThrow, kStore | kRegBFieldOrConstant, kVerifyRegA | kVerifyRegBField) \
|
||||||
|
V(0x68, SPUT_WIDE, "sput-wide", k21c, kIndexFieldRef, kContinue | kThrow, kStore | kRegBFieldOrConstant, kVerifyRegAWide | kVerifyRegBField) \
|
||||||
|
V(0x69, SPUT_OBJECT, "sput-object", k21c, kIndexFieldRef, kContinue | kThrow, kStore | kRegBFieldOrConstant, kVerifyRegA | kVerifyRegBField) \
|
||||||
|
V(0x6A, SPUT_BOOLEAN, "sput-boolean", k21c, kIndexFieldRef, kContinue | kThrow, kStore | kRegBFieldOrConstant, kVerifyRegA | kVerifyRegBField) \
|
||||||
|
V(0x6B, SPUT_BYTE, "sput-byte", k21c, kIndexFieldRef, kContinue | kThrow, kStore | kRegBFieldOrConstant, kVerifyRegA | kVerifyRegBField) \
|
||||||
|
V(0x6C, SPUT_CHAR, "sput-char", k21c, kIndexFieldRef, kContinue | kThrow, kStore | kRegBFieldOrConstant, kVerifyRegA | kVerifyRegBField) \
|
||||||
|
V(0x6D, SPUT_SHORT, "sput-short", k21c, kIndexFieldRef, kContinue | kThrow, kStore | kRegBFieldOrConstant, kVerifyRegA | kVerifyRegBField) \
|
||||||
|
V(0x6E, INVOKE_VIRTUAL, "invoke-virtual", k35c, kIndexMethodRef, kContinue | kThrow | kInvoke, 0, kVerifyRegBMethod | kVerifyVarArgNonZero) \
|
||||||
|
V(0x6F, INVOKE_SUPER, "invoke-super", k35c, kIndexMethodRef, kContinue | kThrow | kInvoke, 0, kVerifyRegBMethod | kVerifyVarArgNonZero) \
|
||||||
|
V(0x70, INVOKE_DIRECT, "invoke-direct", k35c, kIndexMethodRef, kContinue | kThrow | kInvoke, 0, kVerifyRegBMethod | kVerifyVarArgNonZero) \
|
||||||
|
V(0x71, INVOKE_STATIC, "invoke-static", k35c, kIndexMethodRef, kContinue | kThrow | kInvoke, 0, kVerifyRegBMethod | kVerifyVarArg) \
|
||||||
|
V(0x72, INVOKE_INTERFACE, "invoke-interface", k35c, kIndexMethodRef, kContinue | kThrow | kInvoke, 0, kVerifyRegBMethod | kVerifyVarArgNonZero) \
|
||||||
|
V(0x73, RETURN_VOID_NO_BARRIER, "return-void-no-barrier", k10x, kIndexNone, kReturn, 0, kVerifyNone) \
|
||||||
|
V(0x74, INVOKE_VIRTUAL_RANGE, "invoke-virtual/range", k3rc, kIndexMethodRef, kContinue | kThrow | kInvoke, 0, kVerifyRegBMethod | kVerifyVarArgRangeNonZero) \
|
||||||
|
V(0x75, INVOKE_SUPER_RANGE, "invoke-super/range", k3rc, kIndexMethodRef, kContinue | kThrow | kInvoke, 0, kVerifyRegBMethod | kVerifyVarArgRangeNonZero) \
|
||||||
|
V(0x76, INVOKE_DIRECT_RANGE, "invoke-direct/range", k3rc, kIndexMethodRef, kContinue | kThrow | kInvoke, 0, kVerifyRegBMethod | kVerifyVarArgRangeNonZero) \
|
||||||
|
V(0x77, INVOKE_STATIC_RANGE, "invoke-static/range", k3rc, kIndexMethodRef, kContinue | kThrow | kInvoke, 0, kVerifyRegBMethod | kVerifyVarArgRange) \
|
||||||
|
V(0x78, INVOKE_INTERFACE_RANGE, "invoke-interface/range", k3rc, kIndexMethodRef, kContinue | kThrow | kInvoke, 0, kVerifyRegBMethod | kVerifyVarArgRangeNonZero) \
|
||||||
|
V(0x79, UNUSED_79, "unused-79", k10x, kIndexUnknown, 0, 0, kVerifyError) \
|
||||||
|
V(0x7A, UNUSED_7A, "unused-7a", k10x, kIndexUnknown, 0, 0, kVerifyError) \
|
||||||
|
V(0x7B, NEG_INT, "neg-int", k12x, kIndexNone, kContinue, 0, kVerifyRegA | kVerifyRegB) \
|
||||||
|
V(0x7C, NOT_INT, "not-int", k12x, kIndexNone, kContinue, 0, kVerifyRegA | kVerifyRegB) \
|
||||||
|
V(0x7D, NEG_LONG, "neg-long", k12x, kIndexNone, kContinue, 0, kVerifyRegAWide | kVerifyRegBWide) \
|
||||||
|
V(0x7E, NOT_LONG, "not-long", k12x, kIndexNone, kContinue, 0, kVerifyRegAWide | kVerifyRegBWide) \
|
||||||
|
V(0x7F, NEG_FLOAT, "neg-float", k12x, kIndexNone, kContinue, 0, kVerifyRegA | kVerifyRegB) \
|
||||||
|
V(0x80, NEG_DOUBLE, "neg-double", k12x, kIndexNone, kContinue, 0, kVerifyRegAWide | kVerifyRegBWide) \
|
||||||
|
V(0x81, INT_TO_LONG, "int-to-long", k12x, kIndexNone, kContinue, kCast, kVerifyRegAWide | kVerifyRegB) \
|
||||||
|
V(0x82, INT_TO_FLOAT, "int-to-float", k12x, kIndexNone, kContinue, kCast, kVerifyRegA | kVerifyRegB) \
|
||||||
|
V(0x83, INT_TO_DOUBLE, "int-to-double", k12x, kIndexNone, kContinue, kCast, kVerifyRegAWide | kVerifyRegB) \
|
||||||
|
V(0x84, LONG_TO_INT, "long-to-int", k12x, kIndexNone, kContinue, kCast, kVerifyRegA | kVerifyRegBWide) \
|
||||||
|
V(0x85, LONG_TO_FLOAT, "long-to-float", k12x, kIndexNone, kContinue, kCast, kVerifyRegA | kVerifyRegBWide) \
|
||||||
|
V(0x86, LONG_TO_DOUBLE, "long-to-double", k12x, kIndexNone, kContinue, kCast, kVerifyRegAWide | kVerifyRegBWide) \
|
||||||
|
V(0x87, FLOAT_TO_INT, "float-to-int", k12x, kIndexNone, kContinue, kCast, kVerifyRegA | kVerifyRegB) \
|
||||||
|
V(0x88, FLOAT_TO_LONG, "float-to-long", k12x, kIndexNone, kContinue, kCast, kVerifyRegAWide | kVerifyRegB) \
|
||||||
|
V(0x89, FLOAT_TO_DOUBLE, "float-to-double", k12x, kIndexNone, kContinue, kCast, kVerifyRegAWide | kVerifyRegB) \
|
||||||
|
V(0x8A, DOUBLE_TO_INT, "double-to-int", k12x, kIndexNone, kContinue, kCast, kVerifyRegA | kVerifyRegBWide) \
|
||||||
|
V(0x8B, DOUBLE_TO_LONG, "double-to-long", k12x, kIndexNone, kContinue, kCast, kVerifyRegAWide | kVerifyRegBWide) \
|
||||||
|
V(0x8C, DOUBLE_TO_FLOAT, "double-to-float", k12x, kIndexNone, kContinue, kCast, kVerifyRegA | kVerifyRegBWide) \
|
||||||
|
V(0x8D, INT_TO_BYTE, "int-to-byte", k12x, kIndexNone, kContinue, kCast, kVerifyRegA | kVerifyRegB) \
|
||||||
|
V(0x8E, INT_TO_CHAR, "int-to-char", k12x, kIndexNone, kContinue, kCast, kVerifyRegA | kVerifyRegB) \
|
||||||
|
V(0x8F, INT_TO_SHORT, "int-to-short", k12x, kIndexNone, kContinue, kCast, kVerifyRegA | kVerifyRegB) \
|
||||||
|
V(0x90, ADD_INT, "add-int", k23x, kIndexNone, kContinue, kAdd, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
|
||||||
|
V(0x91, SUB_INT, "sub-int", k23x, kIndexNone, kContinue, kSubtract, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
|
||||||
|
V(0x92, MUL_INT, "mul-int", k23x, kIndexNone, kContinue, kMultiply, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
|
||||||
|
V(0x93, DIV_INT, "div-int", k23x, kIndexNone, kContinue | kThrow, kDivide, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
|
||||||
|
V(0x94, REM_INT, "rem-int", k23x, kIndexNone, kContinue | kThrow, kRemainder, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
|
||||||
|
V(0x95, AND_INT, "and-int", k23x, kIndexNone, kContinue, kAnd, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
|
||||||
|
V(0x96, OR_INT, "or-int", k23x, kIndexNone, kContinue, kOr, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
|
||||||
|
V(0x97, XOR_INT, "xor-int", k23x, kIndexNone, kContinue, kXor, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
|
||||||
|
V(0x98, SHL_INT, "shl-int", k23x, kIndexNone, kContinue, kShl, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
|
||||||
|
V(0x99, SHR_INT, "shr-int", k23x, kIndexNone, kContinue, kShr, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
|
||||||
|
V(0x9A, USHR_INT, "ushr-int", k23x, kIndexNone, kContinue, kUshr, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
|
||||||
|
V(0x9B, ADD_LONG, "add-long", k23x, kIndexNone, kContinue, kAdd, kVerifyRegAWide | kVerifyRegBWide | kVerifyRegCWide) \
|
||||||
|
V(0x9C, SUB_LONG, "sub-long", k23x, kIndexNone, kContinue, kSubtract, kVerifyRegAWide | kVerifyRegBWide | kVerifyRegCWide) \
|
||||||
|
V(0x9D, MUL_LONG, "mul-long", k23x, kIndexNone, kContinue, kMultiply, kVerifyRegAWide | kVerifyRegBWide | kVerifyRegCWide) \
|
||||||
|
V(0x9E, DIV_LONG, "div-long", k23x, kIndexNone, kContinue | kThrow, kDivide, kVerifyRegAWide | kVerifyRegBWide | kVerifyRegCWide) \
|
||||||
|
V(0x9F, REM_LONG, "rem-long", k23x, kIndexNone, kContinue | kThrow, kRemainder, kVerifyRegAWide | kVerifyRegBWide | kVerifyRegCWide) \
|
||||||
|
V(0xA0, AND_LONG, "and-long", k23x, kIndexNone, kContinue, kAnd, kVerifyRegAWide | kVerifyRegBWide | kVerifyRegCWide) \
|
||||||
|
V(0xA1, OR_LONG, "or-long", k23x, kIndexNone, kContinue, kOr, kVerifyRegAWide | kVerifyRegBWide | kVerifyRegCWide) \
|
||||||
|
V(0xA2, XOR_LONG, "xor-long", k23x, kIndexNone, kContinue, kXor, kVerifyRegAWide | kVerifyRegBWide | kVerifyRegCWide) \
|
||||||
|
V(0xA3, SHL_LONG, "shl-long", k23x, kIndexNone, kContinue, kShl, kVerifyRegAWide | kVerifyRegBWide | kVerifyRegC) \
|
||||||
|
V(0xA4, SHR_LONG, "shr-long", k23x, kIndexNone, kContinue, kShr, kVerifyRegAWide | kVerifyRegBWide | kVerifyRegC) \
|
||||||
|
V(0xA5, USHR_LONG, "ushr-long", k23x, kIndexNone, kContinue, kUshr, kVerifyRegAWide | kVerifyRegBWide | kVerifyRegC) \
|
||||||
|
V(0xA6, ADD_FLOAT, "add-float", k23x, kIndexNone, kContinue, kAdd, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
|
||||||
|
V(0xA7, SUB_FLOAT, "sub-float", k23x, kIndexNone, kContinue, kSubtract, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
|
||||||
|
V(0xA8, MUL_FLOAT, "mul-float", k23x, kIndexNone, kContinue, kMultiply, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
|
||||||
|
V(0xA9, DIV_FLOAT, "div-float", k23x, kIndexNone, kContinue, kDivide, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
|
||||||
|
V(0xAA, REM_FLOAT, "rem-float", k23x, kIndexNone, kContinue, kRemainder, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
|
||||||
|
V(0xAB, ADD_DOUBLE, "add-double", k23x, kIndexNone, kContinue, kAdd, kVerifyRegAWide | kVerifyRegBWide | kVerifyRegCWide) \
|
||||||
|
V(0xAC, SUB_DOUBLE, "sub-double", k23x, kIndexNone, kContinue, kSubtract, kVerifyRegAWide | kVerifyRegBWide | kVerifyRegCWide) \
|
||||||
|
V(0xAD, MUL_DOUBLE, "mul-double", k23x, kIndexNone, kContinue, kMultiply, kVerifyRegAWide | kVerifyRegBWide | kVerifyRegCWide) \
|
||||||
|
V(0xAE, DIV_DOUBLE, "div-double", k23x, kIndexNone, kContinue, kDivide, kVerifyRegAWide | kVerifyRegBWide | kVerifyRegCWide) \
|
||||||
|
V(0xAF, REM_DOUBLE, "rem-double", k23x, kIndexNone, kContinue, kRemainder, kVerifyRegAWide | kVerifyRegBWide | kVerifyRegCWide) \
|
||||||
|
V(0xB0, ADD_INT_2ADDR, "add-int/2addr", k12x, kIndexNone, kContinue, kAdd, kVerifyRegA | kVerifyRegB) \
|
||||||
|
V(0xB1, SUB_INT_2ADDR, "sub-int/2addr", k12x, kIndexNone, kContinue, kSubtract, kVerifyRegA | kVerifyRegB) \
|
||||||
|
V(0xB2, MUL_INT_2ADDR, "mul-int/2addr", k12x, kIndexNone, kContinue, kMultiply, kVerifyRegA | kVerifyRegB) \
|
||||||
|
V(0xB3, DIV_INT_2ADDR, "div-int/2addr", k12x, kIndexNone, kContinue | kThrow, kDivide, kVerifyRegA | kVerifyRegB) \
|
||||||
|
V(0xB4, REM_INT_2ADDR, "rem-int/2addr", k12x, kIndexNone, kContinue | kThrow, kRemainder, kVerifyRegA | kVerifyRegB) \
|
||||||
|
V(0xB5, AND_INT_2ADDR, "and-int/2addr", k12x, kIndexNone, kContinue, kAnd, kVerifyRegA | kVerifyRegB) \
|
||||||
|
V(0xB6, OR_INT_2ADDR, "or-int/2addr", k12x, kIndexNone, kContinue, kOr, kVerifyRegA | kVerifyRegB) \
|
||||||
|
V(0xB7, XOR_INT_2ADDR, "xor-int/2addr", k12x, kIndexNone, kContinue, kXor, kVerifyRegA | kVerifyRegB) \
|
||||||
|
V(0xB8, SHL_INT_2ADDR, "shl-int/2addr", k12x, kIndexNone, kContinue, kShl, kVerifyRegA | kVerifyRegB) \
|
||||||
|
V(0xB9, SHR_INT_2ADDR, "shr-int/2addr", k12x, kIndexNone, kContinue, kShr, kVerifyRegA | kVerifyRegB) \
|
||||||
|
V(0xBA, USHR_INT_2ADDR, "ushr-int/2addr", k12x, kIndexNone, kContinue, kUshr, kVerifyRegA | kVerifyRegB) \
|
||||||
|
V(0xBB, ADD_LONG_2ADDR, "add-long/2addr", k12x, kIndexNone, kContinue, kAdd, kVerifyRegAWide | kVerifyRegBWide) \
|
||||||
|
V(0xBC, SUB_LONG_2ADDR, "sub-long/2addr", k12x, kIndexNone, kContinue, kSubtract, kVerifyRegAWide | kVerifyRegBWide) \
|
||||||
|
V(0xBD, MUL_LONG_2ADDR, "mul-long/2addr", k12x, kIndexNone, kContinue, kMultiply, kVerifyRegAWide | kVerifyRegBWide) \
|
||||||
|
V(0xBE, DIV_LONG_2ADDR, "div-long/2addr", k12x, kIndexNone, kContinue | kThrow, kDivide, kVerifyRegAWide | kVerifyRegBWide) \
|
||||||
|
V(0xBF, REM_LONG_2ADDR, "rem-long/2addr", k12x, kIndexNone, kContinue | kThrow, kRemainder, kVerifyRegAWide | kVerifyRegBWide) \
|
||||||
|
V(0xC0, AND_LONG_2ADDR, "and-long/2addr", k12x, kIndexNone, kContinue, kAnd, kVerifyRegAWide | kVerifyRegBWide) \
|
||||||
|
V(0xC1, OR_LONG_2ADDR, "or-long/2addr", k12x, kIndexNone, kContinue, kOr, kVerifyRegAWide | kVerifyRegBWide) \
|
||||||
|
V(0xC2, XOR_LONG_2ADDR, "xor-long/2addr", k12x, kIndexNone, kContinue, kXor, kVerifyRegAWide | kVerifyRegBWide) \
|
||||||
|
V(0xC3, SHL_LONG_2ADDR, "shl-long/2addr", k12x, kIndexNone, kContinue, kShl, kVerifyRegAWide | kVerifyRegB) \
|
||||||
|
V(0xC4, SHR_LONG_2ADDR, "shr-long/2addr", k12x, kIndexNone, kContinue, kShr, kVerifyRegAWide | kVerifyRegB) \
|
||||||
|
V(0xC5, USHR_LONG_2ADDR, "ushr-long/2addr", k12x, kIndexNone, kContinue, kUshr, kVerifyRegAWide | kVerifyRegB) \
|
||||||
|
V(0xC6, ADD_FLOAT_2ADDR, "add-float/2addr", k12x, kIndexNone, kContinue, kAdd, kVerifyRegA | kVerifyRegB) \
|
||||||
|
V(0xC7, SUB_FLOAT_2ADDR, "sub-float/2addr", k12x, kIndexNone, kContinue, kSubtract, kVerifyRegA | kVerifyRegB) \
|
||||||
|
V(0xC8, MUL_FLOAT_2ADDR, "mul-float/2addr", k12x, kIndexNone, kContinue, kMultiply, kVerifyRegA | kVerifyRegB) \
|
||||||
|
V(0xC9, DIV_FLOAT_2ADDR, "div-float/2addr", k12x, kIndexNone, kContinue, kDivide, kVerifyRegA | kVerifyRegB) \
|
||||||
|
V(0xCA, REM_FLOAT_2ADDR, "rem-float/2addr", k12x, kIndexNone, kContinue, kRemainder, kVerifyRegA | kVerifyRegB) \
|
||||||
|
V(0xCB, ADD_DOUBLE_2ADDR, "add-double/2addr", k12x, kIndexNone, kContinue, kAdd, kVerifyRegAWide | kVerifyRegBWide) \
|
||||||
|
V(0xCC, SUB_DOUBLE_2ADDR, "sub-double/2addr", k12x, kIndexNone, kContinue, kSubtract, kVerifyRegAWide | kVerifyRegBWide) \
|
||||||
|
V(0xCD, MUL_DOUBLE_2ADDR, "mul-double/2addr", k12x, kIndexNone, kContinue, kMultiply, kVerifyRegAWide | kVerifyRegBWide) \
|
||||||
|
V(0xCE, DIV_DOUBLE_2ADDR, "div-double/2addr", k12x, kIndexNone, kContinue, kDivide, kVerifyRegAWide | kVerifyRegBWide) \
|
||||||
|
V(0xCF, REM_DOUBLE_2ADDR, "rem-double/2addr", k12x, kIndexNone, kContinue, kRemainder, kVerifyRegAWide | kVerifyRegBWide) \
|
||||||
|
V(0xD0, ADD_INT_LIT16, "add-int/lit16", k22s, kIndexNone, kContinue, kAdd | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB) \
|
||||||
|
V(0xD1, RSUB_INT, "rsub-int", k22s, kIndexNone, kContinue, kSubtract | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB) \
|
||||||
|
V(0xD2, MUL_INT_LIT16, "mul-int/lit16", k22s, kIndexNone, kContinue, kMultiply | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB) \
|
||||||
|
V(0xD3, DIV_INT_LIT16, "div-int/lit16", k22s, kIndexNone, kContinue | kThrow, kDivide | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB) \
|
||||||
|
V(0xD4, REM_INT_LIT16, "rem-int/lit16", k22s, kIndexNone, kContinue | kThrow, kRemainder | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB) \
|
||||||
|
V(0xD5, AND_INT_LIT16, "and-int/lit16", k22s, kIndexNone, kContinue, kAnd | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB) \
|
||||||
|
V(0xD6, OR_INT_LIT16, "or-int/lit16", k22s, kIndexNone, kContinue, kOr | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB) \
|
||||||
|
V(0xD7, XOR_INT_LIT16, "xor-int/lit16", k22s, kIndexNone, kContinue, kXor | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB) \
|
||||||
|
V(0xD8, ADD_INT_LIT8, "add-int/lit8", k22b, kIndexNone, kContinue, kAdd | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB) \
|
||||||
|
V(0xD9, RSUB_INT_LIT8, "rsub-int/lit8", k22b, kIndexNone, kContinue, kSubtract | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB) \
|
||||||
|
V(0xDA, MUL_INT_LIT8, "mul-int/lit8", k22b, kIndexNone, kContinue, kMultiply | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB) \
|
||||||
|
V(0xDB, DIV_INT_LIT8, "div-int/lit8", k22b, kIndexNone, kContinue | kThrow, kDivide | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB) \
|
||||||
|
V(0xDC, REM_INT_LIT8, "rem-int/lit8", k22b, kIndexNone, kContinue | kThrow, kRemainder | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB) \
|
||||||
|
V(0xDD, AND_INT_LIT8, "and-int/lit8", k22b, kIndexNone, kContinue, kAnd | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB) \
|
||||||
|
V(0xDE, OR_INT_LIT8, "or-int/lit8", k22b, kIndexNone, kContinue, kOr | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB) \
|
||||||
|
V(0xDF, XOR_INT_LIT8, "xor-int/lit8", k22b, kIndexNone, kContinue, kXor | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB) \
|
||||||
|
V(0xE0, SHL_INT_LIT8, "shl-int/lit8", k22b, kIndexNone, kContinue, kShl | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB) \
|
||||||
|
V(0xE1, SHR_INT_LIT8, "shr-int/lit8", k22b, kIndexNone, kContinue, kShr | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB) \
|
||||||
|
V(0xE2, USHR_INT_LIT8, "ushr-int/lit8", k22b, kIndexNone, kContinue, kUshr | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB) \
|
||||||
|
V(0xE3, IGET_QUICK, "iget-quick", k22c, kIndexFieldOffset, kContinue | kThrow, kLoad | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRuntimeOnly) \
|
||||||
|
V(0xE4, IGET_WIDE_QUICK, "iget-wide-quick", k22c, kIndexFieldOffset, kContinue | kThrow, kLoad | kRegCFieldOrConstant, kVerifyRegAWide | kVerifyRegB | kVerifyRuntimeOnly) \
|
||||||
|
V(0xE5, IGET_OBJECT_QUICK, "iget-object-quick", k22c, kIndexFieldOffset, kContinue | kThrow, kLoad | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRuntimeOnly) \
|
||||||
|
V(0xE6, IPUT_QUICK, "iput-quick", k22c, kIndexFieldOffset, kContinue | kThrow, kStore | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRuntimeOnly) \
|
||||||
|
V(0xE7, IPUT_WIDE_QUICK, "iput-wide-quick", k22c, kIndexFieldOffset, kContinue | kThrow, kStore | kRegCFieldOrConstant, kVerifyRegAWide | kVerifyRegB | kVerifyRuntimeOnly) \
|
||||||
|
V(0xE8, IPUT_OBJECT_QUICK, "iput-object-quick", k22c, kIndexFieldOffset, kContinue | kThrow, kStore | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRuntimeOnly) \
|
||||||
|
V(0xE9, INVOKE_VIRTUAL_QUICK, "invoke-virtual-quick", k35c, kIndexVtableOffset, kContinue | kThrow | kInvoke, 0, kVerifyVarArgNonZero | kVerifyRuntimeOnly) \
|
||||||
|
V(0xEA, INVOKE_VIRTUAL_RANGE_QUICK, "invoke-virtual/range-quick", k3rc, kIndexVtableOffset, kContinue | kThrow | kInvoke, 0, kVerifyVarArgRangeNonZero | kVerifyRuntimeOnly) \
|
||||||
|
V(0xEB, IPUT_BOOLEAN_QUICK, "iput-boolean-quick", k22c, kIndexFieldOffset, kContinue | kThrow, kStore | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRuntimeOnly) \
|
||||||
|
V(0xEC, IPUT_BYTE_QUICK, "iput-byte-quick", k22c, kIndexFieldOffset, kContinue | kThrow, kStore | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRuntimeOnly) \
|
||||||
|
V(0xED, IPUT_CHAR_QUICK, "iput-char-quick", k22c, kIndexFieldOffset, kContinue | kThrow, kStore | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRuntimeOnly) \
|
||||||
|
V(0xEE, IPUT_SHORT_QUICK, "iput-short-quick", k22c, kIndexFieldOffset, kContinue | kThrow, kStore | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRuntimeOnly) \
|
||||||
|
V(0xEF, IGET_BOOLEAN_QUICK, "iget-boolean-quick", k22c, kIndexFieldOffset, kContinue | kThrow, kLoad | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRuntimeOnly) \
|
||||||
|
V(0xF0, IGET_BYTE_QUICK, "iget-byte-quick", k22c, kIndexFieldOffset, kContinue | kThrow, kLoad | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRuntimeOnly) \
|
||||||
|
V(0xF1, IGET_CHAR_QUICK, "iget-char-quick", k22c, kIndexFieldOffset, kContinue | kThrow, kLoad | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRuntimeOnly) \
|
||||||
|
V(0xF2, IGET_SHORT_QUICK, "iget-short-quick", k22c, kIndexFieldOffset, kContinue | kThrow, kLoad | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRuntimeOnly) \
|
||||||
|
V(0xF3, UNUSED_F3, "unused-f3", k10x, kIndexUnknown, 0, 0, kVerifyError) \
|
||||||
|
V(0xF4, UNUSED_F4, "unused-f4", k10x, kIndexUnknown, 0, 0, kVerifyError) \
|
||||||
|
V(0xF5, UNUSED_F5, "unused-f5", k10x, kIndexUnknown, 0, 0, kVerifyError) \
|
||||||
|
V(0xF6, UNUSED_F6, "unused-f6", k10x, kIndexUnknown, 0, 0, kVerifyError) \
|
||||||
|
V(0xF7, UNUSED_F7, "unused-f7", k10x, kIndexUnknown, 0, 0, kVerifyError) \
|
||||||
|
V(0xF8, UNUSED_F8, "unused-f8", k10x, kIndexUnknown, 0, 0, kVerifyError) \
|
||||||
|
V(0xF9, UNUSED_F9, "unused-f9", k10x, kIndexUnknown, 0, 0, kVerifyError) \
|
||||||
|
V(0xFA, INVOKE_POLYMORPHIC, "invoke-polymorphic", k45cc, kIndexMethodAndProtoRef, kContinue | kThrow | kInvoke, 0, kVerifyRegBMethod | kVerifyVarArgNonZero | kVerifyRegHPrototype) \
|
||||||
|
V(0xFB, INVOKE_POLYMORPHIC_RANGE, "invoke-polymorphic/range", k4rcc, kIndexMethodAndProtoRef, kContinue | kThrow | kInvoke, 0, kVerifyRegBMethod | kVerifyVarArgRangeNonZero | kVerifyRegHPrototype) \
|
||||||
|
V(0xFC, INVOKE_CUSTOM, "invoke-custom", k35c, kIndexCallSiteRef, kContinue | kThrow, 0, kVerifyRegBCallSite | kVerifyVarArg) \
|
||||||
|
V(0xFD, INVOKE_CUSTOM_RANGE, "invoke-custom/range", k3rc, kIndexCallSiteRef, kContinue | kThrow, 0, kVerifyRegBCallSite | kVerifyVarArgRange) \
|
||||||
|
V(0xFE, CONST_METHOD_HANDLE, "const-method-handle", k21c, kIndexMethodHandleRef, kContinue | kThrow, 0, kVerifyRegA | kVerifyRegBMethodHandle) \
|
||||||
|
V(0xFF, CONST_METHOD_TYPE, "const-method-type", k21c, kIndexProtoRef, kContinue | kThrow, 0, kVerifyRegA | kVerifyRegBPrototype) |
||||||
|
|
||||||
|
#define DEX_INSTRUCTION_FORMAT_LIST(V) \ |
||||||
|
V(k10x) \
|
||||||
|
V(k12x) \
|
||||||
|
V(k11n) \
|
||||||
|
V(k11x) \
|
||||||
|
V(k10t) \
|
||||||
|
V(k20t) \
|
||||||
|
V(k22x) \
|
||||||
|
V(k21t) \
|
||||||
|
V(k21s) \
|
||||||
|
V(k21h) \
|
||||||
|
V(k21c) \
|
||||||
|
V(k23x) \
|
||||||
|
V(k22b) \
|
||||||
|
V(k22t) \
|
||||||
|
V(k22s) \
|
||||||
|
V(k22c) \
|
||||||
|
V(k32x) \
|
||||||
|
V(k30t) \
|
||||||
|
V(k31t) \
|
||||||
|
V(k31i) \
|
||||||
|
V(k31c) \
|
||||||
|
V(k35c) \
|
||||||
|
V(k3rc) \
|
||||||
|
V(k45cc) \
|
||||||
|
V(k4rcc) \
|
||||||
|
V(k51l) |
||||||
|
|
||||||
|
#endif // ART_LIBDEXFILE_DEX_DEX_INSTRUCTION_LIST_H_
|
||||||
|
#undef ART_LIBDEXFILE_DEX_DEX_INSTRUCTION_LIST_H_ // the guard in this file is just for cpplint
|
@ -0,0 +1,219 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2014 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_DEX_INSTRUCTION_UTILS_H_ |
||||||
|
#define ART_LIBDEXFILE_DEX_DEX_INSTRUCTION_UTILS_H_ |
||||||
|
|
||||||
|
#include "dex_instruction.h" |
||||||
|
|
||||||
|
namespace art_lkchan { |
||||||
|
|
||||||
|
// Dex invoke type corresponds to the ordering of INVOKE instructions;
|
||||||
|
// this order is the same for range and non-range invokes.
|
||||||
|
enum DexInvokeType : uint8_t { |
||||||
|
kDexInvokeVirtual = 0, // invoke-virtual, invoke-virtual-range
|
||||||
|
kDexInvokeSuper, // invoke-super, invoke-super-range
|
||||||
|
kDexInvokeDirect, // invoke-direct, invoke-direct-range
|
||||||
|
kDexInvokeStatic, // invoke-static, invoke-static-range
|
||||||
|
kDexInvokeInterface, // invoke-interface, invoke-interface-range
|
||||||
|
kDexInvokeTypeCount |
||||||
|
}; |
||||||
|
|
||||||
|
// Dex instruction memory access types correspond to the ordering of GET/PUT instructions;
|
||||||
|
// this order is the same for IGET, IPUT, SGET, SPUT, AGET and APUT.
|
||||||
|
enum DexMemAccessType : uint8_t { |
||||||
|
kDexMemAccessWord = 0, // op 0; int or float, the actual type is not encoded.
|
||||||
|
kDexMemAccessWide, // op_WIDE 1; long or double, the actual type is not encoded.
|
||||||
|
kDexMemAccessObject, // op_OBJECT 2; the actual reference type is not encoded.
|
||||||
|
kDexMemAccessBoolean, // op_BOOLEAN 3
|
||||||
|
kDexMemAccessByte, // op_BYTE 4
|
||||||
|
kDexMemAccessChar, // op_CHAR 5
|
||||||
|
kDexMemAccessShort, // op_SHORT 6
|
||||||
|
kDexMemAccessTypeCount |
||||||
|
}; |
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& os, const DexMemAccessType& type); |
||||||
|
|
||||||
|
// NOTE: The following functions disregard quickened instructions.
|
||||||
|
|
||||||
|
// By "direct" const we mean to exclude const-string and const-class
|
||||||
|
// which load data from somewhere else, i.e. indirectly.
|
||||||
|
constexpr bool IsInstructionDirectConst(Instruction::Code opcode) { |
||||||
|
return Instruction::CONST_4 <= opcode && opcode <= Instruction::CONST_WIDE_HIGH16; |
||||||
|
} |
||||||
|
|
||||||
|
constexpr bool IsInstructionConstWide(Instruction::Code opcode) { |
||||||
|
return Instruction::CONST_WIDE_16 <= opcode && opcode <= Instruction::CONST_WIDE_HIGH16; |
||||||
|
} |
||||||
|
|
||||||
|
constexpr bool IsInstructionReturn(Instruction::Code opcode) { |
||||||
|
return Instruction::RETURN_VOID <= opcode && opcode <= Instruction::RETURN_OBJECT; |
||||||
|
} |
||||||
|
|
||||||
|
constexpr bool IsInstructionInvoke(Instruction::Code opcode) { |
||||||
|
return Instruction::INVOKE_VIRTUAL <= opcode && opcode <= Instruction::INVOKE_INTERFACE_RANGE && |
||||||
|
opcode != Instruction::RETURN_VOID_NO_BARRIER; |
||||||
|
} |
||||||
|
|
||||||
|
constexpr bool IsInstructionQuickInvoke(Instruction::Code opcode) { |
||||||
|
return opcode == Instruction::INVOKE_VIRTUAL_QUICK || |
||||||
|
opcode == Instruction::INVOKE_VIRTUAL_RANGE_QUICK; |
||||||
|
} |
||||||
|
|
||||||
|
constexpr bool IsInstructionInvokeStatic(Instruction::Code opcode) { |
||||||
|
return opcode == Instruction::INVOKE_STATIC || opcode == Instruction::INVOKE_STATIC_RANGE; |
||||||
|
} |
||||||
|
|
||||||
|
constexpr bool IsInstructionGoto(Instruction::Code opcode) { |
||||||
|
return Instruction::GOTO <= opcode && opcode <= Instruction::GOTO_32; |
||||||
|
} |
||||||
|
|
||||||
|
constexpr bool IsInstructionIfCc(Instruction::Code opcode) { |
||||||
|
return Instruction::IF_EQ <= opcode && opcode <= Instruction::IF_LE; |
||||||
|
} |
||||||
|
|
||||||
|
constexpr bool IsInstructionIfCcZ(Instruction::Code opcode) { |
||||||
|
return Instruction::IF_EQZ <= opcode && opcode <= Instruction::IF_LEZ; |
||||||
|
} |
||||||
|
|
||||||
|
constexpr bool IsInstructionIGet(Instruction::Code code) { |
||||||
|
return Instruction::IGET <= code && code <= Instruction::IGET_SHORT; |
||||||
|
} |
||||||
|
|
||||||
|
constexpr bool IsInstructionIPut(Instruction::Code code) { |
||||||
|
return Instruction::IPUT <= code && code <= Instruction::IPUT_SHORT; |
||||||
|
} |
||||||
|
|
||||||
|
constexpr bool IsInstructionSGet(Instruction::Code code) { |
||||||
|
return Instruction::SGET <= code && code <= Instruction::SGET_SHORT; |
||||||
|
} |
||||||
|
|
||||||
|
constexpr bool IsInstructionSPut(Instruction::Code code) { |
||||||
|
return Instruction::SPUT <= code && code <= Instruction::SPUT_SHORT; |
||||||
|
} |
||||||
|
|
||||||
|
constexpr bool IsInstructionAGet(Instruction::Code code) { |
||||||
|
return Instruction::AGET <= code && code <= Instruction::AGET_SHORT; |
||||||
|
} |
||||||
|
|
||||||
|
constexpr bool IsInstructionAPut(Instruction::Code code) { |
||||||
|
return Instruction::APUT <= code && code <= Instruction::APUT_SHORT; |
||||||
|
} |
||||||
|
|
||||||
|
constexpr bool IsInstructionIGetOrIPut(Instruction::Code code) { |
||||||
|
return Instruction::IGET <= code && code <= Instruction::IPUT_SHORT; |
||||||
|
} |
||||||
|
|
||||||
|
constexpr bool IsInstructionIGetQuickOrIPutQuick(Instruction::Code code) { |
||||||
|
return (code >= Instruction::IGET_QUICK && code <= Instruction::IPUT_OBJECT_QUICK) || |
||||||
|
(code >= Instruction::IPUT_BOOLEAN_QUICK && code <= Instruction::IGET_SHORT_QUICK); |
||||||
|
} |
||||||
|
|
||||||
|
constexpr bool IsInstructionSGetOrSPut(Instruction::Code code) { |
||||||
|
return Instruction::SGET <= code && code <= Instruction::SPUT_SHORT; |
||||||
|
} |
||||||
|
|
||||||
|
constexpr bool IsInstructionAGetOrAPut(Instruction::Code code) { |
||||||
|
return Instruction::AGET <= code && code <= Instruction::APUT_SHORT; |
||||||
|
} |
||||||
|
|
||||||
|
constexpr bool IsInstructionBinOp2Addr(Instruction::Code code) { |
||||||
|
return Instruction::ADD_INT_2ADDR <= code && code <= Instruction::REM_DOUBLE_2ADDR; |
||||||
|
} |
||||||
|
|
||||||
|
constexpr bool IsInvokeInstructionRange(Instruction::Code opcode) { |
||||||
|
DCHECK(IsInstructionInvoke(opcode)); |
||||||
|
return opcode >= Instruction::INVOKE_VIRTUAL_RANGE; |
||||||
|
} |
||||||
|
|
||||||
|
constexpr DexInvokeType InvokeInstructionType(Instruction::Code opcode) { |
||||||
|
DCHECK(IsInstructionInvoke(opcode)); |
||||||
|
return static_cast<DexInvokeType>(IsInvokeInstructionRange(opcode) |
||||||
|
? (opcode - Instruction::INVOKE_VIRTUAL_RANGE) |
||||||
|
: (opcode - Instruction::INVOKE_VIRTUAL)); |
||||||
|
} |
||||||
|
|
||||||
|
constexpr DexMemAccessType IGetMemAccessType(Instruction::Code code) { |
||||||
|
DCHECK(IsInstructionIGet(code)); |
||||||
|
return static_cast<DexMemAccessType>(code - Instruction::IGET); |
||||||
|
} |
||||||
|
|
||||||
|
constexpr DexMemAccessType IPutMemAccessType(Instruction::Code code) { |
||||||
|
DCHECK(IsInstructionIPut(code)); |
||||||
|
return static_cast<DexMemAccessType>(code - Instruction::IPUT); |
||||||
|
} |
||||||
|
|
||||||
|
constexpr DexMemAccessType SGetMemAccessType(Instruction::Code code) { |
||||||
|
DCHECK(IsInstructionSGet(code)); |
||||||
|
return static_cast<DexMemAccessType>(code - Instruction::SGET); |
||||||
|
} |
||||||
|
|
||||||
|
constexpr DexMemAccessType SPutMemAccessType(Instruction::Code code) { |
||||||
|
DCHECK(IsInstructionSPut(code)); |
||||||
|
return static_cast<DexMemAccessType>(code - Instruction::SPUT); |
||||||
|
} |
||||||
|
|
||||||
|
constexpr DexMemAccessType AGetMemAccessType(Instruction::Code code) { |
||||||
|
DCHECK(IsInstructionAGet(code)); |
||||||
|
return static_cast<DexMemAccessType>(code - Instruction::AGET); |
||||||
|
} |
||||||
|
|
||||||
|
constexpr DexMemAccessType APutMemAccessType(Instruction::Code code) { |
||||||
|
DCHECK(IsInstructionAPut(code)); |
||||||
|
return static_cast<DexMemAccessType>(code - Instruction::APUT); |
||||||
|
} |
||||||
|
|
||||||
|
constexpr DexMemAccessType IGetOrIPutMemAccessType(Instruction::Code code) { |
||||||
|
DCHECK(IsInstructionIGetOrIPut(code)); |
||||||
|
return (code >= Instruction::IPUT) ? IPutMemAccessType(code) : IGetMemAccessType(code); |
||||||
|
} |
||||||
|
|
||||||
|
inline DexMemAccessType IGetQuickOrIPutQuickMemAccessType(Instruction::Code code) { |
||||||
|
DCHECK(IsInstructionIGetQuickOrIPutQuick(code)); |
||||||
|
switch (code) { |
||||||
|
case Instruction::IGET_QUICK: case Instruction::IPUT_QUICK: |
||||||
|
return kDexMemAccessWord; |
||||||
|
case Instruction::IGET_WIDE_QUICK: case Instruction::IPUT_WIDE_QUICK: |
||||||
|
return kDexMemAccessWide; |
||||||
|
case Instruction::IGET_OBJECT_QUICK: case Instruction::IPUT_OBJECT_QUICK: |
||||||
|
return kDexMemAccessObject; |
||||||
|
case Instruction::IGET_BOOLEAN_QUICK: case Instruction::IPUT_BOOLEAN_QUICK: |
||||||
|
return kDexMemAccessBoolean; |
||||||
|
case Instruction::IGET_BYTE_QUICK: case Instruction::IPUT_BYTE_QUICK: |
||||||
|
return kDexMemAccessByte; |
||||||
|
case Instruction::IGET_CHAR_QUICK: case Instruction::IPUT_CHAR_QUICK: |
||||||
|
return kDexMemAccessChar; |
||||||
|
case Instruction::IGET_SHORT_QUICK: case Instruction::IPUT_SHORT_QUICK: |
||||||
|
return kDexMemAccessShort; |
||||||
|
default: |
||||||
|
LOG(FATAL) << code; |
||||||
|
UNREACHABLE(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
constexpr DexMemAccessType SGetOrSPutMemAccessType(Instruction::Code code) { |
||||||
|
DCHECK(IsInstructionSGetOrSPut(code)); |
||||||
|
return (code >= Instruction::SPUT) ? SPutMemAccessType(code) : SGetMemAccessType(code); |
||||||
|
} |
||||||
|
|
||||||
|
constexpr DexMemAccessType AGetOrAPutMemAccessType(Instruction::Code code) { |
||||||
|
DCHECK(IsInstructionAGetOrAPut(code)); |
||||||
|
return (code >= Instruction::APUT) ? APutMemAccessType(code) : AGetMemAccessType(code); |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace art_lkchan
|
||||||
|
|
||||||
|
#endif // ART_LIBDEXFILE_DEX_DEX_INSTRUCTION_UTILS_H_
|
@ -0,0 +1,169 @@ |
|||||||
|
/*
|
||||||
|
* 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_lkchan { |
||||||
|
|
||||||
|
/* 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<ApiList>(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<uint32_t>(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<ApiList>(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<uint32_t>(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_lkchan
|
||||||
|
|
||||||
|
|
||||||
|
#endif // ART_LIBDEXFILE_DEX_HIDDEN_API_ACCESS_FLAGS_H_
|
@ -0,0 +1,38 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011 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_INVOKE_TYPE_H_ |
||||||
|
#define ART_LIBDEXFILE_DEX_INVOKE_TYPE_H_ |
||||||
|
|
||||||
|
#include <iosfwd> |
||||||
|
|
||||||
|
namespace art_lkchan { |
||||||
|
|
||||||
|
enum InvokeType : uint32_t { |
||||||
|
kStatic, // <<static>>
|
||||||
|
kDirect, // <<direct>>
|
||||||
|
kVirtual, // <<virtual>>
|
||||||
|
kSuper, // <<super>>
|
||||||
|
kInterface, // <<interface>>
|
||||||
|
kPolymorphic, // <<polymorphic>>
|
||||||
|
kMaxInvokeType = kPolymorphic |
||||||
|
}; |
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& os, const InvokeType& rhs); |
||||||
|
|
||||||
|
} // namespace art_lkchan
|
||||||
|
|
||||||
|
#endif // ART_LIBDEXFILE_DEX_INVOKE_TYPE_H_
|
@ -0,0 +1,91 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2013 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_METHOD_REFERENCE_H_ |
||||||
|
#define ART_LIBDEXFILE_DEX_METHOD_REFERENCE_H_ |
||||||
|
|
||||||
|
#include <stdint.h> |
||||||
|
#include <string> |
||||||
|
#include "dex/dex_file.h" |
||||||
|
#include "dex/dex_file_reference.h" |
||||||
|
|
||||||
|
namespace art_lkchan { |
||||||
|
|
||||||
|
// A method is uniquely located by its DexFile and the method_ids_ table index into that DexFile
|
||||||
|
class MethodReference : public DexFileReference { |
||||||
|
public: |
||||||
|
MethodReference(const DexFile* file, uint32_t index) : DexFileReference(file, index) {} |
||||||
|
std::string PrettyMethod(bool with_signature = true) const { |
||||||
|
return dex_file->PrettyMethod(index, with_signature); |
||||||
|
} |
||||||
|
const DexFile::MethodId& GetMethodId() const { |
||||||
|
return dex_file->GetMethodId(index); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
// Compare the actual referenced method signatures. Used for method reference deduplication.
|
||||||
|
struct MethodReferenceValueComparator { |
||||||
|
bool operator()(MethodReference mr1, MethodReference mr2) const { |
||||||
|
if (mr1.dex_file == mr2.dex_file) { |
||||||
|
DCHECK_EQ(mr1.index < mr2.index, SlowCompare(mr1, mr2)); |
||||||
|
return mr1.index < mr2.index; |
||||||
|
} else { |
||||||
|
return SlowCompare(mr1, mr2); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
bool SlowCompare(MethodReference mr1, MethodReference mr2) const { |
||||||
|
// The order is the same as for method ids in a single dex file.
|
||||||
|
// Compare the class descriptors first.
|
||||||
|
const DexFile::MethodId& mid1 = mr1.GetMethodId(); |
||||||
|
const DexFile::MethodId& mid2 = mr2.GetMethodId(); |
||||||
|
int descriptor_diff = strcmp(mr1.dex_file->StringByTypeIdx(mid1.class_idx_), |
||||||
|
mr2.dex_file->StringByTypeIdx(mid2.class_idx_)); |
||||||
|
if (descriptor_diff != 0) { |
||||||
|
return descriptor_diff < 0; |
||||||
|
} |
||||||
|
// Compare names second.
|
||||||
|
int name_diff = strcmp(mr1.dex_file->GetMethodName(mid1), mr2.dex_file->GetMethodName(mid2)); |
||||||
|
if (name_diff != 0) { |
||||||
|
return name_diff < 0; |
||||||
|
} |
||||||
|
// And then compare proto ids, starting with return type comparison.
|
||||||
|
const DexFile::ProtoId& prid1 = mr1.dex_file->GetProtoId(mid1.proto_idx_); |
||||||
|
const DexFile::ProtoId& prid2 = mr2.dex_file->GetProtoId(mid2.proto_idx_); |
||||||
|
int return_type_diff = strcmp(mr1.dex_file->StringByTypeIdx(prid1.return_type_idx_), |
||||||
|
mr2.dex_file->StringByTypeIdx(prid2.return_type_idx_)); |
||||||
|
if (return_type_diff != 0) { |
||||||
|
return return_type_diff < 0; |
||||||
|
} |
||||||
|
// And finishing with lexicographical parameter comparison.
|
||||||
|
const DexFile::TypeList* params1 = mr1.dex_file->GetProtoParameters(prid1); |
||||||
|
size_t param1_size = (params1 != nullptr) ? params1->Size() : 0u; |
||||||
|
const DexFile::TypeList* params2 = mr2.dex_file->GetProtoParameters(prid2); |
||||||
|
size_t param2_size = (params2 != nullptr) ? params2->Size() : 0u; |
||||||
|
for (size_t i = 0, num = std::min(param1_size, param2_size); i != num; ++i) { |
||||||
|
int param_diff = strcmp(mr1.dex_file->StringByTypeIdx(params1->GetTypeItem(i).type_idx_), |
||||||
|
mr2.dex_file->StringByTypeIdx(params2->GetTypeItem(i).type_idx_)); |
||||||
|
if (param_diff != 0) { |
||||||
|
return param_diff < 0; |
||||||
|
} |
||||||
|
} |
||||||
|
return param1_size < param2_size; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
} // namespace art_lkchan
|
||||||
|
|
||||||
|
#endif // ART_LIBDEXFILE_DEX_METHOD_REFERENCE_H_
|
@ -0,0 +1,58 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011 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. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <string> |
||||||
|
|
||||||
|
#include "modifiers.h" |
||||||
|
|
||||||
|
namespace art_lkchan { |
||||||
|
|
||||||
|
std::string PrettyJavaAccessFlags(uint32_t access_flags) { |
||||||
|
std::string result; |
||||||
|
if ((access_flags & kAccPublic) != 0) { |
||||||
|
result += "public "; |
||||||
|
} |
||||||
|
if ((access_flags & kAccProtected) != 0) { |
||||||
|
result += "protected "; |
||||||
|
} |
||||||
|
if ((access_flags & kAccPrivate) != 0) { |
||||||
|
result += "private "; |
||||||
|
} |
||||||
|
if ((access_flags & kAccFinal) != 0) { |
||||||
|
result += "final "; |
||||||
|
} |
||||||
|
if ((access_flags & kAccStatic) != 0) { |
||||||
|
result += "static "; |
||||||
|
} |
||||||
|
if ((access_flags & kAccAbstract) != 0) { |
||||||
|
result += "abstract "; |
||||||
|
} |
||||||
|
if ((access_flags & kAccInterface) != 0) { |
||||||
|
result += "interface "; |
||||||
|
} |
||||||
|
if ((access_flags & kAccTransient) != 0) { |
||||||
|
result += "transient "; |
||||||
|
} |
||||||
|
if ((access_flags & kAccVolatile) != 0) { |
||||||
|
result += "volatile "; |
||||||
|
} |
||||||
|
if ((access_flags & kAccSynchronized) != 0) { |
||||||
|
result += "synchronized "; |
||||||
|
} |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace art_lkchan
|
@ -0,0 +1,149 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2012 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_MODIFIERS_H_ |
||||||
|
#define ART_LIBDEXFILE_DEX_MODIFIERS_H_ |
||||||
|
|
||||||
|
#include <stdint.h> |
||||||
|
|
||||||
|
namespace art_lkchan { |
||||||
|
|
||||||
|
static constexpr uint32_t kAccPublic = 0x0001; // class, field, method, ic
|
||||||
|
static constexpr uint32_t kAccPrivate = 0x0002; // field, method, ic
|
||||||
|
static constexpr uint32_t kAccProtected = 0x0004; // field, method, ic
|
||||||
|
static constexpr uint32_t kAccStatic = 0x0008; // field, method, ic
|
||||||
|
static constexpr uint32_t kAccFinal = 0x0010; // class, field, method, ic
|
||||||
|
static constexpr uint32_t kAccSynchronized = 0x0020; // method (only allowed on natives)
|
||||||
|
static constexpr uint32_t kAccSuper = 0x0020; // class (not used in dex)
|
||||||
|
static constexpr uint32_t kAccVolatile = 0x0040; // field
|
||||||
|
static constexpr uint32_t kAccBridge = 0x0040; // method (1.5)
|
||||||
|
static constexpr uint32_t kAccTransient = 0x0080; // field
|
||||||
|
static constexpr uint32_t kAccVarargs = 0x0080; // method (1.5)
|
||||||
|
static constexpr uint32_t kAccNative = 0x0100; // method
|
||||||
|
static constexpr uint32_t kAccInterface = 0x0200; // class, ic
|
||||||
|
static constexpr uint32_t kAccAbstract = 0x0400; // class, method, ic
|
||||||
|
static constexpr uint32_t kAccStrict = 0x0800; // method
|
||||||
|
static constexpr uint32_t kAccSynthetic = 0x1000; // class, field, method, ic
|
||||||
|
static constexpr uint32_t kAccAnnotation = 0x2000; // class, ic (1.5)
|
||||||
|
static constexpr uint32_t kAccEnum = 0x4000; // class, field, ic (1.5)
|
||||||
|
|
||||||
|
static constexpr uint32_t kAccJavaFlagsMask = 0xffff; // bits set from Java sources (low 16)
|
||||||
|
|
||||||
|
// The following flags are used to insert hidden API access flags into boot
|
||||||
|
// class path dex files. They are decoded by DexFile::ClassDataItemIterator and
|
||||||
|
// removed from the access flags before used by the runtime.
|
||||||
|
static constexpr uint32_t kAccDexHiddenBit = 0x00000020; // field, method (not native)
|
||||||
|
static constexpr uint32_t kAccDexHiddenBitNative = 0x00000200; // method (native)
|
||||||
|
|
||||||
|
static constexpr uint32_t kAccConstructor = 0x00010000; // method (dex only) <(cl)init>
|
||||||
|
static constexpr uint32_t kAccDeclaredSynchronized = 0x00020000; // method (dex only)
|
||||||
|
static constexpr uint32_t kAccClassIsProxy = 0x00040000; // class (dex only)
|
||||||
|
// Set to indicate that the ArtMethod is obsolete and has a different DexCache + DexFile from its
|
||||||
|
// declaring class. This flag may only be applied to methods.
|
||||||
|
static constexpr uint32_t kAccObsoleteMethod = 0x00040000; // method (runtime)
|
||||||
|
// Used by a method to denote that its execution does not need to go through slow path interpreter.
|
||||||
|
static constexpr uint32_t kAccSkipAccessChecks = 0x00080000; // method (runtime, not native)
|
||||||
|
// Used by a class to denote that the verifier has attempted to check it at least once.
|
||||||
|
static constexpr uint32_t kAccVerificationAttempted = 0x00080000; // class (runtime)
|
||||||
|
static constexpr uint32_t kAccSkipHiddenApiChecks = 0x00100000; // class (runtime)
|
||||||
|
// This is set by the class linker during LinkInterfaceMethods. It is used by a method to represent
|
||||||
|
// that it was copied from its declaring class into another class. All methods marked kAccMiranda
|
||||||
|
// and kAccDefaultConflict will have this bit set. Any kAccDefault method contained in the methods_
|
||||||
|
// array of a concrete class will also have this bit set.
|
||||||
|
static constexpr uint32_t kAccCopied = 0x00100000; // method (runtime)
|
||||||
|
static constexpr uint32_t kAccMiranda = 0x00200000; // method (runtime, not native)
|
||||||
|
static constexpr uint32_t kAccDefault = 0x00400000; // method (runtime)
|
||||||
|
// Native method flags are set when linking the methods based on the presence of the
|
||||||
|
// @dalvik.annotation.optimization.{Fast,Critical}Native annotations with build visibility.
|
||||||
|
// Reuse the values of kAccSkipAccessChecks and kAccMiranda which are not used for native methods.
|
||||||
|
static constexpr uint32_t kAccFastNative = 0x00080000; // method (runtime; native only)
|
||||||
|
static constexpr uint32_t kAccCriticalNative = 0x00200000; // method (runtime; native only)
|
||||||
|
|
||||||
|
// Set by the JIT when clearing profiling infos to denote that a method was previously warm.
|
||||||
|
static constexpr uint32_t kAccPreviouslyWarm = 0x00800000; // method (runtime)
|
||||||
|
|
||||||
|
// This is set by the class linker during LinkInterfaceMethods. Prior to that point we do not know
|
||||||
|
// if any particular method needs to be a default conflict. Used to figure out at runtime if
|
||||||
|
// invoking this method will throw an exception.
|
||||||
|
static constexpr uint32_t kAccDefaultConflict = 0x01000000; // method (runtime)
|
||||||
|
|
||||||
|
// Set by the verifier for a method we do not want the compiler to compile.
|
||||||
|
static constexpr uint32_t kAccCompileDontBother = 0x02000000; // method (runtime)
|
||||||
|
|
||||||
|
// Set by the verifier for a method that could not be verified to follow structured locking.
|
||||||
|
static constexpr uint32_t kAccMustCountLocks = 0x04000000; // method (runtime)
|
||||||
|
|
||||||
|
// Set by the class linker for a method that has only one implementation for a
|
||||||
|
// virtual call.
|
||||||
|
static constexpr uint32_t kAccSingleImplementation = 0x08000000; // method (runtime)
|
||||||
|
|
||||||
|
static constexpr uint32_t kAccHiddenApiBits = 0x30000000; // field, method
|
||||||
|
|
||||||
|
// Not currently used, except for intrinsic methods where these bits
|
||||||
|
// are part of the intrinsic ordinal.
|
||||||
|
static constexpr uint32_t kAccMayBeUnusedBits = 0x40000000; |
||||||
|
|
||||||
|
// Set by the compiler driver when compiling boot classes with instrinsic methods.
|
||||||
|
static constexpr uint32_t kAccIntrinsic = 0x80000000; // method (runtime)
|
||||||
|
|
||||||
|
// Special runtime-only flags.
|
||||||
|
// Interface and all its super-interfaces with default methods have been recursively initialized.
|
||||||
|
static constexpr uint32_t kAccRecursivelyInitialized = 0x20000000; |
||||||
|
// Interface declares some default method.
|
||||||
|
static constexpr uint32_t kAccHasDefaultMethod = 0x40000000; |
||||||
|
// class/ancestor overrides finalize()
|
||||||
|
static constexpr uint32_t kAccClassIsFinalizable = 0x80000000; |
||||||
|
|
||||||
|
// Continuous sequence of bits used to hold the ordinal of an intrinsic method. Flags
|
||||||
|
// which overlap are not valid when kAccIntrinsic is set.
|
||||||
|
static constexpr uint32_t kAccIntrinsicBits = kAccMayBeUnusedBits | kAccHiddenApiBits | |
||||||
|
kAccSingleImplementation | kAccMustCountLocks | kAccCompileDontBother | kAccDefaultConflict | |
||||||
|
kAccPreviouslyWarm; |
||||||
|
|
||||||
|
// Valid (meaningful) bits for a field.
|
||||||
|
static constexpr uint32_t kAccValidFieldFlags = kAccPublic | kAccPrivate | kAccProtected | |
||||||
|
kAccStatic | kAccFinal | kAccVolatile | kAccTransient | kAccSynthetic | kAccEnum; |
||||||
|
|
||||||
|
// Valid (meaningful) bits for a method.
|
||||||
|
static constexpr uint32_t kAccValidMethodFlags = kAccPublic | kAccPrivate | kAccProtected | |
||||||
|
kAccStatic | kAccFinal | kAccSynchronized | kAccBridge | kAccVarargs | kAccNative | |
||||||
|
kAccAbstract | kAccStrict | kAccSynthetic | kAccConstructor | kAccDeclaredSynchronized; |
||||||
|
static_assert(((kAccIntrinsic | kAccIntrinsicBits) & kAccValidMethodFlags) == 0, |
||||||
|
"Intrinsic bits and valid dex file method access flags must not overlap."); |
||||||
|
|
||||||
|
// Valid (meaningful) bits for a class (not interface).
|
||||||
|
// Note 1. These are positive bits. Other bits may have to be zero.
|
||||||
|
// Note 2. Inner classes can expose more access flags to Java programs. That is handled by libcore.
|
||||||
|
static constexpr uint32_t kAccValidClassFlags = kAccPublic | kAccFinal | kAccSuper | |
||||||
|
kAccAbstract | kAccSynthetic | kAccEnum; |
||||||
|
|
||||||
|
// Valid (meaningful) bits for an interface.
|
||||||
|
// Note 1. Annotations are interfaces.
|
||||||
|
// Note 2. These are positive bits. Other bits may have to be zero.
|
||||||
|
// Note 3. Inner classes can expose more access flags to Java programs. That is handled by libcore.
|
||||||
|
static constexpr uint32_t kAccValidInterfaceFlags = kAccPublic | kAccInterface | |
||||||
|
kAccAbstract | kAccSynthetic | kAccAnnotation; |
||||||
|
|
||||||
|
static constexpr uint32_t kAccVisibilityFlags = kAccPublic | kAccPrivate | kAccProtected; |
||||||
|
|
||||||
|
// Returns a human-readable version of the Java part of the access flags, e.g., "private static "
|
||||||
|
// (note the trailing whitespace).
|
||||||
|
std::string PrettyJavaAccessFlags(uint32_t access_flags); |
||||||
|
|
||||||
|
} // namespace art_lkchan
|
||||||
|
|
||||||
|
#endif // ART_LIBDEXFILE_DEX_MODIFIERS_H_
|
||||||
|
|
@ -0,0 +1,73 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011 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. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include "dex/primitive.h" |
||||||
|
|
||||||
|
namespace art_lkchan { |
||||||
|
|
||||||
|
static const char* kTypeNames[] = { |
||||||
|
"PrimNot", |
||||||
|
"PrimBoolean", |
||||||
|
"PrimByte", |
||||||
|
"PrimChar", |
||||||
|
"PrimShort", |
||||||
|
"PrimInt", |
||||||
|
"PrimLong", |
||||||
|
"PrimFloat", |
||||||
|
"PrimDouble", |
||||||
|
"PrimVoid", |
||||||
|
}; |
||||||
|
|
||||||
|
static const char* kBoxedDescriptors[] = { |
||||||
|
"Ljava/lang/Object;", |
||||||
|
"Ljava/lang/Boolean;", |
||||||
|
"Ljava/lang/Byte;", |
||||||
|
"Ljava/lang/Character;", |
||||||
|
"Ljava/lang/Short;", |
||||||
|
"Ljava/lang/Integer;", |
||||||
|
"Ljava/lang/Long;", |
||||||
|
"Ljava/lang/Float;", |
||||||
|
"Ljava/lang/Double;", |
||||||
|
"Ljava/lang/Void;", |
||||||
|
}; |
||||||
|
|
||||||
|
#define COUNT_OF(x) (sizeof(x) / sizeof((x)[0])) |
||||||
|
|
||||||
|
const char* Primitive::PrettyDescriptor(Primitive::Type type) { |
||||||
|
static_assert(COUNT_OF(kTypeNames) == static_cast<size_t>(Primitive::kPrimLast) + 1, |
||||||
|
"Missing element"); |
||||||
|
CHECK(Primitive::kPrimNot <= type && type <= Primitive::kPrimVoid) << static_cast<int>(type); |
||||||
|
return kTypeNames[type]; |
||||||
|
} |
||||||
|
|
||||||
|
const char* Primitive::BoxedDescriptor(Primitive::Type type) { |
||||||
|
static_assert(COUNT_OF(kBoxedDescriptors) == static_cast<size_t>(Primitive::kPrimLast) + 1, |
||||||
|
"Missing element"); |
||||||
|
CHECK(Primitive::kPrimNot <= type && type <= Primitive::kPrimVoid) << static_cast<int>(type); |
||||||
|
return kBoxedDescriptors[type]; |
||||||
|
} |
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& os, Primitive::Type type) { |
||||||
|
uint32_t int_type = static_cast<uint32_t>(type); |
||||||
|
if (type <= Primitive::kPrimLast) { |
||||||
|
os << kTypeNames[int_type]; |
||||||
|
} else { |
||||||
|
os << "Type[" << int_type << "]"; |
||||||
|
} |
||||||
|
return os; |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace art_lkchan
|
@ -0,0 +1,226 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011 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_PRIMITIVE_H_ |
||||||
|
#define ART_LIBDEXFILE_DEX_PRIMITIVE_H_ |
||||||
|
|
||||||
|
#include <sys/types.h> |
||||||
|
|
||||||
|
#include <android-base/logging.h> |
||||||
|
|
||||||
|
#include "base/macros.h" |
||||||
|
|
||||||
|
namespace art_lkchan { |
||||||
|
|
||||||
|
static constexpr size_t kObjectReferenceSize = 4; |
||||||
|
|
||||||
|
constexpr size_t ComponentSizeShiftWidth(size_t component_size) { |
||||||
|
return component_size == 1u ? 0u : |
||||||
|
component_size == 2u ? 1u : |
||||||
|
component_size == 4u ? 2u : |
||||||
|
component_size == 8u ? 3u : 0u; |
||||||
|
} |
||||||
|
|
||||||
|
class Primitive { |
||||||
|
public: |
||||||
|
enum Type { |
||||||
|
kPrimNot = 0, |
||||||
|
kPrimBoolean, |
||||||
|
kPrimByte, |
||||||
|
kPrimChar, |
||||||
|
kPrimShort, |
||||||
|
kPrimInt, |
||||||
|
kPrimLong, |
||||||
|
kPrimFloat, |
||||||
|
kPrimDouble, |
||||||
|
kPrimVoid, |
||||||
|
kPrimLast = kPrimVoid |
||||||
|
}; |
||||||
|
|
||||||
|
static constexpr Type GetType(char type) { |
||||||
|
switch (type) { |
||||||
|
case 'B': |
||||||
|
return kPrimByte; |
||||||
|
case 'C': |
||||||
|
return kPrimChar; |
||||||
|
case 'D': |
||||||
|
return kPrimDouble; |
||||||
|
case 'F': |
||||||
|
return kPrimFloat; |
||||||
|
case 'I': |
||||||
|
return kPrimInt; |
||||||
|
case 'J': |
||||||
|
return kPrimLong; |
||||||
|
case 'S': |
||||||
|
return kPrimShort; |
||||||
|
case 'Z': |
||||||
|
return kPrimBoolean; |
||||||
|
case 'V': |
||||||
|
return kPrimVoid; |
||||||
|
default: |
||||||
|
return kPrimNot; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
static constexpr size_t ComponentSizeShift(Type type) { |
||||||
|
switch (type) { |
||||||
|
case kPrimVoid: |
||||||
|
case kPrimBoolean: |
||||||
|
case kPrimByte: return 0; |
||||||
|
case kPrimChar: |
||||||
|
case kPrimShort: return 1; |
||||||
|
case kPrimInt: |
||||||
|
case kPrimFloat: return 2; |
||||||
|
case kPrimLong: |
||||||
|
case kPrimDouble: return 3; |
||||||
|
case kPrimNot: return ComponentSizeShiftWidth(kObjectReferenceSize); |
||||||
|
} |
||||||
|
LOG(FATAL) << "Invalid type " << static_cast<int>(type); |
||||||
|
UNREACHABLE(); |
||||||
|
} |
||||||
|
|
||||||
|
static constexpr size_t ComponentSize(Type type) { |
||||||
|
switch (type) { |
||||||
|
case kPrimVoid: return 0; |
||||||
|
case kPrimBoolean: |
||||||
|
case kPrimByte: return 1; |
||||||
|
case kPrimChar: |
||||||
|
case kPrimShort: return 2; |
||||||
|
case kPrimInt: |
||||||
|
case kPrimFloat: return 4; |
||||||
|
case kPrimLong: |
||||||
|
case kPrimDouble: return 8; |
||||||
|
case kPrimNot: return kObjectReferenceSize; |
||||||
|
} |
||||||
|
LOG(FATAL) << "Invalid type " << static_cast<int>(type); |
||||||
|
UNREACHABLE(); |
||||||
|
} |
||||||
|
|
||||||
|
static const char* Descriptor(Type type) { |
||||||
|
switch (type) { |
||||||
|
case kPrimBoolean: |
||||||
|
return "Z"; |
||||||
|
case kPrimByte: |
||||||
|
return "B"; |
||||||
|
case kPrimChar: |
||||||
|
return "C"; |
||||||
|
case kPrimShort: |
||||||
|
return "S"; |
||||||
|
case kPrimInt: |
||||||
|
return "I"; |
||||||
|
case kPrimFloat: |
||||||
|
return "F"; |
||||||
|
case kPrimLong: |
||||||
|
return "J"; |
||||||
|
case kPrimDouble: |
||||||
|
return "D"; |
||||||
|
case kPrimVoid: |
||||||
|
return "V"; |
||||||
|
default: |
||||||
|
LOG(FATAL) << "Primitive char conversion on invalid type " << static_cast<int>(type); |
||||||
|
return nullptr; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
static const char* PrettyDescriptor(Type type); |
||||||
|
|
||||||
|
// Returns the descriptor corresponding to the boxed type of |type|.
|
||||||
|
static const char* BoxedDescriptor(Type type); |
||||||
|
|
||||||
|
// Returns true if |type| is an numeric type.
|
||||||
|
static constexpr bool IsNumericType(Type type) { |
||||||
|
switch (type) { |
||||||
|
case Primitive::Type::kPrimNot: return false; |
||||||
|
case Primitive::Type::kPrimBoolean: return false; |
||||||
|
case Primitive::Type::kPrimByte: return true; |
||||||
|
case Primitive::Type::kPrimChar: return true; |
||||||
|
case Primitive::Type::kPrimShort: return true; |
||||||
|
case Primitive::Type::kPrimInt: return true; |
||||||
|
case Primitive::Type::kPrimLong: return true; |
||||||
|
case Primitive::Type::kPrimFloat: return true; |
||||||
|
case Primitive::Type::kPrimDouble: return true; |
||||||
|
case Primitive::Type::kPrimVoid: return false; |
||||||
|
} |
||||||
|
LOG(FATAL) << "Invalid type " << static_cast<int>(type); |
||||||
|
UNREACHABLE(); |
||||||
|
} |
||||||
|
|
||||||
|
// Return trues if |type| is a signed numeric type.
|
||||||
|
static constexpr bool IsSignedNumericType(Type type) { |
||||||
|
switch (type) { |
||||||
|
case Primitive::Type::kPrimNot: return false; |
||||||
|
case Primitive::Type::kPrimBoolean: return false; |
||||||
|
case Primitive::Type::kPrimByte: return true; |
||||||
|
case Primitive::Type::kPrimChar: return false; |
||||||
|
case Primitive::Type::kPrimShort: return true; |
||||||
|
case Primitive::Type::kPrimInt: return true; |
||||||
|
case Primitive::Type::kPrimLong: return true; |
||||||
|
case Primitive::Type::kPrimFloat: return true; |
||||||
|
case Primitive::Type::kPrimDouble: return true; |
||||||
|
case Primitive::Type::kPrimVoid: return false; |
||||||
|
} |
||||||
|
LOG(FATAL) << "Invalid type " << static_cast<int>(type); |
||||||
|
UNREACHABLE(); |
||||||
|
} |
||||||
|
|
||||||
|
// Returns the number of bits required to hold the largest
|
||||||
|
// positive number that can be represented by |type|.
|
||||||
|
static constexpr size_t BitsRequiredForLargestValue(Type type) { |
||||||
|
switch (type) { |
||||||
|
case Primitive::Type::kPrimNot: return 0u; |
||||||
|
case Primitive::Type::kPrimBoolean: return 1u; |
||||||
|
case Primitive::Type::kPrimByte: return 7u; |
||||||
|
case Primitive::Type::kPrimChar: return 16u; |
||||||
|
case Primitive::Type::kPrimShort: return 15u; |
||||||
|
case Primitive::Type::kPrimInt: return 31u; |
||||||
|
case Primitive::Type::kPrimLong: return 63u; |
||||||
|
case Primitive::Type::kPrimFloat: return 128u; |
||||||
|
case Primitive::Type::kPrimDouble: return 1024u; |
||||||
|
case Primitive::Type::kPrimVoid: return 0u; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Returns true if it is possible to widen type |from| to type |to|. Both |from| and
|
||||||
|
// |to| should be numeric primitive types.
|
||||||
|
static bool IsWidenable(Type from, Type to) { |
||||||
|
if (!IsNumericType(from) || !IsNumericType(to)) { |
||||||
|
// Widening is only applicable between numeric types.
|
||||||
|
return false; |
||||||
|
} |
||||||
|
if (IsSignedNumericType(from) && !IsSignedNumericType(to)) { |
||||||
|
// Nowhere to store the sign bit in |to|.
|
||||||
|
return false; |
||||||
|
} |
||||||
|
if (BitsRequiredForLargestValue(from) > BitsRequiredForLargestValue(to)) { |
||||||
|
// The from,to pair corresponds to a narrowing.
|
||||||
|
return false; |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
static bool Is64BitType(Type type) { |
||||||
|
return type == kPrimLong || type == kPrimDouble; |
||||||
|
} |
||||||
|
|
||||||
|
private: |
||||||
|
DISALLOW_IMPLICIT_CONSTRUCTORS(Primitive); |
||||||
|
}; |
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& os, Primitive::Type state); |
||||||
|
|
||||||
|
} // namespace art_lkchan
|
||||||
|
|
||||||
|
#endif // ART_LIBDEXFILE_DEX_PRIMITIVE_H_
|
@ -0,0 +1,81 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2017 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. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include "standard_dex_file.h" |
||||||
|
|
||||||
|
#include "base/casts.h" |
||||||
|
#include "base/leb128.h" |
||||||
|
#include "code_item_accessors-inl.h" |
||||||
|
#include "dex_file-inl.h" |
||||||
|
|
||||||
|
namespace art_lkchan { |
||||||
|
|
||||||
|
const uint8_t StandardDexFile::kDexMagic[] = { 'd', 'e', 'x', '\n' }; |
||||||
|
const uint8_t StandardDexFile::kDexMagicVersions[StandardDexFile::kNumDexVersions] |
||||||
|
[StandardDexFile::kDexVersionLen] = { |
||||||
|
{'0', '3', '5', '\0'}, |
||||||
|
// Dex version 036 skipped because of an old dalvik bug on some versions of android where dex
|
||||||
|
// files with that version number would erroneously be accepted and run.
|
||||||
|
{'0', '3', '7', '\0'}, |
||||||
|
// Dex version 038: Android "O" and beyond.
|
||||||
|
{'0', '3', '8', '\0'}, |
||||||
|
// Dex verion 039: Beyond Android "O".
|
||||||
|
{'0', '3', '9', '\0'}, |
||||||
|
}; |
||||||
|
|
||||||
|
void StandardDexFile::WriteMagic(uint8_t* magic) { |
||||||
|
std::copy_n(kDexMagic, kDexMagicSize, magic); |
||||||
|
} |
||||||
|
|
||||||
|
void StandardDexFile::WriteCurrentVersion(uint8_t* magic) { |
||||||
|
std::copy_n(kDexMagicVersions[StandardDexFile::kDexVersionLen - 1], |
||||||
|
kDexVersionLen, |
||||||
|
magic + kDexMagicSize); |
||||||
|
} |
||||||
|
|
||||||
|
bool StandardDexFile::IsMagicValid(const uint8_t* magic) { |
||||||
|
return (memcmp(magic, kDexMagic, sizeof(kDexMagic)) == 0); |
||||||
|
} |
||||||
|
|
||||||
|
bool StandardDexFile::IsVersionValid(const uint8_t* magic) { |
||||||
|
const uint8_t* version = &magic[sizeof(kDexMagic)]; |
||||||
|
for (uint32_t i = 0; i < kNumDexVersions; i++) { |
||||||
|
if (memcmp(version, kDexMagicVersions[i], kDexVersionLen) == 0) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
bool StandardDexFile::IsMagicValid() const { |
||||||
|
return IsMagicValid(header_->magic_); |
||||||
|
} |
||||||
|
|
||||||
|
bool StandardDexFile::IsVersionValid() const { |
||||||
|
return IsVersionValid(header_->magic_); |
||||||
|
} |
||||||
|
|
||||||
|
bool StandardDexFile::SupportsDefaultMethods() const { |
||||||
|
return GetDexVersion() >= DexFile::kDefaultMethodsVersion; |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t StandardDexFile::GetCodeItemSize(const DexFile::CodeItem& item) const { |
||||||
|
DCHECK(IsInDataSection(&item)); |
||||||
|
return reinterpret_cast<uintptr_t>(CodeItemDataAccessor(*this, &item).CodeItemDataEnd()) - |
||||||
|
reinterpret_cast<uintptr_t>(&item); |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace art_lkchan
|
@ -0,0 +1,118 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2017 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_STANDARD_DEX_FILE_H_ |
||||||
|
#define ART_LIBDEXFILE_DEX_STANDARD_DEX_FILE_H_ |
||||||
|
|
||||||
|
#include <iosfwd> |
||||||
|
|
||||||
|
#include "dex_file.h" |
||||||
|
|
||||||
|
namespace art_lkchan { |
||||||
|
|
||||||
|
class OatDexFile; |
||||||
|
|
||||||
|
// Standard dex file. This is the format that is packaged in APKs and produced by tools.
|
||||||
|
class StandardDexFile : public DexFile { |
||||||
|
public: |
||||||
|
class Header : public DexFile::Header { |
||||||
|
// Same for now.
|
||||||
|
}; |
||||||
|
|
||||||
|
struct CodeItem : public DexFile::CodeItem { |
||||||
|
static constexpr size_t kAlignment = 4; |
||||||
|
|
||||||
|
private: |
||||||
|
CodeItem() = default; |
||||||
|
|
||||||
|
uint16_t registers_size_; // the number of registers used by this code
|
||||||
|
// (locals + parameters)
|
||||||
|
uint16_t ins_size_; // the number of words of incoming arguments to the method
|
||||||
|
// that this code is for
|
||||||
|
uint16_t outs_size_; // the number of words of outgoing argument space required
|
||||||
|
// by this code for method invocation
|
||||||
|
uint16_t tries_size_; // the number of try_items for this instance. If non-zero,
|
||||||
|
// then these appear as the tries array just after the
|
||||||
|
// insns in this instance.
|
||||||
|
uint32_t debug_info_off_; // Holds file offset to debug info stream.
|
||||||
|
|
||||||
|
uint32_t insns_size_in_code_units_; // size of the insns array, in 2 byte code units
|
||||||
|
uint16_t insns_[1]; // actual array of bytecode.
|
||||||
|
|
||||||
|
ART_FRIEND_TEST(CodeItemAccessorsTest, TestDexInstructionsAccessor); |
||||||
|
friend class CodeItemDataAccessor; |
||||||
|
friend class CodeItemDebugInfoAccessor; |
||||||
|
friend class CodeItemInstructionAccessor; |
||||||
|
friend class DexWriter; |
||||||
|
friend class StandardDexFile; |
||||||
|
DISALLOW_COPY_AND_ASSIGN(CodeItem); |
||||||
|
}; |
||||||
|
|
||||||
|
// Write the standard dex specific magic.
|
||||||
|
static void WriteMagic(uint8_t* magic); |
||||||
|
|
||||||
|
// Write the current version, note that the input is the address of the magic.
|
||||||
|
static void WriteCurrentVersion(uint8_t* magic); |
||||||
|
|
||||||
|
static const uint8_t kDexMagic[kDexMagicSize]; |
||||||
|
static constexpr size_t kNumDexVersions = 4; |
||||||
|
static const uint8_t kDexMagicVersions[kNumDexVersions][kDexVersionLen]; |
||||||
|
|
||||||
|
// Returns true if the byte string points to the magic value.
|
||||||
|
static bool IsMagicValid(const uint8_t* magic); |
||||||
|
virtual bool IsMagicValid() const OVERRIDE; |
||||||
|
|
||||||
|
// Returns true if the byte string after the magic is the correct value.
|
||||||
|
static bool IsVersionValid(const uint8_t* magic); |
||||||
|
virtual bool IsVersionValid() const OVERRIDE; |
||||||
|
|
||||||
|
virtual bool SupportsDefaultMethods() const OVERRIDE; |
||||||
|
|
||||||
|
uint32_t GetCodeItemSize(const DexFile::CodeItem& item) const OVERRIDE; |
||||||
|
|
||||||
|
virtual size_t GetDequickenedSize() const OVERRIDE { |
||||||
|
return Size(); |
||||||
|
} |
||||||
|
|
||||||
|
private: |
||||||
|
StandardDexFile(const uint8_t* base, |
||||||
|
size_t size, |
||||||
|
const std::string& location, |
||||||
|
uint32_t location_checksum, |
||||||
|
const OatDexFile* oat_dex_file, |
||||||
|
std::unique_ptr<DexFileContainer> container) |
||||||
|
: DexFile(base, |
||||||
|
size, |
||||||
|
/*data_begin*/ base, |
||||||
|
/*data_size*/ size, |
||||||
|
location, |
||||||
|
location_checksum, |
||||||
|
oat_dex_file, |
||||||
|
std::move(container), |
||||||
|
/*is_compact_dex*/ false) {} |
||||||
|
|
||||||
|
friend class DexFileLoader; |
||||||
|
friend class DexFileVerifierTest; |
||||||
|
|
||||||
|
ART_FRIEND_TEST(ClassLinkerTest, RegisterDexFileName); // for constructor
|
||||||
|
friend class OptimizingUnitTestHelper; // for constructor
|
||||||
|
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(StandardDexFile); |
||||||
|
}; |
||||||
|
|
||||||
|
} // namespace art_lkchan
|
||||||
|
|
||||||
|
#endif // ART_LIBDEXFILE_DEX_STANDARD_DEX_FILE_H_
|
@ -0,0 +1,71 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 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_STRING_REFERENCE_H_ |
||||||
|
#define ART_LIBDEXFILE_DEX_STRING_REFERENCE_H_ |
||||||
|
|
||||||
|
#include <stdint.h> |
||||||
|
|
||||||
|
#include <android-base/logging.h> |
||||||
|
|
||||||
|
#include "dex/dex_file-inl.h" |
||||||
|
#include "dex/dex_file_reference.h" |
||||||
|
#include "dex/dex_file_types.h" |
||||||
|
#include "dex/utf-inl.h" |
||||||
|
|
||||||
|
namespace art_lkchan { |
||||||
|
|
||||||
|
// A string is located by its DexFile and the string_ids_ table index into that DexFile.
|
||||||
|
class StringReference : public DexFileReference { |
||||||
|
public: |
||||||
|
StringReference(const DexFile* file, dex::StringIndex index) |
||||||
|
: DexFileReference(file, index.index_) {} |
||||||
|
|
||||||
|
dex::StringIndex StringIndex() const { |
||||||
|
return dex::StringIndex(index); |
||||||
|
} |
||||||
|
|
||||||
|
const char* GetStringData() const { |
||||||
|
return dex_file->GetStringData(dex_file->GetStringId(StringIndex())); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
// Compare the actual referenced string values. Used for string reference deduplication.
|
||||||
|
struct StringReferenceValueComparator { |
||||||
|
bool operator()(const StringReference& sr1, const StringReference& sr2) const { |
||||||
|
// Note that we want to deduplicate identical strings even if they are referenced
|
||||||
|
// by different dex files, so we need some (any) total ordering of strings, rather
|
||||||
|
// than references. However, the references should usually be from the same dex file,
|
||||||
|
// so we choose the dex file string ordering so that we can simply compare indexes
|
||||||
|
// and avoid the costly string comparison in the most common case.
|
||||||
|
if (sr1.dex_file == sr2.dex_file) { |
||||||
|
// Use the string order enforced by the dex file verifier.
|
||||||
|
DCHECK_EQ( |
||||||
|
sr1.index < sr2.index, |
||||||
|
CompareModifiedUtf8ToModifiedUtf8AsUtf16CodePointValues(sr1.GetStringData(), |
||||||
|
sr2.GetStringData()) < 0); |
||||||
|
return sr1.index < sr2.index; |
||||||
|
} else { |
||||||
|
// Cannot compare indexes, so do the string comparison.
|
||||||
|
return CompareModifiedUtf8ToModifiedUtf8AsUtf16CodePointValues(sr1.GetStringData(), |
||||||
|
sr2.GetStringData()) < 0; |
||||||
|
} |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
} // namespace art_lkchan
|
||||||
|
|
||||||
|
#endif // ART_LIBDEXFILE_DEX_STRING_REFERENCE_H_
|
@ -0,0 +1,400 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2015 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_TEST_DEX_FILE_BUILDER_H_ |
||||||
|
#define ART_LIBDEXFILE_DEX_TEST_DEX_FILE_BUILDER_H_ |
||||||
|
|
||||||
|
#include <zlib.h> |
||||||
|
|
||||||
|
#include <cstring> |
||||||
|
#include <map> |
||||||
|
#include <set> |
||||||
|
#include <vector> |
||||||
|
|
||||||
|
#include <android-base/logging.h> |
||||||
|
|
||||||
|
#include "dex/dex_file_loader.h" |
||||||
|
#include "dex/standard_dex_file.h" |
||||||
|
|
||||||
|
namespace art_lkchan { |
||||||
|
|
||||||
|
class TestDexFileBuilder { |
||||||
|
public: |
||||||
|
TestDexFileBuilder() |
||||||
|
: strings_(), types_(), fields_(), protos_(), dex_file_data_() { |
||||||
|
} |
||||||
|
|
||||||
|
void AddString(const std::string& str) { |
||||||
|
CHECK(dex_file_data_.empty()); |
||||||
|
auto it = strings_.emplace(str, IdxAndDataOffset()).first; |
||||||
|
CHECK_LT(it->first.length(), 128u); // Don't allow multi-byte length in uleb128.
|
||||||
|
} |
||||||
|
|
||||||
|
void AddType(const std::string& descriptor) { |
||||||
|
CHECK(dex_file_data_.empty()); |
||||||
|
AddString(descriptor); |
||||||
|
types_.emplace(descriptor, 0u); |
||||||
|
} |
||||||
|
|
||||||
|
void AddField(const std::string& class_descriptor, const std::string& type, |
||||||
|
const std::string& name) { |
||||||
|
CHECK(dex_file_data_.empty()); |
||||||
|
AddType(class_descriptor); |
||||||
|
AddType(type); |
||||||
|
AddString(name); |
||||||
|
FieldKey key = { class_descriptor, type, name }; |
||||||
|
fields_.emplace(key, 0u); |
||||||
|
} |
||||||
|
|
||||||
|
void AddMethod(const std::string& class_descriptor, const std::string& signature, |
||||||
|
const std::string& name) { |
||||||
|
CHECK(dex_file_data_.empty()); |
||||||
|
AddType(class_descriptor); |
||||||
|
AddString(name); |
||||||
|
|
||||||
|
ProtoKey proto_key = CreateProtoKey(signature); |
||||||
|
AddString(proto_key.shorty); |
||||||
|
AddType(proto_key.return_type); |
||||||
|
for (const auto& arg_type : proto_key.args) { |
||||||
|
AddType(arg_type); |
||||||
|
} |
||||||
|
auto it = protos_.emplace(proto_key, IdxAndDataOffset()).first; |
||||||
|
const ProtoKey* proto = &it->first; // Valid as long as the element remains in protos_.
|
||||||
|
|
||||||
|
MethodKey method_key = { |
||||||
|
class_descriptor, name, proto |
||||||
|
}; |
||||||
|
methods_.emplace(method_key, 0u); |
||||||
|
} |
||||||
|
|
||||||
|
// NOTE: The builder holds the actual data, so it must live as long as the dex file.
|
||||||
|
std::unique_ptr<const DexFile> Build(const std::string& dex_location) { |
||||||
|
CHECK(dex_file_data_.empty()); |
||||||
|
union { |
||||||
|
uint8_t data[sizeof(DexFile::Header)]; |
||||||
|
uint64_t force_alignment; |
||||||
|
} header_data; |
||||||
|
std::memset(header_data.data, 0, sizeof(header_data.data)); |
||||||
|
DexFile::Header* header = reinterpret_cast<DexFile::Header*>(&header_data.data); |
||||||
|
std::copy_n(StandardDexFile::kDexMagic, 4u, header->magic_); |
||||||
|
std::copy_n(StandardDexFile::kDexMagicVersions[0], 4u, header->magic_ + 4u); |
||||||
|
header->header_size_ = sizeof(DexFile::Header); |
||||||
|
header->endian_tag_ = DexFile::kDexEndianConstant; |
||||||
|
header->link_size_ = 0u; // Unused.
|
||||||
|
header->link_off_ = 0u; // Unused.
|
||||||
|
header->map_off_ = 0u; // Unused. TODO: This is wrong. Dex files created by this builder
|
||||||
|
// cannot be verified. b/26808512
|
||||||
|
|
||||||
|
uint32_t data_section_size = 0u; |
||||||
|
|
||||||
|
uint32_t string_ids_offset = sizeof(DexFile::Header); |
||||||
|
uint32_t string_idx = 0u; |
||||||
|
for (auto& entry : strings_) { |
||||||
|
entry.second.idx = string_idx; |
||||||
|
string_idx += 1u; |
||||||
|
entry.second.data_offset = data_section_size; |
||||||
|
data_section_size += entry.first.length() + 1u /* length */ + 1u /* null-terminator */; |
||||||
|
} |
||||||
|
header->string_ids_size_ = strings_.size(); |
||||||
|
header->string_ids_off_ = strings_.empty() ? 0u : string_ids_offset; |
||||||
|
|
||||||
|
uint32_t type_ids_offset = string_ids_offset + strings_.size() * sizeof(DexFile::StringId); |
||||||
|
uint32_t type_idx = 0u; |
||||||
|
for (auto& entry : types_) { |
||||||
|
entry.second = type_idx; |
||||||
|
type_idx += 1u; |
||||||
|
} |
||||||
|
header->type_ids_size_ = types_.size(); |
||||||
|
header->type_ids_off_ = types_.empty() ? 0u : type_ids_offset; |
||||||
|
|
||||||
|
uint32_t proto_ids_offset = type_ids_offset + types_.size() * sizeof(DexFile::TypeId); |
||||||
|
uint32_t proto_idx = 0u; |
||||||
|
for (auto& entry : protos_) { |
||||||
|
entry.second.idx = proto_idx; |
||||||
|
proto_idx += 1u; |
||||||
|
size_t num_args = entry.first.args.size(); |
||||||
|
if (num_args != 0u) { |
||||||
|
entry.second.data_offset = RoundUp(data_section_size, 4u); |
||||||
|
data_section_size = entry.second.data_offset + 4u + num_args * sizeof(DexFile::TypeItem); |
||||||
|
} else { |
||||||
|
entry.second.data_offset = 0u; |
||||||
|
} |
||||||
|
} |
||||||
|
header->proto_ids_size_ = protos_.size(); |
||||||
|
header->proto_ids_off_ = protos_.empty() ? 0u : proto_ids_offset; |
||||||
|
|
||||||
|
uint32_t field_ids_offset = proto_ids_offset + protos_.size() * sizeof(DexFile::ProtoId); |
||||||
|
uint32_t field_idx = 0u; |
||||||
|
for (auto& entry : fields_) { |
||||||
|
entry.second = field_idx; |
||||||
|
field_idx += 1u; |
||||||
|
} |
||||||
|
header->field_ids_size_ = fields_.size(); |
||||||
|
header->field_ids_off_ = fields_.empty() ? 0u : field_ids_offset; |
||||||
|
|
||||||
|
uint32_t method_ids_offset = field_ids_offset + fields_.size() * sizeof(DexFile::FieldId); |
||||||
|
uint32_t method_idx = 0u; |
||||||
|
for (auto& entry : methods_) { |
||||||
|
entry.second = method_idx; |
||||||
|
method_idx += 1u; |
||||||
|
} |
||||||
|
header->method_ids_size_ = methods_.size(); |
||||||
|
header->method_ids_off_ = methods_.empty() ? 0u : method_ids_offset; |
||||||
|
|
||||||
|
// No class defs.
|
||||||
|
header->class_defs_size_ = 0u; |
||||||
|
header->class_defs_off_ = 0u; |
||||||
|
|
||||||
|
uint32_t data_section_offset = method_ids_offset + methods_.size() * sizeof(DexFile::MethodId); |
||||||
|
header->data_size_ = data_section_size; |
||||||
|
header->data_off_ = (data_section_size != 0u) ? data_section_offset : 0u; |
||||||
|
|
||||||
|
uint32_t total_size = data_section_offset + data_section_size; |
||||||
|
|
||||||
|
dex_file_data_.resize(total_size); |
||||||
|
|
||||||
|
for (const auto& entry : strings_) { |
||||||
|
CHECK_LT(entry.first.size(), 128u); |
||||||
|
uint32_t raw_offset = data_section_offset + entry.second.data_offset; |
||||||
|
dex_file_data_[raw_offset] = static_cast<uint8_t>(entry.first.size()); |
||||||
|
std::memcpy(&dex_file_data_[raw_offset + 1], entry.first.c_str(), entry.first.size() + 1); |
||||||
|
Write32(string_ids_offset + entry.second.idx * sizeof(DexFile::StringId), raw_offset); |
||||||
|
} |
||||||
|
|
||||||
|
for (const auto& entry : types_) { |
||||||
|
Write32(type_ids_offset + entry.second * sizeof(DexFile::TypeId), GetStringIdx(entry.first)); |
||||||
|
++type_idx; |
||||||
|
} |
||||||
|
|
||||||
|
for (const auto& entry : protos_) { |
||||||
|
size_t num_args = entry.first.args.size(); |
||||||
|
uint32_t type_list_offset = |
||||||
|
(num_args != 0u) ? data_section_offset + entry.second.data_offset : 0u; |
||||||
|
uint32_t raw_offset = proto_ids_offset + entry.second.idx * sizeof(DexFile::ProtoId); |
||||||
|
Write32(raw_offset + 0u, GetStringIdx(entry.first.shorty)); |
||||||
|
Write16(raw_offset + 4u, GetTypeIdx(entry.first.return_type)); |
||||||
|
Write32(raw_offset + 8u, type_list_offset); |
||||||
|
if (num_args != 0u) { |
||||||
|
CHECK_NE(entry.second.data_offset, 0u); |
||||||
|
Write32(type_list_offset, num_args); |
||||||
|
for (size_t i = 0; i != num_args; ++i) { |
||||||
|
Write16(type_list_offset + 4u + i * sizeof(DexFile::TypeItem), |
||||||
|
GetTypeIdx(entry.first.args[i])); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
for (const auto& entry : fields_) { |
||||||
|
uint32_t raw_offset = field_ids_offset + entry.second * sizeof(DexFile::FieldId); |
||||||
|
Write16(raw_offset + 0u, GetTypeIdx(entry.first.class_descriptor)); |
||||||
|
Write16(raw_offset + 2u, GetTypeIdx(entry.first.type)); |
||||||
|
Write32(raw_offset + 4u, GetStringIdx(entry.first.name)); |
||||||
|
} |
||||||
|
|
||||||
|
for (const auto& entry : methods_) { |
||||||
|
uint32_t raw_offset = method_ids_offset + entry.second * sizeof(DexFile::MethodId); |
||||||
|
Write16(raw_offset + 0u, GetTypeIdx(entry.first.class_descriptor)); |
||||||
|
auto it = protos_.find(*entry.first.proto); |
||||||
|
CHECK(it != protos_.end()); |
||||||
|
Write16(raw_offset + 2u, it->second.idx); |
||||||
|
Write32(raw_offset + 4u, GetStringIdx(entry.first.name)); |
||||||
|
} |
||||||
|
|
||||||
|
// Leave signature as zeros.
|
||||||
|
|
||||||
|
header->file_size_ = dex_file_data_.size(); |
||||||
|
|
||||||
|
// Write the complete header early, as part of it needs to be checksummed.
|
||||||
|
std::memcpy(&dex_file_data_[0], header_data.data, sizeof(DexFile::Header)); |
||||||
|
|
||||||
|
// Checksum starts after the checksum field.
|
||||||
|
size_t skip = sizeof(header->magic_) + sizeof(header->checksum_); |
||||||
|
header->checksum_ = adler32(adler32(0L, Z_NULL, 0), |
||||||
|
dex_file_data_.data() + skip, |
||||||
|
dex_file_data_.size() - skip); |
||||||
|
|
||||||
|
// Write the complete header again, just simpler that way.
|
||||||
|
std::memcpy(&dex_file_data_[0], header_data.data, sizeof(DexFile::Header)); |
||||||
|
|
||||||
|
static constexpr bool kVerify = false; |
||||||
|
static constexpr bool kVerifyChecksum = false; |
||||||
|
std::string error_msg; |
||||||
|
const DexFileLoader dex_file_loader; |
||||||
|
std::unique_ptr<const DexFile> dex_file(dex_file_loader.Open( |
||||||
|
&dex_file_data_[0], |
||||||
|
dex_file_data_.size(), |
||||||
|
dex_location, |
||||||
|
0u, |
||||||
|
nullptr, |
||||||
|
kVerify, |
||||||
|
kVerifyChecksum, |
||||||
|
&error_msg)); |
||||||
|
CHECK(dex_file != nullptr) << error_msg; |
||||||
|
return dex_file; |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t GetStringIdx(const std::string& type) { |
||||||
|
auto it = strings_.find(type); |
||||||
|
CHECK(it != strings_.end()); |
||||||
|
return it->second.idx; |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t GetTypeIdx(const std::string& type) { |
||||||
|
auto it = types_.find(type); |
||||||
|
CHECK(it != types_.end()); |
||||||
|
return it->second; |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t GetFieldIdx(const std::string& class_descriptor, const std::string& type, |
||||||
|
const std::string& name) { |
||||||
|
FieldKey key = { class_descriptor, type, name }; |
||||||
|
auto it = fields_.find(key); |
||||||
|
CHECK(it != fields_.end()); |
||||||
|
return it->second; |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t GetMethodIdx(const std::string& class_descriptor, const std::string& signature, |
||||||
|
const std::string& name) { |
||||||
|
ProtoKey proto_key = CreateProtoKey(signature); |
||||||
|
MethodKey method_key = { class_descriptor, name, &proto_key }; |
||||||
|
auto it = methods_.find(method_key); |
||||||
|
CHECK(it != methods_.end()); |
||||||
|
return it->second; |
||||||
|
} |
||||||
|
|
||||||
|
private: |
||||||
|
struct IdxAndDataOffset { |
||||||
|
uint32_t idx; |
||||||
|
uint32_t data_offset; |
||||||
|
}; |
||||||
|
|
||||||
|
struct FieldKey { |
||||||
|
const std::string class_descriptor; |
||||||
|
const std::string type; |
||||||
|
const std::string name; |
||||||
|
}; |
||||||
|
struct FieldKeyComparator { |
||||||
|
bool operator()(const FieldKey& lhs, const FieldKey& rhs) const { |
||||||
|
if (lhs.class_descriptor != rhs.class_descriptor) { |
||||||
|
return lhs.class_descriptor < rhs.class_descriptor; |
||||||
|
} |
||||||
|
if (lhs.name != rhs.name) { |
||||||
|
return lhs.name < rhs.name; |
||||||
|
} |
||||||
|
return lhs.type < rhs.type; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
struct ProtoKey { |
||||||
|
std::string shorty; |
||||||
|
std::string return_type; |
||||||
|
std::vector<std::string> args; |
||||||
|
}; |
||||||
|
struct ProtoKeyComparator { |
||||||
|
bool operator()(const ProtoKey& lhs, const ProtoKey& rhs) const { |
||||||
|
if (lhs.return_type != rhs.return_type) { |
||||||
|
return lhs.return_type < rhs.return_type; |
||||||
|
} |
||||||
|
size_t min_args = std::min(lhs.args.size(), rhs.args.size()); |
||||||
|
for (size_t i = 0; i != min_args; ++i) { |
||||||
|
if (lhs.args[i] != rhs.args[i]) { |
||||||
|
return lhs.args[i] < rhs.args[i]; |
||||||
|
} |
||||||
|
} |
||||||
|
return lhs.args.size() < rhs.args.size(); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
struct MethodKey { |
||||||
|
std::string class_descriptor; |
||||||
|
std::string name; |
||||||
|
const ProtoKey* proto; |
||||||
|
}; |
||||||
|
struct MethodKeyComparator { |
||||||
|
bool operator()(const MethodKey& lhs, const MethodKey& rhs) const { |
||||||
|
if (lhs.class_descriptor != rhs.class_descriptor) { |
||||||
|
return lhs.class_descriptor < rhs.class_descriptor; |
||||||
|
} |
||||||
|
if (lhs.name != rhs.name) { |
||||||
|
return lhs.name < rhs.name; |
||||||
|
} |
||||||
|
return ProtoKeyComparator()(*lhs.proto, *rhs.proto); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
ProtoKey CreateProtoKey(const std::string& signature) { |
||||||
|
CHECK_EQ(signature[0], '('); |
||||||
|
const char* args = signature.c_str() + 1; |
||||||
|
const char* args_end = std::strchr(args, ')'); |
||||||
|
CHECK(args_end != nullptr); |
||||||
|
const char* return_type = args_end + 1; |
||||||
|
|
||||||
|
ProtoKey key = { |
||||||
|
std::string() + ((*return_type == '[') ? 'L' : *return_type), |
||||||
|
return_type, |
||||||
|
std::vector<std::string>() |
||||||
|
}; |
||||||
|
while (args != args_end) { |
||||||
|
key.shorty += (*args == '[') ? 'L' : *args; |
||||||
|
const char* arg_start = args; |
||||||
|
while (*args == '[') { |
||||||
|
++args; |
||||||
|
} |
||||||
|
if (*args == 'L') { |
||||||
|
do { |
||||||
|
++args; |
||||||
|
CHECK_NE(args, args_end); |
||||||
|
} while (*args != ';'); |
||||||
|
} |
||||||
|
++args; |
||||||
|
key.args.emplace_back(arg_start, args); |
||||||
|
} |
||||||
|
return key; |
||||||
|
} |
||||||
|
|
||||||
|
void Write32(size_t offset, uint32_t value) { |
||||||
|
CHECK_LE(offset + 4u, dex_file_data_.size()); |
||||||
|
CHECK_EQ(dex_file_data_[offset + 0], 0u); |
||||||
|
CHECK_EQ(dex_file_data_[offset + 1], 0u); |
||||||
|
CHECK_EQ(dex_file_data_[offset + 2], 0u); |
||||||
|
CHECK_EQ(dex_file_data_[offset + 3], 0u); |
||||||
|
dex_file_data_[offset + 0] = static_cast<uint8_t>(value >> 0); |
||||||
|
dex_file_data_[offset + 1] = static_cast<uint8_t>(value >> 8); |
||||||
|
dex_file_data_[offset + 2] = static_cast<uint8_t>(value >> 16); |
||||||
|
dex_file_data_[offset + 3] = static_cast<uint8_t>(value >> 24); |
||||||
|
} |
||||||
|
|
||||||
|
void Write16(size_t offset, uint32_t value) { |
||||||
|
CHECK_LE(value, 0xffffu); |
||||||
|
CHECK_LE(offset + 2u, dex_file_data_.size()); |
||||||
|
CHECK_EQ(dex_file_data_[offset + 0], 0u); |
||||||
|
CHECK_EQ(dex_file_data_[offset + 1], 0u); |
||||||
|
dex_file_data_[offset + 0] = static_cast<uint8_t>(value >> 0); |
||||||
|
dex_file_data_[offset + 1] = static_cast<uint8_t>(value >> 8); |
||||||
|
} |
||||||
|
|
||||||
|
std::map<std::string, IdxAndDataOffset> strings_; |
||||||
|
std::map<std::string, uint32_t> types_; |
||||||
|
std::map<FieldKey, uint32_t, FieldKeyComparator> fields_; |
||||||
|
std::map<ProtoKey, IdxAndDataOffset, ProtoKeyComparator> protos_; |
||||||
|
std::map<MethodKey, uint32_t, MethodKeyComparator> methods_; |
||||||
|
|
||||||
|
std::vector<uint8_t> dex_file_data_; |
||||||
|
}; |
||||||
|
|
||||||
|
} // namespace art_lkchan
|
||||||
|
|
||||||
|
#endif // ART_LIBDEXFILE_DEX_TEST_DEX_FILE_BUILDER_H_
|
@ -0,0 +1,55 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 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_TYPE_REFERENCE_H_ |
||||||
|
#define ART_LIBDEXFILE_DEX_TYPE_REFERENCE_H_ |
||||||
|
|
||||||
|
#include <stdint.h> |
||||||
|
|
||||||
|
#include <android-base/logging.h> |
||||||
|
|
||||||
|
#include "dex/dex_file_types.h" |
||||||
|
#include "dex/string_reference.h" |
||||||
|
|
||||||
|
namespace art_lkchan { |
||||||
|
|
||||||
|
class DexFile; |
||||||
|
|
||||||
|
// A type is located by its DexFile and the string_ids_ table index into that DexFile.
|
||||||
|
class TypeReference : public DexFileReference { |
||||||
|
public: |
||||||
|
TypeReference(const DexFile* file, dex::TypeIndex index) |
||||||
|
: DexFileReference(file, index.index_) {} |
||||||
|
|
||||||
|
dex::TypeIndex TypeIndex() const { |
||||||
|
return dex::TypeIndex(index); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
// Compare the actual referenced type names. Used for type reference deduplication.
|
||||||
|
struct TypeReferenceValueComparator { |
||||||
|
bool operator()(const TypeReference& tr1, const TypeReference& tr2) const { |
||||||
|
// Note that we want to deduplicate identical boot image types even if they are
|
||||||
|
// referenced by different dex files, so we simply compare the descriptors.
|
||||||
|
StringReference sr1(tr1.dex_file, tr1.dex_file->GetTypeId(tr1.TypeIndex()).descriptor_idx_); |
||||||
|
StringReference sr2(tr2.dex_file, tr2.dex_file->GetTypeId(tr2.TypeIndex()).descriptor_idx_); |
||||||
|
return StringReferenceValueComparator()(sr1, sr2); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
} // namespace art_lkchan
|
||||||
|
|
||||||
|
#endif // ART_LIBDEXFILE_DEX_TYPE_REFERENCE_H_
|
@ -0,0 +1,99 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011 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_UTF_INL_H_ |
||||||
|
#define ART_LIBDEXFILE_DEX_UTF_INL_H_ |
||||||
|
|
||||||
|
#include "utf.h" |
||||||
|
|
||||||
|
namespace art_lkchan { |
||||||
|
|
||||||
|
inline uint16_t GetTrailingUtf16Char(uint32_t maybe_pair) { |
||||||
|
return static_cast<uint16_t>(maybe_pair >> 16); |
||||||
|
} |
||||||
|
|
||||||
|
inline uint16_t GetLeadingUtf16Char(uint32_t maybe_pair) { |
||||||
|
return static_cast<uint16_t>(maybe_pair & 0x0000FFFF); |
||||||
|
} |
||||||
|
|
||||||
|
inline uint32_t GetUtf16FromUtf8(const char** utf8_data_in) { |
||||||
|
const uint8_t one = *(*utf8_data_in)++; |
||||||
|
if ((one & 0x80) == 0) { |
||||||
|
// one-byte encoding
|
||||||
|
return one; |
||||||
|
} |
||||||
|
|
||||||
|
const uint8_t two = *(*utf8_data_in)++; |
||||||
|
if ((one & 0x20) == 0) { |
||||||
|
// two-byte encoding
|
||||||
|
return ((one & 0x1f) << 6) | (two & 0x3f); |
||||||
|
} |
||||||
|
|
||||||
|
const uint8_t three = *(*utf8_data_in)++; |
||||||
|
if ((one & 0x10) == 0) { |
||||||
|
return ((one & 0x0f) << 12) | ((two & 0x3f) << 6) | (three & 0x3f); |
||||||
|
} |
||||||
|
|
||||||
|
// Four byte encodings need special handling. We'll have
|
||||||
|
// to convert them into a surrogate pair.
|
||||||
|
const uint8_t four = *(*utf8_data_in)++; |
||||||
|
|
||||||
|
// Since this is a 4 byte UTF-8 sequence, it will lie between
|
||||||
|
// U+10000 and U+1FFFFF.
|
||||||
|
//
|
||||||
|
// TODO: What do we do about values in (U+10FFFF, U+1FFFFF) ? The
|
||||||
|
// spec says they're invalid but nobody appears to check for them.
|
||||||
|
const uint32_t code_point = ((one & 0x0f) << 18) | ((two & 0x3f) << 12) |
||||||
|
| ((three & 0x3f) << 6) | (four & 0x3f); |
||||||
|
|
||||||
|
uint32_t surrogate_pair = 0; |
||||||
|
// Step two: Write out the high (leading) surrogate to the bottom 16 bits
|
||||||
|
// of the of the 32 bit type.
|
||||||
|
surrogate_pair |= ((code_point >> 10) + 0xd7c0) & 0xffff; |
||||||
|
// Step three : Write out the low (trailing) surrogate to the top 16 bits.
|
||||||
|
surrogate_pair |= ((code_point & 0x03ff) + 0xdc00) << 16; |
||||||
|
|
||||||
|
return surrogate_pair; |
||||||
|
} |
||||||
|
|
||||||
|
inline int CompareModifiedUtf8ToModifiedUtf8AsUtf16CodePointValues(const char* utf8_1, |
||||||
|
const char* utf8_2) { |
||||||
|
uint32_t c1, c2; |
||||||
|
do { |
||||||
|
c1 = *utf8_1; |
||||||
|
c2 = *utf8_2; |
||||||
|
// Did we reach a terminating character?
|
||||||
|
if (c1 == 0) { |
||||||
|
return (c2 == 0) ? 0 : -1; |
||||||
|
} else if (c2 == 0) { |
||||||
|
return 1; |
||||||
|
} |
||||||
|
|
||||||
|
c1 = GetUtf16FromUtf8(&utf8_1); |
||||||
|
c2 = GetUtf16FromUtf8(&utf8_2); |
||||||
|
} while (c1 == c2); |
||||||
|
|
||||||
|
const uint32_t leading_surrogate_diff = GetLeadingUtf16Char(c1) - GetLeadingUtf16Char(c2); |
||||||
|
if (leading_surrogate_diff != 0) { |
||||||
|
return static_cast<int>(leading_surrogate_diff); |
||||||
|
} |
||||||
|
|
||||||
|
return GetTrailingUtf16Char(c1) - GetTrailingUtf16Char(c2); |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace art_lkchan
|
||||||
|
|
||||||
|
#endif // ART_LIBDEXFILE_DEX_UTF_INL_H_
|
@ -0,0 +1,321 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011 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. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include "utf.h" |
||||||
|
|
||||||
|
#include <android-base/logging.h> |
||||||
|
#include <android-base/stringprintf.h> |
||||||
|
#include <android-base/strings.h> |
||||||
|
|
||||||
|
#include "base/casts.h" |
||||||
|
#include "utf-inl.h" |
||||||
|
|
||||||
|
namespace art_lkchan { |
||||||
|
|
||||||
|
using android_lkchan::base::StringAppendF; |
||||||
|
using android_lkchan::base::StringPrintf; |
||||||
|
|
||||||
|
// This is used only from debugger and test code.
|
||||||
|
size_t CountModifiedUtf8Chars(const char* utf8) { |
||||||
|
return CountModifiedUtf8Chars(utf8, strlen(utf8)); |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* This does not validate UTF8 rules (nor did older code). But it gets the right answer |
||||||
|
* for valid UTF-8 and that's fine because it's used only to size a buffer for later |
||||||
|
* conversion. |
||||||
|
* |
||||||
|
* Modified UTF-8 consists of a series of bytes up to 21 bit Unicode code points as follows: |
||||||
|
* U+0001 - U+007F 0xxxxxxx |
||||||
|
* U+0080 - U+07FF 110xxxxx 10xxxxxx |
||||||
|
* U+0800 - U+FFFF 1110xxxx 10xxxxxx 10xxxxxx |
||||||
|
* U+10000 - U+1FFFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx |
||||||
|
* |
||||||
|
* U+0000 is encoded using the 2nd form to avoid nulls inside strings (this differs from |
||||||
|
* standard UTF-8). |
||||||
|
* The four byte encoding converts to two utf16 characters. |
||||||
|
*/ |
||||||
|
size_t CountModifiedUtf8Chars(const char* utf8, size_t byte_count) { |
||||||
|
DCHECK_LE(byte_count, strlen(utf8)); |
||||||
|
size_t len = 0; |
||||||
|
const char* end = utf8 + byte_count; |
||||||
|
for (; utf8 < end; ++utf8) { |
||||||
|
int ic = *utf8; |
||||||
|
len++; |
||||||
|
if (LIKELY((ic & 0x80) == 0)) { |
||||||
|
// One-byte encoding.
|
||||||
|
continue; |
||||||
|
} |
||||||
|
// Two- or three-byte encoding.
|
||||||
|
utf8++; |
||||||
|
if ((ic & 0x20) == 0) { |
||||||
|
// Two-byte encoding.
|
||||||
|
continue; |
||||||
|
} |
||||||
|
utf8++; |
||||||
|
if ((ic & 0x10) == 0) { |
||||||
|
// Three-byte encoding.
|
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
// Four-byte encoding: needs to be converted into a surrogate
|
||||||
|
// pair.
|
||||||
|
utf8++; |
||||||
|
len++; |
||||||
|
} |
||||||
|
return len; |
||||||
|
} |
||||||
|
|
||||||
|
// This is used only from debugger and test code.
|
||||||
|
void ConvertModifiedUtf8ToUtf16(uint16_t* utf16_data_out, const char* utf8_data_in) { |
||||||
|
while (*utf8_data_in != '\0') { |
||||||
|
const uint32_t ch = GetUtf16FromUtf8(&utf8_data_in); |
||||||
|
const uint16_t leading = GetLeadingUtf16Char(ch); |
||||||
|
const uint16_t trailing = GetTrailingUtf16Char(ch); |
||||||
|
|
||||||
|
*utf16_data_out++ = leading; |
||||||
|
if (trailing != 0) { |
||||||
|
*utf16_data_out++ = trailing; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void ConvertModifiedUtf8ToUtf16(uint16_t* utf16_data_out, size_t out_chars, |
||||||
|
const char* utf8_data_in, size_t in_bytes) { |
||||||
|
const char *in_start = utf8_data_in; |
||||||
|
const char *in_end = utf8_data_in + in_bytes; |
||||||
|
uint16_t *out_p = utf16_data_out; |
||||||
|
|
||||||
|
if (LIKELY(out_chars == in_bytes)) { |
||||||
|
// Common case where all characters are ASCII.
|
||||||
|
for (const char *p = in_start; p < in_end;) { |
||||||
|
// Safe even if char is signed because ASCII characters always have
|
||||||
|
// the high bit cleared.
|
||||||
|
*out_p++ = dchecked_integral_cast<uint16_t>(*p++); |
||||||
|
} |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
// String contains non-ASCII characters.
|
||||||
|
for (const char *p = in_start; p < in_end;) { |
||||||
|
const uint32_t ch = GetUtf16FromUtf8(&p); |
||||||
|
const uint16_t leading = GetLeadingUtf16Char(ch); |
||||||
|
const uint16_t trailing = GetTrailingUtf16Char(ch); |
||||||
|
|
||||||
|
*out_p++ = leading; |
||||||
|
if (trailing != 0) { |
||||||
|
*out_p++ = trailing; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void ConvertUtf16ToModifiedUtf8(char* utf8_out, size_t byte_count, |
||||||
|
const uint16_t* utf16_in, size_t char_count) { |
||||||
|
if (LIKELY(byte_count == char_count)) { |
||||||
|
// Common case where all characters are ASCII.
|
||||||
|
const uint16_t *utf16_end = utf16_in + char_count; |
||||||
|
for (const uint16_t *p = utf16_in; p < utf16_end;) { |
||||||
|
*utf8_out++ = dchecked_integral_cast<char>(*p++); |
||||||
|
} |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
// String contains non-ASCII characters.
|
||||||
|
while (char_count--) { |
||||||
|
const uint16_t ch = *utf16_in++; |
||||||
|
if (ch > 0 && ch <= 0x7f) { |
||||||
|
*utf8_out++ = ch; |
||||||
|
} else { |
||||||
|
// Char_count == 0 here implies we've encountered an unpaired
|
||||||
|
// surrogate and we have no choice but to encode it as 3-byte UTF
|
||||||
|
// sequence. Note that unpaired surrogates can occur as a part of
|
||||||
|
// "normal" operation.
|
||||||
|
if ((ch >= 0xd800 && ch <= 0xdbff) && (char_count > 0)) { |
||||||
|
const uint16_t ch2 = *utf16_in; |
||||||
|
|
||||||
|
// Check if the other half of the pair is within the expected
|
||||||
|
// range. If it isn't, we will have to emit both "halves" as
|
||||||
|
// separate 3 byte sequences.
|
||||||
|
if (ch2 >= 0xdc00 && ch2 <= 0xdfff) { |
||||||
|
utf16_in++; |
||||||
|
char_count--; |
||||||
|
const uint32_t code_point = (ch << 10) + ch2 - 0x035fdc00; |
||||||
|
*utf8_out++ = (code_point >> 18) | 0xf0; |
||||||
|
*utf8_out++ = ((code_point >> 12) & 0x3f) | 0x80; |
||||||
|
*utf8_out++ = ((code_point >> 6) & 0x3f) | 0x80; |
||||||
|
*utf8_out++ = (code_point & 0x3f) | 0x80; |
||||||
|
continue; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (ch > 0x07ff) { |
||||||
|
// Three byte encoding.
|
||||||
|
*utf8_out++ = (ch >> 12) | 0xe0; |
||||||
|
*utf8_out++ = ((ch >> 6) & 0x3f) | 0x80; |
||||||
|
*utf8_out++ = (ch & 0x3f) | 0x80; |
||||||
|
} else /*(ch > 0x7f || ch == 0)*/ { |
||||||
|
// Two byte encoding.
|
||||||
|
*utf8_out++ = (ch >> 6) | 0xc0; |
||||||
|
*utf8_out++ = (ch & 0x3f) | 0x80; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
int32_t ComputeUtf16HashFromModifiedUtf8(const char* utf8, size_t utf16_length) { |
||||||
|
uint32_t hash = 0; |
||||||
|
while (utf16_length != 0u) { |
||||||
|
const uint32_t pair = GetUtf16FromUtf8(&utf8); |
||||||
|
const uint16_t first = GetLeadingUtf16Char(pair); |
||||||
|
hash = hash * 31 + first; |
||||||
|
--utf16_length; |
||||||
|
const uint16_t second = GetTrailingUtf16Char(pair); |
||||||
|
if (second != 0) { |
||||||
|
hash = hash * 31 + second; |
||||||
|
DCHECK_NE(utf16_length, 0u); |
||||||
|
--utf16_length; |
||||||
|
} |
||||||
|
} |
||||||
|
return static_cast<int32_t>(hash); |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t ComputeModifiedUtf8Hash(const char* chars) { |
||||||
|
uint32_t hash = 0; |
||||||
|
while (*chars != '\0') { |
||||||
|
hash = hash * 31 + *chars++; |
||||||
|
} |
||||||
|
return static_cast<int32_t>(hash); |
||||||
|
} |
||||||
|
|
||||||
|
int CompareModifiedUtf8ToUtf16AsCodePointValues(const char* utf8, const uint16_t* utf16, |
||||||
|
size_t utf16_length) { |
||||||
|
for (;;) { |
||||||
|
if (*utf8 == '\0') { |
||||||
|
return (utf16_length == 0) ? 0 : -1; |
||||||
|
} else if (utf16_length == 0) { |
||||||
|
return 1; |
||||||
|
} |
||||||
|
|
||||||
|
const uint32_t pair = GetUtf16FromUtf8(&utf8); |
||||||
|
|
||||||
|
// First compare the leading utf16 char.
|
||||||
|
const uint16_t lhs = GetLeadingUtf16Char(pair); |
||||||
|
const uint16_t rhs = *utf16++; |
||||||
|
--utf16_length; |
||||||
|
if (lhs != rhs) { |
||||||
|
return lhs > rhs ? 1 : -1; |
||||||
|
} |
||||||
|
|
||||||
|
// Then compare the trailing utf16 char. First check if there
|
||||||
|
// are any characters left to consume.
|
||||||
|
const uint16_t lhs2 = GetTrailingUtf16Char(pair); |
||||||
|
if (lhs2 != 0) { |
||||||
|
if (utf16_length == 0) { |
||||||
|
return 1; |
||||||
|
} |
||||||
|
|
||||||
|
const uint16_t rhs2 = *utf16++; |
||||||
|
--utf16_length; |
||||||
|
if (lhs2 != rhs2) { |
||||||
|
return lhs2 > rhs2 ? 1 : -1; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
size_t CountUtf8Bytes(const uint16_t* chars, size_t char_count) { |
||||||
|
size_t result = 0; |
||||||
|
const uint16_t *end = chars + char_count; |
||||||
|
while (chars < end) { |
||||||
|
const uint16_t ch = *chars++; |
||||||
|
if (LIKELY(ch != 0 && ch < 0x80)) { |
||||||
|
result++; |
||||||
|
continue; |
||||||
|
} |
||||||
|
if (ch < 0x800) { |
||||||
|
result += 2; |
||||||
|
continue; |
||||||
|
} |
||||||
|
if (ch >= 0xd800 && ch < 0xdc00) { |
||||||
|
if (chars < end) { |
||||||
|
const uint16_t ch2 = *chars; |
||||||
|
// If we find a properly paired surrogate, we emit it as a 4 byte
|
||||||
|
// UTF sequence. If we find an unpaired leading or trailing surrogate,
|
||||||
|
// we emit it as a 3 byte sequence like would have done earlier.
|
||||||
|
if (ch2 >= 0xdc00 && ch2 < 0xe000) { |
||||||
|
chars++; |
||||||
|
result += 4; |
||||||
|
continue; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
result += 3; |
||||||
|
} |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
static inline constexpr bool NeedsEscaping(uint16_t ch) { |
||||||
|
return (ch < ' ' || ch > '~'); |
||||||
|
} |
||||||
|
|
||||||
|
std::string PrintableChar(uint16_t ch) { |
||||||
|
std::string result; |
||||||
|
result += '\''; |
||||||
|
if (NeedsEscaping(ch)) { |
||||||
|
StringAppendF(&result, "\\u%04x", ch); |
||||||
|
} else { |
||||||
|
result += static_cast<std::string::value_type>(ch); |
||||||
|
} |
||||||
|
result += '\''; |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
std::string PrintableString(const char* utf) { |
||||||
|
std::string result; |
||||||
|
result += '"'; |
||||||
|
const char* p = utf; |
||||||
|
size_t char_count = CountModifiedUtf8Chars(p); |
||||||
|
for (size_t i = 0; i < char_count; ++i) { |
||||||
|
uint32_t ch = GetUtf16FromUtf8(&p); |
||||||
|
if (ch == '\\') { |
||||||
|
result += "\\\\"; |
||||||
|
} else if (ch == '\n') { |
||||||
|
result += "\\n"; |
||||||
|
} else if (ch == '\r') { |
||||||
|
result += "\\r"; |
||||||
|
} else if (ch == '\t') { |
||||||
|
result += "\\t"; |
||||||
|
} else { |
||||||
|
const uint16_t leading = GetLeadingUtf16Char(ch); |
||||||
|
|
||||||
|
if (NeedsEscaping(leading)) { |
||||||
|
StringAppendF(&result, "\\u%04x", leading); |
||||||
|
} else { |
||||||
|
result += static_cast<std::string::value_type>(leading); |
||||||
|
} |
||||||
|
|
||||||
|
const uint32_t trailing = GetTrailingUtf16Char(ch); |
||||||
|
if (trailing != 0) { |
||||||
|
// All high surrogates will need escaping.
|
||||||
|
StringAppendF(&result, "\\u%04x", trailing); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
result += '"'; |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace art_lkchan
|
@ -0,0 +1,135 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011 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_UTF_H_ |
||||||
|
#define ART_LIBDEXFILE_DEX_UTF_H_ |
||||||
|
|
||||||
|
#include "base/macros.h" |
||||||
|
|
||||||
|
#include <stddef.h> |
||||||
|
#include <stdint.h> |
||||||
|
|
||||||
|
#include <string> |
||||||
|
|
||||||
|
/*
|
||||||
|
* All UTF-8 in art is actually modified UTF-8. Mostly, this distinction |
||||||
|
* doesn't matter. |
||||||
|
* |
||||||
|
* See http://en.wikipedia.org/wiki/UTF-8#Modified_UTF-8 for the details.
|
||||||
|
*/ |
||||||
|
namespace art_lkchan { |
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the number of UTF-16 characters in the given modified UTF-8 string. |
||||||
|
*/ |
||||||
|
size_t CountModifiedUtf8Chars(const char* utf8); |
||||||
|
size_t CountModifiedUtf8Chars(const char* utf8, size_t byte_count); |
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the number of modified UTF-8 bytes needed to represent the given |
||||||
|
* UTF-16 string. |
||||||
|
*/ |
||||||
|
size_t CountUtf8Bytes(const uint16_t* chars, size_t char_count); |
||||||
|
|
||||||
|
/*
|
||||||
|
* Convert from Modified UTF-8 to UTF-16. |
||||||
|
*/ |
||||||
|
void ConvertModifiedUtf8ToUtf16(uint16_t* utf16_out, const char* utf8_in); |
||||||
|
void ConvertModifiedUtf8ToUtf16(uint16_t* utf16_out, size_t out_chars, |
||||||
|
const char* utf8_in, size_t in_bytes); |
||||||
|
|
||||||
|
/*
|
||||||
|
* Compare two modified UTF-8 strings as UTF-16 code point values in a non-locale sensitive manner |
||||||
|
*/ |
||||||
|
ALWAYS_INLINE int CompareModifiedUtf8ToModifiedUtf8AsUtf16CodePointValues(const char* utf8_1, |
||||||
|
const char* utf8_2); |
||||||
|
|
||||||
|
/*
|
||||||
|
* Compare a null-terminated modified UTF-8 string with a UTF-16 string (not null-terminated) |
||||||
|
* as code point values in a non-locale sensitive manner. |
||||||
|
*/ |
||||||
|
int CompareModifiedUtf8ToUtf16AsCodePointValues(const char* utf8, const uint16_t* utf16, |
||||||
|
size_t utf16_length); |
||||||
|
|
||||||
|
/*
|
||||||
|
* Convert from UTF-16 to Modified UTF-8. Note that the output is _not_ |
||||||
|
* NUL-terminated. You probably need to call CountUtf8Bytes before calling |
||||||
|
* this anyway, so if you want a NUL-terminated string, you know where to |
||||||
|
* put the NUL byte. |
||||||
|
*/ |
||||||
|
void ConvertUtf16ToModifiedUtf8(char* utf8_out, size_t byte_count, |
||||||
|
const uint16_t* utf16_in, size_t char_count); |
||||||
|
|
||||||
|
/*
|
||||||
|
* The java.lang.String hashCode() algorithm. |
||||||
|
*/ |
||||||
|
template<typename MemoryType> |
||||||
|
int32_t ComputeUtf16Hash(const MemoryType* chars, size_t char_count) { |
||||||
|
uint32_t hash = 0; |
||||||
|
while (char_count--) { |
||||||
|
hash = hash * 31 + *chars++; |
||||||
|
} |
||||||
|
return static_cast<int32_t>(hash); |
||||||
|
} |
||||||
|
|
||||||
|
int32_t ComputeUtf16HashFromModifiedUtf8(const char* utf8, size_t utf16_length); |
||||||
|
|
||||||
|
// Compute a hash code of a modified UTF-8 string. Not the standard java hash since it returns a
|
||||||
|
// uint32_t and hashes individual chars instead of codepoint words.
|
||||||
|
uint32_t ComputeModifiedUtf8Hash(const char* chars); |
||||||
|
|
||||||
|
/*
|
||||||
|
* Retrieve the next UTF-16 character or surrogate pair from a UTF-8 string. |
||||||
|
* single byte, 2-byte and 3-byte UTF-8 sequences result in a single UTF-16 |
||||||
|
* character (possibly one half of a surrogate) whereas 4-byte UTF-8 sequences |
||||||
|
* result in a surrogate pair. Use GetLeadingUtf16Char and GetTrailingUtf16Char |
||||||
|
* to process the return value of this function. |
||||||
|
* |
||||||
|
* Advances "*utf8_data_in" to the start of the next character. |
||||||
|
* |
||||||
|
* WARNING: If a string is corrupted by dropping a '\0' in the middle |
||||||
|
* of a multi byte sequence, you can end up overrunning the buffer with |
||||||
|
* reads (and possibly with the writes if the length was computed and |
||||||
|
* cached before the damage). For performance reasons, this function |
||||||
|
* assumes that the string being parsed is known to be valid (e.g., by |
||||||
|
* already being verified). Most strings we process here are coming |
||||||
|
* out of dex files or other internal translations, so the only real |
||||||
|
* risk comes from the JNI NewStringUTF call. |
||||||
|
*/ |
||||||
|
uint32_t GetUtf16FromUtf8(const char** utf8_data_in); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the leading UTF-16 character from a surrogate pair, or the sole |
||||||
|
* UTF-16 character from the return value of GetUtf16FromUtf8. |
||||||
|
*/ |
||||||
|
ALWAYS_INLINE uint16_t GetLeadingUtf16Char(uint32_t maybe_pair); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the trailing UTF-16 character from a surrogate pair, or 0 otherwise |
||||||
|
* from the return value of GetUtf16FromUtf8. |
||||||
|
*/ |
||||||
|
ALWAYS_INLINE uint16_t GetTrailingUtf16Char(uint32_t maybe_pair); |
||||||
|
|
||||||
|
// Returns a printable (escaped) version of a character.
|
||||||
|
std::string PrintableChar(uint16_t ch); |
||||||
|
|
||||||
|
// Returns an ASCII string corresponding to the given UTF-8 string.
|
||||||
|
// Java escapes are used for non-ASCII characters.
|
||||||
|
std::string PrintableString(const char* utf8); |
||||||
|
|
||||||
|
} // namespace art_lkchan
|
||||||
|
|
||||||
|
#endif // ART_LIBDEXFILE_DEX_UTF_H_
|
@ -0,0 +1,37 @@ |
|||||||
|
//
|
||||||
|
// Created by Milk on 2021/6/6.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <sys/types.h> |
||||||
|
#include "ProcessHook.h" |
||||||
|
#import "jniHook/JniHook.h" |
||||||
|
#import "utils/Log.h" |
||||||
|
#import "xhook/xhook.h" |
||||||
|
|
||||||
|
HOOK_JNI(void, sendSignal, JNIEnv *env, jobject obj, jint pid, jint signal) { |
||||||
|
ALOGE("hooked sendSignal"); |
||||||
|
} |
||||||
|
|
||||||
|
HOOK_JNI(void, sendSignalQuiet, JNIEnv *env, jobject obj, jint pid, jint signal) { |
||||||
|
ALOGE("hooked sendSignalQuiet"); |
||||||
|
} |
||||||
|
|
||||||
|
HOOK_JNI(jint, killProcessGroup, JNIEnv *env, jobject obj, jint uid, jint pid) { |
||||||
|
ALOGE("hooked killProcessGroup"); |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
void ProcessHook::init(JNIEnv *env) { |
||||||
|
const char *className = "android/os/Process"; |
||||||
|
JniHook::HookJniFun(env, className, "sendSignal", "(II)V", |
||||||
|
(void *) new_sendSignal, |
||||||
|
(void **) (&orig_sendSignal), true); |
||||||
|
|
||||||
|
JniHook::HookJniFun(env, className, "sendSignalQuiet", "(II)V", |
||||||
|
(void *) new_sendSignalQuiet, |
||||||
|
(void **) (&orig_sendSignalQuiet), true); |
||||||
|
|
||||||
|
JniHook::HookJniFun(env, className, "killProcessGroup", "(II)I", |
||||||
|
(void *) new_killProcessGroup, |
||||||
|
(void **) (&orig_killProcessGroup), true); |
||||||
|
} |
@ -0,0 +1,15 @@ |
|||||||
|
//
|
||||||
|
// Created by Milk on 2021/6/6.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef BLACKDEX_PROCESSHOOK_H |
||||||
|
#define BLACKDEX_PROCESSHOOK_H |
||||||
|
#include "BaseHook.h" |
||||||
|
|
||||||
|
class ProcessHook : public BaseHook { |
||||||
|
public: |
||||||
|
static void init(JNIEnv *env); |
||||||
|
}; |
||||||
|
|
||||||
|
|
||||||
|
#endif //BLACKDEX_PROCESSHOOK_H
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue