从引导加载程序调用C代码
我想写一个引导程序。我想编译一些C代码,这样引导加载程序就可以将其加载到内存中并跳转到内存中 我有两个问题:从引导加载程序调用C代码,c,gcc,assembly,x86,bootloader,C,Gcc,Assembly,X86,Bootloader,我想写一个引导程序。我想编译一些C代码,这样引导加载程序就可以将其加载到内存中并跳转到内存中 我有两个问题: 调用约定与x86上的相同吗?即堆栈上的参数,从左到右 如何使用gcc生成原始二进制文件 调用约定类似于IA32,只是所有内容都是以16位字而不是32位字推送的 您可能无法使用GCC直接创建平面二进制文件。GCC是否仍然支持16位Intel机器?不过,您应该能够使用从链接对象中获取原始二进制数据。你可能也想调查一下 8086是x86。8088/86使用了不同的型号,小型、中型、大型和大
- 调用约定与x86上的相同吗?即堆栈上的参数,从左到右
- 如何使用gcc生成原始二进制文件
cdecl
,stdcall
,fastcall
,等等)。你在用什么编译器
第三,gcc不会将代码编译为16位x86指令
正如@dwelch所建议的,使用开放式Watcom C/C++或古老的Borland/Turbo C/C++,它们是免费的,可以编译16位代码
下面是如何完成这一切的。根据您前面的问题,我假设您希望为现代x86机器(即386或更高版本)创建引导加载程序 在实模式下,默认操作数和地址大小为16位。不幸的是,GCC无法生成16位x86汇编代码。但是,通过将指令
.code16gcc
放在每个文件的顶部,您可以告诉as
使用将覆盖地址和操作数大小的指令前缀。英特尔64和IA-32体系结构软件开发人员手册第1卷第3.3.5节详细介绍了这些前缀
有关.code16gcc
的更多信息,请参见。请注意,本手册来自2003年,并且.code16gcc
不再是实验性的,或者至少稳定到足以供Linux使用
由于gcc不知道如何处理汇编代码,所以调用约定将保持不变。是可用于生成引导加载程序的ld脚本。您可以使用链接器脚本使用gcc链接器创建普通二进制文件。 关键是输出_格式(二进制)指令:
//========================================
FILE: linker.ld
//========================================
OUTPUT_FORMAT(binary)
SECTIONS {
.text : { *(.text) }
.data : { *(.data) }
.bss : { *(.bss) }
}
//========================================
我在makefile中调用了链接器,如下所示(而linker.ld是链接器脚本文件):
我已经用
//========================================
gcc -nostdinc -nostdlib -ffreestanding -c <code files> -o theObjectCode.o
//========================================
汇编代码调用C语言文件loaderMain.C中定义的符号。
为了生成16位模式兼容的代码,您必须在使用的每个C文件的第一行代码之前声明16位指令集的用法。这只能通过内联汇编指令AFAIK完成:
asm(".code16gcc\n"); // use 16bit real mode code set
/* ... some C code .. */
// ... and here is the C entry code ... //
void loaderMain() {
uint cmdlen = 0;
bool terminate = false;
print(NL);
print(NL);
print("*** EOS LOADER has taken over control. ***\r\n\r\n");
print("Enter commands on the command line below.\r\n");
print("Command are executed by pressing the <ENTER> key.\r\n");
print("The command \'help\' shows a list of all EOS LOADER commands.\r\n");
print("HAVE FUN!\r\n");
print(NL);
while (!terminate) {
print("EOS:>");
cmdlen = readLine();
buffer[cmdlen] = '\0';
print(NL);
terminate = command();
}
shutdown();
}
顺便说一句,函数的C头声明如下所示:
//=====================
void kbdread(char* pc, (unsigned char)* psc);
//=====================
希望这能有所帮助。干杯。您可能需要检查编译器。我很久没有构建Linux内核了,但我记得2.2系列使用它来编译一些引导代码。还有一个。GCC4.3有一个16位x86补丁:请参阅。
asm(".code16gcc\n"); // use 16bit real mode code set
/* ... some C code .. */
// ... and here is the C entry code ... //
void loaderMain() {
uint cmdlen = 0;
bool terminate = false;
print(NL);
print(NL);
print("*** EOS LOADER has taken over control. ***\r\n\r\n");
print("Enter commands on the command line below.\r\n");
print("Command are executed by pressing the <ENTER> key.\r\n");
print("The command \'help\' shows a list of all EOS LOADER commands.\r\n");
print("HAVE FUN!\r\n");
print(NL);
while (!terminate) {
print("EOS:>");
cmdlen = readLine();
buffer[cmdlen] = '\0';
print(NL);
terminate = command();
}
shutdown();
}
//======================
.text
.code16gcc
.globl kbdread // declares a global symbol so that the function can be called from C
.type kbdread, @function // declares the symbol as a function
kbdread: // the entry point label which has to the same as the symbol
// this is the conventional stack frame for function entry
pushl %ebp
movl %esp, %ebp
// memory space for local variables would be allocated by decrementing the stack pointer accordingly
// the parameter arguments are being addressed by the base pointer which points to the same address while bein within the function
pushw %ds // I'm paranoid, I know...
pushw %es
pushw %fs
pushl %eax
pushl %ebx
pushl %ecx
pushl %edx
pushl %esi
pushl %edi
xorl %eax, %eax // calls the keyboard interrupt in order to read char code and scan code
int $0x16
xorl %edi, %edi
movl 8(%ebp), %edi // moves the pointer to the memory location in which the char code will be stored into EDI
movb %al, (%edi) // moves the char code from AL to the memory location to which EDI points
xorl %edi, %edi // paranoid again (but who knows how well the bios handles extended registers??)..
movl 12(%ebp), %edi // moves the pointer to the memory location in which the scan code will be stored into EDI
movb %ah, (%edi) // moves the scan code from AH to the memory location to which EDI points
popl %edi // restoring the values from stack..
popl %esi
popl %edx
popl %ecx
popl %ebx
popl %eax
popw %fs
popw %es
popw %ds
leave // .. and the conventional end frame for functions.
ret // be aware that you are responsible to restore the stack when you have declared local variables on the stack ponter.
// the leave instruction is a convenience method to do that. but it is part of not early X86 instruction set (as well as extended registers)
// so be careful which instruftion you actually use if you have to stay compatible with older computer models.
//=====================
//=====================
void kbdread(char* pc, (unsigned char)* psc);
//=====================