Arrays 在程序集中声明和索引qwords的整数数组
我有一个关于如何在程序集中初始化数组的问题。我试过:Arrays 在程序集中声明和索引qwords的整数数组,arrays,assembly,x86-64,att,Arrays,Assembly,X86 64,Att,我有一个关于如何在程序集中初始化数组的问题。我试过: .bss #the array unsigned: .skip 10000 .data #these are the values that I want to put in the array par4: .quad 500 par5: .quad 10 par6: .quad 15 这就是我如何声明我的字符串和我想放入其中的变量。 这就是我如何将它们放入阵列的方法: movq $0 , %r8 movq par4
.bss
#the array
unsigned: .skip 10000
.data
#these are the values that I want to put in the array
par4: .quad 500
par5: .quad 10
par6: .quad 15
这就是我如何声明我的字符串和我想放入其中的变量。
这就是我如何将它们放入阵列的方法:
movq $0 , %r8
movq par4 , %rax
movq %rax , unsigned(%r8)
incq %r8
movq par5 , %rax
movq %rax , unsigned(%r8)
incq %r8
movq par6 , %rax
movq %rax , unsigned(%r8)
我试着打印元素以检查是否一切正常,只有最后一个打印正常,其他两个有一些奇怪的值
也许这不是我应该声明和使用它的方式?首先,
unsigned
是C中类型的名称,因此它对于数组来说是一个糟糕的选择。让我们把它叫做arr
您希望将BSS中的空间块视为数组qword元素。所以每个元素是8个字节因此需要存储到arr+0
、arr+8
和arr+16
(数组的总大小为10000字节,即10000/8个字)
但是您使用的是%r8
作为字节偏移量,而不是缩放索引。这通常是一件好事,其他一切都是一样的;在某些CPU上,索引寻址模式在某些情况下较慢。但问题是,您只能使用inc
将其增加1
,而不是使用添加$8,%r8
因此,您实际上是在存储到arr+0
、arr+1
和arr+2
,8字节的存储相互重叠,只剩下最后一个存储的最低有效字节。x86是小端字节,因此内存的结果内容实际上是这样的,后面是剩余的未写入字节,它们保持为零
# static array that matches what you actually stored
arr: .byte 500 & 0xFF, 10, 15, 0, 0, 0, 0, 0, 0, 0, ...
当然,您可以使用.data
部分中的.qword
来声明包含所需内容的静态数组。但由于只有前3个元素非零,因此将其放入BSS对于这么大的一个元素是有意义的,而不是将操作系统页面置于磁盘的零中
如果要完全展开,而不是从
par4
开始在3元素qword数组上使用循环,则根本不需要增加寄存器。您也不需要将初始值设定项放在数据内存中,您可以使用立即数,因为它们都适合作为32位符号扩展
# these are assemble-time constants, not associated with a section
.equ par4, 500
.equ par5, 10
.equ par6, 15
.text # already the default section but whatever
.globl _start
_start:
movq $par4, arr(%rip) # use RIP-relative addressing when there's no register
movq $par5, arr+8(%rip)
movq $par6, arr+16(%rip)
mov $60, %eax
syscall # Linux exit(0)
.bss
arr: .skip 10000
您可以在GDB下运行它并检查内存以查看您得到了什么。(使用gcc-nostlib-static foo.s编译它)。在GDB中,用starti
启动程序(在入口点停止),然后用si
单步执行。使用x/4g&arr
将arr
处的内存内容转储为4个qwords的数组
或者,如果您确实想使用寄存器,也可以只循环指针而不是索引
lea arr(%rip), %rdi # or mov $arr, %edi in a non-PIE executable
movq $par4, (%rdi)
add $8, %rdi # advance the pointer 8 bytes = 1 element
movq $par5, (%rdi)
add $8, %rdi
movq $par6, (%rdi)
或按比例索引:
## Scaled-index addressing
movq $par4, arr(%rip)
mov $1, %eax
movq $par5, arr(,%rax,8) # [arr + rax*8]
inc %eax
movq $par6, arr(,%rax,8)
有趣的技巧:你可以只做一个字节存储而不是qword存储来设置低位字节,剩下的保持为零。这将节省代码大小,但如果您立即加载qword,您将得到一个商店转发暂停。(~10个周期的额外延迟,用于存储/重新加载将缓存中的数据与存储缓冲区中的存储合并)
或者如果您仍然希望从
.rodata
中的par4
复制24个字节,则可以使用SSE。x86-64保证SSE2可用
movaps par4(%rip), %xmm0
movaps %xmm0, arr(%rip) # copy par4 and par5
mov par6(%rip), %rax # aka par4+16
mov %rax, arr+16(%rip)
.section .rodata # read-only data.
.p2align 4 # align by 2^4 = 16 for movaps
par4: .quad 500
par5: .quad 10
par6: .quad 15
.bss
.p2align 4 # align by 16 for movaps
arr: .skip 10000
# or use .lcomm arr, 10000 without even switching to .bss
或者使用SSE4.1,您可以加载并扩展小常量,这样就不需要为要复制到BSS阵列中的每个小数字使用一个完整的qword
movzxwq initializers(%rip), %xmm0 # zero-extend 2 words into 2 qwords
movaps %xmm0, arr(%rip)
movzwl initializers+4(%rip), %eax # zero-extending word load
mov %rax, arr+16(%rip)
.section .rodata
initializers: .word 500, 10, 15
您正在使用
%r8
作为字节偏移量,而incq%r8
只将其提前1个字节,而不是8个字节。您需要缩放索引寻址模式或添加$8,%r8
。此外,您还可以通过将数组放入.data
而不是bss来静态初始化数组,如从par4
开始的3元素数组。或者您可以使用.eq
常量并将mov immediate放入内存。这里也不需要增加指针,只需存储到unsigned
、unsigned+8
和unsigned+16
。此外,unsigned
将是一个不方便的符号名称选择,如果您想从C访问数组。unsigned
是一个关键字,因此您不能声明外部uint64\u unsigned>[1000/8]
。(我找不到一个合适的规范问答,所以我决定写一个,因为这个问题很短,格式很好,没有其他干扰因素。不过,我知道我已经多次看到这个错误。特别是在英特尔语法中,寻址与扩展索引的C数组索引具有相同的外观。)