Aka*_*uki 7 c linux inline-assembly bootloader
我将编写我的第一个"hello world"bootloader程序.我在CodeProject网站上找到了一篇文章.这里是它的链接.
http://www.codeproject.com/Articles/664165/Writing-a-boot-loader-in-Assembly-and-C-Part
Up-to assembly level编程很顺利,但是当我用c编写程序时,与本文中给出的相同,我遇到了运行时错误.我的.c
文件中写的代码如下.
__asm__(".code16\n");
__asm__("jmpl $0x0000,$main\n");
void printstring(const char* pstr)
{
while(*pstr)
{
__asm__ __volatile__("int $0x10": :"a"(0x0e00|*pstr),"b"(0x0007));
++pstr;
}
}
void main()
{
printstring("Akatsuki9");
}
Run Code Online (Sandbox Code Playgroud)
我使用创建了图像文件floppy.img
并检查输出bochs
.它显示的是这样的东西
Booting from floppy...
S
Run Code Online (Sandbox Code Playgroud)
它应该是Akatsuki9
.我不知道我在哪里弄错了?任何人都可以帮我找到为什么我会遇到这个运行时错误?
简要回答:问题在于gcc(事实上,这是生成代码的特定应用)而不是C程序本身.它隐藏在汇编代码中.
长答案:对问题的具体细节进行更长(更精细)的解释:(
有了汇编代码会有所帮助.可以使用gcc的-S开关获取或使用我从gcc获得的那个;我最后附上了它.如果您还不知道opcode-prefixing,c-parameter传递汇编等,那么请查看以下背景信息部分.查看汇编源代码,很明显它是32位代码.GCC'.code16'为32位模式处理器生成16位代码(使用操作数大小前缀).当这个相同的精确代码在实际(即16位)模式下运行时,它被视为32位代码.这不是问题(80386及以后的处理器可以这样执行它,以前的处理器只是忽略操作数大小前缀).出现此问题的原因是gcc根据(处理器)操作的32位模式计算偏移量,这在执行引导代码时不是(默认情况下).
一些背景信息(有经验的汇编语言程序员应该跳过这个):
1.操作数大小前缀:在x86中,前缀字节(0x66,0x67等)用于获取指令的变体.0x66是操作数大小前缀,用于获取非默认操作数大小的指令; gas使用这种技术为'.code16'生成代码.例如,在实际(即16位)模式中,89 D8
对应于movw %bx,%ax
while 66 89 D8
对应movl %ebx,%eax
.这种关系在32位模式下反转.
2.参数传递C:参数在堆栈上传递并通过EBP寄存器访问.
3.来电说明:调用是一个分支操作,下一条指令的地址保存在堆栈中(用于恢复).在呼叫附近仅保存IP(在16位模式下)或EIP(在32位模式下).far Call将CS(代码段寄存器)与IP/EIP一起保存.
4.按下操作:将值保存在堆栈中.从ESP中减去对象的大小.
movl %esp, %ebp
main 开始:{{%ebp设置为等于%esp}} pushl $.LC0
从Stack Pointer中减去4 {{.LC0对char*"Akatsuki9"进行处理; 它被保存在堆栈上(由printstring函数访问)}} call printstring
从 打印字符串中的堆栈指针(16位模式; IP是2字节)中减去2pushl %ebp
:{{4从%esp中减去}} movl %esp, %ebp
{{ %ebp和%esp是目前在char + pstr }的2 + 4(= 6)字节 }} pushl %ebx
更改%esp但不是%ebp movl 8(%ebp), %edx
{{ 在'%ebp + 8处访问'pstr' }}
在%ebp + 8而不是%ebp + 6上访问'pstr' (gcc已计算出偏移量为8,假设为32位EIP); 程序刚刚获得了一个无效指针,当程序稍后解除引用时会引起问题:movsbl (%edx), %eax
.
截至目前,我不知道一个很好的解决方案,这将与gcc一起使用.对于编写引导扇区代码,我认为本机16位代码生成器更有效(如上所述,大小限制和其他怪癖).如果你坚持使用目前只生成32位模式代码的gcc,修复方法是避免传递函数参数.有关更多信息,请参阅gcc和gas手册.如果有解决方法或某些选项适用于gcc,请告诉我.
我找到了一个程序修复程序,使其在仍然使用gcc时可以达到预期目的.有点hackish明显不推荐.为什么发布呢?好吧,有点概念证明.这是:(只需用这个替换你的printstring函数)
void printstring(const char* pstr)
{
const char *hackPtr = *(const char**)((char *)&pstr-2);
while(*hackPtr)
{
__asm__ __volatile__("int $0x10": :"a"(0x0e00|*hackPtr),"b"(0x0007));
++hackPtr;
}
}
Run Code Online (Sandbox Code Playgroud)
我邀请@Akatsuki和其他人(有兴趣)来验证它是否有效.从我上面的答案和添加的C指针算法,你可以看出为什么它应该.
.file "bootl.c"
#APP
.code16
jmpl $0x0000,$main
#NO_APP
.text
.globl printstring
.type printstring, @function
printstring:
.LFB0:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
pushl %ebx
.cfi_offset 3, -12
movl 8(%ebp), %edx
movl $7, %ebx
.L2:
movsbl (%edx), %eax
testb %al, %al
je .L6
orb $14, %ah
#APP
# 8 "bootl.c" 1
int $0x10
# 0 "" 2
#NO_APP
incl %edx
jmp .L2
.L6:
popl %ebx
.cfi_restore 3
popl %ebp
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
.LFE0:
.size printstring, .-printstring
.section .rodata.str1.1,"aMS",@progbits,1
.LC0:
.string "Akatsuki9"
.section .text.startup,"ax",@progbits
.globl main
.type main, @function
main:
.LFB1:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
pushl $.LC0
call printstring
popl %eax
leave
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
.LFE1:
.size main, .-main
.ident "GCC: (Ubuntu 4.8.2-19ubuntu1) 4.8.2"
.section .note.GNU-stack,"",@progbits
Run Code Online (Sandbox Code Playgroud)