在汇编中制作整数数组

问题描述 投票:0回答:1

所以我有一个关于如何在程序集中创建数组的问题。所以我尝试了:

.bss
#the array
unsigned:    .skip 10000
.data
#this 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)

并且我尝试打印这些元素以检查一切是否正常,只有最后一个可以正常打印,其他两个具有一些奇怪的值。也许这不是我应该声明和使用的方式。对不起,菜鸟的错误,我还在学习。 :)

assembly x86-64 att
1个回答
0
投票

首先,unsigned是C中类型的名称,因此对于数组而言,这是一个糟糕的选择。让我们叫它arr

[您希望将BSS中的该空间块视为qword元素数组。因此,每个元素为8个字节。 因此您需要存储到arr+0arr+8arr+16

但是您将%r8用作字节偏移量,而不是缩放索引。在其他所有条件相同的情况下,这通常是一件好事。在某些情况下,某些CPU上的索引寻址模式较慢。但是问题是您只能通过1将其增加inc,而不是使用add $8, %r8

所以您实际上是存储到arr+0arr+1arr+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, ...

您当然可以只使用.qword部分中的.data声明一个包含所需内容的静态数组。但是,只有前三个元素不为零时,将它放在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 $60, %eax
    syscall               # Linux exit(0)

.bss
    arr:   .skip 10000

您可以在GDB下运行它,并检查内存以查看得到的结果。 (用gcc -nostdlib -static foo.s编译)

或者,如果您确实想使用寄存器,则最好只循环指针而不是索引。

    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个周期额外的延迟)


或者,如果您did仍要从par4中的.rodata复制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
© www.soinside.com 2019 - 2024. All rights reserved.