Page 1 of 1

Mixing C and nasm (again) [Solved]

Posted: Tue Feb 12, 2008 7:58 am
by jaswax
Well...Here we go again. I apologize if this is the wrong way to do this. If it is just let me know and I will correct it for the next post. I have another post with the same subject but I have a different problem - sorta.

The problem is with passing string type arguments from a C caller to nasm.

Example of what I want to do:

Code: Select all

int put_string(char str);

int main ()
{
  put_string ("Hello World");
}
Asm code example:

Code: Select all

put_string:
        push	ebp
	mov	eax, esp
	mov	ebp, eax
	mov	esi, ebp
        add      esi, 8
        ....
        ....                        ;Some code to print my string
ret
I have no problem passing integer type information and to me this should be no different really but for some reason it is. When I passed the string with "" it appears to be nothing on the stack. When I pass it with single quotes I only get the last 4 characters of my string. I would like to be able to have it work both ways. I have also tried declaring my arguments in different ways such as: int put_string (char *str);. Any help on this would be great.

Posted: Tue Feb 12, 2008 8:03 am
by AJ
Hi,

There seems to be a lot of preamble there you don't need. For a start, you can do a direct MOV EBP, ESP.

Remember with a string, you are passing a character pointer and that parameters passed in the stack are accessed by address. That means that you need to use [ESI+8], not ESI+8.

Cheers,
Adam

[edit]Oh, and you are popping EBP at the end too, right?[/edit]

Posted: Tue Feb 12, 2008 8:32 am
by Solar
...and you have .rodata linked into your executable?

Posted: Tue Feb 12, 2008 8:35 am
by jaswax
I was aware that I have some garbage code. I got frustrated with my attempt at passing a string and started trying to brute force it to work. Obviously it got me nowhere. Thanks for info on mov ebp, esp. I was not aware that you could do that.

Ok, so let me see if I get this right. When my function executes it should pass the address of the first character of my string on the stack and not the actual string, right?

Does it matter how my function gets prototyped? I.E. int put_string(char str); or int put_string(char *str);, or int put_string (char *);, etc

BTW: Thank you for the help.

Posted: Tue Feb 12, 2008 8:39 am
by jaswax
@Solar

Good thought. I double checked and yes I do!!!

Posted: Tue Feb 12, 2008 9:11 am
by AJ
jaswax wrote: Does it matter how my function gets prototyped? I.E. int put_string(char str); or int put_string(char *str);, or int put_string (char *);, etc
You definitely don't want to use the first version (char str), because in this case, GCC should only pass the lowest 8 bits of the value of 'str'. If you were doing a put_char function, that would be correct. However, yout second two are identical - it doesn't matter if you don't five the variable a name. Here's how I would do it (I'm not an assembly expert and this is off the top of my head):

Code: Select all

[global put_string]
put_string:
   push ebp
   mov ebp, esp

   mov esi, [ebp+8]       ;esi now contains your character *pointer*
    
   .loop
        mov al, byte [esi]  ;al now contains the first character
        ; something here to put your character
   jmp .loop
   pop ebp
ret
That's the sort of function I would use. Of course, you may want to use a proper loop and lodsb and so on, but it gives you the general idea.

Cheers,
Adam

Posted: Tue Feb 12, 2008 10:46 am
by jaswax
Your code example was awesome. Still no joy though. I decided to go about debugging this a little differently though. I looked at the contents of [ebp + 8]. It does contain the address to my string BUT when I assign that address to esi and then look at the contents of [esi]. [esi] is 0! I decided to look at several different effective addresses [esi + 1], [esi + 2], [esi + 3], etc all of the ones that I looked at are 0. [esi] should have the byte code to the first character but it does not. I am guessing that I must have an error in my code somewhere else. Any idea on where to look, or what to look for?

Is it possible that I am not getting any space reserved for my string and that is why [esi] is 0? If this is the case then how to I force C to reserve some space for a string? (I think this should already be done at compile time because the string is already defined as "Hello World" but I could be wrong.)

Just as a side note, when I use 'Hello World' as apposed to "Hello World" I do get the byte codes of my string but they are at [ebp + 8] instead of a address to the string and I only get the last 4 letters of my string 'orld'!

Any other thoughts, ideas, comments, etc are welcome.

Posted: Tue Feb 12, 2008 6:47 pm
by Jef
code is correct.

BUT when you pass the parameters, you must put the pointer of the string

you say:

Code: Select all

int put_string(char str);
it must be

Code: Select all

int put_string(char *str);
then at asm code:
as AJ says.
and if "mov al, byte [esi]" confuses you,
use something like this:

Code: Select all

push ebp
mov ebp, esp
mov esi, [ebp]       ;the first parameter

.loop
   lodsb
   ;now AL has the first/second/x byte 
   cmp al, 0           ;in case that the string is ASCIIZ
   jne .loop

pop ebp

Posted: Wed Feb 13, 2008 2:24 am
by Solar
jaswax wrote:Just as a side note, when I use 'Hello World' as apposed to "Hello World" I do get the byte codes of my string but they are at [ebp + 8] instead of a address to the string and I only get the last 4 letters of my string 'orld'!
'Hello World' is undefined.

"..." is a string (pointer to first character. '...' is a character constant, i.e. a single character value. 'Hello World' is something strange. (And I honestly have no idea why you get the last four letters.)

Posted: Wed Feb 13, 2008 8:12 am
by jaswax
@AJ and Jef: I thank you both for you input of example code. Very help and did help me fix some bugs and clean up my grabage code as well as make it more efficient.

@Solar: Very good info about the strings. I did not realize the difference between "" and '' so thank you.

Unfortunately, I still have the same problem. I realize I need to code for "" so that I get the pointer to the string. At least that make sense to me. I do get the pointer passed from the C caller to the asm code but it seems to point to nothing. I even opend up my hex editor to see if my "Hello World" string was in the right place in my bin file. It is!! My only guess is that I have a bug in some other part of my code that is causing this wierd problem. I bet that I could just code this thing in C and have it work just fine but it still leaves me with a possible bug somewhere.

So instead of posting all of my code here (there is a lot of code), I will be stepping though my code tonight to see if I can spot the problem.

I think I will leave this post as unsolved for the moment so that if you guys or anyone else wants to post suggestions/ideas then feel free. I am definately open to ideas.

Thanks again to all.

Posted: Wed Feb 13, 2008 8:45 am
by Solar
Programming from the Ground Up - while using AT&T syntax which causes nausea in some people - has an excellent fourth chapter on calling conventions and parameter passing.

Posted: Sat Feb 16, 2008 12:44 pm
by jaswax
Ok, after many days of looking at code I found that the problem was actually with my linker script. It was not setup correctly for my .text, .data, .rodata, .bss.

Earlier in this thread Solar had suggested that there might be a problem with my .rodata. While it looked to me as though it was there. It was actually not. Now I feel like a dumb ***. I think this mistake tops them all!!!

@Solar: Thanks for the data. The PDF book "Programming Ground Up", is an excellent source of info. Thank you.

As noted in the subject of my first post of this thread: My problem is solved.

Posted: Mon Feb 18, 2008 6:17 am
by Solar
Never trust the front ("command looked alright and executed alright, so everything is a-OK"). Only ever trust the behind ("I disassembled the thing and the string is there"). 8)