writing

Exploring Facebook's iOS Client - The RODATA Section探索 Facebook iOS 客户端 - Section RODATA

· Performance & internals

Translated from Chinese — .

On this page

Observation

When viewing Facebook’s iOS binary with MachOView, I noticed several Sections that differ from most other Apps.

The image below is Facebook:

Facebook

The image below is WeChat:

Initial Analysis

    __TEXT
    The __TEXT segment contains our code to be run. It’s mapped as read-only and executable. The process is allowed to execute the code, but not to modify it. The code can not alter itself, and these mapped pages can therefore never become dirty.
    __DATA
    The __DATA segment is mapped read/write but non-executable. It contains values that need to be updated.
    __RODATA   :  read only data , still non-executable.

TEXT is the code section, DATA is the data section, and RODATA is the read-only data section. Facebook moved part of the content from the code section to the read-only section, which tells us this content doesn’t need to be executed; from the image below we can also see it’s all constants.

Why

Look at the sizes of Facebook’s various segments, as shown below:

Moving content from the TEXT segment to the RODATA segment reduces the size of the TEXT segment.

Check Apple’s review requirements:

https://developer.apple.com/library/ios/documentation/LanguagesUtilities/Conceptual/iTunesConnect_Guide/Chapters/SubmittingTheApp.html

We can see that to support iOS 7.x or 8.x, the maximum size of the TEXT segment is 60MB (per architecture’s TEXT segment). Facebook’s TEXT segment is about to hit 60MB.

How To Do It

If our App’s TEXT size is about to hit 60MB, how do we handle it like Facebook?

man ld:

Add the link options:

-Wl,-rename_section,__TEXT,__cstring,__RODATA,__cstring
-Wl,-rename_section,__TEXT,__gcc_except_tab,__RODATA,__gcc_except_tab
-Wl,-rename_section,__TEXT,__const,__RODATA,__const
-Wl,-rename_section,__TEXT,__objc_methname,__RODATA,__objc_methname
-Wl,-rename_section,__TEXT,__objc_classname,__RODATA,__objc_classname
-Wl,-rename_section,__TEXT,__objc_methtype,__RODATA,__objc_methtype

As shown:

Does It Affect App Execution?

After testing in my own App, there’s no effect at all.

Is It Really OK?

There’s a tiny worry: does the Objective-C Runtime have any relationship with these constants? Why does modifying the segment name of the constants have no effect on the program’s execution?

Code: https://opensource.apple.com/tarballs/objc4/objc4-680.tar.gz

Searching the code for methname and methtype turns up no related info, so I roughly guess the runtime doesn’t do special processing to fetch these constants.

Searching https://github.com/llvm-mirror/llvm for objc_methname or objc_methtype, objc_methtype is only found in some tests. And objc_methname, besides being used in tests, is also used in the ARC optimizer.

Would modifying the segment affect this optimizer?

Further looking for the source of GlobalVariable::getSection(). getSection comes from GlobalVariable’s parent class GlobalObject.

From the current info, the possible content of section is __TEXT,__objc_methname or __RODATA,__objc_methname, and the code above is Section.find, which searches within a string, so it can automatically adapt to segment modifications.

Other segments probably use a similar approach, so we can be sure there’s no effect.

How Much Can the TEXT Segment Be Slimmed Down

Taking Facebook armv7 as an example, the RODATA segment is about 14MB.

Earlier Thoughts

At first I thought moving data that doesn’t need to be loaded immediately from the TEXT segment to RODATA was to speed up App launch, but thinking about the advantage of memory-mapped mmap — being able to quickly handle large files — loading it all together wouldn’t have any performance impact.

Reference Project

Code: https://github.com/everettjf/Yolo/tree/master/FBInjectableTest

Additional Notes

A segment’s permissions can be changed to VM_PROT_READ via the following link parameter.

-Wl,-segprot,__RODATA,r,r

Other Resources

文章目录

现象

使用 MachOView 查看 Facebook 的 iOS 二进制文件时,发现有几个 Section 与其他大多数 App 不同。

下图是 Facebook :

Facebook

下图是微信:

初步分析

    __TEXT
    The __TEXT segment contains our code to be run. It’s mapped as read-only and executable. The process is allowed to execute the code, but not to modify it. The code can not alter itself, and these mapped pages can therefore never become dirty.
    __DATA
    The __DATA segment is mapped read/write but non-executable. It contains values that need to be updated.
    __RODATA   :  read only data , still non-executable.

TEXT 是代码段,DATA是数据段,RODATA是只读数据段。Facebook把代码段中的部分内容移动到只读段,可知这些内容是不需要执行的,从下图也可看出都是常量。

为什么

看 Facebook 的各个段大小,如下图:

把 TEXT 段的内容移到 RODATA 段,也就是减少了 TEXT 段的大小。

查看Apple的审核要求:

https://developer.apple.com/library/ios/documentation/LanguagesUtilities/Conceptual/iTunesConnect_Guide/Chapters/SubmittingTheApp.html

可知,如果要支持 iOS7.x或8.x,TEXT段的大小最大为 60MB(每个架构的TEXT段)。 Facebook 的 TEXT段马上就到 60MB。

如何做

如果我们的App的TEXT大小马上到60MB,如何处理成Facebook这样呢?

man ld:

加入链接选项:

-Wl,-rename_section,__TEXT,__cstring,__RODATA,__cstring
-Wl,-rename_section,__TEXT,__gcc_except_tab,__RODATA,__gcc_except_tab
-Wl,-rename_section,__TEXT,__const,__RODATA,__const
-Wl,-rename_section,__TEXT,__objc_methname,__RODATA,__objc_methname
-Wl,-rename_section,__TEXT,__objc_classname,__RODATA,__objc_classname
-Wl,-rename_section,__TEXT,__objc_methtype,__RODATA,__objc_methtype

如图:

影响App运行吗?

经过在自己的App中测试,没有任何影响。

真正可以吗?

还有个小小担心,Objective C Runtime与这些常量有什么关系吗?修改了常量的段(segment)名字为什么对程序运行没有影响。

代码:https://opensource.apple.com/tarballs/objc4/objc4-680.tar.gz

代码中搜索 methname methtype 都没有相关信息,大概推测 runtime 没有对这些常量的获取做针对性的处理。

在 https://github.com/llvm-mirror/llvm 搜索 objc_methname 或 objc_methtype , objc_methtype只在一些test中搜索到。而 objc_methname 除了test中使用,还在 ARC 的优化器中使用。

修改segment会影响这个优化器吗?

进一步查找 GlobalVariable::getSection() 的来源。 getSection 来自 GlobalVariable的父类 GlobalObject。

从目前信息看来,section可能的内容是 __TEXT,__objc_methname 或 __RODATA,__objc_methname ,而上面的代码是 Section.find,是在字符串中查找,也就能自动适配segment的修改。

其他一些段大概使用类似的方式,可以确定不会有影响了。

TEXT段能瘦身多少

以 Facebook armv7 为例,RODATA segment大小约14MB。

历史想法

开始以为把 TEXT段中不需要马上加载的数据移到RODATA中是为了加快App启动速度,但想想内存映射 mmap 的优势就是可以快速处理大文件,一起加载并不会有什么性能影响。

参考工程

代码: https://github.com/everettjf/Yolo/tree/master/FBInjectableTest

补充补充

segment的权限,可以通过下面的链接参数改为VM_PROT_READ.

-Wl,-segprot,__RODATA,r,r

其他资料