Page 1 of 3
Calling C function from assembly
Posted: Thu Jan 24, 2013 3:34 am
by BMW
I have a C function:
Code: Select all
void kmain(void* mmap_address, uint32_t mmapentrycount, uint32_t memSize)
I call the function in assembly like this:
Code: Select all
mov eax, [memSize]
push eax
movzx eax, word [mmap_entries]
push eax
mov eax, MemMapAddress ;MemMapAddress is a defined constant
push eax
call kmain
However when I access the arguments in the C function, it gives the wrong argument - memSize actually gives mmapentrycount, and mmapentrycount actually gives mmap_address.....
Am I calling the function wrong?
And if you're going to tell me to RTFM or something similar, I am going off this:
http://www.delorie.com/djgpp/doc/ug/asm/calling.html
Re: Calling C function from assembly
Posted: Thu Jan 24, 2013 3:52 am
by Combuster
Since your problem have yet to occur in the posted code snippets, and the consequential lack of trust in your question accuracy or your toolchain setup, post the exact code and disassembly of kmain, including compilation commands.
Oh and, since you probably wouldn't bother if I didn't call you out on it, read the disassembly. My crystal ball says it is not what you expect.
Re: Calling C function from assembly
Posted: Thu Jan 24, 2013 6:49 am
by Casm
That looks like the calling convention most 32 bit C compilers use, but maybe yours is different.
You could try pushing it from left to right, instead of right to left, or try sticking _cdecl in front of the function definition. If neither of those work, you will need to consult the documentation for your compiler.
Are you using a 64 bit compiler?
Re: Calling C function from assembly
Posted: Thu Jan 24, 2013 2:55 pm
by BMW
kmain:
Code: Select all
void kmain(void* mmap_address, uint32_t mmapentrycount, uint32_t memSize)
{
SetColour(0x1F);
ClearScreen();
PrintString("Skux OS\n");
SetupInterrupts();
timer_install();
keyboard_install();
__asm__ __volatile__("sti");
//initialise physical memory manager
PrintNumberBase16(memSize >> 24);
PrintNumberBase16(memSize >> 16 & 0xFF);
PrintNumberBase16(memSize >> 8 & 0xFF);
PrintNumberBase16(memSize & 0xFF);
//Process memory map
SMAP_entry* sm = (SMAP_entry*)mmap_address;
for(unsigned int i=0;i<mmapentrycount;i++)
{
if(sm[i].Type == 1 || sm[i].Type == 3) //Free for use or ACPI reclaimable
{
//init region
}
}
for(;;)
{
__asm__ __volatile__("hlt");
}
}
This is the assembly code generated by gcc:
Code: Select all
.file "kernel.c"
.section .rodata
.LC0:
.string "Skux OS\n"
.text
.globl kmain
.type kmain, @function
kmain:
.LFB0:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
subl $40, %esp
movl $31, (%esp)
call SetColour
call ClearScreen
movl $.LC0, (%esp)
call PrintString
call SetupInterrupts
call timer_install
call keyboard_install
#APP
# 14 "kernel.c" 1
sti
# 0 "" 2
#NO_APP
movl 16(%ebp), %eax
shrl $24, %eax
movzbl %al, %eax
movl %eax, (%esp)
call PrintNumberBase16
movl 16(%ebp), %eax
shrl $16, %eax
movzbl %al, %eax
movl %eax, (%esp)
call PrintNumberBase16
movl 16(%ebp), %eax
shrl $8, %eax
movzbl %al, %eax
movl %eax, (%esp)
call PrintNumberBase16
movl 16(%ebp), %eax
movzbl %al, %eax
movl %eax, (%esp)
call PrintNumberBase16
movl 8(%ebp), %eax
movl %eax, -16(%ebp)
movl $0, -12(%ebp)
jmp .L2
.L4:
movl -12(%ebp), %edx
movl %edx, %eax
addl %eax, %eax
addl %edx, %eax
sall $3, %eax
movl %eax, %edx
movl -16(%ebp), %eax
addl %edx, %eax
movzwl 16(%eax), %eax
cmpw $1, %ax
je .L3
movl -12(%ebp), %edx
movl %edx, %eax
addl %eax, %eax
addl %edx, %eax
sall $3, %eax
.L3:
addl $1, -12(%ebp)
.L2:
movl -12(%ebp), %eax
cmpl 12(%ebp), %eax
jb .L4
.L5:
#APP
# 39 "kernel.c" 1
hlt
# 0 "" 2
#NO_APP
jmp .L5
.cfi_endproc
.LFE0:
.size kmain, .-kmain
.ident "GCC: (Ubuntu/Linaro 4.7.2-2ubuntu1) 4.7.2"
.section .note.GNU-stack,"",@progbits
And this is how I compile kmain:
Code: Select all
gcc -o kernel.o -c kernel.c -Wall -Wextra -nostdlib -nostartfiles -nodefaultlibs -fno-builtin -std=c99
I have had a look at the assembly code but I don't really understand GAS that well.
Re: Calling C function from assembly
Posted: Thu Jan 24, 2013 3:00 pm
by BMW
Casm wrote:That looks like the calling convention most 32 bit C compilers use, but maybe yours is different.
You could try pushing it from left to right, instead of right to left, or try sticking _cdecl in front of the function definition. If neither of those work, you will need to consult the documentation for your compiler.
Are you using a 64 bit compiler?
GCC doesn't like _cdecl or __cdecl....
The GCC docs say the same way
http://gcc.gnu.org/onlinedocs/gnat_ugn_ ... ntion.html
I am using 32-bit compiler.
Re: Calling C function from assembly
Posted: Thu Jan 24, 2013 3:55 pm
by iansjack
Write a simple wrapper function that calls the kmain() function. Compile to assembler code with gcc. Study that generated assembler code to see what you should be doing when you call a C function. Pay particular attention to any stack-alignment instructions.
If you want to mix assembler and C functions compiled with gcc life will be a lot easier if you use "as" rather than other assemblers (IMO).
Re: Calling C function from assembly
Posted: Thu Jan 24, 2013 5:24 pm
by BMW
iansjack wrote:Write a simple wrapper function that calls the kmain() function. Compile to assembler code with gcc. Study that generated assembler code to see what you should be doing when you call a C function. Pay particular attention to any stack-alignment instructions.
If you want to mix assembler and C functions compiled with gcc life will be a lot easier if you use "as" rather than other assemblers (IMO).
I am not switching from NASM to GAS... sorry... I hate GAS.
EDIT: Ok I compiled a call to kmain() to assembly... here it is:
Code: Select all
andl $-16, %esp
subl $16, %esp
movl $3, 8(%esp)
movl $2, 4(%esp)
movl $1, (%esp)
call kmain
If I understand it correctly, it ANDs esp with -16, subtracts 16 from esp then puts each argument on the stack (right to left) then calls the function.
I don't understand why it ANDs esp with -16???? (-16 binary = 1111111111110000) Is that the stack alignment you are talking about?
Re: Calling C function from assembly
Posted: Thu Jan 24, 2013 6:32 pm
by Casm
BMW wrote:iansjack wrote:
If I understand it correctly, it ANDs esp with -16, subtracts 16 from esp then puts each argument on the stack (right to left) then calls the function.
I don't understand why it ANDs esp with -16???? (-16 binary = 1111111111110000) Is that the stack alignment you are talking about?
I am not overly familiar with AT&T syntax, but the code following that seems to be:
Code: Select all
and esp, -16 ;if esp was 0x1234, that would make it 0x1230
sub esp, 16 ;and that would make it 0x1220 - rounded down to a 16 byte boundary
mov dwrord ptr [esp + 8], 3
mov dword ptr [esp + 4], 2
mov dword ptr [esp], 1
Which would be roughly equivalent to:
Code: Select all
and esp, -16
sub esp, 4 ;that is probably the bit which is causing the problems.
mov eax, 3
push eax
mov eax, 2
push eax
mov eax, 1
push eax
Re: Calling C function from assembly
Posted: Fri Jan 25, 2013 1:46 am
by iansjack
It's your choice to avoid using what is (IMO) the best tool for this particular job. Personally I relish the chance to learn as much as possible. You will need to understand the assembler syntax used by GCC and "as" if you want to mix C and assembler.
GCC (modern versions) require that the stack is aligned to a 16-bit boundary before a function call.
You are learning some good techniques here to aid in your progress:
1. When things go wrong start debugging. If necessary single-step through the assembler code.
2. Study the code produced by your compiler. It is very instructive. Try to understand why it does what it does. Look at every compiler switch that you use and make sure that you understand exactly what each one does.
3. Learn and use all the tools in your toolset. Have you played with "objdump" yet?
4. Read the appropriate manuals to understand how your processor, other hardware, and toolset work. Read them again. And keep them handy for reference.
5. Remember that computers work logically. Hardware failures aside, there is always a rational explanation for unexpected behaviour. Sometimes it may be a bug in the tools you are using but 99% (or more) of the time it is down to imperfect understanding.
Once you start on the complicated stuff you will need to remember those points and a whole lot more.
Re: Calling C function from assembly
Posted: Fri Jan 25, 2013 2:39 am
by Combuster
@iansjack: Was it necessary to religiously advocate the use of AT&T syntax? Yes, you need to be able to read it, but that doesn't mean you'd need to write everything in it (and besides, objdump can do intel syntax for a reason)
@BMW: there's way too much code between your "inspection" and where the values are loaded. You won't be able to easily single-step in that code to see if the values on either end are loaded as you expect - especially not in the likely case your unposted code screwed something up again.
Re: Calling C function from assembly
Posted: Fri Jan 25, 2013 3:02 am
by iansjack
@Cobuster - When I repeatedly use the expression IMO you should understand that I am expressing my personal opinion and not religiously advocating anything. I'm sorry if that is not clear.
I sincerely believe that if the OP were to learn to read and write AT&T syntax he would find life easier when interfacing C and assembler. But that's just my belief from my experience; it's not a religion. I'll try to avoid mentioning the subject in future.
Re: Calling C function from assembly
Posted: Sat Jan 26, 2013 6:28 am
by Gigasoft
There is nothing wrong with the code that was posted. Is one of the functions that kmain calls written in assembly, and changing EBP?
Re: Calling C function from assembly
Posted: Sat Jan 26, 2013 7:39 am
by iansjack
I would try to narrow the problem down. When you call the function from a C wrapper does it give the correct results? If so the problem is in the call mechanism. If the fault is with the function, try commenting out all functions called by kmain. Does it work now? (If not then something is very wrong.) If so, reneable the functions one by one until you discover where the fault lies. Then you can investigate that function, and so on.
Re: Calling C function from assembly
Posted: Sun Jan 27, 2013 4:07 am
by BMW
Ok, I reckon I will need to align the stack to a 16 bit boundary... I will try it tomorrow, too tired atm.
When calling the function and aligning stack to 16 bit boundary, I will need to store ESP aye? Or it will muck up the stack.
Re: Calling C function from assembly
Posted: Sun Jan 27, 2013 4:24 am
by Combuster
BMW wrote:16 bit boundary...
bit != byte