Что делает cltq в сборке? - PullRequest
       156

Что делает cltq в сборке?

23 голосов
/ 02 июля 2011
0x0000000000400553 <main+59>:   mov    -0x4(%rbp),%eax
0x0000000000400556 <main+62>:   cltq   
0x0000000000400558 <main+64>:   shl    $0x3,%rax
0x000000000040055c <main+68>:   mov    %rax,%rdx

На самом деле моя программа проста:

5   int main(int argc, char *argv[]) { 
6     int i = 0;
7     while(environ[i]) {
8       printf("%s\n", environ[i++]);
9     }
10    return 0;

Но вывод сборки довольно длинный:

Dump of assembler code for function main:
0x0000000000400518 <main+0>:    push   %rbp
0x0000000000400519 <main+1>:    mov    %rsp,%rbp
0x000000000040051c <main+4>:    sub    $0x20,%rsp
0x0000000000400520 <main+8>:    mov    %edi,-0x14(%rbp)
0x0000000000400523 <main+11>:   mov    %rsi,-0x20(%rbp)
0x0000000000400527 <main+15>:   movl   $0x0,-0x4(%rbp)
0x000000000040052e <main+22>:   jmp    0x400553 <main+59>
0x0000000000400530 <main+24>:   mov    -0x4(%rbp),%eax
0x0000000000400533 <main+27>:   cltq   
0x0000000000400535 <main+29>:   shl    $0x3,%rax
0x0000000000400539 <main+33>:   mov    %rax,%rdx
0x000000000040053c <main+36>:   mov    0x2003e5(%rip),%rax        # 0x600928 <environ@@GLIBC_2.2.5>
0x0000000000400543 <main+43>:   lea    (%rdx,%rax,1),%rax
0x0000000000400547 <main+47>:   mov    (%rax),%rdi
0x000000000040054a <main+50>:   addl   $0x1,-0x4(%rbp)
0x000000000040054e <main+54>:   callq  0x400418 <puts@plt>
0x0000000000400553 <main+59>:   mov    -0x4(%rbp),%eax
0x0000000000400556 <main+62>:   cltq   
0x0000000000400558 <main+64>:   shl    $0x3,%rax
0x000000000040055c <main+68>:   mov    %rax,%rdx
0x000000000040055f <main+71>:   mov    0x2003c2(%rip),%rax        # 0x600928 <environ@@GLIBC_2.2.5>
0x0000000000400566 <main+78>:   lea    (%rdx,%rax,1),%rax
0x000000000040056a <main+82>:   mov    (%rax),%rax
0x000000000040056d <main+85>:   test   %rax,%rax
0x0000000000400570 <main+88>:   jne    0x400530 <main+24>
0x0000000000400572 <main+90>:   mov    $0x0,%eax
0x0000000000400577 <main+95>:   leaveq 
0x0000000000400578 <main+96>:   retq   
End of assembler dump.

Чего я не понимаю, так это блока:

0x000000000040052e <main+22>:   jmp    0x400553 <main+59>
0x0000000000400530 <main+24>:   mov    -0x4(%rbp),%eax
0x0000000000400533 <main+27>:   cltq   
0x0000000000400535 <main+29>:   shl    $0x3,%rax
0x0000000000400539 <main+33>:   mov    %rax,%rdx
0x000000000040053c <main+36>:   mov    0x2003e5(%rip),%rax        # 0x600928 <environ@@GLIBC_2.2.5>
0x0000000000400543 <main+43>:   lea    (%rdx,%rax,1),%rax
0x0000000000400547 <main+47>:   mov    (%rax),%rdi
0x000000000040054a <main+50>:   addl   $0x1,-0x4(%rbp)
0x000000000040054e <main+54>:   callq  0x400418 <puts@plt>
0x0000000000400553 <main+59>:   mov    -0x4(%rbp),%eax
0x0000000000400556 <main+62>:   cltq   
0x0000000000400558 <main+64>:   shl    $0x3,%rax
0x000000000040055c <main+68>:   mov    %rax,%rdx
0x000000000040055f <main+71>:   mov    0x2003c2(%rip),%rax        # 0x600928 <environ@@GLIBC_2.2.5>
0x0000000000400566 <main+78>:   lea    (%rdx,%rax,1),%rax
0x000000000040056a <main+82>:   mov    (%rax),%rax
0x000000000040056d <main+85>:   test   %rax,%rax
0x0000000000400570 <main+88>:   jne    0x400530 <main+24>

Ответы [ 4 ]

23 голосов

Мнемоника

cltq - мнемоника gas для Intel cdqe, как указано в: https://sourceware.org/binutils/docs/as/i386_002dMnemonics.html

Мнемоники:

  • Преобразование длинных в квад (cltq): стиль AT & T
  • Преобразование удвоения в четырехкратное расширение (cdqe): Intel

Терминология:

  • quad (он же quad-word) == 8 байт
  • длинный (AT & T) == двойное слово (Intel) == 4 байта

Это одна из немногих инструкций, чье имя GAS сильно отличается от версии Intel. as допускает использование мнемоники, но ассемблеры Intel-синтаксиса, такие как NASM, могут принимать только имена Intel.

Эффект

Знак расширяет 4 байта в 8 байтов, что в дополнении 2 означает, что для:

  • отрицательных чисел, биты старших 4 байтов должны быть установлены в 1
  • положительные числа, они должны быть установлены в 0

В C это обычно представляет приведение от int до long.

* * Пример тысяча сорок-девять:
mov $0123456700000001, %rax  # eax=1, high bytes of rax=garbage
cltq
# %rax == $0000 0000 0000 0001

mov $-1, %eax   # %rax = 0000 0000 FFFF FFFF
cltq
# %rax == $FFFF FFFF FFFF FFFF == qword $-1

Эта инструкция доступна только для 64-разрядных систем.

Также рассмотрите следующие инструкции:

  • CWDE (AT & T CWTL), CBW (AT & T CBTW): меньшие версии CDQE, также присутствующие в 32-битном
  • CQO семейство, знак которого распространяется RAX в RDX:RAX
  • MOVSX семейство, знак которого расширяется и перемещается: что делает инструкция movsbl?

Минимальные запускаемые примеры на GitHub с утверждениями:

C пример

GCC 4.9.3 испускает:

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv) {
    int i = strtol(argv[1], (char **)NULL, 16);;
    long int l = i;
    printf("%lx\n", l);
}

Компилировать и разбирать:

gcc -ggdb3 -std=c99 -O0 a.c
objdump -S a.out

содержит:

    int main(int argc, char **argv) {
  ...
    long int l2 = i;
  400545:       8b 45 fc                mov    -0x4(%rbp),%eax
  400548:       48 98                   cltq   
  40054a:       48 89 45 f0             mov    %rax,-0x10(%rbp)

и поведение:

$ ./a.out 0x80000000
ffffffff80000000
$ ./a.out 0x40000000
40000000
20 голосов
/ 02 июля 2011

cltq продвигает int к int64. shl 3,% rax смещает 64-битный указатель (умножает все, что есть в rax, на 8). код выполняет цикл по списку указателей на переменные окружения. когда он находит нулевое значение, это конец, и он выпадает из цикла.

Вот изображение того, как Linux хранит переменные среды в ОЗУ над стеком. Вы увидите указатели, начинающиеся с 0xbffff75c; это указывает на 0xbffff893, "TERM = rxvt".

jcomeau@intrepid:/tmp$ gdb test
GNU gdb (GDB) 7.2-debian
Copyright (C) 2010 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "i486-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /tmp/test...(no debugging symbols found)...done.
(gdb) break main
Breakpoint 1 at 0x80483e7
(gdb) run
Starting program: /tmp/test 

Breakpoint 1, 0x080483e7 in main ()
(gdb) info reg
eax            0xbffff754   -1073744044
ecx            0xe88ed1c    243854620
edx            0x1  1
ebx            0xb7fc5ff4   -1208197132
esp            0xbffff6a8   0xbffff6a8
ebp            0xbffff6a8   0xbffff6a8
esi            0x0  0
edi            0x0  0
eip            0x80483e7    0x80483e7 <main+3>
eflags         0x200246 [ PF ZF IF ID ]
cs             0x73 115
ss             0x7b 123
ds             0x7b 123
es             0x7b 123
fs             0x0  0
gs             0x33 51
(gdb) x/160x 0xbffff6a8
0xbffff6a8: 0xbffff728  0xb7e86e46  0x00000001  0xbffff754
0xbffff6b8: 0xbffff75c  0xb7fe2940  0xb7ff7351  0xffffffff
0xbffff6c8: 0xb7ffeff4  0x08048254  0x00000001  0xbffff710
0xbffff6d8: 0xb7ff0976  0xb7fffac0  0xb7fe2c38  0xb7fc5ff4
0xbffff6e8: 0x00000000  0x00000000  0xbffff728  0x21b99b0c
0xbffff6f8: 0x0e88ed1c  0x00000000  0x00000000  0x00000000
0xbffff708: 0x00000001  0x08048330  0x00000000  0xb7ff64f0
0xbffff718: 0xb7e86d6b  0xb7ffeff4  0x00000001  0x08048330
0xbffff728: 0x00000000  0x08048351  0x080483e4  0x00000001
0xbffff738: 0xbffff754  0x08048440  0x08048430  0xb7ff12f0
0xbffff748: 0xbffff74c  0xb7fff908  0x00000001  0xbffff889
0xbffff758: 0x00000000  0xbffff893  0xbffff89d  0xbffff8ad
0xbffff768: 0xbffff8fd  0xbffff90c  0xbffff91c  0xbffff92d
0xbffff778: 0xbffff93a  0xbffff94d  0xbffff97a  0xbffffe6a
0xbffff788: 0xbffffe75  0xbffffef7  0xbfffff0e  0xbfffff1d
0xbffff798: 0xbfffff26  0xbfffff30  0xbfffff41  0xbfffff6a
0xbffff7a8: 0xbfffff73  0xbfffff8a  0xbfffff9d  0xbfffffa5
0xbffff7b8: 0xbfffffbc  0xbfffffcc  0xbfffffdf  0x00000000
0xbffff7c8: 0x00000020  0xffffe420  0x00000021  0xffffe000
0xbffff7d8: 0x00000010  0x078bfbff  0x00000006  0x00001000
0xbffff7e8: 0x00000011  0x00000064  0x00000003  0x08048034
0xbffff7f8: 0x00000004  0x00000020  0x00000005  0x00000008
0xbffff808: 0x00000007  0xb7fe3000  0x00000008  0x00000000
---Type <return> to continue, or q <return> to quit---
0xbffff818: 0x00000009  0x08048330  0x0000000b  0x000003e8
0xbffff828: 0x0000000c  0x000003e8  0x0000000d  0x000003e8
0xbffff838: 0x0000000e  0x000003e8  0x00000017  0x00000000
0xbffff848: 0x00000019  0xbffff86b  0x0000001f  0xbffffff2
0xbffff858: 0x0000000f  0xbffff87b  0x00000000  0x00000000
0xbffff868: 0x50000000  0x7d410985  0x1539ef2a  0x7a3f5e9a
0xbffff878: 0x6964fe17  0x00363836  0x00000000  0x00000000
0xbffff888: 0x6d742f00  0x65742f70  0x54007473  0x3d4d5245
0xbffff898: 0x74767872  0x45485300  0x2f3d4c4c  0x2f6e6962
0xbffff8a8: 0x68736162  0x47445800  0x5345535f  0x4e4f4953
0xbffff8b8: 0x4f4f435f  0x3d45494b  0x37303534  0x66656135
0xbffff8c8: 0x32353131  0x63346334  0x30393436  0x35386331
0xbffff8d8: 0x39346134  0x37316135  0x3033312d  0x31383339
0xbffff8e8: 0x2e303736  0x31303832  0x382d3033  0x33323731
0xbffff8f8: 0x39373936  0x53494800  0x5a495354  0x30313d45
0xbffff908: 0x00303030  0x48535548  0x49474f4c  0x41463d4e
0xbffff918: 0x0045534c  0x444e4957  0x4449574f  0x3833383d
(gdb) x/20s 0xbffff888
0xbffff888:  ""
0xbffff889:  "/tmp/test"
0xbffff893:  "TERM=rxvt"
0xbffff89d:  "SHELL=/bin/bash"
0xbffff8ad:  "XDG_SESSION_COOKIE=45075aef11524c4c64901c854a495a17-1309381670.280130-817236979"
0xbffff8fd:  "HISTSIZE=10000"
0xbffff90c:  "HUSHLOGIN=FALSE"
0xbffff91c:  "WINDOWID=8388614"
0xbffff92d:  "USER=jcomeau"
0xbffff93a:  "HISTFILESIZE=10000"
0xbffff94d:  "LD_LIBRARY_PATH=/usr/src/jet/lib/x86/shared:"
0xbffff97a:  "LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arj=01;31"...
0xbffffa42:  ":*.taz=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lz=01;31:*.xz=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.d"...
0xbffffb0a:  "eb=01;31:*.rpm=01;31:*.jar=01;31:*.rar=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35"...
0xbffffbd2:  ":*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=---Type <return> to continue, or q <return> to quit---
01;35:*.m2v=01;35:*.mkv=01;35:*.ogm=01;35:*.mp4=01;35:*.m4"...
0xbffffc9a:  "v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*."...
0xbffffd62:  "yuv=01;35:*.cgm=01;35:*.emf=01;35:*.axv=01;35:*.anx=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=00;36:*.au=00;36:*.flac=00;36:*.mid=00;36:*.midi=00;36:*.mka=00;36:*.mp3=00;36:*.mpc=00;36:*.ogg=00;36:*.ra=00;3"...
0xbffffe2a:  "6:*.wav=00;36:*.axa=00;36:*.oga=00;36:*.spx=00;36:*.xspf=00;36:"
0xbffffe6a:  "COLUMNS=80"
0xbffffe75:  "PATH=/usr/src/jet/bin:/usr/local/bin:/usr/bin:/bin:/usr/games:/home/jcomeau:/home/jcomeau/bin:/home/jcomeau/src:/sbin:/usr/sbin:."
(gdb) quit
A debugging session is active.

    Inferior 1 [process 10880] will be killed.

Quit anyway? (y or n) y

Ваш компилятор достаточно умен, чтобы оптимизировать просто отформатированный printf до puts. извлечение строки окружения и постинкремент i прямо в коде. Если вы не поймете это самостоятельно, вы никогда этого не поймете. Просто «будьте» компьютером и выполните цикл, используя данные, которые я выложил для вас с помощью gdb, и вам все это станет ясно.

7 голосов
/ 23 мая 2012

Если ваша ОС 64-битная, если вы не объявляете функцию, находящуюся в другом файле, но хотите использовать ее в этом файле. GCC по умолчанию будет считать эту функцию 32-битной. Таким образом, cltq будет использовать только младшие 32 бита RAX (возвращаемое значение), старшие 32 бита будут заполнены 1 или 0. надеюсь, эта сеть поможет вам http://www.mystone7.com/2012/05/23/cltq/

6 голосов
/ 29 июля 2017

cltq знак расширяет EAX в RAX.Это краткая форма movslq %eax, %rax, сохраняющая байты кода.Он существует из-за того, что x86-64 эволюционировал с 8086 до 386 до AMD64.

Он копирует знаковый бит EAX во все старшие биты расширенного регистра, потому что так работает дополнение 2.Мнемоника - сокращение от Convert Long to Quad.


Синтаксис AT & T (используется GNU as / objdump) использует некоторые мнемоники, нежели Intel, для некоторых инструкций (см. Официальные документы )1011 *).Вы можете использовать objdump -drwC -Mintel или gcc -masm=intel -S, чтобы получить синтаксис Intel с использованием мнемоники, которую Intel и AMD описывают в своих руководствах по инструкциям (см. Ссылки в вики-теге .факт: в качестве входных данных газ принимает либо мнемонику в любом режиме).

machine    mnemonics:                MOVSX equivalent
code         AT&T    Intel           AT&T               Intel

 66 98       cbtw    cbw             movsbw %al,%ax     movsx  ax,al
 98          cwtl    cwde            movswl %ax,%eax    movsx  eax,ax
 48 98       cltq    cdqe            movslq %eax,%rax   movsxd rax,eax

Intel insn ref ручной ввод для этих 3 insns .

cltq / cdqe, очевидно, доступен только в 64-битном режиме, но два других доступны во всех режимах. movsx и movzx были введены только с 386, что позволяет легко / эффективно подписывать / обнулятьрасширять регистры, отличные от al / ax, или подписывать / обнулять расширение на лету во время загрузки.

Думайте о cltq / cdqe как о специальном случае более короткого кодирования movslq %eax,%raxОн работает так же быстро. Но единственным преимуществом является сохранение пары байт кода, поэтому не стоит жертвовать чем-то другим, чтобы использовать его вместо movsxd / movzx.


Связанная группа инструкций копирует бит знака [e / r] ax во все биты [e / r] dИкс. Расширение знака eax в edx:eax полезно перед idiv или просто перед возвратом широкого целого числа в паре регистров.

             AT&T   /  Intel  mnemonic                 effect
 66 99       cwtd      cwd     word->doubleword        dx = signbit(ax)
 99          cltd      cdq     doubleword->quadword   edx = signbit(eax)
 48 99       cqto      cqo     quadword->octword      rdx = signbit(rax)

У них нет ни одногоэквивалентный инструкции, но вы можете сделать их в двух инструкциях: например, mov %eax, %edx / sar $32, %edx.


Вспоминая мнемонику

Мнемоника Intel для расширенияв пределах rax все оканчивается на e, за исключением исходного 8086 cbw.Вы можете помнить этот случай, потому что даже 8086 обрабатывали 16-битные целые числа в одном регистре, поэтому нет необходимости устанавливать dl в знаковый бит al.div r8 и idiv r8 читать дивиденды от ax, а не от dl:al.Так что cbw sign-extends al в ax.

Мнемоника AT & T не имеет очевидного намека на то, чтобы помочь вам вспомнить, какой из них есть какой.Некоторые из тех, которые пишут в *dx, заканчиваются d (для dx?) Вместо обычного l для long.cqto нарушает этот шаблон, но октавное число равно 128b и, следовательно, должно быть объединением rdx:rax.

IMO мнемонику Intel легче запомнить, а синтаксис Intel легче читать в целом.(Сначала я выучил синтаксис AT & T, но привык к Intel, потому что чтение руководств Intel / AMD полезно!)


Обратите внимание, что для нулевого расширения, mov %edi,%edi нулевого расширения%edi в %rdi, поскольку любая запись в 32-битный регистр обнуляет старшие 32 бита .(Но на практике попробуйте mov в другой регистр (например, mov %eax, %ecx), потому что тот же самый побеждает mov-elission в процессорах Intel . Вы часто будете видеть сгенерированный компилятором asm для функций с 32-битные аргументы без знака используют mov для расширения нуля и, к сожалению, часто с тем же регистром, что и src и destination.

Для 8 или 16 из 32 (или неявно 64), and $0xff, %eax работает кака также movzbl %al, %eax. (Или лучше movzbl %al, %ecx, так что mov-elission может сделать нулевую задержку на процессорах, где movzx).

...