3

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
Je Rog
  • 5,675
  • 8
  • 39
  • 47

2 Answers2

3

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).

user786653
  • 29,780
  • 4
  • 43
  • 53
  • What does `.skip 64` mean here? – Je Rog Aug 24 '11 at 14:28
  • It reserves 64 bytes for the buffer. It can also be used in the `.bss` section, where the buffer probably should have been placed. You can also allocate the buffer on the stack if you wish. – user786653 Aug 24 '11 at 14:33
  • Does it depends on the endianness ? – Je Rog Aug 24 '11 at 14:40
  • Not sure what you're asking. x86_64 machines are always little endian, but the code does not depend it, i.e. you could translate it to assembler language for a big endian machine and it would still work. – user786653 Aug 24 '11 at 14:44
  • There's still 2 places, `jbe 2f`, `jnz 1b`,what do `f` and `b` mean? – Je Rog Aug 24 '11 at 14:48
  • @Je Rog: http://tigcc.ticalc.org/doc/gnuasm.html#SEC48L short version: f=forward/b=backward, ie. `jbe 2f` means jump forward if below or equal to the local `2` label. – user786653 Aug 24 '11 at 15:04
  • Where (you use gdb to determine that) and for what values? – user786653 Aug 24 '11 at 15:11
  • I just made a little modification to make it position independent: http://pastebin.com/K9qjJz2E ,I can't say for sure which instruction causes the segfault,as `stepi` doesn't show me the instruction called somehow.. – Je Rog Aug 24 '11 at 15:21
  • I just found that segmentation fault happens at `mov %dl, (%rcx)` – Je Rog Aug 24 '11 at 15:57
  • Why the original doesn't work? I've already changed the priviledge as `PROT_READ | PROT_WRITE` by `mmap`. – Je Rog Aug 24 '11 at 16:37
  • If you change the privileges of the page containing the buffer to `PROT_READ|PROT_WRITE|PROT_EXEC` with e.g. `mprotect(2)` it should work, but IMO the stack version is preferable. – user786653 Aug 24 '11 at 16:41
  • The stack version works.Yes,I changed it by `mprotect` too,but the original version fails.Why? Also, is there any particular reason that you asked for 72 bytes when only 64 is necessary? – Je Rog Aug 24 '11 at 16:51
  • Can you paste the complete code of the no-stack version ?As it's still causing segmengtation fault for me... – Je Rog Aug 24 '11 at 17:17
  • I tried again,it's still causing Segmentation fault.And I also tried to run it in your way: `gcc x.s -nostdlib -ggdb -o x`,it works.So I guess it's not relocatable? Here's my loader code in c: http://pastebin.com/VUp79NEn , could you take a look why the no-stack version doesn't work with it? I've personally used it to load and run other PIC with no problem before... – Je Rog Aug 24 '11 at 17:59
  • @Je Rog: Applying the changes from my edit ( http://pastebin.com/7PB44Q19 ) makes it work with your program. At least on my machine. If it *still* doesn't work I think it might be suitable for a new question. – user786653 Aug 24 '11 at 18:45
  • My final doubt is: is there any particular reason that you asked for 72 bytes when only 64 is needed, or say,8 more than needed? – Je Rog Aug 25 '11 at 13:52
  • @Je Rog: Not in the code as shown, I was at one point planning on using it and forgot to change it back. – user786653 Aug 25 '11 at 16:52
0

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.

Community
  • 1
  • 1
Necrolis
  • 25,836
  • 3
  • 63
  • 101