strlen从C到汇编,处理器为什么要做这些步骤?

问题描述 投票:0回答:2

我在研究这个C函数的汇编代码:

int stringlen(char *s1, char *s2) {
    int result = 0; 
    int i = 0; 
    while (s1[i] != 0) {
        result++; 
        i++; 
    }
    int j = 0; 
    while (s2[j] != 0) {
        result++; 
        j++; 
    }
    return result; 
}

int main(void) {
    char s1[] = "this is "; 
    char s2[] = "a test"; 
    int result = stringlen(s1, s2); 
    return 0; 
}

基于汇编代码,在“LN3”我有,

$LN3:
    push    rbp
    push    rsi
    push    rdi
    sub     rsp, 336                ; 00000150H
    lea     rbp, QWORD PTR [rsp+32]
    lea     rdi, QWORD PTR [rsp+32]
    mov     ecx, 28
    mov     eax, -858993460             ; ccccccccH
    rep     stosd
    mov     rax, QWORD PTR __security_cookie
    xor     rax, rbp
    mov     QWORD PTR __$ArrayPad$[rbp], rax
    lea     rcx, OFFSET FLAT:__EFF96FC6_main@c
    call    __CheckForDebuggerJustMyCode 

这与

main
功能有关。

第一题:

  • 为什么我必须从
    336
    中减去
    rsp

然后,我知道

lea
是用来保存地址的,以便于编码(因为使用指针可能容易出错),并且我知道
QWORD
表示四字,它只是表示 4 个字节(即 = 32 位)。

第二题:

  • 为什么我必须在
    rsp + 32
    rbp
    中保存相同的地址(在
    rdi
    )?

最后2个问题:

我想我知道

ecx
寄存器通常用来保存数组或字符串的维数,但是

  • 为什么我必须将

    28
    移动到
    ecx
    ?同样,

  • 为什么我必须将

    -858993460
    移动到
    eax
    ?这两个数字总是一样的吗?或者它们会根据我创建的功能而改变吗?如果它们发生变化,我怎么知道这些寄存器中的哪个数字移动了?有没有办法计算它们?

c assembly visual-c++ stack x86-64
2个回答
3
投票

您发布的程序集输出与您的代码无关:它似乎是 Microsoft 编译器生成的序言,作为其安全性增强的一部分。除非您对这个特定主题感兴趣,否则您可以忽略此代码并专注于为您的程序生成的实际汇编代码。

您可以使用 Godbolt 的在线编译器资源管理器非常有效地做到这一点

您可以测试不同的编译器和选项。

在链接页面上,您会注意到启用了优化的 clang 编译器只为

main
函数生成了 2 条指令,因为它确定函数调用没有副作用并且没有使用返回值。

stringlen
函数中,它生成一条指令
lea rax,[rax + 1]
来递增
rax
而不是
inc rax
add rax,1
以避免更新在以下指令中测试的标志
jne .LBB0_7
.


0
投票

可以这样翻译。我也是装配新手。

顺便说一下,

QWORD
是8字节,
DWORD
是4字节。

main :
    push    rbp
    mov     rbp, rsp
    sub     rsp, 32
    movabs  rax, 2338328219631577204          ; arbitrary value moved to rax
    mov     QWORD PTR[rbp - 13], rax          ; first 8 char in s1
    mov     BYTE PTR[rbp - 5], 0              ; null byte terminator of s1
    mov     DWORD PTR[rbp - 20], 1702109281   ; first 4 byte of s2  
    mov     DWORD PTR[rbp - 17], 7631717      ; last 3 byte of s2 including null byte
    lea     rdx, [rbp - 20]                   ; s1 decays to char pointer, rdx holds it s1[0] address 
    lea     rax, [rbp - 13]                   ; s2 decays to char pointer, rax holds it s2[0] address            
    mov     rsi, rdx                          ; move s1[0]'s address to rsi  
    mov     rdi, rax                          ; move s2[0]'s address to rdi  
    call    stringlen                           
    mov     DWORD PTR[rbp - 4], eax           ; get return value from function, put it in int variable 
    mov     eax, 0                            ; put 0 to eax, eax will be return value from main()  
stringlen:
    push    rbp
    mov     rbp, rsp
    mov     QWORD PTR[rbp - 24], rdi          ; move s1's address to char pointer(8 byte)
    mov     QWORD PTR[rbp - 32], rsi          ; move s2's address to char pointer(8 byte)
    // no 7 byte differences between [rbp - 13] && [rbp - 20] like in main()
    mov     DWORD PTR[rbp - 4], 0             ; move 0 to int variable (int result)
    mov     DWORD PTR[rbp - 8], 0             ; move 0 to int variable (int i)   
    jmp.L2
.L3:
    add     DWORD PTR[rbp - 4], 1             ; increase result++
    add     DWORD PTR[rbp - 8], 1             ; increase i++
.L2 :
    mov     eax, DWORD PTR[rbp - 8]           ; move (int i) to eax // firstLoop[int i = 0]
    movsx   rdx, eax                          ; move sign extended eax to rdi  
    // that integer(int i) can be used as negative too (that is why sign extend i guess)
    mov     rax, QWORD PTR[rbp - 24]          ; move s1's address to rax 
    add     rax, rdx                          ; (ptr + 0) // pointer aritmetics rax = s1[0]'s address
    movzx   eax, BYTE PTR[rax]                ; get s[0] first char of s1[](char array) zero extend to eax
    // zero extending says that i will be used as unsigned char 
    // that 1 byte will be used from 0 to 255 for ASCII Values
    test    al, al                            ; bitwise AND operation && s[i] && s[i] == 0 ZF set 
    // if ZF set je will be chosen else jne.L3 will be chosen
    // loop begins 
    jne.L3                                  
    // when int i's value become 8 which s[8] == '\0' it will get out of the loop and continiues
    mov     DWORD PTR[rbp - 12], 0            ; move 0 to int variable (result)
    jmp.L4
.L4 :
    mov     eax, DWORD PTR[rbp - 12]          
    movsx   rdx, eax                          
    mov     rax, QWORD PTR[rbp - 32]            
    add     rax, rdx
    movzx   eax, BYTE PTR[rax]
    test    al, al
    jne.L5
    mov     eax, DWORD PTR[rbp - 4]           ; return (int result) to main() inside eax; 
.L5:
    add     DWORD PTR[rbp - 4], 1
    add     DWORD PTR[rbp - 12], 1
© www.soinside.com 2019 - 2024. All rights reserved.