Assembly 如何将RAX中的值写入汇编中的标准输出?
我可以使用syscall for write将内存中的一些数据打印到STDOUT:Assembly 如何将RAX中的值写入汇编中的标准输出?,assembly,x86-64,system-calls,Assembly,X86 64,System Calls,我可以使用syscall for write将内存中的一些数据打印到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: m
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
您不能编写纯数字(因为它们将被解释为指针),您需要将该数字转换为字符串,并将指向该字符串的指针作为
write
的参数传递。你必须先把它转换成文本。您可以选择简单的方法,使用libc中的printf
,或者,如果您愿意,可以编写自己的转换实用程序
更新:如果您希望代码与位置无关,那么使用堆栈就更容易了。简单地将缓冲区移动到代码段中,因为您在注释中链接的代码不再起作用,因为现代处理器的代码段是只读的。我已经更新了代码,将堆栈用于临时存储
.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
如果您想要在代码段中实际存储缓冲区,那么可以这样做。必须使用例如mprotect(2)
将buffer
所在的页面标记为可写。此处在汇编中未进行错误检查(并假设sysconf(\u SC\u PAGE\u 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)
这里的跳过64是什么意思?它为缓冲区保留64个字节。它也可以在.bss
部分中使用,在该部分中可能应该放置缓冲区。如果你愿意,你也可以在堆栈上分配缓冲区。这取决于尾数吗?不确定你在问什么。x86_64机器始终是小端机,但代码并不依赖于它,也就是说,你可以将它翻译成大端机的汇编语言,它仍然可以工作。还有两个地方,jbe2f
,jnz1b
,f
和b
是什么意思?