地址是在它之后还是之前执行?

问题描述:

对不起,我不知道该怎么问这个问题,但让我看看能不能把它画出来。我有这个程序:地址是在它之后还是之前执行?

section .data 
    s1 db "nice",10,0 

section .text 
    global _start 

_start: 
    mov rax, s1 
    call _print 
    call _exit 


_print: 
    push rax 
    mov rbx, 0 
_printLoop: 
    inc rax 
    inc rbx 
    mov cl, [rax] 
    cmp cl, 0 
    jne _printLoop 

    mov rax, 1 
    mov rdi, 1 
    pop rsi 
    mov rdx, rbx 
    syscall 
    ret 

_exit: 
    mov rax, 60 
    mov rdi, 0 
    syscall 

其中有一个子例程_print将打印一些空终止的字符串与地址存储在rax中。我的问题是,我假设,如果我有记忆的指针一些字符串(如S1),那么它会是这个样子:

rax 
s1 
| 
[n][i][c][e][\n][\0] 

所以,在我的脑海,我想检查存储在s1中以确保第一个字符(“nice”中的'n')不是空终止符。但这不是这个程序的工作原理。它增加兵营和RBX(RBX存储计)和S1 + 1首先检查,像这样:

s1 rax 
| | 
[n][i][c][e][\n][\0] 

所以,我能想到的唯一解释,就是S1指针或者,在一开始,分在分配给字符串的字节之前,如下所示:

s1 
| 
[][n][i][c][e][\n][\0] 

,或者当从中读取时,它向后读取,而不是向前读取。但我不确定它是如何工作的。我希望我已经足够清楚,有人为我解决这个问题。谢谢。这个x86-64

+0

你能详细说明吗?它似乎不像第一个字符比较 – Mauricio

+0

因此,事实上,“n”没有得到比较? – Mauricio

+0

好,所以算法只是假设第一个字符是好的。谢谢 – Mauricio

只要字符串至少有一个字符,写入的代码的长度就是正确的。 (这是一个合理的假设,因为打印没有字符的字符串会是什么意思,但不幸的是它不健壮。)

修复它以正确处理空字符串的一个简单方法是:

_print: 
    mov rsi, rax 
    mov rbx, 0 

    cmp byte [rax], 0 
    jz _printSkip 

_printLoop: 
    inc rax 
    inc rbx 
    mov cl, [rax] 
    cmp cl, 0 
    jne _printLoop 

    mov rax, 1 
    mov rdi, 1 
    mov rdx, rbx 
    syscall 

_printSkip: 
    ret 

除了在循环之前为空字符串添加检查之外,我将其更改为将指针直接放入rsi而不是堆栈中。通过不使用堆栈来节省rax,它可以避免在跳过系统调用的情况下需要堆栈清理。

在我的其他答案中,我尽可能地做了少量更改以清楚地显示修复问题所需的更改。在这个答案中,我展示了我将如何写它:

_print: 
    cmp byte [rax], 0 
    jz _printSkip 

    mov rsi, rax 
    xor edx, edx 
_printLoop: 
    inc edx 
    cmp byte [rsi+rdx], 0 
    jnz _printLoop 

    mov eax, 1 
    mov edi, 1 
    syscall 

_printSkip: 
    ret