开发WinixJ的第一步工作我打算模仿Linux。
Linux是先将bootsect、setup、head、system等文件压缩成一个IMAGE文件,然后再dd进软盘,之后bootsect开始加载过程。我打算采用这种方法,因为这种方法可以省去将软盘做成某种格式然后用晦涩的汇编在boot中寻找loader、kernel加载。不过不同的地方也很明显:Linux0.11处理的system文件是a.out格式的二进制文件,这种文件格式非常简单,因此相对容易加载进内存;但是WinixJ在Linux3.0.0.12下开发,此时的GCC已经不支持a.out格式的输出,因此我只能将WinixJ内核编译为ELF格式的二进制文件,而ELF格式的文件相对a.out要复杂的多,因此我们开发了proc_kernel.c文件专门处理ELF格式文件,该程序产生kernel.map输出文件。
先把代码贴出:
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <unistd.h>
5 #include <sys/stat.h>
6
7 typedef unsigned int Elf32_Addr;
8 typedef unsigned short Elf32_Half;
9 typedef unsigned int Elf32_Off;
10 typedef unsigned int Elf32_Sword;
11 typedef unsigned int Elf32_Word;
12
13 #define EI_NIDENT 16
14 #define MAX_BUF_LEN 1024
15
16 //ELF文件的ELF header结构
17 typedef struct
18 {
19 unsigned char e_ident[EI_NIDENT]; //ELF魔数
20 Elf32_Half e_type; //文件类型
21 Elf32_Half e_machine; //支持的机器架构
22 Elf32_Word e_version; //版本号
23 Elf32_Addr e_entry; //程序的入口地址,在编译内核的时候可由ld程序手工指定
24 Elf32_Off e_phoff; //program header在文件中的偏移量
25 Elf32_Off e_shoff; //section header在文件中的偏移量
26 Elf32_Word e_flags; //标志
27 Elf32_Half e_ehsize; //ELF头的大小
28 Elf32_Half e_phentsize; //每个program header entry的大小
29 Elf32_Half e_phnum; //program header entry的数量
30 Elf32_Half e_shentsize; //每个section header entry的大小
31 Elf32_Half e_shnum; //section header entry的数量
32 Elf32_Half e_shstrndx;
33 } Elf32_Ehdr;
34 #define ELFHDR_LEN sizeof(Elf32_Ehdr)
35
36 //ELF文件的program header结构
37 typedef struct
38 {
39 Elf32_Word p_type; //该头所指向的program segment的类型
40 Elf32_Off p_offset; //该头所指向的program segment在文件中的偏移
41 Elf32_Addr p_vaddr; //该头所指向的program segment加载进内存后的虚拟地址
42 Elf32_Addr p_paddr; //该头所指向的program segment加载进内存后的物理地址
43 Elf32_Word p_filesz; //该头所指向的program segment在文件中的大小
44 Elf32_Word p_memsz; //该头所指向的program segment在内存中的大小
45 Elf32_Word p_flags;
46 Elf32_Word p_align;
47 }Elf32_Phdr;
48 #define PHDR_LEN sizeof(Elf32_Phdr)
49
50 //输出到kernel.map文件中的时候,对每一个program segment,
51 //都有一个Seghdr开头,表征该段的信息,包括段的大小,以及段
52 //的起始虚拟地址
53 typedef struct
54 {
55 int memsz;
56 int vaddr;
57 }Seghdr;
58 #define SEGHDR_LEN sizeof(Seghdr)
59
60 #define FILE_NAME_LEN 50
61 //缓冲区
62 unsigned char buffer[MAX_BUF_LEN];
63 char infilename[FILE_NAME_LEN];
64 char outfilename[FILE_NAME_LEN];
65 FILE *ifp, *ofp;
66
67 static void usage()
68 {
69 fprintf(stderr, "Usage: proc_kernel [-r ../boot/boot] [-w ../Image]\n");
70 }
71
72 void die()
73 {
74 fclose(ifp);
75 fclose(ofp);
76 exit(1);
77 }
78
79 static void init()
80 {
81 strcpy(infilename, "../kernel/kernel"); //默认输入文件为在顶层目录的kernel子目录中的内核文件
82 strcpy(outfilename, "../kernel/kernel.map"); //默认输出到顶层目录的kernel子目录中,文件名为kernel.map
83 }
84
85 static void proc_opt(int argc, char * const *argv)
86 {
87 int ch;
88 opterr = 0; //不显示错误信息
89
90 while ((ch = getopt(argc, argv, "r:w:h")) != -1)
91 {
92 switch (ch)
93 {
94 case 'r': //指定kernel文件名
95 strcpy(infilename, optarg);
96 break;
97 case 'w': //指定输出的系统映像文件名
98 strcpy(outfilename, optarg);
99 break;
100 case 'h':
101 usage();
102 exit(1);
103 }
104 }
105 }
106
107 static void open_file()
108 {
109 //如果输入的内核文件不存在
110 if (0 != access(infilename, F_OK))
111 {
112 fprintf(stderr, "\"%s\": No such file.\n", infilename);
113 exit(1);
114 }
115
116 //如果输出的内核映像文件已经存在,报warning
117 if (0 == access(outfilename, F_OK))
118 {
119 fprintf(stderr, "Warning: The file \"%s\" exists.\n", outfilename);
120 fprintf(stderr, "But we will go on \n");
121 }
122
123 ifp = fopen(infilename, "r+");
124 //如果不能打开输入文件
125 if (NULL == infilename)
126 {
127 fprintf(stderr, "cannot open the file \"%s\".\n", infilename);
128 exit(1);
129 }
130
131 ofp = fopen(outfilename, "w+");
132 //如果不能创建kernel.map文件
133 if (NULL == ofp)
134 {
135 fprintf(stderr, "cannot create the file \"%s\".\n", outfilename);
136 exit(1);
137 }
138 }
139
140 int main(int argc, char *const *argv)
141 {
142 int pht_offset, n, i;
143 Elf32_Ehdr elf_header;//保存elf文件头
144 Elf32_Phdr p_header_buf;
145 Seghdr seg_header_buf;
146 unsigned short loadable_seg_num = 0;
147
148 init();
149 proc_opt(argc, argv);
150 open_file();
151
152 //读出ELF文件头
153 n = fread((void *)&elf_header, ELFHDR_LEN, 1, ifp);
154
155 if (n < 1)
156 {
157 fprintf(stderr, "cannot read the \"%s\" file's ELF header!\n", infilename);
158 die();
159 }
160
161 //输出文件的文件头格式为
162 //0 1 2 3 4 5 6 7 8 9 10 11(以字节为单位)
163 //k e r n e l |入口地址| |段数|
164 n = fwrite("kernel", 6, 1, ofp);
165
166 if (n < 1)
167 {
168 fprintf(stderr, "cannot write into \"%s\".\n", outfilename);
169 die();
170 }
171
172 //entry address
173 n = fwrite((void *)(&(elf_header.e_entry)), 4, 1, ofp);
174
175 if (n < 1)
176 {
177 fprintf(stderr, "cannot write into \"%s\".\n", outfilename);
178 die();
179 }
180
181 fprintf(stdout, "entry address: %x\n", elf_header.e_entry);
182 //the number of segments
183 n = fwrite(" ", 2, 1, ofp);
184
185 if (n < 1)
186 {
187 fprintf(stderr, "cannot write into \"%s\".\n", outfilename);
188 die();
189 }
190
191 //判断输入文件是否是ELF格式文件
192 //ELF格式的文件头部的前四字节是“.ELF”的ascii码
193 if (((int *)(elf_header.e_ident))[0]!= 0x464c457f)
194 {
195 fprintf(stderr, "\"%s\" is not an ELF file!\n", infilename);
196 die();
197 }
198
199 //判断文件是否是可执行文件
200 if (elf_header.e_type != 2)
201 {
202 fprintf(stderr, "\"%s\" is not an excutable file!\n", infilename);
203 die();
204 }
205
206 //该ELF支持的机器架构是否是80386类型
207 if (elf_header.e_machine != 3)
208 {
209 fprintf(stderr, "\"%s\" is not for I386!\n", infilename);
210 die();
211 }
212
213 //输出内核文件包含的程序段信息
214 fprintf(stdout, "\"%s\"含有的程序段的段数: %d\n", infilename, elf_header.e_phnum);
215
216 for (i = 0; i < elf_header.e_phnum; ++i)
217 {
218 if (PHDR_LEN != elf_header.e_phentsize)
219 {
220 fprintf(stderr, "program header entry is confused!\n");
221 die();
222 }
223
224 fseek(ifp, elf_header.e_phoff + i * elf_header.e_phentsize, SEEK_SET);
225 n = fread(&p_header_buf, elf_header.e_phentsize, 1, ifp);
226
227 if (n < 1)
228 {
229 fprintf(stderr, "cannot read the program header entry!\n");
230 die();
231 }
232
233 fprintf(stdout, "第%d个段的段头内容:\n", i + 1);
234 fprintf(stdout, "\tp_type: 0x%x\n", p_header_buf.p_type);
235 fprintf(stdout, "\tp_offset: 0x%x\n", p_header_buf.p_offset);
236 fprintf(stdout, "\tp_vaddr: 0x%x\n", p_header_buf.p_vaddr);
237 fprintf(stdout, "\tp_paddr: 0x%x\n", p_header_buf.p_paddr);
238 fprintf(stdout, "\tp_filesz: 0x%x\n", p_header_buf.p_filesz);
239 fprintf(stdout, "\tp_memsz: 0x%x\n", p_header_buf.p_memsz);
240 fprintf(stdout, "\tp_flags: 0x%x\n", p_header_buf.p_flags);
241 fprintf(stdout, "\tp_align: 0x%x\n", p_header_buf.p_align);
242
243 if (1 != p_header_buf.p_type)//is not PT_LOAD
244 {
245 fprintf(stderr, "this segment is not loadable\n");
246 continue;
247 }
248
249 loadable_seg_num++;
250 //对每个程序段都在头部加上描述该程序段的信息头
251 //包含程序段的长度以及程序段加载到内存时的虚拟地址
252 seg_header_buf.memsz = p_header_buf.p_filesz;
253 seg_header_buf.vaddr = p_header_buf.p_vaddr;
254
255 n = fwrite((void *)&seg_header_buf, SEGHDR_LEN, 1, ofp);
256 if (1 != n)
257 {
258 fprintf(stderr, "cannot write the segment length into \"%s\".\n", outfilename);
259 die();
260 }
261
262 //将段内容写进输出文件中
263 fseek(ifp, p_header_buf.p_offset, SEEK_SET);
264
265 while (p_header_buf.p_filesz > MAX_BUF_LEN)
266 {
267 n = fread(buffer, 1, MAX_BUF_LEN, ifp);
268
269 if (MAX_BUF_LEN != n)
270 {
271 fprintf(stderr, "cannot read the segment from \"%s\".\n", infilename);
272 die();
273 }
274
275 p_header_buf.p_filesz -= MAX_BUF_LEN;
276 n = fwrite(buffer, MAX_BUF_LEN, 1, ofp);
277
278 if (1 != n)
279 {
280 fprintf(stderr, "cannot write the segment into \"%s\".\n", outfilename);
281 die();
282 }
283 }
284
285 if (p_header_buf.p_filesz > 0)
286 {
287 n = fread(buffer, p_header_buf.p_filesz, 1, ifp);
288
289 if (1 != n)
290 {
291 fprintf(stderr, "cannot read the segment from \"%s\".\n", infilename);
292 die();
293 }
294
295 n = fwrite(buffer, p_header_buf.p_filesz, 1, ofp);
296
297 if (1 != n)
298 {
299 fprintf(stderr, "cannot write the segment into \"%s\".\n", outfilename);
300 die();
301 }
302 }
303 }
304
305 //将输入文件中可加载段的段数写进输出文件头中
306 fseek(ofp, 10, SEEK_SET);
307 n = fwrite((void *)&loadable_seg_num, 2, 1, ofp);
308
309 if (1 != n)
310 {
311 fprintf(stderr, "cannot write the entry address into the \"kernel.map\" file!\n");
312 die();
313 }
314
315 fclose(ifp);
316 fclose(ofp);
317 return 0;
318 } 先看看ELF格式文件的组织形式吧:
整个ELF文件中固定的部分只有ELF头,它表征整个ELF文件的组织形式。其他的包括程序头和节头都不是固定的,甚至可以不包括。程序头和节头分别是从文件执行角度和文件链接角度来看的。因为我们只关心如何将ELF加载进内存,因此我们只关心程序头。每个程序头包括表征一个程序是否可执行以及在文件中的位置和加载到内存中时的起始虚拟地址等信息。我们需要的就是这个信息。
再看看我们想生成的map文件的组织格式:
可见文件格式还是相当简单的,因为我们的目的就是让lorder可以非常轻松的将文件加载进内存指定位置。
红色区域为头部,指定Magic Number:“kernel”,占6字节,之后是内核的入口地址,再然后是可加载程序段的段数。
之后每个可加载程序段都有一个段头,该段头指定段的大小和段在内存中的起始虚拟地址。
我们的程序核心目的就是将原始的ELF格式内核文件转变成我们定义的简单的map文件,以供loader加载。
posted on 2011-11-20 14:35
myjfm 阅读(449)
评论(0) 编辑 收藏 引用 所属分类:
操作系统