标签(空格分隔): javaVM patchoat art android5.1
patchoat进程是由zygote进程第一次启动时,如果在/data/dalvik-cache/x86/下没有jvm的一些缓存文件,则会fork出一个子进程,来进行这些文件的创建,同时其父进程zygote会处于等待状态,直至patchoat进程工作完成,进程patchoat的启动是通过fork和execv系统调用产生的,其启动参数为,我这里为x86的架构。 /system/bin/patchoat –input-image-location=/system/framework/boot.art –output-image-file=/data/dalvik-cache/x86/system@framework@boot.art –input-oat-location=/system/framework/boot.oat –output-oat-file=/data/dalvik-cache/x86/system@framework@boot.oat –instruction-set=x86 –base-offset-delta=-344064
在分析之前,先贴上本文的时序图,提前有一个宏观上的了解。 下面结合源码详细分析每一个过程。
Step1: patchoat.main (/art/patchoat/patchoat.cc)
这个方法实现很简单,就是继续调用namespace art下的patchoat函数
Step2: patchoat.patchoat (/art/patchoat/patchoat.cc)
static int patchoat(
int argc,
char **argv) {
InitLogging(argv);
MemMap::Init();
...
for (
int i =
0; i < argc; i++) {
...
}
...
bool ret;
if (have_image_files && have_oat_files) {
TimingLogger::ScopedTiming pt(
"patch image and oat", &timings);
ret = PatchOat::Patch(input_oat.
get(), input_image_location, base_delta,
output_oat.
get(), output_image.
get(), isa, &timings,
output_oat_fd >=
0,
new_oat_out);
ret = ret && FinishFile(output_image.
get(), ret);
ret = ret && FinishFile(output_oat.
get(), ret);
}
else if (have_oat_files) {
TimingLogger::ScopedTiming pt(
"patch oat", &timings);
ret = PatchOat::Patch(input_oat.
get(), base_delta, output_oat.
get(), &timings,
output_oat_fd >=
0,
new_oat_out);
ret = ret && FinishFile(output_oat.
get(), ret);
}
else if (have_image_files) {
TimingLogger::ScopedTiming pt(
"patch image", &timings);
ret = PatchOat::Patch(input_image_location, base_delta, output_image.
get(), isa, &timings);
ret = ret && FinishFile(output_image.
get(), ret);
}
else {
CHECK(
false);
ret =
true;
}
if (kIsDebugBuild) {
LOG(INFO) <<
"Exiting with return ... " << ret;
}
cleanup(ret);
return (ret) ? EXIT_SUCCESS : EXIT_FAILURE;
}
在这个方法中,首先for循环中解析execv系统调用的argv参数,have_image_files和have_oat_files均为true,在我们这种情况下,由于系统第一次运行/data/dalvik-cache/x86目录下为空,从命令行解析参数执行效果来看,会在/data/dalvik-cache/x86目录下创建system@framework@boot.art和system@framework@boot.oat文件,用来最后将重定向后的缓存文件生成到此处,接下来去调用函数PatchOat::Patch去执行下一步。
Step3: PatchOat::Patch (/art/patchoat/patchoat.cc)
bool PatchOat
::Patch(File
* input_oat, const std
::string& image_location, off_t delta,
File
* output_oat, File
* output_image, InstructionSet isa,
TimingLogger
* timings,
bool output_oat_opened_from_fd,
bool new_oat_out) {
...
const char
* isa_name
= GetInstructionSetString(isa);
std
::string image_filename;
if (
!LocationToFilename(image_location, isa,
&image_filename)) {
LOG(ERROR)
<< "Unable to find image at location " << image_location;
return false;
}
std
::unique_ptr<File
> input_image(OS
::OpenFileForReading(image_filename
.c_str()));
if (input_image
.get()
== nullptr) {
LOG(ERROR)
<< "unable to open input image file at " << image_filename
<< " for location " << image_location;
return false;
}
int64_t image_len
= input_image
->GetLength();
if (image_len
< 0) {
LOG(ERROR)
<< "Error while getting image length";
return false;
}
ImageHeader image_header;
if (sizeof(image_header)
!= input_image
->Read(reinterpret_cast
<char
*>(
&image_header),
sizeof(image_header),
0)) {
LOG(ERROR)
<< "Unable to read image header from image file " << input_image
->GetPath();
}
IsImagePic(image_header, input_image
->GetPath());
RuntimeOptions options;
NoopCompilerCallbacks callbacks;
options
.push_back(std
::make_pair(
"compilercallbacks",
&callbacks));
std
::string img
= "-Ximage:" + image_location;
options
.push_back(std
::make_pair(img
.c_str(), nullptr));
options
.push_back(std
::make_pair(
"imageinstructionset", reinterpret_cast
<const
void*>(isa_name)));
if (
!Runtime
::Create(options,
false)) {
LOG(ERROR)
<< "Unable to initialize runtime";
return false;
}
Thread::Current()
->TransitionFromRunnableToSuspended(kNative);
ScopedObjectAccess soa(
Thread::Current());
t
.NewTiming(
"Image and oat Patching setup");
std
::string error_msg;
std
::unique_ptr<MemMap
> image(MemMap
::MapFile(image_len, PROT_READ
| PROT_WRITE, MAP_PRIVATE,
input_image
->Fd(),
0,
input_image
->GetPath()
.c_str(),
&error_msg));
if (image
.get()
== nullptr) {
LOG(ERROR)
<< "unable to map image file " << input_image
->GetPath()
<< " : " << error_msg;
return false;
}
gc
::space::ImageSpace* ispc
= Runtime
::Current()
->GetHeap()
->GetImageSpace();
std
::unique_ptr<ElfFile
> elf(ElfFile
::Open(input_oat,
PROT_READ
| PROT_WRITE, MAP_PRIVATE,
&error_msg));
if (elf
.get()
== nullptr) {
LOG(ERROR)
<< "unable to open oat file " << input_oat
->GetPath()
<< " : " << error_msg;
return false;
}
bool skip_patching_oat
= false;
MaybePic is_oat_pic
= IsOatPic(elf
.get());
if (is_oat_pic
>= ERROR_FIRST) {
return false;
}
else if (is_oat_pic
== PIC) {
if (
!ReplaceOatFileWithSymlink(input_oat
->GetPath(),
output_oat
->GetPath(),
output_oat_opened_from_fd,
new_oat_out)) {
return false;
}
skip_patching_oat
= true;
}
else {
CHECK(is_oat_pic
== NOT_PIC);
}
PatchOat p(isa, elf
.release(), image
.release(), ispc
->GetLiveBitmap(), ispc
->GetMemMap(),
delta, timings);
t
.NewTiming(
"Patching files");
if (
!skip_patching_oat
&& !p
.PatchElf()) {
LOG(ERROR)
<< "Failed to patch oat file " << input_oat
->GetPath();
return false;
}
if (
!p
.PatchImage()) {
LOG(ERROR)
<< "Failed to patch image file " << input_image
->GetPath();
return false;
}
t
.NewTiming(
"Writing files");
if (
!skip_patching_oat
&& !p
.WriteElf(output_oat)) {
LOG(ERROR)
<< "Failed to write oat file " << input_oat
->GetPath();
return false;
}
if (
!p
.WriteImage(output_image)) {
LOG(ERROR)
<< "Failed to write image file " << input_image
->GetPath();
return false;
}
return true;
}
为了更清楚的分析,函数的入参分别为 input_oat -> /system/framework/x86/boot.oat image_location -> /system/framework/boot.art delta -> -344064 output_oat -> /data/dalvik-cache/x86/system@framework@boot.oat output_image -> /data/dalvik-cache/x86/system@framework@boot.art isa -> 对应x86 timings -> 不关注此参数,为一个时间的log output_oat_opened_from_fd -> false new_oat_out -> true boot.art与boot.oat与其说是ART虚拟机的两种执行格式,不如说他俩就是ART虚拟机的一部分,ART离开了这两个文件,也就无法启动了。boot.art是一个img文件,而boot.oat文件可以将其理解为ART虚拟机的启动类。函数接下来创建input_image一个文件指针,它指向的文件为/system/framework/x86/boot.art的镜像文件,而后读取这个文件的头信息到ImageHeader类型的image_header中,它是用来读和验证art文件的一个对象,这里只是做了一些验证的信息,接下来设置RuntimeOptions参数,创建一个Runtime对象。
Step4: Runtime::Create (/art/runtime/runtime.cc)
正如前文分析过,这个方法实现很简单,就是看单例对象instance_是否存在,不存在,则创建一个,并调用Init函数。
Step5: Runtime::Init (/art/runtime/runtime.cc)
首先分析函数的入参RuntimeOptions options,它里面的内容为”[compilercallbacks, ],[-Ximage:/system/framework/boot.art],[imageinstructionset, ]”,在函数中,调用ParsedOptions类进行参数解析,所有关于jvm的设置均在此类中进行解析和设置,再通过这些参数创建一个Heap对象,我们先分析在Heap创建时候做了哪些事情。在Init函数中,因为此时我们并不是Zygote进程,而是由zygote进程fork出来的,这个进程的作用就是对对boot.oat和boot.art进行重定位,重定位之后新的boot.art和boot.art会存放在/data/dalvik-cache中,因此只需要关注ImageSpace这块的代码,这个进程在之后就结束了,,在这个函数中,我们只看Heap创建的代码,下面继续分析
Step6:Heap::Heap (/art/runtime/gc/heap.cc)
Heap::Heap(
...) {
...
if (!image_file_name.empty()) {
std::string error_msg;
space::ImageSpace* image_space = space::ImageSpace::Create(image_file_name.c_str(),
image_instruction_set,
&error_msg);
if (image_space != nullptr) {
AddSpace(image_space);
// Oat files referenced by image files immediately follow them
in memory, ensure alloc space
// isn
't going to get in the middle
byte* oat_file_end_addr = image_space->GetImageHeader().GetOatFileEnd();
CHECK_GT(oat_file_end_addr, image_space->End());
requested_alloc_space_begin = AlignUp(oat_file_end_addr, kPageSize);
} else {
LOG(WARNING) << "Could not create image space with image file '" << image_file_name << "'. "
<< "Attempting to fall back to imageless running. Error was: " << error_msg;
}
}
...
}
构造函数Heap对许多成员变量做了一些初始化,我们这里并不关注,在zygote进程创建堆时需要重点关注这些,因为它才会真正的创建虚拟机和启动java世界。这个函数中,可以看到继续调用了ImageSpace::Create创建了一个ImageSpace对象,此时参数image_file_name为/system/framework/boot.art,image_instruction_set为x86架构。
Step7: ImageSpace::Create (/art/runtime/gc/space/image_space.cc)
ImageSpace
* ImageSpace
::Create(const char
* image_location,
const InstructionSet image_isa,
std
::string* error_msg) {
std
::string system_filename;
bool has_system
= false;
std
::string cache_filename;
bool has_cache
= false;
bool dalvik_cache_exists
= false;
bool is_global_cache
= true;
const bool found_image
= FindImageFilename(image_location, image_isa,
&system_filename,
&has_system,
&cache_filename,
&dalvik_cache_exists,
&has_cache,
&is_global_cache);
if (Runtime
::Current()
->IsZygote()) {
MarkZygoteStart(image_isa);
}
ImageSpace
* space;
bool relocate
= Runtime
::Current()
->ShouldRelocate();
bool can_compile
= Runtime
::Current()
->IsImageDex2OatEnabled();
if (found_image) {
const std
::string* image_filename;
bool is_system
= false;
bool relocated_version_used
= false;
if (relocate) {
if (
!dalvik_cache_exists) {
*error_msg = StringPrintf(
"Requiring relocation for image '%s' at '%s' but we do not have "
"any dalvik_cache to find/place it in.",
image_location, system_filename
.c_str());
return nullptr;
}
if (has_system) {
if (has_cache
&& ChecksumsMatch(system_filename
.c_str(), cache_filename
.c_str())) {
image_filename
= &cache_filename;
relocated_version_used
= true;
}
else {
std
::string reason;
bool success;
if (
!can_compile) {
reason
= "Image dex2oat disabled by -Xnoimage-dex2oat.";
success
= false;
}
else if (
!ImageCreationAllowed(is_global_cache,
&reason)) {
success
= false;
}
else {
success
= RelocateImage(image_location, cache_filename
.c_str(), image_isa,
&reason);
}
if (success) {
relocated_version_used
= true;
image_filename
= &cache_filename;
}
else {
*error_msg = StringPrintf(
"Unable to relocate image '%s' from '%s' to '%s': %s",
image_location, system_filename
.c_str(),
cache_filename
.c_str(), reason
.c_str());
PruneDalvikCache(image_isa);
return nullptr;
}
}
}
else {
CHECK(has_cache);
image_filename
= &cache_filename;
}
}
else {
if (has_system
&& has_cache) {
if (ChecksumsMatch(system_filename
.c_str(), cache_filename
.c_str())) {
image_filename
= &cache_filename;
relocated_version_used
= true;
}
else {
image_filename
= &system_filename;
is_system
= true;
}
}
else if (has_system) {
image_filename
= &system_filename;
is_system
= true;
}
else {
CHECK(has_cache);
image_filename
= &cache_filename;
}
}
{
ScopedFlock image_lock;
image_lock
.Init(image_filename
->c_str(),
error_msg);
VLOG(startup)
<< "Using image file " << image_filename
->c_str()
<< " for image location "
<< image_location;
space
= ImageSpace
::Init(image_filename
->c_str(), image_location,
!(is_system
|| relocated_version_used),
error_msg);
}
if (space
!= nullptr) {
return space;
}
if (relocated_version_used) {
*error_msg = StringPrintf(
"Attempted to use relocated version of %s at %s generated from %s "
"but image failed to load: %s",
image_location, cache_filename
.c_str(), system_filename
.c_str(),
error_msg->c_str());
PruneDalvikCache(image_isa);
return nullptr;
}
else if (is_system) {
*error_msg = StringPrintf(
"Failed to load /system image '%s': %s",
image_filename
->c_str(),
error_msg->c_str());
return nullptr;
}
else {
LOG(WARNING)
<< *error_msg;
}
}
if (
!can_compile) {
*error_msg = "Not attempting to compile image because -Xnoimage-dex2oat";
return nullptr;
}
else if (
!dalvik_cache_exists) {
*error_msg = StringPrintf(
"No place to put generated image.");
return nullptr;
}
else if (
!ImageCreationAllowed(is_global_cache,
error_msg)) {
return nullptr;
}
else if (
!GenerateImage(cache_filename, image_isa,
error_msg)) {
*error_msg = StringPrintf(
"Failed to generate image '%s': %s",
cache_filename
.c_str(),
error_msg->c_str());
PruneDalvikCache(image_isa);
return nullptr;
}
else {
if (
!CheckSpace(cache_filename,
error_msg)) {
PruneDalvikCache(image_isa);
return nullptr;
}
ScopedFlock image_lock;
image_lock
.Init(cache_filename
.c_str(),
error_msg);
space
= ImageSpace
::Init(cache_filename
.c_str(), image_location,
true,
error_msg);
if (space
== nullptr) {
*error_msg = StringPrintf(
"Failed to load generated image '%s': %s",
cache_filename
.c_str(),
error_msg->c_str());
}
return space;
}
}
这个函数的实现和很简单,先是调用FindImageFilename根据架构和参数image_location去寻找对应架构下的image文件,然后根据结果去设置一些标志位的值,具体可以仔细阅读代码,此时found_image为ture,relocate为false,can_compile为true,has_system为ture, has_cache为true,因为在patchoat函数中已经在/data/dalvik-cache/x86下创建system@framework@boot.art和system@framework@boot.oat。函数接着会进入if分支,由这些标志位信息,由于此时system@framework@boot.art和system@framework@boot.oat只是一个空的文件,因此ChecksumsMatch会失败,继续分析最终函数执行到ImageSpace::Init去初始化这块ImageSpace,该函数三个入参分别为/system/framework/x86/boot.art,/system/framework/boot.art,false。
Step8: ImageSpace::Init (/art/runtime/gc/space/image_space.cc)
ImageSpace
* ImageSpace
::Init(const char
* image_filename, const char
* image_location,
bool validate_oat_file, std
::string* error_msg) {
...
std
::unique_ptr<File
> file(OS
::OpenFileForReading(image_filename));
if (file
.get()
== NULL) {
*error_msg = StringPrintf(
"Failed to open '%s'", image_filename);
return nullptr;
}
ImageHeader image_header;
bool success
= file
->ReadFully(
&image_header, sizeof(image_header));
if (
!success
|| !image_header
.IsValid()) {
*error_msg = StringPrintf(
"Invalid image header in '%s'", image_filename);
return nullptr;
}
std
::unique_ptr<MemMap
> map(MemMap
::MapFileAtAddress(image_header
.GetImageBegin(),
image_header
.GetImageSize(),
PROT_READ
| PROT_WRITE,
MAP_PRIVATE,
file
->Fd(),
0,
false,
image_filename,
error_msg));
if (
map.get()
== NULL) {
return nullptr;
}
std
::unique_ptr<MemMap
> image_map(
MemMap
::MapFileAtAddress(nullptr, image_header
.GetImageBitmapSize(),
PROT_READ, MAP_PRIVATE,
file
->Fd(), image_header
.GetBitmapOffset(),
false,
image_filename,
error_msg));
if (image_map
.get()
== nullptr) {
*error_msg = StringPrintf(
"Failed to map image bitmap: %s",
error_msg->c_str());
return nullptr;
}
uint32_t bitmap_index
= bitmap_index_
.FetchAndAddSequentiallyConsistent(
1);
std
::string bitmap_name(StringPrintf(
"imagespace %s live-bitmap %u", image_filename,
bitmap_index));
std
::unique_ptr<accounting
::ContinuousSpaceBitmap> bitmap(
accounting
::ContinuousSpaceBitmap::CreateFromMemMap(bitmap_name, image_map
.release(),
reinterpret_cast
<byte
*>(
map->Begin()),
map->Size()));
if (bitmap
.get()
== nullptr) {
*error_msg = StringPrintf(
"Could not create bitmap '%s'", bitmap_name
.c_str());
return nullptr;
}
std
::unique_ptr<ImageSpace
> space(
new ImageSpace(image_filename, image_location,
map.release(), bitmap
.release()));
space
->oat_file_
.reset(space
->OpenOatFile(image_filename,
error_msg));
if (space
->oat_file_
.get()
== nullptr) {
DCHECK(
!error_msg->empty());
return nullptr;
}
if (validate_oat_file
&& !space
->ValidateOatFile(
error_msg)) {
DCHECK(
!error_msg->empty());
return nullptr;
}
Runtime
* runtime
= Runtime
::Current();
runtime
->SetInstructionSet(space
->oat_file_
->GetOatHeader()
.GetInstructionSet());
mirror
::Object* resolution_method
= image_header
.GetImageRoot(ImageHeader
::kResolutionMethod);
runtime
->SetResolutionMethod(down_cast
<mirror
::ArtMethod*>(resolution_method));
mirror
::Object* imt_conflict_method
= image_header
.GetImageRoot(ImageHeader
::kImtConflictMethod);
runtime
->SetImtConflictMethod(down_cast
<mirror
::ArtMethod*>(imt_conflict_method));
mirror
::Object* imt_unimplemented_method
=
image_header
.GetImageRoot(ImageHeader
::kImtUnimplementedMethod);
runtime
->SetImtUnimplementedMethod(down_cast
<mirror
::ArtMethod*>(imt_unimplemented_method));
mirror
::Object* default_imt
= image_header
.GetImageRoot(ImageHeader
::kDefaultImt);
runtime
->SetDefaultImt(down_cast
<mirror
::ObjectArray<mirror
::ArtMethod>*>(default_imt));
mirror
::Object* callee_save_method
= image_header
.GetImageRoot(ImageHeader
::kCalleeSaveMethod);
runtime
->SetCalleeSaveMethod(down_cast
<mirror
::ArtMethod*>(callee_save_method),
Runtime
::kSaveAll);
callee_save_method
= image_header
.GetImageRoot(ImageHeader
::kRefsOnlySaveMethod);
runtime
->SetCalleeSaveMethod(down_cast
<mirror
::ArtMethod*>(callee_save_method),
Runtime
::kRefsOnly);
callee_save_method
= image_header
.GetImageRoot(ImageHeader
::kRefsAndArgsSaveMethod);
runtime
->SetCalleeSaveMethod(down_cast
<mirror
::ArtMethod*>(callee_save_method),
Runtime
::kRefsAndArgs);
if (VLOG_IS_ON(heap)
|| VLOG_IS_ON(startup)) {
LOG(INFO)
<< "ImageSpace::Init exiting (" << PrettyDuration(NanoTime()
- start_time)
<< ") " << *space
.get();
}
return space
.release();
}
函数先是调用OpenFileForReading打开这个文件,即是/system/framework/x86/boot.art,file是指向这个文件的文件指针,接下来调用ReadFully将这个文件的头信息读入image_header中,具体文件头结构涉及到art虚拟机文件格式,这里不需要太关注,以免失去了文章的主线,获取到这个文件头信息之后,检查这个文件是否是有效的art文件,校验通过之后,接着调用MemMap::MapFileAtAddress去映射到内存,MapFileAtAddress函数中,做了一些页对齐的设置,而后就是mmap的东西。在这里,一共映射了两块内存区域。 map: 映射整个boot.art文件到内存,起始地址是固定的 image_map: 映射了boot.art文件中的Bitmap的内容到内存,起始地址由系统决定 接下来通过这两个映射区的指针,创建ImageSpace对象,接下来设置其成员变量oat_file_的值,它是一个std::unique_ptr类型的智能指针,通过成员函数OpenOatFile创建一个OatFile对象。继续分析Init的最后一部分,接着获取当前的Runtime实例,设置一些参数,比如架构信息等,最后函数返回一个ImageSpace指针。
Setp9,Step10,Step11
函数层层返回,最后回带Patch::Patch函数中继续执行,input_image为指向/system/framework/x86/boot.art的文件指针,然后将其映射到内存,返回一个std::unique_ptr类型的智能指针image,接着再通过input_oat(/system/framework/x86/boot.oat)文件创建一个std::unique_ptr类型的智能指针elf,然后再根据这些信息,新建一个PatchOat对象,下面来看PatchOat的构造函数
PatchOat(
InstructionSet isa,
ElfFile*
oat_file,
MemMap*
image,
gc::accounting::ContinuousSpaceBitmap*
bitmap,
MemMap*
heap,
off_t delta,
TimingLogger*
timings)
:
oat_file_(
oat_file),
image_(
image),
bitmap_(
bitmap),
heap_(
heap),
delta_(
delta),
isa_(
isa),
timings_(
timings)
{}
构造函数实现很简单,就是对其成员变量赋值,ElfFile类型的指针oat_file_指向/system/framework/x86/boot.oat,MemMap类型的image_指针指向/system/framework/x86/boot.art,bitmap_和heap_存放的是ImageSpace中的地址,delta就是上文那个随机地址变量,为什么需要这个呢,Android源码中指定了一个base地址作为其加载到内存的默认地址,如果不重定位的话,会导致使用这个ROM的Android 设备image空间起始地址都一样,这容易被攻击。所以就需要重定位。一般情况下,/data/dalvik-cache中的boot.art和boot.oat都是经过重定位的。/system/frmework中的是没有经过重定位的。重定位其实很简单,就是在一定范围内产生一个随机数,然后实际加载地址是base+这个随机数。接下来会调用4个方法PatchElf、PatchImage、WriteElf、WriteImage。下面一个个来分析。
Step12: PatchOat::PatchElf (/art/patchoat/patchoat.cc)
bool PatchOat
::PatchElf() {
TimingLogger
::ScopedTiming t(
"Fixup Elf Text Section", timings_);
if (
!PatchTextSection()) {
return false;
}
if (
!PatchOatHeader()) {
return false;
}
bool need_fixup
= false;
t
.NewTiming(
"Fixup Elf Headers");
for (unsigned int i
= 0; i
< oat_file_
->GetProgramHeaderNum(); i
++) {
Elf32_Phdr
* hdr
= oat_file_
->GetProgramHeader(i);
CHECK(hdr
!= nullptr);
if (hdr
->p_vaddr
!= 0 && hdr
->p_vaddr
!= hdr
->p_offset) {
need_fixup
= true;
hdr
->p_vaddr
+= delta_;
}
if (hdr
->p_paddr
!= 0 && hdr
->p_paddr
!= hdr
->p_offset) {
need_fixup
= true;
hdr
->p_paddr
+= delta_;
}
}
if (
!need_fixup) {
return true;
}
t
.NewTiming(
"Fixup Section Headers");
for (unsigned int i
= 0; i
< oat_file_
->GetSectionHeaderNum(); i
++) {
Elf32_Shdr
* hdr
= oat_file_
->GetSectionHeader(i);
CHECK(hdr
!= nullptr);
if (hdr
->sh_addr
!= 0) {
hdr
->sh_addr
+= delta_;
}
}
t
.NewTiming(
"Fixup Dynamics");
for (Elf32_Word i
= 0; i
< oat_file_
->GetDynamicNum(); i
++) {
Elf32_Dyn
& dyn
= oat_file_
->GetDynamic(i);
if (IsDynamicSectionPointer(dyn
.d_tag, oat_file_
->GetHeader()
.e_machine)) {
dyn
.d_un
.d_ptr
+= delta_;
}
}
t
.NewTiming(
"Fixup Elf Symbols");
Elf32_Shdr
* dynsym_sec
= oat_file_
->FindSectionByName(
".dynsym");
CHECK(dynsym_sec
!= nullptr);
if (
!PatchSymbols(dynsym_sec)) {
return false;
}
Elf32_Shdr
* symtab_sec
= oat_file_
->FindSectionByName(
".symtab");
if (symtab_sec
!= nullptr) {
if (
!PatchSymbols(symtab_sec)) {
return false;
}
}
return true;
}
这个函数主要就是根据生成的随机地址delta_,调整oat_file_映射的内存区域的值,oat_file_对应于/system/framework/x86/boot.oat,包括代码段,ota的头,符号表等,都是通过在原有基础上,给一个delta_的偏移量来实现的。具体涉及的知识点比较复杂,暂且先不关注。
Step13: PatchOat::PatchImage (/art/patchoat/patchoat.cc)
bool PatchOat
::PatchImage() {
ImageHeader
* image_header
= reinterpret_cast
<ImageHeader
*>(image_
->Begin());
CHECK_GT(image_
->Size(), sizeof(ImageHeader));
mirror
::Object* img_roots
= image_header
->GetImageRoots();
image_header
->RelocateImage(delta_);
VisitObject(img_roots);
if (
!image_header
->IsValid()) {
LOG(ERROR)
<< "reloction renders image header invalid";
return false;
}
{
TimingLogger
::ScopedTiming t(
"Walk Bitmap", timings_);
WriterMutexLock mu(
Thread::Current(),
*Locks
::heap_bitmap_lock_);
bitmap_
->Walk(PatchOat
::BitmapCallback, this);
}
return true;
}
函数首先是将image映射的内存区对应于ImageHeader那块内存地址,赋值给image_header,而后调用函数RelocateImage传入这个随机地址变量,对ImageHeader内存块进行一个偏移量设置,这里image_对应的即是/system/framework/x86/boot.art。
Step14: PatchOat::WriteElf (/art/patchoat/patchoat.cc)
分析函数的入参,output_oat为/data/dalvik-cache/x86/system@framework@boot.oat的文件指针,这个函数就是将/system/framework/x86/boot.oat内容拷贝至/data/dalvik-cache/x86/system@framework@boot.oat。
Step15: PatchOat::WriteImage (/art/patchoat/patchoat.cc)
分析函数的入参,output_image为/data/dalvik-cache/x86/system@framework@boot.art的文件指针,这个函数就是将/system/framework/x86/boot.art内容拷贝至/data/dalvik-cache/x86/system@framework@boot.art。
Step16: patchoat (/art/patchoat/patchoat.cc)
最终函数返回至patchoat,同时flush缓冲区,将文件写入。至此,/data/dalvik-cache/x86下的缓存文件就已经全部创建成功,同时也是经过重定位了的。这个进程也就完成了它的全部工作,接下来回到主函数main,结束进程。我们整个这个patchoat的工作就已经分析完成,接下来其父进程zygote的waitpid会被唤醒,继续执行,开启java世界,我们后文将继续分析。