我正在参与「启航计划」
弥补阐明
上一篇文章讲了如安在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的具体内容。