Support field VarHandle objects in boot image.
Test: Build with WIP changes to implement AtomicInteger with
VarHandle. Build succeeds.
Test: m test-art-host-gtest
Test: testrunner.py --host --optimizing
Bug: 191765508
Change-Id: I084ab7b3cd082570a090f3470fdfb792b80f4505
diff --git a/dex2oat/linker/image_writer.cc b/dex2oat/linker/image_writer.cc
index 38855cb..d7c2d65 100644
--- a/dex2oat/linker/image_writer.cc
+++ b/dex2oat/linker/image_writer.cc
@@ -72,6 +72,7 @@
#include "mirror/object_array-alloc-inl.h"
#include "mirror/object_array-inl.h"
#include "mirror/string-inl.h"
+#include "mirror/var_handle.h"
#include "nterp_helpers.h"
#include "oat.h"
#include "oat_file.h"
@@ -3077,6 +3078,19 @@
}
}
+ArtField* ImageWriter::NativeLocationInImage(ArtField* src_field) {
+ // Fields are not individually stored in the native relocation map. Use the field array.
+ ObjPtr<mirror::Class> declaring_class = src_field->GetDeclaringClass();
+ LengthPrefixedArray<ArtField>* src_fields =
+ src_field->IsStatic() ? declaring_class->GetSFieldsPtr() : declaring_class->GetIFieldsPtr();
+ DCHECK(src_fields != nullptr);
+ LengthPrefixedArray<ArtField>* dst_fields = NativeLocationInImage(src_fields);
+ DCHECK(dst_fields != nullptr);
+ size_t field_offset =
+ reinterpret_cast<uint8_t*>(src_field) - reinterpret_cast<uint8_t*>(src_fields);
+ return reinterpret_cast<ArtField*>(reinterpret_cast<uint8_t*>(dst_fields) + field_offset);
+}
+
class ImageWriter::NativeLocationVisitor {
public:
explicit NativeLocationVisitor(ImageWriter* image_writer)
@@ -3147,17 +3161,24 @@
ObjPtr<mirror::Class> klass = orig->GetClass();
if (klass == GetClassRoot<mirror::Method>(class_roots) ||
klass == GetClassRoot<mirror::Constructor>(class_roots)) {
- // Need to go update the ArtMethod.
+ // Need to update the ArtMethod.
auto* dest = down_cast<mirror::Executable*>(copy);
auto* src = down_cast<mirror::Executable*>(orig);
ArtMethod* src_method = src->GetArtMethod();
CopyAndFixupPointer(dest, mirror::Executable::ArtMethodOffset(), src_method);
+ } else if (klass == GetClassRoot<mirror::FieldVarHandle>(class_roots) ||
+ klass == GetClassRoot<mirror::StaticFieldVarHandle>(class_roots)) {
+ // Need to update the ArtField.
+ auto* dest = down_cast<mirror::FieldVarHandle*>(copy);
+ auto* src = down_cast<mirror::FieldVarHandle*>(orig);
+ ArtField* src_field = src->GetArtField();
+ CopyAndFixupPointer(dest, mirror::FieldVarHandle::ArtFieldOffset(), src_field);
} else if (klass == GetClassRoot<mirror::DexCache>(class_roots)) {
down_cast<mirror::DexCache*>(copy)->ResetNativeFields();
} else if (klass->IsClassLoaderClass()) {
mirror::ClassLoader* copy_loader = down_cast<mirror::ClassLoader*>(copy);
// If src is a ClassLoader, set the class table to null so that it gets recreated by the
- // ClassLoader.
+ // ClassLinker.
copy_loader->SetClassTable(nullptr);
// Also set allocator to null to be safe. The allocator is created when we create the class
// table. We also never expect to unload things in the image since they are held live as
@@ -3525,30 +3546,36 @@
dest->Assign(GetImageAddress(src.Ptr()));
}
-void ImageWriter::CopyAndFixupPointer(void** target, void* value, PointerSize pointer_size) {
- void* new_value = NativeLocationInImage(value);
+template <typename ValueType>
+void ImageWriter::CopyAndFixupPointer(
+ void** target, ValueType src_value, PointerSize pointer_size) {
+ DCHECK(src_value != nullptr);
+ void* new_value = NativeLocationInImage(src_value);
+ DCHECK(new_value != nullptr);
if (pointer_size == PointerSize::k32) {
*reinterpret_cast<uint32_t*>(target) = reinterpret_cast32<uint32_t>(new_value);
} else {
*reinterpret_cast<uint64_t*>(target) = reinterpret_cast64<uint64_t>(new_value);
}
- DCHECK(value != nullptr);
}
-void ImageWriter::CopyAndFixupPointer(void** target, void* value)
+template <typename ValueType>
+void ImageWriter::CopyAndFixupPointer(void** target, ValueType src_value)
REQUIRES_SHARED(Locks::mutator_lock_) {
- CopyAndFixupPointer(target, value, target_ptr_size_);
+ CopyAndFixupPointer(target, src_value, target_ptr_size_);
}
+template <typename ValueType>
void ImageWriter::CopyAndFixupPointer(
- void* object, MemberOffset offset, void* value, PointerSize pointer_size) {
+ void* object, MemberOffset offset, ValueType src_value, PointerSize pointer_size) {
void** target =
reinterpret_cast<void**>(reinterpret_cast<uint8_t*>(object) + offset.Uint32Value());
- return CopyAndFixupPointer(target, value, pointer_size);
+ return CopyAndFixupPointer(target, src_value, pointer_size);
}
-void ImageWriter::CopyAndFixupPointer(void* object, MemberOffset offset, void* value) {
- return CopyAndFixupPointer(object, offset, value, target_ptr_size_);
+template <typename ValueType>
+void ImageWriter::CopyAndFixupPointer(void* object, MemberOffset offset, ValueType src_value) {
+ return CopyAndFixupPointer(object, offset, src_value, target_ptr_size_);
}
} // namespace linker
diff --git a/dex2oat/linker/image_writer.h b/dex2oat/linker/image_writer.h
index 1321ced..1a0d4c8 100644
--- a/dex2oat/linker/image_writer.h
+++ b/dex2oat/linker/image_writer.h
@@ -566,6 +566,7 @@
// Location of where the object will be when the image is loaded at runtime.
template <typename T>
T* NativeLocationInImage(T* obj) REQUIRES_SHARED(Locks::mutator_lock_);
+ ArtField* NativeLocationInImage(ArtField* src_field) REQUIRES_SHARED(Locks::mutator_lock_);
// Return true if `dex_cache` belongs to the image we're writing.
// For a boot image, this is true for all dex caches.
@@ -603,15 +604,19 @@
void CopyAndFixupReference(DestType* dest, ObjPtr<mirror::Object> src)
REQUIRES_SHARED(Locks::mutator_lock_);
- // Copy a native pointer and record image relocation.
- void CopyAndFixupPointer(void** target, void* value, PointerSize pointer_size)
+ // Translate a native pointer to the destination value and store in the target location.
+ template <typename ValueType>
+ void CopyAndFixupPointer(void** target, ValueType src_value, PointerSize pointer_size)
REQUIRES_SHARED(Locks::mutator_lock_);
- void CopyAndFixupPointer(void** target, void* value)
+ template <typename ValueType>
+ void CopyAndFixupPointer(void** target, ValueType src_value)
REQUIRES_SHARED(Locks::mutator_lock_);
+ template <typename ValueType>
void CopyAndFixupPointer(
- void* object, MemberOffset offset, void* value, PointerSize pointer_size)
+ void* object, MemberOffset offset, ValueType src_value, PointerSize pointer_size)
REQUIRES_SHARED(Locks::mutator_lock_);
- void CopyAndFixupPointer(void* object, MemberOffset offset, void* value)
+ template <typename ValueType>
+ void CopyAndFixupPointer(void* object, MemberOffset offset, ValueType src_value)
REQUIRES_SHARED(Locks::mutator_lock_);
/*
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index d70b1cf..c1410ce 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -55,6 +55,7 @@
#include "mirror/executable-inl.h"
#include "mirror/object-inl.h"
#include "mirror/object-refvisitor-inl.h"
+#include "mirror/var_handle.h"
#include "oat.h"
#include "oat_file.h"
#include "profile/profile_compilation_info.h"
@@ -1201,6 +1202,9 @@
return true;
}
ScopedDebugDisallowReadBarriers sddrb(Thread::Current());
+ // TODO: Assert that the app image does not contain any Method, Constructor,
+ // FieldVarHandle or StaticFieldVarHandle. These require extra relocation
+ // for the `ArtMethod*` and `ArtField*` pointers they contain.
using ForwardObject = ForwardAddress<RelocationRange, RelocationRange>;
ForwardObject forward_object(boot_image, app_image_objects);
@@ -2530,6 +2534,8 @@
ObjPtr<mirror::Class> class_class;
ObjPtr<mirror::Class> method_class;
ObjPtr<mirror::Class> constructor_class;
+ ObjPtr<mirror::Class> field_var_handle_class;
+ ObjPtr<mirror::Class> static_field_var_handle_class;
{
ObjPtr<mirror::ObjectArray<mirror::Object>> image_roots =
simple_relocate_visitor(first_header.GetImageRoots<kWithoutReadBarrier>().Ptr());
@@ -2549,6 +2555,10 @@
class_class = GetClassRoot<mirror::Class, kWithoutReadBarrier>(class_roots);
method_class = GetClassRoot<mirror::Method, kWithoutReadBarrier>(class_roots);
constructor_class = GetClassRoot<mirror::Constructor, kWithoutReadBarrier>(class_roots);
+ field_var_handle_class =
+ GetClassRoot<mirror::FieldVarHandle, kWithoutReadBarrier>(class_roots);
+ static_field_var_handle_class =
+ GetClassRoot<mirror::StaticFieldVarHandle, kWithoutReadBarrier>(class_roots);
} else {
DCHECK(!patched_objects->Test(class_roots.Ptr()));
class_class = simple_relocate_visitor(
@@ -2557,6 +2567,10 @@
GetClassRoot<mirror::Method, kWithoutReadBarrier>(class_roots).Ptr());
constructor_class = simple_relocate_visitor(
GetClassRoot<mirror::Constructor, kWithoutReadBarrier>(class_roots).Ptr());
+ field_var_handle_class = simple_relocate_visitor(
+ GetClassRoot<mirror::FieldVarHandle, kWithoutReadBarrier>(class_roots).Ptr());
+ static_field_var_handle_class = simple_relocate_visitor(
+ GetClassRoot<mirror::StaticFieldVarHandle, kWithoutReadBarrier>(class_roots).Ptr());
}
}
@@ -2667,6 +2681,13 @@
as_executable->SetArtMethod</*kTransactionActive=*/ false,
/*kCheckTransaction=*/ true,
kVerifyNone>(patched_method);
+ } else if (klass == field_var_handle_class || klass == static_field_var_handle_class) {
+ // Patch the ArtField* in the mirror::FieldVarHandle subobject.
+ ObjPtr<mirror::FieldVarHandle> as_field_var_handle =
+ ObjPtr<mirror::FieldVarHandle>::DownCast(object);
+ ArtField* unpatched_field = as_field_var_handle->GetArtField<kVerifyNone>();
+ ArtField* patched_field = main_relocate_visitor(unpatched_field);
+ as_field_var_handle->SetArtField<kVerifyNone>(patched_field);
}
}
pos += RoundUp(object->SizeOf<kVerifyNone>(), kObjectAlignment);
diff --git a/runtime/mirror/var_handle.cc b/runtime/mirror/var_handle.cc
index c071e23..8f17c58 100644
--- a/runtime/mirror/var_handle.cc
+++ b/runtime/mirror/var_handle.cc
@@ -1646,16 +1646,12 @@
UNREACHABLE();
}
-ArtField* FieldVarHandle::GetField() {
- return reinterpret_cast64<ArtField*>(GetField64(ArtFieldOffset()));
-}
-
bool FieldVarHandle::Access(AccessMode access_mode,
ShadowFrame* shadow_frame,
const InstructionOperands* const operands,
JValue* result) {
ShadowFrameGetter getter(*shadow_frame, operands);
- ArtField* field = GetField();
+ ArtField* field = GetArtField();
ObjPtr<Object> obj;
if (field->IsStatic()) {
DCHECK_LE(operands->GetNumberOfOperands(),
@@ -1998,22 +1994,20 @@
}
void FieldVarHandle::VisitTarget(ReflectiveValueVisitor* v) {
- ArtField* orig = GetField();
+ ArtField* orig = GetArtField();
ArtField* new_value =
v->VisitField(orig, HeapReflectiveSourceInfo(kSourceJavaLangInvokeFieldVarHandle, this));
if (orig != new_value) {
- SetField64</*kTransactionActive*/ false>(ArtFieldOffset(),
- reinterpret_cast<uintptr_t>(new_value));
+ SetArtField(new_value);
}
}
void StaticFieldVarHandle::VisitTarget(ReflectiveValueVisitor* v) {
- ArtField* orig = GetField();
+ ArtField* orig = GetArtField();
ArtField* new_value =
v->VisitField(orig, HeapReflectiveSourceInfo(kSourceJavaLangInvokeFieldVarHandle, this));
if (orig != new_value) {
- SetField64</*kTransactionActive*/ false>(ArtFieldOffset(),
- reinterpret_cast<uintptr_t>(new_value));
+ SetArtField(new_value);
SetFieldObject<false>(DeclaringClassOffset(), new_value->GetDeclaringClass());
}
}
diff --git a/runtime/mirror/var_handle.h b/runtime/mirror/var_handle.h
index 3747b3e..864a53b 100644
--- a/runtime/mirror/var_handle.h
+++ b/runtime/mirror/var_handle.h
@@ -218,7 +218,17 @@
JValue* result)
REQUIRES_SHARED(Locks::mutator_lock_);
- ArtField* GetField() REQUIRES_SHARED(Locks::mutator_lock_);
+ template <VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ ArtField* GetArtField() REQUIRES_SHARED(Locks::mutator_lock_) {
+ return reinterpret_cast64<ArtField*>(GetField64<kVerifyFlags>(ArtFieldOffset()));
+ }
+
+ template <VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ void SetArtField(ArtField* art_field) REQUIRES_SHARED(Locks::mutator_lock_) {
+ SetField64</*kTransactionActive*/ false,
+ /*kCheckTransaction=*/ true,
+ kVerifyFlags>(ArtFieldOffset(), reinterpret_cast64<uint64_t>(art_field));
+ }
// Used for updating var-handles to obsolete fields.
void VisitTarget(ReflectiveValueVisitor* v) REQUIRES(Locks::mutator_lock_);
diff --git a/runtime/mirror/var_handle_test.cc b/runtime/mirror/var_handle_test.cc
index c32c702..9c1a2e7 100644
--- a/runtime/mirror/var_handle_test.cc
+++ b/runtime/mirror/var_handle_test.cc
@@ -285,7 +285,7 @@
StackHandleScope<6> hs(self);
Handle<mirror::FieldVarHandle> fvh(hs.NewHandle(CreateFieldVarHandle(self, value, mask)));
EXPECT_FALSE(fvh.IsNull());
- EXPECT_EQ(value, fvh->GetField());
+ EXPECT_EQ(value, fvh->GetArtField());
// Check access modes
EXPECT_TRUE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kGet));
@@ -488,7 +488,7 @@
StackHandleScope<6> hs(self);
Handle<mirror::FieldVarHandle> fvh(hs.NewHandle(CreateFieldVarHandle(self, value, mask)));
EXPECT_FALSE(fvh.IsNull());
- EXPECT_EQ(value, fvh->GetField());
+ EXPECT_EQ(value, fvh->GetArtField());
// Check access modes
EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kGet));