一、前言
1.1 背景
最近在学习Hook技术,想了解一下在实现JavaHook时,将Java方法转成Native方法时,需要修改ArtMethod对象的成员变量的值。为了弄清楚变量的偏移,笔者在AOSP中写了一个可执行(以下简称"Demo“),用于计算这个偏移量(对于有C、C++语言基础的开发者来说,这个答案可能一眼就能看出来,哈哈)。
在做这个事情的过程中,遇到了一些小问题,故记录一下,形成本文。
1.2 Demo目录结构
笔者在将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编译相关的知识,敬请期待!