assembler and assembly x86 gas att

assembler - and assembly x86



¿Qué hace cltq en montaje? (4)

Si su sistema operativo es de 64 bits, si no declara que una función reside en otro archivo, pero desea usarla en este archivo. Por defecto, GCC pensará que esta función es de 32 bits. Por lo tanto, cltq solo utilizará 32 bits bajos de RAX (valor de retorno), los 32 bits altos se rellenarán en 1 o 0. Espero que esta web le ayude http://www.mystone7.com/2012/05/23/cltq/

0x0000000000400553 <main+59>: mov -0x4(%rbp),%eax 0x0000000000400556 <main+62>: cltq 0x0000000000400558 <main+64>: shl $0x3,%rax 0x000000000040055c <main+68>: mov %rax,%rdx

De hecho mi programa es tan simple como:

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;

Pero la salida de montaje es bastante larga:

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.

Lo que no entiendo es este bloque:

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>


cltq promueve un int a un int64. shl 3,% rax hace un desplazamiento a un puntero de 64 bits (multiplica lo que está en rax por 8). Lo que hace el código es recorrer una lista de punteros a variables de entorno. cuando encuentra un valor de cero, ese es el final, y se sale del bucle.

Aquí hay una descripción de cómo Linux almacena las variables de entorno en la RAM, por encima de la pila. Verás los punteros comenzando en 0xbffff75c; que apunta a 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

Su compilador es aparentemente lo suficientemente inteligente como para optimizar el printf formato simple para puts . la obtención de la cadena de entorno y el postincremento de i están justo ahí en el código. Si no resuelve algo de esto por su cuenta, nunca lo entenderá realmente. Simplemente "sé" la computadora y recorre el bucle, utilizando los datos que descargué para ti con gdb, y todo debería quedar claro para ti.


cltq sign-extiende EAX en RAX. Es una forma movslq %eax, %rax de movslq %eax, %rax , que guarda bytes de código. Existe debido a cómo x86-64 evolucionó de 8086 a 386 a AMD64.

Copia el bit de signo de EAX en todos los bits superiores del registro más amplio, porque así es como funciona el complemento de 2. El mnemotécnico es la abreviatura de Convert Long to Quad.

La sintaxis de AT&T (utilizada por GNU as / objdump ) utiliza mnemotécnicas diferentes a las de Intel para algunas instrucciones (consulte los https://sourceware.org/binutils/docs/as/i386_002dMnemonics.html ). Puede usar objdump -drwC -Mintel o gcc -masm=intel -S para obtener la sintaxis Intel usando los mnemotécnicos que Intel y AMD documentan en sus manuales de referencia de instrucciones (vea los enlaces en la etiqueta wiki de x86 . acepta cualquier mnemotécnica en cualquier modo).

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 de entrada para estas 3 insns .

cltq / cdqe obviamente solo está disponible en modo de 64 bits, pero los otros dos están disponibles en todos los modos. movsx y movzx solo se introdujeron con 386, lo que facilitó / hizo más fácil firmar / poner en cero registros distintos de al / ax , o firmar / poner en cero en la marcha mientras se carga.

Piense en cltq / cdqe como una codificación más corta de casos especiales de movslq %eax,%rax . Se ejecuta igual de rápido. Pero el único beneficio es guardar un par de bytes de código, por lo que no vale la pena sacrificar nada más para usarlo en lugar de movsxd / movzx .

Un grupo de instrucciones relacionado copia el bit de signo de [e / r] ax en todos los bits de [e / r] dx. La extensión de signo eax en edx:eax es útil antes de idiv , o simplemente antes de devolver un entero ancho en un par de registros.

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)

Estos no tienen una instrucción única equivalente, pero puede hacerlo en dos instrucciones: por ejemplo, mov %eax, %edx / sar $32, %edx .

Recordando los mnemónicos.

Los mnemónicos de Intel para Extender dentro de rax todos terminan con e , excepto el 8086 cbw original. Puede recordar ese caso porque incluso 8086 manejó enteros de 16 bits en un solo registro, por lo que no habría necesidad de establecer dl en el bit de signo de al . div r8 e idiv r8 leen el dividendo de ax , no de dl:al . Así que cbw firma-se extiende al ax .

Los mnemónicos de AT&T no tienen una pista obvia para ayudarte a recordar cuál es cuál. Algunos de los que escriben en *dx terminan con d (¿para dx?) En lugar del habitual l por long . cqto rompe ese patrón, pero una palabra clave es 128b y, por lo tanto, tiene que ser la concatenación de rdx:rax .

En mi opinión, los mnemónicos Intel son más fáciles de recordar, y la sintaxis de Intel es más fácil de leer en general. (Aprendí la sintaxis de AT&T primero, pero me acostumbré a Intel porque ¡es útil leer manuales de Intel / AMD!)

Tenga en cuenta que para extensión cero, mov %edi,%edi cero extiende %edi en %rdi , porque cualquier escritura en un registro de 32 bits pone a cero los 32 bits superiores . (Pero en la práctica, intente mov a un registro diferente (por ejemplo, mov %eax, %ecx ) porque las mismas eliminaciones de mov de eliminación en las CPU de Intel . A menudo verá asm generado por compilador para funciones con uso de argumentos sin signo de 32 bits un mov a cero, y desafortunadamente a menudo con el mismo registro que src y destino.

Para 8 o 16 a 32 (o implícitamente 64), and $0xff, %eax funciona tan bien como movzbl %al, %eax . (O mejor movzbl %al, %ecx , movzbl %al, %ecx lo que la eliminación de mov puede hacer que la latencia sea cero en las CPU donde movzx ).


Mnemotécnico

cltq es el mnemotécnico del gas para el cdqe de Intel según se documenta en: https://sourceware.org/binutils/docs/as/i386_002dMnemonics.html

Los mnemónicos son:

  • Convertir largo a cltq ( cltq ): AT & T-style
  • Convertir doble a Quad Extend ( cdqe ): Intel

Terminología:

  • quad (también conocido como quad-word) == 8 bytes
  • long (AT&T) == palabra doble (Intel) == 4 bytes

Esta es una de las pocas instrucciones cuyo nombre de GAS es muy diferente de la versión de Intel. as acepta mnemónico, pero los ensambladores de sintaxis Intel como NASM solo pueden aceptar los nombres de Intel.

Efecto

El signo extiende 4 bytes a 8 bytes, lo que en el complemento a 2 significa que para:

  • números negativos, los bits de los 4 bytes superiores deben establecerse en 1
  • Números positivos, se deben establecer en 0

En C, eso suele representar un reparto desde int firmado hasta long .

Ejemplo:

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

Esta instrucción solo está disponible en 64 bits.

Considera también las siguientes instrucciones:

  • CWDE (AT&T CWTL ), CBW (AT&T CBTW ): versiones más pequeñas de CDQE , también presentes en 32 bits
  • Familia CQO , cuyo signo extiende RAX a RDX:RAX
  • Familia MOVSX , cuyo signo se extiende y mueve: ¿qué hace la instrucción movsbl?

Ejemplos mínimos ejecutables en GitHub con aserciones:

Ejemplo de c

GCC 4.9.3 lo emite:

#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); }

Compilar y desmontar:

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

contiene:

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)

y el comportamiento es:

$ ./a.out 0x80000000 ffffffff80000000 $ ./a.out 0x40000000 40000000