Tengo una pequeña duda...
En C es posible hacer esto:
/*...*/
struct _hdr
char data1;
double data2;
char *data3;
};
/*...*/
struct _hdr foo (void)
{
struct _hdr instance;
/*...*/
return instance;
}
/*...*/
¿Como es posible hacerlo segun la convencion de llamadas de C?
CitarInteger values and memory addresses are returned in the EAX register
https://en.wikipedia.org/wiki/X86_calling_conventions (https://en.wikipedia.org/wiki/X86_calling_conventions)
Entiendo que una estructura no es un valor entero (a menos que sea una direccion de memoria). Entonces, ¿Como haciendo uso de cdecl se puede retornar una estructura entera? La citada en el codigo no cabe siquiera en EDX:EAX.
Saludos y gracias por su atencion.
Es a gusto del compilador.
CitarIn regard to how to return values, some compilers return simple data structures with a length of 2 registers or less in the register pair EAX:EDX, and larger structures and class objects requiring special treatment by the exception handler (e.g., a defined constructor, destructor, or assignment) are returned in memory. To pass "in memory", the caller allocates memory and passes a pointer to it as a hidden first parameter; the callee populates the memory and returns the pointer, popping the hidden pointer when returning
https://en.wikipedia.org/wiki/X86_calling_conventions#cdecl
struct _hdr foo (void)
{
struct _hdr instance;
instance.data1 = 'A';
instance.data2 = 45.04;
instance.data3 = "PROBANDO 123";
return instance;
}
void main()
{
foo();
__debugbreak();
}
0:000> u main
test!main [c:\src\test.c @ 26]:
00ffaa50 55 push ebp
00ffaa51 8bec mov ebp,esp
00ffaa53 83ec18 sub esp,18h
00ffaa56 8d45e8 lea eax,[ebp-18h]
00ffaa59 50 push eax
00ffaa5a e80e80ffff call test!ILT+6760(_foo) (00ff2a6d)
00ffaa5f 83c404 add esp,4
00ffaa62 cc int 3
00ffaa63 33c0 xor eax,eax
00ffaa65 8be5 mov esp,ebp
00ffaa67 5d pop ebp
00ffaa68 c3 ret
Al frenarse en el breakpoint:
0:000> dt test!_Hdr @eax
+0x000 data1 : 65 'A'
+0x008 data2 : 45.039999999999999147
+0x010 data3 : 0x00cc3290 "PROBANDO 123"
0:000> u test!foo
test!foo [c:\src\test.c @ 15]:
00ffaa00 55 push ebp
00ffaa01 8bec mov ebp,esp
00ffaa03 83ec18 sub esp,18h
00ffaa06 c645e841 mov byte ptr [ebp-18h],41h ;'A'
00ffaa0a f20f1005a88e0401 movsd xmm0,mmword ptr [test!_real (01048ea8)] ;ver abajo
00ffaa12 f20f1145f0 movsd mmword ptr [ebp-10h],xmm0
00ffaa17 c745f890320501 mov dword ptr [ebp-8],offset test!__acrt_initial_multibyte_data+0x220 (01053290) ;ver abajo
00ffaa1e 8b4508 mov eax,dword ptr [ebp+8]
00ffaa21 8b4de8 mov ecx,dword ptr [ebp-18h]
00ffaa24 8908 mov dword ptr [eax],ecx
00ffaa26 8b55ec mov edx,dword ptr [ebp-14h]
00ffaa29 895004 mov dword ptr [eax+4],edx
00ffaa2c 8b4df0 mov ecx,dword ptr [ebp-10h]
00ffaa2f 894808 mov dword ptr [eax+8],ecx
00ffaa32 8b55f4 mov edx,dword ptr [ebp-0Ch]
00ffaa35 89500c mov dword ptr [eax+0Ch],edx
00ffaa38 8b4df8 mov ecx,dword ptr [ebp-8]
00ffaa3b 894810 mov dword ptr [eax+10h],ecx
00ffaa3e 8b55fc mov edx,dword ptr [ebp-4]
00ffaa41 895014 mov dword ptr [eax+14h],edx
00ffaa44 8b4508 mov eax,dword ptr [ebp+8]
00ffaa47 8be5 mov esp,ebp
00ffaa49 5d pop ebp
00ffaa4a c3 ret
0:000> dD 01048ea8 l1
01048ea8 45.04
0:000> da 01053290
01053290 "PROBANDO 123"
Ahora podes probar sacando data2 de la estructura, data1 sera devuelto en EAX y data3 en EDX.
Supuse que se trataba de algo asi. No entiendo el codigo que usa el set de instrucciones MMX, pero entiendo lo que esta haciendo.
Aunque sinceramente pense que hacia uso de un ciclo para guardar los datos byte a byte.
CitarAhora podes probar sacando data2 de la estructura, data1 sera devuelto en EAX y data3 en EDX.
Me parece impresionante lo exhaustiva que llega a ser la tecnologia.
Saludos y gracias por tu respuesta.
Cita de: marax en 7 Marzo 2021, 12:54 PMAunque sinceramente pense que hacia uso de un ciclo para guardar los datos byte a byte.
A la larga lo terminara haciendo si la estructura es suficientemente extensa.
De nadas ::)
A ver que pasa con GCC:
#include <stdio.h>
struct _hdr
{
char data1;
double data2;
char* data3;
};
struct _hdr __attribute__((__cdecl__)) foo()
{
struct _hdr instance;
instance.data1 = 'A';
instance.data2 = 45.04;
instance.data3 = (char*)"PROBANDO 123";
return instance;
}
int __attribute__((noinline)) __attribute__((__cdecl__)) foo2(int a, int b)
{
return 0;
}
int main(int argc,char*argv[])
{
struct _hdr estructura;
int entero=foo2(1,2);
estructura=foo();
printf("%c %.2f %s %i\n",estructura.data1,estructura.data2,estructura.data3,entero);
return 0;
}
Al parecer una función común y corriente como foo2 en cdecl funciona perfectamente, pero es interesante ver lo que pasa con foo:
(https://i.imgur.com/k8j9C7Y.png)
Está pasando un puntero como argumento, lo interesante es que al parecer GCC cambia la convención a tipo stdcall:
(https://i.imgur.com/X7J42iD.png)
Si arreglo y paso como parametro el puntero a la estructura vemos como el descompilador funciona casi retornando el mismo código original:
(https://i.imgur.com/UyWj7oJ.png)
Por lo que en este caso aunque además de retornar la dirección del puntero (aunque no es usado) a la estructura también trabaja con un argumento oculto.
B#