I can use syscall for write to print some data in memory to STDOUT:
ssize_t write(int fd, const void *buf, size_t count);
That is:
movq $1, %rax
movq $1, %rdi
move address_of_variable %rsi
movq $5, %rdx
syscall
But how can I print register values?
UPDATE
.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
You have to convert it to text first. You can go the easy route and use e.g. printf
from libc or, if you're so inclined, write your own conversion utility.
Update: If you want the code to be position independent it's easier to use the stack. Simply moving the buffer into the code segment as the code you linked to in the comments doesn't work anymore since modern processors have code segments as read only. I have updated the code to use the stack for temporary storage.
.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
If, perversely, you want to actually store the buffer in the code segment it can be done. You have to mark the page where buffer
resides as writable using e.g. mprotect(2)
. Here done without error checking (and assuming sysconf(_SC_PAGE_SIZE) == 4096
) in assembly:
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
And now the code should work even when the buffer is located in the code segment. BTW I use sys_exit in my example because I'm testing it as a stand alone file with gcc x.s -nostdlib -ggdb -o x
.
Update2: To use it when the code is relocated use RIP-relative addressing. Change bufferend
to bufferend(%rip)
.
you can't write pure numbers (as they will be interpreted as pointers), you need to convert that number to a string and pass the pointer to that string as an arg of write
. something along the lines of this.
精彩评论