90cfc7d2

1 Conceptos generales

a) Convención de llamada es un contrato que describe, entre varias cosas, la forma en que las funciones serán llamadas, como organizar los parámetros y qué invariantes estructurales deben mantenerse. Esto sirve para poder hacer llamadas a funciones de forma consistente.

La ABI de System V para 32 bits define lo siguiente:

  • Los parámetros siempre se pasan por stack, de derecha a izquierda.
  • El stack tiene que estar alineado a 4 bytes, o 16 bytes para llamar funciones de C.
  • El valor de retorno se pasa por EAX.
  • EBX, EBP, ESI y EDI son no volátiles.

La ABI de System V para AMD64 es parecida a la de 32 bits, con las siguientes diferencias:

  • Los parámetros se pasan primero por registros y, si se necesitan más, se usa el stack.
  • El stack tiene que estar alineado a 8 bytes, o 16 para llamar funciones de C.
  • RBX, RBP, R12, R13, R14 y R15, son no volátiles.

b) En C el compilador ya conoce las ABIs y se encarga generar el assembly correcto para que se cumpla la convención. Cuando se programa en assembly, es responsabilidad del programador.

c) El stack frame es una sección del stack donde se guarda el contexto de una función. Esto incluye variables locales, parámetros y copias de registros no volatiles (Como el Base Pointer o el Stack Pointer).

El prólogo de una función es una parte del código que se pone al principio para reservar espacio en el stack frame, backupear registros no volátiles y obtener los parámetros pasados por el stack.

El epílogo se encarga de revertir el prólogo, restaurar los registros no volatiles, resetear el stack y ejecutar el RET.

d) Las variables temporales son almacenadas en el stack, luego de que el prólogo haya reserva espacio.

e) Para llamar a funciones de libc hay que alinear el stack a 16 bytes. Debido a que el CALL pushea el Instruction Pointer, quedaria alineado a 4 u 8, dependiendo de si es de 32 o 64 bits (4 u 8 bytes el tamaño del IP).

f)

  • En un programa ya compilado, cada atributo del struct pixel_t tiene un offset asignado dado por su órden en el código fuente. Aunque se reordene, el programa sigue accediendo a los campos con los offsets originales. Esto impacta la ejecución de la función a_escala_de_grises ya que se modifican los aliasings.
  • En AMD64 el float* es un puntero de 64 bits, que se interpretaría como el largo del array. Aunque tamano tenga el mismo tamaño de bytes, al intercambiar los parámetros se producirían errores en el runtime ya que se intentaría acceder a un espacio de memoria en tamano como primer elemento de array.
  • Ya que el valor de retorno sí o sí debe volver por el registro acumulador, para ciertos valores no va a haber problema. Sin embargo cuando retorne un valor mayor a 16 bits, el programa solo utilizaría los bits que espera. Ejemplo: el programa veria dos usuarios con ID 0x1_0000_0001 e ID 0x1. En el assembly generado se ve que al recibir el valor de registrar, se ejecuta un MOVZX EAX, AL, seteando a 0 todos los bits de RAX excepto por los de AL.

Intel® 64 and IA-32 Architectures Software Developer's Manual Volume 1: Basic Architecture

3-12 Vol. 1

BASIC EXECUTION ENVIRONMENT

When in 64-bit mode, operand size determines the number of valid bits in the destination general-purpose register:

  • 64-bit operands generate a 64-bit result in the destination general-purpose register.
  • 32-bit operands generate a 32-bit result, zero-extended to a 64-bit result in the destination general-purpose register.
  • 8-bit and 16-bit operands generate an 8-bit or 16-bit result. The upper 56 bits or 48 bits (respectively) of the destination general-purpose register are not modified by the operation. If the result of an 8-bit or 16-bit operation is intended for 64-bit address calculation, explicitly sign-extend the register to the full 64-bits.
  • Al haber aumentado el dominio de user_id, el programa podría pasarle como argumento un número de 16 bits más otros bits restantes considerados basura si el pasaje de parámetros se usa la instrucción MOV y no otro como MOVZX. Esto tiraría error por no encontrar el usuario.

  • El asm de la función multiplicar hace lo siguiente: el parámetro int32_t lo interpreta como un float y lo mueve a un registro xmm. Luego, el parámetro float guardado en el stack lo utiliza como multiplicador y el resultado se guarda en el mismo xmm. Finalmente, se castea de vuelta a un integer y se devuelve. Siguiendo ese orden, en caso de permutarse los parámetros, fallaría el primer casteo ya que se espera un integer y no un float. Con esto, podría dar resultados inesperados o directamente tirar un error.

En general, un cambio en la interfaz causaría representaciones inválidas y, por consiguiente, bugs o errores de runtime. En caso de sólo ser una permutación de parámetros, es posible evitar esto si la función es conmutativa y los tipos se mantienen iguales.

2 C/ASM pasaje de parámetros

Archivo: checkpoint2.asm

3 Recorrido de estructuras C/ASM

Archivo: checkpoint3.asm

4 Memoria dinámica con strings

Archivo: checkpoint4.asm

a) stack_snooper toma su base pointer y lo usa para llegar al base pointer del caller. Ambos punteros se pasan a la función print_stack_from_a_to_b. stack_snooper_n salta de base pointer en base pointer n veces. El puntero resultante y el original se pasan como parametro a print_stack_from_a_to_b

b) Ejemplo de llamar al snooper en strCmp:

------------------ Snooper -----------------
--------------------------------------------
Printing stack from: 0x7fffa7bc3b10
                to: 0x7fffa7bc3b70
============================================
[0x7fffa7bc3b10 (from + 00)] 0x7fffa7bc3b40
[0x7fffa7bc3b18 (from + 08)] 0x55be84ef4496
[0x7fffa7bc3b20 (from + 16)] 0x55be84ef5004
[0x7fffa7bc3b28 (from + 24)] 0x55be84ef5009
[0x7fffa7bc3b30 (from + 32)] 0x7fffa7bc3c88
[0x7fffa7bc3b38 (from + 40)] 0xffffffffdeadbeef
[0x7fffa7bc3b40 (from + 48)] 0x7fffa7bc3b70
[0x7fffa7bc3b48 (from + 56)] 0x55be84ef41ca
[0x7fffa7bc3b50 (from + 64)] 0000000000
[0x7fffa7bc3b58 (from + 72)] 0x7fefb840f9e0
[0x7fffa7bc3b60 (from + 80)] 0x55be84ef5004
[0x7fffa7bc3b68 (from + 88)] 0x55be84ef5009
============================================

str1 = 55be84ef5004
str2 = 55be84ef5009

Nuestro sentinela es 0xdeadbeef.

Corregido por Ignacio