【问题标题】:How can I print Numbers with aarch64, without using C如何在不使用 C 的情况下使用 aarch64 打印数字
【发布时间】:2021-04-27 14:04:10
【问题描述】:

正如问题所述,我想在 aarch64 中打印多位数字。

通常在解决问题时,我首先使用 python 来查看它们是否有效,特别是因为我对任何组装都很陌生。我的 Pythonic 解决方案是

number = 1234

while number != 0:
   digit = 1234 % 10         # always gives me the last digit
   print(digit, end='')      # to print the digit and not the next line
   number = number - digit   # to remove the digit I just got so the last digit is 0
   number = number / 10      # to remove said last digit and shorten number by 1

我试图在 AARCH64 中实现这一点的方式:

printNumber:
        mov  x16, #10                  /* apparently need this for udiv/msub */
        udiv x14, x12, x16             /* x12 is the number I defined above, bc idk what registers are save to use (e.g. when syscall 64, print, happens, 0-8 are used) */
        msub x13, x17, x16, x12        /* thanks to: https://stackoverflow.com/questions/35351470/obtaining-remainder-using-single-aarch64-instruction */
        sub  x12, x12, x13             /* x13 is what above is digit, x12 is number */
        udiv x12, x12, x16
        add  x13, x13, #48             /* digit to string, possible error source 1 */

        mov  x0,  #1                   /* the print part */
        mov  x1,  x13                  /* the other part where I suspect a mistake */
        mov  x2,  #1
        mov  w8,  #64
        svc  #0

        cmp  x12, #0                   /* the loop part */
        beq  exit                      /* genereric exit method I left out */
        b    printNumber

代码状态:它编译和运行没有问题,虽然它什么也没打印,但我无法调试,因为我使用实际的 aarch64 设备进行编程并且没有使用模拟器(尽管我确信有一种方法我不使用'不知道)

我希望很清楚我想要做什么,我知道一些问题,比如我应该将堆栈用于其中一些事情,但我希望我可以让它发挥作用。我也希望可以看到我为此付出了一些努力并试图寻找这样的解决方案,但找不到任何这样做的人,或者任何其他打印数字的非 c-lib 方式(请注意我不需要数字作为str,我真的只想打印它)

【问题讨论】:

  • write 系统调用需要一个指向内存的指针。你需要把你的数字放入记忆中。
  • sub 和第二个 udiv 不是必需的。 AArch64 中的无符号整数除法会向零截断,因此在第一个 udiv 之后,x14 中已经有了 number 的新值。我认为 Python 的等价物是number = number // 10。此外,您可以在循环开始之前移动mov x16, #10;无需在每次迭代时重做。
  • @MaritnGe 然后是 Linux (Android)。考虑使用运行普通 Linux 的 ARM 设备,例如 Raspberry Pi。否则您以后可能会遇到麻烦,因为 Android 对允许运行的代码类型有一些限制。
  • 是的,如果您想学习这样的基础知识,最好使用 sim 或开发平台。
  • @MaritnGe Termux 不是 Linux 模拟器。它是一个类似于您在桌面系统上使用的终端仿真器。您编写的程序直接在系统上运行,无需任何仿真。

标签: android assembly arm arm64


【解决方案1】:

正如 Jester 所指出的,您无法打印的原因是write 系统调用需要一个指针 指向要写入x1 的数据,而不是数据本身。您需要将角色存储到内存中的某个适当地址 (strb) 并将该地址传递到 x1

一种方法是使用堆栈;只需从堆栈指针中减去(16 的倍数,用于对齐)为自己分配一些内存,并记住在完成后将其放回。这是一个应该有效的示例; XXX 是我添加或更改的行。

printNumberEntry:                      /* XXX */
        sub  sp, sp, #16               /* XXX allocate 16 bytes of stack space */
printNumber:
        mov  x16, #10                  /* apparently need this for udiv/msub */
        udiv x14, x12, x16             /* x12 is the number I defined above, bc idk what registers are save to use (e.g. when syscall 64, print, happens, 0-8 are used) */
        msub x13, x14, x16, x12        /* XXX fix unrelated bug */
        sub  x12, x12, x13             /* x13 is what above is digit, x12 is number */
        udiv x12, x12, x16
        add  x13, x13, #48             /* digit to string, possible error source 1 */

        strb w13, [sp]                 /* XXX Store the low byte of x13/w13 in memory at address sp */

        mov  x0,  #1                   /* the print part */
        mov  x1,  sp                   /* XXX x1 points to the byte to be written */
        mov  x2,  #1
        mov  w8,  #64
        svc  #0

        cmp  x12, #0                   /* the loop part */
        beq  exit                      /* genereric exit method I left out */
        b    printNumber

exit:                                  /* XXX */
        add  sp, sp, #16               /* XXX restore stack before returning */
        ret                            /* XXX */

我还修复了一个不相关的错误:在您的 msub 中,x17 应该是 x14,因为那是商所在的位置。

另一种方法是在静态内存中保留一个字节:

        .bss
dataToWrite:
        .resb 1
        .text
printNumberEntry:                      /* XXX */
        adr  x1, dataToWrite           /* XXX keep address in x1 throughout */
printNumber:
        mov  x16, #10                  /* apparently need this for udiv/msub */
        udiv x14, x12, x16             /* x12 is the number I defined above, bc idk what registers are save to use (e.g. when syscall 64, print, happens, 0-8 are used) */
        msub x13, x14, x16, x12        /* XXX fix unrelated bug */
        sub  x12, x12, x13             /* x13 is what above is digit, x12 is number */
        udiv x12, x12, x16
        add  x13, x13, #48             /* digit to string, possible error source 1 */

        strb w13, [x1]                 /* XXX Store the low byte of x13/w13 at dataToWrite */

        mov  x0,  #1                   /* the print part */
        /* x1 already contains the proper address */
        mov  x2,  #1
        mov  w8,  #64
        svc  #0

        cmp  x12, #0                   /* the loop part */
        beq  exit                      /* genereric exit method I left out */
        b    printNumber

exit:                                  /* XXX */
        ret                            /* XXX */
        /* your code */

缺点是这将使您的函数无法用于信号处理程序、多线程等,因为它们都会尝试使用相同的字节。

其他说明:

  • 当然,数字以相反的顺序打印。那是我猜你以后会做的事情。

  • sub 和第二个 udiv 是不必要的,因为第一个 udiv 已经产生了四舍五入的商(如 Python 的 x14 = x12 // 10)。所以你可以用mov x12, x14替换这两条指令。

  • 您的几个寄存器在整个函数中保持恒定值 (x16, x1, x2, x8),因此您可以在循环之外对其进行初始化,而不是在每次迭代时重复重复。 (注意x0被系统调用的返回值覆盖,所以每次都需要重新初始化。)

  • 真的想找到一种能够使用调试器的方法。否则,请为更多的情况做好准备,在这些情况下,您花费半小时编写 StackOverflow 问题并等待更多小时以获得答案,因为您可以使用调试器在三分钟内发现自己的错误。如果您无法在 Android 设备上安装 gdb,那么请考虑在可能的情况下设置另一个 Linux AArch64 机器(比如 Raspberry Pi 4,或者云服务器,甚至是 qemu 模拟器)。

【讨论】:

    猜你喜欢
    • 2017-03-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-09
    • 2020-06-05
    • 1970-01-01
    • 2019-02-10
    • 1970-01-01
    相关资源
    最近更新 更多