summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Vladimir Marko <vmarko@google.com> 2019-11-06 17:09:30 +0000
committer Vladimir Marko <vmarko@google.com> 2019-11-27 10:02:22 +0000
commit7dac864d7eae3d731eeacf55cfc7f71b4df6cee3 (patch)
tree9f27bb1c0b6f076c2dd1b721441200f9f7a78022
parent047081a1509762879a9eb51981982af65f419bac (diff)
Clean up JNI dlsym lookup trampoline.
Make sure the GenericJniTrampoline recognizes the trampoline in primary boot image oat file. Rename that trampoline, add a test and flag some issues in the code. Test: New test 178-app-image-native-method. Test: m test-art-host-gtest Test: testrunner.py --host --optimizing Test: testrunner.py --host -t 178-app-image-native-method Bug: 112189621 Change-Id: I8f8cd11998af536fd3842dd4183a25f0367655a6
-rw-r--r--dex2oat/driver/compiler_driver.cc2
-rw-r--r--dex2oat/driver/compiler_driver.h2
-rw-r--r--dex2oat/linker/image_writer.cc10
-rw-r--r--dex2oat/linker/image_writer.h2
-rw-r--r--dex2oat/linker/oat_writer.cc10
-rw-r--r--dex2oat/linker/oat_writer.h4
-rw-r--r--imgdiag/imgdiag.cc4
-rw-r--r--oatdump/oatdump.cc6
-rw-r--r--runtime/class_linker.cc12
-rw-r--r--runtime/class_linker.h1
-rw-r--r--runtime/entrypoints/quick/quick_trampoline_entrypoints.cc8
-rw-r--r--runtime/oat.cc20
-rw-r--r--runtime/oat.h8
-rwxr-xr-xtest/178-app-image-native-method/check18
-rw-r--r--test/178-app-image-native-method/expected.txt6
-rw-r--r--test/178-app-image-native-method/info.txt1
-rw-r--r--test/178-app-image-native-method/native_methods.cc118
-rw-r--r--test/178-app-image-native-method/profile8
-rw-r--r--test/178-app-image-native-method/run25
-rw-r--r--test/178-app-image-native-method/src/Main.java254
-rw-r--r--test/Android.bp1
21 files changed, 482 insertions, 38 deletions
diff --git a/dex2oat/driver/compiler_driver.cc b/dex2oat/driver/compiler_driver.cc
index 99895b14a3..ae73f49508 100644
--- a/dex2oat/driver/compiler_driver.cc
+++ b/dex2oat/driver/compiler_driver.cc
@@ -294,7 +294,7 @@ CompilerDriver::~CompilerDriver() {
type ## _ENTRYPOINT_OFFSET(PointerSize::k32, offset)); \
}
-std::unique_ptr<const std::vector<uint8_t>> CompilerDriver::CreateJniDlsymLookup() const {
+std::unique_ptr<const std::vector<uint8_t>> CompilerDriver::CreateJniDlsymLookupTrampoline() const {
CREATE_TRAMPOLINE(JNI, kJniAbi, pDlsymLookup)
}
diff --git a/dex2oat/driver/compiler_driver.h b/dex2oat/driver/compiler_driver.h
index b474de5f8d..4aeb34de90 100644
--- a/dex2oat/driver/compiler_driver.h
+++ b/dex2oat/driver/compiler_driver.h
@@ -121,7 +121,7 @@ class CompilerDriver {
}
// Generate the trampolines that are invoked by unresolved direct methods.
- std::unique_ptr<const std::vector<uint8_t>> CreateJniDlsymLookup() const;
+ std::unique_ptr<const std::vector<uint8_t>> CreateJniDlsymLookupTrampoline() const;
std::unique_ptr<const std::vector<uint8_t>> CreateQuickGenericJniTrampoline() const;
std::unique_ptr<const std::vector<uint8_t>> CreateQuickImtConflictTrampoline() const;
std::unique_ptr<const std::vector<uint8_t>> CreateQuickResolutionTrampoline() const;
diff --git a/dex2oat/linker/image_writer.cc b/dex2oat/linker/image_writer.cc
index 13f22d92e8..3d998c9e3e 100644
--- a/dex2oat/linker/image_writer.cc
+++ b/dex2oat/linker/image_writer.cc
@@ -3355,8 +3355,8 @@ const uint8_t* ImageWriter::GetOatAddress(StubType type) const {
// TODO: We could maybe clean this up if we stored them in an array in the oat header.
case StubType::kQuickGenericJNITrampoline:
return static_cast<const uint8_t*>(header.GetQuickGenericJniTrampoline());
- case StubType::kJNIDlsymLookup:
- return static_cast<const uint8_t*>(header.GetJniDlsymLookup());
+ case StubType::kJNIDlsymLookupTrampoline:
+ return static_cast<const uint8_t*>(header.GetJniDlsymLookupTrampoline());
case StubType::kQuickIMTConflictTrampoline:
return static_cast<const uint8_t*>(header.GetQuickImtConflictTrampoline());
case StubType::kQuickResolutionTrampoline:
@@ -3471,7 +3471,7 @@ void ImageWriter::CopyAndFixupMethod(ArtMethod* orig,
// The native method's pointer is set to a stub to lookup via dlsym.
// Note this is not the code_ pointer, that is handled above.
copy->SetEntryPointFromJniPtrSize(
- GetOatAddress(StubType::kJNIDlsymLookup), target_ptr_size_);
+ GetOatAddress(StubType::kJNIDlsymLookupTrampoline), target_ptr_size_);
} else {
CHECK(copy->GetDataPtrSize(target_ptr_size_) == nullptr);
}
@@ -3606,8 +3606,8 @@ void ImageWriter::UpdateOatFileHeader(size_t oat_index, const OatHeader& oat_hea
if (oat_index == GetDefaultOatIndex()) {
// Primary oat file, read the trampolines.
- cur_image_info.SetStubOffset(StubType::kJNIDlsymLookup,
- oat_header.GetJniDlsymLookupOffset());
+ cur_image_info.SetStubOffset(StubType::kJNIDlsymLookupTrampoline,
+ oat_header.GetJniDlsymLookupTrampolineOffset());
cur_image_info.SetStubOffset(StubType::kQuickGenericJNITrampoline,
oat_header.GetQuickGenericJniTrampolineOffset());
cur_image_info.SetStubOffset(StubType::kQuickIMTConflictTrampoline,
diff --git a/dex2oat/linker/image_writer.h b/dex2oat/linker/image_writer.h
index b2b811f541..22b7739b8b 100644
--- a/dex2oat/linker/image_writer.h
+++ b/dex2oat/linker/image_writer.h
@@ -233,7 +233,7 @@ class ImageWriter final {
friend std::ostream& operator<<(std::ostream& stream, const NativeObjectRelocationType& type);
enum class StubType {
- kJNIDlsymLookup,
+ kJNIDlsymLookupTrampoline,
kQuickGenericJNITrampoline,
kQuickIMTConflictTrampoline,
kQuickResolutionTrampoline,
diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc
index 92de151416..d75f427639 100644
--- a/dex2oat/linker/oat_writer.cc
+++ b/dex2oat/linker/oat_writer.cc
@@ -416,7 +416,7 @@ OatWriter::OatWriter(const CompilerOptions& compiler_options,
size_quickening_info_alignment_(0),
size_interpreter_to_interpreter_bridge_(0),
size_interpreter_to_compiled_code_bridge_(0),
- size_jni_dlsym_lookup_(0),
+ size_jni_dlsym_lookup_trampoline_(0),
size_quick_generic_jni_trampoline_(0),
size_quick_imt_conflict_trampoline_(0),
size_quick_resolution_trampoline_(0),
@@ -2207,7 +2207,7 @@ size_t OatWriter::InitOatCode(size_t offset) {
} \
offset += (field)->size();
- DO_TRAMPOLINE(jni_dlsym_lookup_, JniDlsymLookup);
+ DO_TRAMPOLINE(jni_dlsym_lookup_trampoline_, JniDlsymLookupTrampoline);
DO_TRAMPOLINE(quick_generic_jni_trampoline_, QuickGenericJniTrampoline);
DO_TRAMPOLINE(quick_imt_conflict_trampoline_, QuickImtConflictTrampoline);
DO_TRAMPOLINE(quick_resolution_trampoline_, QuickResolutionTrampoline);
@@ -2215,7 +2215,7 @@ size_t OatWriter::InitOatCode(size_t offset) {
#undef DO_TRAMPOLINE
} else {
- oat_header_->SetJniDlsymLookupOffset(0);
+ oat_header_->SetJniDlsymLookupTrampolineOffset(0);
oat_header_->SetQuickGenericJniTrampolineOffset(0);
oat_header_->SetQuickImtConflictTrampolineOffset(0);
oat_header_->SetQuickResolutionTrampolineOffset(0);
@@ -2754,7 +2754,7 @@ bool OatWriter::CheckOatSize(OutputStream* out, size_t file_offset, size_t relat
DO_STAT(size_quickening_info_alignment_);
DO_STAT(size_interpreter_to_interpreter_bridge_);
DO_STAT(size_interpreter_to_compiled_code_bridge_);
- DO_STAT(size_jni_dlsym_lookup_);
+ DO_STAT(size_jni_dlsym_lookup_trampoline_);
DO_STAT(size_quick_generic_jni_trampoline_);
DO_STAT(size_quick_imt_conflict_trampoline_);
DO_STAT(size_quick_resolution_trampoline_);
@@ -3085,7 +3085,7 @@ size_t OatWriter::WriteCode(OutputStream* out, size_t file_offset, size_t relati
DCHECK_OFFSET(); \
} while (false)
- DO_TRAMPOLINE(jni_dlsym_lookup_);
+ DO_TRAMPOLINE(jni_dlsym_lookup_trampoline_);
DO_TRAMPOLINE(quick_generic_jni_trampoline_);
DO_TRAMPOLINE(quick_imt_conflict_trampoline_);
DO_TRAMPOLINE(quick_resolution_trampoline_);
diff --git a/dex2oat/linker/oat_writer.h b/dex2oat/linker/oat_writer.h
index 19c0cfefa2..5015ec3156 100644
--- a/dex2oat/linker/oat_writer.h
+++ b/dex2oat/linker/oat_writer.h
@@ -474,7 +474,7 @@ class OatWriter {
dchecked_vector<OatDexFile> oat_dex_files_;
dchecked_vector<OatClassHeader> oat_class_headers_;
dchecked_vector<OatClass> oat_classes_;
- std::unique_ptr<const std::vector<uint8_t>> jni_dlsym_lookup_;
+ std::unique_ptr<const std::vector<uint8_t>> jni_dlsym_lookup_trampoline_;
std::unique_ptr<const std::vector<uint8_t>> quick_generic_jni_trampoline_;
std::unique_ptr<const std::vector<uint8_t>> quick_imt_conflict_trampoline_;
std::unique_ptr<const std::vector<uint8_t>> quick_resolution_trampoline_;
@@ -494,7 +494,7 @@ class OatWriter {
uint32_t size_quickening_info_alignment_;
uint32_t size_interpreter_to_interpreter_bridge_;
uint32_t size_interpreter_to_compiled_code_bridge_;
- uint32_t size_jni_dlsym_lookup_;
+ uint32_t size_jni_dlsym_lookup_trampoline_;
uint32_t size_quick_generic_jni_trampoline_;
uint32_t size_quick_imt_conflict_trampoline_;
uint32_t size_quick_resolution_trampoline_;
diff --git a/imgdiag/imgdiag.cc b/imgdiag/imgdiag.cc
index bc63ab1235..10f4015015 100644
--- a/imgdiag/imgdiag.cc
+++ b/imgdiag/imgdiag.cc
@@ -849,9 +849,9 @@ class RegionSpecializedBase<ArtMethod> : public RegionCommon<ArtMethod> {
std::vector<const OatFile*> boot_oat_files = oat_file_manager.GetBootOatFiles();
for (const OatFile* oat_file : boot_oat_files) {
const OatHeader& oat_header = oat_file->GetOatHeader();
- const void* jdl = oat_header.GetJniDlsymLookup();
+ const void* jdl = oat_header.GetJniDlsymLookupTrampoline();
if (jdl != nullptr) {
- entry_point_names_[jdl] = "JniDlsymLookup (from boot oat file)";
+ entry_point_names_[jdl] = "JniDlsymLookupTrampoline (from boot oat file)";
}
const void* qgjt = oat_header.GetQuickGenericJniTrampoline();
if (qgjt != nullptr) {
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index 5679a7c2d5..95f085c627 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -195,7 +195,7 @@ class OatSymbolizer final {
info.code_size = 0; /* The symbol lasts until the next symbol. */ \
method_debug_infos_.push_back(std::move(info)); \
}
- DO_TRAMPOLINE(JniDlsymLookup);
+ DO_TRAMPOLINE(JniDlsymLookupTrampoline);
DO_TRAMPOLINE(QuickGenericJniTrampoline);
DO_TRAMPOLINE(QuickImtConflictTrampoline);
DO_TRAMPOLINE(QuickResolutionTrampoline);
@@ -445,8 +445,8 @@ class OatDumper {
os << StringPrintf("\n\n");
DUMP_OAT_HEADER_OFFSET("EXECUTABLE", GetExecutableOffset);
- DUMP_OAT_HEADER_OFFSET("JNI DLSYM LOOKUP",
- GetJniDlsymLookupOffset);
+ DUMP_OAT_HEADER_OFFSET("JNI DLSYM LOOKUP TRAMPOLINE",
+ GetJniDlsymLookupTrampolineOffset);
DUMP_OAT_HEADER_OFFSET("QUICK GENERIC JNI TRAMPOLINE",
GetQuickGenericJniTrampolineOffset);
DUMP_OAT_HEADER_OFFSET("QUICK IMT CONFLICT TRAMPOLINE",
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 438d5cb709..3f904f3546 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -611,6 +611,7 @@ ClassLinker::ClassLinker(InternTable* intern_table, bool fast_class_not_found_ex
log_new_roots_(false),
intern_table_(intern_table),
fast_class_not_found_exceptions_(fast_class_not_found_exceptions),
+ jni_dlsym_lookup_trampoline_(nullptr),
quick_resolution_trampoline_(nullptr),
quick_imt_conflict_trampoline_(nullptr),
quick_generic_jni_trampoline_(nullptr),
@@ -834,8 +835,10 @@ bool ClassLinker::InitWithoutImage(std::vector<std::unique_ptr<const DexFile>> b
quick_generic_jni_trampoline_ = GetQuickGenericJniStub();
if (!runtime->IsAotCompiler()) {
// We need to set up the generic trampolines since we don't have an image.
+ jni_dlsym_lookup_trampoline_ = GetJniDlsymLookupStub();
quick_resolution_trampoline_ = GetQuickResolutionStub();
quick_imt_conflict_trampoline_ = GetQuickImtConflictStub();
+ quick_generic_jni_trampoline_ = GetQuickGenericJniStub();
quick_to_interpreter_bridge_trampoline_ = GetQuickToInterpreterBridge();
}
@@ -1183,6 +1186,7 @@ bool ClassLinker::InitFromBootImage(std::string* error_msg) {
runtime->GetOatFileManager().RegisterImageOatFiles(spaces);
DCHECK(!oat_files.empty());
const OatHeader& default_oat_header = oat_files[0]->GetOatHeader();
+ jni_dlsym_lookup_trampoline_ = default_oat_header.GetJniDlsymLookupTrampoline();
quick_resolution_trampoline_ = default_oat_header.GetQuickResolutionTrampoline();
quick_imt_conflict_trampoline_ = default_oat_header.GetQuickImtConflictTrampoline();
quick_generic_jni_trampoline_ = default_oat_header.GetQuickGenericJniTrampoline();
@@ -1191,6 +1195,8 @@ bool ClassLinker::InitFromBootImage(std::string* error_msg) {
// Check that the other images use the same trampoline.
for (size_t i = 1; i < oat_files.size(); ++i) {
const OatHeader& ith_oat_header = oat_files[i]->GetOatHeader();
+ const void* ith_jni_dlsym_lookup_trampoline_ =
+ ith_oat_header.GetJniDlsymLookupTrampoline();
const void* ith_quick_resolution_trampoline =
ith_oat_header.GetQuickResolutionTrampoline();
const void* ith_quick_imt_conflict_trampoline =
@@ -1199,7 +1205,8 @@ bool ClassLinker::InitFromBootImage(std::string* error_msg) {
ith_oat_header.GetQuickGenericJniTrampoline();
const void* ith_quick_to_interpreter_bridge_trampoline =
ith_oat_header.GetQuickToInterpreterBridge();
- if (ith_quick_resolution_trampoline != quick_resolution_trampoline_ ||
+ if (ith_jni_dlsym_lookup_trampoline_ != jni_dlsym_lookup_trampoline_ ||
+ ith_quick_resolution_trampoline != quick_resolution_trampoline_ ||
ith_quick_imt_conflict_trampoline != quick_imt_conflict_trampoline_ ||
ith_quick_generic_jni_trampoline != quick_generic_jni_trampoline_ ||
ith_quick_to_interpreter_bridge_trampoline != quick_to_interpreter_bridge_trampoline_) {
@@ -9506,7 +9513,8 @@ bool ClassLinker::IsQuickGenericJniStub(const void* entry_point) const {
}
bool ClassLinker::IsJniDlsymLookupStub(const void* entry_point) const {
- return entry_point == GetJniDlsymLookupStub();
+ return entry_point == GetJniDlsymLookupStub() ||
+ (jni_dlsym_lookup_trampoline_ == entry_point);
}
const void* ClassLinker::GetRuntimeQuickGenericJniStub() const {
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index a1ba461270..877b9647aa 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -1404,6 +1404,7 @@ class ClassLinker {
// Trampolines within the image the bounce to runtime entrypoints. Done so that there is a single
// patch point within the image. TODO: make these proper relocations.
+ const void* jni_dlsym_lookup_trampoline_;
const void* quick_resolution_trampoline_;
const void* quick_imt_conflict_trampoline_;
const void* quick_generic_jni_trampoline_;
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index 6a52d24f38..1cc4d24b58 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -2389,7 +2389,7 @@ extern "C" TwoWordReturn artQuickGenericJniTrampoline(Thread* self, ArtMethod**
// Ensure static method's class is initialized.
StackHandleScope<1> hs(self);
Handle<mirror::Class> h_class(hs.NewHandle(declaring_class));
- if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(self, h_class, true, true)) {
+ if (!runtime->GetClassLinker()->EnsureInitialized(self, h_class, true, true)) {
DCHECK(Thread::Current()->IsExceptionPending()) << called->PrettyMethod();
self->PopHandleScope();
// A negative value denotes an error.
@@ -2432,7 +2432,11 @@ extern "C" TwoWordReturn artQuickGenericJniTrampoline(Thread* self, ArtMethod**
// In the second case, we need to execute the binding and continue with the actual native function
// pointer.
DCHECK(nativeCode != nullptr);
- if (nativeCode == GetJniDlsymLookupStub()) {
+ if (runtime->GetClassLinker()->IsJniDlsymLookupStub(nativeCode)) {
+ // FIXME: This is broken for @FastNative and @CriticalNative as we're still runnable.
+ // Calls from compiled stubs are also broken.
+ // TODO: We could just let the GenericJNI stub call the ArtFindNativeMethod()
+ // rather than calling it explicitly here.
nativeCode = artFindNativeMethod(self);
if (nativeCode == nullptr) {
diff --git a/runtime/oat.cc b/runtime/oat.cc
index 3fceec9364..bc52ec0f55 100644
--- a/runtime/oat.cc
+++ b/runtime/oat.cc
@@ -72,7 +72,7 @@ OatHeader::OatHeader(InstructionSet instruction_set,
dex_file_count_(dex_file_count),
oat_dex_files_offset_(0),
executable_offset_(0),
- jni_dlsym_lookup_offset_(0),
+ jni_dlsym_lookup_trampoline_offset_(0),
quick_generic_jni_trampoline_offset_(0),
quick_imt_conflict_trampoline_offset_(0),
quick_resolution_trampoline_offset_(0),
@@ -200,20 +200,20 @@ static const void* GetTrampoline(const OatHeader& header, uint32_t offset) {
return (offset != 0u) ? reinterpret_cast<const uint8_t*>(&header) + offset : nullptr;
}
-const void* OatHeader::GetJniDlsymLookup() const {
- return GetTrampoline(*this, GetJniDlsymLookupOffset());
+const void* OatHeader::GetJniDlsymLookupTrampoline() const {
+ return GetTrampoline(*this, GetJniDlsymLookupTrampolineOffset());
}
-uint32_t OatHeader::GetJniDlsymLookupOffset() const {
+uint32_t OatHeader::GetJniDlsymLookupTrampolineOffset() const {
DCHECK(IsValid());
- return jni_dlsym_lookup_offset_;
+ return jni_dlsym_lookup_trampoline_offset_;
}
-void OatHeader::SetJniDlsymLookupOffset(uint32_t offset) {
+void OatHeader::SetJniDlsymLookupTrampolineOffset(uint32_t offset) {
DCHECK(IsValid());
- DCHECK_EQ(jni_dlsym_lookup_offset_, 0U) << offset;
+ DCHECK_EQ(jni_dlsym_lookup_trampoline_offset_, 0U) << offset;
- jni_dlsym_lookup_offset_ = offset;
+ jni_dlsym_lookup_trampoline_offset_ = offset;
}
const void* OatHeader::GetQuickGenericJniTrampoline() const {
@@ -222,12 +222,12 @@ const void* OatHeader::GetQuickGenericJniTrampoline() const {
uint32_t OatHeader::GetQuickGenericJniTrampolineOffset() const {
DCHECK(IsValid());
- CHECK_GE(quick_generic_jni_trampoline_offset_, jni_dlsym_lookup_offset_);
+ CHECK_GE(quick_generic_jni_trampoline_offset_, jni_dlsym_lookup_trampoline_offset_);
return quick_generic_jni_trampoline_offset_;
}
void OatHeader::SetQuickGenericJniTrampolineOffset(uint32_t offset) {
- CHECK(offset == 0 || offset >= jni_dlsym_lookup_offset_);
+ CHECK(offset == 0 || offset >= jni_dlsym_lookup_trampoline_offset_);
DCHECK(IsValid());
DCHECK_EQ(quick_generic_jni_trampoline_offset_, 0U) << offset;
diff --git a/runtime/oat.h b/runtime/oat.h
index 352b9e892e..a3f87221e9 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -69,9 +69,9 @@ class PACKED(4) OatHeader {
uint32_t GetExecutableOffset() const;
void SetExecutableOffset(uint32_t executable_offset);
- const void* GetJniDlsymLookup() const;
- uint32_t GetJniDlsymLookupOffset() const;
- void SetJniDlsymLookupOffset(uint32_t offset);
+ const void* GetJniDlsymLookupTrampoline() const;
+ uint32_t GetJniDlsymLookupTrampolineOffset() const;
+ void SetJniDlsymLookupTrampolineOffset(uint32_t offset);
const void* GetQuickGenericJniTrampoline() const;
uint32_t GetQuickGenericJniTrampolineOffset() const;
@@ -122,7 +122,7 @@ class PACKED(4) OatHeader {
uint32_t dex_file_count_;
uint32_t oat_dex_files_offset_;
uint32_t executable_offset_;
- uint32_t jni_dlsym_lookup_offset_;
+ uint32_t jni_dlsym_lookup_trampoline_offset_;
uint32_t quick_generic_jni_trampoline_offset_;
uint32_t quick_imt_conflict_trampoline_offset_;
uint32_t quick_resolution_trampoline_offset_;
diff --git a/test/178-app-image-native-method/check b/test/178-app-image-native-method/check
new file mode 100755
index 0000000000..53362959d8
--- /dev/null
+++ b/test/178-app-image-native-method/check
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright (C) 2019 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.
+
+# Filter out error messages for missing native methods.
+grep -v 'No implementation found for ' "$2" | diff -q "$1" - >/dev/null
diff --git a/test/178-app-image-native-method/expected.txt b/test/178-app-image-native-method/expected.txt
new file mode 100644
index 0000000000..02384cd2d0
--- /dev/null
+++ b/test/178-app-image-native-method/expected.txt
@@ -0,0 +1,6 @@
+JNI_OnLoad called
+test
+testMissing
+JNI_OnLoad called
+test
+testMissing
diff --git a/test/178-app-image-native-method/info.txt b/test/178-app-image-native-method/info.txt
new file mode 100644
index 0000000000..4cf01fe80f
--- /dev/null
+++ b/test/178-app-image-native-method/info.txt
@@ -0,0 +1 @@
+Tests that native methods in app image using compiled stubs or Generic JNI work correctly.
diff --git a/test/178-app-image-native-method/native_methods.cc b/test/178-app-image-native-method/native_methods.cc
new file mode 100644
index 0000000000..5c4fb3ee82
--- /dev/null
+++ b/test/178-app-image-native-method/native_methods.cc
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2019 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 "jni.h"
+
+namespace art {
+
+static inline bool VerifyManyParameters(
+ jint i1, jlong l1, jfloat f1, jdouble d1,
+ jint i2, jlong l2, jfloat f2, jdouble d2,
+ jint i3, jlong l3, jfloat f3, jdouble d3,
+ jint i4, jlong l4, jfloat f4, jdouble d4,
+ jint i5, jlong l5, jfloat f5, jdouble d5,
+ jint i6, jlong l6, jfloat f6, jdouble d6,
+ jint i7, jlong l7, jfloat f7, jdouble d7,
+ jint i8, jlong l8, jfloat f8, jdouble d8) {
+ return
+ (i1 == 11) && (l1 == 12) && (f1 == 13.0) && (d1 == 14.0) &&
+ (i2 == 21) && (l2 == 22) && (f2 == 23.0) && (d2 == 24.0) &&
+ (i3 == 31) && (l3 == 32) && (f3 == 33.0) && (d3 == 34.0) &&
+ (i4 == 41) && (l4 == 42) && (f4 == 43.0) && (d4 == 44.0) &&
+ (i5 == 51) && (l5 == 52) && (f5 == 53.0) && (d5 == 54.0) &&
+ (i6 == 61) && (l6 == 62) && (f6 == 63.0) && (d6 == 64.0) &&
+ (i7 == 71) && (l7 == 72) && (f7 == 73.0) && (d7 == 74.0) &&
+ (i8 == 81) && (l8 == 82) && (f8 == 83.0) && (d8 == 84.0);
+}
+
+extern "C" JNIEXPORT jint JNICALL Java_Test_nativeMethod(JNIEnv*, jclass, jint i) {
+ return i;
+}
+
+extern "C" JNIEXPORT jint JNICALL Java_Test_nativeMethodWithManyParameters(
+ JNIEnv*, jclass,
+ jint i1, jlong l1, jfloat f1, jdouble d1,
+ jint i2, jlong l2, jfloat f2, jdouble d2,
+ jint i3, jlong l3, jfloat f3, jdouble d3,
+ jint i4, jlong l4, jfloat f4, jdouble d4,
+ jint i5, jlong l5, jfloat f5, jdouble d5,
+ jint i6, jlong l6, jfloat f6, jdouble d6,
+ jint i7, jlong l7, jfloat f7, jdouble d7,
+ jint i8, jlong l8, jfloat f8, jdouble d8) {
+ bool ok = VerifyManyParameters(
+ i1, l1, f1, d1,
+ i2, l2, f2, d2,
+ i3, l3, f3, d3,
+ i4, l4, f4, d4,
+ i5, l5, f5, d5,
+ i6, l6, f6, d6,
+ i7, l7, f7, d7,
+ i8, l8, f8, d8);
+ return ok ? 42 : -1;
+}
+
+extern "C" JNIEXPORT jint JNICALL Java_TestFast_nativeMethod(JNIEnv*, jclass, jint i) {
+ return i;
+}
+
+extern "C" JNIEXPORT jint JNICALL Java_TestFast_nativeMethodWithManyParameters(
+ JNIEnv*, jclass,
+ jint i1, jlong l1, jfloat f1, jdouble d1,
+ jint i2, jlong l2, jfloat f2, jdouble d2,
+ jint i3, jlong l3, jfloat f3, jdouble d3,
+ jint i4, jlong l4, jfloat f4, jdouble d4,
+ jint i5, jlong l5, jfloat f5, jdouble d5,
+ jint i6, jlong l6, jfloat f6, jdouble d6,
+ jint i7, jlong l7, jfloat f7, jdouble d7,
+ jint i8, jlong l8, jfloat f8, jdouble d8) {
+ bool ok = VerifyManyParameters(
+ i1, l1, f1, d1,
+ i2, l2, f2, d2,
+ i3, l3, f3, d3,
+ i4, l4, f4, d4,
+ i5, l5, f5, d5,
+ i6, l6, f6, d6,
+ i7, l7, f7, d7,
+ i8, l8, f8, d8);
+ return ok ? 42 : -1;
+}
+
+extern "C" JNIEXPORT jint JNICALL Java_TestCritical_nativeMethod(jint i) {
+ return i;
+}
+
+extern "C" JNIEXPORT jint JNICALL Java_TestCritical_nativeMethodWithManyParameters(
+ jint i1, jlong l1, jfloat f1, jdouble d1,
+ jint i2, jlong l2, jfloat f2, jdouble d2,
+ jint i3, jlong l3, jfloat f3, jdouble d3,
+ jint i4, jlong l4, jfloat f4, jdouble d4,
+ jint i5, jlong l5, jfloat f5, jdouble d5,
+ jint i6, jlong l6, jfloat f6, jdouble d6,
+ jint i7, jlong l7, jfloat f7, jdouble d7,
+ jint i8, jlong l8, jfloat f8, jdouble d8) {
+ bool ok = VerifyManyParameters(
+ i1, l1, f1, d1,
+ i2, l2, f2, d2,
+ i3, l3, f3, d3,
+ i4, l4, f4, d4,
+ i5, l5, f5, d5,
+ i6, l6, f6, d6,
+ i7, l7, f7, d7,
+ i8, l8, f8, d8);
+ return ok ? 42 : -1;
+}
+
+} // namespace art
diff --git a/test/178-app-image-native-method/profile b/test/178-app-image-native-method/profile
new file mode 100644
index 0000000000..597dde1af9
--- /dev/null
+++ b/test/178-app-image-native-method/profile
@@ -0,0 +1,8 @@
+LMain;
+LTest;
+HSPLMain;->test()V
+HSPLMain;->testFast()V
+HSPLMain;->testCritical()V
+HSPLMain;->testMissing()V
+HSPLMain;->testMissingFast()V
+HSPLMain;->testMissingCritical()V
diff --git a/test/178-app-image-native-method/run b/test/178-app-image-native-method/run
new file mode 100644
index 0000000000..3cb4d09d0d
--- /dev/null
+++ b/test/178-app-image-native-method/run
@@ -0,0 +1,25 @@
+#!/bin/bash
+#
+# Copyright (C) 2019 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.
+
+# Use a profile to put specific classes in the app image.
+${RUN} $@ --profile -Xcompiler-option --compiler-filter=speed-profile
+return_status1=$?
+
+# Also run with the verify filter to avoid compiling JNI stubs.
+${RUN} ${@} --profile -Xcompiler-option --compiler-filter=verify
+return_status2=$?
+
+(exit ${return_status1}) # && (exit ${return_status2})
diff --git a/test/178-app-image-native-method/src/Main.java b/test/178-app-image-native-method/src/Main.java
new file mode 100644
index 0000000000..d63d11273b
--- /dev/null
+++ b/test/178-app-image-native-method/src/Main.java
@@ -0,0 +1,254 @@
+/*
+ * Copyright 2019 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.
+ */
+
+import dalvik.annotation.optimization.FastNative;
+import dalvik.annotation.optimization.CriticalNative;
+
+public class Main {
+
+ public static void main(String[] args) throws Exception {
+ System.loadLibrary(args[0]);
+
+ if (!checkAppImageLoaded()) {
+ System.out.println("AppImage not loaded.");
+ }
+ // To avoid going through resolution trampoline, make test classes visibly initialized.
+ new Test();
+ new TestFast();
+ new TestCritical();
+ new TestMissing();
+ new TestMissingFast();
+ new TestMissingCritical();
+ makeVisiblyInitialized(); // Make sure they are visibly initialized.
+
+ // FIXME: @FastNative and @CriticalNative fail a state check in artFindNativeMethod().
+ test();
+ // testFast();
+ // testCritical();
+ testMissing();
+ // testMissingFast();
+ // testMissingCritical();
+ }
+
+ static void test() {
+ System.out.println("test");
+ assertEquals(42, Test.nativeMethod(42));
+ assertEquals(42, Test.nativeMethodWithManyParameters(
+ 11, 12L, 13.0f, 14.0d,
+ 21, 22L, 23.0f, 24.0d,
+ 31, 32L, 33.0f, 34.0d,
+ 41, 42L, 43.0f, 44.0d,
+ 51, 52L, 53.0f, 54.0d,
+ 61, 62L, 63.0f, 64.0d,
+ 71, 72L, 73.0f, 74.0d,
+ 81, 82L, 83.0f, 84.0d));
+ }
+
+ static void testFast() {
+ System.out.println("testFast");
+ assertEquals(42, TestFast.nativeMethod(42));
+ assertEquals(42, TestFast.nativeMethodWithManyParameters(
+ 11, 12L, 13.0f, 14.0d,
+ 21, 22L, 23.0f, 24.0d,
+ 31, 32L, 33.0f, 34.0d,
+ 41, 42L, 43.0f, 44.0d,
+ 51, 52L, 53.0f, 54.0d,
+ 61, 62L, 63.0f, 64.0d,
+ 71, 72L, 73.0f, 74.0d,
+ 81, 82L, 83.0f, 84.0d));
+ }
+
+ static void testCritical() {
+ System.out.println("testCritical");
+ assertEquals(42, TestCritical.nativeMethod(42));
+ assertEquals(42, TestCritical.nativeMethodWithManyParameters(
+ 11, 12L, 13.0f, 14.0d,
+ 21, 22L, 23.0f, 24.0d,
+ 31, 32L, 33.0f, 34.0d,
+ 41, 42L, 43.0f, 44.0d,
+ 51, 52L, 53.0f, 54.0d,
+ 61, 62L, 63.0f, 64.0d,
+ 71, 72L, 73.0f, 74.0d,
+ 81, 82L, 83.0f, 84.0d));
+ }
+
+ static void testMissing() {
+ System.out.println("testMissing");
+
+ try {
+ TestMissing.nativeMethod(42);
+ throw new Error("UNREACHABLE");
+ } catch (LinkageError expected) {}
+
+ try {
+ TestMissing.nativeMethodWithManyParameters(
+ 11, 12L, 13.0f, 14.0d,
+ 21, 22L, 23.0f, 24.0d,
+ 31, 32L, 33.0f, 34.0d,
+ 41, 42L, 43.0f, 44.0d,
+ 51, 52L, 53.0f, 54.0d,
+ 61, 62L, 63.0f, 64.0d,
+ 71, 72L, 73.0f, 74.0d,
+ 81, 82L, 83.0f, 84.0d);
+ throw new Error("UNREACHABLE");
+ } catch (LinkageError expected) {}
+ }
+
+ static void testMissingFast() {
+ System.out.println("testMissingFast");
+
+ try {
+ TestMissingFast.nativeMethod(42);
+ throw new Error("UNREACHABLE");
+ } catch (LinkageError expected) {}
+
+ try {
+ TestMissingFast.nativeMethodWithManyParameters(
+ 11, 12L, 13.0f, 14.0d,
+ 21, 22L, 23.0f, 24.0d,
+ 31, 32L, 33.0f, 34.0d,
+ 41, 42L, 43.0f, 44.0d,
+ 51, 52L, 53.0f, 54.0d,
+ 61, 62L, 63.0f, 64.0d,
+ 71, 72L, 73.0f, 74.0d,
+ 81, 82L, 83.0f, 84.0d);
+ throw new Error("UNREACHABLE");
+ } catch (LinkageError expected) {}
+ }
+
+ static void testMissingCritical() {
+ System.out.println("testMissingCritical");
+
+ try {
+ TestMissingCritical.nativeMethod(42);
+ throw new Error("UNREACHABLE");
+ } catch (LinkageError expected) {}
+
+ try {
+ TestMissingCritical.nativeMethodWithManyParameters(
+ 11, 12L, 13.0f, 14.0d,
+ 21, 22L, 23.0f, 24.0d,
+ 31, 32L, 33.0f, 34.0d,
+ 41, 42L, 43.0f, 44.0d,
+ 51, 52L, 53.0f, 54.0d,
+ 61, 62L, 63.0f, 64.0d,
+ 71, 72L, 73.0f, 74.0d,
+ 81, 82L, 83.0f, 84.0d);
+ throw new Error("UNREACHABLE");
+ } catch (LinkageError expected) {}
+ }
+
+ static void assertEquals(int expected, int actual) {
+ if (expected != actual) {
+ throw new AssertionError("Expected " + expected + " got " + actual);
+ }
+ }
+
+ public static native boolean checkAppImageLoaded();
+ public static native void makeVisiblyInitialized();
+}
+
+class Test {
+ public static native int nativeMethod(int i);
+
+ public static native int nativeMethodWithManyParameters(
+ int i1, long l1, float f1, double d1,
+ int i2, long l2, float f2, double d2,
+ int i3, long l3, float f3, double d3,
+ int i4, long l4, float f4, double d4,
+ int i5, long l5, float f5, double d5,
+ int i6, long l6, float f6, double d6,
+ int i7, long l7, float f7, double d7,
+ int i8, long l8, float f8, double d8);
+}
+
+class TestFast {
+ @FastNative
+ public static native int nativeMethod(int i);
+
+ @FastNative
+ public static native int nativeMethodWithManyParameters(
+ int i1, long l1, float f1, double d1,
+ int i2, long l2, float f2, double d2,
+ int i3, long l3, float f3, double d3,
+ int i4, long l4, float f4, double d4,
+ int i5, long l5, float f5, double d5,
+ int i6, long l6, float f6, double d6,
+ int i7, long l7, float f7, double d7,
+ int i8, long l8, float f8, double d8);
+}
+
+class TestCritical {
+ @CriticalNative
+ public static native int nativeMethod(int i);
+
+ @CriticalNative
+ public static native int nativeMethodWithManyParameters(
+ int i1, long l1, float f1, double d1,
+ int i2, long l2, float f2, double d2,
+ int i3, long l3, float f3, double d3,
+ int i4, long l4, float f4, double d4,
+ int i5, long l5, float f5, double d5,
+ int i6, long l6, float f6, double d6,
+ int i7, long l7, float f7, double d7,
+ int i8, long l8, float f8, double d8);
+}
+
+class TestMissing {
+ public static native int nativeMethod(int i);
+
+ public static native int nativeMethodWithManyParameters(
+ int i1, long l1, float f1, double d1,
+ int i2, long l2, float f2, double d2,
+ int i3, long l3, float f3, double d3,
+ int i4, long l4, float f4, double d4,
+ int i5, long l5, float f5, double d5,
+ int i6, long l6, float f6, double d6,
+ int i7, long l7, float f7, double d7,
+ int i8, long l8, float f8, double d8);
+}
+
+class TestMissingFast {
+ @FastNative
+ public static native int nativeMethod(int i);
+
+ @FastNative
+ public static native int nativeMethodWithManyParameters(
+ int i1, long l1, float f1, double d1,
+ int i2, long l2, float f2, double d2,
+ int i3, long l3, float f3, double d3,
+ int i4, long l4, float f4, double d4,
+ int i5, long l5, float f5, double d5,
+ int i6, long l6, float f6, double d6,
+ int i7, long l7, float f7, double d7,
+ int i8, long l8, float f8, double d8);
+}
+
+class TestMissingCritical {
+ @CriticalNative
+ public static native int nativeMethod(int i);
+
+ @CriticalNative
+ public static native int nativeMethodWithManyParameters(
+ int i1, long l1, float f1, double d1,
+ int i2, long l2, float f2, double d2,
+ int i3, long l3, float f3, double d3,
+ int i4, long l4, float f4, double d4,
+ int i5, long l5, float f5, double d5,
+ int i6, long l6, float f6, double d6,
+ int i7, long l7, float f7, double d7,
+ int i8, long l8, float f8, double d8);
+}
diff --git a/test/Android.bp b/test/Android.bp
index f10d3788bf..de2ab3f5bb 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -543,6 +543,7 @@ cc_defaults {
"169-threadgroup-jni/jni_daemon_thread.cc",
"172-app-image-twice/debug_print_class.cc",
"177-visibly-initialized-deadlock/visibly_initialized.cc",
+ "178-app-image-native-method/native_methods.cc",
"1945-proxy-method-arguments/get_args.cc",
"203-multi-checkpoint/multi_checkpoint.cc",
"305-other-fault-handler/fault_handler.cc",