lab1——内核、Boot和printf
一.实验思考题
Thinking 1.1 也许你会发现我们的readelf 程序是不能解析之前生成的内核文件(内
核文件是可执行文件) 的,而我们刚才介绍的工具readelf 则可以解析,这是为什么
呢?(提示:尝试使用readelf -h,观察不同)
数据一个是小端存储一个是大端存储
Thinking 1.2 内核入口在什么地方?main 函数在什么地方?我们是怎么让内核进 入到想要的 main 函数的呢?又是怎么进行跨文件调用函数的呢?
内核入口在 ./boot/start.S
main函数在 ./readelf/main.c
从内核入口jal跳转到到main函数
gcc编译链接的时候,会把跨文件的函数所在的相应位置更新到jal指令上,从而实现跨文件调用
二. 实验难点图示
难点一:
1.2练习我耗时接近三小时,主要问题出在对于c语言中指针运算的理解有问题上
因为指针的运算中,指针+数字,其中数字并不反应指针类型的信息,所以我一直没有弄明白
Elf32_Off e_shoff;
段头表所在处与文件头的偏移究竟是相对于什么类型的指针的。
误以为指针和指针偏移量是和oo中对象一样一起封装起来的属性(感觉脑子糊涂了),所以一直在试图通过强制类型转化偏移量 例如(u_char)offset
来把偏移量变成和指针相匹配的数字。
看了同学的讨论我才意识到偏移量的类型其实跟指针压根就没有关系
所以题目中段头表所在处与文件头的偏移直接和文件头的 u_char* binary
相加即可得到表头段所在的地址。
(就这一行折腾了这么久...)
而指针的强制类型转化其实实现的是读取数据的方式,把指针从u_char*
强制转化成Elf32_Shdr*
即可调用结构体Elf32_Shdr
对应section header的相关属性;同时只需要每次循环+1就能移动指针到下一个相同类型的元素上。
难点二:
剩下的大部分时间基本都在琢磨printf的实现上了。
开始没有看懂开头的查找%是什么意思,以为flush the string说的是把%之前的字符串放到buf里输出出去,但又这样还得统计已经输出过的字符,非常麻烦。
又通读了几遍程序,才明白这个函数的实现是一个字符一个字符的判断,找不到%直接输出即可
之后测试的时候发现自己的printf没有输出正确的负数,输出的值特别大。
仔细研究printNum的实现原理,发现他读入的数字是 unsigned long
,所以如果负数其实是通过在函数外面先变成正数,再把negFlag置1输出负号来实现的输出。
体会与感想
lab1总计用时10个小时左右,其实难度不高,但是无奈自己太菜了,理解起来比较慢。已经写好的代码研究了比较长的时间才基本搞懂是什么意思,做完之后有一种豁然开朗的感觉。
这次的思考题感觉难度明显上升,也不太清楚应该从哪寻找相关的答案的线索,也许指导书可以给一点小提示?。
lab1extra用时两个多小时,其实不到一个小时就已经把大段和小端的数据转化写完了,只是过于粗心,把标志位5看成了4,结果一直得到很奇怪的结果,百思不得其解,耽误了大量的时间。
遗留难点/指导书反馈
难点一:
1.2练习中,如何判断结构体Elf32_Shdr Elf32_Ehdr
中的偏移量,是相对于什么指针的偏移量?(即需要把指针转化为 什么数据类型的指针之后,再加上偏移量,才能得到正确结果?)是默认为u_char
类型吗?
这一点似乎并没有在指导书中明确指出,也是造成1.2练习中花费大量时间的主要原因
难点二:
1.3练习中,源代码里的 end = . ;
有什么作用吗?
指导书上的代码似乎没有出现end的相关介绍