Linking a C program to an Assembly OS's system calls

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
User avatar
IanSeyler
Member
Member
Posts: 326
Joined: Mon Jul 28, 2008 9:46 am
Location: Ontario, Canada
Contact:

Linking a C program to an Assembly OS's system calls

Post by IanSeyler »

Hi everyone,

My OS BareMetal is written in x86-64 assembly and I would like to get C programs to access to OS system calls.

How can I pass the needed information in C to my OS? The assembly os_print_string function takes a string at address RSI.

Does this code look ok? Or is there a better way to do it?

Code: Select all

// gcc -o testc.o -c testc.c -m64 -nostdlib -nostartfiles -nodefaultlibs -O2 -fomit-frame-pointer
// ld -T app.ld -o testc.bin testc.o

void printstring(char* string);

int main(void)
{
	char *str = "Hello world, from C!";

	printstring (str); // Print the string using the OS system call.

	return 0;
}


// os_print_string -- Displays text
//  IN:	RSI = message location (zero-terminated string)
// OUT:	All registers perserved
void printstring(char* string)
{
	asm("nop"); // To see if we get here. Debug this to see how the string address is passed. Move to RSI if needed.
	asm("jmp 0x00020010"); // A link to os_print_string is at this address
}
This code does execute correctly but I would like to use the OS calls instead:

Code: Select all

      char *str = "Hello world, from C!", *ch;
      unsigned short *vidmem = (unsigned short*) 0xb8000;
      unsigned i;

      for (ch = str, i = 0; *ch; ch++, i++)
      {
              vidmem[i] = (unsigned char) *ch | 0x0700;
      }
BareMetal OS - http://www.returninfinity.com/
Mono-tasking 64-bit OS for x86-64 based computers, written entirely in Assembly
User avatar
Combuster
Member
Member
Posts: 9301
Joined: Wed Oct 18, 2006 3:45 am
Libera.chat IRC: [com]buster
Location: On the balcony, where I can actually keep 1½m distance
Contact:

Re: Linking a C program to an Assembly OS's system calls

Post by Combuster »

Care to explain what you didn't understand about the wiki page on System Calls?
"Certainly avoid yourself. He is a newbie and might not realize it. You'll hate his code deeply a few years down the road." - Sortie
[ My OS ] [ VDisk/SFS ]
User avatar
IanSeyler
Member
Member
Posts: 326
Joined: Mon Jul 28, 2008 9:46 am
Location: Ontario, Canada
Contact:

Re: Linking a C program to an Assembly OS's system calls

Post by IanSeyler »

Hi Combuster,

I looked at it as well as the doc on Inline Assembly but the specifics were lacking. I don't use interrupts for system calls and have no need for changing privilege levels.

I'm looking for help on getting the C->ASM "glue" to work.
BareMetal OS - http://www.returninfinity.com/
Mono-tasking 64-bit OS for x86-64 based computers, written entirely in Assembly
User avatar
Combuster
Member
Member
Posts: 9301
Joined: Wed Oct 18, 2006 3:45 am
Libera.chat IRC: [com]buster
Location: On the balcony, where I can actually keep 1½m distance
Contact:

Re: Linking a C program to an Assembly OS's system calls

Post by Combuster »

You can use interrupts and call gates within ring 0 with little to no modifications. Still, I'm missing large parts of your puzzle. By saying you don't use interrupts for system calls, you are also saying you are using something else - yet you never told us what that was.
"Certainly avoid yourself. He is a newbie and might not realize it. You'll hate his code deeply a few years down the road." - Sortie
[ My OS ] [ VDisk/SFS ]
User avatar
IanSeyler
Member
Member
Posts: 326
Joined: Mon Jul 28, 2008 9:46 am
Location: Ontario, Canada
Contact:

Re: Linking a C program to an Assembly OS's system calls

Post by IanSeyler »

Sorry that I didn't explain the issue more clearly.

BareMetal uses a call table at the start of the kernel:

Code: Select all

kernel_start:
	jmp start		; Skip over the function call index

	; Aligned for simplicity.
	align 16

	jmp os_print_string	; 0x00100010
	align 8
	jmp os_print_char
	align 8
	jmp os_print_char_hex
	align 8
	...etc...
so I just needed help with getting C to jump to the right area in the kernel. Even if the actual code behind os_print_string changes location the index will always point to it.

Here is the code that works:

Code: Select all

// gcc -o testc.o -c testc.c -m64 -nostdlib -nostartfiles -nodefaultlibs -O2 -fomit-frame-pointer
// ld -T app.ld -o testc.bin testc.o

void printstring(char *string);

int main(void)
{
	static char str[] = "Hello world, from C!";

	printstring(str);

	return 0;
}

// C call to os_print_string
// C passes the string address in RDI instead of RSI
void printstring(char *string)
{
	asm ("xchg %rsi, %rdi");
	asm ("jmp 0x00100010");
	asm ("xchg %rsi, %rdi");
}
Compiling that C program and running it under my OS is successful! Very primitive but it works.

Thanks,
-Ian
BareMetal OS - http://www.returninfinity.com/
Mono-tasking 64-bit OS for x86-64 based computers, written entirely in Assembly
User avatar
NickJohnson
Member
Member
Posts: 1249
Joined: Tue Mar 24, 2009 8:11 pm
Location: Sunnyvale, California

Re: Linking a C program to an Assembly OS's system calls

Post by NickJohnson »

You probably want to put together some assembly functions that convert the C calling convention to your "system call" format, and put them together into a library of some sort that C programs can link to. For example, if you want your printstring function to work like this:

Code: Select all

void printstring(const char *string);
and the argument needs to end up in RSI, just use something like this: (I don't know x86_64 asm very well, but you should get the idea)

Code: Select all

[bits 64]

global printstring
printstring:
push rsi
mov rsi, [rsp+16]
call 0x001000010
pop rsi
If your "system call" modifies registers without saving/restoring them, you may have to go to this sort of extreme:

Code: Select all

[bits 64]

global printstring
printstring:
push rsi
mov rsi, [rsp+16]
pusha
call 0x001000010
popa
pop rsi
User avatar
IanSeyler
Member
Member
Posts: 326
Joined: Mon Jul 28, 2008 9:46 am
Location: Ontario, Canada
Contact:

Re: Linking a C program to an Assembly OS's system calls

Post by IanSeyler »

Yes, the plan is to create a proper library so any C/C++ app can call the OS functions. I'm still figuring out the "glue" with regards to the stack.

Current code is this an it works!

Code: Select all

// gcc -o testc1.o -c testc1.c -m64 -nostdlib -nostartfiles -nodefaultlibs -fomit-frame-pointer
// ld -T app.ld -o testc1.app testc1.o

void b_print_string(const char *string);
unsigned char b_input_wait_for_key(void);

int main(void)
{
	unsigned char tchar;
	
	b_print_string("Hello world, from C!\nHit a key: ");
	tchar = b_input_wait_for_key();
	
	if (tchar == 0x61)
	{
		b_print_string("key was 'a'\n");
	}
	else
	{
		b_print_string("key was not 'a'\n");
	}

	return 0;
}


// C call to os_print_string
// C passes the string address in RDI instead of RSI
void b_print_string(const char *string)
{
	asm ("xchg %rsi, %rdi");
	asm ("call 0x00100010"); // Do a call so it returns back
	asm ("xchg %rsi, %rdi");
}

// C call to os_input_key_wait
unsigned char b_input_wait_for_key(void)
{
	unsigned char temp = 0;
	asm ("call 0x00100038");
	asm ("mov %0, %%al" :"=r"(temp)); // Return the value in AL
	return temp;
}
BareMetal OS - http://www.returninfinity.com/
Mono-tasking 64-bit OS for x86-64 based computers, written entirely in Assembly
User avatar
NickJohnson
Member
Member
Posts: 1249
Joined: Tue Mar 24, 2009 8:11 pm
Location: Sunnyvale, California

Re: Linking a C program to an Assembly OS's system calls

Post by NickJohnson »

But my point is that the functions that have to do the system calls should be in pure assembly: assembly is good at interfacing with C, but C is not very good at interfacing with assembly. Inline assembly is implementation specific, and is hard to keep really stable, because it is a hack anyway. Everything above those interface stubs can and should be C if you're making a C library.
User avatar
Owen
Member
Member
Posts: 1700
Joined: Fri Jun 13, 2008 3:21 pm
Location: Cambridge, United Kingdom
Contact:

Re: Linking a C program to an Assembly OS's system calls

Post by Owen »

Code: Select all

void b_print_string(const char *string)
{
   asm ("xchg %rsi, %rdi");
   asm ("call 0x00100010"); // Do a call so it returns back
   asm ("xchg %rsi, %rdi");
}
Aaah! For all you know, GCC might be using %rdi for something other than you expect it to. Also, it's completely allowed to reorder or even eliminate that code as it has no established side effects.

It should probably look something like

Code: Select all

inline void b_print_string(const char* str) { 
    asm volatile("call 0x00100010" :: "?"(str) : "**List clobbered registers here**");
}
(Replacing the ? with whatever the GCC instruction is for "Put this value in %rsi; I've forgotten).

That says "This instruction has side effects you don't know about (volatile), and also "don't reorder this". It's also inlined, helping GCC assign registers more efficiently.

I'm curious though - any reason why your system calls can't follow the C convention? It would be more efficient
User avatar
IanSeyler
Member
Member
Posts: 326
Joined: Mon Jul 28, 2008 9:46 am
Location: Ontario, Canada
Contact:

Re: Linking a C program to an Assembly OS's system calls

Post by IanSeyler »

NickJohnson,

Your way would be ideal but I still need to figure out how to link that kind of code in C. It looks like the MikeOS C library does it that way. The OS is x86-64 only so cross-compatibility is not an issue (as long as GCC always works the same way).

Owen,

You are correct about GCC. Who knows what it might actually use. Taking your advice the code is now this (the address of the string is copied to the RSI register):

Code: Select all

// C call to os_print_string
void b_print_string_new(const char *string)
{
        asm volatile ("call 0x00100010" :: "S"(string)); // Do a call so it returns back
}
Almost all of my system calls preserve the registers. Except for things like returning the length of a string or key from input via keyboard. How do you mean the "C convention"? I wrote my system calls in a way that made the most sense in x86-64 assembly (Print a string that is located at the source (RSI), Write keyboard input to the location at the destination (RDI), return a character count in RCX, etc..).

Thanks,
-Ian
BareMetal OS - http://www.returninfinity.com/
Mono-tasking 64-bit OS for x86-64 based computers, written entirely in Assembly
User avatar
Brynet-Inc
Member
Member
Posts: 2426
Joined: Tue Oct 17, 2006 9:29 pm
Libera.chat IRC: brynet
Location: Canada
Contact:

Re: Linking a C program to an Assembly OS's system calls

Post by Brynet-Inc »

ReturnInfinity wrote:How do you mean the "C convention"?
Calling conventions... juicy behind the scenes details that the compiler handles, how arguments are passed to different routines.. does the caller or callee clean things up?

http://en.wikipedia.org/wiki/X86_calling_conventions

Perhaps you should try disassembling something compiled by the compiler, on x86 the "cdecl" convention is common.. but different compilers and operating systems use different conventions.

Writing these low level wrappers in assembly is a good idea, if you handle the prologue and epilogue code right.. then it should be simple to call it from C, and you don't risk the compiler optimizing away important details.

Does that help clarify things?
Image
Twitter: @canadianbryan. Award by smcerm, I stole it. Original was larger.
User avatar
IanSeyler
Member
Member
Posts: 326
Joined: Mon Jul 28, 2008 9:46 am
Location: Ontario, Canada
Contact:

Re: Linking a C program to an Assembly OS's system calls

Post by IanSeyler »

That helps a lot. Judging from what I see in Bochs as the C program runs it is definitely "cdecl".

Thank-you very much,
-Ian
BareMetal OS - http://www.returninfinity.com/
Mono-tasking 64-bit OS for x86-64 based computers, written entirely in Assembly
User avatar
Owen
Member
Member
Posts: 1700
Joined: Fri Jun 13, 2008 3:21 pm
Location: Cambridge, United Kingdom
Contact:

Re: Linking a C program to an Assembly OS's system calls

Post by Owen »

I'd still inline the code; then GCC should be able to generate code with speed close to that of a native call.

And it should be using the AMD64 ABI convention.
User avatar
IanSeyler
Member
Member
Posts: 326
Joined: Mon Jul 28, 2008 9:46 am
Location: Ontario, Canada
Contact:

Re: Linking a C program to an Assembly OS's system calls

Post by IanSeyler »

I tried the inline option but it made no difference to the final program file. It looks like it does the same thing with or without that option.

I am compiling to a flat binary but I don't see that causing a problem.

EDIT: And I will look into the x86-64 ABI specs. I think R8 and R9 are included (as well as RAX, RCX, and RDX) now.
BareMetal OS - http://www.returninfinity.com/
Mono-tasking 64-bit OS for x86-64 based computers, written entirely in Assembly
User avatar
Owen
Member
Member
Posts: 1700
Joined: Fri Jun 13, 2008 3:21 pm
Location: Cambridge, United Kingdom
Contact:

Re: Linking a C program to an Assembly OS's system calls

Post by Owen »

What optimization options did you pass GCC? It won't inline code unless optimizations are enabled (Or you use __attribute__((forceinline)), (or is that alwaysinline?))
Post Reply