【问题标题】:Assembly , syscall not work as expected. Ubuntu Linux x86_64 , using AT&T syntax组装,系统调用无法按预期工作。 Ubuntu Linux x86_64,使用 AT&T 语法
【发布时间】:2021-04-08 22:35:05
【问题描述】:

我正在测试使用 .bss 分配内存区域以保存单个数字。然后将该号码打印到控制台。输出不如预期。我应该得到 e 数字 (12),但得到一个换行符。

系统配置:

$ uname -a
Linux 5.8.0-48-generic #54~20.04.1-Ubuntu SMP Sat Mar 20 13:40:25 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux

description: CPU
product: Intel(R) Core(TM) i5-8350U CPU @ 1.70GHz

代码:

# compile with: gcc -ggdb -nostdlib -no-pie  test.s -o test

.bss
.lcomm          output,1

.global _start
.text

_start:
        # test .bss and move numer 12 to rbx where memory are allocated in .bss
        mov     $output, %rbx    # rbx to hold address of allocated space
        mov     $12,%rdx          # Move a number to rdx
        mov     %rdx,(%rbx)       # Move content in rdx to the address where rbx points to (e.g ->output)

        # setup for write syscall:  
        mov     $1,%rax          # system call for write, according to syscall table (http://blog.rchapman.org/posts/Linux_System_Call_Table_for_x86_64/)
        mov     $1,%rdi          # fd = 1, stdout
        mov     $output,%rsi     # adress of string to output moved to rsi
        mov     $1,%rdx          # number of bytes to be written

        syscall                  # should write 12 in console

        mov     $60,%rax
        xor     %rdi,%rdi
        syscall                 # exit normally

我已经用第一个系统调用(使用 GDB)设置了一个断点,以查看寄存器:

i r rax rbx rdx rdi rsi

rax            0x1                 1
rbx            0x402000            4202496
rdx            0x1                 1
rdi            0x1                 1
rsi            0x402000            4202496

x/1 0x402000
0x402000 <output>:  12

系统调用后的输出为空白,预计会得到数字“12”:

:~/Dokumenter/ASM/dec$ gcc -ggdb -nostdlib -no-pie  test.s -o test
:~/Dokumenter/ASM/dec$ ./test

:~/Dokumenter/ASM/dec$ ./test

:~/Dokumenter/ASM/dec$ 

所以,我的问题是,是否有任何明显的解释来解释为什么我会变得空白而不是 12?

【问题讨论】:

  • 它写入了字节 12,在 ASCII 中是换页符。如果你想看到12这两个字符,你需要写这两个字符,例如1 是 ASCII 码 49 (0x31)。 write 系统调用不会为您进行二进制到十进制的转换。
  • 顺便说一句,您的mov %rdx,(%rbx) 将 8 个字节写入一个只有 1 空间的变量。您将要了解操作数大小。但是,这三个指令序列可以替换为movb $12, output
  • 谢谢,但即使我将大小增加到 8 (.lcomm output,8),并写入不同的字节长度 (2,4,8) - (mov $8,%rdx),它仍然只是打印一个换行符.. 还简化了测试 movb 的代码,但结果相同.. (movb $12,output) mov $2,%rdx # number of bytes to be written
  • 是的,因为您现在正在写出两个字节 12, 0,它们不会显示为数字 12;它是一个换页符,后跟一个空字符。你必须写出两个字节49, 50。要了解我的意思,请执行movb $49, outputmovb $50, output+1。查看man ascii,看看哪些数值对应哪些字符。
  • 你也可以写movb $'1', output,汇编器会为你计算出正确的ASCII值。

标签: linux assembly gcc system-calls


【解决方案1】:
mov     $output,%rsi     # address of string to output moved to rsi
                                      ^^^^^^

字符串的地址。$12不是字符序列"12"。如果要打印字符串12,则需要将0x310x32'1''2')加载到内存区域(使其足够大)使用2 作为长度.

例如,movw $0x3231, output 或更好的movw $0x3231, output(%rip) 对静态数据使用 RIP 相对寻址,就像 x86-64 的正常情况一样。 (与 NASM 不同,GAS 语法不会将$'12' 作为写入相同整数常量的一种方式。)

如果您想将整数打印为字符串,您可能需要对其进行数学运算,以便一次只处理一个数字。 (Printing an integer as a string with AT&T syntax, with Linux system calls instead of printf)

【讨论】:

  • 谢谢。我得到了它!!每个数字必须使用两个字节:`movb $0x31,output movb $0x32,output+1 and mov $2,%rdx`
  • @MartinHaneferd:您当然可以将两个字节值存储为一个字存储的一部分;看我的编辑。 (记住 x86 是 little-endian)。此外,由于无论如何您都将地址放入寄存器中,因此不妨使用您最终想要用于系统调用的 RSI,而不是单独使用 RBX。 mov $2, %edx 是将一个小的正整数写入 64 位寄存器的更有效方法。并非所有内容都应为 64 位操作数大小,例如 mov %rdx,(%rbx) 是 64 位存储。
  • @Martin,您的评论中没有任何内容涉及我的“让它足够大”的评论(尽管您可能没有评论就这样做了)。如果没有,您可能希望确保.lcomm 分配的不仅仅是一个字节。
  • @paxdiablo ,是的,我将 .lcomm 增加了两个字节。我还发现,通过将数字切成两位数(字节),然后将 0x30 添加到每个数字并放入字节 1 和 2,它就可以工作了。因此,我的问题的主要答案是将常规数字显示为文本/字符串,必须先将其转换为 ascii。所以,再一次,谢谢你的回答。我的头又回到了正轨:-)
猜你喜欢
  • 1970-01-01
  • 2015-03-15
  • 2012-10-14
  • 2015-07-21
  • 2018-01-31
  • 1970-01-01
  • 2014-04-22
  • 1970-01-01
  • 2020-04-16
相关资源
最近更新 更多