我正在参与「启航计划」

弥补阐明

上一篇文章讲了如安在LLDB中查看JS目标的特点值,了解了Butterfly等概念。

有一个点之前没有提到,关于一个非array的JS目标,如果其特点数量较少,则JSC并不会为其分配相应的Butterfly目标,而是直接存在目标尾部(叫做inline property),存到Butterfly处的则称做out-of-line property。

:看起来特点offset在64以下的特点都是用inline存储(非array的情况):

 static constexpr PropertyOffset firstOutOfLineOffset = 64;

Structure

本节主要讲Structure。

JavaScriptCore 的概念中,Structure对应的便是JS目标的Prototype(原型)。每个JS目标都有一个Structure,Structure之间能够有承继联系,当JS目标的结构(也即prototype)发生变化时,JS目标会生成一个新的(或复用一个已有的)Stucture。一个目标中有哪些特点,以及这些特点对应到目标内存中的偏移都存在Structure及其相应ClassInfo目标中。

从上一篇对JS目标内存布局的描绘可知,JS目标中存储了StructureID的值,是一个int值,那么怎么从StructureID得到真实的Structure目标呢?

以下代码片段描绘了StuctureID类的部分界说:

constexpr uintptr_t structureHeapAddressSize = 4 * GB;
class StructureID {
public:
    static constexpr uint32_t nukedStructureIDBit = 1;
    static constexpr CPURegister structureIDMask = structureHeapAddressSize - 1;
    ...
    StructureID decontaminate() const { return StructureID(m_bits & ~nukedStructureIDBit); }
    inline Structure* decode() const;
    ...
private:
    explicit StructureID(uint32_t bits) : m_bits(bits) { }
    uint32_t m_bits { 0 };
};

在StructureID内部存储了一个uint32_t,其代表Structure表内的偏移,真实的Structure数据是存在Structure表中。

StrctureID有个inline成员函数decode(),此方法即实现了从StructureID到Structure的转换:

ALWAYS_INLINE Structure* StructureID::decode() const
{
    // Take care to only use the bits from m_bits in the structure's address reservation.
    ASSERT(decontaminate());
    return reinterpret_cast<Structure*>((static_cast<uintptr_t>(decontaminate().m_bits) & structureIDMask) + g_jscConfig.startOfStructureHeap);
}

从上面的代码片段能够看出,Structure的具体位置是某个全局变量(g_jscConfig.startOfStructureHeap)加上 StructureID 作为偏移。

下一个问题便是:如安在运行时找到g_jscConfig?

看一下g_jscConfig及其相关的关键目标的界说:

#define g_jscConfig (*bitwise_cast<JSC::Config*>(&g_wtfConfig.spaceForExtensions))
//注:12 * sizeof(void *) == 12 * 8 == 0x60
#define g_wtfConfig (*bitwise_cast<WTF::Config*>(&WebConfig::g_config[WTF::startSlotOfWTFConfig]))  

其中:

constexpr size_t reservedSlotsForGigacageConfig = 12;
constexpr size_t startSlotOfWTFConfig = Gigacage::reservedSlotsForGigacageConfig;

也即,如果知道g_config,即可经过以下方法得到g_jscConfig及其startOfStructureHeap:

&g_wtfConfig == &g_config + 0x60   //g_config->wtfConfig
&g_jscConfig == &g_wtfConfig + 0x140  //g_wtfConfig->spaceForExtensions
g_jscConfig->startOfStructureHeap == *(&g_jscConfig + 0x30)  //g_jscConfig->startOfStructureHeap

看了一下JavasScriptCore的导出符号,其中包含了g_config,使用
dlsym(RTLD_DEFAULT, "g_config")即可取到。

在lldb中,也能够直接print全局变量g_config的地址:

(lldb) p &g_config

下一篇,咱们接着看Structure的具体内容。