October 07, 2013

#Lab1

这个Lab干什么

  1. 了解Linux启动流程。
  2. 通过具体代码trace启动指令.

##Exercise #1 学习,了解ATT汇编语法。

##Exercise #2 学习, 了解C语言。 特别是指针

##Exercise #3

Take a look at the lab tools guide, especially the section on GDB commands. Even if you’re familiar with GDB, this includes some esoteric GDB commands that are useful for OS work.

Set a breakpoint at address 0x7c00, which is where the boot sector will be loaded. Continue execution until that breakpoint. Trace through the code in boot/boot.S, using the source code and the disassembly file obj/boot/boot.asm to keep track of where you are. Also use the x/i command in GDB to disassemble sequences of instructions in the boot loader, and compare the original boot loader source code with both the disassembly in obj/boot/boot.asm and GDB.

  • 0x7c00是bootloader读取的第一条指令的地址。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
obj/boot/boot.asm
obj/boot/boot.out:     file format elf32-i386
Disassembly of section .text:
00007c00 <start>:
.set CR0_PE_ON,      0x1         # protected mode enable flag
.as
.globl start
start:
  .code16                     # Assemble for 16-bit mode
  cli                         # Disable interrupts
    7c00:	fa                   	cli
  cld                         # String operations increment
    7c01:	fc                   	cld
     # Set up the important data segment registers (DS, ES, SS).
  xorw    %ax,%ax             # Segment number zero
    7c02:	31 c0                	xor    %eax,%eax
  movw    %ax,%ds             # -> Data Segment
    7c04:	8e d8                	mov    %eax,%ds
  movw    %ax,%es             # -> Extra Segment
    7c06:	8e c0                	mov    %eax,%es
  movw    %ax,%ss             # -> Stack Segment
    7c08:	8e d0                	mov    %eax,%ss
  ...

Trace into bootmain() in boot/main.c, and then into readsect(). Identify the exact assembly instructions that correspond to each of the statements in readsect(). Trace through the rest of readsect() and back out into bootmain(), and identify the begin and end of the for loop that reads the remaining sectors of the kernel from the disk. Find out what code will run when the loop is finished, set a breakpoint there, and continue to that breakpoint. Then step through the remainder of the boot loader.

  • 继续追踪bootloader的启动流程, boot.结束之后会调用boot/main.c里bootmain函数.
  • 阅读boot/boot.S可知, 最后执行的语句是
1
2
3
4
    call bootmain
    7c45:	e8 c2 00 00 00
    call   7d0c <bootmain>
    //we can also verify that 0x7d0c is the address of function bootmain in boot/main.c

##Exercise #4

阅读boot/main.c的代码.

  • Load Address(LMA) & Link Address(VMA)
    • 首先用objdump查看kernel elf文件.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
objdump -h obj/kern/kernel
obj/kern/kernel:     file format elf32-i386
Sections:
Idx Name          Size      VMA       LMA       File off  Algn
  0 .text         00001a47  f0100000  00100000  00001000  2**4
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
  1 .rodata       00000714  f0101a60  00101a60  00002a60  2**5
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  2 .stab         0000390d  f0102174  00102174  00003174  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  3 .stabstr      000018a2  f0105a81  00105a81  00006a81  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  4 .data         0000a300  f0108000  00108000  00009000  2**12
                  CONTENTS, ALLOC, LOAD, DATA
  5 .bss          00000644  f0112300  00112300  00013300  2**5
                  ALLOC
  6 .comment      00000027  00000000  00000000  00013300  2**0
                  CONTENTS, READONLY
  • 一般来说, Load Address(LMA) 是程序(代码/.text)在内存中的位置, 计算机,期望Link Address(VMA) 执行(代码/.text)
  • 大部分时候, 两者是一样的. 关于这儿的0xf01000000x00100000, 在后边会有解释.

  • 预备知识
  • bootmain主要只做了这么几件事
    1. 读取kernel ELF
    2. 根据kernel ELF获取kernel的载入地址 (Load Address) , 并载入.
    3. 载入kernel之后, 根据ELF头获取kernel需要执行的函数地址, 并执行.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#define SECTSIZE    512
#define ELFHDR      ((struct Elf *) 0x10000) // scratch space
void readsect(void*, uint32_t);
void readseg(uint32_t, uint32_t, uint32_t);
void
bootmain(void)
{
    struct Proghdr *ph, *eph;
    // read 1st page off disk
    readseg((uint32_t) ELFHDR, SECTSIZE*8, 0);
    // is this a valid ELF?
    if (ELFHDR->e_magic != ELF_MAGIC)
        goto bad;
    // load each program segment (ignores ph flags)

    ph = (struct Proghdr *) ((uint8_t *) ELFHDR + ELFHDR->e_phoff);
    eph = ph + ELFHDR->e_phnum;
    for (; ph < eph; ph++)
        // p_pa is the load address of this segment (as well
        // as the physical address)
        readseg(ph->p_pa, ph->p_memsz, ph->p_offset);

    // call the entry point from the ELF header
    // note: does not return!
        ((void (*)(void)) (ELFHDR->e_entry))();

    // SUNUS: call this function and enter the kernel.
bad:
    outw(0x8A00, 0x8A00);
    outw(0x8A00, 0x8E00);
    while (1)
        /* do nothing */;
}
  • 1
    
    ((void (*)(void)) (ELFHDR->e_entry))();
    之后,我们进入了 kernel.
1
2
((void (*)(void)) (ELFHDR->e_entry))();
7d63:	ff 15 18 00 01 00    	call   *0x10018
  • 在0x7d63处下断点,之后si执行一条指令。正是 kern/entry.S 的第一条指令。 也标志着我们进入了kernel :), 通过查看obj/kern/kernel文件, 我们知道了进入kernel后的第一条指令位于 0x0010000c 处, 并且对应的文件为 kern/entry.S
1
2
3
4
5
obj/kern/kernel:     file format elf32-i386
architecture: i386, flags 0x00000112:
EXEC_P, HAS_SYMS, D_PAGED
start address 0x0010000c
    

##Exercise #7

Use QEMU and GDB to trace into the JOS kernel and stop at the movl %eax, %cr0. Examine memory at 0x00100000 and at 0xf0100000. Now, single step over that instruction using the stepi GDB command. Again, examine memory at 0x00100000 and at 0xf0100000. Make sure you understand what just happened.

What is the first instruction after the new mapping is established that would fail to work properly if the mapping weren’t in place? Comment out the movl %eax, %cr0 in kern/entry.S, trace into it, and see if you were right.

  • 这就很简单了,

    1
    
    movl %eax, %cr0
    执行完之后开启了内存映射.(boot/boot.s也对%cr0做过一次操作, 在那儿是开启32bit模式)

  • 打开kernel的反汇编文件 obj/kern/kernel.asm, 得到打开内存应试的指令的地址为 0xf010025

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
obj/kern/kernel:     file format elf32-i386
Disassembly of section .text:
f0100000 <_start+0xeffffff4>:
.globl		_start
_start = RELOC(entry)
.globl entry
entry:
	movw	$0x1234,0x472			# warm boot
f0100000:	02 b0 ad 1b 00 00    	add    0x1bad(%eax),%dh
f0100006:	00 00                	add    %al,(%eax)
f0100008:	fe 4f 52             	decb   0x52(%edi)
f010000b:	e4 66                	in     $0x66,%al
f010000c <entry>:
f010000c:	66 c7 05 72 04 00 00 	movw   $0x1234,0x472
f0100013:	34 12
	# sufficient until we set up our real page table in mem_init
	# in lab 2.
	# Load the physical address of entry_pgdir into cr3.  entry_pgdir
	# is defined in entrypgdir.c.
	movl	$(RELOC(entry_pgdir)), %eax
f0100015:	b8 00 00 11 00       	mov    $0x110000,%eax
	movl	%eax, %cr3
f010001a:	0f 22 d8             	mov    %eax,%cr3
	# Turn on paging.
	movl	%cr0, %eax
f010001d:	0f 20 c0             	mov    %cr0,%eax
	orl	$(CR0_PE|CR0_PG|CR0_WP), %eax
f0100020:	0d 01 00 01 80       	or     $0x80010001,%eax
	movl	%eax, %cr0
f0100025:	0f 22 c0             	mov    %eax,%cr0
  • 0x0010025处下断点, 与预想一致 即 0xf0100000的内存与 0x00100000内存内容相同.
    • (由于在该指令之前, 并没有打开内存映射. 所以 0xf0100025实际上还是 0x0010025 )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
=> 0x100025:	mov    %eax,%cr0
Breakpoint 2, 0x00100025 in ?? ()
(gdb) x/8x 0xf0100025
0xf0100025 <entry+25>:	0x00000000	0x00000000	0x00000000	0x00000000
0xf0100035 <relocated+6>:	0x00000000	0x00000000	0x00000000	0x00000000
(gdb) x/8x 0x00100025
0x100025:	0xb8c0220f	0xf010002f	0x00bde0ff	0xbc000000
0x100035:	0xf0110000	0x00005fe8	0x55feeb00	0x8353e589
(gdb) si
=> 0x100028:	mov    $0xf010002f,%eax
0x00100028 in ?? ()
(gdb) x/8x 0xf0100025
0xf0100025 <entry+25>:	0xb8c0220f	0xf010002f	0x00bde0ff	0xbc000000
0xf0100035 <relocated+6>:	0xf0110000	0x00005fe8	0x55feeb00	0x8353e589
(gdb) x/8x 0xf0100025
0xf0100025 <entry+25>:	0xb8c0220f	0xf010002f	0x00bde0ff	0xbc000000
0xf0100035 <relocated+6>:	0xf0110000	0x00005fe8	0x55feeb00	0x8353e589

Git: rebase is better than pull

why we should use rebase more often, 为什么要多使用rebase Continue reading

Mit 6828 lab2 Memory Management

Published on December 06, 2013

Mit 6828 lab1 ex12, Backtrace

Published on October 13, 2013