【问题标题】:How do I write the value in RAX to STDOUT in assembly?如何在汇编中将 RAX 中的值写入 STDOUT?
【发布时间】:2011-11-02 20:09:48
【问题描述】:

我可以使用 syscall 进行写入,将内存中的一些数据打印到 STDOUT:

ssize_t write(int fd, const void *buf, size_t count);

即:

movq    $1, %rax
movq    $1, %rdi
move address_of_variable %rsi
movq    $5, %rdx
syscall

但是如何打印寄存器值?

更新

.text
        call start
    start:
        movq $100, %rdi
        movq $10, %rsi
        call print_number
        ret

    buffer:
        .skip 64
    bufferend:
    # rdi = number
    # rsi = base
    print_number:
        leaq bufferend, %rcx
        movq %rdi, %rax
    1:
        xorq %rdx, %rdx
        divq %rsi

        add $'0', %dl
        cmp $'9', %dl
        jbe 2f
        add $'A'-'0'-10, %dl

    2:
        sub $1, %rcx
        mov %dl, (%rcx)

        and %rax, %rax
        jnz 1b

        mov %rcx, %rsi
        lea bufferend, %rdx
        sub %rcx, %rdx

        movq    $1, %rax
        movq    $1, %rdi
        syscall
        ret

【问题讨论】:

标签: assembly x86-64 system-calls


【解决方案1】:

您必须先将其转换为文本。您可以走简单的路线并使用例如printf 来自 libc,或者,如果您愿意,可以编写自己的转换实用程序。

更新:如果您希望代码与位置无关,则使用堆栈会更容易。简单地将缓冲区移动到代码段中,因为您在 cmets 中链接到的代码不再起作用,因为现代处理器的代码段是只读的。我已更新代码以使用堆栈进行临时存储。

    .text

    call start
start:
    movq    $186, %rax # sys_gettid
    syscall

    movq %rax, %rdi
    movq $10, %rsi
    call print_number

    #ret
    mov $60, %rax # sys_exit
    mov $0, %rdi
    syscall

# rdi = number
# rsi = base
print_number:
    sub $72, %rsp # alloc room for buffer on the stack, 8 more than needed

    lea 64(%rsp), %rcx # 64(%rsp) == buffer end
    movq %rdi, %rax # rax holds current number 
1:
    xorq %rdx, %rdx # clear rdx (div uses the 128-bit number in rdx:rax)
    divq %rsi # divide by base, updating rax for the next iteration 
              # and giving us our digit in rdx (dl)
    add $'0', %dl # turn into printable character
    cmp $'9', %dl # handle digits > 10
    jbe 2f
    add $'A'-'0'-10, %dl # adjust number so A=10, B=11 ...

2:
    sub $1, %rcx # subtract before adding character as we start from the end
    mov %dl, (%rcx) # store character in string

    and %rax, %rax # rax == 0?
    jnz 1b # if not, then keep working

    mov %rcx, %rsi # buf = address of last character stored
    lea 64(%rsp), %rdx # calculate length by subtracting buffer end
    sub %rcx, %rdx # from the buffer start

    movq $1, %rax # sys_write
    movq $1, %rdi # fd = STDOUT_FILENO
    syscall

    add $72, %rsp # adjust stack back
    ret

如果您想将缓冲区实际存储在代码段中,则可以这样做。您必须使用例如将buffer 所在的页面标记为可写。 mprotect(2)。这里没有错误检查(并假设sysconf(_SC_PAGE_SIZE) == 4096)在汇编中完成:

     mov $10, %rax # sys_mprotect
     lea buffer, %rdi # addr
     and $-4096, %rdi # page align
     mov $4096, %rsi # len
     mov $7, %rdx #prot = PROT_READ|PROT_WRITE|PROT_EXEC
     syscall

现在,即使缓冲区位于代码段中,代码也应该可以工作。顺便说一句,我在示例中使用 sys_exit 是因为我正在使用 gcc x.s -nostdlib -ggdb -o x 将其作为独立文件进行测试。

Update2:要在重新定位代码时使用它,请使用 RIP 相对寻址。将bufferend 更改为bufferend(%rip)

【讨论】:

  • 这里的.skip 64 是什么意思?
  • 它为缓冲区保留 64 个字节。它也可以用在.bss 部分,可能应该放置缓冲区的地方。如果您愿意,也可以在堆栈上分配缓冲区。
  • 是否取决于字节序?
  • 不确定你在问什么。 x86_64 机器总是小端,但代码不依赖它,即你可以将它翻译成大端机器的汇编语言,它仍然可以工作。
  • 还有2个地方,jbe 2fjnz 1bfb是什么意思?
【解决方案2】:

您不能编写纯数字(因为它们将被解释为指针),您需要将该数字转换为字符串并将指向该字符串的指针作为 write 的 arg 传递。类似于this

【讨论】:

    猜你喜欢
    • 2019-10-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多