CSAPP - 反编译 string_length

虽然先前已经把 phase_1 和 phase_2 做出来了, 但其实是参考了网络上的答案, 仅仅是大概知道了关键汇编代码。但其实并没有真的懂。为啥呢?因为很多模棱两可的地方是靠猜测的,而猜测是脆弱的。

重新看 phase_1, 第一个门槛是 string_length 函数。尝试逐句翻译回 C 代码。

前提条件: 知道 eax 存储返回值, 知道 rdi 存储函数第一个参数。使用Intel风格的汇编。

bash 复制代码
(gdb) disassemble string_length
Dump of assembler code for function string_length:
   0x000000000040131b <+0>:     cmp    BYTE PTR [rdi],0x0
   0x000000000040131e <+3>:     je     0x401332 <string_length+23>
   0x0000000000401320 <+5>:     mov    rdx,rdi
   0x0000000000401323 <+8>:     add    rdx,0x1
   0x0000000000401327 <+12>:    mov    eax,edx
   0x0000000000401329 <+14>:    sub    eax,edi
   0x000000000040132b <+16>:    cmp    BYTE PTR [rdx],0x0
   0x000000000040132e <+19>:    jne    0x401323 <string_length+8>
   0x0000000000401330 <+21>:    repz ret 
   0x0000000000401332 <+23>:    mov    eax,0x0
   0x0000000000401337 <+28>:    ret    
End of assembler dump.

尝试反汇编为 C 代码:

1)返回值类型:看到了对 eax 寄存器的操作。基本上是 int 类型。C代码为:

c 复制代码
int string_length()
{
    ...
}

2)cmp BYTE PTR [rdi], 0x0: 这句是 rdi 寄存器里的值表示的内存地址里的值,和0作比较。用C代码表示为:

c 复制代码
int string_length(const char* str)
{
    if (*str == '\0')
    {
        return 0;
    }
}
  1. je 0x401332 <string_length+23>: 和上一句连在一起的, return 0.
  2. mov rdx, rdi: 把函数第一个参数,赋值到一个新的变量里头,大概是:
c 复制代码
int string_length(const char* str)
{
    if (*str == '\0')
    {
        return 0;
    }
    const char* ptr = str;
}
  1. add rdx, 0x1: 新赋值的变量加1:
c 复制代码
int string_length(const char* str)
{
    if (*str == '\0')
    {
        return 0;
    }
    const char* ptr = str;
    ptr += 1;
}
  1. mov eax, edx: 把刚刚加1的变量,放到 eax 寄存器, 也就是和返回值有关系。没法直接写C代码。继续看。
  2. sub eax, edi: 让 eax 寄存器减掉 edi 寄存器。C代码:
c 复制代码
int string_length(const char* str)
{
    if (*str == '\0')
    {
        return 0;
    }
    const char* ptr = str;
    ptr += 1;
    int ret = ptr - str;
    return ret;
}
  1. cmp BYTE PTR [rdx], 0x0: 把 rdx 寄存器里的值对应的内存地址处的值, 和0比较。看不出来C代码。继续看汇编.
  2. jne 0x401323 <string_length+8>: 如果刚刚的比较结果不相等,也就是说 rdx != 0, 那么跳转到 add rdx, 0x1 这句。代码:
c 复制代码
int string_length(const char* str)
{
    if (*str == '\0')
    {
        return 0;
    }
    const char* ptr = str;
    int ret;
hello:
    ptr += 1;
    ret = ptr - str;
    if (*ptr != '\0')
    {
        goto hello;
    }
    return ret;
}
  1. repz ret: 没啥高深的,就是跳转到 ret

经过上面这段梳理,写出来的C代码很混乱。goto 和 if 的组合,基本上是等价于 while 循环:

c 复制代码
int string_length(const char* str)
{
    if (*str == '\0')
    {
        return 0;
    }
    const char* ptr = str;
    int ret;

    do {
        ptr += 1;
        ret = ptr - str;
    } while (*ptr != '\0')
    return ret;
}

再进一步, 感觉 ret 的赋值做了重复计算, do while 也不如 while 直接:

c 复制代码
int string_length(const char* str)
{
    if (*str == '\0')
    {
        return 0;
    }
    const char* ptr;
    for (ptr = str + 1; ptr != '\0'; ptr++);
    return ptr - str;
}
相关推荐
Severus_black2 小时前
【C++初阶】list使用及相关实现,思路顺滑流畅!
c语言·数据结构·c++·stl·list
希和20276 小时前
C语言程序设计题|有序数组插入数字冒泡排序
c语言·数据结构·算法
Zwawa21 小时前
屏幕不止一种:OLED、LCD1602字符屏、TFT/IPS图形屏和串口屏怎么用,这与TTY和PTY又有什么关系
c语言·单片机·嵌入式
Fruiticecake21 小时前
C 程序员速通现代 C++(一):lambda、std::function、移动语义与智能指针
c语言·c++
田甲21 小时前
做一款 Type-C 母座读卡器:GL3224 双卡方案实测
c语言·开发语言
·present·1 天前
CSAPP(lecture 13_02 Linking)
c语言·c++
wuyk5552 天前
Keil工具链-第05讲-启动文件与__main到底做了什么
c语言·stm32·单片机·嵌入式硬件·物联网
Navigator_Z2 天前
LeetCode //C - 1317. Convert Integer to the Sum of Two No-Zero Integers
c语言·算法·leetcode
冻柠檬飞冰走茶2 天前
字母移动游戏题解:贪心求最长可保留连续子串(附 C 实现)
c语言·开发语言·数据结构·算法·动态规划
Hhy_11072 天前
《C++深度解构06》手撕 string —— 从底层内存管理到深浅拷贝彻底吃透
c语言·开发语言·c++·学习·visual studio