使用 Intel x86 程序集在 Unix 上手动空终止(地址与内存操作数中的值)?

Manual Null-Termination on Unix using Intel x86 Assembly (address vs value in memory operands)?

我一定是遗漏了一些明显的东西,但我似乎找不到字符串的结尾。

我的代码从几个调用开始,如下所示:

; read user input
;
    mov     eax, SYSCALL_READ       ; read function
    mov     ebx, STDIN              ; Arg 1: file descriptor
    mov     ecx, buf                ; Arg 2: address of buffer (buffer is input)
    mov     edx, BUFLEN             ; Arg 3: buffer length (defined as 256)
    int     080h

    mov     [rlen], eax             ; save length of string read

教授给了我们一个 shell 程序来工作,但我对其中的大部分都掌握得很好。让我失望的是,我的印象是 rlen 现在应该包含我正在使用的字符串的长度,但是当我键入以下内容时:

mov     byte[esi + rlen], 92            ; add a zero

我遇到段错误。同样,如果我使用 [buf + rlen]。 buf 和 ESI 本身都不会导致段错误,所以在我看来 rlen 没有按照我的想法去做。

谁能帮我弄清楚这是怎么回事?

您的代码有两个问题:

mov     byte[esi + rlen], 92
  • 92 != 0。终止零字节 '[=13=]' 是一个值为零的整数。

  • rlen 是一个地址,不是那个地址的值。

所以测试读取 returned >= 0,然后使用仍在寄存器中的 return 值。

; read(2) return value still in eax

test eax, eax
jl  read_error    ; handle errors on eax less than zero.

mov esi, buf      ; mov imm32 to get the address in a register

mov  [rlen], eax  ; store return value to the rlen global

mov  byte ptr[esi + eax], 0
;or:  mov byte ptr [buf + eax], 0  ; works because the buffer is statically allocated.

jz  handle_EOF    ; flags still set from test

或者,如果您将 ecx 复制到未被读取系统调用破坏的寄存器,则可以使用它而不是重新加载。

在一个函数中,将局部变量视为存在于寄存器中,并且内存位置仅在您 运行 寄存器不足时可以溢出它们的地方。不要像非优化编译器和 store/reload 你不需要的变量。这在像 x86-64 这样有 16 个寄存器的架构上更容易; 32 位 x86 非常有限,并且有一个过时的 args-on-the-stack ABI。


如果您的缓冲区已经清零,您可以只向 read(2) 传递一个比缓冲区大小小 1 的计数。不过,在读取 returns 后将最后一个字节归零更好。