操作系统真相还原--第七章中断实验BUG--找不到中断向量表

目录

  • 一、问题描述
  • 二、解决思路
    • [2.1 尝试从编译命令层面解决](#2.1 尝试从编译命令层面解决)
    • [2.2 反汇编kernel.bin分析原因](#2.2 反汇编kernel.bin分析原因)
    • [2.3 制作test文件验证链接是否存在问题](#2.3 制作test文件验证链接是否存在问题)
    • [2.4 修改loader.S解决问题](#2.4 修改loader.S解决问题)
  • 三、结尾

一、问题描述

在进行第七章中断实验时,出现一个问题。当使用lidt加载中断向量表命令后,使用bochs的调试命令Info idt,发现中断号对应的中断函数地址有问题:

从图中可以看到,DPL正确,选择子正确,但是基址有问题,全为0

二、解决思路

先说结论:问题出现在lodaer.S中,在加载elf文件的program段时,只运行了一次加载过程,导致只加载了.text段,也就是代码段,没加载.data段。

2.1 尝试从编译命令层面解决

完成该实验时ubuntu的gcc版本为7.5,根据有的博主描述,需要降版本到4.4,于是进行降版本,参考文章:

《操作系统真象还原》第五章 ---- 轻取物理内存容量 启用分页畅游虚拟空间 力斧直斩内核先劈一角 闲庭信步摸谈特权级

并深入学习了nasm编译和ld链接的原理,以为是链接出现了问题,于是修改编译命令,最终使用的编译命令如下:

bash 复制代码
#var for boot

#Var for GCC
# 此处可能需要加stck_protect那个编译选项
CFLAGS += -fno-builtin 
INCLUDE := \
		-Ikernel/ \
		-Iinclude/ \
		-Ilib/ \
		-Ilib/kernel/ \
    	-Ilib/kernel/include
GCC32 += -m32
GCC_MARCH += -march=i386


#Var for ld
BUILD_SRC := build/main.o \
		     build/init.o \
		 	 build/interrupt.o \
		 	 build/print.o \
		 	 build/kernel.o
BUILD_DES := build/kernel.bin
LD_ENTRY_ADDR := -Ttext 0xc0001500
LD_ENTRY_FUNC := main

#Var for gene
CURRENT_PATH := $(shell pwd)
_all:

all: _all
	@echo "==========================start building...===================================="
	@echo "=============================#1 start compile=============================="
	gcc ${GCC32} ${INCLUDE} -c ${CFLAGS} -o build/main.o kernel/main.c
	nasm -f elf -o build/print.o lib/kernel/print.S
	nasm -f elf -o build/kernel.o kernel/kernel.S
	gcc ${GCC32} ${INCLUDE} -c ${CFLAGS} -o build/interrupt.o drivers/interrupt/interrupt.c
	gcc ${GCC32} ${INCLUDE} -c ${CFLAGS} -o build/init.o init/init.c
	@echo "=============================#2 start ld=================================="
	ld -m elf_i386 ${LD_ENTRY_ADDR} -e ${LD_ENTRY_FUNC} -o ${BUILD_DES} ${BUILD_SRC}
	dd if=${CURRENT_PATH}/${BUILD_DES} of=${CURRENT_PATH}/hd60M.img bs=512 count=200 seek=9 conv=notrunc
	@echo "build successful!"
	@echo "objdump..............."
	objdump -d build/kernel.bin > build/asm/kernel.bin_gcc_4.4.7.asm

第七章实验的文件结构如下,大致仿照linux的结构来建立的文件夹:

然而,此阶段并没有解决问题,仍然存在该问题。

2.2 反汇编kernel.bin分析原因

上一步没有结果,反复对照各博主实验过程与《操作系统真相还原》书中给出的代码,可以保证C代码和汇编代码kernel.S没有任何问题。于是反汇编kernel.bin,分析问题。通过gcc的反汇编工具objdump来反汇编:

bash 复制代码
objdump -d build/kernel.bin > build/asm/kernel.bin_gcc_4.4.7.asm

结合interrupt.c中的C代码:

c 复制代码
interrupt.c:
/*创建中断门描述符*/
static void make_idt_desc(struct gate_desc* p_gdesc,uint8_t attr,intr_handler function){
    p_gdesc->func_offset_low_word=(uint32_t)function & 0x0000ffff;
    p_gdesc->selector=SELECTOR_K_CODE;   // 中断后进入内核代码
    p_gdesc->dcount=0;
    p_gdesc->attribute=attr;
    p_gdesc->func_offset_high_word=((uint32_t)function & 0xffff0000)>>16;
}

/*初始化中断描述符表*/
static void idt_desc_init(void){
    int i;
    for (i=0;i<IDT_DESC_CNT;++i){
        make_idt_desc(&idt[i],IDT_DESC_ATTR_DPL0,intr_entry_table[i]);
    }
    put_str("   idt_desc_init done\n");
}

定位汇编中的问题命令:

可以看到,中断向量函数基址作为函数调用的第三个参数,被赋值给了eax并进行压栈。在此处打断点并查看eax寄存器的值,发现为0。说明此处没有把中断处理函数基址赋值给eax寄存器。

此处令我疑惑,不可能找不到中断处理函数基址,因为链接已经成功了,可以看到反汇编后的代码中有这些中断处理函数:

此处未能发现问题,主要是汇编代码太多了,不容易分析问题。于是做了一个简单的实验,来分析gcc编译文件与nasm编译文件的链接问题

2.3 制作test文件验证链接是否存在问题

制作main_test.c:

c 复制代码
#include "stdint.h"
typedef void* intr_handler;
extern intr_handler intr_entry_table[0x21];
void main(void) 
{

  uint32_t test_low = (uint32_t)intr_entry_table[0] & 0x0000ffff;
  uint32_t test_high = ((uint32_t)intr_entry_table[0] & 0xffff0000)>>16;
  
  while(1); 
}

制作kernel_test.S:

c 复制代码
[bits 32]
[section .data]
global intr_entry_table
intr_entry_table:

%macro VECTOR 1
[section .text]

intr%1entry:                ;每个中断处理程序都要压入中断向量号,一个中断类型一个中断处理程序
   out 0xa0,al              ;向从片发送
   iret                     ;从中断返回
[section .data]
   dd intr%1entry           ;存储各个中断入口程序的地址,形成intr_entry_table数组
%endmacro

VECTOR 0x0
VECTOR 0x1
VECTOR 0x2
VECTOR 0x3

编译链接命令:

bash 复制代码
debug: _all
	@echo "==========================Debug...===================================="
	gcc ${GCC_MARCH} ${GCC32} ${INCLUDE} -c ${CFLAGS} -o debug/build/main_test.o debug/main_test.c
	nasm -f elf -o debug/build/kernel_test.o debug/kernel_test.S
	ld -m elf_i386 ${LD_ENTRY_ADDR} -e ${LD_ENTRY_FUNC} -o debug/build/kernel_test.bin debug/build/main_test.o debug/build/kernel_test.o  
	objdump -d debug/build/kernel_test.bin > debug/build/asm/kernel_test.bin.asm
	objdump -d debug/build/kernel_test.o > debug/build/asm/kernel_test.o.asm
	objdump -d debug/build/main_test.o > debug/build/asm/main_test.o.asm

debug_dd: _all
	dd if=${CURRENT_PATH}/debug/build/kernel_test.bin of=${CURRENT_PATH}/hd60M.img bs=512 count=200 seek=9 conv=notrunc

对kernel_test.bin进行反汇编,结果如下:

想说的都在汇编中加了注释了,关键就是0xc000252c这个地址中的数据,是不是intr0x0entry的起始地址,也就是是不是0xc0001520。但是在反汇编之后的文件中根本没找到0xc000252c这个地址,这也很让我疑惑。

将该kernel_test.bin载入bochs中运行,使用xp/2 0x252c(实地址,对应虚拟地址0xc000252c),查看0xc000252c这个地址中的数据,发现全是0。这个时候想起来了,在kernel.S中,把中断函数的地址,存在了data段中。所以说0xc000252c这个地址,应该是data段的地址。于是解析kernel_test.bin这个elf文件:

发现确实是data段在内存中的首地址,所以只要看一眼data段的首地址的内容(文件内偏移0x052c),就能确定是不是编译链接有问题了。通过16进制打开kernel_test.bin文件:

发现0x052c地址(对应虚拟地址0xc000252c)中是有内容的,并不是0,而且就是c0001520,也就是第0个中断处理函数的地址:

至此可以确定,编译没有问题,出问题的是data段的加载有问题,可能根本没有加载进来,也可能加载到了错误的位置。

2.4 修改loader.S解决问题

知道问题在哪了,就好改多了。回头看loader.S,错误很快就发现了:

在写loader.S时没有加loop这条命令,导致只加载了一段,也就是只加载了text段,没加载其他段。

三、结尾

这个问题说大不大说小不小,现在比较忙,每天只能抽出不到一个小时来做一下这个实验,一出现这种问题就心烦意乱,花了四五天的时间来搜索解决方案,完全不想调试。趁着周末沉下心来一看,一步步调试其实也能找到问题。

相关推荐
想放学的刺客5 分钟前
单片机嵌入式试题(第27期)设计可移植、可配置的外设驱动框架的关键要点
c语言·stm32·单片机·嵌入式硬件·物联网
Yana.nice16 分钟前
openssl将证书从p7b转换为crt格式
java·linux
AI逐月21 分钟前
tmux 常用命令总结:从入门到稳定使用的一篇实战博客
linux·服务器·ssh·php
BackCatK Chen38 分钟前
第 1 篇:软件视角扫盲|TMC2240 软件核心特性 + 学习路径(附工具清单)
c语言·stm32·单片机·学习·电机驱动·保姆级教程·tmc2240
小白跃升坊1 小时前
基于1Panel的AI运维
linux·运维·人工智能·ai大模型·教学·ai agent
跃渊Yuey1 小时前
【Linux】线程同步与互斥
linux·笔记
舰长1151 小时前
linux 实现文件共享的实现方式比较
linux·服务器·网络
zmjjdank1ng1 小时前
Linux 输出重定向
linux·运维
路由侠内网穿透.1 小时前
本地部署智能家居集成解决方案 ESPHome 并实现外部访问( Linux 版本)
linux·运维·服务器·网络协议·智能家居
梵刹古音1 小时前
【C语言】 格式控制符与输入输出函数
c语言·开发语言·嵌入式