首先是 编写 elf 头文件。
下载elf_foarmat.pdf
主要是elf 文件头, 这个主要用于找到 section的地址。
/**
* ELF相关头文件及配置
*
* 作者:李述铜
* 联系邮箱: 527676163@qq.com
*/
#ifndef OS_ELF_H
#define OS_ELF_H
#include "types.h"
// ELF相关数据类型
typedef uint32_t Elf32_Addr;
typedef uint16_t Elf32_Half;
typedef uint32_t Elf32_Off;
typedef uint32_t Elf32_Sword;
typedef uint32_t Elf32_Word;
#pragma pack(1)
// ELF Header
#define EI_NIDENT 16
#define ELF_MAGIC 0x7F
typedef struct {
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_Half e_ehsize;
Elf32_Half e_phentsize;
Elf32_Half e_phnum;
Elf32_Half e_shentsize;
Elf32_Half e_shnum;
Elf32_Half e_shstrndx;
}Elf32_Ehdr;
#define PT_LOAD 1
typedef struct {
Elf32_Word p_type;
Elf32_Off p_offset;
Elf32_Addr p_vaddr;
Elf32_Addr p_paddr;
Elf32_Word p_filesz;
Elf32_Word p_memsz;
Elf32_Word p_flags;
Elf32_Word p_align;
} Elf32_Phdr;
#pragma pack()
#endif //OS_ELF_H
然后是 load_32.c 中实现对elf 文件的解析。
// 这是读取 elf 文件的 代码段与数据段的函数
static uint32_t reload_elf_file (uint8_t * file_buffer) {
// 读取的只是ELF文件,不像BIN那样可直接运行,需要从中加载出有效数据和代码
// 简单判断是否是合法的ELF文件
Elf32_Ehdr * elf_hdr = (Elf32_Ehdr *)file_buffer;
if ((elf_hdr->e_ident[0] != ELF_MAGIC) || (elf_hdr->e_ident[1] != 'E')
|| (elf_hdr->e_ident[2] != 'L') || (elf_hdr->e_ident[3] != 'F')) {
return 0;
}
// 然后从中加载程序头,将内容拷贝到相应的位置
// e_phnum 代表着 program 表 的数量, 其实就一个。
// 这里一个program 表头代表着 一个 Program 段
for (int i = 0; i < elf_hdr->e_phnum; i++) {
// 找到 program header 的地址。
// e_phoof 代表着 program 段的偏移
Elf32_Phdr * phdr = (Elf32_Phdr *)(file_buffer + elf_hdr->e_phoff) + i;
if (phdr->p_type != PT_LOAD) {
continue;
}
// 全部使用物理地址,此时分页机制还未打开
uint8_t * src = file_buffer + phdr->p_offset;
uint8_t * dest = (uint8_t *)phdr->p_paddr;
//这里复制的其实 是 .text , .data .bss 段,都复制了,疑问这几个段在一个 program 段内。
for (int j = 0; j < phdr->p_filesz; j++) {
*dest++ = *src++;
}
// memsz和filesz不同时,后续要填0
// 这是对 .bss 段的处理。
// 按理说 p_memsz 要比 p_filesz 要大的,所以 将 p_memsz - phdr->p_filesz 大的地方写0 就可以了。
dest= (uint8_t *)phdr->p_paddr + phdr->p_filesz;
for (int j = 0; j < phdr->p_memsz - phdr->p_filesz; j++) {
*dest++ = 0;
}
}
return elf_hdr->e_entry;
}
然后需要注意 bss 段的拷贝。
在 program 表内有个 p_memsz 与 p_filesz
这两的差值 ,就是需要 填0 的 .bss 段。
程序的入口地址 在elf 的头里面。
// 这里获得了 elf 的入口地址。 uint32_t kernel_entry = reload_elf_file((uint8_t *)SYS_KERNEL_LOAD_ADDR); if (kernel_entry == 0) { die(-1); } ((void (*)(boot_info_t *))kernel_entry)(&boot_info);
然后是 loader_32.c 中的死机函数。
static void die (int code) {
for (;;) {
}
}
然后去改调试文件的内容。改 launch.json, 这个地方不理解,可能跟 调试有关。
程序是可以走到最后的。
来看看 我的程序的反汇编。
感觉我的程序不对呀。但是确实能够走到最后。
先不管了。