0

I'd like to convert an integer to a string (char *) so I can print it - in pure aarch64. This is what I came up with:

        ; adr x20, ->result         // x20 = char *

        ; mov x14, 0                // x14 = digit counter
        ; mov x15, 12345            // x15 = number to convert
        ; mov x16, 10               // x16 = divisor

    ; ->divide:
        ; udiv x17, x15, x16        // x17 = quotient
        ; msub x18, x17, x16, x15   // x18 = remainder
        ; add x18, x18, 48          // x18 = remainder as char
        ; strb w18, [x20, x14]      // put char in char *
        ; add x14, x14, 1           // x14 = digit counter
        ; mov x15, x17              // x16 = remaining number to convert
        ; cbnz x17, ->divide        // repeat until quotient is zero

    ; ->result:
        ; .byte 0, 0, 0, 0, 0, 0

Values are hardcoded because I feel it as a comfortable starting point.

This is the logic I'm trying to follow:

  1. Divide the number by 10: the remainder is the last digit;
  2. Add 48 to the remainder, so I get the last digit as a ascii char;
  3. Write that char to a buffer (incrementally);
  4. Repeat until the quotient becomes zero.

However, the code immediately occurs into a segmentation fault on this instruction strb w18, [x20, x14]. Using gdb, it seemed everything was correct: x18 had 53 (5 - the last digit of 12345 - + 48).

However, I guess I still don't get how str(b) works.

First of all, adr x20, ->result will load the pointer to result (a pointer to [0, 0, 0, 0, 0, 0], the buffer) at x20, let's say 0x7fb697d40c. Then, strb w18, [x20, x14] should take the least byte of w18 (53 in the first round) and put it at 0x7fb697d40c + the content of x14 (0 in the first round). So I expect 0x7fb697d40c to point to [53, 0, 0, 0, 0, 0].

However, SIGSEGV occurs and I don't know why. What am I missing?

Edit Here it is the source code using the usual syntax.

_start:
    mov x14, #0
    mov x15, #12345
    mov x16, #10

    adr x20, result

divide:
    udiv x17, x15, x16
    msub x18, x17, x16, x15
    add x18, x18, #48
    strb w18, [x20, x14]
    add x14, x14, #1
    mov x15, x17
    cbnz x17, divide

result: 
    .byte 0, 0, 0, 0, 0, 0

Solution: As Nate pointed out in the comments, the memory range where the code was located was not writable. Since I didn't want to involve mprotect to alter the permissions, I just ended up using the stack.

  • Instead of commented-out code, could you please provide a [Minimal, Reproducible Example](https://stackoverflow.com/help/minimal-reproducible-example) that could be copied/pasted into a file (say, `str.s`), and assembled/linked as is ? this would greatly facilitate answering your question. – Frant Jan 28 '21 at 19:37
  • @Frant I forgot to say that I'm assembling that code at runtime using dynasm-rs, that's why I'm using its syntax. I'll update the question asap –  Jan 28 '21 at 19:55
  • 3
    Are you sure that `result` is being placed in a writable data section? Normally you would expect to see `.data` before that label. – Nate Eldredge Jan 28 '21 at 20:05
  • @NateEldredge Sorry, I forgot to remove those two `ldr` lines. I updated the question. However, you are correct, the data section I'm trying to store to is not writeable. Thanks! –  Jan 28 '21 at 20:11
  • 1
    Almost a duplicate of [Segfault when writing to string allocated by db \[assembly\]](https://stackoverflow.com/q/25124591) but that's about shellcode so the answer doesn't really suggest putting writeable static data in .bss or .data. – Peter Cordes Jan 28 '21 at 21:31

0 Answers0