assembly cltq在汇编中做什么?

ljsrvy3e  于 2023-03-02  发布在  其他
关注(0)|答案(4)|浏览(182)
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>
voj3qocg

voj3qocg1#

    • 助记符**

cltq是英特尔cdqegas助记符,如以下网址所示:https://sourceware.org/binutils/docs/as/i386_002dMnemonics.html
助记符包括:

  • 将长轴转换为四轴(cltq):AT & T风格
  • 将双精度转换为四精度扩展( cdqe ): Intel

术语:

  • 四倍(又称四倍字)== 8字节
  • 长(AT & T)==双字(Intel)== 4个字节

这是GAS名称与Intel版本非常不同的少数指令之一。as接受两种助记符中的任何一种,但像NASM这样的Intel语法汇编器可能只接受Intel名称。

    • 效果**

它将4字节符号扩展为8字节,这在2的补码中意味着:

  • 负数,则高4个字节的位必须设置为1
  • 正数,必须设置为0

在C中,这通常表示从有符号intlong的强制转换。
示例:

mov $0x0123456700000001, %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(美国电话电报公司CWTL),CBW(美国电话电报公司CBTW):CDQE的较小版本,也存在于32位
  • CQO系列,哪个符号将RAX扩展到RDX:RAX
  • MOVSX系列,符号扩展和移动:what does movsbl instruction do?

在GitHub上使用Assert的最小可运行示例:

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
xt0899hw

xt0899hw2#

cltq提升一个int到一个int 64. shl 3,%rax做一个偏移到一个64-bit指针(乘任何是在rax由8).什么代码正在做是循环通过一个指针列表到环境变量.当它发现一个值为零,那是这结束,并且它退出这循环.
下面是Linux如何将环境变量存储在RAM中堆栈之上的一个视图。您将看到指针从0xbffff 75 c开始;其指向0xbffff 893,“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为你转储的数据,逐步完成循环。你就会明白了。

v1uwarro

v1uwarro3#

cltqCDQE的AT & T助记符,它将EAX符号扩展为RAX。它是movslq %eax, %rax的缩写形式,节省代码字节。它的存在是因为x86 - 64如何从8086到386再到AMD64。
它将EAX的符号位复制到更宽寄存器的所有高位,因为2的补码就是这样工作的。助记符是Convert Long to Quad的缩写。
AT & T语法(GNU as/objdump使用)对某些指令使用与Intel不同的助记符(请参阅official docs)。您可以使用objdump -drwC -Mintelgcc -masm=intel -S获取Intel语法,使用Intel和AMD在其指令参考手册中记录的助记符(请参阅x86标记wiki中的链接)。(有趣的事实:作为输入,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 manual entry for these 3 insns.
显然,cltq/cdqe仅在64位模式下可用,但其它两个在所有模式下都可用。movsxmovzx仅在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 $31, %edx

    • 记住记忆**

英特尔用于在rax内扩展的助记符都以e结尾,除了原来的8086 cbw。您可以记住这种情况,因为即使是8086也可以在单个寄存器中处理16位整数。因此不需要将dl设置为al的符号位。div r8idiv r8ax读取被除数,而不是从dl:al。所以cbwal符号扩展为ax
AT & T的助记符没有明显的提示来帮助你记住哪个是哪个。一些写*dx的助记符以d结尾(对于dx?),而不是通常的l结尾。cqto打破了这种模式,但一个八位字是128b,因此必须是rdx:rax的串联。
英特尔的助记符更容易记住,英特尔的语法也更容易阅读。(我先学了AT & T的语法,但后来习惯了英特尔,因为阅读英特尔/AMD的手册很有用!)
注意,**表示零扩展,mov %edi,%edi**将%edi零扩展为%rdi,因为any write to a 32-bit register zeros the upper 32 bits
(In实践中,尝试将mov扩展到另一个寄存器(例如mov %eax, %ecx),因为same,samedefeats mov-elimination in Intel CPUs。您经常会看到编译器为具有32位无符号参数的函数生成的asm使用mov进行零扩展,不幸的是,它通常与src和destination使用相同的寄存器。)
从8或16到32(隐式为64),and $0xff, %eax可以工作,但效率低于movzbl %al, %eax$0xff不适合8位 * 符号扩展 * 立即数,因此需要完整的4字节0x000000ff立即数。(或者更好,movzbl %al, %ecx,因此移动消除可以使其在Intel CPU上的延迟为零,其中移动消除适用于movzx 8-〉32。)

z9ju0rcb

z9ju0rcb4#

如果你的操作系统是64位,如果你没有声明一个函数驻留在另一个文件中,但你想在这个文件中使用它。GCC会默认认为这个函数是32位的。所以cltq只会使用RAX的低32位(返回值),高32位将填充1或0。希望这个网站能帮助你http://www.mystone7.com/2012/05/23/cltq/

相关问题