【问题标题】:How map.find() works looking at the outputted assemblymap.find() 如何查看输出的程序集
【发布时间】:2023-03-22 13:38:01
【问题描述】:

我在调试一个 Android 应用程序的崩溃时遇到了以下几行:

void myobject::save(std::string toSave) {
...
}
...
std::map<std::string, std::string> dict;
...    
myobject::save(dict.find("username")->second);

生成的程序集:

02F05DFC               bl         _ZNKSt6__ndk16__treeINS_12__value_typeINS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEES7_EENS_19__map_value_compareIS7_S8_NS_4lessIS7_EELb1EEENS5_IS8_EEE4findIS7_EENS_21__tree_const_iteratorIS8_PNS_11__tree_nodeIS8_PvEEiEERKT_
3096                   str        r6, [sp, #0x108 + var_48]
0146                   mov        r1, r0
CDE92E66               strd       r6, r6, [sp, #0x108 + var_50]
11F81C2F               ldrb       r2, [r1, #0x1c]!
12F0010F               tst.w      r2, #0x1
06D1                   bne        loc_THERE

                  loc_FIRST:
D1E90002               ldrd       r0, r2, [r1]
8968                   ldr        r1, [r1, #0x8]
3091                   str        r1, [sp, #0x108 + var_48]
CDE92E02               strd       r0, r2, [sp, #0x108 + var_50]
04E0                   b          loc_HERE

                  loc_THERE:
D0E90821               ldrd       r2, r1, [r0, #0x20]
2EA8                   add        r0, sp, #0xb8_ZNSt6__ndk112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6__initEPKcj
FDF743FD               bl         _ZNSt6__ndk112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6__initEPKcj

                  loc_HERE:
2EA9                   add        r1, sp, #0xb8
2046                   mov        r0, r4
04F047FB               bl         _ZN6myobject11saveENSt6__ndk112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEE

我发现了错误。当 find() 返回 end() 迭代器时会发生崩溃。但是,有趣的是,崩溃发生在 new() 内部(在 basic_string::__init() 内部完成)。当 find() 返回 end() 迭代器时,似乎 end() (end()->second) 的取消引用不会崩溃,但是当它将 end()->second 转换为 std::string 时会崩溃() 在传递给 myobject::save() 之前。

我试图查看 libcxx 实现,但没有找到我想要的。有谁知道 loc_FIRST 和 loc_THERE 两个块的用途是什么?为什么要检查 *(r1+0x1c) == 0x1?有人能解释一下汇编代码中“->second”的位置吗?

【问题讨论】:

  • 专业提示:在任何返回迭代器的函数上,它可能是 end 迭代器,在使用它之前,请始终检查 end 的返回值,
  • end()-&gt;second 表现出未定义的行为。表现出未定义行为的程序的确切故障模式并不是那么有趣。你为什么在乎?但如果你坚持:end() 实际上是一个指向垃圾的指针。然后将该垃圾传递给std::string 复制构造函数,因为save() 按值获取其参数。复制构造函数尝试实际使用该垃圾的内容,然后崩溃。
  • c++filt 刚刚创造了一个损坏符号名称最不可读扩展的新记录:std::__ndk1::__tree_const_iterator&lt;std::__ndk1::__value_type&lt;std::__ndk1::basic_string&lt;char, std::__ndk1::char_traits&lt;char&gt;, std::__ndk1::allocator&lt;char&gt; &gt;, std::__ndk1::basic_string&lt;char, std::__ndk1::char_traits&lt;char&gt;, std::__ndk1::allocator&lt;char&gt; &gt; &gt;, std::__ndk1::__tree_node&lt;std::__ndk1::__value_type&lt;std::__ndk1::basic_string&lt;char, std::__ndk1::char_traits&lt;char&gt;, ... 停在那里,因为它对于另外 1000 个字符的评论来说太长了!
  • (来自第一个bl 指令,它返回一个指向某种对象的指针,该对象在+0x1c 有一个成员,您的代码正在检查它的低位。注意它是@987654333 @,不是cmp,所以结果只取决于ldrb加载的字节的低位,而不是全部)。
  • 我认为它正在做一个小字符串优化(SSO)。 loc_FIRST 情况是字符串包含在 SSO 缓冲区中的情况,并且只是将字符串的 12 个字节直接复制到字符串的堆栈位置。 loc_THERE 情况是 SSO 处于非活动状态并调用 std::string::__init 进行分配和其他非 SSO 初始化工作。所以本质上,该分支是std::string 构造的一部分,该构造已内联到此代码中(但慢速非 SSO 路径仍然不合时宜)。被检查的位可能表明 SSO 是否对该字符串处于活动状态。

标签: c++ dictionary assembly std libc++


【解决方案1】:

我认为它正在做一个小的字符串优化 (SSO)。 loc_FIRST 情况是字符串包含在 SSO 缓冲区中的情况,并且只是将字符串的 12 个字节直接复制到字符串的堆栈位置。 loc_THERE 情况是 SSO 处于非活动状态并调用 std::string::__init 来进行分配和其他非 SSO 初始化工作。所以本质上,该分支是已内联到此代码中的 std::string 构造的一部分(但慢速非 SSO 路径仍然不合时宜)。被检查的位可能表示 SSO 是否对该字符串有效。 – BeeOnRope 3 月 27 日 21:02

【讨论】:

    猜你喜欢
    • 2011-01-22
    • 2021-07-08
    • 1970-01-01
    • 2020-01-29
    • 1970-01-01
    • 1970-01-01
    • 2012-11-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多