Android - ART压缩指针

引言

在分析ART虚拟机的中对象模型时,发现Mirror Object类中引用的其他object指针/对象头的指向Class的指针,都使用了HeapReference来包装,都是HeapReference类型,而HeapReference类只是对uint32_t类型的值进行包装,在64位系统是8字节的指针,4字节怎么装得下去的,然后发现他对指针进行了压缩:

emplate<class MirrorType>
class MANAGED HeapReference {
 private:
  using Compression = PtrCompression<kPoisonHeapReferences, MirrorType>;
 public:
  HeapReference() REQUIRES_SHARED(Locks::mutator_lock_) : HeapReference(nullptr) {}
  template <bool kIsVolatile = false>
  MirrorType* AsMirrorPtr() const REQUIRES_SHARED(Locks::mutator_lock_) {
    return Compression::Decompress(
        kIsVolatile ? reference_.load(std::memory_order_seq_cst) : reference_.LoadJavaData());
  }
  template <bool kIsVolatile = false>
  void Assign(MirrorType* other) REQUIRES_SHARED(Locks::mutator_lock_) {
    if (kIsVolatile) {
      reference_.store(Compression::Compress(other), std::memory_order_seq_cst);
    } else {
      reference_.StoreJavaData(Compression::Compress(other));
    }
  }
  template <bool kIsVolatile = false>
  void Assign(ObjPtr<MirrorType> ptr) REQUIRES_SHARED(Locks::mutator_lock_);
  void Clear() {
    reference_.StoreJavaData(0);
    DCHECK(IsNull());
  }
  bool IsNull() const {
    return reference_.LoadJavaData() == 0;
  }
  static HeapReference<MirrorType> FromMirrorPtr(MirrorType* mirror_ptr)
      REQUIRES_SHARED(Locks::mutator_lock_) {
    return HeapReference<MirrorType>(mirror_ptr);
  }
  bool CasWeakRelaxed(MirrorType* old_ptr, MirrorType* new_ptr)
      REQUIRES_SHARED(Locks::mutator_lock_);
 private:
  explicit HeapReference(MirrorType* mirror_ptr) REQUIRES_SHARED(Locks::mutator_lock_)
      : reference_(Compression::Compress(mirror_ptr)) {}
  // The encoded reference to a mirror::Object. Atomically updateable.
  Atomic<uint32_t> reference_;
};

这个类型保存的reference_ 的是通过PtrCompression 进行的压缩

template<bool kPoisonReferences, class MirrorType>
class PtrCompression {
 public:
  // Compress reference to its bit representation.
  static uint32_t Compress(MirrorType* mirror_ptr) {
    uint32_t as_bits = reinterpret_cast32<uint32_t>(mirror_ptr);
    return kPoisonReferences ? -as_bits : as_bits;
  }
  // Uncompress an encoded reference from its bit representation.
  static MirrorType* Decompress(uint32_t ref) {
    uint32_t as_bits = kPoisonReferences ? -ref : ref;
    return reinterpret_cast32<MirrorType*>(as_bits);
  }
  // Convert an ObjPtr to a compressed reference.
  static uint32_t Compress(ObjPtr<MirrorType> ptr) REQUIRES_SHARED(Locks::mutator_lock_);
};

这个32位无符号数,可以通过Decompress()函数强制转换为Java对象的指针,代码如下:

 template <bool kIsVolatile = false>
  MirrorType* AsMirrorPtr() const REQUIRES_SHARED(Locks::mutator_lock_) {
    return Compression::Decompress(
        kIsVolatile ? reference_.load(std::memory_order_seq_cst) : reference_.LoadJavaData());
  }

ArtMethod的成员变量declaring_class_是一个uint32_t,这块为什么要用uint32_t的代替,而且后面用reinterpret_cast把这个uint32_转为了一个指针,但是这里面指针的大小不应该是8字节64位的吗 它又是怎么转的?

不会, 它只要保证ObjectReference引用的东西在4gb空间内就行, 其它的不受影响.
参考http://androidxref.com/7.1.1_r6/xref/art/runtime/runtime.h#728,

Special low 4gb pool for compiler linear alloc. We need ArtFields to be in low 4gb if we are compiling using a 32 bit image on a 64 bit compiler in case we resolve things in the image since the field arrays are int arrays in this case.

参考文章

  • https://juejin.cn/post/7270871863160733752

  • https://www.pqpo.me/2017/07/07/hotfix-method-hook/#comment-265