Что делает cltq в сборке?
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 ответа
мнемонический
cltq
это gas
мнемоника для Intel cdqe
как задокументировано по адресу: https://sourceware.org/binutils/docs/as/i386_002dMnemonics.html
Мнемоника это:
- Конвертировать долго в квад (
cltq
): AT & T-стиль - Конвертировать Double в Quad Extend (
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 & TCWTL
),CBW
(AT & TCBTW
): меньшие версииCDQE
также присутствует в 32-битной версииCQO
семья, знак которой распространяетсяRAX
вRDX:RAX
MOVSX
семейство, которое как знак расширяет, так и перемещает: что делает инструкция movsbl?
Минимальные примеры запуска на GitHub с утверждениями:
CWDE
а такжеCWTL
CDQE
а такжеCLTQ
Пример 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
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, и все это должно стать для вас понятным.
cltq
знак расширяет EAX в RAX. Это короткая форма movslq %eax, %rax
, сохраняя байты кода. Он существует благодаря тому, как x86-64 эволюционировал с 8086 до 386 до AMD64.
Он копирует знаковый бит EAX во все старшие биты расширенного регистра, потому что так работает дополнение 2. Мнемоника - это сокращение от Convert Long to Quad.
Синтаксис AT&T (используется GNU as
/ objdump
) использует некоторые мнемоники, чем Intel, для некоторых инструкций (см. официальные документы). Ты можешь использовать objdump -drwC -Mintel
или же gcc -masm=intel -S
чтобы получить синтаксис Intel, используя мнемонику, которую Intel и AMD описывают в своих справочных руководствах (см. ссылки в вики-теге x86. (Интересный факт: в качестве входных данных gas принимает любую мнемонику в любом режиме).
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]dx. Вход удлинителей 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
подписаться продолжается al
в ax
,
Мнемоника AT&T не имеет очевидного намека на то, чтобы помочь вам вспомнить, какой из них есть какой. Некоторые из тех, которые пишут *dx
конец с d
(для дх?) вместо обычного l
за long
, cqto
нарушает этот шаблон, но октавное число равно 128b и, следовательно, должно быть объединение rdx:rax
,
ИМО мнемонику Intel легче запомнить, а Intel-синтаксис легче читать в целом. (Сначала я выучил синтаксис AT&T, но привык к Intel, потому что чтение руководств Intel/AMD полезно!)
Обратите внимание, что для нулевого расширения, mov %edi,%edi
нулевой простирается %edi
в %rdi
потому что любая запись в 32-битный регистр обнуляет старшие 32 бита. (Но на практике попробуйте mov
в другой регистр (например, mov %eax, %ecx
) потому что то же самое побеждает Mov-ликвидации в процессорах Intel. Вы часто будете видеть сгенерированный компилятором asm для функций с 32-битными беззнаковыми аргументами, использующими mov
к нулю-расширению, и, к сожалению, часто с тем же регистром, что и src и destination.
От 8 или 16 до 32 (или неявно 64), and $0xff, %eax
работает так же как movzbl %al, %eax
, (Или лучше, movzbl %al, %ecx
поэтому mov-elission может сделать это нулевой задержкой на процессорах, где movzx
).
Если ваша ОС 64-битная, если вы не объявляете функцию, находящуюся в другом файле, но вы хотите использовать ее в этом файле. GCC по умолчанию будет считать эту функцию 32-битной. Так что cltq будет использовать только 32-битный RAX(возвращаемое значение), старший 32-бит будет заполнен 1 или 0. Надеюсь, эта сеть поможет вам http://www.mystone7.com/2012/05/23/cltq/