Skip to content

Commit

Permalink
Add getTrampolineForFunctionPointer method to native bridge
Browse files Browse the repository at this point in the history
Add a function to native bridge that generates trampolines
for guest pointers and call it for native-bridged namespaces.

Bug: http://b/330367443
Test: art/libnativebridge/tests/runtests.sh --skip-target
Test: ./android-cts/tools/cts-tradefed run cts -m CtsJniTestCases
Change-Id: I02cab5ea6c87cb4d5065033a4b6793eb57fe0f52
  • Loading branch information
dimitry- authored and Treehugger Robot committed Apr 5, 2024
1 parent 4e6db6e commit 5fe81fd
Show file tree
Hide file tree
Showing 10 changed files with 127 additions and 4 deletions.
19 changes: 19 additions & 0 deletions libnativebridge/include/nativebridge/native_bridge.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,11 @@ void* NativeBridgeGetTrampoline2(void* handle,
uint32_t len,
enum JNICallType jni_call_type);

void* NativeBridgeGetTrampolineForFunctionPointer(const void* method,
const char* shorty,
uint32_t len,
enum JNICallType jni_call_type);

// 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.
//
Expand Down Expand Up @@ -421,6 +426,20 @@ struct NativeBridgeCallbacks {
const char* shorty,
uint32_t len,
enum JNICallType jni_call_type);

// Get a native bridge trampoline for specified native method implementation pointer.
//
// Parameters:
// method [IN] pointer to method implementation (ususally registered via call to
// RegisterNatives).
// 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
void* (*getTrampolineForFunctionPointer)(const void* method,
const char* shorty,
uint32_t len,
enum JNICallType jni_call_type);
};

// Runtime interfaces to native bridge.
Expand Down
17 changes: 17 additions & 0 deletions libnativebridge/native_bridge.cc
Original file line number Diff line number Diff line change
Expand Up @@ -593,6 +593,23 @@ void* NativeBridgeGetTrampoline2(
return callbacks->getTrampoline(handle, name, shorty, len);
}

void* NativeBridgeGetTrampolineForFunctionPointer(const void* method,
const char* shorty,
uint32_t len,
JNICallType jni_call_type) {
if (!NativeBridgeInitialized()) {
return nullptr;
}

if (isCompatibleWith(CRITICAL_NATIVE_SUPPORT_VERSION)) {
return callbacks->getTrampolineForFunctionPointer(method, shorty, len, jni_call_type);
} else {
ALOGE("not compatible with version %d, getTrampolineFnPtrWithJNICallType() isn't invoked",
CRITICAL_NATIVE_SUPPORT_VERSION);
return nullptr;
}
}

bool NativeBridgeIsSupported(const char* libpath) {
if (NativeBridgeInitialized()) {
return callbacks->isSupported(libpath);
Expand Down
8 changes: 8 additions & 0 deletions libnativebridge/native_bridge_lazy.cc
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,14 @@ void* NativeBridgeGetTrampoline2(
return f(handle, name, shorty, len, jni_call_type);
}

void* NativeBridgeGetTrampolineForFunctionPointer(const void* method,
const char* shorty,
uint32_t len,
JNICallType jni_call_type) {
static auto f = GET_FUNC_PTR(NativeBridgeGetTrampolineForFunctionPointer);
return f(method, shorty, len, jni_call_type);
}

const char* NativeBridgeGetError() {
static auto f = GET_FUNC_PTR(NativeBridgeGetError);
return f();
Expand Down
12 changes: 12 additions & 0 deletions libnativebridge/tests/NativeBridge7CriticalNative_lib.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ static bool g_legacy_get_trampoline_called = false;
static bool g_get_trampoline2_called = false;
static JNICallType g_jni_call_type = kJNICallTypeRegular;

static bool g_get_trampoline_fn_ptr_called = false;
static JNICallType g_fn_ptr_jni_call_type = kJNICallTypeRegular;

void ResetTrampolineCalledState() {
g_legacy_get_trampoline_called = false;
g_get_trampoline2_called = false;
Expand All @@ -41,4 +44,13 @@ bool IsGetTrampoline2Called() { return g_get_trampoline2_called; }

JNICallType GetTrampoline2JNICallType() { return g_jni_call_type; }

void SetGetTrampolineFnPtrCalled(JNICallType jni_call_type) {
g_get_trampoline_fn_ptr_called = true;
g_fn_ptr_jni_call_type = jni_call_type;
}

bool IsGetTrampolineFnPtrCalled() { return g_get_trampoline_fn_ptr_called; }

JNICallType GetTrampolineFnPtrJNICallType() { return g_fn_ptr_jni_call_type; }

} // namespace android
4 changes: 4 additions & 0 deletions libnativebridge/tests/NativeBridge7CriticalNative_lib.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ void SetGetTrampoline2Called(JNICallType jni_call_type);
bool IsGetTrampoline2Called();
JNICallType GetTrampoline2JNICallType();

void SetGetTrampolineFnPtrCalled(JNICallType jni_call_type);
bool IsGetTrampolineFnPtrCalled();
JNICallType GetTrampolineFnPtrJNICallType();

} // namespace android

#endif // ART_LIBNATIVEBRIDGE_TESTS_NATIVEBRIDGE7CRITICALNATIVE_LIB_H_
9 changes: 9 additions & 0 deletions libnativebridge/tests/NativeBridge7CriticalNative_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,15 @@ TEST_F(NativeBridgeTest, V7_CriticalNative) {
ASSERT_FALSE(IsLegacyGetTrampolineCalled());
ASSERT_TRUE(IsGetTrampoline2Called());
EXPECT_EQ(GetTrampoline2JNICallType(), kJNICallTypeCriticalNative);

ASSERT_FALSE(IsGetTrampolineFnPtrCalled());

EXPECT_EQ(
NativeBridgeGetTrampolineForFunctionPointer(nullptr, "shorty", 6, kJNICallTypeCriticalNative),
nullptr);
ASSERT_FALSE(IsLegacyGetTrampolineCalled());
ASSERT_TRUE(IsGetTrampolineFnPtrCalled());
EXPECT_EQ(GetTrampolineFnPtrJNICallType(), kJNICallTypeCriticalNative);
}

} // namespace android
12 changes: 11 additions & 1 deletion libnativebridge/tests/NativeBridgeTestCase7.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,15 @@ extern "C" void* native_bridge7_getTrampoline2(void* /* handle */,
return nullptr;
}

extern "C" void* native_bridge7_getTrampolineForFunctionPointer(
const void* /* method */,
const char* /* shorty */,
uint32_t /* len */,
android::JNICallType jni_call_type) {
android::SetGetTrampolineFnPtrCalled(jni_call_type);
return nullptr;
}

extern "C" bool native_bridge7_isSupported(const char* /* libpath */) { return false; }

extern "C" const struct android::NativeBridgeRuntimeValues* native_bridge7_getAppEnv(
Expand Down Expand Up @@ -134,4 +143,5 @@ android::NativeBridgeCallbacks NativeBridgeItf{
// v6
&native_bridge7_preZygoteFork,
// v7
&native_bridge7_getTrampoline2};
&native_bridge7_getTrampoline2,
&native_bridge7_getTrampolineForFunctionPointer};
3 changes: 3 additions & 0 deletions libnativeloader/include/nativeloader/native_loader.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ FindNativeLoaderNamespaceByClassLoader(JNIEnv* env, jobject class_loader);
__attribute__((visibility("default"))) void* OpenNativeLibraryInNamespace(
struct NativeLoaderNamespace* ns, const char* path, bool* needs_native_bridge,
char** error_msg);

__attribute__((visibility("default"))) bool IsNamespaceNativeBridged(
const struct NativeLoaderNamespace* ns);
#endif

__attribute__((visibility("default")))
Expand Down
2 changes: 2 additions & 0 deletions libnativeloader/native_loader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -527,6 +527,8 @@ void* OpenNativeLibraryInNamespace(NativeLoaderNamespace* ns, const char* path,
return handle.ok() ? *handle : nullptr;
}

bool IsNamespaceNativeBridged(const struct NativeLoaderNamespace* ns) { return ns->IsBridged(); }

// native_bridge_namespaces are not supported for callers of this function.
// This function will return nullptr in the case when application is running
// on native bridge.
Expand Down
45 changes: 42 additions & 3 deletions runtime/jni/jni_internal.cc
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@

#include "jni_internal.h"

#include <cstdarg>
#include <log/log.h>

#include <cstdarg>
#include <memory>
#include <utility>

Expand All @@ -37,10 +38,10 @@
#include "dex/dex_file-inl.h"
#include "dex/utf-inl.h"
#include "fault_handler.h"
#include "handle_scope.h"
#include "hidden_api.h"
#include "gc/accounting/card_table-inl.h"
#include "gc_root.h"
#include "handle_scope.h"
#include "hidden_api.h"
#include "indirect_reference_table-inl.h"
#include "interpreter/interpreter.h"
#include "java_vm_ext.h"
Expand All @@ -58,7 +59,9 @@
#include "mirror/string-alloc-inl.h"
#include "mirror/string-inl.h"
#include "mirror/throwable.h"
#include "nativebridge/native_bridge.h"
#include "nativehelper/scoped_local_ref.h"
#include "nativeloader/native_loader.h"
#include "parsed_options.h"
#include "reflection.h"
#include "runtime.h"
Expand Down Expand Up @@ -2575,6 +2578,9 @@ class JNI {
<< c->PrettyDescriptor();
return JNI_OK;
}
bool is_class_loader_namespace_natively_bridged =
IsClassLoaderNamespaceNativelyBridged(soa, c->GetClassLoader());

CHECK_NON_NULL_ARGUMENT_FN_NAME("RegisterNatives", methods, JNI_ERR);
for (jint i = 0; i < method_count; ++i) {
const char* name = methods[i].name;
Expand Down Expand Up @@ -2683,6 +2689,9 @@ class JNI {
// TODO: make this a hard register error in the future.
}

if (is_class_loader_namespace_natively_bridged) {
fnPtr = GenerateNativeBridgeTrampoline(fnPtr, m);
}
const void* final_function_ptr = class_linker->RegisterNative(soa.Self(), m, fnPtr);
UNUSED(final_function_ptr);
}
Expand Down Expand Up @@ -2905,6 +2914,36 @@ class JNI {
return array;
}

static bool IsClassLoaderNamespaceNativelyBridged(ScopedObjectAccess& soa,
ObjPtr<mirror::ClassLoader> class_loader)
REQUIRES_SHARED(Locks::mutator_lock_) {
#if defined(__ANDROID__)
ScopedLocalRef<jobject> jclass_loader(soa.Env(), soa.AddLocalReference<jobject>(class_loader));
android::NativeLoaderNamespace* ns =
android::FindNativeLoaderNamespaceByClassLoader(soa.Env(), jclass_loader.get());
return ns != nullptr && android::IsNamespaceNativeBridged(ns);
#else
UNUSED(soa, class_loader);
return false;
#endif
}

static const void* GenerateNativeBridgeTrampoline(const void* fn_ptr, ArtMethod* method)
REQUIRES_SHARED(Locks::mutator_lock_) {
#if defined(__ANDROID__)
uint32_t shorty_length;
const char* shorty = method->GetShorty(&shorty_length);
android::JNICallType jni_call_type = method->IsCriticalNative() ?
android::JNICallType::kJNICallTypeCriticalNative :
android::JNICallType::kJNICallTypeRegular;
return NativeBridgeGetTrampolineForFunctionPointer(
fn_ptr, shorty, shorty_length, jni_call_type);
#else
UNUSED(method);
return fn_ptr;
#endif
}

template <typename ArrayT, typename ElementT, typename ArtArrayT>
static ElementT* GetPrimitiveArray(JNIEnv* env, ArrayT java_array, jboolean* is_copy) {
CHECK_NON_NULL_ARGUMENT(java_array);
Expand Down

0 comments on commit 5fe81fd

Please sign in to comment.