<30天自治操作系统>(day 2) 读后感

这两天无聊,随便找个写代码的练手。就就开始读这本书了。

day.2代码部分使用intel的汇编格式,nasm 的编译器。我在想如何将它尽可能的用C来写。

考虑到我只有linux下的gcc编译器,又不想装太多,所以就在linux下来试试吧。

编译的脚本流程:

gcc -fno-pie -m16 --static -c day2.c -o day2.o
ld -f elf32 -m elf_i386 -fno-pie --script=boot.ld -shared --map --static day2.o -o day2

这里必须指定编译为16位的,这样才能在实模式下正确运行,如果是32位模式的话,调用call会出现call飞到其他位置,为了这个内容,我调试了一下午。

正常编译的可执行文件包含了很多其他用不到的内容,比如PE格式之类,对于loader而言,这些都是无法执行的,因此需要定制编译的脚本, 下面是脚本的内容:


ENTRY(_entry)
OUTPUT_FORMAT(binary)

SECTIONS
{
. = 0x7c00;

. = ALIGN(4);
.text :
{
*(.text)
*(.text.*)
}
. = ALIGN(4);
.data :
{
*(.data)
*(.data.*)

}

. = ALIGN(4);
.rodata :
{
*(.rodata)
*(.rodata.*)
}

. = ALIGN(4);
.sdata :
{
*(.sdata)
*(.sdata.*)
}

. = ALIGN(4);
.scommon :
{
*(.scommon)
*(.scommon.*)

}

. = .;

. = ALIGN(16);
__got_start = .;
.got : { *(.got) }
__got_end = .;

. = ALIGN(4);
.sbss :
{
*(.sbss)
*(.sbss.*)

}
.bss :
{
*(.bss)
*(.bss.*)

}
. = ALIGN(16);
.rel.dyn : { *(.rel.dyn) }
.dynamic :{ *(.dynamic)}

.interp : { *(.interp) }
.dynstr : { *(.dynstr) KEEP(*(.dynstr))}
.dynsym : { *(.dynsym) KEEP(*(.dynsym))}
.bootflag1 0x1FE+0x7C00 : {*(.bootflag1)}

}

其实大部分都没啥用,主要是定义入口函数为_entry, 输出格式为binary而非elf格式,代码的起始位置为0x7c00.

默认的nasm程序和gcc编译器生成的二进制文件,在制作启动盘的时候,都缺少启动磁盘和启动分区的标志。使用脚本的话,就方便很多了,可以直接将启动的标记写入到指定位置去。 也就是上面的bootflag1 和 bootflag2。因为day2中的数据非常的少,所以不考虑加载更多内容,因此将data的数据放到标记之前,这样启动的时候读取一个分区的数据就将要用到的 data放入到第一个分区中了,也因此, 这部分的代码+数据不能超过512-4个字节

下面是helloworld bootloader的c代码版本:


void _init();
void _fin();

__attribute__ ((naked)) void _entry()
{
__asm("jmp *%0":
:"r"(_init));
}

char msg[] = "\n\nhello, world\n";

void show_char(char c)
{
__asm("mov %0, %%al":
:"r"(c));
__asm("mov $0x0e, %ah");
__asm("mov $15, %bx");
__asm("int $0x10");
}

void _init()
{
int i =0;
while(i&lt;sizeof(msg)) {
show_char(msg[i]);
i++;
}
_fin();
}

void _fin()
{
__asm("hlt");
}
unsigned char __attribute__((section (".bootflag1"))) bootflag1[] __attribute__ ((aligned (1))) = {0x55,0xaa};//0xfff0aa55;

系统起始部分其实啥也没有,所以不可避免的还是要使用汇编相关的代码,只是使用C来组织下而已, 注意的是,这里的入口函数是_entry, 而非一般c语言的main函数,这在编译的自定义脚本中声明了。函数的naked属性声明,在没有使用变量的情况下,不会生成栈相关的处理。

发表评论

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据