Как получить `mov rdx, symbol` для перемещения значения символа, а не значения по адресу символа в clang intel-syntax? - PullRequest
1 голос
/ 11 ноября 2019

У меня есть следующий код, который я использую с clang на macOS:

.intel_syntax noprefix

.data

hello:  .ascii  "Hello world\n"
hello_len = . - hello

.text

.globl  _main

_main:
        mov     rax, 0x2000004
        mov     rdi, 1
        lea     rsi, [rip + hello]
        mov     rdx, hello_len       # <-------
        syscall

        mov     rax, 0x2000001
        syscall

Хотя похоже, что он должен напечатать «Hello World» и выйти, на самом деле это segfaults. Оказывается, это потому, что mov rdx, hello_len на самом деле пытается переместить значение по адресу hello_len, а не само значение hello_len.

Если бы я использовал синтаксис AT & T, строка была бы movq $hello_len, %rdxкоторый работает правильно. Что эквивалентно в версии clang синтаксиса GAS Intel?

Ответы [ 2 ]

2 голосов
/ 11 ноября 2019

С реальным GAS (в Linux) ваш код собирается в mov rdx, sign_extended_imm32 так, как вы хотите.

Но, к сожалению, clang собирает его в mov rdx, [0xc]. Это может быть или не быть ошибкой, но это определенно несовместимость. (Команда MacOS gcc вообще не является коллекцией компиляторов GNU, это Apple Clang: бэкэнд LLVM, внешний интерфейс clang, абсолютно никакого отношения к проекту GNU.)

OFFSET hello_len непохоже на работу . (Я неправильно предположил, что это будет на первый взгляд, но clang не поддерживает оператор OFFSET; он .intel_syntax не полностью пригоден для использования.)


Clang не может даже собрать егособственный .intel_syntax noprefix вывод.
Не может быть способа заставить лягушку синтаксиса Intel использовать значение (адрес) символа в качестве немедленного.

// hello.c
char hello[] = "abcdef";
char *foo() { return hello; }

clang -S print mov edi, offset hello который не будет собираться с помощью встроенного ассемблера clang! https://godbolt.org/z/x7vmm4.

$ clang -fno-pie -O1 -S -masm=intel hello.c
$ clang -c hello.s
hello.s:10:18: error: cannot use more than one symbol in memory operand
        mov     eax, offset hello
                            ^
$ clang --version
clang version 8.0.1 (tags/RELEASE_801/final)
Target: x86_64-pc-linux-gnu
   ...

IMO, это ошибка, вы должны сообщить об этом на https://bugs.llvm.org

от clang (исполняемые файлы Linux без PIE могут использовать статические преимуществаадреса находятся в младших 32 битах виртуального адресного пространства, используя mov r32, imm32 вместо REA-относительного LEA. И, конечно, не mov r64, imm64.)


Обходные пути: вы не можете просто использоватьС препроцессором. . - hello является контекстно-зависимым;он имеет другое значение, когда . - это другая позиция. Таким образом, подстановка текста не будет работать.

Ugly Обходной путь: переключитесь на .att_syntax и обратно:

Переключитесь на .att_syntax и обратно на mov $hello_len, %edx

Uglyи неэффективный обходной путь: lea

Это не сработает для 64-битных констант, но вы можете использовать lea для помещения адреса символа в регистр.

К сожалению, clang / LLVMвсегда использует режим адресации disp32, даже для регистра + маленькая константа, когда маленькая константа является именованным символом. Я предполагаю, что это действительно обрабатывает его как адрес, который может иметь перемещение.

С учетом этого источника:

##  your .rodata and  =  or .equ symbol definitions

_main:
        mov     eax, 0x2000004             # optimized from RAX
        mov     edi, 1
        lea     rsi, [rip + hello]
        mov     edx, hello_len             # load
        lea     edx, [hello_len]           # absolute disp32
        lea     edx, [rdi-1 + hello_len]   # reg + disp8 hopefully
#       mov     esi, offset hello          # clang chokes.
#        mov     rdx, OFFSET FLAT hello_len       # clang still chokes
.att_syntax
       lea    -1+hello_len(%rdi), %edx
       lea    -1+12(%rdi), %edx
       mov    $hello_len, %edx
.intel_syntax noprefix
        syscall

        mov     rax, 0x2000001
        syscall

clang собирает его в этот машинный код, разобранный objdump -drwC -Mintel. Обратите внимание, что LEA необходим ModRM + SIB для кодирования 32-битного режима абсолютной адресации в 64-битном коде.

   0:   b8 04 00 00 02          mov    eax,0x2000004       # efficient 5-byte mov r32, imm32
   5:   bf 01 00 00 00          mov    edi,0x1
                                                            # RIP-relative LEA
   a:   48 8d 35 00 00 00 00    lea    rsi,[rip+0x0]        # 11 <_main+0x11>   d: R_X86_64_PC32        .data-0x4

  11:   8b 14 25 0c 00 00 00    mov    edx,DWORD PTR ds:0xc   # the load we didn't want
  18:   8d 14 25 0c 00 00 00    lea    edx,ds:0xc             # LEA from the same [disp32] addressing mode.
  1f:   8d 97 0b 00 00 00       lea    edx,[rdi+0xb]          # [rdi+disp32] addressing mode, missed optimization to disp8
  25:   8d 97 0b 00 00 00       lea    edx,[rdi+0xb]          # AT&T lea    -1+hello_len(%rdi), %edx same problem
  2b:   8d 57 0b                lea    edx,[rdi+0xb]          # AT&T with lea hard-coded -1+12(%rdi)
  2e:   ba 0c 00 00 00          mov    edx,0xc                # AT&T mov    $hello_len, %edx

  33:   0f 05                   syscall 
  35:   48 c7 c0 01 00 00 02    mov    rax,0x2000001          # inefficient mov r64, sign_extended_imm32 from your source
  3c:   0f 05                   syscall 

GAS, собирающий один и тот же источник, делает 8d 57 0b lea edx,[rdi+0xb] для lea edx, [rdi-1 + hello_len] версии.

См. https://codegolf.stackexchange.com/questions/132981/tips-for-golfing-in-x86-x64-machine-code/132985#132985 - LEA из регистра с известной константой является выигрышем для размера кода с близкими / маленькими константами и на самом деле хорош для производительности . (Пока известная константа получилась таким образом без зависимости от длинной цепочки вычислений).

Но, как вы можете видеть, clang не может оптимизировать это и все еще использует режим адресации reg + disp32, даже когдасмещение будет соответствовать дисплею8. Это все еще немного лучший размер кода, чем [abs disp32], для которого требуется байт SIB;без байта SIB эта кодировка означает [RIP + rel32].

1 голос
/ 11 ноября 2019

Если вы измените свой код операции на:

lea rax, hello_len

, это сработает. В старом unix as, = или более многословном .set, оперируемом lvalue. В этой реальности hello_len - это адрес;в частности, адрес 12.

Я не могу вспомнить = в синтаксисе masm. Я помню, что equ служил аналогичной цели, но все это было плохо указано. В основном мы использовали cpp (и иногда awk), чтобы поднять нас, и избежали особенностей асма.

...