在汇编中声明和索引整型 qwords 数组
Declaring and indexing an integer array of qwords in assembly
我有一个关于如何在程序集中初始化数组的问题。我试过了:
.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 [=11=] , %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 中的 space 块视为数组 qword 元素。所以每个元素都是8个字节。 所以你需要存储到arr+0
,arr+8
,和arr+16
.(你的数组总大小是10000字节,也就是10000/ 8 个 qwords).
但是您使用 %r8
作为字节偏移量,而不是缩放索引。这通常是一件好事,其他条件相同;在某些情况下,索引寻址模式在某些 CPU 上速度较慢。但问题是你只增加 1
和 inc
,而不是 add , %r8
.
所以 你实际上存储到 arr+0
、arr+1
和 arr+2
,8 字节存储相互重叠 , 只留下最后一个存储的最低有效字节。 x86 是 little-endian 所以内存的结果内容实际上是这个,然后是其余未写入的字节保持零。
# 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 中对于那么大的元素是有意义的,而不是将 OS 页面放在磁盘的零中。
如果您要完全展开而不是对从 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 , %eax
syscall # Linux exit(0)
.bss
arr: .skip 10000
您可以 运行 在 GDB 下检查内存以查看您得到了什么。 (用 gcc -nostdlib -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 , %rdi # advance the pointer 8 bytes = 1 element
movq $par5, (%rdi)
add , %rdi
movq $par6, (%rdi)
或缩放索引:
## Scaled-index addressing
movq $par4, arr(%rip)
mov , %eax
movq $par5, arr(,%rax,8) # [arr + rax*8]
inc %eax
movq $par6, arr(,%rax,8)
有趣的技巧:您可以只进行字节存储而不是 qword 存储来设置低字节,并将其余部分保留为零。这将节省代码大小,但如果您立即执行 qword 加载,则会出现存储转发停顿。 (store/reload 将来自缓存的数据与来自存储缓冲区的存储合并的额外延迟约 10 个周期)
或者如果您做了仍然想从par4
复制24个字节到.rodata
,您可以使用上海证交所。 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
我有一个关于如何在程序集中初始化数组的问题。我试过了:
.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 [=11=] , %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 中的 space 块视为数组 qword 元素。所以每个元素都是8个字节。 所以你需要存储到arr+0
,arr+8
,和arr+16
.(你的数组总大小是10000字节,也就是10000/ 8 个 qwords).
但是您使用 %r8
作为字节偏移量,而不是缩放索引。这通常是一件好事,其他条件相同;在某些情况下,索引寻址模式在某些 CPU 上速度较慢。但问题是你只增加 1
和 inc
,而不是 add , %r8
.
所以 你实际上存储到 arr+0
、arr+1
和 arr+2
,8 字节存储相互重叠 , 只留下最后一个存储的最低有效字节。 x86 是 little-endian 所以内存的结果内容实际上是这个,然后是其余未写入的字节保持零。
# 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 中对于那么大的元素是有意义的,而不是将 OS 页面放在磁盘的零中。
如果您要完全展开而不是对从 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 , %eax
syscall # Linux exit(0)
.bss
arr: .skip 10000
您可以 运行 在 GDB 下检查内存以查看您得到了什么。 (用 gcc -nostdlib -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 , %rdi # advance the pointer 8 bytes = 1 element
movq $par5, (%rdi)
add , %rdi
movq $par6, (%rdi)
或缩放索引:
## Scaled-index addressing
movq $par4, arr(%rip)
mov , %eax
movq $par5, arr(,%rax,8) # [arr + rax*8]
inc %eax
movq $par6, arr(,%rax,8)
有趣的技巧:您可以只进行字节存储而不是 qword 存储来设置低字节,并将其余部分保留为零。这将节省代码大小,但如果您立即执行 qword 加载,则会出现存储转发停顿。 (store/reload 将来自缓存的数据与来自存储缓冲区的存储合并的额外延迟约 10 个周期)
或者如果您做了仍然想从par4
复制24个字节到.rodata
,您可以使用上海证交所。 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