summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--dex2oat/dex2oat.cc4
-rw-r--r--dex2oat/linker/oat_writer.cc5
-rw-r--r--oatdump/oatdump_test.cc2
-rw-r--r--openjdkjvmti/ti_class.cc22
-rw-r--r--runtime/Android.bp1
-rw-r--r--runtime/class_linker.cc302
-rw-r--r--runtime/class_linker.h9
-rw-r--r--runtime/class_loader_utils.h116
-rw-r--r--runtime/common_runtime_test.cc58
-rw-r--r--runtime/debug_print.cc113
-rw-r--r--runtime/debug_print.h34
-rw-r--r--runtime/entrypoints/quick/quick_trampoline_entrypoints.cc22
-rw-r--r--runtime/hidden_api.cc116
-rw-r--r--runtime/hidden_api.h54
-rw-r--r--runtime/hidden_api_test.cc34
-rw-r--r--runtime/interpreter/unstarted_runtime.cc6
-rw-r--r--runtime/jni_internal.cc7
-rw-r--r--runtime/native/java_lang_Class.cc35
-rw-r--r--runtime/runtime.h3
-rw-r--r--runtime/verifier/method_verifier.cc12
-rw-r--r--runtime/well_known_classes.cc6
-rw-r--r--runtime/well_known_classes.h3
-rw-r--r--test/674-hiddenapi/build11
-rw-r--r--test/674-hiddenapi/src-ex/ChildClass.java181
-rw-r--r--test/674-hiddenapi/src-ex/Linking.java52
-rw-r--r--test/674-hiddenapi/src/DummyClass.java26
-rw-r--r--test/674-hiddenapi/src/ParentInterface.java2
-rw-r--r--tools/veridex/Android.bp2
-rw-r--r--tools/veridex/README.md2
-rwxr-xr-xtools/veridex/appcompat.sh2
-rw-r--r--tools/veridex/flow_analysis.cc602
-rw-r--r--tools/veridex/flow_analysis.h147
-rw-r--r--tools/veridex/hidden_api.h12
-rw-r--r--tools/veridex/hidden_api_finder.cc65
-rw-r--r--tools/veridex/hidden_api_finder.h4
-rw-r--r--tools/veridex/precise_hidden_api_finder.cc92
-rw-r--r--tools/veridex/precise_hidden_api_finder.h55
-rw-r--r--tools/veridex/resolver.cc36
-rw-r--r--tools/veridex/resolver.h5
-rw-r--r--tools/veridex/veridex.cc66
-rw-r--r--tools/veridex/veridex.h38
41 files changed, 1875 insertions, 489 deletions
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 44c9816991..760147e62e 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -454,7 +454,9 @@ NO_RETURN static void Usage(const char* fmt, ...) {
UsageError(" The image writer will group them together.");
UsageError("");
UsageError(" --compact-dex-level=none|fast: None avoids generating compact dex, fast");
- UsageError(" generates compact dex with low compile time.");
+ UsageError(" generates compact dex with low compile time. If speed-profile is specified as");
+ UsageError(" the compiler filter and the profile is not empty, the default compact dex");
+ UsageError(" level is always used.");
UsageError("");
UsageError(" --deduplicate-code=true|false: enable|disable code deduplication. Deduplicated");
UsageError(" code will have an arbitrary symbol tagged with [DEDUPED].");
diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc
index 089aa80ad4..c635d59cd7 100644
--- a/dex2oat/linker/oat_writer.cc
+++ b/dex2oat/linker/oat_writer.cc
@@ -440,6 +440,11 @@ OatWriter::OatWriter(bool compiling_boot_image,
absolute_patch_locations_(),
profile_compilation_info_(info),
compact_dex_level_(compact_dex_level) {
+ // If we have a profile, always use at least the default compact dex level. The reason behind
+ // this is that CompactDex conversion is not more expensive than normal dexlayout.
+ if (info != nullptr && compact_dex_level_ == CompactDexLevel::kCompactDexLevelNone) {
+ compact_dex_level_ = kDefaultCompactDexLevel;
+ }
}
static bool ValidateDexFileHeader(const uint8_t* raw_header, const char* location) {
diff --git a/oatdump/oatdump_test.cc b/oatdump/oatdump_test.cc
index 18cb2fde88..b4eddb91f9 100644
--- a/oatdump/oatdump_test.cc
+++ b/oatdump/oatdump_test.cc
@@ -72,6 +72,8 @@ TEST_F(OatDumpTest, TestSymbolizeStatic) {
}
TEST_F(OatDumpTest, TestExportDex) {
+ // Test is failing on target, b/77469384.
+ TEST_DISABLED_FOR_TARGET();
std::string error_msg;
ASSERT_TRUE(Exec(kDynamic, kModeOat, {"--export-dex-to=" + tmp_dir_}, kListOnly, &error_msg))
<< error_msg;
diff --git a/openjdkjvmti/ti_class.cc b/openjdkjvmti/ti_class.cc
index e55eec4c30..261fe3e810 100644
--- a/openjdkjvmti/ti_class.cc
+++ b/openjdkjvmti/ti_class.cc
@@ -41,6 +41,7 @@
#include "base/macros.h"
#include "base/utils.h"
#include "class_linker.h"
+#include "class_loader_utils.h"
#include "class_table-inl.h"
#include "common_throws.h"
#include "dex/art_dex_file_loader.h"
@@ -942,23 +943,12 @@ jvmtiError ClassUtil::GetClassLoaderClassDescriptors(jvmtiEnv* env,
art::Handle<art::mirror::ClassLoader> class_loader(
hs.NewHandle(soa.Decode<art::mirror::ClassLoader>(loader)));
std::vector<const art::DexFile*> dex_files;
- ClassLoaderHelper::VisitDexFileObjects(
- self,
+ art::VisitClassLoaderDexFiles(
+ soa,
class_loader,
- [&] (art::ObjPtr<art::mirror::Object> dex_file) REQUIRES_SHARED(art::Locks::mutator_lock_) {
- art::StackHandleScope<2> hs(self);
- art::Handle<art::mirror::Object> h_dex_file(hs.NewHandle(dex_file));
- art::Handle<art::mirror::LongArray> cookie(
- hs.NewHandle(ClassLoaderHelper::GetDexFileCookie(h_dex_file)));
- size_t num_elements = cookie->GetLength();
- // We need to skip over the oat_file that's the first element. The other elements are all
- // dex files.
- for (size_t i = 1; i < num_elements; i++) {
- dex_files.push_back(
- reinterpret_cast<const art::DexFile*>(static_cast<uintptr_t>(cookie->Get(i))));
- }
- // Iterate over all dex files.
- return true;
+ [&](const art::DexFile* dex_file) {
+ dex_files.push_back(dex_file);
+ return true; // Continue with other dex files.
});
// We hold the loader so the dex files won't go away until after this call at worst.
return CopyClassDescriptors(env, dex_files, count_ptr, classes);
diff --git a/runtime/Android.bp b/runtime/Android.bp
index c0f1c366b8..08025824c3 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -46,6 +46,7 @@ cc_defaults {
"class_table.cc",
"common_throws.cc",
"compiler_filter.cc",
+ "debug_print.cc",
"debugger.cc",
"dex/art_dex_file_loader.cc",
"dex/dex_file_annotations.cc",
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 879301c4a0..d98e0b2c54 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -53,6 +53,7 @@
#include "class_loader_utils.h"
#include "class_table-inl.h"
#include "compiler_callbacks.h"
+#include "debug_print.h"
#include "debugger.h"
#include "dex/descriptors_names.h"
#include "dex/dex_file-inl.h"
@@ -1124,12 +1125,8 @@ static bool FlattenPathClassLoader(ObjPtr<mirror::ClassLoader> class_loader,
DCHECK(out_dex_file_names != nullptr);
DCHECK(error_msg != nullptr);
ScopedObjectAccessUnchecked soa(Thread::Current());
- ArtField* const dex_path_list_field =
- jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList);
- ArtField* const dex_elements_field =
- jni::DecodeArtField(WellKnownClasses::dalvik_system_DexPathList_dexElements);
- CHECK(dex_path_list_field != nullptr);
- CHECK(dex_elements_field != nullptr);
+ StackHandleScope<1> hs(soa.Self());
+ Handle<mirror::ClassLoader> handle(hs.NewHandle(class_loader));
while (!ClassLinker::IsBootClassLoader(soa, class_loader)) {
if (soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_PathClassLoader) !=
class_loader->GetClass()) {
@@ -1138,32 +1135,29 @@ static bool FlattenPathClassLoader(ObjPtr<mirror::ClassLoader> class_loader,
// Unsupported class loader.
return false;
}
- ObjPtr<mirror::Object> dex_path_list = dex_path_list_field->GetObject(class_loader);
- if (dex_path_list != nullptr) {
- // DexPathList has an array dexElements of Elements[] which each contain a dex file.
- ObjPtr<mirror::Object> dex_elements_obj = dex_elements_field->GetObject(dex_path_list);
- // Loop through each dalvik.system.DexPathList$Element's dalvik.system.DexFile and look
- // at the mCookie which is a DexFile vector.
- if (dex_elements_obj != nullptr) {
- ObjPtr<mirror::ObjectArray<mirror::Object>> dex_elements =
- dex_elements_obj->AsObjectArray<mirror::Object>();
- // Reverse order since we insert the parent at the front.
- for (int32_t i = dex_elements->GetLength() - 1; i >= 0; --i) {
- ObjPtr<mirror::Object> element = dex_elements->GetWithoutChecks(i);
- if (element == nullptr) {
- *error_msg = StringPrintf("Null dex element at index %d", i);
- return false;
- }
- ObjPtr<mirror::String> name;
- if (!GetDexPathListElementName(element, &name)) {
- *error_msg = StringPrintf("Invalid dex path list element at index %d", i);
- return false;
- }
- if (name != nullptr) {
- out_dex_file_names->push_front(name.Ptr());
- }
- }
+ // Get element names. Sets error to true on failure.
+ auto add_element_names = [&](ObjPtr<mirror::Object> element, bool* error)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (element == nullptr) {
+ *error_msg = "Null dex element";
+ *error = true; // Null element is a critical error.
+ return false; // Had an error, stop the visit.
}
+ ObjPtr<mirror::String> name;
+ if (!GetDexPathListElementName(element, &name)) {
+ *error_msg = "Invalid dex path list element";
+ *error = false; // Invalid element is not a critical error.
+ return false; // Stop the visit.
+ }
+ if (name != nullptr) {
+ out_dex_file_names->push_front(name.Ptr());
+ }
+ return true; // Continue with the next Element.
+ };
+ bool error = VisitClassLoaderDexElements(soa, handle, add_element_names, /* error */ false);
+ if (error) {
+ // An error occurred during DexPathList Element visiting.
+ return false;
}
class_loader = class_loader->GetParent();
}
@@ -2473,71 +2467,33 @@ ObjPtr<mirror::Class> ClassLinker::FindClassInBaseDexClassLoaderClassPath(
const char* descriptor,
size_t hash,
Handle<mirror::ClassLoader> class_loader) {
- CHECK(IsPathOrDexClassLoader(soa, class_loader) || IsDelegateLastClassLoader(soa, class_loader))
+ DCHECK(IsPathOrDexClassLoader(soa, class_loader) || IsDelegateLastClassLoader(soa, class_loader))
<< "Unexpected class loader for descriptor " << descriptor;
- Thread* self = soa.Self();
- ArtField* const cookie_field =
- jni::DecodeArtField(WellKnownClasses::dalvik_system_DexFile_cookie);
- ArtField* const dex_file_field =
- jni::DecodeArtField(WellKnownClasses::dalvik_system_DexPathList__Element_dexFile);
- ObjPtr<mirror::Object> dex_path_list =
- jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList)->
- GetObject(class_loader.Get());
- if (dex_path_list != nullptr && dex_file_field != nullptr && cookie_field != nullptr) {
- // DexPathList has an array dexElements of Elements[] which each contain a dex file.
- ObjPtr<mirror::Object> dex_elements_obj =
- jni::DecodeArtField(WellKnownClasses::dalvik_system_DexPathList_dexElements)->
- GetObject(dex_path_list);
- // Loop through each dalvik.system.DexPathList$Element's dalvik.system.DexFile and look
- // at the mCookie which is a DexFile vector.
- if (dex_elements_obj != nullptr) {
- StackHandleScope<1> hs(self);
- Handle<mirror::ObjectArray<mirror::Object>> dex_elements =
- hs.NewHandle(dex_elements_obj->AsObjectArray<mirror::Object>());
- for (int32_t i = 0; i < dex_elements->GetLength(); ++i) {
- ObjPtr<mirror::Object> element = dex_elements->GetWithoutChecks(i);
- if (element == nullptr) {
- // Should never happen, fall back to java code to throw a NPE.
- break;
- }
- ObjPtr<mirror::Object> dex_file = dex_file_field->GetObject(element);
- if (dex_file != nullptr) {
- ObjPtr<mirror::LongArray> long_array = cookie_field->GetObject(dex_file)->AsLongArray();
- if (long_array == nullptr) {
- // This should never happen so log a warning.
- LOG(WARNING) << "Null DexFile::mCookie for " << descriptor;
- break;
- }
- int32_t long_array_size = long_array->GetLength();
- // First element is the oat file.
- for (int32_t j = kDexFileIndexStart; j < long_array_size; ++j) {
- const DexFile* cp_dex_file = reinterpret_cast<const DexFile*>(static_cast<uintptr_t>(
- long_array->GetWithoutChecks(j)));
- const DexFile::ClassDef* dex_class_def =
- OatDexFile::FindClassDef(*cp_dex_file, descriptor, hash);
- if (dex_class_def != nullptr) {
- ObjPtr<mirror::Class> klass = DefineClass(self,
- descriptor,
- hash,
- class_loader,
- *cp_dex_file,
- *dex_class_def);
- if (klass == nullptr) {
- CHECK(self->IsExceptionPending()) << descriptor;
- self->ClearException();
- // TODO: Is it really right to break here, and not check the other dex files?
- return nullptr;
- }
- return klass;
- }
- }
- }
- }
- }
- self->AssertNoPendingException();
- }
- return nullptr;
+ ObjPtr<mirror::Class> ret;
+ auto define_class = [&](const DexFile* cp_dex_file) REQUIRES_SHARED(Locks::mutator_lock_) {
+ const DexFile::ClassDef* dex_class_def =
+ OatDexFile::FindClassDef(*cp_dex_file, descriptor, hash);
+ if (dex_class_def != nullptr) {
+ ObjPtr<mirror::Class> klass = DefineClass(soa.Self(),
+ descriptor,
+ hash,
+ class_loader,
+ *cp_dex_file,
+ *dex_class_def);
+ if (klass == nullptr) {
+ CHECK(soa.Self()->IsExceptionPending()) << descriptor;
+ soa.Self()->ClearException();
+ // TODO: Is it really right to break here, and not check the other dex files?
+ }
+ ret = klass;
+ return false; // Found a Class (or error == nullptr), stop visit.
+ }
+ return true; // Continue with the next DexFile.
+ };
+
+ VisitClassLoaderDexFiles(soa, class_loader, define_class);
+ return ret;
}
mirror::Class* ClassLinker::FindClass(Thread* self,
@@ -7878,106 +7834,6 @@ ObjPtr<mirror::Class> ClassLinker::DoResolveType(dex::TypeIndex type_idx,
return resolved;
}
-std::string DescribeSpace(ObjPtr<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_) {
- std::ostringstream oss;
- gc::Heap* heap = Runtime::Current()->GetHeap();
- gc::space::ContinuousSpace* cs = heap->FindContinuousSpaceFromAddress(klass.Ptr());
- if (cs != nullptr) {
- if (cs->IsImageSpace()) {
- oss << "image;" << cs->GetName() << ";" << cs->AsImageSpace()->GetImageFilename();
- } else {
- oss << "continuous;" << cs->GetName();
- }
- } else {
- gc::space::DiscontinuousSpace* ds =
- heap->FindDiscontinuousSpaceFromObject(klass, /* fail_ok */ true);
- if (ds != nullptr) {
- oss << "discontinuous;" << ds->GetName();
- } else {
- oss << "invalid";
- }
- }
- return oss.str();
-}
-
-std::string DescribeLoaders(ObjPtr<mirror::ClassLoader> loader, const char* class_descriptor)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- std::ostringstream oss;
- uint32_t hash = ComputeModifiedUtf8Hash(class_descriptor);
- ObjPtr<mirror::Class> path_class_loader =
- WellKnownClasses::ToClass(WellKnownClasses::dalvik_system_PathClassLoader);
- ObjPtr<mirror::Class> dex_class_loader =
- WellKnownClasses::ToClass(WellKnownClasses::dalvik_system_DexClassLoader);
- ObjPtr<mirror::Class> delegate_last_class_loader =
- WellKnownClasses::ToClass(WellKnownClasses::dalvik_system_DelegateLastClassLoader);
-
- // Print the class loader chain.
- bool found_class = false;
- const char* loader_separator = "";
- if (loader == nullptr) {
- oss << "BootClassLoader"; // This would be unexpected.
- }
- for (; loader != nullptr; loader = loader->GetParent()) {
- oss << loader_separator << loader->GetClass()->PrettyDescriptor();
- loader_separator = ";";
- // If we didn't find the interface yet, try to find it in the current class loader.
- if (!found_class) {
- ClassTable* table = Runtime::Current()->GetClassLinker()->ClassTableForClassLoader(loader);
- ObjPtr<mirror::Class> klass =
- (table != nullptr) ? table->Lookup(class_descriptor, hash) : nullptr;
- if (klass != nullptr) {
- found_class = true;
- oss << "[hit:" << DescribeSpace(klass) << "]";
- }
- }
-
- // For PathClassLoader, DexClassLoader or DelegateLastClassLoader
- // also dump the dex file locations.
- if (loader->GetClass() == path_class_loader ||
- loader->GetClass() == dex_class_loader ||
- loader->GetClass() == delegate_last_class_loader) {
- ArtField* const cookie_field =
- jni::DecodeArtField(WellKnownClasses::dalvik_system_DexFile_cookie);
- ArtField* const dex_file_field =
- jni::DecodeArtField(WellKnownClasses::dalvik_system_DexPathList__Element_dexFile);
- ObjPtr<mirror::Object> dex_path_list =
- jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList)->
- GetObject(loader);
- if (dex_path_list != nullptr && dex_file_field != nullptr && cookie_field != nullptr) {
- ObjPtr<mirror::Object> dex_elements_obj =
- jni::DecodeArtField(WellKnownClasses::dalvik_system_DexPathList_dexElements)->
- GetObject(dex_path_list);
- if (dex_elements_obj != nullptr) {
- ObjPtr<mirror::ObjectArray<mirror::Object>> dex_elements =
- dex_elements_obj->AsObjectArray<mirror::Object>();
- oss << "(";
- const char* path_separator = "";
- for (int32_t i = 0; i != dex_elements->GetLength(); ++i) {
- ObjPtr<mirror::Object> element = dex_elements->GetWithoutChecks(i);
- ObjPtr<mirror::Object> dex_file =
- (element != nullptr) ? dex_file_field->GetObject(element) : nullptr;
- ObjPtr<mirror::LongArray> long_array =
- (dex_file != nullptr) ? cookie_field->GetObject(dex_file)->AsLongArray() : nullptr;
- if (long_array != nullptr) {
- int32_t long_array_size = long_array->GetLength();
- // First element is the oat file.
- for (int32_t j = kDexFileIndexStart; j < long_array_size; ++j) {
- const DexFile* cp_dex_file = reinterpret_cast<const DexFile*>(
- static_cast<uintptr_t>(long_array->GetWithoutChecks(j)));
- oss << path_separator << cp_dex_file->GetLocation();
- path_separator = ":";
- }
- }
- }
- oss << ")";
- }
- }
- }
- }
-
- return oss.str();
-}
-
ArtMethod* ClassLinker::FindResolvedMethod(ObjPtr<mirror::Class> klass,
ObjPtr<mirror::DexCache> dex_cache,
ObjPtr<mirror::ClassLoader> class_loader,
@@ -7993,8 +7849,8 @@ ArtMethod* ClassLinker::FindResolvedMethod(ObjPtr<mirror::Class> klass,
}
DCHECK(resolved == nullptr || resolved->GetDeclaringClassUnchecked() != nullptr);
if (resolved != nullptr &&
- hiddenapi::ShouldBlockAccessToMember(
- resolved, class_loader, dex_cache, hiddenapi::kLinking)) {
+ hiddenapi::GetMemberAction(
+ resolved, class_loader, dex_cache, hiddenapi::kLinking) == hiddenapi::kDeny) {
resolved = nullptr;
}
if (resolved != nullptr) {
@@ -8017,6 +7873,40 @@ ArtMethod* ClassLinker::FindResolvedMethod(ObjPtr<mirror::Class> klass,
return resolved;
}
+// Returns true if `method` is either null or hidden.
+// Does not print any warnings if it is hidden.
+static bool CheckNoSuchMethod(ArtMethod* method,
+ ObjPtr<mirror::DexCache> dex_cache,
+ ObjPtr<mirror::ClassLoader> class_loader)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ return method == nullptr ||
+ hiddenapi::GetMemberAction(method,
+ class_loader,
+ dex_cache,
+ hiddenapi::kNone) // do not print warnings
+ == hiddenapi::kDeny;
+}
+
+ArtMethod* ClassLinker::FindIncompatibleMethod(ObjPtr<mirror::Class> klass,
+ ObjPtr<mirror::DexCache> dex_cache,
+ ObjPtr<mirror::ClassLoader> class_loader,
+ uint32_t method_idx) {
+ if (klass->IsInterface()) {
+ ArtMethod* method = klass->FindClassMethod(dex_cache, method_idx, image_pointer_size_);
+ return CheckNoSuchMethod(method, dex_cache, class_loader) ? nullptr : method;
+ } else {
+ // If there was an interface method with the same signature, we would have
+ // found it in the "copied" methods. Only DCHECK that the interface method
+ // really does not exist.
+ if (kIsDebugBuild) {
+ ArtMethod* method =
+ klass->FindInterfaceMethod(dex_cache, method_idx, image_pointer_size_);
+ DCHECK(CheckNoSuchMethod(method, dex_cache, class_loader));
+ }
+ return nullptr;
+ }
+}
+
template <ClassLinker::ResolveMode kResolveMode>
ArtMethod* ClassLinker::ResolveMethod(uint32_t method_idx,
Handle<mirror::DexCache> dex_cache,
@@ -8092,13 +7982,7 @@ ArtMethod* ClassLinker::ResolveMethod(uint32_t method_idx,
// If we had a method, or if we can find one with another lookup type,
// it's an incompatible-class-change error.
if (resolved == nullptr) {
- if (klass->IsInterface()) {
- resolved = klass->FindClassMethod(dex_cache.Get(), method_idx, pointer_size);
- } else {
- // If there was an interface method with the same signature,
- // we would have found it also in the "copied" methods.
- DCHECK(klass->FindInterfaceMethod(dex_cache.Get(), method_idx, pointer_size) == nullptr);
- }
+ resolved = FindIncompatibleMethod(klass, dex_cache.Get(), class_loader.Get(), method_idx);
}
if (resolved != nullptr) {
ThrowIncompatibleClassChangeError(type, resolved->GetInvokeType(), resolved, referrer);
@@ -8136,8 +8020,8 @@ ArtMethod* ClassLinker::ResolveMethodWithoutInvokeType(uint32_t method_idx,
resolved = klass->FindClassMethod(dex_cache.Get(), method_idx, image_pointer_size_);
}
if (resolved != nullptr &&
- hiddenapi::ShouldBlockAccessToMember(
- resolved, class_loader.Get(), dex_cache.Get(), hiddenapi::kLinking)) {
+ hiddenapi::GetMemberAction(
+ resolved, class_loader.Get(), dex_cache.Get(), hiddenapi::kLinking) == hiddenapi::kDeny) {
resolved = nullptr;
}
return resolved;
@@ -8216,8 +8100,8 @@ ArtField* ClassLinker::ResolveField(uint32_t field_idx,
}
if (resolved == nullptr ||
- hiddenapi::ShouldBlockAccessToMember(
- resolved, class_loader.Get(), dex_cache.Get(), hiddenapi::kLinking)) {
+ hiddenapi::GetMemberAction(
+ resolved, class_loader.Get(), dex_cache.Get(), hiddenapi::kLinking) == hiddenapi::kDeny) {
const char* name = dex_file.GetFieldName(field_id);
const char* type = dex_file.GetFieldTypeDescriptor(field_id);
ThrowNoSuchFieldError(is_static ? "static " : "instance ", klass, type, name);
@@ -8250,8 +8134,8 @@ ArtField* ClassLinker::ResolveFieldJLS(uint32_t field_idx,
StringPiece type(dex_file.GetFieldTypeDescriptor(field_id));
resolved = mirror::Class::FindField(self, klass, name, type);
if (resolved != nullptr &&
- hiddenapi::ShouldBlockAccessToMember(
- resolved, class_loader.Get(), dex_cache.Get(), hiddenapi::kLinking)) {
+ hiddenapi::GetMemberAction(
+ resolved, class_loader.Get(), dex_cache.Get(), hiddenapi::kLinking) == hiddenapi::kDeny) {
resolved = nullptr;
}
if (resolved != nullptr) {
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 2471f146aa..c46e8271e5 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -321,6 +321,15 @@ class ClassLinker {
uint32_t method_idx)
REQUIRES_SHARED(Locks::mutator_lock_);
+ // Find a method using the wrong lookup mechanism. If `klass` is an interface,
+ // search for a class method. If it is a class, search for an interface method.
+ // This is useful when throwing IncompatibleClassChangeError.
+ ArtMethod* FindIncompatibleMethod(ObjPtr<mirror::Class> klass,
+ ObjPtr<mirror::DexCache> dex_cache,
+ ObjPtr<mirror::ClassLoader> class_loader,
+ uint32_t method_idx)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
// Resolve a method with a given ID from the DexFile associated with the given DexCache
// and ClassLoader, storing the result in DexCache. The ClassLinker and ClassLoader are
// used as in ResolveType. What is unique is the method type argument which is used to
diff --git a/runtime/class_loader_utils.h b/runtime/class_loader_utils.h
index d160a511de..1439f11636 100644
--- a/runtime/class_loader_utils.h
+++ b/runtime/class_loader_utils.h
@@ -17,8 +17,12 @@
#ifndef ART_RUNTIME_CLASS_LOADER_UTILS_H_
#define ART_RUNTIME_CLASS_LOADER_UTILS_H_
+#include "art_field-inl.h"
+#include "base/mutex.h"
#include "handle_scope.h"
+#include "jni_internal.h"
#include "mirror/class_loader.h"
+#include "native/dalvik_system_DexFile.h"
#include "scoped_thread_state_change-inl.h"
#include "well_known_classes.h"
@@ -26,7 +30,7 @@ namespace art {
// Returns true if the given class loader is either a PathClassLoader or a DexClassLoader.
// (they both have the same behaviour with respect to class lockup order)
-static bool IsPathOrDexClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
+inline bool IsPathOrDexClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
Handle<mirror::ClassLoader> class_loader)
REQUIRES_SHARED(Locks::mutator_lock_) {
mirror::Class* class_loader_class = class_loader->GetClass();
@@ -37,7 +41,7 @@ static bool IsPathOrDexClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_DexClassLoader));
}
-static bool IsDelegateLastClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
+inline bool IsDelegateLastClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
Handle<mirror::ClassLoader> class_loader)
REQUIRES_SHARED(Locks::mutator_lock_) {
mirror::Class* class_loader_class = class_loader->GetClass();
@@ -45,6 +49,114 @@ static bool IsDelegateLastClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_DelegateLastClassLoader);
}
+// Visit the DexPathList$Element instances in the given classloader with the given visitor.
+// Constraints on the visitor:
+// * The visitor should return true to continue visiting more Elements.
+// * The last argument of the visitor is an out argument of RetType. It will be returned
+// when the visitor ends the visit (by returning false).
+// This function assumes that the given classloader is a subclass of BaseDexClassLoader!
+template <typename Visitor, typename RetType>
+inline RetType VisitClassLoaderDexElements(ScopedObjectAccessAlreadyRunnable& soa,
+ Handle<mirror::ClassLoader> class_loader,
+ Visitor fn,
+ RetType defaultReturn)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ Thread* self = soa.Self();
+ ObjPtr<mirror::Object> dex_path_list =
+ jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList)->
+ GetObject(class_loader.Get());
+ if (dex_path_list != nullptr) {
+ // DexPathList has an array dexElements of Elements[] which each contain a dex file.
+ ObjPtr<mirror::Object> dex_elements_obj =
+ jni::DecodeArtField(WellKnownClasses::dalvik_system_DexPathList_dexElements)->
+ GetObject(dex_path_list);
+ // Loop through each dalvik.system.DexPathList$Element's dalvik.system.DexFile and look
+ // at the mCookie which is a DexFile vector.
+ if (dex_elements_obj != nullptr) {
+ StackHandleScope<1> hs(self);
+ Handle<mirror::ObjectArray<mirror::Object>> dex_elements =
+ hs.NewHandle(dex_elements_obj->AsObjectArray<mirror::Object>());
+ for (int32_t i = 0; i < dex_elements->GetLength(); ++i) {
+ ObjPtr<mirror::Object> element = dex_elements->GetWithoutChecks(i);
+ if (element == nullptr) {
+ // Should never happen, fail.
+ break;
+ }
+ RetType ret_value;
+ if (!fn(element, &ret_value)) {
+ return ret_value;
+ }
+ }
+ }
+ self->AssertNoPendingException();
+ }
+ return defaultReturn;
+}
+
+// Visit the DexFiles in the given classloader with the given visitor.
+// Constraints on the visitor:
+// * The visitor should return true to continue visiting more DexFiles.
+// * The last argument of the visitor is an out argument of RetType. It will be returned
+// when the visitor ends the visit (by returning false).
+// This function assumes that the given classloader is a subclass of BaseDexClassLoader!
+template <typename Visitor, typename RetType>
+inline RetType VisitClassLoaderDexFiles(ScopedObjectAccessAlreadyRunnable& soa,
+ Handle<mirror::ClassLoader> class_loader,
+ Visitor fn,
+ RetType defaultReturn)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ ArtField* const cookie_field =
+ jni::DecodeArtField(WellKnownClasses::dalvik_system_DexFile_cookie);
+ ArtField* const dex_file_field =
+ jni::DecodeArtField(WellKnownClasses::dalvik_system_DexPathList__Element_dexFile);
+ if (dex_file_field == nullptr || cookie_field == nullptr) {
+ return defaultReturn;
+ }
+ auto visit_dex_files = [&](ObjPtr<mirror::Object> element, RetType* ret)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ ObjPtr<mirror::Object> dex_file = dex_file_field->GetObject(element);
+ if (dex_file != nullptr) {
+ ObjPtr<mirror::LongArray> long_array = cookie_field->GetObject(dex_file)->AsLongArray();
+ if (long_array == nullptr) {
+ // This should never happen so log a warning.
+ LOG(WARNING) << "Null DexFile::mCookie";
+ *ret = defaultReturn;
+ return true;
+ }
+ int32_t long_array_size = long_array->GetLength();
+ // First element is the oat file.
+ for (int32_t j = kDexFileIndexStart; j < long_array_size; ++j) {
+ const DexFile* cp_dex_file = reinterpret_cast<const DexFile*>(static_cast<uintptr_t>(
+ long_array->GetWithoutChecks(j)));
+ RetType ret_value;
+ if (!fn(cp_dex_file, /* out */ &ret_value)) {
+ *ret = ret_value;
+ return false;
+ }
+ }
+ }
+ return true;
+ };
+
+ return VisitClassLoaderDexElements(soa, class_loader, visit_dex_files, defaultReturn);
+}
+
+// Simplified version of the above, w/o out argument.
+template <typename Visitor>
+inline void VisitClassLoaderDexFiles(ScopedObjectAccessAlreadyRunnable& soa,
+ Handle<mirror::ClassLoader> class_loader,
+ Visitor fn)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ auto helper = [&fn](const art::DexFile* dex_file, void** ATTRIBUTE_UNUSED)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ return fn(dex_file);
+ };
+ VisitClassLoaderDexFiles<decltype(helper), void*>(soa,
+ class_loader,
+ helper,
+ /* default */ nullptr);
+}
+
} // namespace art
#endif // ART_RUNTIME_CLASS_LOADER_UTILS_H_
diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc
index add300bdf3..05159e253d 100644
--- a/runtime/common_runtime_test.cc
+++ b/runtime/common_runtime_test.cc
@@ -36,6 +36,7 @@
#include "base/stl_util.h"
#include "base/unix_file/fd_file.h"
#include "class_linker.h"
+#include "class_loader_utils.h"
#include "compiler_callbacks.h"
#include "dex/art_dex_file_loader.h"
#include "dex/dex_file-inl.h"
@@ -543,58 +544,23 @@ std::vector<const DexFile*> CommonRuntimeTestImpl::GetDexFiles(jobject jclass_lo
std::vector<const DexFile*> CommonRuntimeTestImpl::GetDexFiles(
ScopedObjectAccess& soa,
Handle<mirror::ClassLoader> class_loader) {
- std::vector<const DexFile*> ret;
-
DCHECK(
(class_loader->GetClass() ==
soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_PathClassLoader)) ||
(class_loader->GetClass() ==
soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_DelegateLastClassLoader)));
- // The class loader is a PathClassLoader which inherits from BaseDexClassLoader.
- // We need to get the DexPathList and loop through it.
- ArtField* cookie_field = jni::DecodeArtField(WellKnownClasses::dalvik_system_DexFile_cookie);
- ArtField* dex_file_field =
- jni::DecodeArtField(WellKnownClasses::dalvik_system_DexPathList__Element_dexFile);
- ObjPtr<mirror::Object> dex_path_list =
- jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList)->
- GetObject(class_loader.Get());
- if (dex_path_list != nullptr && dex_file_field!= nullptr && cookie_field != nullptr) {
- // DexPathList has an array dexElements of Elements[] which each contain a dex file.
- ObjPtr<mirror::Object> dex_elements_obj =
- jni::DecodeArtField(WellKnownClasses::dalvik_system_DexPathList_dexElements)->
- GetObject(dex_path_list);
- // Loop through each dalvik.system.DexPathList$Element's dalvik.system.DexFile and look
- // at the mCookie which is a DexFile vector.
- if (dex_elements_obj != nullptr) {
- StackHandleScope<1> hs(soa.Self());
- Handle<mirror::ObjectArray<mirror::Object>> dex_elements =
- hs.NewHandle(dex_elements_obj->AsObjectArray<mirror::Object>());
- for (int32_t i = 0; i < dex_elements->GetLength(); ++i) {
- ObjPtr<mirror::Object> element = dex_elements->GetWithoutChecks(i);
- if (element == nullptr) {
- // Should never happen, fall back to java code to throw a NPE.
- break;
- }
- ObjPtr<mirror::Object> dex_file = dex_file_field->GetObject(element);
- if (dex_file != nullptr) {
- ObjPtr<mirror::LongArray> long_array = cookie_field->GetObject(dex_file)->AsLongArray();
- DCHECK(long_array != nullptr);
- int32_t long_array_size = long_array->GetLength();
- for (int32_t j = kDexFileIndexStart; j < long_array_size; ++j) {
- const DexFile* cp_dex_file = reinterpret_cast<const DexFile*>(static_cast<uintptr_t>(
- long_array->GetWithoutChecks(j)));
- if (cp_dex_file == nullptr) {
- LOG(WARNING) << "Null DexFile";
- continue;
- }
- ret.push_back(cp_dex_file);
- }
- }
- }
- }
- }
-
+ std::vector<const DexFile*> ret;
+ VisitClassLoaderDexFiles(soa,
+ class_loader,
+ [&](const DexFile* cp_dex_file) {
+ if (cp_dex_file == nullptr) {
+ LOG(WARNING) << "Null DexFile";
+ } else {
+ ret.push_back(cp_dex_file);
+ }
+ return true;
+ });
return ret;
}
diff --git a/runtime/debug_print.cc b/runtime/debug_print.cc
new file mode 100644
index 0000000000..44a183669d
--- /dev/null
+++ b/runtime/debug_print.cc
@@ -0,0 +1,113 @@
+/*
+ * 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 <sstream>
+
+#include "debug_print.h"
+
+#include "class_linker.h"
+#include "class_table.h"
+#include "class_loader_utils.h"
+#include "dex/utf.h"
+#include "gc/heap.h"
+#include "gc/space/space-inl.h"
+#include "mirror/class.h"
+#include "mirror/class_loader.h"
+#include "runtime.h"
+#include "scoped_thread_state_change-inl.h"
+#include "thread-current-inl.h"
+#include "well_known_classes.h"
+
+namespace art {
+
+std::string DescribeSpace(ObjPtr<mirror::Class> klass) {
+ std::ostringstream oss;
+ gc::Heap* heap = Runtime::Current()->GetHeap();
+ gc::space::ContinuousSpace* cs =
+ heap->FindContinuousSpaceFromObject(klass.Ptr(), /* fail_ok */ true);
+ if (cs != nullptr) {
+ if (cs->IsImageSpace()) {
+ oss << "image;" << cs->GetName() << ";" << cs->AsImageSpace()->GetImageFilename();
+ } else {
+ oss << "continuous;" << cs->GetName();
+ }
+ } else {
+ gc::space::DiscontinuousSpace* ds =
+ heap->FindDiscontinuousSpaceFromObject(klass, /* fail_ok */ true);
+ if (ds != nullptr) {
+ oss << "discontinuous;" << ds->GetName();
+ } else {
+ oss << "invalid";
+ }
+ }
+ return oss.str();
+}
+
+std::string DescribeLoaders(ObjPtr<mirror::ClassLoader> loader, const char* class_descriptor) {
+ std::ostringstream oss;
+ uint32_t hash = ComputeModifiedUtf8Hash(class_descriptor);
+ ObjPtr<mirror::Class> path_class_loader =
+ WellKnownClasses::ToClass(WellKnownClasses::dalvik_system_PathClassLoader);
+ ObjPtr<mirror::Class> dex_class_loader =
+ WellKnownClasses::ToClass(WellKnownClasses::dalvik_system_DexClassLoader);
+ ObjPtr<mirror::Class> delegate_last_class_loader =
+ WellKnownClasses::ToClass(WellKnownClasses::dalvik_system_DelegateLastClassLoader);
+
+ // Print the class loader chain.
+ bool found_class = false;
+ const char* loader_separator = "";
+ if (loader == nullptr) {
+ oss << "BootClassLoader"; // This would be unexpected.
+ }
+ for (; loader != nullptr; loader = loader->GetParent()) {
+ oss << loader_separator << loader->GetClass()->PrettyDescriptor();
+ loader_separator = ";";
+ // If we didn't find the class yet, try to find it in the current class loader.
+ if (!found_class) {
+ ClassTable* table = Runtime::Current()->GetClassLinker()->ClassTableForClassLoader(loader);
+ ObjPtr<mirror::Class> klass =
+ (table != nullptr) ? table->Lookup(class_descriptor, hash) : nullptr;
+ if (klass != nullptr) {
+ found_class = true;
+ oss << "[hit:" << DescribeSpace(klass) << "]";
+ }
+ }
+
+ // For PathClassLoader, DexClassLoader or DelegateLastClassLoader
+ // also dump the dex file locations.
+ if (loader->GetClass() == path_class_loader ||
+ loader->GetClass() == dex_class_loader ||
+ loader->GetClass() == delegate_last_class_loader) {
+ oss << "(";
+ ScopedObjectAccessUnchecked soa(Thread::Current());
+ StackHandleScope<1> hs(soa.Self());
+ Handle<mirror::ClassLoader> handle(hs.NewHandle(loader));
+ const char* path_separator = "";
+ VisitClassLoaderDexFiles(soa,
+ handle,
+ [&](const DexFile* dex_file) {
+ oss << path_separator << dex_file->GetLocation();
+ path_separator = ":";
+ return true; // Continue with the next DexFile.
+ });
+ oss << ")";
+ }
+ }
+
+ return oss.str();
+}
+
+} // namespace art
diff --git a/runtime/debug_print.h b/runtime/debug_print.h
new file mode 100644
index 0000000000..479c36a02f
--- /dev/null
+++ b/runtime/debug_print.h
@@ -0,0 +1,34 @@
+/*
+ * 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_RUNTIME_DEBUG_PRINT_H_
+#define ART_RUNTIME_DEBUG_PRINT_H_
+
+#include "base/mutex.h"
+#include "mirror/object.h"
+
+// Helper functions for printing extra information for certain hard to diagnose bugs.
+
+namespace art {
+
+std::string DescribeSpace(ObjPtr<mirror::Class> klass)
+ REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR;
+std::string DescribeLoaders(ObjPtr<mirror::ClassLoader> loader, const char* class_descriptor)
+ REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR;
+
+} // namespace art
+
+#endif // ART_RUNTIME_DEBUG_PRINT_H_
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index 2eb3ab923c..66cec7113e 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -19,6 +19,7 @@
#include "base/enums.h"
#include "callee_save_frame.h"
#include "common_throws.h"
+#include "debug_print.h"
#include "debugger.h"
#include "dex/dex_file-inl.h"
#include "dex/dex_file_types.h"
@@ -1187,9 +1188,24 @@ static std::string DumpInstruction(ArtMethod* method, uint32_t dex_pc)
}
}
+static void DumpB74410240ClassData(ObjPtr<mirror::Class> klass)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ std::string storage;
+ const char* descriptor = klass->GetDescriptor(&storage);
+ LOG(FATAL_WITHOUT_ABORT) << " " << DescribeLoaders(klass->GetClassLoader(), descriptor);
+ const OatDexFile* oat_dex_file = klass->GetDexFile().GetOatDexFile();
+ if (oat_dex_file != nullptr) {
+ const OatFile* oat_file = oat_dex_file->GetOatFile();
+ const char* dex2oat_cmdline =
+ oat_file->GetOatHeader().GetStoreValueByKey(OatHeader::kDex2OatCmdLineKey);
+ LOG(FATAL_WITHOUT_ABORT) << " OatFile: " << oat_file->GetLocation()
+ << "; " << (dex2oat_cmdline != nullptr ? dex2oat_cmdline : "<not recorded>");
+ }
+}
+
static void DumpB74410240DebugData(ArtMethod** sp) REQUIRES_SHARED(Locks::mutator_lock_) {
// Mimick the search for the caller and dump some data while doing so.
- LOG(FATAL_WITHOUT_ABORT) << "Dumping debugging data for b/74410240.";
+ LOG(FATAL_WITHOUT_ABORT) << "Dumping debugging data, please attach a bugreport to b/74410240.";
constexpr CalleeSaveType type = CalleeSaveType::kSaveRefsAndArgs;
CHECK_EQ(*sp, Runtime::Current()->GetCalleeSaveMethod(type));
@@ -1227,6 +1243,7 @@ static void DumpB74410240DebugData(ArtMethod** sp) REQUIRES_SHARED(Locks::mutato
<< " dex pc: " << dex_pc
<< " dex file: " << outer_method->GetDexFile()->GetLocation()
<< " class table: " << class_linker->ClassTableForClassLoader(outer_method->GetClassLoader());
+ DumpB74410240ClassData(outer_method->GetDeclaringClass());
LOG(FATAL_WITHOUT_ABORT) << " instruction: " << DumpInstruction(outer_method, dex_pc);
ArtMethod* caller = outer_method;
@@ -1260,7 +1277,8 @@ static void DumpB74410240DebugData(ArtMethod** sp) REQUIRES_SHARED(Locks::mutato
<< " dex pc: " << dex_pc
<< " dex file: " << caller->GetDexFile()->GetLocation()
<< " class table: "
- << class_linker->ClassTableForClassLoader(outer_method->GetClassLoader());
+ << class_linker->ClassTableForClassLoader(caller->GetClassLoader());
+ DumpB74410240ClassData(caller->GetDeclaringClass());
LOG(FATAL_WITHOUT_ABORT) << " instruction: " << DumpInstruction(caller, dex_pc);
}
}
diff --git a/runtime/hidden_api.cc b/runtime/hidden_api.cc
index f0b36a090a..02b4f5349d 100644
--- a/runtime/hidden_api.cc
+++ b/runtime/hidden_api.cc
@@ -16,13 +16,20 @@
#include "hidden_api.h"
+#include <nativehelper/scoped_local_ref.h>
+
#include "base/dumpable.h"
+#include "thread-current-inl.h"
+#include "well_known_classes.h"
namespace art {
namespace hiddenapi {
static inline std::ostream& operator<<(std::ostream& os, AccessMethod value) {
switch (value) {
+ case kNone:
+ LOG(FATAL) << "Internal access to hidden API should not be logged";
+ UNREACHABLE();
case kReflection:
os << "reflection";
break;
@@ -42,12 +49,11 @@ static constexpr bool EnumsEqual(EnforcementPolicy policy, HiddenApiAccessFlags:
// GetMemberAction-related static_asserts.
static_assert(
- EnumsEqual(EnforcementPolicy::kAllLists, HiddenApiAccessFlags::kLightGreylist) &&
EnumsEqual(EnforcementPolicy::kDarkGreyAndBlackList, HiddenApiAccessFlags::kDarkGreylist) &&
EnumsEqual(EnforcementPolicy::kBlacklistOnly, HiddenApiAccessFlags::kBlacklist),
"Mismatch between EnforcementPolicy and ApiList enums");
static_assert(
- EnforcementPolicy::kAllLists < EnforcementPolicy::kDarkGreyAndBlackList &&
+ EnforcementPolicy::kJustWarn < EnforcementPolicy::kDarkGreyAndBlackList &&
EnforcementPolicy::kDarkGreyAndBlackList < EnforcementPolicy::kBlacklistOnly,
"EnforcementPolicy values ordering not correct");
@@ -111,62 +117,104 @@ void MemberSignature::WarnAboutAccess(AccessMethod access_method,
}
template<typename T>
-bool ShouldBlockAccessToMemberImpl(T* member, Action action, AccessMethod access_method) {
+Action GetMemberActionImpl(T* member, Action action, AccessMethod access_method) {
+ DCHECK_NE(action, kAllow);
+
// Get the signature, we need it later.
MemberSignature member_signature(member);
Runtime* runtime = Runtime::Current();
- if (action == kDeny) {
- // If we were about to deny, check for an exemption first.
- // Exempted APIs are treated as light grey list.
+ // Check for an exemption first. Exempted APIs are treated as white list.
+ // We only do this if we're about to deny, or if the app is debuggable. This is because:
+ // - we only print a warning for light greylist violations for debuggable apps
+ // - for non-debuggable apps, there is no distinction between light grey & whitelisted APIs.
+ // - we want to avoid the overhead of checking for exemptions for light greylisted APIs whenever
+ // possible.
+ if (action == kDeny || runtime->IsJavaDebuggable()) {
if (member_signature.IsExempted(runtime->GetHiddenApiExemptions())) {
- action = kAllowButWarn;
+ action = kAllow;
// Avoid re-examining the exemption list next time.
- // Note this results in the warning below showing "light greylist", which
- // seems like what one would expect. Exemptions effectively add new members to
- // the light greylist.
+ // Note this results in no warning for the member, which seems like what one would expect.
+ // Exemptions effectively adds new members to the whitelist.
member->SetAccessFlags(HiddenApiAccessFlags::EncodeForRuntime(
- member->GetAccessFlags(), HiddenApiAccessFlags::kLightGreylist));
+ member->GetAccessFlags(), HiddenApiAccessFlags::kWhitelist));
+ return kAllow;
}
- }
- // Print a log message with information about this class member access.
- // We do this regardless of whether we block the access or not.
- member_signature.WarnAboutAccess(access_method,
- HiddenApiAccessFlags::DecodeFromRuntime(member->GetAccessFlags()));
+ if (access_method != kNone) {
+ // Print a log message with information about this class member access.
+ // We do this if we're about to block access, or the app is debuggable.
+ member_signature.WarnAboutAccess(access_method,
+ HiddenApiAccessFlags::DecodeFromRuntime(member->GetAccessFlags()));
+ }
+ }
if (action == kDeny) {
// Block access
- return true;
+ return action;
}
// Allow access to this member but print a warning.
DCHECK(action == kAllowButWarn || action == kAllowButWarnAndToast);
- // Depending on a runtime flag, we might move the member into whitelist and
- // skip the warning the next time the member is accessed.
- if (runtime->ShouldDedupeHiddenApiWarnings()) {
- member->SetAccessFlags(HiddenApiAccessFlags::EncodeForRuntime(
- member->GetAccessFlags(), HiddenApiAccessFlags::kWhitelist));
- }
+ if (access_method != kNone) {
+ // Depending on a runtime flag, we might move the member into whitelist and
+ // skip the warning the next time the member is accessed.
+ if (runtime->ShouldDedupeHiddenApiWarnings()) {
+ member->SetAccessFlags(HiddenApiAccessFlags::EncodeForRuntime(
+ member->GetAccessFlags(), HiddenApiAccessFlags::kWhitelist));
+ }
- // If this action requires a UI warning, set the appropriate flag.
- if (action == kAllowButWarnAndToast || runtime->ShouldAlwaysSetHiddenApiWarningFlag()) {
- runtime->SetPendingHiddenApiWarning(true);
+ // If this action requires a UI warning, set the appropriate flag.
+ if (action == kAllowButWarnAndToast || runtime->ShouldAlwaysSetHiddenApiWarningFlag()) {
+ runtime->SetPendingHiddenApiWarning(true);
+ }
}
- return false;
+ return action;
}
// Need to instantiate this.
-template bool ShouldBlockAccessToMemberImpl<ArtField>(ArtField* member,
- Action action,
- AccessMethod access_method);
-template bool ShouldBlockAccessToMemberImpl<ArtMethod>(ArtMethod* member,
- Action action,
- AccessMethod access_method);
-
+template Action GetMemberActionImpl<ArtField>(ArtField* member,
+ Action action,
+ AccessMethod access_method);
+template Action GetMemberActionImpl<ArtMethod>(ArtMethod* member,
+ Action action,
+ AccessMethod access_method);
} // namespace detail
+
+template<typename T>
+void NotifyHiddenApiListener(T* member) {
+ Runtime* runtime = Runtime::Current();
+ if (!runtime->IsAotCompiler()) {
+ ScopedObjectAccessUnchecked soa(Thread::Current());
+
+ ScopedLocalRef<jobject> consumer_object(soa.Env(),
+ soa.Env()->GetStaticObjectField(
+ WellKnownClasses::dalvik_system_VMRuntime,
+ WellKnownClasses::dalvik_system_VMRuntime_nonSdkApiUsageConsumer));
+ // If the consumer is non-null, we call back to it to let it know that we
+ // have encountered an API that's in one of our lists.
+ if (consumer_object != nullptr) {
+ detail::MemberSignature member_signature(member);
+ std::ostringstream member_signature_str;
+ member_signature.Dump(member_signature_str);
+
+ ScopedLocalRef<jobject> signature_str(
+ soa.Env(),
+ soa.Env()->NewStringUTF(member_signature_str.str().c_str()));
+
+ // Call through to Consumer.accept(String memberSignature);
+ soa.Env()->CallVoidMethod(consumer_object.get(),
+ WellKnownClasses::java_util_function_Consumer_accept,
+ signature_str.get());
+ }
+ }
+}
+
+template void NotifyHiddenApiListener<ArtMethod>(ArtMethod* member);
+template void NotifyHiddenApiListener<ArtField>(ArtField* member);
+
} // namespace hiddenapi
} // namespace art
diff --git a/runtime/hidden_api.h b/runtime/hidden_api.h
index cc6c146f00..d2c71a7d35 100644
--- a/runtime/hidden_api.h
+++ b/runtime/hidden_api.h
@@ -33,7 +33,7 @@ namespace hiddenapi {
// frameworks/base/core/java/android/content/pm/ApplicationInfo.java
enum class EnforcementPolicy {
kNoChecks = 0,
- kAllLists = 1, // ban anything but whitelist
+ kJustWarn = 1, // keep checks enabled, but allow everything (enables logging)
kDarkGreyAndBlackList = 2, // ban dark grey & blacklist
kBlacklistOnly = 3, // ban blacklist violations only
kMax = kBlacklistOnly,
@@ -53,12 +53,13 @@ enum Action {
};
enum AccessMethod {
+ kNone, // internal test that does not correspond to an actual access by app
kReflection,
kJNI,
kLinking,
};
-inline Action GetMemberAction(uint32_t access_flags) {
+inline Action GetActionFromAccessFlags(uint32_t access_flags) {
EnforcementPolicy policy = Runtime::Current()->GetHiddenApiEnforcementPolicy();
if (policy == EnforcementPolicy::kNoChecks) {
// Exit early. Nothing to enforce.
@@ -69,6 +70,11 @@ inline Action GetMemberAction(uint32_t access_flags) {
if (api_list == HiddenApiAccessFlags::kWhitelist) {
return kAllow;
}
+ // if policy is "just warn", always warn. We returned above for whitelist APIs.
+ if (policy == EnforcementPolicy::kJustWarn) {
+ return kAllowButWarn;
+ }
+ DCHECK(policy >= EnforcementPolicy::kDarkGreyAndBlackList);
// The logic below relies on equality of values in the enums EnforcementPolicy and
// HiddenApiAccessFlags::ApiList, and their ordering. Assertions are in hidden_api.cc.
if (static_cast<int>(policy) > static_cast<int>(api_list)) {
@@ -108,9 +114,7 @@ class MemberSignature {
};
template<typename T>
-bool ShouldBlockAccessToMemberImpl(T* member,
- Action action,
- AccessMethod access_method)
+Action GetMemberActionImpl(T* member, Action action, AccessMethod access_method)
REQUIRES_SHARED(Locks::mutator_lock_);
// Returns true if the caller is either loaded by the boot strap class loader or comes from
@@ -138,28 +142,28 @@ inline bool IsCallerInPlatformDex(ObjPtr<mirror::ClassLoader> caller_class_loade
// return true if the caller is located in the platform.
// This function might print warnings into the log if the member is hidden.
template<typename T>
-inline bool ShouldBlockAccessToMember(T* member,
- Thread* self,
- std::function<bool(Thread*)> fn_caller_in_platform,
- AccessMethod access_method)
+inline Action GetMemberAction(T* member,
+ Thread* self,
+ std::function<bool(Thread*)> fn_caller_in_platform,
+ AccessMethod access_method)
REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK(member != nullptr);
- Action action = GetMemberAction(member->GetAccessFlags());
+ Action action = GetActionFromAccessFlags(member->GetAccessFlags());
if (action == kAllow) {
// Nothing to do.
- return false;
+ return action;
}
// Member is hidden. Invoke `fn_caller_in_platform` and find the origin of the access.
// This can be *very* expensive. Save it for last.
if (fn_caller_in_platform(self)) {
// Caller in the platform. Exit.
- return false;
+ return kAllow;
}
// Member is hidden and caller is not in the platform.
- return detail::ShouldBlockAccessToMemberImpl(member, action, access_method);
+ return detail::GetMemberActionImpl(member, action, access_method);
}
inline bool IsCallerInPlatformDex(ObjPtr<mirror::Class> caller)
@@ -172,18 +176,26 @@ inline bool IsCallerInPlatformDex(ObjPtr<mirror::Class> caller)
// `caller_class_loader`.
// This function might print warnings into the log if the member is hidden.
template<typename T>
-inline bool ShouldBlockAccessToMember(T* member,
- ObjPtr<mirror::ClassLoader> caller_class_loader,
- ObjPtr<mirror::DexCache> caller_dex_cache,
- AccessMethod access_method)
+inline Action GetMemberAction(T* member,
+ ObjPtr<mirror::ClassLoader> caller_class_loader,
+ ObjPtr<mirror::DexCache> caller_dex_cache,
+ AccessMethod access_method)
REQUIRES_SHARED(Locks::mutator_lock_) {
bool caller_in_platform = detail::IsCallerInPlatformDex(caller_class_loader, caller_dex_cache);
- return ShouldBlockAccessToMember(member,
- /* thread */ nullptr,
- [caller_in_platform] (Thread*) { return caller_in_platform; },
- access_method);
+ return GetMemberAction(member,
+ /* thread */ nullptr,
+ [caller_in_platform] (Thread*) { return caller_in_platform; },
+ access_method);
}
+// Calls back into managed code to notify VMRuntime.nonSdkApiUsageConsumer that
+// |member| was accessed. This is usually called when an API is on the black,
+// dark grey or light grey lists. Given that the callback can execute arbitrary
+// code, a call to this method can result in thread suspension.
+template<typename T> void NotifyHiddenApiListener(T* member)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+
} // namespace hiddenapi
} // namespace art
diff --git a/runtime/hidden_api_test.cc b/runtime/hidden_api_test.cc
index 5a31dd4972..65d6363bfd 100644
--- a/runtime/hidden_api_test.cc
+++ b/runtime/hidden_api_test.cc
@@ -22,6 +22,7 @@
namespace art {
using hiddenapi::detail::MemberSignature;
+using hiddenapi::GetActionFromAccessFlags;
class HiddenApiTest : public CommonRuntimeTest {
protected:
@@ -84,6 +85,39 @@ class HiddenApiTest : public CommonRuntimeTest {
ArtMethod* class3_method1_i_;
};
+TEST_F(HiddenApiTest, CheckGetActionFromRuntimeFlags) {
+ uint32_t whitelist = HiddenApiAccessFlags::EncodeForRuntime(0, HiddenApiAccessFlags::kWhitelist);
+ uint32_t lightgreylist =
+ HiddenApiAccessFlags::EncodeForRuntime(0, HiddenApiAccessFlags::kLightGreylist);
+ uint32_t darkgreylist =
+ HiddenApiAccessFlags::EncodeForRuntime(0, HiddenApiAccessFlags::kDarkGreylist);
+ uint32_t blacklist = HiddenApiAccessFlags::EncodeForRuntime(0, HiddenApiAccessFlags::kBlacklist);
+
+ runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kNoChecks);
+ ASSERT_EQ(GetActionFromAccessFlags(whitelist), hiddenapi::kAllow);
+ ASSERT_EQ(GetActionFromAccessFlags(lightgreylist), hiddenapi::kAllow);
+ ASSERT_EQ(GetActionFromAccessFlags(darkgreylist), hiddenapi::kAllow);
+ ASSERT_EQ(GetActionFromAccessFlags(blacklist), hiddenapi::kAllow);
+
+ runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kJustWarn);
+ ASSERT_EQ(GetActionFromAccessFlags(whitelist), hiddenapi::kAllow);
+ ASSERT_EQ(GetActionFromAccessFlags(lightgreylist), hiddenapi::kAllowButWarn);
+ ASSERT_EQ(GetActionFromAccessFlags(darkgreylist), hiddenapi::kAllowButWarn);
+ ASSERT_EQ(GetActionFromAccessFlags(blacklist), hiddenapi::kAllowButWarn);
+
+ runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kDarkGreyAndBlackList);
+ ASSERT_EQ(GetActionFromAccessFlags(whitelist), hiddenapi::kAllow);
+ ASSERT_EQ(GetActionFromAccessFlags(lightgreylist), hiddenapi::kAllowButWarn);
+ ASSERT_EQ(GetActionFromAccessFlags(darkgreylist), hiddenapi::kDeny);
+ ASSERT_EQ(GetActionFromAccessFlags(blacklist), hiddenapi::kDeny);
+
+ runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kBlacklistOnly);
+ ASSERT_EQ(GetActionFromAccessFlags(whitelist), hiddenapi::kAllow);
+ ASSERT_EQ(GetActionFromAccessFlags(lightgreylist), hiddenapi::kAllowButWarn);
+ ASSERT_EQ(GetActionFromAccessFlags(darkgreylist), hiddenapi::kAllowButWarnAndToast);
+ ASSERT_EQ(GetActionFromAccessFlags(blacklist), hiddenapi::kDeny);
+}
+
TEST_F(HiddenApiTest, CheckMembersRead) {
ASSERT_NE(nullptr, class1_field1_);
ASSERT_NE(nullptr, class1_field12_);
diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc
index 4a2dd3bc3f..6e920363e8 100644
--- a/runtime/interpreter/unstarted_runtime.cc
+++ b/runtime/interpreter/unstarted_runtime.cc
@@ -181,11 +181,13 @@ static mirror::String* GetClassName(Thread* self, ShadowFrame* shadow_frame, siz
template<typename T>
static ALWAYS_INLINE bool ShouldBlockAccessToMember(T* member, ShadowFrame* frame)
REQUIRES_SHARED(Locks::mutator_lock_) {
- return hiddenapi::ShouldBlockAccessToMember(
+ // All uses in this file are from reflection
+ constexpr hiddenapi::AccessMethod access_method = hiddenapi::kReflection;
+ return hiddenapi::GetMemberAction(
member,
frame->GetMethod()->GetDeclaringClass()->GetClassLoader(),
frame->GetMethod()->GetDeclaringClass()->GetDexCache(),
- hiddenapi::kReflection); // all uses in this file are from reflection
+ access_method) == hiddenapi::kDeny;
}
void UnstartedRuntime::UnstartedClassForNameCommon(Thread* self,
diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc
index f309581735..9dbcded867 100644
--- a/runtime/jni_internal.cc
+++ b/runtime/jni_internal.cc
@@ -87,8 +87,13 @@ static bool IsCallerInPlatformDex(Thread* self) REQUIRES_SHARED(Locks::mutator_l
template<typename T>
ALWAYS_INLINE static bool ShouldBlockAccessToMember(T* member, Thread* self)
REQUIRES_SHARED(Locks::mutator_lock_) {
- return hiddenapi::ShouldBlockAccessToMember(
+ hiddenapi::Action action = hiddenapi::GetMemberAction(
member, self, IsCallerInPlatformDex, hiddenapi::kJNI);
+ if (action != hiddenapi::kAllow) {
+ hiddenapi::NotifyHiddenApiListener(member);
+ }
+
+ return action == hiddenapi::kDeny;
}
// Helpers to call instrumentation functions for fields. These take jobjects so we don't need to set
diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc
index ad05856eaf..a8b203bff2 100644
--- a/runtime/native/java_lang_Class.cc
+++ b/runtime/native/java_lang_Class.cc
@@ -98,8 +98,13 @@ ALWAYS_INLINE static bool ShouldEnforceHiddenApi(Thread* self)
template<typename T>
ALWAYS_INLINE static bool ShouldBlockAccessToMember(T* member, Thread* self)
REQUIRES_SHARED(Locks::mutator_lock_) {
- return hiddenapi::ShouldBlockAccessToMember(
+ hiddenapi::Action action = hiddenapi::GetMemberAction(
member, self, IsCallerInPlatformDex, hiddenapi::kReflection);
+ if (action != hiddenapi::kAllow) {
+ hiddenapi::NotifyHiddenApiListener(member);
+ }
+
+ return action == hiddenapi::kDeny;
}
// Returns true if a class member should be discoverable with reflection given
@@ -113,7 +118,8 @@ ALWAYS_INLINE static bool IsDiscoverable(bool public_only,
return false;
}
- if (enforce_hidden_api && hiddenapi::GetMemberAction(access_flags) == hiddenapi::kDeny) {
+ if (enforce_hidden_api &&
+ hiddenapi::GetActionFromAccessFlags(access_flags) == hiddenapi::kDeny) {
return false;
}
@@ -433,12 +439,14 @@ static jobject Class_getPublicFieldRecursive(JNIEnv* env, jobject javaThis, jstr
return nullptr;
}
- mirror::Field* field = GetPublicFieldRecursive(
- soa.Self(), DecodeClass(soa, javaThis), name_string);
- if (field == nullptr || ShouldBlockAccessToMember(field->GetArtField(), soa.Self())) {
+ StackHandleScope<1> hs(soa.Self());
+ Handle<mirror::Field> field = hs.NewHandle(GetPublicFieldRecursive(
+ soa.Self(), DecodeClass(soa, javaThis), name_string));
+ if (field.Get() == nullptr ||
+ ShouldBlockAccessToMember(field->GetArtField(), soa.Self())) {
return nullptr;
}
- return soa.AddLocalReference<jobject>(field);
+ return soa.AddLocalReference<jobject>(field.Get());
}
static jobject Class_getDeclaredField(JNIEnv* env, jobject javaThis, jstring name) {
@@ -477,15 +485,17 @@ static jobject Class_getDeclaredConstructorInternal(
ScopedFastNativeObjectAccess soa(env);
DCHECK_EQ(Runtime::Current()->GetClassLinker()->GetImagePointerSize(), kRuntimePointerSize);
DCHECK(!Runtime::Current()->IsActiveTransaction());
- ObjPtr<mirror::Constructor> result =
+
+ StackHandleScope<1> hs(soa.Self());
+ Handle<mirror::Constructor> result = hs.NewHandle(
mirror::Class::GetDeclaredConstructorInternal<kRuntimePointerSize, false>(
soa.Self(),
DecodeClass(soa, javaThis),
- soa.Decode<mirror::ObjectArray<mirror::Class>>(args));
+ soa.Decode<mirror::ObjectArray<mirror::Class>>(args)));
if (result == nullptr || ShouldBlockAccessToMember(result->GetArtMethod(), soa.Self())) {
return nullptr;
}
- return soa.AddLocalReference<jobject>(result);
+ return soa.AddLocalReference<jobject>(result.Get());
}
static ALWAYS_INLINE inline bool MethodMatchesConstructor(
@@ -535,18 +545,19 @@ static jobjectArray Class_getDeclaredConstructorsInternal(
static jobject Class_getDeclaredMethodInternal(JNIEnv* env, jobject javaThis,
jstring name, jobjectArray args) {
ScopedFastNativeObjectAccess soa(env);
+ StackHandleScope<1> hs(soa.Self());
DCHECK_EQ(Runtime::Current()->GetClassLinker()->GetImagePointerSize(), kRuntimePointerSize);
DCHECK(!Runtime::Current()->IsActiveTransaction());
- ObjPtr<mirror::Method> result =
+ Handle<mirror::Method> result = hs.NewHandle(
mirror::Class::GetDeclaredMethodInternal<kRuntimePointerSize, false>(
soa.Self(),
DecodeClass(soa, javaThis),
soa.Decode<mirror::String>(name),
- soa.Decode<mirror::ObjectArray<mirror::Class>>(args));
+ soa.Decode<mirror::ObjectArray<mirror::Class>>(args)));
if (result == nullptr || ShouldBlockAccessToMember(result->GetArtMethod(), soa.Self())) {
return nullptr;
}
- return soa.AddLocalReference<jobject>(result);
+ return soa.AddLocalReference<jobject>(result.Get());
}
static jobjectArray Class_getDeclaredMethodsUnchecked(JNIEnv* env, jobject javaThis,
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 03f17bc04a..c14593749e 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -1004,7 +1004,8 @@ class Runtime {
// Whether access checks on hidden API should be performed.
hiddenapi::EnforcementPolicy hidden_api_policy_;
- // List of signature prefixes of methods that have been removed from the blacklist
+ // List of signature prefixes of methods that have been removed from the blacklist, and treated
+ // as if whitelisted.
std::vector<std::string> hidden_api_exemptions_;
// Whether the application has used an API which is not restricted but we
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index 0206d2c1e5..dfb98b6083 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -3801,16 +3801,8 @@ ArtMethod* MethodVerifier::ResolveMethodAndCheckAccess(
must_fail = true;
// Try to find the method also with the other type for better error reporting below
// but do not store such bogus lookup result in the DexCache or VerifierDeps.
- if (klass->IsInterface()) {
- // NB This is normally not really allowed but we want to get any static or private object
- // methods for error message purposes. This will never be returned.
- // TODO We might want to change the verifier to not require this.
- res_method = klass->FindClassMethod(dex_cache_.Get(), dex_method_idx, pointer_size);
- } else {
- // If there was an interface method with the same signature,
- // we would have found it also in the "copied" methods.
- DCHECK(klass->FindInterfaceMethod(dex_cache_.Get(), dex_method_idx, pointer_size) == nullptr);
- }
+ res_method = class_linker->FindIncompatibleMethod(
+ klass, dex_cache_.Get(), class_loader_.Get(), dex_method_idx);
}
if (res_method == nullptr) {
diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc
index bf36ccf0fa..742e713774 100644
--- a/runtime/well_known_classes.cc
+++ b/runtime/well_known_classes.cc
@@ -77,6 +77,7 @@ jclass WellKnownClasses::java_nio_ByteBuffer;
jclass WellKnownClasses::java_nio_DirectByteBuffer;
jclass WellKnownClasses::java_util_ArrayList;
jclass WellKnownClasses::java_util_Collections;
+jclass WellKnownClasses::java_util_function_Consumer;
jclass WellKnownClasses::libcore_reflect_AnnotationFactory;
jclass WellKnownClasses::libcore_reflect_AnnotationMember;
jclass WellKnownClasses::libcore_util_EmptyArray;
@@ -115,6 +116,7 @@ jmethodID WellKnownClasses::java_lang_Thread_run;
jmethodID WellKnownClasses::java_lang_ThreadGroup_add;
jmethodID WellKnownClasses::java_lang_ThreadGroup_removeThread;
jmethodID WellKnownClasses::java_nio_DirectByteBuffer_init;
+jmethodID WellKnownClasses::java_util_function_Consumer_accept;
jmethodID WellKnownClasses::libcore_reflect_AnnotationFactory_createAnnotation;
jmethodID WellKnownClasses::libcore_reflect_AnnotationMember_init;
jmethodID WellKnownClasses::org_apache_harmony_dalvik_ddmc_DdmServer_broadcast;
@@ -125,6 +127,7 @@ jfieldID WellKnownClasses::dalvik_system_DexFile_fileName;
jfieldID WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList;
jfieldID WellKnownClasses::dalvik_system_DexPathList_dexElements;
jfieldID WellKnownClasses::dalvik_system_DexPathList__Element_dexFile;
+jfieldID WellKnownClasses::dalvik_system_VMRuntime_nonSdkApiUsageConsumer;
jfieldID WellKnownClasses::java_lang_Thread_daemon;
jfieldID WellKnownClasses::java_lang_Thread_group;
jfieldID WellKnownClasses::java_lang_Thread_lock;
@@ -349,6 +352,7 @@ void WellKnownClasses::Init(JNIEnv* env) {
java_nio_DirectByteBuffer = CacheClass(env, "java/nio/DirectByteBuffer");
java_util_ArrayList = CacheClass(env, "java/util/ArrayList");
java_util_Collections = CacheClass(env, "java/util/Collections");
+ java_util_function_Consumer = CacheClass(env, "java/util/function/Consumer");
libcore_reflect_AnnotationFactory = CacheClass(env, "libcore/reflect/AnnotationFactory");
libcore_reflect_AnnotationMember = CacheClass(env, "libcore/reflect/AnnotationMember");
libcore_util_EmptyArray = CacheClass(env, "libcore/util/EmptyArray");
@@ -379,6 +383,7 @@ void WellKnownClasses::Init(JNIEnv* env) {
java_lang_ThreadGroup_add = CacheMethod(env, java_lang_ThreadGroup, false, "add", "(Ljava/lang/Thread;)V");
java_lang_ThreadGroup_removeThread = CacheMethod(env, java_lang_ThreadGroup, false, "threadTerminated", "(Ljava/lang/Thread;)V");
java_nio_DirectByteBuffer_init = CacheMethod(env, java_nio_DirectByteBuffer, false, "<init>", "(JI)V");
+ java_util_function_Consumer_accept = CacheMethod(env, java_util_function_Consumer, false, "accept", "(Ljava/lang/Object;)V");
libcore_reflect_AnnotationFactory_createAnnotation = CacheMethod(env, libcore_reflect_AnnotationFactory, true, "createAnnotation", "(Ljava/lang/Class;[Llibcore/reflect/AnnotationMember;)Ljava/lang/annotation/Annotation;");
libcore_reflect_AnnotationMember_init = CacheMethod(env, libcore_reflect_AnnotationMember, false, "<init>", "(Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/reflect/Method;)V");
org_apache_harmony_dalvik_ddmc_DdmServer_broadcast = CacheMethod(env, org_apache_harmony_dalvik_ddmc_DdmServer, true, "broadcast", "(I)V");
@@ -389,6 +394,7 @@ void WellKnownClasses::Init(JNIEnv* env) {
dalvik_system_DexFile_fileName = CacheField(env, dalvik_system_DexFile, false, "mFileName", "Ljava/lang/String;");
dalvik_system_DexPathList_dexElements = CacheField(env, dalvik_system_DexPathList, false, "dexElements", "[Ldalvik/system/DexPathList$Element;");
dalvik_system_DexPathList__Element_dexFile = CacheField(env, dalvik_system_DexPathList__Element, false, "dexFile", "Ldalvik/system/DexFile;");
+ dalvik_system_VMRuntime_nonSdkApiUsageConsumer = CacheField(env, dalvik_system_VMRuntime, true, "nonSdkApiUsageConsumer", "Ljava/util/function/Consumer;");
java_lang_Thread_daemon = CacheField(env, java_lang_Thread, false, "daemon", "Z");
java_lang_Thread_group = CacheField(env, java_lang_Thread, false, "group", "Ljava/lang/ThreadGroup;");
java_lang_Thread_lock = CacheField(env, java_lang_Thread, false, "lock", "Ljava/lang/Object;");
diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h
index d5d7033132..7b1a2943d2 100644
--- a/runtime/well_known_classes.h
+++ b/runtime/well_known_classes.h
@@ -85,6 +85,7 @@ struct WellKnownClasses {
static jclass java_lang_Throwable;
static jclass java_util_ArrayList;
static jclass java_util_Collections;
+ static jclass java_util_function_Consumer;
static jclass java_nio_ByteBuffer;
static jclass java_nio_DirectByteBuffer;
static jclass libcore_reflect_AnnotationFactory;
@@ -125,6 +126,7 @@ struct WellKnownClasses {
static jmethodID java_lang_ThreadGroup_add;
static jmethodID java_lang_ThreadGroup_removeThread;
static jmethodID java_nio_DirectByteBuffer_init;
+ static jmethodID java_util_function_Consumer_accept;
static jmethodID libcore_reflect_AnnotationFactory_createAnnotation;
static jmethodID libcore_reflect_AnnotationMember_init;
static jmethodID org_apache_harmony_dalvik_ddmc_DdmServer_broadcast;
@@ -135,6 +137,7 @@ struct WellKnownClasses {
static jfieldID dalvik_system_DexFile_fileName;
static jfieldID dalvik_system_DexPathList_dexElements;
static jfieldID dalvik_system_DexPathList__Element_dexFile;
+ static jfieldID dalvik_system_VMRuntime_nonSdkApiUsageConsumer;
static jfieldID java_lang_reflect_Executable_artMethod;
static jfieldID java_lang_reflect_Proxy_h;
static jfieldID java_lang_Thread_daemon;
diff --git a/test/674-hiddenapi/build b/test/674-hiddenapi/build
index 9012e8fd13..330a6def29 100644
--- a/test/674-hiddenapi/build
+++ b/test/674-hiddenapi/build
@@ -16,15 +16,6 @@
set -e
-# Special build logic to handle src-ex .java files which have code that only builds on RI.
-custom_build_logic() {
- [[ -d ignore.src-ex ]] && mv ignore.src-ex src-ex
- # src-ex uses code that can only build on RI.
- ${JAVAC} -source 1.8 -target 1.8 -sourcepath src-ex -sourcepath src -d classes-ex $(find src-ex -name '*.java')
- # remove src-ex so that default-build doesn't try to build it.
- [[ -d src-ex ]] && mv src-ex ignore.src-ex
-}
-
# Build the jars twice. First with applying hiddenapi, creating a boot jar, then
# a second time without to create a normal jar. We need to do this because we
# want to load the jar once as an app module and once as a member of the boot
@@ -33,7 +24,6 @@ custom_build_logic() {
# class path dex files, so the boot jar loads fine in the latter case.
export USE_HIDDENAPI=true
-custom_build_logic
./default-build "$@"
# Move the jar file into the resource folder to be bundled with the test.
@@ -45,5 +35,4 @@ mv ${TEST_NAME}.jar res/boot.jar
rm -rf classes*
export USE_HIDDENAPI=false
-custom_build_logic
./default-build "$@"
diff --git a/test/674-hiddenapi/src-ex/ChildClass.java b/test/674-hiddenapi/src-ex/ChildClass.java
index 582e907ca3..71c92fba64 100644
--- a/test/674-hiddenapi/src-ex/ChildClass.java
+++ b/test/674-hiddenapi/src-ex/ChildClass.java
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+import dalvik.system.VMRuntime;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
@@ -21,11 +22,7 @@ import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
-
-import javax.lang.model.element.AnnotationMirror;
-import javax.lang.model.type.PrimitiveType;
-import javax.lang.model.type.TypeKind;
-import javax.lang.model.type.TypeVisitor;
+import java.util.function.Consumer;
public class ChildClass {
enum PrimitiveType {
@@ -129,6 +126,7 @@ public class ChildClass {
checkLinking("LinkFieldGet" + suffix, /*takesParameter*/ false, expected);
checkLinking("LinkFieldSet" + suffix, /*takesParameter*/ true, expected);
checkLinking("LinkMethod" + suffix, /*takesParameter*/ false, expected);
+ checkLinking("LinkMethodInterface" + suffix, /*takesParameter*/ false, expected);
}
// Check whether Class.newInstance succeeds.
@@ -136,6 +134,47 @@ public class ChildClass {
}
}
+ static final class RecordingConsumer implements Consumer<String> {
+ public String recordedValue = null;
+
+ @Override
+ public void accept(String value) {
+ recordedValue = value;
+ }
+ }
+
+ private static void checkMemberCallback(Class<?> klass, String name,
+ boolean isPublic, boolean isField) {
+ try {
+ RecordingConsumer consumer = new RecordingConsumer();
+ VMRuntime.setNonSdkApiUsageConsumer(consumer);
+ try {
+ if (isPublic) {
+ if (isField) {
+ klass.getField(name);
+ } else {
+ klass.getMethod(name);
+ }
+ } else {
+ if (isField) {
+ klass.getDeclaredField(name);
+ } else {
+ klass.getDeclaredMethod(name);
+ }
+ }
+ } catch (NoSuchFieldException|NoSuchMethodException ignored) {
+ // We're not concerned whether an exception is thrown or not - we're
+ // only interested in whether the callback is invoked.
+ }
+
+ if (consumer.recordedValue == null || !consumer.recordedValue.contains(name)) {
+ throw new RuntimeException("No callback for member: " + name);
+ }
+ } finally {
+ VMRuntime.setNonSdkApiUsageConsumer(null);
+ }
+ }
+
private static void checkField(Class<?> klass, String name, boolean isStatic,
Visibility visibility, Behaviour behaviour) throws Exception {
@@ -174,48 +213,52 @@ public class ChildClass {
// Finish here if we could not discover the field.
- if (!canDiscover) {
- return;
- }
+ if (canDiscover) {
+ // Test that modifiers are unaffected.
- // Test that modifiers are unaffected.
+ if (Reflection.canObserveFieldHiddenAccessFlags(klass, name)) {
+ throwModifiersException(klass, name, true);
+ }
- if (Reflection.canObserveFieldHiddenAccessFlags(klass, name)) {
- throwModifiersException(klass, name, true);
- }
+ // Test getters and setters when meaningful.
- // Test getters and setters when meaningful.
+ clearWarning();
+ if (!Reflection.canGetField(klass, name)) {
+ throwAccessException(klass, name, true, "Field.getInt()");
+ }
+ if (hasPendingWarning() != setsWarning) {
+ throwWarningException(klass, name, true, "Field.getInt()", setsWarning);
+ }
- clearWarning();
- if (!Reflection.canGetField(klass, name)) {
- throwAccessException(klass, name, true, "Field.getInt()");
- }
- if (hasPendingWarning() != setsWarning) {
- throwWarningException(klass, name, true, "Field.getInt()", setsWarning);
- }
+ clearWarning();
+ if (!Reflection.canSetField(klass, name)) {
+ throwAccessException(klass, name, true, "Field.setInt()");
+ }
+ if (hasPendingWarning() != setsWarning) {
+ throwWarningException(klass, name, true, "Field.setInt()", setsWarning);
+ }
- clearWarning();
- if (!Reflection.canSetField(klass, name)) {
- throwAccessException(klass, name, true, "Field.setInt()");
- }
- if (hasPendingWarning() != setsWarning) {
- throwWarningException(klass, name, true, "Field.setInt()", setsWarning);
- }
+ clearWarning();
+ if (!JNI.canGetField(klass, name, isStatic)) {
+ throwAccessException(klass, name, true, "getIntField");
+ }
+ if (hasPendingWarning() != setsWarning) {
+ throwWarningException(klass, name, true, "getIntField", setsWarning);
+ }
- clearWarning();
- if (!JNI.canGetField(klass, name, isStatic)) {
- throwAccessException(klass, name, true, "getIntField");
- }
- if (hasPendingWarning() != setsWarning) {
- throwWarningException(klass, name, true, "getIntField", setsWarning);
+ clearWarning();
+ if (!JNI.canSetField(klass, name, isStatic)) {
+ throwAccessException(klass, name, true, "setIntField");
+ }
+ if (hasPendingWarning() != setsWarning) {
+ throwWarningException(klass, name, true, "setIntField", setsWarning);
+ }
}
+ // Test that callbacks are invoked correctly.
clearWarning();
- if (!JNI.canSetField(klass, name, isStatic)) {
- throwAccessException(klass, name, true, "setIntField");
- }
- if (hasPendingWarning() != setsWarning) {
- throwWarningException(klass, name, true, "setIntField", setsWarning);
+ if (setsWarning || !canDiscover) {
+ checkMemberCallback(klass, name, isPublic, true /* isField */);
}
}
@@ -257,42 +300,46 @@ public class ChildClass {
// Finish here if we could not discover the field.
- if (!canDiscover) {
- return;
- }
+ if (canDiscover) {
+ // Test that modifiers are unaffected.
- // Test that modifiers are unaffected.
+ if (Reflection.canObserveMethodHiddenAccessFlags(klass, name)) {
+ throwModifiersException(klass, name, false);
+ }
- if (Reflection.canObserveMethodHiddenAccessFlags(klass, name)) {
- throwModifiersException(klass, name, false);
- }
+ // Test whether we can invoke the method. This skips non-static interface methods.
- // Test whether we can invoke the method. This skips non-static interface methods.
+ if (!klass.isInterface() || isStatic) {
+ clearWarning();
+ if (!Reflection.canInvokeMethod(klass, name)) {
+ throwAccessException(klass, name, false, "invoke()");
+ }
+ if (hasPendingWarning() != setsWarning) {
+ throwWarningException(klass, name, false, "invoke()", setsWarning);
+ }
- if (!klass.isInterface() || isStatic) {
- clearWarning();
- if (!Reflection.canInvokeMethod(klass, name)) {
- throwAccessException(klass, name, false, "invoke()");
- }
- if (hasPendingWarning() != setsWarning) {
- throwWarningException(klass, name, false, "invoke()", setsWarning);
- }
+ clearWarning();
+ if (!JNI.canInvokeMethodA(klass, name, isStatic)) {
+ throwAccessException(klass, name, false, "CallMethodA");
+ }
+ if (hasPendingWarning() != setsWarning) {
+ throwWarningException(klass, name, false, "CallMethodA()", setsWarning);
+ }
- clearWarning();
- if (!JNI.canInvokeMethodA(klass, name, isStatic)) {
- throwAccessException(klass, name, false, "CallMethodA");
- }
- if (hasPendingWarning() != setsWarning) {
- throwWarningException(klass, name, false, "CallMethodA()", setsWarning);
+ clearWarning();
+ if (!JNI.canInvokeMethodV(klass, name, isStatic)) {
+ throwAccessException(klass, name, false, "CallMethodV");
+ }
+ if (hasPendingWarning() != setsWarning) {
+ throwWarningException(klass, name, false, "CallMethodV()", setsWarning);
+ }
}
+ }
- clearWarning();
- if (!JNI.canInvokeMethodV(klass, name, isStatic)) {
- throwAccessException(klass, name, false, "CallMethodV");
- }
- if (hasPendingWarning() != setsWarning) {
- throwWarningException(klass, name, false, "CallMethodV()", setsWarning);
- }
+ // Test that callbacks are invoked correctly.
+ clearWarning();
+ if (setsWarning || !canDiscover) {
+ checkMemberCallback(klass, name, isPublic, false /* isField */);
}
}
diff --git a/test/674-hiddenapi/src-ex/Linking.java b/test/674-hiddenapi/src-ex/Linking.java
index a89b92b2b9..0fa0b1912f 100644
--- a/test/674-hiddenapi/src-ex/Linking.java
+++ b/test/674-hiddenapi/src-ex/Linking.java
@@ -174,6 +174,32 @@ class LinkMethodBlacklist {
}
}
+// INVOKE INSTANCE INTERFACE METHOD
+
+class LinkMethodInterfaceWhitelist {
+ public static int access() {
+ return DummyClass.getInterfaceInstance().methodPublicWhitelist();
+ }
+}
+
+class LinkMethodInterfaceLightGreylist {
+ public static int access() {
+ return DummyClass.getInterfaceInstance().methodPublicLightGreylist();
+ }
+}
+
+class LinkMethodInterfaceDarkGreylist {
+ public static int access() {
+ return DummyClass.getInterfaceInstance().methodPublicDarkGreylist();
+ }
+}
+
+class LinkMethodInterfaceBlacklist {
+ public static int access() {
+ return DummyClass.getInterfaceInstance().methodPublicBlacklist();
+ }
+}
+
// INVOKE STATIC METHOD
class LinkMethodStaticWhitelist {
@@ -199,3 +225,29 @@ class LinkMethodStaticBlacklist {
return ParentClass.methodPublicStaticBlacklist();
}
}
+
+// INVOKE INTERFACE STATIC METHOD
+
+class LinkMethodInterfaceStaticWhitelist {
+ public static int access() {
+ return ParentInterface.methodPublicStaticWhitelist();
+ }
+}
+
+class LinkMethodInterfaceStaticLightGreylist {
+ public static int access() {
+ return ParentInterface.methodPublicStaticLightGreylist();
+ }
+}
+
+class LinkMethodInterfaceStaticDarkGreylist {
+ public static int access() {
+ return ParentInterface.methodPublicStaticDarkGreylist();
+ }
+}
+
+class LinkMethodInterfaceStaticBlacklist {
+ public static int access() {
+ return ParentInterface.methodPublicStaticBlacklist();
+ }
+}
diff --git a/test/674-hiddenapi/src/DummyClass.java b/test/674-hiddenapi/src/DummyClass.java
new file mode 100644
index 0000000000..51281a2666
--- /dev/null
+++ b/test/674-hiddenapi/src/DummyClass.java
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+
+public class DummyClass implements ParentInterface {
+ public int methodPublicWhitelist() { return 1; }
+ public int methodPublicLightGreylist() { return 2; }
+ public int methodPublicDarkGreylist() { return 3; }
+ public int methodPublicBlacklist() { return 4; }
+
+ public static ParentInterface getInterfaceInstance() {
+ return new DummyClass();
+ }
+}
diff --git a/test/674-hiddenapi/src/ParentInterface.java b/test/674-hiddenapi/src/ParentInterface.java
index e36fe0e6b2..f79ac9d661 100644
--- a/test/674-hiddenapi/src/ParentInterface.java
+++ b/test/674-hiddenapi/src/ParentInterface.java
@@ -23,9 +23,9 @@ public interface ParentInterface {
// INSTANCE METHOD
int methodPublicWhitelist();
- int methodPublicBlacklist();
int methodPublicLightGreylist();
int methodPublicDarkGreylist();
+ int methodPublicBlacklist();
// STATIC METHOD
static int methodPublicStaticWhitelist() { return 21; }
diff --git a/tools/veridex/Android.bp b/tools/veridex/Android.bp
index cbf62d9e9c..570960c4da 100644
--- a/tools/veridex/Android.bp
+++ b/tools/veridex/Android.bp
@@ -16,8 +16,10 @@ cc_binary {
name: "veridex",
host_supported: true,
srcs: [
+ "flow_analysis.cc",
"hidden_api.cc",
"hidden_api_finder.cc",
+ "precise_hidden_api_finder.cc",
"resolver.cc",
"veridex.cc",
],
diff --git a/tools/veridex/README.md b/tools/veridex/README.md
index 0f91b08771..f85a51bb48 100644
--- a/tools/veridex/README.md
+++ b/tools/veridex/README.md
@@ -11,4 +11,4 @@ To build it:
> make appcompat
To run it:
-> ./art/tools/veridex/appcompat.sh test.apk
+> ./art/tools/veridex/appcompat.sh --dex-file=test.apk
diff --git a/tools/veridex/appcompat.sh b/tools/veridex/appcompat.sh
index f75aa4f0d0..31a8654b58 100755
--- a/tools/veridex/appcompat.sh
+++ b/tools/veridex/appcompat.sh
@@ -48,4 +48,4 @@ ${ANDROID_HOST_OUT}/bin/veridex \
--blacklist=${PACKAGING}/hiddenapi-blacklist.txt \
--light-greylist=${PACKAGING}/hiddenapi-light-greylist.txt \
--dark-greylist=${PACKAGING}/hiddenapi-dark-greylist.txt \
- --dex-file=$1
+ $@
diff --git a/tools/veridex/flow_analysis.cc b/tools/veridex/flow_analysis.cc
new file mode 100644
index 0000000000..abd0b9b28c
--- /dev/null
+++ b/tools/veridex/flow_analysis.cc
@@ -0,0 +1,602 @@
+/*
+ * 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 "flow_analysis.h"
+
+#include "dex/bytecode_utils.h"
+#include "dex/code_item_accessors-inl.h"
+#include "dex/dex_instruction-inl.h"
+#include "dex/dex_file-inl.h"
+#include "dex/dex_file_exception_helpers.h"
+#include "resolver.h"
+#include "veridex.h"
+
+namespace art {
+
+
+void VeriFlowAnalysis::SetAsBranchTarget(uint32_t dex_pc) {
+ if (dex_registers_[dex_pc] == nullptr) {
+ dex_registers_[dex_pc].reset(
+ new std::vector<RegisterValue>(code_item_accessor_.RegistersSize()));
+ }
+}
+
+bool VeriFlowAnalysis::IsBranchTarget(uint32_t dex_pc) {
+ return dex_registers_[dex_pc] != nullptr;
+}
+
+bool VeriFlowAnalysis::MergeRegisterValues(uint32_t dex_pc) {
+ // TODO: Do the merging. Right now, just return that we should continue
+ // the iteration if the instruction has not been visited.
+ return !instruction_infos_[dex_pc].has_been_visited;
+}
+
+void VeriFlowAnalysis::SetVisited(uint32_t dex_pc) {
+ instruction_infos_[dex_pc].has_been_visited = true;
+}
+
+void VeriFlowAnalysis::FindBranches() {
+ SetAsBranchTarget(0);
+
+ if (code_item_accessor_.TriesSize() != 0) {
+ // TODO: We need to mark the range of dex pcs as flowing in the handlers.
+ /*
+ for (const DexFile::TryItem& try_item : code_item_accessor_.TryItems()) {
+ uint32_t dex_pc_start = try_item.start_addr_;
+ uint32_t dex_pc_end = dex_pc_start + try_item.insn_count_;
+ }
+ */
+
+ // Create branch targets for exception handlers.
+ const uint8_t* handlers_ptr = code_item_accessor_.GetCatchHandlerData();
+ uint32_t handlers_size = DecodeUnsignedLeb128(&handlers_ptr);
+ for (uint32_t idx = 0; idx < handlers_size; ++idx) {
+ CatchHandlerIterator iterator(handlers_ptr);
+ for (; iterator.HasNext(); iterator.Next()) {
+ SetAsBranchTarget(iterator.GetHandlerAddress());
+ }
+ handlers_ptr = iterator.EndDataPointer();
+ }
+ }
+
+ // Iterate over all instructions and find branching instructions.
+ for (const DexInstructionPcPair& pair : code_item_accessor_) {
+ const uint32_t dex_pc = pair.DexPc();
+ const Instruction& instruction = pair.Inst();
+
+ if (instruction.IsBranch()) {
+ SetAsBranchTarget(dex_pc + instruction.GetTargetOffset());
+ } else if (instruction.IsSwitch()) {
+ DexSwitchTable table(instruction, dex_pc);
+ for (DexSwitchTableIterator s_it(table); !s_it.Done(); s_it.Advance()) {
+ SetAsBranchTarget(dex_pc + s_it.CurrentTargetOffset());
+ if (table.ShouldBuildDecisionTree() && !s_it.IsLast()) {
+ SetAsBranchTarget(s_it.GetDexPcForCurrentIndex());
+ }
+ }
+ }
+ }
+}
+
+void VeriFlowAnalysis::UpdateRegister(uint32_t dex_register,
+ RegisterSource kind,
+ VeriClass* cls,
+ uint32_t source_id) {
+ current_registers_[dex_register] = RegisterValue(
+ kind, DexFileReference(&resolver_->GetDexFile(), source_id), cls);
+}
+
+void VeriFlowAnalysis::UpdateRegister(uint32_t dex_register, const RegisterValue& value) {
+ current_registers_[dex_register] = value;
+}
+
+void VeriFlowAnalysis::UpdateRegister(uint32_t dex_register, const VeriClass* cls) {
+ current_registers_[dex_register] =
+ RegisterValue(RegisterSource::kNone, DexFileReference(nullptr, 0), cls);
+}
+
+const RegisterValue& VeriFlowAnalysis::GetRegister(uint32_t dex_register) {
+ return current_registers_[dex_register];
+}
+
+RegisterValue VeriFlowAnalysis::GetReturnType(uint32_t method_index) {
+ const DexFile& dex_file = resolver_->GetDexFile();
+ const DexFile::MethodId& method_id = dex_file.GetMethodId(method_index);
+ const DexFile::ProtoId& proto_id = dex_file.GetMethodPrototype(method_id);
+ VeriClass* cls = resolver_->GetVeriClass(proto_id.return_type_idx_);
+ return RegisterValue(RegisterSource::kMethod, DexFileReference(&dex_file, method_index), cls);
+}
+
+RegisterValue VeriFlowAnalysis::GetFieldType(uint32_t field_index) {
+ const DexFile& dex_file = resolver_->GetDexFile();
+ const DexFile::FieldId& field_id = dex_file.GetFieldId(field_index);
+ VeriClass* cls = resolver_->GetVeriClass(field_id.type_idx_);
+ return RegisterValue(RegisterSource::kField, DexFileReference(&dex_file, field_index), cls);
+}
+
+void VeriFlowAnalysis::AnalyzeCode() {
+ std::vector<uint32_t> work_list;
+ work_list.push_back(0);
+ // Iterate over the code.
+ // When visiting unconditional branches (goto), move to that instruction.
+ // When visiting conditional branches, move to one destination, and put the other
+ // in the worklist.
+ while (!work_list.empty()) {
+ uint32_t dex_pc = work_list.back();
+ work_list.pop_back();
+ CHECK(IsBranchTarget(dex_pc));
+ current_registers_ = *dex_registers_[dex_pc].get();
+ while (true) {
+ const uint16_t* insns = code_item_accessor_.Insns() + dex_pc;
+ const Instruction& inst = *Instruction::At(insns);
+ ProcessDexInstruction(inst);
+ SetVisited(dex_pc);
+
+ int opcode_flags = Instruction::FlagsOf(inst.Opcode());
+ if ((opcode_flags & Instruction::kContinue) != 0) {
+ if ((opcode_flags & Instruction::kBranch) != 0) {
+ uint32_t branch_dex_pc = dex_pc + inst.GetTargetOffset();
+ if (MergeRegisterValues(branch_dex_pc)) {
+ work_list.push_back(branch_dex_pc);
+ }
+ }
+ dex_pc += inst.SizeInCodeUnits();
+ } else if ((opcode_flags & Instruction::kBranch) != 0) {
+ dex_pc += inst.GetTargetOffset();
+ DCHECK(IsBranchTarget(dex_pc));
+ } else {
+ break;
+ }
+
+ if (IsBranchTarget(dex_pc)) {
+ if (MergeRegisterValues(dex_pc)) {
+ current_registers_ = *dex_registers_[dex_pc].get();
+ } else {
+ break;
+ }
+ }
+ }
+ }
+}
+
+void VeriFlowAnalysis::ProcessDexInstruction(const Instruction& instruction) {
+ switch (instruction.Opcode()) {
+ case Instruction::CONST_4:
+ case Instruction::CONST_16:
+ case Instruction::CONST:
+ case Instruction::CONST_HIGH16: {
+ int32_t register_index = instruction.VRegA();
+ UpdateRegister(register_index, VeriClass::integer_);
+ break;
+ }
+
+ case Instruction::CONST_WIDE_16:
+ case Instruction::CONST_WIDE_32:
+ case Instruction::CONST_WIDE:
+ case Instruction::CONST_WIDE_HIGH16: {
+ int32_t register_index = instruction.VRegA();
+ UpdateRegister(register_index, VeriClass::long_);
+ break;
+ }
+
+ case Instruction::MOVE:
+ case Instruction::MOVE_FROM16:
+ case Instruction::MOVE_16: {
+ UpdateRegister(instruction.VRegA(), GetRegister(instruction.VRegB()));
+ break;
+ }
+
+ case Instruction::MOVE_WIDE:
+ case Instruction::MOVE_WIDE_FROM16:
+ case Instruction::MOVE_WIDE_16: {
+ UpdateRegister(instruction.VRegA(), GetRegister(instruction.VRegB()));
+ break;
+ }
+
+ case Instruction::MOVE_OBJECT:
+ case Instruction::MOVE_OBJECT_16:
+ case Instruction::MOVE_OBJECT_FROM16: {
+ UpdateRegister(instruction.VRegA(), GetRegister(instruction.VRegB()));
+ break;
+ }
+ case Instruction::CONST_CLASS: {
+ UpdateRegister(instruction.VRegA_21c(),
+ RegisterSource::kClass,
+ VeriClass::class_,
+ instruction.VRegB_21c());
+ break;
+ }
+ case Instruction::CONST_STRING: {
+ UpdateRegister(instruction.VRegA_21c(),
+ RegisterSource::kString,
+ VeriClass::string_,
+ instruction.VRegB_21c());
+ break;
+ }
+
+ case Instruction::CONST_STRING_JUMBO: {
+ UpdateRegister(instruction.VRegA_31c(),
+ RegisterSource::kString,
+ VeriClass::string_,
+ instruction.VRegB_31c());
+ break;
+ }
+ case Instruction::INVOKE_DIRECT:
+ case Instruction::INVOKE_INTERFACE:
+ case Instruction::INVOKE_STATIC:
+ case Instruction::INVOKE_SUPER:
+ case Instruction::INVOKE_VIRTUAL: {
+ VeriMethod method = resolver_->GetMethod(instruction.VRegB_35c());
+ uint32_t args[5];
+ instruction.GetVarArgs(args);
+ if (method == VeriClass::forName_) {
+ RegisterValue value = GetRegister(args[0]);
+ last_result_ = RegisterValue(
+ value.GetSource(), value.GetDexFileReference(), VeriClass::class_);
+ } else if (IsGetField(method)) {
+ RegisterValue cls = GetRegister(args[0]);
+ RegisterValue name = GetRegister(args[1]);
+ field_uses_.push_back(std::make_pair(cls, name));
+ last_result_ = GetReturnType(instruction.VRegB_35c());
+ } else if (IsGetMethod(method)) {
+ RegisterValue cls = GetRegister(args[0]);
+ RegisterValue name = GetRegister(args[1]);
+ method_uses_.push_back(std::make_pair(cls, name));
+ last_result_ = GetReturnType(instruction.VRegB_35c());
+ } else if (method == VeriClass::getClass_) {
+ RegisterValue obj = GetRegister(args[0]);
+ last_result_ = RegisterValue(
+ obj.GetSource(), obj.GetDexFileReference(), VeriClass::class_);
+ } else {
+ last_result_ = GetReturnType(instruction.VRegB_35c());
+ }
+ break;
+ }
+
+ case Instruction::INVOKE_DIRECT_RANGE:
+ case Instruction::INVOKE_INTERFACE_RANGE:
+ case Instruction::INVOKE_STATIC_RANGE:
+ case Instruction::INVOKE_SUPER_RANGE:
+ case Instruction::INVOKE_VIRTUAL_RANGE: {
+ last_result_ = GetReturnType(instruction.VRegB_3rc());
+ break;
+ }
+
+ case Instruction::MOVE_RESULT:
+ case Instruction::MOVE_RESULT_WIDE:
+ case Instruction::MOVE_RESULT_OBJECT: {
+ UpdateRegister(instruction.VRegA(), last_result_);
+ break;
+ }
+ case Instruction::RETURN_VOID:
+ case Instruction::RETURN_OBJECT:
+ case Instruction::RETURN_WIDE:
+ case Instruction::RETURN: {
+ break;
+ }
+ #define IF_XX(cond) \
+ case Instruction::IF_##cond: break; \
+ case Instruction::IF_##cond##Z: break
+
+ IF_XX(EQ);
+ IF_XX(NE);
+ IF_XX(LT);
+ IF_XX(LE);
+ IF_XX(GT);
+ IF_XX(GE);
+
+ case Instruction::GOTO:
+ case Instruction::GOTO_16:
+ case Instruction::GOTO_32: {
+ break;
+ }
+ case Instruction::INVOKE_POLYMORPHIC: {
+ // TODO
+ break;
+ }
+
+ case Instruction::INVOKE_POLYMORPHIC_RANGE: {
+ // TODO
+ break;
+ }
+
+ case Instruction::NEG_INT:
+ case Instruction::NEG_LONG:
+ case Instruction::NEG_FLOAT:
+ case Instruction::NEG_DOUBLE:
+ case Instruction::NOT_INT:
+ case Instruction::NOT_LONG: {
+ UpdateRegister(instruction.VRegA(), VeriClass::integer_);
+ break;
+ }
+
+ case Instruction::INT_TO_LONG:
+ case Instruction::INT_TO_FLOAT:
+ case Instruction::INT_TO_DOUBLE:
+ case Instruction::LONG_TO_INT:
+ case Instruction::LONG_TO_FLOAT:
+ case Instruction::LONG_TO_DOUBLE:
+ case Instruction::FLOAT_TO_INT:
+ case Instruction::FLOAT_TO_LONG:
+ case Instruction::FLOAT_TO_DOUBLE:
+ case Instruction::DOUBLE_TO_INT:
+ case Instruction::DOUBLE_TO_LONG:
+ case Instruction::DOUBLE_TO_FLOAT:
+ case Instruction::INT_TO_BYTE:
+ case Instruction::INT_TO_SHORT:
+ case Instruction::INT_TO_CHAR: {
+ UpdateRegister(instruction.VRegA(), VeriClass::integer_);
+ break;
+ }
+
+ case Instruction::ADD_INT:
+ case Instruction::ADD_LONG:
+ case Instruction::ADD_DOUBLE:
+ case Instruction::ADD_FLOAT:
+ case Instruction::SUB_INT:
+ case Instruction::SUB_LONG:
+ case Instruction::SUB_FLOAT:
+ case Instruction::SUB_DOUBLE:
+ case Instruction::MUL_INT:
+ case Instruction::MUL_LONG:
+ case Instruction::MUL_FLOAT:
+ case Instruction::MUL_DOUBLE:
+ case Instruction::DIV_INT:
+ case Instruction::DIV_LONG:
+ case Instruction::DIV_FLOAT:
+ case Instruction::DIV_DOUBLE:
+ case Instruction::REM_INT:
+ case Instruction::REM_LONG:
+ case Instruction::REM_FLOAT:
+ case Instruction::REM_DOUBLE:
+ case Instruction::AND_INT:
+ case Instruction::AND_LONG:
+ case Instruction::SHL_INT:
+ case Instruction::SHL_LONG:
+ case Instruction::SHR_INT:
+ case Instruction::SHR_LONG:
+ case Instruction::USHR_INT:
+ case Instruction::USHR_LONG:
+ case Instruction::OR_INT:
+ case Instruction::OR_LONG:
+ case Instruction::XOR_INT:
+ case Instruction::XOR_LONG: {
+ UpdateRegister(instruction.VRegA(), VeriClass::integer_);
+ break;
+ }
+
+ case Instruction::ADD_INT_2ADDR:
+ case Instruction::ADD_LONG_2ADDR:
+ case Instruction::ADD_DOUBLE_2ADDR:
+ case Instruction::ADD_FLOAT_2ADDR:
+ case Instruction::SUB_INT_2ADDR:
+ case Instruction::SUB_LONG_2ADDR:
+ case Instruction::SUB_FLOAT_2ADDR:
+ case Instruction::SUB_DOUBLE_2ADDR:
+ case Instruction::MUL_INT_2ADDR:
+ case Instruction::MUL_LONG_2ADDR:
+ case Instruction::MUL_FLOAT_2ADDR:
+ case Instruction::MUL_DOUBLE_2ADDR:
+ case Instruction::DIV_INT_2ADDR:
+ case Instruction::DIV_LONG_2ADDR:
+ case Instruction::REM_INT_2ADDR:
+ case Instruction::REM_LONG_2ADDR:
+ case Instruction::REM_FLOAT_2ADDR:
+ case Instruction::REM_DOUBLE_2ADDR:
+ case Instruction::SHL_INT_2ADDR:
+ case Instruction::SHL_LONG_2ADDR:
+ case Instruction::SHR_INT_2ADDR:
+ case Instruction::SHR_LONG_2ADDR:
+ case Instruction::USHR_INT_2ADDR:
+ case Instruction::USHR_LONG_2ADDR:
+ case Instruction::DIV_FLOAT_2ADDR:
+ case Instruction::DIV_DOUBLE_2ADDR:
+ case Instruction::AND_INT_2ADDR:
+ case Instruction::AND_LONG_2ADDR:
+ case Instruction::OR_INT_2ADDR:
+ case Instruction::OR_LONG_2ADDR:
+ case Instruction::XOR_INT_2ADDR:
+ case Instruction::XOR_LONG_2ADDR: {
+ UpdateRegister(instruction.VRegA(), VeriClass::integer_);
+ break;
+ }
+
+ case Instruction::ADD_INT_LIT16:
+ case Instruction::AND_INT_LIT16:
+ case Instruction::OR_INT_LIT16:
+ case Instruction::XOR_INT_LIT16:
+ case Instruction::RSUB_INT:
+ case Instruction::MUL_INT_LIT16:
+ case Instruction::DIV_INT_LIT16:
+ case Instruction::REM_INT_LIT16: {
+ UpdateRegister(instruction.VRegA(), VeriClass::integer_);
+ break;
+ }
+
+ case Instruction::ADD_INT_LIT8:
+ case Instruction::AND_INT_LIT8:
+ case Instruction::OR_INT_LIT8:
+ case Instruction::XOR_INT_LIT8:
+ case Instruction::RSUB_INT_LIT8:
+ case Instruction::MUL_INT_LIT8:
+ case Instruction::DIV_INT_LIT8:
+ case Instruction::REM_INT_LIT8:
+ case Instruction::SHL_INT_LIT8:
+ case Instruction::SHR_INT_LIT8: {
+ case Instruction::USHR_INT_LIT8: {
+ UpdateRegister(instruction.VRegA(), VeriClass::integer_);
+ break;
+ }
+
+ case Instruction::NEW_INSTANCE: {
+ VeriClass* cls = resolver_->GetVeriClass(dex::TypeIndex(instruction.VRegB_21c()));
+ UpdateRegister(instruction.VRegA(), cls);
+ break;
+ }
+
+ case Instruction::NEW_ARRAY: {
+ dex::TypeIndex type_index(instruction.VRegC_22c());
+ VeriClass* cls = resolver_->GetVeriClass(type_index);
+ UpdateRegister(instruction.VRegA_22c(), cls);
+ break;
+ }
+
+ case Instruction::FILLED_NEW_ARRAY: {
+ dex::TypeIndex type_index(instruction.VRegB_35c());
+ VeriClass* cls = resolver_->GetVeriClass(type_index);
+ UpdateRegister(instruction.VRegA_22c(), cls);
+ break;
+ }
+
+ case Instruction::FILLED_NEW_ARRAY_RANGE: {
+ dex::TypeIndex type_index(instruction.VRegB_3rc());
+ uint32_t register_index = instruction.VRegC_3rc();
+ VeriClass* cls = resolver_->GetVeriClass(type_index);
+ UpdateRegister(register_index, cls);
+ break;
+ }
+
+ case Instruction::FILL_ARRAY_DATA: {
+ break;
+ }
+
+ case Instruction::CMP_LONG:
+ case Instruction::CMPG_FLOAT:
+ case Instruction::CMPG_DOUBLE:
+ case Instruction::CMPL_FLOAT:
+ case Instruction::CMPL_DOUBLE:
+ UpdateRegister(instruction.VRegA(), VeriClass::integer_);
+ break;
+ }
+
+ case Instruction::NOP:
+ break;
+
+ case Instruction::IGET:
+ case Instruction::IGET_WIDE:
+ case Instruction::IGET_OBJECT:
+ case Instruction::IGET_BOOLEAN:
+ case Instruction::IGET_BYTE:
+ case Instruction::IGET_CHAR:
+ case Instruction::IGET_SHORT: {
+ UpdateRegister(instruction.VRegA_22c(), GetFieldType(instruction.VRegC_22c()));
+ break;
+ }
+
+ case Instruction::IPUT:
+ case Instruction::IPUT_WIDE:
+ case Instruction::IPUT_OBJECT:
+ case Instruction::IPUT_BOOLEAN:
+ case Instruction::IPUT_BYTE:
+ case Instruction::IPUT_CHAR:
+ case Instruction::IPUT_SHORT: {
+ break;
+ }
+
+ case Instruction::SGET:
+ case Instruction::SGET_WIDE:
+ case Instruction::SGET_OBJECT:
+ case Instruction::SGET_BOOLEAN:
+ case Instruction::SGET_BYTE:
+ case Instruction::SGET_CHAR:
+ case Instruction::SGET_SHORT: {
+ UpdateRegister(instruction.VRegA_22c(), GetFieldType(instruction.VRegC_22c()));
+ break;
+ }
+
+ case Instruction::SPUT:
+ case Instruction::SPUT_WIDE:
+ case Instruction::SPUT_OBJECT:
+ case Instruction::SPUT_BOOLEAN:
+ case Instruction::SPUT_BYTE:
+ case Instruction::SPUT_CHAR:
+ case Instruction::SPUT_SHORT: {
+ break;
+ }
+
+#define ARRAY_XX(kind, anticipated_type) \
+ case Instruction::AGET##kind: { \
+ UpdateRegister(instruction.VRegA_23x(), anticipated_type); \
+ break; \
+ } \
+ case Instruction::APUT##kind: { \
+ break; \
+ }
+
+ ARRAY_XX(, VeriClass::integer_);
+ ARRAY_XX(_WIDE, VeriClass::long_);
+ ARRAY_XX(_BOOLEAN, VeriClass::boolean_);
+ ARRAY_XX(_BYTE, VeriClass::byte_);
+ ARRAY_XX(_CHAR, VeriClass::char_);
+ ARRAY_XX(_SHORT, VeriClass::short_);
+
+ case Instruction::AGET_OBJECT: {
+ // TODO: take the component type.
+ UpdateRegister(instruction.VRegA_23x(), VeriClass::object_);
+ break;
+ }
+
+ case Instruction::APUT_OBJECT: {
+ break;
+ }
+
+ case Instruction::ARRAY_LENGTH: {
+ UpdateRegister(instruction.VRegA_12x(), VeriClass::integer_);
+ break;
+ }
+
+ case Instruction::MOVE_EXCEPTION: {
+ UpdateRegister(instruction.VRegA_11x(), VeriClass::throwable_);
+ break;
+ }
+
+ case Instruction::THROW: {
+ break;
+ }
+
+ case Instruction::INSTANCE_OF: {
+ uint8_t destination = instruction.VRegA_22c();
+ UpdateRegister(destination, VeriClass::boolean_);
+ break;
+ }
+
+ case Instruction::CHECK_CAST: {
+ uint8_t reference = instruction.VRegA_21c();
+ dex::TypeIndex type_index(instruction.VRegB_21c());
+ UpdateRegister(reference, resolver_->GetVeriClass(type_index));
+ break;
+ }
+
+ case Instruction::MONITOR_ENTER:
+ case Instruction::MONITOR_EXIT: {
+ break;
+ }
+
+ case Instruction::SPARSE_SWITCH:
+ case Instruction::PACKED_SWITCH:
+ break;
+
+ default:
+ break;
+ }
+}
+
+void VeriFlowAnalysis::Run() {
+ FindBranches();
+ AnalyzeCode();
+}
+
+} // namespace art
diff --git a/tools/veridex/flow_analysis.h b/tools/veridex/flow_analysis.h
new file mode 100644
index 0000000000..c065fb8c24
--- /dev/null
+++ b/tools/veridex/flow_analysis.h
@@ -0,0 +1,147 @@
+/*
+ * 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_TOOLS_VERIDEX_FLOW_ANALYSIS_H_
+#define ART_TOOLS_VERIDEX_FLOW_ANALYSIS_H_
+
+#include "dex/code_item_accessors.h"
+#include "dex/dex_file_reference.h"
+#include "dex/method_reference.h"
+#include "veridex.h"
+
+namespace art {
+
+class VeridexClass;
+class VeridexResolver;
+
+/**
+ * The source where a dex register comes from.
+ */
+enum class RegisterSource {
+ kParameter,
+ kField,
+ kMethod,
+ kClass,
+ kString,
+ kNone
+};
+
+/**
+ * Abstract representation of a dex register value.
+ */
+class RegisterValue {
+ public:
+ RegisterValue() : source_(RegisterSource::kNone), reference_(nullptr, 0), type_(nullptr) {}
+ RegisterValue(RegisterSource source, DexFileReference reference, const VeriClass* type)
+ : source_(source), reference_(reference), type_(type) {}
+
+ RegisterSource GetSource() const { return source_; }
+ DexFileReference GetDexFileReference() const { return reference_; }
+ const VeriClass* GetType() const { return type_; }
+
+ const char* ToString() const {
+ switch (source_) {
+ case RegisterSource::kString:
+ return reference_.dex_file->StringDataByIdx(dex::StringIndex(reference_.index));
+ case RegisterSource::kClass:
+ return reference_.dex_file->StringByTypeIdx(dex::TypeIndex(reference_.index));
+ default:
+ return "<unknown>";
+ }
+ }
+
+ private:
+ RegisterSource source_;
+ DexFileReference reference_;
+ const VeriClass* type_;
+};
+
+struct InstructionInfo {
+ bool has_been_visited;
+};
+
+class VeriFlowAnalysis {
+ public:
+ VeriFlowAnalysis(VeridexResolver* resolver,
+ const CodeItemDataAccessor& code_item_accessor)
+ : resolver_(resolver),
+ code_item_accessor_(code_item_accessor),
+ dex_registers_(code_item_accessor.InsnsSizeInCodeUnits()),
+ instruction_infos_(code_item_accessor.InsnsSizeInCodeUnits()) {}
+
+ void Run();
+
+ const std::vector<std::pair<RegisterValue, RegisterValue>>& GetFieldUses() const {
+ return field_uses_;
+ }
+
+ const std::vector<std::pair<RegisterValue, RegisterValue>>& GetMethodUses() const {
+ return method_uses_;
+ }
+
+ private:
+ // Find all branches in the code.
+ void FindBranches();
+
+ // Analyze all non-deead instructions in the code.
+ void AnalyzeCode();
+
+ // Set the instruction at the given pc as a branch target.
+ void SetAsBranchTarget(uint32_t dex_pc);
+
+ // Whether the instruction at the given pc is a branch target.
+ bool IsBranchTarget(uint32_t dex_pc);
+
+ // Merge the register values at the given pc with `current_registers`.
+ // Return whether the register values have changed, and the instruction needs
+ // to be visited again.
+ bool MergeRegisterValues(uint32_t dex_pc);
+
+ void UpdateRegister(
+ uint32_t dex_register, RegisterSource kind, VeriClass* cls, uint32_t source_id);
+ void UpdateRegister(uint32_t dex_register, const RegisterValue& value);
+ void UpdateRegister(uint32_t dex_register, const VeriClass* cls);
+ const RegisterValue& GetRegister(uint32_t dex_register);
+ void ProcessDexInstruction(const Instruction& inst);
+ void SetVisited(uint32_t dex_pc);
+ RegisterValue GetReturnType(uint32_t method_index);
+ RegisterValue GetFieldType(uint32_t field_index);
+
+ VeridexResolver* resolver_;
+ const CodeItemDataAccessor& code_item_accessor_;
+
+ // Vector of register values for all branch targets.
+ std::vector<std::unique_ptr<std::vector<RegisterValue>>> dex_registers_;
+
+ // The current values of dex registers.
+ std::vector<RegisterValue> current_registers_;
+
+ // Information on each instruction useful for the analysis.
+ std::vector<InstructionInfo> instruction_infos_;
+
+ // The value of invoke instructions, to be fetched when visiting move-result.
+ RegisterValue last_result_;
+
+ // List of reflection field uses found.
+ std::vector<std::pair<RegisterValue, RegisterValue>> field_uses_;
+
+ // List of reflection method uses found.
+ std::vector<std::pair<RegisterValue, RegisterValue>> method_uses_;
+};
+
+} // namespace art
+
+#endif // ART_TOOLS_VERIDEX_FLOW_ANALYSIS_H_
diff --git a/tools/veridex/hidden_api.h b/tools/veridex/hidden_api.h
index 5893b8ae33..4c67768a00 100644
--- a/tools/veridex/hidden_api.h
+++ b/tools/veridex/hidden_api.h
@@ -18,6 +18,7 @@
#define ART_TOOLS_VERIDEX_HIDDEN_API_H_
#include "dex/hidden_api_access_flags.h"
+#include "dex/method_reference.h"
#include <ostream>
#include <set>
@@ -58,6 +59,10 @@ class HiddenApi {
static std::string GetApiFieldName(const DexFile& dex_file, uint32_t field_index);
+ static std::string GetApiMethodName(MethodReference ref) {
+ return HiddenApi::GetApiMethodName(*ref.dex_file, ref.index);
+ }
+
private:
static bool IsInList(const std::string& name, const std::set<std::string>& list) {
return list.find(name) != list.end();
@@ -70,6 +75,13 @@ class HiddenApi {
std::set<std::string> dark_greylist_;
};
+struct HiddenApiStats {
+ uint32_t count = 0;
+ uint32_t reflection_count = 0;
+ uint32_t linking_count = 0;
+ uint32_t api_counts[4] = { 0, 0, 0, 0 };
+};
+
} // namespace art
#endif // ART_TOOLS_VERIDEX_HIDDEN_API_H_
diff --git a/tools/veridex/hidden_api_finder.cc b/tools/veridex/hidden_api_finder.cc
index 4885e02769..b9be618ed7 100644
--- a/tools/veridex/hidden_api_finder.cc
+++ b/tools/veridex/hidden_api_finder.cc
@@ -193,32 +193,26 @@ void HiddenApiFinder::CollectAccesses(VeridexResolver* resolver) {
}
}
-static std::string GetApiMethodName(MethodReference ref) {
- return HiddenApi::GetApiMethodName(*ref.dex_file, ref.index);
-}
-
void HiddenApiFinder::Run(const std::vector<std::unique_ptr<VeridexResolver>>& resolvers) {
for (const std::unique_ptr<VeridexResolver>& resolver : resolvers) {
CollectAccesses(resolver.get());
}
-
- Dump(std::cout);
}
-void HiddenApiFinder::Dump(std::ostream& os) {
+void HiddenApiFinder::Dump(std::ostream& os,
+ HiddenApiStats* stats,
+ bool dump_reflection) {
static const char* kPrefix = " ";
- uint32_t count = 0;
- uint32_t linking_count = method_locations_.size() + field_locations_.size();
- uint32_t api_counts[4] = {0, 0, 0, 0};
+ stats->linking_count = method_locations_.size() + field_locations_.size();
// Dump methods from hidden APIs linked against.
for (const std::pair<std::string, std::vector<MethodReference>>& pair : method_locations_) {
HiddenApiAccessFlags::ApiList api_list = hidden_api_.GetApiList(pair.first);
- api_counts[api_list]++;
- os << "#" << ++count << ": Linking " << api_list << " " << pair.first << " use(s):";
+ stats->api_counts[api_list]++;
+ os << "#" << ++stats->count << ": Linking " << api_list << " " << pair.first << " use(s):";
os << std::endl;
for (const MethodReference& ref : pair.second) {
- os << kPrefix << GetApiMethodName(ref) << std::endl;
+ os << kPrefix << HiddenApi::GetApiMethodName(ref) << std::endl;
}
os << std::endl;
}
@@ -226,42 +220,35 @@ void HiddenApiFinder::Dump(std::ostream& os) {
// Dump fields from hidden APIs linked against.
for (const std::pair<std::string, std::vector<MethodReference>>& pair : field_locations_) {
HiddenApiAccessFlags::ApiList api_list = hidden_api_.GetApiList(pair.first);
- api_counts[api_list]++;
- os << "#" << ++count << ": Linking " << api_list << " " << pair.first << " use(s):";
+ stats->api_counts[api_list]++;
+ os << "#" << ++stats->count << ": Linking " << api_list << " " << pair.first << " use(s):";
os << std::endl;
for (const MethodReference& ref : pair.second) {
- os << kPrefix << GetApiMethodName(ref) << std::endl;
+ os << kPrefix << HiddenApi::GetApiMethodName(ref) << std::endl;
}
os << std::endl;
}
- // Dump potential reflection uses.
- for (const std::string& cls : classes_) {
- for (const std::string& name : strings_) {
- std::string full_name = cls + "->" + name;
- HiddenApiAccessFlags::ApiList api_list = hidden_api_.GetApiList(full_name);
- api_counts[api_list]++;
- if (api_list != HiddenApiAccessFlags::kWhitelist) {
- os << "#" << ++count << ": Reflection " << api_list << " " << full_name
- << " potential use(s):";
- os << std::endl;
- for (const MethodReference& ref : reflection_locations_[name]) {
- os << kPrefix << GetApiMethodName(ref) << std::endl;
+ if (dump_reflection) {
+ // Dump potential reflection uses.
+ for (const std::string& cls : classes_) {
+ for (const std::string& name : strings_) {
+ std::string full_name = cls + "->" + name;
+ HiddenApiAccessFlags::ApiList api_list = hidden_api_.GetApiList(full_name);
+ stats->api_counts[api_list]++;
+ if (api_list != HiddenApiAccessFlags::kWhitelist) {
+ stats->reflection_count++;
+ os << "#" << ++stats->count << ": Reflection " << api_list << " " << full_name
+ << " potential use(s):";
+ os << std::endl;
+ for (const MethodReference& ref : reflection_locations_[name]) {
+ os << kPrefix << HiddenApi::GetApiMethodName(ref) << std::endl;
+ }
+ os << std::endl;
}
- os << std::endl;
}
}
}
-
- os << count << " hidden API(s) used: "
- << linking_count << " linked against, "
- << count - linking_count << " potentially through reflection" << std::endl;
- os << kPrefix << api_counts[HiddenApiAccessFlags::kBlacklist]
- << " in blacklist" << std::endl;
- os << kPrefix << api_counts[HiddenApiAccessFlags::kDarkGreylist]
- << " in dark greylist" << std::endl;
- os << kPrefix << api_counts[HiddenApiAccessFlags::kLightGreylist]
- << " in light greylist" << std::endl;
}
} // namespace art
diff --git a/tools/veridex/hidden_api_finder.h b/tools/veridex/hidden_api_finder.h
index 243079c187..f7d3dc832d 100644
--- a/tools/veridex/hidden_api_finder.h
+++ b/tools/veridex/hidden_api_finder.h
@@ -27,6 +27,7 @@
namespace art {
class HiddenApi;
+struct HiddenApiStats;
class VeridexResolver;
/**
@@ -40,11 +41,12 @@ class HiddenApiFinder {
// hidden API uses.
void Run(const std::vector<std::unique_ptr<VeridexResolver>>& app_resolvers);
+ void Dump(std::ostream& os, HiddenApiStats* stats, bool dump_reflection);
+
private:
void CollectAccesses(VeridexResolver* resolver);
void CheckMethod(uint32_t method_idx, VeridexResolver* resolver, MethodReference ref);
void CheckField(uint32_t field_idx, VeridexResolver* resolver, MethodReference ref);
- void Dump(std::ostream& os);
const HiddenApi& hidden_api_;
std::set<std::string> classes_;
diff --git a/tools/veridex/precise_hidden_api_finder.cc b/tools/veridex/precise_hidden_api_finder.cc
new file mode 100644
index 0000000000..2092af3d75
--- /dev/null
+++ b/tools/veridex/precise_hidden_api_finder.cc
@@ -0,0 +1,92 @@
+/*
+ * 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 "precise_hidden_api_finder.h"
+
+#include "dex/code_item_accessors-inl.h"
+#include "dex/dex_instruction-inl.h"
+#include "dex/dex_file.h"
+#include "dex/method_reference.h"
+#include "flow_analysis.h"
+#include "hidden_api.h"
+#include "resolver.h"
+#include "veridex.h"
+
+#include <iostream>
+
+namespace art {
+
+void PreciseHiddenApiFinder::Run(const std::vector<std::unique_ptr<VeridexResolver>>& resolvers) {
+ for (const std::unique_ptr<VeridexResolver>& resolver : resolvers) {
+ const DexFile& dex_file = resolver->GetDexFile();
+ size_t class_def_count = dex_file.NumClassDefs();
+ for (size_t class_def_index = 0; class_def_index < class_def_count; ++class_def_index) {
+ const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index);
+ const uint8_t* class_data = dex_file.GetClassData(class_def);
+ if (class_data == nullptr) {
+ // Empty class.
+ continue;
+ }
+ ClassDataItemIterator it(dex_file, class_data);
+ it.SkipAllFields();
+ for (; it.HasNextMethod(); it.Next()) {
+ const DexFile::CodeItem* code_item = it.GetMethodCodeItem();
+ if (code_item == nullptr) {
+ continue;
+ }
+ CodeItemDataAccessor code_item_accessor(dex_file, code_item);
+ VeriFlowAnalysis ana(resolver.get(), code_item_accessor);
+ ana.Run();
+ if (!ana.GetFieldUses().empty()) {
+ field_uses_[MethodReference(&dex_file, it.GetMemberIndex())] = ana.GetFieldUses();
+ }
+ if (!ana.GetMethodUses().empty()) {
+ method_uses_[MethodReference(&dex_file, it.GetMemberIndex())] = ana.GetMethodUses();
+ }
+ }
+ }
+ }
+}
+
+void PreciseHiddenApiFinder::Dump(std::ostream& os, HiddenApiStats* stats) {
+ static const char* kPrefix = " ";
+ for (auto kinds : { field_uses_, method_uses_ }) {
+ for (auto it : kinds) {
+ MethodReference ref = it.first;
+ for (const std::pair<RegisterValue, RegisterValue>& info : it.second) {
+ if ((info.first.GetSource() == RegisterSource::kClass ||
+ info.first.GetSource() == RegisterSource::kString) &&
+ info.second.GetSource() == RegisterSource::kString) {
+ std::string cls(info.first.ToString());
+ std::string name(info.second.ToString());
+ std::string full_name = cls + "->" + name;
+ HiddenApiAccessFlags::ApiList api_list = hidden_api_.GetApiList(full_name);
+ stats->api_counts[api_list]++;
+ if (api_list != HiddenApiAccessFlags::kWhitelist) {
+ ++stats->reflection_count;
+ os << "#" << ++stats->count << ": Reflection " << api_list << " " << full_name
+ << " use:";
+ os << std::endl;
+ os << kPrefix << HiddenApi::GetApiMethodName(ref) << std::endl;
+ os << std::endl;
+ }
+ }
+ }
+ }
+ }
+}
+
+} // namespace art
diff --git a/tools/veridex/precise_hidden_api_finder.h b/tools/veridex/precise_hidden_api_finder.h
new file mode 100644
index 0000000000..22744a6f1f
--- /dev/null
+++ b/tools/veridex/precise_hidden_api_finder.h
@@ -0,0 +1,55 @@
+/*
+ * 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_TOOLS_VERIDEX_PRECISE_HIDDEN_API_FINDER_H_
+#define ART_TOOLS_VERIDEX_PRECISE_HIDDEN_API_FINDER_H_
+
+#include "dex/method_reference.h"
+#include "flow_analysis.h"
+
+#include <iostream>
+#include <map>
+#include <set>
+#include <string>
+
+namespace art {
+
+class HiddenApi;
+struct HiddenApiStats;
+class VeridexResolver;
+
+/**
+ * Reports known uses of hidden APIs from reflection.
+ */
+class PreciseHiddenApiFinder {
+ public:
+ explicit PreciseHiddenApiFinder(const HiddenApi& hidden_api) : hidden_api_(hidden_api) {}
+
+ // Iterate over the dex files associated with the passed resolvers to report
+ // hidden API uses.
+ void Run(const std::vector<std::unique_ptr<VeridexResolver>>& app_resolvers);
+
+ void Dump(std::ostream& os, HiddenApiStats* stats);
+
+ private:
+ const HiddenApi& hidden_api_;
+ std::map<MethodReference, std::vector<std::pair<RegisterValue, RegisterValue>>> field_uses_;
+ std::map<MethodReference, std::vector<std::pair<RegisterValue, RegisterValue>>> method_uses_;
+};
+
+} // namespace art
+
+#endif // ART_TOOLS_VERIDEX_PRECISE_HIDDEN_API_FINDER_H_
diff --git a/tools/veridex/resolver.cc b/tools/veridex/resolver.cc
index 13dda5c199..9113039b04 100644
--- a/tools/veridex/resolver.cc
+++ b/tools/veridex/resolver.cc
@@ -59,6 +59,14 @@ void VeridexResolver::Run() {
static bool HasSameNameAndSignature(const DexFile& dex_file,
const DexFile::MethodId& method_id,
const char* method_name,
+ const char* type) {
+ return strcmp(method_name, dex_file.GetMethodName(method_id)) == 0 &&
+ strcmp(type, dex_file.GetMethodSignature(method_id).ToString().c_str()) == 0;
+}
+
+static bool HasSameNameAndSignature(const DexFile& dex_file,
+ const DexFile::MethodId& method_id,
+ const char* method_name,
const Signature& signature) {
return strcmp(method_name, dex_file.GetMethodName(method_id)) == 0 &&
dex_file.GetMethodSignature(method_id) == signature;
@@ -241,6 +249,34 @@ VeriField VeridexResolver::LookupFieldIn(const VeriClass& kls,
return nullptr;
}
+VeriMethod VeridexResolver::LookupDeclaredMethodIn(const VeriClass& kls,
+ const char* method_name,
+ const char* type) const {
+ if (kls.IsPrimitive()) {
+ return nullptr;
+ }
+ if (kls.IsArray()) {
+ return nullptr;
+ }
+ VeridexResolver* resolver = GetResolverOf(kls);
+ const DexFile& other_dex_file = resolver->dex_file_;
+ const uint8_t* class_data = other_dex_file.GetClassData(*kls.GetClassDef());
+ if (class_data != nullptr) {
+ ClassDataItemIterator it(other_dex_file, class_data);
+ it.SkipAllFields();
+ for (; it.HasNextMethod(); it.Next()) {
+ const DexFile::MethodId& other_method_id = other_dex_file.GetMethodId(it.GetMemberIndex());
+ if (HasSameNameAndSignature(other_dex_file,
+ other_method_id,
+ method_name,
+ type)) {
+ return it.DataPointer();
+ }
+ }
+ }
+ return nullptr;
+}
+
VeriMethod VeridexResolver::GetMethod(uint32_t method_index) {
VeriMethod method_info = method_infos_[method_index];
if (method_info == nullptr) {
diff --git a/tools/veridex/resolver.h b/tools/veridex/resolver.h
index 06c8aa70c5..52b15fe0ed 100644
--- a/tools/veridex/resolver.h
+++ b/tools/veridex/resolver.h
@@ -66,6 +66,11 @@ class VeridexResolver {
const char* field_name,
const char* field_type);
+ // Lookup a method declared in `kls`.
+ VeriMethod LookupDeclaredMethodIn(const VeriClass& kls,
+ const char* method_name,
+ const char* signature) const;
+
// Resolve all type_id/method_id/field_id.
void ResolveAll();
diff --git a/tools/veridex/veridex.cc b/tools/veridex/veridex.cc
index 16e9f0e55b..6e72faaf57 100644
--- a/tools/veridex/veridex.cc
+++ b/tools/veridex/veridex.cc
@@ -22,6 +22,7 @@
#include "dex/dex_file_loader.h"
#include "hidden_api.h"
#include "hidden_api_finder.h"
+#include "precise_hidden_api_finder.h"
#include "resolver.h"
#include <sstream>
@@ -47,8 +48,18 @@ VeriClass* VeriClass::float_ = &f_;
VeriClass* VeriClass::double_ = &d_;
VeriClass* VeriClass::long_ = &j_;
VeriClass* VeriClass::void_ = &v_;
+
// Will be set after boot classpath has been resolved.
VeriClass* VeriClass::object_ = nullptr;
+VeriClass* VeriClass::class_ = nullptr;
+VeriClass* VeriClass::string_ = nullptr;
+VeriClass* VeriClass::throwable_ = nullptr;
+VeriMethod VeriClass::forName_ = nullptr;
+VeriMethod VeriClass::getField_ = nullptr;
+VeriMethod VeriClass::getDeclaredField_ = nullptr;
+VeriMethod VeriClass::getMethod_ = nullptr;
+VeriMethod VeriClass::getDeclaredMethod_ = nullptr;
+VeriMethod VeriClass::getClass_ = nullptr;
struct VeridexOptions {
const char* dex_file = nullptr;
@@ -56,6 +67,7 @@ struct VeridexOptions {
const char* blacklist = nullptr;
const char* light_greylist = nullptr;
const char* dark_greylist = nullptr;
+ bool precise = true;
};
static const char* Substr(const char* str, int index) {
@@ -76,6 +88,7 @@ static void ParseArgs(VeridexOptions* options, int argc, char** argv) {
static const char* kBlacklistOption = "--blacklist=";
static const char* kDarkGreylistOption = "--dark-greylist=";
static const char* kLightGreylistOption = "--light-greylist=";
+ static const char* kImprecise = "--imprecise";
for (int i = 0; i < argc; ++i) {
if (StartsWith(argv[i], kDexFileOption)) {
@@ -88,6 +101,8 @@ static void ParseArgs(VeridexOptions* options, int argc, char** argv) {
options->dark_greylist = Substr(argv[i], strlen(kDarkGreylistOption));
} else if (StartsWith(argv[i], kLightGreylistOption)) {
options->light_greylist = Substr(argv[i], strlen(kLightGreylistOption));
+ } else if (strcmp(argv[i], kImprecise) == 0) {
+ options->precise = false;
}
}
}
@@ -157,21 +172,70 @@ class Veridex {
std::vector<std::unique_ptr<VeridexResolver>> boot_resolvers;
Resolve(boot_dex_files, resolver_map, type_map, &boot_resolvers);
- // Now that boot classpath has been resolved, fill j.l.Object.
+ // Now that boot classpath has been resolved, fill classes and reflection
+ // methods.
VeriClass::object_ = type_map["Ljava/lang/Object;"];
+ VeriClass::class_ = type_map["Ljava/lang/Class;"];
+ VeriClass::string_ = type_map["Ljava/lang/String;"];
+ VeriClass::throwable_ = type_map["Ljava/lang/Throwable;"];
+ VeriClass::forName_ = boot_resolvers[0]->LookupDeclaredMethodIn(
+ *VeriClass::class_, "forName", "(Ljava/lang/String;)Ljava/lang/Class;");
+ VeriClass::getField_ = boot_resolvers[0]->LookupDeclaredMethodIn(
+ *VeriClass::class_, "getField", "(Ljava/lang/String;)Ljava/lang/reflect/Field;");
+ VeriClass::getDeclaredField_ = boot_resolvers[0]->LookupDeclaredMethodIn(
+ *VeriClass::class_, "getDeclaredField", "(Ljava/lang/String;)Ljava/lang/reflect/Field;");
+ VeriClass::getMethod_ = boot_resolvers[0]->LookupDeclaredMethodIn(
+ *VeriClass::class_,
+ "getMethod",
+ "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;");
+ VeriClass::getDeclaredMethod_ = boot_resolvers[0]->LookupDeclaredMethodIn(
+ *VeriClass::class_,
+ "getDeclaredMethod",
+ "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;");
+ VeriClass::getClass_ = boot_resolvers[0]->LookupDeclaredMethodIn(
+ *VeriClass::object_, "getClass", "()Ljava/lang/Class;");
std::vector<std::unique_ptr<VeridexResolver>> app_resolvers;
Resolve(app_dex_files, resolver_map, type_map, &app_resolvers);
// Find and log uses of hidden APIs.
HiddenApi hidden_api(options.blacklist, options.dark_greylist, options.light_greylist);
+ HiddenApiStats stats;
+
HiddenApiFinder api_finder(hidden_api);
api_finder.Run(app_resolvers);
+ api_finder.Dump(std::cout, &stats, !options.precise);
+
+ if (options.precise) {
+ PreciseHiddenApiFinder precise_api_finder(hidden_api);
+ precise_api_finder.Run(app_resolvers);
+ precise_api_finder.Dump(std::cout, &stats);
+ }
+
+ DumpSummaryStats(std::cout, stats);
+
+ if (options.precise) {
+ std::cout << "To run an analysis that can give more reflection accesses, " << std::endl
+ << "but could include false positives, pass the --imprecise flag. " << std::endl;
+ }
return 0;
}
private:
+ static void DumpSummaryStats(std::ostream& os, const HiddenApiStats& stats) {
+ static const char* kPrefix = " ";
+ os << stats.count << " hidden API(s) used: "
+ << stats.linking_count << " linked against, "
+ << stats.reflection_count << " through reflection" << std::endl;
+ os << kPrefix << stats.api_counts[HiddenApiAccessFlags::kBlacklist]
+ << " in blacklist" << std::endl;
+ os << kPrefix << stats.api_counts[HiddenApiAccessFlags::kDarkGreylist]
+ << " in dark greylist" << std::endl;
+ os << kPrefix << stats.api_counts[HiddenApiAccessFlags::kLightGreylist]
+ << " in light greylist" << std::endl;
+ }
+
static bool Load(const std::string& filename,
std::string& content,
std::vector<std::unique_ptr<const DexFile>>* dex_files,
diff --git a/tools/veridex/veridex.h b/tools/veridex/veridex.h
index 0c928ab166..75e4845293 100644
--- a/tools/veridex/veridex.h
+++ b/tools/veridex/veridex.h
@@ -25,6 +25,18 @@
namespace art {
/**
+ * Abstraction for fields defined in dex files. Currently, that's a pointer into their
+ * `encoded_field` description.
+ */
+using VeriField = const uint8_t*;
+
+/**
+ * Abstraction for methods defined in dex files. Currently, that's a pointer into their
+ * `encoded_method` description.
+ */
+using VeriMethod = const uint8_t*;
+
+/**
* Abstraction for classes defined, or implicitly defined (for arrays and primitives)
* in dex files.
*/
@@ -52,6 +64,9 @@ class VeriClass {
const DexFile::ClassDef* GetClassDef() const { return class_def_; }
static VeriClass* object_;
+ static VeriClass* class_;
+ static VeriClass* string_;
+ static VeriClass* throwable_;
static VeriClass* boolean_;
static VeriClass* byte_;
static VeriClass* char_;
@@ -62,23 +77,26 @@ class VeriClass {
static VeriClass* long_;
static VeriClass* void_;
+ static VeriMethod forName_;
+ static VeriMethod getField_;
+ static VeriMethod getDeclaredField_;
+ static VeriMethod getMethod_;
+ static VeriMethod getDeclaredMethod_;
+ static VeriMethod getClass_;
+
private:
Primitive::Type kind_;
uint8_t dimensions_;
const DexFile::ClassDef* class_def_;
};
-/**
- * Abstraction for fields defined in dex files. Currently, that's a pointer into their
- * `encoded_field` description.
- */
-using VeriField = const uint8_t*;
+inline bool IsGetMethod(VeriMethod method) {
+ return method == VeriClass::getMethod_ || method == VeriClass::getDeclaredMethod_;
+}
-/**
- * Abstraction for methods defined in dex files. Currently, that's a pointer into their
- * `encoded_method` description.
- */
-using VeriMethod = const uint8_t*;
+inline bool IsGetField(VeriMethod method) {
+ return method == VeriClass::getField_ || method == VeriClass::getDeclaredField_;
+}
/**
* Map from name to VeriClass to quickly lookup classes.