Add some new features

1.Compile with cmake
2.Support for fixing codeItem
3.Optimisation de l'espace de stockage
pull/16/head
Milk 4 years ago
parent 095ad946e2
commit 27660d7c67
  1. 13
      Bcore/black-hook/src/main/java/top/niunaijun/jnihook/MethodUtils.java
  2. 37
      Bcore/black-hook/src/main/java/top/niunaijun/jnihook/ReflectCore.java
  3. 16
      Bcore/black-hook/src/main/java/top/niunaijun/jnihook/jni/ArtMethod.java
  4. 26
      Bcore/black-hook/src/main/java/top/niunaijun/jnihook/jni/JniHook.java
  5. 5
      Bcore/build.gradle
  6. 66
      Bcore/src/main/cpp/CMakeLists.txt
  7. 174
      Bcore/src/main/cpp/DexDump.cpp
  8. 2
      Bcore/src/main/cpp/DexDump.h
  9. 2
      Bcore/src/main/cpp/IO.cpp
  10. 0
      Bcore/src/main/cpp/IO.h
  11. 97
      Bcore/src/main/cpp/VmCore.cpp
  12. 1
      Bcore/src/main/cpp/VmCore.h
  13. 345
      Bcore/src/main/cpp/android-base/file.cpp
  14. 82
      Bcore/src/main/cpp/android-base/file.h
  15. 503
      Bcore/src/main/cpp/android-base/logging.cpp
  16. 501
      Bcore/src/main/cpp/android-base/logging.h
  17. 197
      Bcore/src/main/cpp/android-base/macros.h
  18. 41
      Bcore/src/main/cpp/android-base/memory.h
  19. 85
      Bcore/src/main/cpp/android-base/stringprintf.cpp
  20. 56
      Bcore/src/main/cpp/android-base/stringprintf.h
  21. 137
      Bcore/src/main/cpp/android-base/strings.cpp
  22. 79
      Bcore/src/main/cpp/android-base/strings.h
  23. 113
      Bcore/src/main/cpp/android-base/thread_annotations.h
  24. 154
      Bcore/src/main/cpp/android-base/unique_fd.h
  25. 106
      Bcore/src/main/cpp/android-base/utf8.h
  26. 504
      Bcore/src/main/cpp/base/bit_utils.h
  27. 170
      Bcore/src/main/cpp/base/casts.h
  28. 45
      Bcore/src/main/cpp/base/enums.h
  29. 139
      Bcore/src/main/cpp/base/globals.h
  30. 74
      Bcore/src/main/cpp/base/hash_map.h
  31. 693
      Bcore/src/main/cpp/base/hash_set.h
  32. 70
      Bcore/src/main/cpp/base/iteration_range.h
  33. 377
      Bcore/src/main/cpp/base/leb128.h
  34. 101
      Bcore/src/main/cpp/base/macros.h
  35. 41
      Bcore/src/main/cpp/base/memory.h
  36. 74
      Bcore/src/main/cpp/base/memory_tool.h
  37. 180
      Bcore/src/main/cpp/base/safe_map.h
  38. 158
      Bcore/src/main/cpp/base/stl_util.h
  39. 41
      Bcore/src/main/cpp/base/stl_util_identity.h
  40. 289
      Bcore/src/main/cpp/base/stringpiece.h
  41. 31
      Bcore/src/main/cpp/base/value_object.h
  42. 100
      Bcore/src/main/cpp/dex/base64_test_util.h
  43. 147
      Bcore/src/main/cpp/dex/bytecode_utils.h
  44. 42
      Bcore/src/main/cpp/dex/class_reference.h
  45. 204
      Bcore/src/main/cpp/dex/code_item_accessors-inl.h
  46. 166
      Bcore/src/main/cpp/dex/code_item_accessors.h
  47. 108
      Bcore/src/main/cpp/dex/compact_dex_file.cc
  48. 305
      Bcore/src/main/cpp/dex/compact_dex_file.h
  49. 49
      Bcore/src/main/cpp/dex/compact_dex_level.h
  50. 37
      Bcore/src/main/cpp/dex/compact_dex_utils.h
  51. 133
      Bcore/src/main/cpp/dex/compact_offset_table.cc
  52. 69
      Bcore/src/main/cpp/dex/compact_offset_table.h
  53. 426
      Bcore/src/main/cpp/dex/descriptors_names.cc
  54. 63
      Bcore/src/main/cpp/dex/descriptors_names.h
  55. 93
      Bcore/src/main/cpp/dex/dex_cache_resolved_classes.h
  56. 532
      Bcore/src/main/cpp/dex/dex_file-inl.h
  57. 817
      Bcore/src/main/cpp/dex/dex_file.cc
  58. 1461
      Bcore/src/main/cpp/dex/dex_file.h
  59. 104
      Bcore/src/main/cpp/dex/dex_file_exception_helpers.cc
  60. 68
      Bcore/src/main/cpp/dex/dex_file_exception_helpers.h
  61. 107
      Bcore/src/main/cpp/dex/dex_file_layout.cc
  62. 127
      Bcore/src/main/cpp/dex/dex_file_layout.h
  63. 508
      Bcore/src/main/cpp/dex/dex_file_loader.cc
  64. 199
      Bcore/src/main/cpp/dex/dex_file_loader.h
  65. 52
      Bcore/src/main/cpp/dex/dex_file_reference.h
  66. 272
      Bcore/src/main/cpp/dex/dex_file_tracking_registrar.cc
  67. 81
      Bcore/src/main/cpp/dex/dex_file_tracking_registrar.h
  68. 117
      Bcore/src/main/cpp/dex/dex_file_types.h
  69. 3290
      Bcore/src/main/cpp/dex/dex_file_verifier.cc
  70. 246
      Bcore/src/main/cpp/dex/dex_file_verifier.h
  71. 558
      Bcore/src/main/cpp/dex/dex_instruction-inl.h
  72. 581
      Bcore/src/main/cpp/dex/dex_instruction.cc
  73. 757
      Bcore/src/main/cpp/dex/dex_instruction.h
  74. 237
      Bcore/src/main/cpp/dex/dex_instruction_iterator.h
  75. 308
      Bcore/src/main/cpp/dex/dex_instruction_list.h
  76. 219
      Bcore/src/main/cpp/dex/dex_instruction_utils.h
  77. 169
      Bcore/src/main/cpp/dex/hidden_api_access_flags.h
  78. 38
      Bcore/src/main/cpp/dex/invoke_type.h
  79. 91
      Bcore/src/main/cpp/dex/method_reference.h
  80. 58
      Bcore/src/main/cpp/dex/modifiers.cc
  81. 149
      Bcore/src/main/cpp/dex/modifiers.h
  82. 73
      Bcore/src/main/cpp/dex/primitive.cc
  83. 226
      Bcore/src/main/cpp/dex/primitive.h
  84. 81
      Bcore/src/main/cpp/dex/standard_dex_file.cc
  85. 118
      Bcore/src/main/cpp/dex/standard_dex_file.h
  86. 71
      Bcore/src/main/cpp/dex/string_reference.h
  87. 400
      Bcore/src/main/cpp/dex/test_dex_file_builder.h
  88. 55
      Bcore/src/main/cpp/dex/type_reference.h
  89. 99
      Bcore/src/main/cpp/dex/utf-inl.h
  90. 321
      Bcore/src/main/cpp/dex/utf.cc
  91. 135
      Bcore/src/main/cpp/dex/utf.h
  92. 0
      Bcore/src/main/cpp/hook/BaseHook.cpp
  93. 2
      Bcore/src/main/cpp/hook/BaseHook.h
  94. 37
      Bcore/src/main/cpp/hook/ProcessHook.cpp
  95. 15
      Bcore/src/main/cpp/hook/ProcessHook.h
  96. 3
      Bcore/src/main/cpp/hook/UnixFileSystemHook.cpp
  97. 0
      Bcore/src/main/cpp/hook/UnixFileSystemHook.h
  98. 2
      Bcore/src/main/cpp/hook/VMClassLoaderHook.cpp
  99. 0
      Bcore/src/main/cpp/hook/VMClassLoaderHook.h
  100. 0
      Bcore/src/main/cpp/jnihook/Art.h
  101. Some files were not shown because too many files have changed in this diff Show More

@ -11,6 +11,7 @@ package top.niunaijun.jnihook;
import androidx.annotation.Keep;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
@Keep
@ -39,6 +40,18 @@ public class MethodUtils {
return buf.toString();
}
// native call
public static String getDesc(final Constructor<?> method) {
final StringBuffer buf = new StringBuffer();
buf.append("(");
final Class<?>[] types = method.getParameterTypes();
for (int i = 0; i < types.length; ++i) {
buf.append(getDesc(types[i]));
}
buf.append(")V");
return buf.toString();
}
private static String getDesc(final Class<?> returnType) {
if (returnType.isPrimitive()) {
return getPrimitiveLetter(returnType);

@ -1,37 +0,0 @@
package top.niunaijun.jnihook;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import top.niunaijun.jnihook.jni.JniHook;
/**
* Created by Milk on 2021/5/7.
* * _
* (`ω
*  つ0
* しーJ
* 此处无Bug
*/
public class ReflectCore {
public static void set(Class<?> clazz) {
try {
Field accessFlags = Class.class.getDeclaredField("accessFlags");
accessFlags.setAccessible(true);
int o = (int) accessFlags.get(clazz);
accessFlags.set(clazz, o | 0x0001);
} catch (Throwable e) {
e.printStackTrace();
}
for (Method declaredMethod : clazz.getDeclaredMethods()) {
JniHook.setAccessible(clazz, declaredMethod);
}
for (Field declaredField : clazz.getDeclaredFields()) {
JniHook.setAccessible(clazz, declaredField);
}
for (Class<?> declaredClass : clazz.getDeclaredClasses()) {
set(declaredClass);
}
}
}

@ -0,0 +1,16 @@
package top.niunaijun.jnihook.jni;
/**
* Created by Milk on 2021/6/5.
* * _
* (`ω
*  つ0
* しーJ
* 此处无Bug
*/
public class ArtMethod {
public static final native void nativeOffset();
public static final native void nativeOffset2();
}

@ -1,26 +0,0 @@
package top.niunaijun.jnihook.jni;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* Created by Milk on 3/7/21.
* * _
* (`ω
*  つ0
* しーJ
* 此处无Bug
*/
public final class JniHook {
public static final int NATIVE_OFFSET = 0;
public static final int NATIVE_OFFSET_2 = 0;
public static final native void nativeOffset();
public static final native void nativeOffset2();
public static native void setAccessible(Class<?> clazz, Method method);
public static native void setAccessible(Class<?> clazz, Field field);
}

@ -24,8 +24,8 @@ android {
}
}
externalNativeBuild {
ndkBuild {
path file('src/main/jni/Android.mk')
cmake {
path file('./src/main/cpp/CMakeLists.txt')
}
}
dexOptions {
@ -62,7 +62,6 @@ tasks.withType(Javadoc) {
options.addStringOption('charSet', 'UTF-8')
}
dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation 'androidx.appcompat:appcompat:1.2.0'

@ -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);
}
}

@ -12,7 +12,7 @@
class DexDump {
public:
static void dumpDex(JNIEnv *env, jlong cookie, jstring dir);
static void dumpDex(JNIEnv *env, jlong cookie, jstring dir, jboolean fix);
};

@ -3,7 +3,7 @@
//
#include "IO.h"
#include "Log.h"
#include "utils/Log.h"
jmethodID getAbsolutePathMethodId;

@ -3,16 +3,17 @@
//
#include "VmCore.h"
#include "Log.h"
#include "utils/Log.h"
#include "IO.h"
#include <jni.h>
#include <JniHook/JniHook.h>
#include <Hook/VMClassLoaderHook.h>
#include <Hook/UnixFileSystemHook.h>
#include <Hook/BinderHook.h>
#include <Hook/RuntimeHook.h>
#include <jniHook/JniHook.h>
#include <jniHook/ArtM.h>
#include <hook/ProcessHook.h>
#include <hook/VMClassLoaderHook.h>
#include <hook/UnixFileSystemHook.h>
#include "DexDump.h"
#include "Utils/HexDump.h"
#import "xhook/xhook.h"
struct {
JavaVM *vm;
@ -22,9 +23,63 @@ struct {
jmethodID redirectPathFile;
jmethodID loadEmptyDex;
jmethodID loadEmptyDexL;
jmethodID findMethod;
int api_level;
} VMEnv;
bool is_str_utf8(const char* str)
{
unsigned int nBytes = 0;//UFT8可用1-6个字节编码,ASCII用一个字节
unsigned char chr = *str;
bool bAllAscii = true;
for (unsigned int i = 0; str[i] != '\0'; ++i){
chr = *(str + i);
//判断是否ASCII编码,如果不是,说明有可能是UTF8,ASCII用7位编码,最高位标记为0,0xxxxxxx
if (nBytes == 0 && (chr & 0x80) != 0){
bAllAscii = false;
}
if (nBytes == 0) {
//如果不是ASCII码,应该是多字节符,计算字节数
if (chr >= 0x80) {
if (chr >= 0xFC && chr <= 0xFD){
nBytes = 6;
}
else if (chr >= 0xF8){
nBytes = 5;
}
else if (chr >= 0xF0){
nBytes = 4;
}
else if (chr >= 0xE0){
nBytes = 3;
}
else if (chr >= 0xC0){
nBytes = 2;
}
else{
return false;
}
nBytes--;
}
}
else{
//多字节符的非首字节,应为 10xxxxxx
if ((chr & 0xC0) != 0x80){
return false;
}
//减到为零为止
nBytes--;
}
}
//违返UTF8编码规则
if (nBytes != 0) {
return false;
}
if (bAllAscii){ //如果全部都是ASCII, 也是UTF8
return true;
}
return true;
}
JNIEnv *getEnv() {
JNIEnv *env;
@ -45,6 +100,24 @@ int VmCore::getCallingUid(JNIEnv *env, int orig) {
return env->CallStaticIntMethod(VMEnv.VMCoreClass, VMEnv.getCallingUidId, orig);
}
jobject VmCore::findMethod(JNIEnv *env, const char *className, const char *methodName,
const char *signature) {
try {
if (!is_str_utf8(className) || !is_str_utf8(methodName) || !is_str_utf8(methodName)) {
return nullptr;
}
auto clazz = (jclass) env->CallStaticObjectMethod(VMEnv.VMCoreClass, VMEnv.findMethod,
env->NewStringUTF(className),
env->NewStringUTF(methodName),
env->NewStringUTF(signature));
env->ExceptionClear();
return clazz;
} catch (const char* &e) {
ALOGE("catch findMethod %s", e);
return nullptr;
}
}
jstring VmCore::redirectPathString(JNIEnv *env, jstring path) {
env = ensureEnvCreated();
return (jstring) env->CallStaticObjectMethod(VMEnv.VMCoreClass, VMEnv.redirectPathString, path);
@ -72,8 +145,7 @@ void nativeHook(JNIEnv *env) {
BaseHook::init(env);
UnixFileSystemHook::init(env);
VMClassLoaderHook::init(env);
// RuntimeHook::init(env);
// BinderHook::init(env);
ProcessHook::init(env);
}
void hideXposed(JNIEnv *env, jclass clazz) {
@ -92,7 +164,10 @@ void init(JNIEnv *env, jobject clazz, jint api_level) {
"(Ljava/io/File;)Ljava/io/File;");
VMEnv.loadEmptyDex = env->GetStaticMethodID(VMEnv.VMCoreClass, "loadEmptyDex",
"()[J");
VMEnv.findMethod = env->GetStaticMethodID(VMEnv.VMCoreClass, "findMethod",
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;");
ArtM::InitArtMethod(env, api_level);
JniHook::InitJniHook(env, api_level);
}
@ -107,8 +182,8 @@ void enableIO(JNIEnv *env, jclass clazz) {
nativeHook(env);
}
void dumpDex(JNIEnv *env, jclass clazz, jlong cookie, jstring dir) {
DexDump::dumpDex(env, cookie, dir);
void dumpDex(JNIEnv *env, jclass clazz, jlong cookie, jstring dir, jboolean fixCodeItem) {
DexDump::dumpDex(env, cookie, dir, fixCodeItem);
}
static JNINativeMethod gMethods[] = {
@ -116,7 +191,7 @@ static JNINativeMethod gMethods[] = {
{"addIORule", "(Ljava/lang/String;Ljava/lang/String;)V", (void *) addIORule},
{"enableIO", "()V", (void *) enableIO},
{"init", "(I)V", (void *) init},
{"dumpDex", "(JLjava/lang/String;)V", (void *) dumpDex},
{"dumpDex", "(JLjava/lang/String;Z)V", (void *) dumpDex},
};
int registerNativeMethods(JNIEnv *env, const char *className,

@ -13,6 +13,7 @@ class VmCore {
public:
static JavaVM *getJavaVM();
static int getApiLevel();
static jobject findMethod(JNIEnv *env, const char *className, const char *methodName, const char *signature);
static int getCallingUid(JNIEnv *env, int orig);
static jstring redirectPathString(JNIEnv *env, jstring path);
static jobject redirectPathFile(JNIEnv *env, jobject path);

@ -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,
&registers_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, &param_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(&current_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(&current_data_));
handler_.address_ = DecodeUnsignedLeb128(&current_data_);
remaining_count_--;
return;
}
if (catch_all_) {
handler_.type_idx_ = dex::TypeIndex(DexFile::kDexNoIndex16);
handler_.address_ = DecodeUnsignedLeb128(&current_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_

@ -6,7 +6,7 @@
#define VIRTUALM_BASEHOOK_H
#include <jni.h>
#include <Log.h>
#include <utils/Log.h>
class BaseHook {
public:

@ -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

@ -4,8 +4,7 @@
#include <IO.h>
#include "UnixFileSystemHook.h"
#import "JniHook/JniHook.h"
#include "BaseHook.h"
#import "jniHook/JniHook.h"
/*
* Class: java_io_UnixFileSystem

@ -8,7 +8,7 @@
#include <cstring>
#include "VMClassLoaderHook.h"
#import "JniHook/JniHook.h"
#import "jniHook/jniHook.h"
static bool hideXposedClass = false;
HOOK_JNI(jobject, findLoadedClass, JNIEnv *env, jobject obj, jobject class_loader, jstring name) {

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save