Объявление и индексирование целочисленного массива qwords в сборке

У меня вопрос, как инициализировать массив в сборке. Я попытался:

.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)

Я попытался распечатать элементы, чтобы проверить, все ли в порядке, и только последний выводится нормально, у двух других есть какие-то странные значения.

Может быть, я не должен так декларировать и работать с этим?

1 ответ

Решение

Прежде всего, unsigned- это имя типа в C, поэтому это плохой выбор для массива. Назовем этоarr вместо.

Вы хотите рассматривать этот блок пространства в BSS как элементы массива qword. Таким образом, каждый элемент составляет 8 байтов. Значит вам нужно хранить доarr+0, arr+8, а также arr+16. (Общий размер вашего массива составляет 10000 байт, что составляет 10000/8 qwords).

Но вы используете %r8как байтовое смещение, а не масштабированный индекс. В целом это хорошо, при прочих равных; режимы индексированной адресации в некоторых случаях медленнее на некоторых процессорах. Но проблема в том, что вы увеличиваете его только на1 с участием inc, не со add $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, ...

Конечно, вы могли бы просто использовать .qword в .dataраздел, чтобы объявить статический массив с желаемым содержимым. Но поскольку только первые 3 элемента не равны нулю, размещение его в BSS имеет смысл для одного такого большого, вместо того, чтобы иметь страницу ОС с нулями с диска.


Если вы собираетесь полностью развернуть вместо того, чтобы использовать цикл над 3-элементным массивом qword, начиная с par4, вам вообще не нужно увеличивать регистр. Вам также не нужно, чтобы инициализаторы находились в памяти данных, вы можете просто использовать немедленно, потому что все они подходят как 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). В GDB запустите программу сstarti (чтобы остановиться в точке входа), затем пошагово с si. Использоватьx /4g &arr сбросить содержимое памяти на arr как массив из 4-х слов.

Или, если вы действительно хотите использовать регистр, можно просто зациклить указатель вместо индекса.

    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 циклов дополнительной задержки для сохранения / перезагрузки для слияния данных из кеша с хранилищем из буфера хранилища)


Или, если вы все еще хотите скопировать 24 байта изpar4 в .rodata, вы можете использовать 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 вы можете загружать + расширять небольшие константы, поэтому вам не нужно целое qword для каждого небольшого числа, которое вы собираетесь скопировать в массив BSS.

    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
Другие вопросы по тегам