Chinaunix首页 | 论坛 | 博客
  • 博客访问: 488853
  • 博文数量: 164
  • 博客积分: 4024
  • 博客等级: 上校
  • 技术积分: 1580
  • 用 户 组: 普通用户
  • 注册时间: 2009-10-10 16:27
文章分类

全部博文(164)

文章存档

2011年(1)

2010年(108)

2009年(55)

我的朋友

分类:

2010-06-28 13:50:39

【linux 下编程】

一、用NASM与C语言在LINUX平台编程

初次接触linux 还真有点适应困难,那么现在就来慢慢适用它吧!

(在linux 下访问windows共享文件夹可以:

mount -t smbfs -o username=name,password=pwd,ip=192.168.*.* //MachineName/share /home/share)

在linux 用nasm 编写一个Hello World! 需要的:

1、编译:nasm -f elf 386.asm -o a.o   编译一个elf(可执行连接格式)文件,它是32位intel标准可移持二进制格式。

2、链接:ld -s a.o -o a       链接成 elf文件

3、运行 ./a 就可以运行了。

那么就来个asm 与c 同步编写代码吧.

;------------386.asm-----------

extern choose ; int choose(int a, int b);导入函数


[section .data] ; 全局数据段

num1st   dd 3
num2nd   dd 4


[section .text] ; 代码段

global _start ; 我们必须导出 _start 这个入口,以便让链接器识别。
global myprint ; 导出这个函数为了让 bar.c 使用

_start:
push num2nd   ; ┓
push num1st   ; ┃
call choose   ; ┣ choose(num1st, num2nd);
add esp, 4   ; ┛

mov ebx, 0
mov eax, 1   ; sys_exit
int 0x80   ; 系统调用

; void myprint(char* msg, int len)
myprint:
mov edx, [esp + 8] ; len
mov ecx, [esp + 4] ; msg
mov ebx, 1
mov eax, 4   ; sys_write
int 0x80   ; 系统调用
ret

;------------------bar.c-------------------
void myprint(char* msg, int len);

int choose(int a, int b)
{
if(a >= b){
   myprint("the 1st one\n", 13);
}
else{
   myprint("the 2nd one\n", 13);
}

return 0;
}

可以看到在汇编代码区有两重要的说明。

一个是Global (导出),再一个是extern(导入)。导出的作用是将当前函数名提供给其它二进制代码块来调用。

不管是汇编语言还是C语言写程序 最终都会汇编或者编译成二进制链接文件.那么此时就可以将这两个二进制块链接起来。定义外部导入函数也好内部导出也好。都是在链接的时候将对应的函数引用改成对应的二进制代码块地址。

;-------------------------------------------------------------------------------------------------------------------

在linux 平台分别用:

nasm -f elf 386.asm -o 386.o ;汇编成elf格式的链接文件

gcc -c bar.c -o bar.o               ;gcc 编译成elf格式的链接文件

ld -s 386.o bar.o -o 386bar       ;链接386.o与bar.o,变成ELF格式文件 386bar 它可以直接在linux 平台运行。

二、ELF(Executable Linkable Format)可执行可链接文件格式.

下面就是ELF的头部信息:

#define EI_NIDENT 16
typedef struct{
unsigned char e_ident[EI_NIDENT];
Elf32_Half e_type;             
Elf32_Half e_machine;
Elf32_Word e_version;
Elf32_Addr e_entry;    
Elf32_Off e_phoff;
Elf32_Off e_shoff;
Elf32_Word e_flags;
Elf32_Haif e_ehsize;
Elf32_Haif e_phentsize;
Elf32_Haif e_phnum;
Elf32_Haif e_shentsize;
Elf32_Haif e_shnum;
Elf32_Haif e_shstrndx;
}Elf32_Ehdr;

为了支持从8位到32位不同架构的处理器,定义了多个字段。以达到支持与机器无关执行代码。

下面就说说这个ELF头结构体各字段的具体意义:

1、e_ident字段:

它里面包含了字符串"ELF",还有其他一些与指令无关的信息。

2、e_type字段:

它标识的是该文件的类型,等于2表示他是exectuable file可执行程序。

3、e_machine字段:

它表示该程序运行的硬件平台,如果是3表示运行在intel 80386体系上。

4、e_version字段:

它表示文件版本信息.

5、e_entry字段:

它表示程序的入口地址,也就是线性地址,一般的地址装入到 0x80488080。

6、e_phoff字段:

program header table 在文件的偏移量(字节单位).。

7、e_shoff字段:

Section header table 在文件中的偏移量(字节单位)。

8、e_flags字段:

对IA32(intel 32)来说此项目为0.

9、e_ehsize字段:

它表示ELF header的大小(字节)。

10、e_phentsize字段:

Program header table 中每一条目的大小。

11、e_phnum字段:

Program header条目总数量。

12、e_shentsize字段:

Section header 单个条目的大小.

13、e_shnum字段:

Section header 条目总数量

14、e_shstrdx字段:

含有所有节名称字符串表的节是在哪个Section条目号,(从零开始数),

;-------------------------------------------------------------------------------------------------------------------

好了一个ELF Header信息说完了。它的大小也就是0x34个字节。接触过WINDOWS PE格式的朋友肯定清楚 它的结构作用与PE有点类似。都是由操作系统的装载器载入的。

ELF Header信息保护了很多文件本身信息,那么这个程序的运行信息保存在哪呢?

它保存在Program Header Table里面。其中一个Program header项就代表一个段。

它的结构是这样的(它的偏移位置在e_phoff处):

typef struct{

               Elf32_Word        p_type;段类型

                Elf32_ Off         p_offset;段的第一个字节在文件中的偏移

                 Elf32_Addr        p_vaddr ; 相当内存的VA值

                 Elf32_Addr        p_paddr ; 物理地址保留

                 Elf32_Word        p_filesz ;段在文件中占用的大小

                 Elf32_Word        p_memsz ;段在内存中的大小

                 Elf32_Word        p_flags ;与段相关的标志

                 Elf32_Word        p_aglin ;段对齐方式

}Elf32_Phdr;

链接了ELF Header 与Program Header Table 后我们就可以正常的加载ELF文件到内存中了。

那么接下就开始行动.

;----------------------------------------------------------------------------------------------------------------------

三、加载C语言编写的ELF文件内核到内存:

1、准备引导程序boot.asm,

它的功能是引导一个软驱。并且在软驱的根目下寻找 loader.bin (用来加载Kernel.bin内核的加载器程序);

找到loader.bin 后并把主控权交给Loader.bin。

2、准备加载内核的加载器Loader.asm.

它是从引导程序跳转过来的。这段程序的主要功能是在根目录下寻址Kernel.bin内核程序。

找到Kernel.bin内核模块后。交控制权交给Kernel.bin。

3、准备Kernel.asm 内核代码块。

它是被Loader加载到8000h物理地址段的。它的大部分模块可以用C语言来写.只要在开始的汇编程序里导出

global _start 就行了。

导出用关键字global.

导入用关键字extern

可以看出来 现在我们的程序 已经从引导到加载内核再到系统内核了。那么接下来的工作就是要编写内核代码了。不过既然我们是386保护模式编程,那么在编写内核之前还要在Loader.bin 里转换一个进入386保护模式。!

可以在Loader.asm 里面加入跳转代码:

; 加载 GDTR
lgdt [GdtPtr]

...

; 准备切换到保护模式
mov eax, cr0
or eax, 1
mov cr0, eax
jmp   SeclectorFlatC:(BaseOfLoaderPhyAddr + LABEL_PM_START)

跳到Loader.bin里面的保护模式段内。进行保护模式的初始化工作.

在保护模式的中断。TSS 等一些毕业的信息定义好后,就可以加载Kernel 内核到内存中了。那么这个时候的主控制权就交给了ELF格式的Kernel 文件了.

四、【总结】:

从开机引导到装入Kernel 转入保护模式具体步骤:

1、Boot.bin

Boot.bin 里面包含了引导扇区的信息,它是FAT12文件系统.

它主功能就是被BIOS装入7c00,然后执行本身的指令。boot在FAT12文件系统下的根目录搜寻Loader.bin程序。其搜索方式是按扇区方式查找的。在七中已经对FAT12有了详细的介绍。找到Loader.bin 文件后。将它加载到指定的内存。并且跳转到Loader.bin文件里的第一个字节。到此为止Boot.bin的引导功能就全部完成了。

2、Loader.bin

它是被Boot.bin引导程序加载到内存的,它的主要功能也是在根目录下搜索文件。不过它搜索的是Kernel.bin 内核文件。当成功找到后。并不会马上跳转到Kernel.bin .而是在自己的段中进行386保护模式的初始化工作。进行 GDT加载。IDT加载,TSS加载、cr3分页开启等一系列保护模式需要的准备工作。当准备工作做完后 ,才考虑跳转到Kernel 内核中去。

第一步跳到Kernel时,它从第一个字节就开始运行了。不过在进入Kernel里面后就需要用到ELF格式的内核程序了。因为现在我们可以用C语言编写内核了。需要根据ELF文件格式找到对应代码段数据。并且把它装入到指定的内存中去 然后才交给它主控制权。

那么就在下章具体的了解 Loader.bin 跳转到 Kernel(ELF格式内核)。

阅读(1037) | 评论(0) | 转发(0) |
0

上一篇:保护模式编程七

下一篇:保护模式编程九

给主人留下些什么吧!~~