5.16 加载内核映像文件(2)

首先是 编写 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, 这个地方不理解,可能跟 调试有关。

程序是可以走到最后的。

来看看 我的程序的反汇编。

感觉我的程序不对呀。但是确实能够走到最后。

先不管了。

相关推荐
八荒我为王2 分钟前
c#编码技巧(十九):各种集合特点汇总
c#·.net
Crossoads14 分钟前
【汇编语言】call 和 ret 指令(一) —— 探讨汇编中的ret和retf指令以及call指令及其多种转移方式
android·开发语言·javascript·汇编·人工智能·数据挖掘·c#
linweidong16 分钟前
MariaDB面试题及参考答案
linux·运维·数据库·负载均衡·dba·mariadb·后端面试
杰哥的技术杂货铺1 小时前
Centos 7 安装 Docker 最新版本
linux·docker·centos
jerry-891 小时前
CentOS 7安装SSHFS 实现远程主机目录 挂载为本地目录
linux·运维·centos
桑榆肖物2 小时前
将 .NET Aspire 添加到现有应用:前端 JavaScript 项目处理
前端·javascript·.net
Labiod4 小时前
PlantUML 安装
linux·运维·服务器
命里有定数6 小时前
Ubuntu问题 -- 设置ubuntu的IP为静态IP (图形化界面设置) 小白友好
linux·tcp/ip·ubuntu·ip
荆棘鸟骑士7 小时前
Linux修改/etc/hosts不起作用(ping: xxx: Name or service not known)的解决方法——开启NSCD
linux
猫猫不是喵喵.7 小时前
【Linux】Linux入门实操——进程管理(重点)
linux·运维·服务器