summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build/Android.bp11
-rw-r--r--libnativebridge/include/nativebridge/native_bridge.h19
-rw-r--r--libnativebridge/native_bridge.cc14
-rw-r--r--libnativebridge/tests/Android.bp19
-rw-r--r--libnativebridge/tests/NativeBridge8IdentifyTrampolines_lib.cpp31
-rw-r--r--libnativebridge/tests/NativeBridge8IdentifyTrampolines_lib.h27
-rw-r--r--libnativebridge/tests/NativeBridge8IdentifyTrampolines_test.cpp39
-rw-r--r--libnativebridge/tests/NativeBridgeTest.h1
-rw-r--r--libnativebridge/tests/NativeBridgeTestCase8.cpp147
-rw-r--r--oatdump/oatdump.cc10
-rw-r--r--runtime/art_method.h3
-rw-r--r--runtime/class_linker.cc294
-rw-r--r--runtime/imtable-inl.h51
-rw-r--r--runtime/imtable.h9
-rw-r--r--runtime/verifier/method_verifier.cc8
-rw-r--r--tools/fuzzer/class-verifier-corpus/b391844326.dexbin0 -> 1240 bytes
16 files changed, 492 insertions, 191 deletions
diff --git a/build/Android.bp b/build/Android.bp
index e8a0b5424b..17f6891c1b 100644
--- a/build/Android.bp
+++ b/build/Android.bp
@@ -177,13 +177,6 @@ art_global_defaults {
// `--exclude-libs` flag is not supported on windows/darwin.
ldflags: ["-Wl,--exclude-libs=libziparchive.a"],
},
- linux_bionic: {
- strip: {
- // Do not strip art libs when building for linux-bionic.
- // Otherwise we can't get any symbols out of crashes.
- none: true,
- },
- },
darwin: {
enabled: false,
},
@@ -200,6 +193,10 @@ art_global_defaults {
// clang/libunwind bugs that cause SEGVs in run-test-004-ThreadStress.
"-fno-omit-frame-pointer",
],
+ // Keep the symbols for host to symbolize crash stack traces.
+ strip: {
+ none: true,
+ },
},
// The build assumes that all our x86/x86_64 hosts (such as buildbots and developer
// desktops) support at least sse4.2/popcount. This firstly implies that the ART
diff --git a/libnativebridge/include/nativebridge/native_bridge.h b/libnativebridge/include/nativebridge/native_bridge.h
index 491521642c..db3d1e9509 100644
--- a/libnativebridge/include/nativebridge/native_bridge.h
+++ b/libnativebridge/include/nativebridge/native_bridge.h
@@ -100,6 +100,8 @@ void* NativeBridgeGetTrampolineForFunctionPointer(const void* method,
uint32_t len,
enum JNICallType jni_call_type);
+bool NativeBridgeIsNativeBridgeFunctionPointer(const void* method);
+
// True if native library paths are valid and is for an ABI that is supported by native bridge.
// The *libpath* must point to a library.
//
@@ -403,9 +405,10 @@ struct NativeBridgeCallbacks {
// Get a native bridge trampoline for specified native method implementation pointer.
//
// Parameters:
- // method [IN] pointer to method implementation (ususally registered via call to
+ // method [IN] pointer to method implementation (usually registered via call to
// RegisterNatives).
- // shorty [IN] short descriptor of native method len [IN] length of shorty
+ // shorty [IN] short descriptor of native method
+ // len [IN] length of shorty
// jni_call_type [IN] the type of JNI call
// Returns:
// address of trampoline if successful, otherwise NULL
@@ -413,6 +416,18 @@ struct NativeBridgeCallbacks {
const char* shorty,
uint32_t len,
enum JNICallType jni_call_type);
+
+ // Check if the method pointer belongs to native_bridge address space.
+ //
+ // Parameters:
+ // method [IN] pointer to a method implementation.
+ //
+ // Returns:
+ // true if the method is in native bridge implementation executable address
+ // space or in other words needs a trampoline to be able to run with native bridge.
+ //
+ // Introduced in: version 8
+ bool (*isNativeBridgeFunctionPointer)(const void* method);
};
// Runtime interfaces to native bridge.
diff --git a/libnativebridge/native_bridge.cc b/libnativebridge/native_bridge.cc
index 4461f79919..af85d4ee35 100644
--- a/libnativebridge/native_bridge.cc
+++ b/libnativebridge/native_bridge.cc
@@ -133,6 +133,8 @@ enum NativeBridgeImplementationVersion {
PRE_ZYGOTE_FORK_VERSION = 6,
// The version with critical_native support
CRITICAL_NATIVE_SUPPORT_VERSION = 7,
+ // The version with native bridge detection fallback for function pointers
+ IDENTIFY_NATIVELY_BRIDGED_FUNCTION_POINTERS_VERSION = 8,
};
// Whether we had an error at some point.
@@ -732,6 +734,18 @@ void* NativeBridgeLoadLibraryExt(const char* libpath, int flag, native_bridge_na
return nullptr;
}
+bool NativeBridgeIsNativeBridgeFunctionPointer(const void* method) {
+ if (NativeBridgeInitialized()) {
+ if (isCompatibleWith(IDENTIFY_NATIVELY_BRIDGED_FUNCTION_POINTERS_VERSION)) {
+ return callbacks->isNativeBridgeFunctionPointer(method);
+ } else {
+ ALOGW("not compatible with version %d, unable to call isNativeBridgeFunctionPointer",
+ IDENTIFY_NATIVELY_BRIDGED_FUNCTION_POINTERS_VERSION);
+ }
+ }
+ return false;
+}
+
} // extern "C"
} // namespace android
diff --git a/libnativebridge/tests/Android.bp b/libnativebridge/tests/Android.bp
index 93ef6feded..add18d12e3 100644
--- a/libnativebridge/tests/Android.bp
+++ b/libnativebridge/tests/Android.bp
@@ -85,6 +85,15 @@ cc_test_library {
],
}
+cc_test_library {
+ name: "libnativebridge8-test-case",
+ srcs: ["NativeBridgeTestCase8.cpp"],
+ defaults: ["libnativebridge-test-case-defaults"],
+ shared_libs: [
+ "libnativebridge8IdentifyTrampolines",
+ ],
+}
+
// A helper library to produce test-case side effect of PreZygoteForkNativeBridge.
cc_test_library {
name: "libnativebridge6prezygotefork",
@@ -98,6 +107,12 @@ cc_test_library {
defaults: ["libnativebridge-test-case-defaults"],
}
+cc_test_library {
+ name: "libnativebridge8IdentifyTrampolines",
+ srcs: ["NativeBridge8IdentifyTrampolines_lib.cpp"],
+ defaults: ["libnativebridge-test-case-defaults"],
+}
+
cc_test {
name: "libnativebridge-tests",
defaults: [
@@ -130,6 +145,7 @@ cc_test {
"NativeBridge3LoadLibraryExt_test.cpp",
"NativeBridge6PreZygoteFork_test.cpp",
"NativeBridge7CriticalNative_test.cpp",
+ "NativeBridge8IdentifyTrampolines_test.cpp",
],
static_libs: [
@@ -140,10 +156,12 @@ cc_test {
"liblog",
"libnativebridge6prezygotefork",
"libnativebridge7criticalnative",
+ "libnativebridge8IdentifyTrampolines",
],
data_libs: [
"libnativebridge6prezygotefork",
"libnativebridge7criticalnative",
+ "libnativebridge8IdentifyTrampolines",
// These are dlopen'd by libnativebridge, not libnativebridge-tests, but
// the former is statically linked into the latter, so the linker will
@@ -153,6 +171,7 @@ cc_test {
"libnativebridge3-test-case",
"libnativebridge6-test-case",
"libnativebridge7-test-case",
+ "libnativebridge8-test-case",
],
target: {
diff --git a/libnativebridge/tests/NativeBridge8IdentifyTrampolines_lib.cpp b/libnativebridge/tests/NativeBridge8IdentifyTrampolines_lib.cpp
new file mode 100644
index 0000000000..0c4ccf03b5
--- /dev/null
+++ b/libnativebridge/tests/NativeBridge8IdentifyTrampolines_lib.cpp
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2025 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 "NativeBridge8IdentifyTrampolines_lib.h"
+
+namespace android {
+
+static const void* g_is_native_bridge_function_pointer_called_for = nullptr;
+
+void SetIsNativeBridgeFunctionPointerCalledFor(const void* ptr) {
+ g_is_native_bridge_function_pointer_called_for = ptr;
+}
+
+bool IsNativeBridgeFunctionPointerCalledFor(const void* ptr) {
+ return g_is_native_bridge_function_pointer_called_for == ptr;
+}
+
+} // namespace android
diff --git a/libnativebridge/tests/NativeBridge8IdentifyTrampolines_lib.h b/libnativebridge/tests/NativeBridge8IdentifyTrampolines_lib.h
new file mode 100644
index 0000000000..7121e397a4
--- /dev/null
+++ b/libnativebridge/tests/NativeBridge8IdentifyTrampolines_lib.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2025 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_LIBNATIVEBRIDGE_TESTS_NATIVEBRIDGE8IDENTIFYTRAMPOLINES_LIB_H_
+#define ART_LIBNATIVEBRIDGE_TESTS_NATIVEBRIDGE8IDENTIFYTRAMPOLINES_LIB_H_
+
+namespace android {
+
+void SetIsNativeBridgeFunctionPointerCalledFor(const void* ptr);
+bool IsNativeBridgeFunctionPointerCalledFor(const void* ptr);
+
+} // namespace android
+
+#endif // ART_LIBNATIVEBRIDGE_TESTS_NATIVEBRIDGE8IDENTIFYTRAMPOLINES_LIB_H_
diff --git a/libnativebridge/tests/NativeBridge8IdentifyTrampolines_test.cpp b/libnativebridge/tests/NativeBridge8IdentifyTrampolines_test.cpp
new file mode 100644
index 0000000000..362df54fa1
--- /dev/null
+++ b/libnativebridge/tests/NativeBridge8IdentifyTrampolines_test.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2025 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 "NativeBridge8IdentifyTrampolines_lib.h"
+#include "NativeBridgeTest.h"
+
+namespace android {
+
+TEST_F(NativeBridgeTest, V8_IdentifyTrampolines) {
+ // Init
+ ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary8, nullptr));
+ ASSERT_TRUE(NativeBridgeAvailable());
+ ASSERT_TRUE(PreInitializeNativeBridge(AppDataDir(), "isa"));
+ ASSERT_TRUE(NativeBridgeAvailable());
+ ASSERT_TRUE(InitializeNativeBridge(nullptr, nullptr));
+ ASSERT_TRUE(NativeBridgeAvailable());
+
+ ASSERT_EQ(NativeBridgeGetVersion(), 8U);
+
+ const void* ptr = reinterpret_cast<void*>(NativeBridgeGetVersion);
+
+ UNUSED(NativeBridgeIsNativeBridgeFunctionPointer(ptr));
+ ASSERT_TRUE(IsNativeBridgeFunctionPointerCalledFor(ptr));
+}
+
+} // namespace android
diff --git a/libnativebridge/tests/NativeBridgeTest.h b/libnativebridge/tests/NativeBridgeTest.h
index 509973d756..88ea0e33c8 100644
--- a/libnativebridge/tests/NativeBridgeTest.h
+++ b/libnativebridge/tests/NativeBridgeTest.h
@@ -30,6 +30,7 @@ constexpr const char* kNativeBridgeLibrary2 = "libnativebridge2-test-case.so";
constexpr const char* kNativeBridgeLibrary3 = "libnativebridge3-test-case.so";
constexpr const char* kNativeBridgeLibrary6 = "libnativebridge6-test-case.so";
constexpr const char* kNativeBridgeLibrary7 = "libnativebridge7-test-case.so";
+constexpr const char* kNativeBridgeLibrary8 = "libnativebridge8-test-case.so";
namespace android {
diff --git a/libnativebridge/tests/NativeBridgeTestCase8.cpp b/libnativebridge/tests/NativeBridgeTestCase8.cpp
new file mode 100644
index 0000000000..c6395f526b
--- /dev/null
+++ b/libnativebridge/tests/NativeBridgeTestCase8.cpp
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2025 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.
+ */
+
+// An implementation of the native-bridge interface for testing.
+
+#include "NativeBridge8IdentifyTrampolines_lib.h"
+#include "nativebridge/native_bridge.h"
+
+// NativeBridgeCallbacks implementations
+extern "C" bool native_bridge8_initialize(
+ const android::NativeBridgeRuntimeCallbacks* /* art_cbs */,
+ const char* /* app_code_cache_dir */,
+ const char* /* isa */) {
+ return true;
+}
+
+extern "C" void* native_bridge8_loadLibrary(const char* /* libpath */, int /* flag */) {
+ return nullptr;
+}
+
+extern "C" void* native_bridge8_getTrampoline(void* /* handle */,
+ const char* /* name */,
+ const char* /* shorty */,
+ uint32_t /* len */) {
+ return nullptr;
+}
+
+extern "C" void* native_bridge8_getTrampoline2(void* /* handle */,
+ const char* /* name */,
+ const char* /* shorty */,
+ uint32_t /* len */,
+ android::JNICallType /* jni_call_type */) {
+ return nullptr;
+}
+
+extern "C" void* native_bridge8_getTrampolineForFunctionPointer(
+ const void* /* method */,
+ const char* /* shorty */,
+ uint32_t /* len */,
+ android::JNICallType /* jni_call_type */) {
+ return nullptr;
+}
+
+extern "C" bool native_bridge8_isSupported(const char* /* libpath */) { return false; }
+
+extern "C" const struct android::NativeBridgeRuntimeValues* native_bridge8_getAppEnv(
+ const char* /* abi */) {
+ return nullptr;
+}
+
+extern "C" bool native_bridge8_isCompatibleWith(uint32_t version) {
+ // For testing, allow 1-8, but disallow 9+.
+ return version <= 8;
+}
+
+extern "C" android::NativeBridgeSignalHandlerFn native_bridge8_getSignalHandler(int /* signal */) {
+ return nullptr;
+}
+
+extern "C" int native_bridge8_unloadLibrary(void* /* handle */) { return 0; }
+
+extern "C" const char* native_bridge8_getError() { return nullptr; }
+
+extern "C" bool native_bridge8_isPathSupported(const char* /* path */) { return true; }
+
+extern "C" android::native_bridge_namespace_t* native_bridge8_createNamespace(
+ const char* /* name */,
+ const char* /* ld_library_path */,
+ const char* /* default_library_path */,
+ uint64_t /* type */,
+ const char* /* permitted_when_isolated_path */,
+ android::native_bridge_namespace_t* /* parent_ns */) {
+ return nullptr;
+}
+
+extern "C" bool native_bridge8_linkNamespaces(android::native_bridge_namespace_t* /* from */,
+ android::native_bridge_namespace_t* /* to */,
+ const char* /* shared_libs_soname */) {
+ return true;
+}
+
+extern "C" void* native_bridge8_loadLibraryExt(const char* /* libpath */,
+ int /* flag */,
+ android::native_bridge_namespace_t* /* ns */) {
+ return nullptr;
+}
+
+extern "C" android::native_bridge_namespace_t* native_bridge8_getVendorNamespace() {
+ return nullptr;
+}
+
+extern "C" android::native_bridge_namespace_t* native_bridge8_getExportedNamespace(
+ const char* /* name */) {
+ return nullptr;
+}
+
+extern "C" bool native_bridge8_isNativeBridgeFunctionPointer(const void* ptr) {
+ android::SetIsNativeBridgeFunctionPointerCalledFor(ptr);
+ return true;
+}
+
+extern "C" void native_bridge8_preZygoteFork() {}
+
+android::NativeBridgeCallbacks NativeBridgeItf{
+ // v1
+ .version = 8,
+ .initialize = &native_bridge8_initialize,
+ .loadLibrary = &native_bridge8_loadLibrary,
+ .getTrampoline = &native_bridge8_getTrampoline,
+ .isSupported = &native_bridge8_isSupported,
+ .getAppEnv = &native_bridge8_getAppEnv,
+ // v2
+ .isCompatibleWith = &native_bridge8_isCompatibleWith,
+ .getSignalHandler = &native_bridge8_getSignalHandler,
+ // v3
+ .unloadLibrary = &native_bridge8_unloadLibrary,
+ .getError = &native_bridge8_getError,
+ .isPathSupported = &native_bridge8_isPathSupported,
+ .unused_initAnonymousNamespace = nullptr,
+ .createNamespace = &native_bridge8_createNamespace,
+ .linkNamespaces = &native_bridge8_linkNamespaces,
+ .loadLibraryExt = &native_bridge8_loadLibraryExt,
+ // v4
+ &native_bridge8_getVendorNamespace,
+ // v5
+ &native_bridge8_getExportedNamespace,
+ // v6
+ &native_bridge8_preZygoteFork,
+ // v7
+ &native_bridge8_getTrampoline2,
+ &native_bridge8_getTrampolineForFunctionPointer,
+ // v8
+ &native_bridge8_isNativeBridgeFunctionPointer,
+};
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index ab22e36c17..b6f681a184 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -3006,10 +3006,18 @@ class IMTDumper {
for (ArtMethod& iface_method : iface->GetVirtualMethods(pointer_size)) {
uint32_t class_hash, name_hash, signature_hash;
- ImTable::GetImtHashComponents(&iface_method, &class_hash, &name_hash, &signature_hash);
+ ImTable::GetImtHashComponents(*iface_method.GetDexFile(),
+ iface_method.GetDexMethodIndex(),
+ &class_hash,
+ &name_hash,
+ &signature_hash);
uint32_t imt_slot = ImTable::GetImtIndex(&iface_method);
+ // Note: For default methods we use the dex method index for calculating the slot.
+ // For abstract methods the compile-time constant `kImTableHashUseName` determines
+ // whether we use the component hashes (current behavior) or the dex method index.
std::cerr << " " << iface_method.PrettyMethod(true)
<< " slot=" << imt_slot
+ << " dex_method_index=" << iface_method.GetDexMethodIndex()
<< std::hex
<< " class_hash=0x" << class_hash
<< " name_hash=0x" << name_hash
diff --git a/runtime/art_method.h b/runtime/art_method.h
index ee11328385..186ff7e45e 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -1126,8 +1126,9 @@ class EXPORT ArtMethod final {
// Non-abstract methods: The hotness we measure for this method. Not atomic,
// as we allow missing increments: if the method is hot, we will see it eventually.
uint16_t hotness_count_;
- // Abstract methods: IMT index.
+ // Abstract interface methods: IMT index.
uint16_t imt_index_;
+ // Abstract class (non-interface) methods: Unused (zero-initialized).
};
// Fake padding field gets inserted here.
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 73c90f58ac..57eb72dea5 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -3827,44 +3827,6 @@ class ClassLinker::OatClassCodeIterator {
const uint32_t num_methods_;
};
-inline void ClassLinker::LinkCode(ArtMethod* method,
- uint32_t class_def_method_index,
- /*inout*/ OatClassCodeIterator* occi) {
- ScopedAssertNoThreadSuspension sants(__FUNCTION__);
- Runtime* const runtime = Runtime::Current();
- if (runtime->IsAotCompiler()) {
- // The following code only applies to a non-compiler runtime.
- return;
- }
-
- // Method shouldn't have already been linked.
- DCHECK_EQ(method->GetEntryPointFromQuickCompiledCode(), nullptr);
- DCHECK(!method->GetDeclaringClass()->IsVisiblyInitialized()); // Actually ClassStatus::Idx.
-
- if (!method->IsInvokable()) {
- EnsureThrowsInvocationError(this, method);
- occi->SkipAbstract(class_def_method_index);
- return;
- }
-
- const void* quick_code = occi->GetAndAdvance(class_def_method_index);
- if (method->IsNative() && quick_code == nullptr) {
- const void* boot_jni_stub = FindBootJniStub(method);
- if (boot_jni_stub != nullptr) {
- // Use boot JNI stub if found.
- quick_code = boot_jni_stub;
- }
- }
- runtime->GetInstrumentation()->InitializeMethodsCode(method, quick_code);
-
- if (method->IsNative()) {
- // Set up the dlsym lookup stub. Do not go through `UnregisterNative()`
- // as the extra processing for @CriticalNative is not needed yet.
- method->SetEntryPointFromJni(
- method->IsCriticalNative() ? GetJniDlsymLookupCriticalStub() : GetJniDlsymLookupStub());
- }
-}
-
void ClassLinker::SetupClass(const DexFile& dex_file,
const dex::ClassDef& dex_class_def,
Handle<mirror::Class> klass,
@@ -3968,6 +3930,153 @@ class ClassLinker::MethodAnnotationsIterator {
const dex::MethodAnnotationsItem* const end_;
};
+void ClassLinker::LoadField(const ClassAccessor::Field& field,
+ Handle<mirror::Class> klass,
+ ArtField* dst) {
+ const uint32_t field_idx = field.GetIndex();
+ dst->SetDexFieldIndex(field_idx);
+ dst->SetDeclaringClass(klass.Get());
+
+ // Get access flags from the DexFile and set hiddenapi runtime access flags.
+ dst->SetAccessFlags(field.GetAccessFlags() | hiddenapi::CreateRuntimeFlags(field));
+}
+
+void ClassLinker::LoadMethod(const DexFile& dex_file,
+ const ClassAccessor::Method& method,
+ ObjPtr<mirror::Class> klass,
+ /*inout*/ MethodAnnotationsIterator* mai,
+ /*out*/ ArtMethod* dst) {
+ ScopedAssertNoThreadSuspension sants(__FUNCTION__);
+
+ const uint32_t dex_method_idx = method.GetIndex();
+ const dex::MethodId& method_id = dex_file.GetMethodId(dex_method_idx);
+ uint32_t name_utf16_length;
+ const char* method_name = dex_file.GetStringDataAndUtf16Length(method_id.name_idx_,
+ &name_utf16_length);
+ std::string_view shorty = dex_file.GetShortyView(dex_file.GetProtoId(method_id.proto_idx_));
+
+ dst->SetDexMethodIndex(dex_method_idx);
+ dst->SetDeclaringClass(klass);
+
+ // Get access flags from the DexFile and set hiddenapi runtime access flags.
+ uint32_t access_flags = method.GetAccessFlags() | hiddenapi::CreateRuntimeFlags(method);
+
+ auto has_ascii_name = [method_name, name_utf16_length](const char* ascii_name,
+ size_t length) ALWAYS_INLINE {
+ DCHECK_EQ(strlen(ascii_name), length);
+ return length == name_utf16_length &&
+ method_name[length] == 0 && // Is `method_name` an ASCII string?
+ memcmp(ascii_name, method_name, length) == 0;
+ };
+ if (UNLIKELY(has_ascii_name("finalize", sizeof("finalize") - 1u))) {
+ // Set finalizable flag on declaring class if the method has the right signature.
+ // When initializing without a boot image, `Object` and `Enum` shall have the finalizable
+ // flag cleared immediately after loading these classes, see `InitWithoutImage()`.
+ if (shorty == "V") {
+ klass->SetFinalizable();
+ }
+ } else if (method_name[0] == '<') {
+ // Fix broken access flags for initializers. Bug 11157540.
+ // `DexFileVerifier` rejects method names starting with '<' other than constructors.
+ DCHECK(has_ascii_name("<init>", sizeof("<init>") - 1u) ||
+ has_ascii_name("<clinit>", sizeof("<clinit>") - 1u)) << method_name;
+ if (UNLIKELY((access_flags & kAccConstructor) == 0)) {
+ LOG(WARNING) << method_name << " didn't have expected constructor access flag in class "
+ << klass->PrettyDescriptor() << " in dex file " << dex_file.GetLocation();
+ access_flags |= kAccConstructor;
+ }
+ }
+
+ access_flags |= GetNterpFastPathFlags(shorty, access_flags, kRuntimeQuickCodeISA);
+
+ if (UNLIKELY((access_flags & kAccNative) != 0u)) {
+ // Check if the native method is annotated with @FastNative or @CriticalNative.
+ const dex::MethodAnnotationsItem* method_annotations = mai->AdvanceTo(dex_method_idx);
+ if (method_annotations != nullptr) {
+ access_flags |=
+ annotations::GetNativeMethodAnnotationAccessFlags(dex_file, *method_annotations);
+ }
+ dst->SetAccessFlags(access_flags);
+ DCHECK(!dst->IsAbstract());
+ DCHECK(!dst->HasCodeItem());
+ DCHECK_EQ(method.GetCodeItemOffset(), 0u);
+ dst->SetDataPtrSize(nullptr, image_pointer_size_); // JNI stub/trampoline not linked yet.
+ } else if ((access_flags & kAccAbstract) != 0u) {
+ dst->SetAccessFlags(access_flags);
+ // Must be done after SetAccessFlags since IsAbstract depends on it.
+ DCHECK(dst->IsAbstract());
+ if (klass->IsInterface()) {
+ dst->CalculateAndSetImtIndex();
+ }
+ DCHECK(!dst->HasCodeItem());
+ DCHECK_EQ(method.GetCodeItemOffset(), 0u);
+ dst->SetDataPtrSize(nullptr, image_pointer_size_); // Single implementation not set yet.
+ } else {
+ const dex::MethodAnnotationsItem* method_annotations = mai->AdvanceTo(dex_method_idx);
+ if (method_annotations != nullptr &&
+ annotations::MethodIsNeverCompile(dex_file, *method_annotations)) {
+ access_flags |= kAccCompileDontBother;
+ }
+ dst->SetAccessFlags(access_flags);
+ DCHECK(!dst->IsAbstract());
+ DCHECK(dst->HasCodeItem());
+ uint32_t code_item_offset = method.GetCodeItemOffset();
+ DCHECK_NE(code_item_offset, 0u);
+ if (Runtime::Current()->IsAotCompiler()) {
+ dst->SetDataPtrSize(reinterpret_cast32<void*>(code_item_offset), image_pointer_size_);
+ } else {
+ dst->SetCodeItem(dex_file.GetCodeItem(code_item_offset), dex_file.IsCompactDexFile());
+ }
+ }
+
+ if ((access_flags & kAccAbstract) == 0u &&
+ Runtime::Current()->IsZygote() &&
+ !Runtime::Current()->GetJITOptions()->GetProfileSaverOptions().GetProfileBootClassPath()) {
+ DCHECK(!ArtMethod::IsAbstract(access_flags));
+ DCHECK(!ArtMethod::IsIntrinsic(access_flags));
+ dst->SetMemorySharedMethod();
+ dst->SetHotCounter();
+ }
+}
+
+inline void ClassLinker::LinkCode(ArtMethod* method,
+ uint32_t class_def_method_index,
+ /*inout*/ OatClassCodeIterator* occi) {
+ ScopedAssertNoThreadSuspension sants(__FUNCTION__);
+ Runtime* const runtime = Runtime::Current();
+ if (runtime->IsAotCompiler()) {
+ // The following code only applies to a non-compiler runtime.
+ return;
+ }
+
+ // Method shouldn't have already been linked.
+ DCHECK_EQ(method->GetEntryPointFromQuickCompiledCode(), nullptr);
+ DCHECK(!method->GetDeclaringClass()->IsVisiblyInitialized()); // Actually ClassStatus::Idx.
+
+ if (!method->IsInvokable()) {
+ EnsureThrowsInvocationError(this, method);
+ occi->SkipAbstract(class_def_method_index);
+ return;
+ }
+
+ const void* quick_code = occi->GetAndAdvance(class_def_method_index);
+ if (method->IsNative() && quick_code == nullptr) {
+ const void* boot_jni_stub = FindBootJniStub(method);
+ if (boot_jni_stub != nullptr) {
+ // Use boot JNI stub if found.
+ quick_code = boot_jni_stub;
+ }
+ }
+ runtime->GetInstrumentation()->InitializeMethodsCode(method, quick_code);
+
+ if (method->IsNative()) {
+ // Set up the dlsym lookup stub. Do not go through `UnregisterNative()`
+ // as the extra processing for @CriticalNative is not needed yet.
+ method->SetEntryPointFromJni(
+ method->IsCriticalNative() ? GetJniDlsymLookupCriticalStub() : GetJniDlsymLookupStub());
+ }
+}
+
void ClassLinker::LoadClass(Thread* self,
const DexFile& dex_file,
const dex::ClassDef& dex_class_def,
@@ -4090,115 +4199,6 @@ void ClassLinker::LoadClass(Thread* self,
self->AllowThreadSuspension();
}
-void ClassLinker::LoadField(const ClassAccessor::Field& field,
- Handle<mirror::Class> klass,
- ArtField* dst) {
- const uint32_t field_idx = field.GetIndex();
- dst->SetDexFieldIndex(field_idx);
- dst->SetDeclaringClass(klass.Get());
-
- // Get access flags from the DexFile and set hiddenapi runtime access flags.
- dst->SetAccessFlags(field.GetAccessFlags() | hiddenapi::CreateRuntimeFlags(field));
-}
-
-void ClassLinker::LoadMethod(const DexFile& dex_file,
- const ClassAccessor::Method& method,
- ObjPtr<mirror::Class> klass,
- /*inout*/ MethodAnnotationsIterator* mai,
- /*out*/ ArtMethod* dst) {
- ScopedAssertNoThreadSuspension sants(__FUNCTION__);
-
- const uint32_t dex_method_idx = method.GetIndex();
- const dex::MethodId& method_id = dex_file.GetMethodId(dex_method_idx);
- uint32_t name_utf16_length;
- const char* method_name = dex_file.GetStringDataAndUtf16Length(method_id.name_idx_,
- &name_utf16_length);
- std::string_view shorty = dex_file.GetShortyView(dex_file.GetProtoId(method_id.proto_idx_));
-
- dst->SetDexMethodIndex(dex_method_idx);
- dst->SetDeclaringClass(klass);
-
- // Get access flags from the DexFile and set hiddenapi runtime access flags.
- uint32_t access_flags = method.GetAccessFlags() | hiddenapi::CreateRuntimeFlags(method);
-
- auto has_ascii_name = [method_name, name_utf16_length](const char* ascii_name,
- size_t length) ALWAYS_INLINE {
- DCHECK_EQ(strlen(ascii_name), length);
- return length == name_utf16_length &&
- method_name[length] == 0 && // Is `method_name` an ASCII string?
- memcmp(ascii_name, method_name, length) == 0;
- };
- if (UNLIKELY(has_ascii_name("finalize", sizeof("finalize") - 1u))) {
- // Set finalizable flag on declaring class if the method has the right signature.
- // When initializing without a boot image, `Object` and `Enum` shall have the finalizable
- // flag cleared immediately after loading these classes, see `InitWithoutImage()`.
- if (shorty == "V") {
- klass->SetFinalizable();
- }
- } else if (method_name[0] == '<') {
- // Fix broken access flags for initializers. Bug 11157540.
- // `DexFileVerifier` rejects method names starting with '<' other than constructors.
- DCHECK(has_ascii_name("<init>", sizeof("<init>") - 1u) ||
- has_ascii_name("<clinit>", sizeof("<clinit>") - 1u)) << method_name;
- if (UNLIKELY((access_flags & kAccConstructor) == 0)) {
- LOG(WARNING) << method_name << " didn't have expected constructor access flag in class "
- << klass->PrettyDescriptor() << " in dex file " << dex_file.GetLocation();
- access_flags |= kAccConstructor;
- }
- }
-
- access_flags |= GetNterpFastPathFlags(shorty, access_flags, kRuntimeQuickCodeISA);
-
- if (UNLIKELY((access_flags & kAccNative) != 0u)) {
- // Check if the native method is annotated with @FastNative or @CriticalNative.
- const dex::MethodAnnotationsItem* method_annotations = mai->AdvanceTo(dex_method_idx);
- if (method_annotations != nullptr) {
- access_flags |=
- annotations::GetNativeMethodAnnotationAccessFlags(dex_file, *method_annotations);
- }
- dst->SetAccessFlags(access_flags);
- DCHECK(!dst->IsAbstract());
- DCHECK(!dst->HasCodeItem());
- DCHECK_EQ(method.GetCodeItemOffset(), 0u);
- dst->SetDataPtrSize(nullptr, image_pointer_size_); // JNI stub/trampoline not linked yet.
- } else if ((access_flags & kAccAbstract) != 0u) {
- dst->SetAccessFlags(access_flags);
- // Must be done after SetAccessFlags since IsAbstract depends on it.
- DCHECK(dst->IsAbstract());
- if (klass->IsInterface()) {
- dst->CalculateAndSetImtIndex();
- }
- DCHECK(!dst->HasCodeItem());
- DCHECK_EQ(method.GetCodeItemOffset(), 0u);
- dst->SetDataPtrSize(nullptr, image_pointer_size_); // Single implementation not set yet.
- } else {
- const dex::MethodAnnotationsItem* method_annotations = mai->AdvanceTo(dex_method_idx);
- if (method_annotations != nullptr &&
- annotations::MethodIsNeverCompile(dex_file, *method_annotations)) {
- access_flags |= kAccCompileDontBother;
- }
- dst->SetAccessFlags(access_flags);
- DCHECK(!dst->IsAbstract());
- DCHECK(dst->HasCodeItem());
- uint32_t code_item_offset = method.GetCodeItemOffset();
- DCHECK_NE(code_item_offset, 0u);
- if (Runtime::Current()->IsAotCompiler()) {
- dst->SetDataPtrSize(reinterpret_cast32<void*>(code_item_offset), image_pointer_size_);
- } else {
- dst->SetCodeItem(dex_file.GetCodeItem(code_item_offset), dex_file.IsCompactDexFile());
- }
- }
-
- if ((access_flags & kAccAbstract) == 0u &&
- Runtime::Current()->IsZygote() &&
- !Runtime::Current()->GetJITOptions()->GetProfileSaverOptions().GetProfileBootClassPath()) {
- DCHECK(!ArtMethod::IsAbstract(access_flags));
- DCHECK(!ArtMethod::IsIntrinsic(access_flags));
- dst->SetMemorySharedMethod();
- dst->SetHotCounter();
- }
-}
-
void ClassLinker::AppendToBootClassPath(Thread* self, const DexFile* dex_file) {
ObjPtr<mirror::DexCache> dex_cache =
AllocAndInitializeDexCache(self, *dex_file, /* class_loader= */ nullptr);
diff --git a/runtime/imtable-inl.h b/runtime/imtable-inl.h
index 9be56cb8f8..8af1d89f60 100644
--- a/runtime/imtable-inl.h
+++ b/runtime/imtable-inl.h
@@ -33,65 +33,52 @@ static constexpr uint32_t kImTableHashCoefficientClass = 427;
static constexpr uint32_t kImTableHashCoefficientName = 16;
static constexpr uint32_t kImTableHashCoefficientSignature = 14;
-inline void ImTable::GetImtHashComponents(ArtMethod* method,
+inline void ImTable::GetImtHashComponents(const DexFile& dex_file,
+ uint32_t dex_method_index,
uint32_t* class_hash,
uint32_t* name_hash,
uint32_t* signature_hash) {
if (kImTableHashUseName) {
- if (method->IsProxyMethod()) {
- *class_hash = 0;
- *name_hash = 0;
- *signature_hash = 0;
- return;
- }
-
- const DexFile* dex_file = method->GetDexFile();
- const dex::MethodId& method_id = dex_file->GetMethodId(method->GetDexMethodIndex());
+ const dex::MethodId& method_id = dex_file.GetMethodId(dex_method_index);
// Class descriptor for the class component.
- *class_hash = ComputeModifiedUtf8Hash(dex_file->GetMethodDeclaringClassDescriptor(method_id));
+ *class_hash = ComputeModifiedUtf8Hash(dex_file.GetMethodDeclaringClassDescriptor(method_id));
// Method name for the method component.
- *name_hash = ComputeModifiedUtf8Hash(dex_file->GetMethodName(method_id));
+ *name_hash = ComputeModifiedUtf8Hash(dex_file.GetMethodName(method_id));
- const dex::ProtoId& proto_id = dex_file->GetMethodPrototype(method_id);
+ const dex::ProtoId& proto_id = dex_file.GetMethodPrototype(method_id);
// Read the proto for the signature component.
uint32_t tmp = ComputeModifiedUtf8Hash(
- dex_file->GetTypeDescriptor(dex_file->GetTypeId(proto_id.return_type_idx_)));
+ dex_file.GetTypeDescriptor(dex_file.GetTypeId(proto_id.return_type_idx_)));
// Mix in the argument types.
// Note: we could consider just using the shorty. This would be faster, at the price of
// potential collisions.
- const dex::TypeList* param_types = dex_file->GetProtoParameters(proto_id);
+ const dex::TypeList* param_types = dex_file.GetProtoParameters(proto_id);
if (param_types != nullptr) {
for (size_t i = 0; i != param_types->Size(); ++i) {
const dex::TypeItem& type = param_types->GetTypeItem(i);
tmp = 31 * tmp + ComputeModifiedUtf8Hash(
- dex_file->GetTypeDescriptor(dex_file->GetTypeId(type.type_idx_)));
+ dex_file.GetTypeDescriptor(dex_file.GetTypeId(type.type_idx_)));
}
}
*signature_hash = tmp;
return;
} else {
- *class_hash = method->GetDexMethodIndex();
+ *class_hash = dex_method_index;
*name_hash = 0;
*signature_hash = 0;
return;
}
}
-inline uint32_t ImTable::GetImtIndex(ArtMethod* method) {
- DCHECK(!method->IsCopied());
- if (!method->IsAbstract()) {
- // For default methods, where we cannot store the imt_index, we use the
- // method_index instead. We mask it with the closest power of two to
- // simplify the interpreter.
- return method->GetMethodIndex() & (ImTable::kSizeTruncToPowerOfTwo - 1);
- }
+inline uint32_t ImTable::GetImtIndexForAbstractMethod(const DexFile& dex_file,
+ uint32_t dex_method_index) {
uint32_t class_hash, name_hash, signature_hash;
- GetImtHashComponents(method, &class_hash, &name_hash, &signature_hash);
+ GetImtHashComponents(dex_file, dex_method_index, &class_hash, &name_hash, &signature_hash);
uint32_t mixed_hash;
if (!kImTableHashUseCoefficients) {
@@ -105,6 +92,18 @@ inline uint32_t ImTable::GetImtIndex(ArtMethod* method) {
return mixed_hash % ImTable::kSize;
}
+inline uint32_t ImTable::GetImtIndex(ArtMethod* method) {
+ DCHECK(!method->IsCopied());
+ DCHECK(!method->IsProxyMethod());
+ if (!method->IsAbstract()) {
+ // For default methods, where we cannot store the imt_index, we use the
+ // method_index instead. We mask it with the closest power of two to
+ // simplify the interpreter.
+ return method->GetMethodIndex() & (ImTable::kSizeTruncToPowerOfTwo - 1);
+ }
+ return GetImtIndexForAbstractMethod(*method->GetDexFile(), method->GetDexMethodIndex());
+}
+
} // namespace art
#endif // ART_RUNTIME_IMTABLE_INL_H_
diff --git a/runtime/imtable.h b/runtime/imtable.h
index 0d604ca244..f7e9066c78 100644
--- a/runtime/imtable.h
+++ b/runtime/imtable.h
@@ -81,11 +81,14 @@ class ImTable {
}
// Converts a method to the base hash components used in GetImtIndex.
- ALWAYS_INLINE static inline void GetImtHashComponents(ArtMethod* method,
+ ALWAYS_INLINE static inline void GetImtHashComponents(const DexFile& dex_file,
+ uint32_t dex_method_index,
uint32_t* class_hash,
uint32_t* name_hash,
- uint32_t* signature_hash)
- REQUIRES_SHARED(Locks::mutator_lock_);
+ uint32_t* signature_hash);
+
+ ALWAYS_INLINE static inline uint32_t GetImtIndexForAbstractMethod(const DexFile& dex_file,
+ uint32_t dex_method_index);
// The (complete) hashing scheme to map an ArtMethod to a slot in the Interface Method Table
// (IMT).
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index 05fb9b39b6..1de1e6bce8 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -1593,6 +1593,10 @@ bool MethodVerifier<kVerifierDebug>::Verify() {
return false;
}
+ if (code_item_accessor_.InsnsSizeInCodeUnits() == 0u) {
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "code item has no opcode";
+ return false;
+ }
// Allocate and initialize an array to hold instruction data.
insn_flags_.reset(allocator_.AllocArray<InstructionFlags>(
code_item_accessor_.InsnsSizeInCodeUnits()));
@@ -1619,10 +1623,6 @@ bool MethodVerifierImpl::ComputeWidthsAndCountOps() {
// We can't assume the instruction is well formed, handle the case where calculating the size
// goes past the end of the code item.
const uint32_t insns_size = code_item_accessor_.InsnsSizeInCodeUnits();
- if (insns_size == 0u) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "code item has no opcode";
- return false;
- }
const Instruction* inst = &code_item_accessor_.InstructionAt(0u);
uint32_t dex_pc = 0u;
while (dex_pc != insns_size) {
diff --git a/tools/fuzzer/class-verifier-corpus/b391844326.dex b/tools/fuzzer/class-verifier-corpus/b391844326.dex
new file mode 100644
index 0000000000..faf536139b
--- /dev/null
+++ b/tools/fuzzer/class-verifier-corpus/b391844326.dex
Binary files differ