NASM - The Netwide Assembler
NASM Forum => Using NASM => Topic started by: dbfn on January 18, 2010, 06:04:18 AM
-
How do i call the function and acess the struct values returned from it?
Example:
struct foo {
int a;
int b;
int c;
int d;
};
struct foo
bar (void)
{
struct foo ret;
ret.a = 1;
ret.b = 2;
ret.c = 3;
ret.d = 4;
return ret;
}
-
Plus...
mov dword & var,register
Wtf it does?
[dword &,han?]
-
I'm highly inexpert at C, but...
struct foo
bar (void)
{
struct foo ret;
ret.a = 1;
ret.b = 2;
ret.c = 3;
ret.d = 4;
return ret;
}
... if I understand this correctly, you're returning the address of a local (stack... "automatic") variable. I don't think that's gonna work.
"mov dword & var, reg" is secret undocumented Nasm syntax. It means the same as "mov [var], reg". You're not supposed to know about it! :) Where'd you see that?
I think we can help you with accessing structure elements C<->asm, but explain more what you're trying to do... Where does an "instance" of this structure exist - hopefully not as an "automatic" variable which is going to go away when the function exits...
Best,
Frank
-
Hello frank! ^^
Dude...even when the variable I return is at a global scope,I still get a segfault.
Take a look:
teste.c:
struct st {
int a;
int b;
};
struct st h;
struct st teste() {
h.a = 10;
h.b = 30;
return h;
}
teste2.asm:
extern teste
section .text
global main
main:
call teste
mov eax,1
mov ebx,0
int 80h
-
Dude...even when the variable I return is at a global scope,I still get a segfault.
I could not duplicate the segfault issue with your example on my box.
Please post your platform (Windows, Linux, etc...), version of NASM, and the assemble/compile/link commands you are using to build your example.
-
Heh, well if he's running 'doze, the int 80h might be a problem. :)
I can confirm that it segfaults (nasm -f elf teste2.asm / gcc -o teste teste.c teste2.o)
Seems to be happening at the "h.a = 10" - mov [ecx], eax, with eax 10 (if I change the file to "h.a = 20", eax is 20). ecx is 0x4004829B - the range that "mmap" uses. I would expect a "static" variable to be around 0x0804???? - I added "static" - no help.
Looks like a C problem to me! :)
Seriously, your asm looks fine (assuming Linux!), and it isn't crashing there... or trying to return from there... I don't know what the problem could be!
Best,
Frank
-
I'm running in Debian 5.0.
Nasm version: 2.07
GCC version: 4.3.2
ld version: the one wich comes with binutils 2.18
The commands I used were very similar to Frank's.
----
'doze
Han?
---
Hum...very strange!
Keith,wich compiler and etc did you use?
How do I make something like this work? o-o
Thanks for your help people!
-
Make that 0x0804xxxx - smiley bit me!
Hey! You never told me where you saw "mov &var, reg"! It'll work, it just isn't well known.
I tried this modification of your C file:
struct st {
int a;
int b;
};
struct st h;
struct st teste() {
printf("h=%x ; &h=%x\n", h, &h);
h.a = 20;
h.b = 30;
return h;
}
Says 0... both times. 'Splains the segfault, but not the solution. I think you need a C guru, son. :)
Best
Frank
-
Ha,I saw the "mov &" thing on a tutorial explaining how to interact the X11 librarie with assembly.
WOW...it looks like i'll never find the solution...t-t
Thanks dude...
Ha...I also tried to assemble with the gnu assembler and got the same result.
-
Keith,wich compiler and etc did you use?
My main box is a Mac running OS 10.6 (Snow Leopard):
NASM 2.07 from Source: nasm -fmacho -o main.o main.asm
gcc 4.2.1 from XCode: gcc -m32 -c -o struct.o struct.c
ld from XCode: ld -o main.bin main.o struct.o
main.bin runs without any segfaults.
I can try again tonight on Ubuntu/ELF and see what happens.
-
Hum...try then...o-o
And why the f*** this works in a Mac box?
-
Use pointer parameters and dereference the argument. The following example increments the x, y, and z values in a POINTS structure.
main.c
#include <stdio.h>
#include <stdlib.h>
typedef struct
{
int x, y, z;
} POINTS;
void IncrementPoints( POINTS * ptrPoints ) __attribute__((stdcall));
int main ( void )
{
POINTS * here;
here = (POINTS *) malloc( sizeof( POINTS ) );
here->x = 1;
here->y = 2;
here->z = 3;
fprintf( stdout, "x: %ld\ny: %ld\nz: %ld\n",
here->x, here->y, here->z );
IncrementPoints( here );
fprintf( stdout, "x: %ld\ny: %ld\nz: %ld\n",
here->x, here->y, here->z );
free( here );
return( 0 );
}
incpts.asm
BITS 32
STRUC POINTS
.x RESD 1
.y RESD 1
.z RESD 1
ENDSTRUC
SECTION .text
Global IncrementPoints
IncrementPoints:
STRUC IP_ARGS
.ptrPoints RESD 1
ENDSTRUC
Push Ebp
Mov Ebp, Esp
Push Esi
Mov Esi, [Ebp + 8 + IP_ARGS.ptrPoints]
Inc DWORD [Esi + POINTS.x]
Inc DWORD [Esi + POINTS.y]
Inc DWORD [Esi + POINTS.z]
Xor Eax, Eax
Pop Esi
Leave
Ret
Build and Run with:
nasm -f elf incpts.asm -o incpts.o
gcc -o test main.c incpts.o
./test
A few things to note are; a) I use stdcall convention whenever I write assembly, so you'll notice the __attribute__ declaration to ensure that it uses stdcall, if you use a different convention then you will need to change that. b) I decided to allocate the 'here' structure on the heap, if you want to use a local stack based variable (ie POINTS here) just call the procedure using 'IncrementPoints( &heap);' instead and all should work fine.
Regards,
Bryant Keller
-
Sorry about that, the answer is the same as before (use pointers) but the demo code was reversed...
main.asm
BITS 32
STRUC POINTS
.x RESD 1
.y RESD 1
.z RESD 1
ENDSTRUC
Extern initPoints
Extern printPoints
Extern freePoints
SECTION .bss
ptrPoints RESD 1
SECTION .text
Global main
main: Push Ebp
Mov Ebp, Esp
Call initPoints
Mov [ptrPoints], Eax
Push Eax
Call printPoints
Mov Eax, [ptrPoints]
Mov DWORD [Eax + POINTS.x], 12
Push DWORD [ptrPoints]
Call printPoints
Push DWORD [ptrPoints]
Call freePoints
Leave
Ret
myPoints.c
#include <stdio.h>
#include <stdlib.h>
typedef struct
{
int x, y, z;
} POINTS;
POINTS * initPoints( void );
void printPoints( POINTS * ptrPoints );
void freePoints ( POINTS * ptrPoints );
POINTS * initPoints( void )
{
POINTS * tmp = (POINTS *) malloc( sizeof( POINTS ) );
tmp->x = tmp->y = tmp->z = 1;
return tmp;
}
void printPoints( POINTS * ptrPoints)
{
fprintf( stdout, "x:\t%d\ny:\t%d\nz:\t%d\n",
ptrPoints->x, ptrPoints->y, ptrPoints->z );
}
void freePoints( POINTS * ptrPoints )
{
free( ptrPoints );
}
Build and Run with:
gcc -c -o myPoints.o myPoints.c
nasm -f elf main.asm -o main.o
gcc -o test main.o myPoints.o
./test
Sorry about that, I'm not in the best frame of mind atm. :-\
Regards,
Bryant Keller
-
Yeap,returning pointers I had no problems since the begining.
But what about returning the struct? : x
-
Yeap,returning pointers I had no problems since the begining.
But what about returning the struct? : x
Well, from a low level viewpoint, you can't really return a "struct", you can only return a pointer/address to it *or* return its contents if it is small enough to fit into 32 bits (in your case).
Utilizing structs in assembly language, just as in C, requires the knowledge of the size and contents of a structure. C makes it easier to deal with conceptually, but the fact is that structs are just arrays with potentially different sized/typed/named elements, so you have to treat them as such.
What you can do, is ensure that the definition of a particular structure is defined in both the C and ASM code (see Bryant's first example with the POINTS struct), and then utilize the language constructs of each to intuitively address/access the members of said structure.
-
all right then....
-
Well, I still don't understand why your example ran on Keith's Mac! (OMG, they really *are* better! :)
I sympathize with your problems with structures in C. When I was trying to teach myself C, I utilized structures extensively. Had *no* idea what I was doing, just bashed at it until it worked - which it eventually did. Now that I've learned assembly, I understand why "returning the structure" wasn't working. The return value is a... well, 16-bit then, 32-bit or more now, value - "the structure" ain't going to fit.
Thanks to Bryant for the example!
Best,
Frank
-
Hum...try then...o-o
Segfaulted on 32-bit Ubuntu. I checked a dump of the file and the Ubuntu version is still trying to access the stack, where-as the Mac version is not.
Ubuntu Binary Dump:
; Disassembly of file: main.bin
; Mon Jan 25 23:37:56 2010
; Mode: 32 bits
; Syntax: YASM/NASM
; Instruction set: 80386
global _edata
global __bss_start
global _end
global teste
global _start
global h
SECTION .text align=16 execute ; section number 1, code
teste: ; Function begin
push ebp ; 080480A0 _ 55
mov ebp, esp ; 080480A1 _ 89. E5
mov ecx, dword [ebp+8H] ; 080480A3 _ 8B. 4D, 08
mov dword [h], 10 ; 080480A6 _ C7. 05, 080490E8(d), 0000000A
mov dword [?_001], 30 ; 080480B0 _ C7. 05, 080490EC(d), 0000001E
mov eax, dword [h] ; 080480BA _ A1, 080490E8(d)
mov edx, dword [?_001] ; 080480BF _ 8B. 15, 080490EC(d)
mov dword [ecx], eax ; 080480C5 _ 89. 01
mov dword [ecx+4H], edx ; 080480C7 _ 89. 51, 04
mov eax, ecx ; 080480CA _ 89. C8
pop ebp ; 080480CC _ 5D
ret 4 ; 080480CD _ C2, 0004
; teste End of function
_start: ; Function begin
call teste ; 080480D0 _ E8, FFFFFFCB
mov esi, h ; 080480D5 _ BE, 080490E8(d)
mov eax, 1 ; 080480DA _ B8, 00000001
mov ebx, 0 ; 080480DF _ BB, 00000000
; Note: Function does not end with ret or jmp
int -128 ; 080480E4 _ CD, 80
; _start End of function
SECTION .bss align=4 noexecute ; section number 2, bss
h: ; qword
resb 4 ; 080490E8
?_001: ; dword
resd 1 ; 080490EC
I'm not exactly an expert in C or compiler theory, and perhaps I am missing something, but I find that trying to access an unknown portion of the stack seems rather foolish... especially when it is above any known portion of a downward growing stack (ebp+[0-3] = ebp; ebp+[4-7] = return address; ebp+[8+] = ???).
Perhaps there was a faulty assumption by the compiler that a pointer to the structure was going to be supplied as the first/only argument?
Perhaps mov ecx, dword [ebp+8H] is consistently grabbing an invalid or NULL pointer, causing a segfault on the subsequent mov dword [ecx], eax?
Mac Binary Dump:
; Disassembly of file: main.bin
; Mon Jan 25 23:23:53 2010
; Mode: 32 bits
; Syntax: YASM/NASM
; Instruction set: 80386
global __mh_execute_header
global start
global _teste
global _h
__mh_execute_header equ 00001000H ; 4096
SECTION ._TEXT.__text align=1 execute ; section number 1, code
start: ; Function begin
call _teste ; 1FB7 _ E8, 0000000C
mov eax, 1 ; 1FBC _ B8, 00000001
mov ebx, 0 ; 1FC1 _ BB, 00000000
int -128 ; 1FC6 _ CD, 80
_teste:
push ebp ; 1FC8 _ 55
mov ebp, esp ; 1FC9 _ 89. E5
sub esp, 8 ; 1FCB _ 83. EC, 08
call ?_001 ; 1FCE _ E8, 00000000
?_001: pop ecx ; 1FD3 _ 59
lea eax, [ecx+102DH] ; 1FD4 _ 8D. 81, 0000102D
mov eax, dword [eax] ; 1FDA _ 8B. 00
mov dword [eax], 10 ; 1FDC _ C7. 00, 0000000A
lea eax, [ecx+102DH] ; 1FE2 _ 8D. 81, 0000102D
mov eax, dword [eax] ; 1FE8 _ 8B. 00
mov dword [eax+4H], 30 ; 1FEA _ C7. 40, 04, 0000001E
lea eax, [ecx+102DH] ; 1FF1 _ 8D. 81, 0000102D
mov eax, dword [eax] ; 1FF7 _ 8B. 00
mov edx, dword [eax+4H] ; 1FF9 _ 8B. 50, 04
mov eax, dword [eax] ; 1FFC _ 8B. 00
leave ; 1FFE _ C9
ret ; 1FFF _ C3
; start End of function
SECTION ._DATA.__common align=4 noexecute ; section number 2, bss
_h: ; byte
resb 8 ; 2000
SECTION ._IMPORT.__pointers align=4 noexecute ; section number 3, data
db 00H, 20H, 00H, 00H ; 3000 _ . ..
The above Mac version, although more indirect, is a little "smarter" (probably has more to do with ABI/API design) in that it only tries to return the values of each structure member in eax and edx. This is the only major difference between the two systems/binaries and could definitely account for a segfault.
In the end, however, you did something that was theoretically acceptable to the compiler but the real results were obviously nothing that was intended. I would recommend brushing up on Pointers in C/C++ (http://www.cplusplus.com/doc/tutorial/pointers/).
Another note about what you are trying to achieve. If you want direct access to the C struct in ASM, you would most likely want to put an extern for it in the ASM code and address it directly.
-
That is truely bizzarre. And some fancy footwork on the part of the Mac!
I have a question about mixed C and asm. What's the C for? :)
Best,
Frank
-
dbfn,
You could _technically_ return a struct even if it doesn't fit into Eax, although I've never seen it done in C. The technique accessed the previous frame to modify a previously declared structure at a known point on the frame. I don't exactly remember where I saw this (I do remember it was written in GoASM's USEDATA directive) but for the sake of modularity it would be worthless -- it has to know where the variable is in the previous frame.
Personally, I prefer to use the dereferenced pointer method shown in my previous post because it could easily be extended to void pointer types to allow for structures which share a base structure (think along the lines of inheritance, very useful in IP based protocols where different "payloads" decide the rest of the structure even though the "header" is effectively the same).
Just to be consistant I would avoid the use of returned structures, yes there are some compilers which will detect a structures size and put it in a "known" location like FPU registers or adjust the stack by moving the ebp/eip to make room for the value. But to be honest there is no "universal method" used by any compiler and your code will be restricted to only a specific set of build tools, whereas the pointer method is always going to be in Eax.
Thanks to Bryant for the example!
Frank,
No problem man. Actually, for the sake of this new forum I've decided to start saving a local copy of my responses as well as working examples in a neat directory structure.
/home/bkeller/Desktop/Support/[username]/[subject]/
/home/bkeller/Desktop/Support/[username]/[subject]/src/*.*
/home/bkeller/Desktop/Support/[username]/[subject]/responses/###.txt
This way not only do I know for certain that all code I post will work (no more tongue-and-cheek code) but I'll also have a way to quickly look up past responses in the event I have a repeat question. I hope this will make me more effect in responding to any questions.
We've talked before about me joining sourceforge to take part in on the discussion group but as I mentioned before, I hated the sourceforge based forums due to their poor support for displaying code snippets. (You would think a site dedicated to hosting open source projects would at least allow users to post readable code). I honestly can't express how happy I am that the board is now running SMF and I look forward to being a regular member here. :)
Keith,
Mac's are so cute. The Mac dump you posted has me completely boggled as to why in the world it would want to use position relative addressing for accessing memory locations. That code reminds me of the old K32B.INC include Homer and I created for tiny executables on win32 (no imports) through use of PRA (The Delta Method) and a few PEB hacks. Outside of that include I've actually never seen PRAs used for, well anything (except maybe shellcode and viral type code). lol
If you have any other code dumps like that (being as I don't own a Mac) toss them to me in PM, I'd love to see what other little tricks Mac compilers have picked up. :)
Regards,
Bryant Keller