AOSP测试用例之可见性问题

86 阅读3分钟

一、前言

1.1 背景

最近在学习Hook技术,想了解一下在实现JavaHook时,将Java方法转成Native方法时,需要修改ArtMethod对象的成员变量的值。为了弄清楚变量的偏移,笔者在AOSP中写了一个可执行(以下简称"Demo“),用于计算这个偏移量(对于有C、C++语言基础的开发者来说,这个答案可能一眼就能看出来,哈哈)。

在做这个事情的过程中,遇到了一些小问题,故记录一下,形成本文。

1.2 Demo目录结构

aosp_demo_list.png

笔者在将demo放置在了 AOSP 目录下的 development中,而要使用的ArtMethod类却在art目录下。

也正是因为这种目录的隔离,导致了编译时的可见性问题,后面会说一下这个问题的如何解决的。

二、简述Demo内容的实现

demo的内容很简单,就两个文件:一个Android.bp , 另一个源文件。

//art_method_test.cpp

#include <iostream>
#include "../../art/runtime/art_method.h"  // 使用源码树中的原始路径
#include <cstddef>
int main() {

    std::cout << "ArtMethod内存布局验证:" << std::endl;
    std::cout << "sizeof(ArtMethod): " << sizeof(art::ArtMethod) << std::endl;
    std::cout << "declaring_class_ offset: " 
              << offsetof(art::ArtMethod, declaring_class_) << std::endl;
    std::cout << "access_flags_ offset: " << offsetof(art::ArtMethod, access_flags_) << std::endl;
    std::cout << "ptr_sized_fields_ offset: " << offsetof(art::ArtMethod, ptr_sized_fields_) << std::endl;
    std::cout << "PtrSizedFields sizeof: " << sizeof(art::ArtMethod::PtrSizedFields) << std::endl;
    return 0;
}

上述内容很简单,offsetof是cstddef头文件中定义的一个宏,用来计算字段偏移的。 接下来,看一下Android.bp中的内容:

cc_test {
    name: "art_method_layout_test",
    srcs: ["art_method_test.cpp"],

    static_libs: [
        "libartbase",
        "libart",
        "libdexfile",
    ],

    header_libs:[
      "libbase_headers",
    ],

    cflags: [
        "-fno-access-control",
		"-Wall",
        "-Werror",
      //  "-Iart/runtime/", 
    ],
    required: ["art"],
    compile_multilib: "both",  // 同时编译32/64位
}

如上面所示,Android.bp文件指明了 源文件、以及其依赖库、需要引入的头文件以及编译时需要的cpp flags。

然后,进行编译:

$ mm development/shark_art_method_test/

输出目录: out/target/product/rk3568_s/data/nativetest64/art_method_layout_test/art_method_layout_test , 然后上传到手机上,进行运行。

三、遇到的问题

  • 跨模块导致的可见性问题
  • ArtMethod成员不可见问题

3.1 跨模块导致的可见性问题

由于Demo 依赖着"libartbase"、"libart"、"libdexfile" , 并且这些库都是art目录内可见,因此需要将这些库的Android.bp的可见性,增加”//development/shark_art_method_test/“,或者改为”全项目可见“ :

// <AOSP>/art/libartbase/Android.bp
art_cc_library {
    name: "libartbase",
    ...
    visibility: [
	    ...
        "//development/shark_art_method_test",
    ],
    ...
}

// <AOSP>/art/runtime/Android.bp
art_cc_library {
    name: "libart",
	...
    visibility: ["//visibility:public"],
    ...
}

// <AOSP>/art/libdexfile/Android.bp
art_cc_library {
    name: "libdexfile",
    visibility: [
	    ...
        "//development/shark_art_method_test",
		...
    ],
    ...
}

3.2 ArtMethod成员不可见问题

class ArtMethod final {

 ...
 
 protected:

  GcRoot<mirror::Class> declaring_class_;

  std::atomic<std::uint32_t> access_flags_;
  
  uint32_t dex_method_index_;
  
  uint16_t method_index_;

  union {
    uint16_t hotness_count_;
    uint16_t imt_index_;
  };
  
  struct PtrSizedFields {
    void* data_;

    void* entry_point_from_quick_compiled_code_;

  } ptr_sized_fields_;
  ...
};

ArtMethod类中,我们期望访问的成员 declaring_class_、access_flags_ 等,都是protected , 因为对于 外部的类,是无法访问到,因此可以在编译时,增加 编译选项 "-fno-access-control", 来突破这个限制。

cc_test {
    name: "art_method_layout_test",
	...
    cflags: [
        "-fno-access-control",
		...
    ],
    ...
}

四、总结

文中的程序,很简单。就是遇到的这两种可见性,需要额外处理一下:

  • AOSP模块间的可见性, 可以通过Android.bp的visibility进行配置;
  • C++语法的可见性,需要指明 ”-fno-access-control“ 来突破限制;

这里可能还有一种简洁的方式: 修改art目录下的Android.bp ,增加可见性。笔者没有进行尝试,但是笔者查看了一下art子模块的都默认继承了art顶层的Android.bp协议了。

五、写在最后

一个普通的老Androider, 再卷一把,加油2025, 干中学! 欢迎一起探讨大前端技术(Android、Framework、HarmonyOS、Flutter 甚至是前端)!

未完待续,笔者会分享AOSP编译相关的知识,敬请期待!