Merge "Handle multidex in LocationIsOnSystemFramework." into pi-dev
diff --git a/libdexfile/dex/modifiers.h b/libdexfile/dex/modifiers.h
index 2425a58..be82fff 100644
--- a/libdexfile/dex/modifiers.h
+++ b/libdexfile/dex/modifiers.h
@@ -58,6 +58,7 @@
static constexpr uint32_t kAccSkipAccessChecks = 0x00080000; // method (runtime, not native)
// Used by a class to denote that the verifier has attempted to check it at least once.
static constexpr uint32_t kAccVerificationAttempted = 0x00080000; // class (runtime)
+static constexpr uint32_t kAccSkipHiddenApiChecks = 0x00100000; // class (runtime)
// This is set by the class linker during LinkInterfaceMethods. It is used by a method to represent
// that it was copied from its declaring class into another class. All methods marked kAccMiranda
// and kAccDefaultConflict will have this bit set. Any kAccDefault method contained in the methods_
diff --git a/runtime/hidden_api.h b/runtime/hidden_api.h
index 65c6406..8e21fd3 100644
--- a/runtime/hidden_api.h
+++ b/runtime/hidden_api.h
@@ -143,31 +143,45 @@
// Returns true if the caller is either loaded by the boot strap class loader or comes from
// a dex file located in ${ANDROID_ROOT}/framework/.
ALWAYS_INLINE
-inline bool IsCallerInPlatformDex(ObjPtr<mirror::ClassLoader> caller_class_loader,
- ObjPtr<mirror::DexCache> caller_dex_cache)
+inline bool IsCallerTrusted(ObjPtr<mirror::Class> caller,
+ ObjPtr<mirror::ClassLoader> caller_class_loader,
+ ObjPtr<mirror::DexCache> caller_dex_cache)
REQUIRES_SHARED(Locks::mutator_lock_) {
if (caller_class_loader.IsNull()) {
+ // Boot class loader.
return true;
- } else if (caller_dex_cache.IsNull()) {
- return false;
- } else {
- const DexFile* caller_dex_file = caller_dex_cache->GetDexFile();
- return caller_dex_file != nullptr && caller_dex_file->IsPlatformDexFile();
}
+
+ if (!caller_dex_cache.IsNull()) {
+ const DexFile* caller_dex_file = caller_dex_cache->GetDexFile();
+ if (caller_dex_file != nullptr && caller_dex_file->IsPlatformDexFile()) {
+ // Caller is in a platform dex file.
+ return true;
+ }
+ }
+
+ if (!caller.IsNull() &&
+ caller->ShouldSkipHiddenApiChecks() &&
+ Runtime::Current()->IsJavaDebuggable()) {
+ // We are in debuggable mode and this caller has been marked trusted.
+ return true;
+ }
+
+ return false;
}
} // namespace detail
// Returns true if access to `member` should be denied to the caller of the
-// reflective query. The decision is based on whether the caller is in the
-// platform or not. Because different users of this function determine this
-// in a different way, `fn_caller_in_platform(self)` is called and should
-// return true if the caller is located in the platform.
+// reflective query. The decision is based on whether the caller is trusted or
+// not. Because different users of this function determine this in a different
+// way, `fn_caller_is_trusted(self)` is called and should return true if the
+// caller is allowed to access the platform.
// This function might print warnings into the log if the member is hidden.
template<typename T>
inline Action GetMemberAction(T* member,
Thread* self,
- std::function<bool(Thread*)> fn_caller_in_platform,
+ std::function<bool(Thread*)> fn_caller_is_trusted,
AccessMethod access_method)
REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK(member != nullptr);
@@ -188,8 +202,8 @@
// 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.
+ if (fn_caller_is_trusted(self)) {
+ // Caller is trusted. Exit.
return kAllow;
}
@@ -197,10 +211,9 @@
return detail::GetMemberActionImpl(member, api_list, action, access_method);
}
-inline bool IsCallerInPlatformDex(ObjPtr<mirror::Class> caller)
- REQUIRES_SHARED(Locks::mutator_lock_) {
+inline bool IsCallerTrusted(ObjPtr<mirror::Class> caller) REQUIRES_SHARED(Locks::mutator_lock_) {
return !caller.IsNull() &&
- detail::IsCallerInPlatformDex(caller->GetClassLoader(), caller->GetDexCache());
+ detail::IsCallerTrusted(caller, caller->GetClassLoader(), caller->GetDexCache());
}
// Returns true if access to `member` should be denied to a caller loaded with
@@ -212,10 +225,11 @@
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);
+ bool is_caller_trusted =
+ detail::IsCallerTrusted(/* caller */ nullptr, caller_class_loader, caller_dex_cache);
return GetMemberAction(member,
/* thread */ nullptr,
- [caller_in_platform] (Thread*) { return caller_in_platform; },
+ [is_caller_trusted] (Thread*) { return is_caller_trusted; },
access_method);
}
diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc
index a757960..57d3b9f 100644
--- a/runtime/jit/jit.cc
+++ b/runtime/jit/jit.cc
@@ -183,10 +183,12 @@
if (jit_compiler_handle_ == nullptr && !LoadCompiler(error_msg)) {
return nullptr;
}
+ bool code_cache_only_for_profile_data = !options->UseJitCompilation();
jit->code_cache_.reset(JitCodeCache::Create(
options->GetCodeCacheInitialCapacity(),
options->GetCodeCacheMaxCapacity(),
jit->generate_debug_info_,
+ code_cache_only_for_profile_data,
error_msg));
if (jit->GetCodeCache() == nullptr) {
return nullptr;
diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
index f0f0df4..1048c80 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -50,7 +50,6 @@
namespace art {
namespace jit {
-static constexpr int kProtAll = PROT_READ | PROT_WRITE | PROT_EXEC;
static constexpr int kProtData = PROT_READ | PROT_WRITE;
static constexpr int kProtCode = PROT_READ | PROT_EXEC;
@@ -161,6 +160,7 @@
JitCodeCache* JitCodeCache::Create(size_t initial_capacity,
size_t max_capacity,
bool generate_debug_info,
+ bool used_only_for_profile_data,
std::string* error_msg) {
ScopedTrace trace(__PRETTY_FUNCTION__);
CHECK_GE(max_capacity, initial_capacity);
@@ -184,6 +184,15 @@
return nullptr;
}
+ // Decide how we should map the code and data sections.
+ // If we use the code cache just for profiling we do not need to map the code section as
+ // executable.
+ // NOTE 1: this is yet another workaround to bypass strict SElinux policies in order to be able
+ // to profile system server.
+ // NOTE 2: We could just not create the code section at all but we will need to
+ // special case too many cases.
+ int memmap_flags_prot_code = used_only_for_profile_data ? (kProtCode & ~PROT_EXEC) : kProtCode;
+
std::string error_str;
// Map name specific for android_os_Debug.cpp accounting.
// Map in low 4gb to simplify accessing root tables for x86_64.
@@ -216,8 +225,11 @@
DCHECK_EQ(code_size + data_size, max_capacity);
uint8_t* divider = data_map->Begin() + data_size;
- MemMap* code_map =
- data_map->RemapAtEnd(divider, "jit-code-cache", kProtAll, &error_str, use_ashmem);
+ MemMap* code_map = data_map->RemapAtEnd(
+ divider,
+ "jit-code-cache",
+ memmap_flags_prot_code | PROT_WRITE,
+ &error_str, use_ashmem);
if (code_map == nullptr) {
std::ostringstream oss;
oss << "Failed to create read write execute cache: " << error_str << " size=" << max_capacity;
@@ -229,7 +241,13 @@
code_size = initial_capacity - data_size;
DCHECK_EQ(code_size + data_size, initial_capacity);
return new JitCodeCache(
- code_map, data_map.release(), code_size, data_size, max_capacity, garbage_collect_code);
+ code_map,
+ data_map.release(),
+ code_size,
+ data_size,
+ max_capacity,
+ garbage_collect_code,
+ memmap_flags_prot_code);
}
JitCodeCache::JitCodeCache(MemMap* code_map,
@@ -237,7 +255,8 @@
size_t initial_code_capacity,
size_t initial_data_capacity,
size_t max_capacity,
- bool garbage_collect_code)
+ bool garbage_collect_code,
+ int memmap_flags_prot_code)
: lock_("Jit code cache", kJitCodeCacheLock),
lock_cond_("Jit code cache condition variable", lock_),
collection_in_progress_(false),
@@ -259,7 +278,8 @@
histogram_code_memory_use_("Memory used for compiled code", 16),
histogram_profiling_info_memory_use_("Memory used for profiling info", 16),
is_weak_access_enabled_(true),
- inline_cache_cond_("Jit inline cache condition variable", lock_) {
+ inline_cache_cond_("Jit inline cache condition variable", lock_),
+ memmap_flags_prot_code_(memmap_flags_prot_code) {
DCHECK_GE(max_capacity, initial_code_capacity + initial_data_capacity);
code_mspace_ = create_mspace_with_base(code_map_->Begin(), code_end_, false /*locked*/);
@@ -275,7 +295,7 @@
"mprotect jit code cache",
code_map_->Begin(),
code_map_->Size(),
- kProtCode);
+ memmap_flags_prot_code_);
CheckedCall(mprotect,
"mprotect jit data cache",
data_map_->Begin(),
@@ -328,28 +348,32 @@
class ScopedCodeCacheWrite : ScopedTrace {
public:
- explicit ScopedCodeCacheWrite(MemMap* code_map, bool only_for_tlb_shootdown = false)
+ explicit ScopedCodeCacheWrite(const JitCodeCache* const code_cache,
+ bool only_for_tlb_shootdown = false)
: ScopedTrace("ScopedCodeCacheWrite"),
- code_map_(code_map),
+ code_cache_(code_cache),
only_for_tlb_shootdown_(only_for_tlb_shootdown) {
ScopedTrace trace("mprotect all");
- CheckedCall(mprotect,
- "make code writable",
- code_map_->Begin(),
- only_for_tlb_shootdown_ ? kPageSize : code_map_->Size(),
- kProtAll);
+ CheckedCall(
+ mprotect,
+ "make code writable",
+ code_cache_->code_map_->Begin(),
+ only_for_tlb_shootdown_ ? kPageSize : code_cache_->code_map_->Size(),
+ code_cache_->memmap_flags_prot_code_ | PROT_WRITE);
}
+
~ScopedCodeCacheWrite() {
ScopedTrace trace("mprotect code");
- CheckedCall(mprotect,
- "make code protected",
- code_map_->Begin(),
- only_for_tlb_shootdown_ ? kPageSize : code_map_->Size(),
- kProtCode);
+ CheckedCall(
+ mprotect,
+ "make code protected",
+ code_cache_->code_map_->Begin(),
+ only_for_tlb_shootdown_ ? kPageSize : code_cache_->code_map_->Size(),
+ code_cache_->memmap_flags_prot_code_);
}
private:
- MemMap* const code_map_;
+ const JitCodeCache* const code_cache_;
// If we're using ScopedCacheWrite only for TLB shootdown, we limit the scope of mprotect to
// one page.
@@ -571,7 +595,7 @@
// so it's possible for the same method_header to start representing
// different compile code.
MutexLock mu(Thread::Current(), lock_);
- ScopedCodeCacheWrite scc(code_map_.get());
+ ScopedCodeCacheWrite scc(this);
for (const OatQuickMethodHeader* method_header : method_headers) {
FreeCode(method_header->GetCode());
}
@@ -590,7 +614,7 @@
// with the classlinker_classes_lock_ held, and suspending ourselves could
// lead to a deadlock.
{
- ScopedCodeCacheWrite scc(code_map_.get());
+ ScopedCodeCacheWrite scc(this);
for (auto it = jni_stubs_map_.begin(); it != jni_stubs_map_.end();) {
it->second.RemoveMethodsIn(alloc);
if (it->second.GetMethods().empty()) {
@@ -729,7 +753,7 @@
MutexLock mu(self, lock_);
WaitForPotentialCollectionToComplete(self);
{
- ScopedCodeCacheWrite scc(code_map_.get());
+ ScopedCodeCacheWrite scc(this);
memory = AllocateCode(total_size);
if (memory == nullptr) {
return nullptr;
@@ -817,7 +841,7 @@
{
// Flush data cache, as compiled code references literals in it.
// We also need a TLB shootdown to act as memory barrier across cores.
- ScopedCodeCacheWrite ccw(code_map_.get(), /* only_for_tlb_shootdown */ true);
+ ScopedCodeCacheWrite ccw(this, /* only_for_tlb_shootdown */ true);
FlushDataCache(reinterpret_cast<char*>(roots_data),
reinterpret_cast<char*>(roots_data + data_size));
}
@@ -895,7 +919,7 @@
}
bool in_cache = false;
- ScopedCodeCacheWrite ccw(code_map_.get());
+ ScopedCodeCacheWrite ccw(this);
if (UNLIKELY(method->IsNative())) {
auto it = jni_stubs_map_.find(JniStubKey(method));
if (it != jni_stubs_map_.end() && it->second.RemoveMethod(method)) {
@@ -1122,7 +1146,7 @@
DCHECK_EQ(per_space_footprint * 2, new_footprint);
mspace_set_footprint_limit(data_mspace_, per_space_footprint);
{
- ScopedCodeCacheWrite scc(code_map_.get());
+ ScopedCodeCacheWrite scc(this);
mspace_set_footprint_limit(code_mspace_, per_space_footprint);
}
}
@@ -1290,7 +1314,7 @@
std::unordered_set<OatQuickMethodHeader*> method_headers;
{
MutexLock mu(self, lock_);
- ScopedCodeCacheWrite scc(code_map_.get());
+ ScopedCodeCacheWrite scc(this);
// Iterate over all compiled code and remove entries that are not marked.
for (auto it = jni_stubs_map_.begin(); it != jni_stubs_map_.end();) {
JniStubData* data = &it->second;
diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h
index dfa7ac0..92deb97 100644
--- a/runtime/jit/jit_code_cache.h
+++ b/runtime/jit/jit_code_cache.h
@@ -68,6 +68,7 @@
namespace jit {
class JitInstrumentationCache;
+class ScopedCodeCacheWrite;
// Alignment in bits that will suit all architectures.
static constexpr int kJitCodeAlignment = 16;
@@ -88,6 +89,7 @@
static JitCodeCache* Create(size_t initial_capacity,
size_t max_capacity,
bool generate_debug_info,
+ bool used_only_for_profile_data,
std::string* error_msg);
~JitCodeCache();
@@ -270,7 +272,8 @@
size_t initial_code_capacity,
size_t initial_data_capacity,
size_t max_capacity,
- bool garbage_collect_code);
+ bool garbage_collect_code,
+ int memmap_flags_prot_code);
// Internal version of 'CommitCode' that will not retry if the
// allocation fails. Return null if the allocation fails.
@@ -446,7 +449,12 @@
// Condition to wait on for accessing inline caches.
ConditionVariable inline_cache_cond_ GUARDED_BY(lock_);
+ // Mapping flags for the code section.
+ const int memmap_flags_prot_code_;
+
friend class art::JitJniStubTestHelper;
+ friend class ScopedCodeCacheWrite;
+
DISALLOW_IMPLICIT_CONSTRUCTORS(JitCodeCache);
};
diff --git a/runtime/jit/profile_compilation_info.cc b/runtime/jit/profile_compilation_info.cc
index a473f3f..1208173 100644
--- a/runtime/jit/profile_compilation_info.cc
+++ b/runtime/jit/profile_compilation_info.cc
@@ -98,9 +98,7 @@
ProfileCompilationInfo::~ProfileCompilationInfo() {
VLOG(profiler) << Dumpable<MemStats>(allocator_.GetMemStats());
- for (DexFileData* data : info_) {
- delete data;
- }
+ ClearData();
}
void ProfileCompilationInfo::DexPcData::AddClass(uint16_t dex_profile_idx,
@@ -2107,4 +2105,12 @@
return true;
}
+void ProfileCompilationInfo::ClearData() {
+ for (DexFileData* data : info_) {
+ delete data;
+ }
+ info_.clear();
+ profile_key_map_.clear();
+}
+
} // namespace art
diff --git a/runtime/jit/profile_compilation_info.h b/runtime/jit/profile_compilation_info.h
index 5c4b9e7..90204cd 100644
--- a/runtime/jit/profile_compilation_info.h
+++ b/runtime/jit/profile_compilation_info.h
@@ -442,6 +442,9 @@
// Checks if the profile is empty.
bool IsEmpty() const;
+ // Clears all the data from the profile.
+ void ClearData();
+
private:
enum ProfileLoadStatus {
kProfileLoadWouldOverwiteData,
diff --git a/runtime/jit/profile_compilation_info_test.cc b/runtime/jit/profile_compilation_info_test.cc
index 0ebadc0..7e12dde 100644
--- a/runtime/jit/profile_compilation_info_test.cc
+++ b/runtime/jit/profile_compilation_info_test.cc
@@ -1339,4 +1339,33 @@
ASSERT_TRUE(loaded_info.Equals(expected_info));
}
+
+TEST_F(ProfileCompilationInfoTest, ClearData) {
+ ProfileCompilationInfo info;
+ for (uint16_t i = 0; i < 10; i++) {
+ ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, /* method_idx */ i, &info));
+ }
+ ASSERT_FALSE(IsEmpty(info));
+ info.ClearData();
+ ASSERT_TRUE(IsEmpty(info));
+}
+
+TEST_F(ProfileCompilationInfoTest, ClearDataAndSave) {
+ ProfileCompilationInfo info;
+ for (uint16_t i = 0; i < 10; i++) {
+ ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, /* method_idx */ i, &info));
+ }
+ info.ClearData();
+
+ ScratchFile profile;
+ ASSERT_TRUE(info.Save(GetFd(profile)));
+ ASSERT_EQ(0, profile.GetFile()->Flush());
+
+ // Check that we get back what we saved.
+ ProfileCompilationInfo loaded_info;
+ ASSERT_TRUE(profile.GetFile()->ResetOffset());
+ ASSERT_TRUE(loaded_info.Load(GetFd(profile)));
+ ASSERT_TRUE(loaded_info.Equals(info));
+}
+
} // namespace art
diff --git a/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc
index fe4b88d..5ca5a7c 100644
--- a/runtime/jit/profile_saver.cc
+++ b/runtime/jit/profile_saver.cc
@@ -523,10 +523,24 @@
uint64_t last_save_number_of_methods = info.GetNumberOfMethods();
uint64_t last_save_number_of_classes = info.GetNumberOfResolvedClasses();
- info.AddMethods(profile_methods, ProfileCompilationInfo::MethodHotness::kFlagPostStartup);
+ // Try to add the method data. Note this may fail is the profile loaded from disk contains
+ // outdated data (e.g. the previous profiled dex files might have been updated).
+ // If this happens we clear the profile data and for the save to ensure the file is cleared.
+ if (!info.AddMethods(profile_methods,
+ ProfileCompilationInfo::MethodHotness::kFlagPostStartup)) {
+ LOG(WARNING) << "Could not add methods to the existing profiler. "
+ << "Clearing the profile data.";
+ info.ClearData();
+ force_save = true;
+ }
+
auto profile_cache_it = profile_cache_.find(filename);
if (profile_cache_it != profile_cache_.end()) {
- info.MergeWith(*(profile_cache_it->second));
+ if (!info.MergeWith(*(profile_cache_it->second))) {
+ LOG(WARNING) << "Could not merge the profile. Clearing the profile data.";
+ info.ClearData();
+ force_save = true;
+ }
}
int64_t delta_number_of_methods =
diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc
index 9dbcded..cd66a60 100644
--- a/runtime/jni_internal.cc
+++ b/runtime/jni_internal.cc
@@ -80,15 +80,15 @@
// things not rendering correctly. E.g. b/16858794
static constexpr bool kWarnJniAbort = false;
-static bool IsCallerInPlatformDex(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) {
- return hiddenapi::IsCallerInPlatformDex(GetCallingClass(self, /* num_frames */ 1));
+static bool IsCallerTrusted(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) {
+ return hiddenapi::IsCallerTrusted(GetCallingClass(self, /* num_frames */ 1));
}
template<typename T>
ALWAYS_INLINE static bool ShouldBlockAccessToMember(T* member, Thread* self)
REQUIRES_SHARED(Locks::mutator_lock_) {
hiddenapi::Action action = hiddenapi::GetMemberAction(
- member, self, IsCallerInPlatformDex, hiddenapi::kJNI);
+ member, self, IsCallerTrusted, hiddenapi::kJNI);
if (action != hiddenapi::kAllow) {
hiddenapi::NotifyHiddenApiListener(member);
}
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index 51d1376..98e25eb 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -210,6 +210,15 @@
return (GetAccessFlags() & kAccClassIsFinalizable) != 0;
}
+ ALWAYS_INLINE bool ShouldSkipHiddenApiChecks() REQUIRES_SHARED(Locks::mutator_lock_) {
+ return (GetAccessFlags() & kAccSkipHiddenApiChecks) != 0;
+ }
+
+ ALWAYS_INLINE void SetSkipHiddenApiChecks() REQUIRES_SHARED(Locks::mutator_lock_) {
+ uint32_t flags = GetAccessFlags();
+ SetAccessFlags(flags | kAccSkipHiddenApiChecks);
+ }
+
ALWAYS_INLINE void SetRecursivelyInitialized() REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK_EQ(GetLockOwnerThreadId(), Thread::Current()->GetThreadId());
uint32_t flags = GetField32(OFFSET_OF_OBJECT_MEMBER(Class, access_flags_));
diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc
index 13319c4..637e7fa 100644
--- a/runtime/native/dalvik_system_DexFile.cc
+++ b/runtime/native/dalvik_system_DexFile.cc
@@ -816,6 +816,28 @@
return static_cast<jlong>(file_size);
}
+static void DexFile_setTrusted(JNIEnv* env, jclass, jobject j_cookie) {
+ Runtime* runtime = Runtime::Current();
+ ScopedObjectAccess soa(env);
+
+ // Currently only allow this for debuggable apps.
+ if (!runtime->IsJavaDebuggable()) {
+ ThrowSecurityException("Can't exempt class, process is not debuggable.");
+ return;
+ }
+
+ std::vector<const DexFile*> dex_files;
+ const OatFile* oat_file;
+ if (!ConvertJavaArrayToDexFiles(env, j_cookie, dex_files, oat_file)) {
+ Thread::Current()->AssertPendingException();
+ return;
+ }
+
+ for (const DexFile* dex_file : dex_files) {
+ const_cast<DexFile*>(dex_file)->SetIsPlatformDexFile();
+ }
+}
+
static JNINativeMethod gMethods[] = {
NATIVE_METHOD(DexFile, closeDexFile, "(Ljava/lang/Object;)Z"),
NATIVE_METHOD(DexFile,
@@ -854,7 +876,8 @@
"(Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String;"),
NATIVE_METHOD(DexFile, getStaticSizeOfDexFile, "(Ljava/lang/Object;)J"),
NATIVE_METHOD(DexFile, getDexFileOptimizationStatus,
- "(Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String;")
+ "(Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String;"),
+ NATIVE_METHOD(DexFile, setTrusted, "(Ljava/lang/Object;)V")
};
void register_dalvik_system_DexFile(JNIEnv* env) {
diff --git a/runtime/native/dalvik_system_VMDebug.cc b/runtime/native/dalvik_system_VMDebug.cc
index fc94266..0955e86 100644
--- a/runtime/native/dalvik_system_VMDebug.cc
+++ b/runtime/native/dalvik_system_VMDebug.cc
@@ -573,6 +573,25 @@
Runtime::Current()->AttachAgent(env, filename, classloader);
}
+static void VMDebug_allowHiddenApiReflectionFrom(JNIEnv* env, jclass, jclass j_caller) {
+ Runtime* runtime = Runtime::Current();
+ ScopedObjectAccess soa(env);
+
+ if (!runtime->IsJavaDebuggable()) {
+ ThrowSecurityException("Can't exempt class, process is not debuggable.");
+ return;
+ }
+
+ StackHandleScope<1> hs(soa.Self());
+ Handle<mirror::Class> h_caller(hs.NewHandle(soa.Decode<mirror::Class>(j_caller)));
+ if (h_caller.IsNull()) {
+ ThrowNullPointerException("argument is null");
+ return;
+ }
+
+ h_caller->SetSkipHiddenApiChecks();
+}
+
static JNINativeMethod gMethods[] = {
NATIVE_METHOD(VMDebug, countInstancesOfClass, "(Ljava/lang/Class;Z)J"),
NATIVE_METHOD(VMDebug, countInstancesOfClasses, "([Ljava/lang/Class;Z)[J"),
@@ -608,6 +627,7 @@
NATIVE_METHOD(VMDebug, getRuntimeStatInternal, "(I)Ljava/lang/String;"),
NATIVE_METHOD(VMDebug, getRuntimeStatsInternal, "()[Ljava/lang/String;"),
NATIVE_METHOD(VMDebug, nativeAttachAgent, "(Ljava/lang/String;Ljava/lang/ClassLoader;)V"),
+ NATIVE_METHOD(VMDebug, allowHiddenApiReflectionFrom, "(Ljava/lang/Class;)V"),
};
void register_dalvik_system_VMDebug(JNIEnv* env) {
diff --git a/runtime/native/dalvik_system_ZygoteHooks.cc b/runtime/native/dalvik_system_ZygoteHooks.cc
index 3dd3e43..891cdfa 100644
--- a/runtime/native/dalvik_system_ZygoteHooks.cc
+++ b/runtime/native/dalvik_system_ZygoteHooks.cc
@@ -177,6 +177,7 @@
DEBUG_GENERATE_MINI_DEBUG_INFO = 1 << 11,
HIDDEN_API_ENFORCEMENT_POLICY_MASK = (1 << 12)
| (1 << 13),
+ PROFILE_SYSTEM_SERVER = 1 << 14,
// bits to shift (flags & HIDDEN_API_ENFORCEMENT_POLICY_MASK) by to get a value
// corresponding to hiddenapi::EnforcementPolicy
@@ -308,6 +309,9 @@
(runtime_flags & HIDDEN_API_ENFORCEMENT_POLICY_MASK) >> API_ENFORCEMENT_POLICY_SHIFT);
runtime_flags &= ~HIDDEN_API_ENFORCEMENT_POLICY_MASK;
+ bool profile_system_server = (runtime_flags & PROFILE_SYSTEM_SERVER) == PROFILE_SYSTEM_SERVER;
+ runtime_flags &= ~PROFILE_SYSTEM_SERVER;
+
if (runtime_flags != 0) {
LOG(ERROR) << StringPrintf("Unknown bits set in runtime_flags: %#x", runtime_flags);
}
@@ -391,7 +395,11 @@
env, is_system_server, action, isa_string.c_str());
} else {
Runtime::Current()->InitNonZygoteOrPostFork(
- env, is_system_server, Runtime::NativeBridgeAction::kUnload, nullptr);
+ env,
+ is_system_server,
+ Runtime::NativeBridgeAction::kUnload,
+ /*isa*/ nullptr,
+ profile_system_server);
}
}
diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc
index e161ed4..c07c32a 100644
--- a/runtime/native/java_lang_Class.cc
+++ b/runtime/native/java_lang_Class.cc
@@ -52,7 +52,7 @@
// Returns true if the first caller outside of the Class class or java.lang.invoke package
// is in a platform DEX file.
-static bool IsCallerInPlatformDex(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) {
+static bool IsCallerTrusted(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) {
// Walk the stack and find the first frame not from java.lang.Class and not from java.lang.invoke.
// This is very expensive. Save this till the last.
struct FirstExternalCallerVisitor : public StackVisitor {
@@ -99,7 +99,7 @@
FirstExternalCallerVisitor visitor(self);
visitor.WalkStack();
return visitor.caller != nullptr &&
- hiddenapi::IsCallerInPlatformDex(visitor.caller->GetDeclaringClass());
+ hiddenapi::IsCallerTrusted(visitor.caller->GetDeclaringClass());
}
// Returns true if the first non-ClassClass caller up the stack is not allowed to
@@ -107,7 +107,7 @@
ALWAYS_INLINE static bool ShouldEnforceHiddenApi(Thread* self)
REQUIRES_SHARED(Locks::mutator_lock_) {
hiddenapi::EnforcementPolicy policy = Runtime::Current()->GetHiddenApiEnforcementPolicy();
- return policy != hiddenapi::EnforcementPolicy::kNoChecks && !IsCallerInPlatformDex(self);
+ return policy != hiddenapi::EnforcementPolicy::kNoChecks && !IsCallerTrusted(self);
}
// Returns true if the first non-ClassClass caller up the stack should not be
@@ -116,7 +116,7 @@
ALWAYS_INLINE static bool ShouldBlockAccessToMember(T* member, Thread* self)
REQUIRES_SHARED(Locks::mutator_lock_) {
hiddenapi::Action action = hiddenapi::GetMemberAction(
- member, self, IsCallerInPlatformDex, hiddenapi::kReflection);
+ member, self, IsCallerTrusted, hiddenapi::kReflection);
if (action != hiddenapi::kAllow) {
hiddenapi::NotifyHiddenApiListener(member);
}
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 891bb8a..4062d17 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -873,7 +873,11 @@
}
void Runtime::InitNonZygoteOrPostFork(
- JNIEnv* env, bool is_system_server, NativeBridgeAction action, const char* isa) {
+ JNIEnv* env,
+ bool is_system_server,
+ NativeBridgeAction action,
+ const char* isa,
+ bool profile_system_server) {
is_zygote_ = false;
if (is_native_bridge_loaded_) {
@@ -896,8 +900,15 @@
heap_->ResetGcPerformanceInfo();
// We may want to collect profiling samples for system server, but we never want to JIT there.
- if ((!is_system_server || !jit_options_->UseJitCompilation()) &&
- !safe_mode_ &&
+ if (is_system_server) {
+ jit_options_->SetUseJitCompilation(false);
+ jit_options_->SetSaveProfilingInfo(profile_system_server);
+ if (profile_system_server) {
+ jit_options_->SetWaitForJitNotificationsToSaveProfile(false);
+ VLOG(profiler) << "Enabling system server profiles";
+ }
+ }
+ if (!safe_mode_ &&
(jit_options_->UseJitCompilation() || jit_options_->GetSaveProfilingInfo()) &&
jit_ == nullptr) {
// Note that when running ART standalone (not zygote, nor zygote fork),
diff --git a/runtime/runtime.h b/runtime/runtime.h
index c8aa4e5..c0ff6b3 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -446,7 +446,11 @@
void PreZygoteFork();
void InitNonZygoteOrPostFork(
- JNIEnv* env, bool is_system_server, NativeBridgeAction action, const char* isa);
+ JNIEnv* env,
+ bool is_system_server,
+ NativeBridgeAction action,
+ const char* isa,
+ bool profile_system_server = false);
const instrumentation::Instrumentation* GetInstrumentation() const {
return &instrumentation_;
diff --git a/tools/veridex/flow_analysis.cc b/tools/veridex/flow_analysis.cc
index e2833bf..154c60f 100644
--- a/tools/veridex/flow_analysis.cc
+++ b/tools/veridex/flow_analysis.cc
@@ -112,7 +112,12 @@
RegisterValue(RegisterSource::kNone, DexFileReference(nullptr, 0), cls);
}
-const RegisterValue& VeriFlowAnalysis::GetRegister(uint32_t dex_register) {
+void VeriFlowAnalysis::UpdateRegister(uint32_t dex_register, int32_t value, const VeriClass* cls) {
+ current_registers_[dex_register] =
+ RegisterValue(RegisterSource::kConstant, value, DexFileReference(nullptr, 0), cls);
+}
+
+const RegisterValue& VeriFlowAnalysis::GetRegister(uint32_t dex_register) const {
return current_registers_[dex_register];
}
@@ -131,6 +136,49 @@
return RegisterValue(RegisterSource::kField, DexFileReference(&dex_file, field_index), cls);
}
+int VeriFlowAnalysis::GetBranchFlags(const Instruction& instruction) const {
+ switch (instruction.Opcode()) {
+ #define IF_XX(cond, op) \
+ case Instruction::IF_##cond: { \
+ RegisterValue lhs = GetRegister(instruction.VRegA()); \
+ RegisterValue rhs = GetRegister(instruction.VRegB()); \
+ if (lhs.IsConstant() && rhs.IsConstant()) { \
+ if (lhs.GetConstant() op rhs.GetConstant()) { \
+ return Instruction::kBranch; \
+ } else { \
+ return Instruction::kContinue; \
+ } \
+ } \
+ break; \
+ } \
+ case Instruction::IF_##cond##Z: { \
+ RegisterValue val = GetRegister(instruction.VRegA()); \
+ if (val.IsConstant()) { \
+ if (val.GetConstant() op 0) { \
+ return Instruction::kBranch; \
+ } else { \
+ return Instruction::kContinue; \
+ } \
+ } \
+ break; \
+ }
+
+ IF_XX(EQ, ==);
+ IF_XX(NE, !=);
+ IF_XX(LT, <);
+ IF_XX(LE, <=);
+ IF_XX(GT, >);
+ IF_XX(GE, >=);
+
+ #undef IF_XX
+
+ default:
+ break;
+ }
+
+ return Instruction::FlagsOf(instruction.Opcode());
+}
+
void VeriFlowAnalysis::AnalyzeCode() {
std::vector<uint32_t> work_list;
work_list.push_back(0);
@@ -149,16 +197,17 @@
ProcessDexInstruction(inst);
SetVisited(dex_pc);
- int opcode_flags = Instruction::FlagsOf(inst.Opcode());
- if ((opcode_flags & Instruction::kContinue) != 0) {
- if ((opcode_flags & Instruction::kBranch) != 0) {
+ int branch_flags = GetBranchFlags(inst);
+
+ if ((branch_flags & Instruction::kContinue) != 0) {
+ if ((branch_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) {
+ } else if ((branch_flags & Instruction::kBranch) != 0) {
dex_pc += inst.GetTargetOffset();
DCHECK(IsBranchTarget(dex_pc));
} else {
@@ -178,12 +227,30 @@
void VeriFlowAnalysis::ProcessDexInstruction(const Instruction& instruction) {
switch (instruction.Opcode()) {
- case Instruction::CONST_4:
- case Instruction::CONST_16:
- case Instruction::CONST:
+ case Instruction::CONST_4: {
+ int32_t register_index = instruction.VRegA();
+ int32_t value = instruction.VRegB_11n();
+ UpdateRegister(register_index, value, VeriClass::integer_);
+ break;
+ }
+ case Instruction::CONST_16: {
+ int32_t register_index = instruction.VRegA();
+ int32_t value = instruction.VRegB_21s();
+ UpdateRegister(register_index, value, VeriClass::integer_);
+ break;
+ }
+
+ case Instruction::CONST: {
+ int32_t register_index = instruction.VRegA();
+ int32_t value = instruction.VRegB_31i();
+ UpdateRegister(register_index, value, VeriClass::integer_);
+ break;
+ }
+
case Instruction::CONST_HIGH16: {
int32_t register_index = instruction.VRegA();
- UpdateRegister(register_index, VeriClass::integer_);
+ int32_t value = instruction.VRegB_21h();
+ UpdateRegister(register_index, value, VeriClass::integer_);
break;
}
@@ -268,6 +335,8 @@
case Instruction::RETURN: {
break;
}
+
+ // If operations will be handled when looking at the control flow.
#define IF_XX(cond) \
case Instruction::IF_##cond: break; \
case Instruction::IF_##cond##Z: break
@@ -279,6 +348,8 @@
IF_XX(GT);
IF_XX(GE);
+ #undef IF_XX
+
case Instruction::GOTO:
case Instruction::GOTO_16:
case Instruction::GOTO_32: {
@@ -495,7 +566,13 @@
case Instruction::SGET_BYTE:
case Instruction::SGET_CHAR:
case Instruction::SGET_SHORT: {
- UpdateRegister(instruction.VRegA_22c(), GetFieldType(instruction.VRegC_22c()));
+ uint32_t dest_reg = instruction.VRegA_21c();
+ uint16_t field_index = instruction.VRegB_21c();
+ if (VeriClass::sdkInt_ != nullptr && resolver_->GetField(field_index) == VeriClass::sdkInt_) {
+ UpdateRegister(dest_reg, gTargetSdkVersion, VeriClass::integer_);
+ } else {
+ UpdateRegister(dest_reg, GetFieldType(instruction.VRegC_22c()));
+ }
break;
}
diff --git a/tools/veridex/flow_analysis.h b/tools/veridex/flow_analysis.h
index 62c9916..fc09360 100644
--- a/tools/veridex/flow_analysis.h
+++ b/tools/veridex/flow_analysis.h
@@ -35,6 +35,7 @@
kMethod,
kClass,
kString,
+ kConstant,
kNone
};
@@ -44,28 +45,33 @@
class RegisterValue {
public:
RegisterValue() : source_(RegisterSource::kNone),
- parameter_index_(0),
+ value_(0),
reference_(nullptr, 0),
type_(nullptr) {}
RegisterValue(RegisterSource source, DexFileReference reference, const VeriClass* type)
- : source_(source), parameter_index_(0), reference_(reference), type_(type) {}
+ : source_(source), value_(0), reference_(reference), type_(type) {}
RegisterValue(RegisterSource source,
- uint32_t parameter_index,
+ uint32_t value,
DexFileReference reference,
const VeriClass* type)
- : source_(source), parameter_index_(parameter_index), reference_(reference), type_(type) {}
+ : source_(source), value_(value), reference_(reference), type_(type) {}
RegisterSource GetSource() const { return source_; }
DexFileReference GetDexFileReference() const { return reference_; }
const VeriClass* GetType() const { return type_; }
uint32_t GetParameterIndex() const {
CHECK(IsParameter());
- return parameter_index_;
+ return value_;
+ }
+ uint32_t GetConstant() const {
+ CHECK(IsConstant());
+ return value_;
}
bool IsParameter() const { return source_ == RegisterSource::kParameter; }
bool IsClass() const { return source_ == RegisterSource::kClass; }
bool IsString() const { return source_ == RegisterSource::kString; }
+ bool IsConstant() const { return source_ == RegisterSource::kConstant; }
std::string ToString() const {
switch (source_) {
@@ -91,7 +97,7 @@
private:
RegisterSource source_;
- uint32_t parameter_index_;
+ uint32_t value_;
DexFileReference reference_;
const VeriClass* type_;
};
@@ -137,12 +143,15 @@
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);
+ void UpdateRegister(uint32_t dex_register, int32_t value, const VeriClass* cls);
void ProcessDexInstruction(const Instruction& inst);
void SetVisited(uint32_t dex_pc);
RegisterValue GetFieldType(uint32_t field_index);
+ int GetBranchFlags(const Instruction& instruction) const;
+
protected:
- const RegisterValue& GetRegister(uint32_t dex_register);
+ const RegisterValue& GetRegister(uint32_t dex_register) const;
RegisterValue GetReturnType(uint32_t method_index);
VeridexResolver* resolver_;
diff --git a/tools/veridex/veridex.cc b/tools/veridex/veridex.cc
index dc7ea94..bcd4815 100644
--- a/tools/veridex/veridex.cc
+++ b/tools/veridex/veridex.cc
@@ -25,6 +25,7 @@
#include "precise_hidden_api_finder.h"
#include "resolver.h"
+#include <cstdlib>
#include <sstream>
namespace art {
@@ -62,6 +63,7 @@
VeriMethod VeriClass::getDeclaredMethod_ = nullptr;
VeriMethod VeriClass::getClass_ = nullptr;
VeriMethod VeriClass::loadClass_ = nullptr;
+VeriField VeriClass::sdkInt_ = nullptr;
struct VeridexOptions {
const char* dex_file = nullptr;
@@ -70,6 +72,7 @@
const char* light_greylist = nullptr;
const char* dark_greylist = nullptr;
bool precise = true;
+ int target_sdk_version = 28; /* P */
};
static const char* Substr(const char* str, int index) {
@@ -91,6 +94,7 @@
static const char* kDarkGreylistOption = "--dark-greylist=";
static const char* kLightGreylistOption = "--light-greylist=";
static const char* kImprecise = "--imprecise";
+ static const char* kTargetSdkVersion = "--target-sdk-version=";
for (int i = 0; i < argc; ++i) {
if (StartsWith(argv[i], kDexFileOption)) {
@@ -105,6 +109,8 @@
options->light_greylist = Substr(argv[i], strlen(kLightGreylistOption));
} else if (strcmp(argv[i], kImprecise) == 0) {
options->precise = false;
+ } else if (StartsWith(argv[i], kTargetSdkVersion)) {
+ options->target_sdk_version = atoi(Substr(argv[i], strlen(kTargetSdkVersion)));
}
}
}
@@ -124,6 +130,7 @@
static int Run(int argc, char** argv) {
VeridexOptions options;
ParseArgs(&options, argc, argv);
+ gTargetSdkVersion = options.target_sdk_version;
std::vector<std::string> boot_content;
std::vector<std::string> app_content;
@@ -200,6 +207,11 @@
VeriClass::loadClass_ = boot_resolvers[0]->LookupDeclaredMethodIn(
*VeriClass::class_loader_, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;");
+ VeriClass* version = type_map["Landroid/os/Build$VERSION;"];
+ if (version != nullptr) {
+ VeriClass::sdkInt_ = boot_resolvers[0]->LookupFieldIn(*version, "SDK_INT", "I");
+ }
+
std::vector<std::unique_ptr<VeridexResolver>> app_resolvers;
Resolve(app_dex_files, resolver_map, type_map, &app_resolvers);
diff --git a/tools/veridex/veridex.h b/tools/veridex/veridex.h
index 9c0a158..31ddbf4 100644
--- a/tools/veridex/veridex.h
+++ b/tools/veridex/veridex.h
@@ -24,6 +24,8 @@
namespace art {
+static int gTargetSdkVersion = 1000; // Will be initialized after parsing options.
+
/**
* Abstraction for fields defined in dex files. Currently, that's a pointer into their
* `encoded_field` description.
@@ -86,6 +88,8 @@
static VeriMethod getClass_;
static VeriMethod loadClass_;
+ static VeriField sdkInt_;
+
private:
Primitive::Type kind_;
uint8_t dimensions_;