Определение переменных в asm - PullRequest
1 голос
/ 05 августа 2020

Допустим ли следующий способ определения переменных в asm?

.globl main
.globl a, b, c, d

a: .byte    4
b: .value   7
c: .long    0x0C    # 11
d: .quad    9

main:
    mov     $0,         %eax
    add     a(%rip),    %eax
    add     b(%rip),    %eax
    add     c(%rip),    %eax
    add     d(%rip),    %eax
    ret

Например, требуется ли / предлагается ли иметь раздел .TEXT? Как именно a(%rip) преобразуется в значение $4? Мне это кажется почти магом c.

1 Ответ

4 голосов
/ 05 августа 2020

Раздел по умолчанию .text; строки перед любой директивой раздела собираются в раздел .text. Так что он у вас есть, и вы фактически помещаете в него все, включая свои данные. (Или константы только для чтения.) Обычно константы stati c следует помещать в .rodata (или .rdata в Windows), а не в .text, по соображениям производительности. (Смешивание кода и данных приводит к потере места в I-cache и D-cache, а также в TLB.)

Он не разрешается сразу в $4 во время сборки, он разрешается в адрес. В этом случае используется режим относительной адресации RIP. См. , что делает "mov offset (% rip),% rax"? / Как работают ссылки на относительные переменные RIP, такие как "[RIP + _a]" в x86-64 GAS Intel-синтаксисе ? для получения дополнительной информации о том, что это означает «адрес символа a относительно RIP», а не на самом деле RIP + абсолютный адрес символа.

В других случаях символ a обычно разрешает свой (32-битный) абсолютный адрес, когда используется как add $a, %rdi или что-то в этом роде.

Только во время выполнения CPU загружает данные (которые вы помещаете туда с такими директивами, как .long) из этого stati c storage . Если вы изменили то, что было в памяти (например, с помощью отладчика или запустив другие инструкции) до выполнения add c(%rip), %eax, оно загрузит другое значение.

Вы поместите свои постоянные данные в .text вместе с код, который обычно не подходит по соображениям производительности. Но это означает, что ассемблер может разрешить относительную адресацию RIP во время сборки вместо того, чтобы использовать только перемещение, которое компоновщик должен заполнить. Хотя кажется, что GAS выбирает не для разрешения ссылок и по-прежнему оставляет его для компоновщик:

$ gcc -c foo.s
$ objdump -drwC -Matt foo.o

foo.o:     file format elf64-x86-64


Disassembly of section .text:

0000000000000000 <a>:
   0:   04                      .byte 0x4

0000000000000001 <b>:
   1:   07                      (bad)  
        ...

0000000000000003 <c>:
   3:   0c 00                   or     $0x0,%al
        ...

0000000000000007 <d>:
   7:   09 00                   or     %eax,(%rax)
   9:   00 00                   add    %al,(%rax)
   b:   00 00                   add    %al,(%rax)
        ...

000000000000000f <main>:
   f:   b8 00 00 00 00          mov    $0x0,%eax
  14:   03 05 00 00 00 00       add    0x0(%rip),%eax        # 1a <main+0xb>    16: R_X86_64_PC32       a-0x4
  1a:   03 05 00 00 00 00       add    0x0(%rip),%eax        # 20 <main+0x11>   1c: R_X86_64_PC32       b-0x4
  20:   03 05 00 00 00 00       add    0x0(%rip),%eax        # 26 <main+0x17>   22: R_X86_64_PC32       c-0x4
  26:   03 05 00 00 00 00       add    0x0(%rip),%eax        # 2c <main+0x1d>   28: R_X86_64_PC32       d-0x4
  2c:   c3                      retq   

(Попытка дизассемблирования ваших данных в виде инструкций происходит из-за того, что вы помещаете их в .text. objdump -d дизассемблирует только .text, а не немедленные константы обычно помещаются в .rodata.)

Связывание его с исполняемым файлом разрешает эти ссылки на символы:

$ gcc -nostdlib -static foo.s    # not a working executable, just link it without extra stuff
$ objdump -drwC -Matt a.out
... (bogus data omitted)

000000000040100f <main>:
  40100f:       b8 00 00 00 00          mov    $0x0,%eax
  401014:       03 05 e6 ff ff ff       add    -0x1a(%rip),%eax        # 401000 <a>
  40101a:       03 05 e1 ff ff ff       add    -0x1f(%rip),%eax        # 401001 <b>
  401020:       03 05 dd ff ff ff       add    -0x23(%rip),%eax        # 401003 <c>
  401026:       03 05 db ff ff ff       add    -0x25(%rip),%eax        # 401007 <d>
  40102c:       c3                      retq   

Обратите внимание на 32-битное кодирование с прямым порядком байтов 2 для относительных смещений в режимах адресации RIP + rel32 . (И комментарий с абсолютным адресом, добавленный objdump для удобства в этом выводе разборки.)

Кстати, у большинства ассемблеров, включая GAS, есть средства макросов, поэтому вы могли бы использовать a = 4 или .equ a, 4, чтобы определить его как константу времени сборки, вместо того, чтобы выводить данные на выходе там. Затем вы использовали бы его как add $a, %eax, который будет собираться в код операции add $sign_extended_imm8, r/m32.

Кроме того, все ваши нагрузки имеют размер двойного слова (определяется операндом регистра), поэтому только 1 из них соответствует размеру используемых вами директив данных. Выполните пошаговое выполнение кода и посмотрите на старшие биты EAX.

Язык ассемблера действительно не имеет переменных. В нем есть инструменты, которые можно использовать для реализации концепции переменных на языке высокого уровня, включая переменные с классом хранения stati c. (Метка и некоторое пространство в .data или .bss. Или .rodata для const «переменных».)

Но если вы используете инструменты по-другому, вы можете делать такие вещи, как загрузка 4 байта которые охватывают .byte, .value (16-битный) и первый байт .long. Итак, после первой инструкции у вас будет EAX + = 0x0c000704 (потому что x86 имеет прямой порядок байтов). Это совершенно законно для записи на ассемблере, и ничего не проверяется, чтобы обеспечить соблюдение концепции переменной, заканчивающейся перед следующей меткой.

(Если вы не используете MASM, у которого есть переменные; в этом случае вы бы пришлось написать add eax, dword ptr [a]; без переопределения размера MASM будет жаловаться на несоответствие между регистром двойного слова и байтовой переменной. Другие варианты синтаксиса asm, такие как NASM и AT&T, предполагают, что вы знаете, что делаете, и не постарайтесь быть "полезным".)

...