Guide code oscillate in memory from Linux0.11

xiaoxiao2021-03-06  15

In fact, it is just a little notes, so there will be mistakes, so that computer science is a very practical science, everything will be drawn in experimental data, I will never conclusion, this also reduces the occurrence of errors.

Reading this article should have a Linux0.11 source code, boot program debugging software BOCHS (actually a virtual machine, but its debugging function is really perfect) and supporting Linux0.11 kernel IMG (Linux-0.11-devel-040329. ZIP). It is best to have a code annotation, recommend Dr. Zhao Wei's "Linux core full notes - core version 0.11". Obviously, the use of Bochs must know that please refer to Chapter 14 of the Linux Core Full Notes; after BOCHS can run correctly, use Bochsdbg to debug, and use the "Linux kernel debugging basic method". If you want to understand the operating system, you should have a certain underlying knowledge, recommend "in-depth understanding of computer systems." If you are very interested in old versions of Linux, it is recommended to go to the Oldlinux Forum, thank Dr. Zhao Wei's selfless dedication.

Establishment of debugging environment

Download Linux-0.11-Devel-040329.zip, unzip to Bochs installation directory, which contains a bochs

2.1.1 Installer and Linux kernel IMG, find 12,36 lines of Bochsrc-HD.BXRC files, modify the $ bxshare is the installation path of Bochs, if it is the upper-level directory, you can change it directly to ".", Such as:

12> RomiMage: file = .. / bios-bochs-limited, address = 0xf0000

Edit the run.bat file, change all of them:

"../bochsdbg" -q -f bochsrc-hd.bxrc

Run run.bat, start the debug tool bochsdbg.

Segmentation addresses in real mode

At 0x0000: 0X

A breakpoint is set at 7C00, which is suspended at the beginning of the Linux boot program. The command line is as follows:

VBREAK 0x0: 0X

7C00

C

(0) Breakpoint 1, 0x

7C00 (0x0: 0x

7C00)

Next At t = 16252460

(0) [0x

00007C00] 0000:

7C00 (UNK. CTXT): MOV AX, 0X

7C0; B

8C007

0x0000: 0X

7C00 is the logical address written in real mode, the method of calculating the physical address is 0x0000 << 4 0x

7C00 = 0X

00007C00, from output information can see the program in physical address 0x

00007C00 is suspended.

The boot program starts its own code from 0x

Copying 7C00 to 0x90000, this process will first put 0x

The 7C0 assignment gives DS as the source data segment, assigns the 0x9000 assignment to the ES as the destination data segment, and the data of DS: Si is copied to the ES: DI location. Now use debugging to verify this replication process and further understand the segmentation behavior in real mode. By DS: Si => 0x

7c0: 0x0 => 0x

7c0 << 4 0x0 = 0x

7C00, by es: di => 0x9000: 0x0 => 0x9000 << 4 0x0 = 0x90000, compare these two absolute addresses 0x

7C00 and 0x90000 data can verify the above replication process. The command line is as follows: X / 8 0X

7C00

[bochs]:

0x

00007C00 : 0x8e

07c0b8 0x9000b8d8 0x00b

9c08e

0x

29f62901

0x

00007c10 : 0xEAA

5F3FF 0x90000018 0xD88EC

88C

0xD08EC08E

U / 10

00007C00: (): MOV AX, 0X

7C0; B

8C007

00007c03: (): MOV DS, AX; 8ed8

00007c05: (): MOV AX, 0x9000; B80090

00007c08: (): MOV ES, AX; 8EC0

00007C

0A: (): MOV CX, 0x100; B90001

00007C0D: (): SUB Si, Si;

29f6

00007C

0F: (): SUB DI, DI; 29FF

00007C11: (): REP MOVSW WORD PTR ES: [DI], Word PTR DS: [Si];

fly

3A5

00007C13: (): JMP FAR 9000: 0018; EA18000090

00007C18: (): MOV AX, CS; 8CC8

Break 0x

7C13

C

(0) Breakpoint 2, 0x

7C

13 in ?? ()

Next at t = 16252723

(0) [0x

00007C13] 0000:

7C13 (unk. Ctxt): JMP FAR 9000: 0018; EA18000090

x / 8 0x90000

[bochs]:

0x00090000 : 0x8e

07c0b8 0x9000b8d8 0x00b

9c08e

0x

29f62901

0x00090010 : 0xEAA

5F3FF 0x90000018 0xD88EC

88C

0xD08EC08E

Seeing the JMP FAR 9000: 0018 in the disassembly code, it can see its actual effect by debugging: Set the CS segment to 0x9000, starting from the offset 0x18, that is, the EIP is 0x18. The command line is as follows:

Info R

......

EIP 0x

7C13 0X

7C13

Eflags 0x246 582

CS 0x0 0

......

N

Next At t = 16252724

(0) [0x00090018] 9000: 0018 (UNK. CTXT): MOV AX, CS; 8cc8

Info R

......

EIP 0x18 0x18

Eflags 0x246 582

CS 0x9000 36864

......

The value of the red labeled CS and EIP combine CS: EIP is a logical location in the real mode. The actual address is also calculated by CS << 4 EIP.

Establish GDT table

In the protection mode, the segment register is stored in a certain index value of the segment descriptor table, and the segment descriptor item specified by the index value contains the base address of the memory segment that requires addressing, the maximum length value of the segment and the segment. Access level and other information. The schematic of the calculated linear address is as follows:

Figure 1: Comparison mode in real mode and protection mode (taken from "Linux core full notes")

Therefore, the GDT table needs to be established before entering the protection mode, and GDTR points to the base base. Before entering the protection mode, the code in Setup.s will set the GDT table whose instructions are:

END_MOVE:

MOV AX, # setupseg! Right, Forgot this at first. Didn't work :-)

MOV DS, AX

LIDT IDT_48! Load IDt with 0,0

LGDT GDT_48! Load GDT with WhatVER APPRIATE

First enter the Setup program (0x9020: 0x0000), find the LGDT GDT_48 instruction location, continue debugging, command line as follows:

Break 0x90200

C

(0) Breakpoint 3, 0x

90200 in ?? ()

Next At t = 16483610

(0) [0x00090200] 9020: 0000 (UNK. CTXT): MOV AX, 0x9000; B80090

U / 100

......

0009029d: (): LIDT DS: 0X

12C;

0F011E

2c01

000902a2: (): LGDT DS: 0x132;

0F01163201

......

Break 0x

902A2

C

(0) Breakpoint 4, 0x

902A

2 in ?? ()

Next At t = 16750806

(0) [0x

000902a2] 9020:

00A2 (UNK. CTXT): LGDT DS: 0x132;

0F01163201

Anti-assembly code LGDT DS: 0x132 indicates that the data in the location of the DS: 0x132 is assigned to GDTR, and the LGDT requires 6 bytes, of which two bytes are the length of the GDT table, and the other 4 bytes indicate the base address of the GDT table. . The actual role of this instruction can be seen by debugging, the command line is as follows:

Info R

......

DS 0x9020 36896

......

XP / 4 0x9020: 0x132 [Bochs]:

0x00090332 : 0x03140800 0x00000009 0x00000000

0x00000000

N

Next At t = 16750807

(0) [0x

000902A7] 9020:

00A7 (UNK. CTXT): Call. 0x109; E

85F00

Dump_CPU

......

GDTR: Base = 0x90314, Limit = 0x800

IDTR: Base = 0x0, LIMIT = 0x0

......

8 bytes starting at 0x00090332 are: 0x03140800 0x00000009, the small-end method used by the Intel machine, that is, 0x0009 is a high 16 bit of the GDT table base address, 0x0314 is the low 16 bits of GDT table base, 0x0800 is the length of the GDT table. . Debug output information GDTR: base = 0x90314, limited = 0x800, verify this result. These constant data are defined in setup.s 205 to 224 lines. You can view the GDT table via the GDT table base. The command line is as follows:

x / 10 0x90314

[bochs]:

0x00090314 : 0x00000000 0x00000000 0x000007FF

0x

00C

09A00

0x00090324 : 0x000007FF 0X

00c09200 0x00000000

0x08000000

0x00090334 : 0x00090314 0x00000000

According to a descriptor 8-byte length, you have it:

0x00000000 0x00000000! Dummy

0x000007FF 0X

00C

09A00! Nuclear code segment descriptor

0x000007FF 0X

00c09200! Nuclear Data Section Descriptor

0x00000000 0x08000000!

0x00090314 0x00000000! The GDT entry is set to the constant data of the idt_48, GDT_48, in this temporary GDT table, and it is not actually indexed.

Next, you will enter the protection mode and use this temporary GDT table to address.

Segmentation addresses in protective mode

Find the code that enters the protection mode in Setup.s:

MOV AX, # 0x0001! protected mode (pe) bit

LMSW AX! This is it!

JMPI 0, 8! JMP Offset 0 of Segment 8 (CS)

The front two line instructions set the protection mode bit PE, the third line code is jumped in the addressing mode in the protection mode. First enter the program to find the location of JMPI 0, 8, and the command line is as follows:

U / 100

......

000902FE: (): MOV AX, 0x1; B80100

00090301: (): LMSW AX; 0F

01f0

00090304: (): JMP FAR 0008: 0000; EA00000800

......

jmp far 0008: 0000 actual effect instruction is set to 0x0008 cs, eip set to 0x0000, 0x0008 in the segment selector is the protection mode here, written in binary form 0000000000001000, privilege level 0 represents the first two 00, the third 0 indicates that the selectator is used to select a global descriptor table, and the high 13-bit 00000000000000 represents the first item using the global descriptor, that is, the previously mentioned core code segment selector: 0x00007FFF 0X

00C

09A00, 0x00007FFF indicates that this section is 0x0000, and the segment is limited to 0x7FFF. Commissioning verification results, the command line is as follows:

BREAK 0x00090304

C

(0) Breakpoint 5, 0x

90304 in ?? ()

Next at t = 16750869

(0) [0x00090304] 9020: 00000104 (UNK. CTXT): JMP FAR 0008: 0000; EA000008

00

N

Next At t = 16750870

(0) [0x00000000] 0008: 00000000 (UNK. CTXT): MOV EAX, 0x10; B8100000

00

After executing the JMP instruction, the program jumps to the absolute address 0x00000000, which is the logical address 0x0008: 0x00000000 in the protection mode, which is actually the code of Head.s. Head.S start code first sets the value of DS, ES, GS, FS segment register to 0x10. This selection is written into a binary form: 0000000000010000, which represents the privilege level 0, select the 2nd item of the global descriptor table, That is, the previously mentioned kernel data selector: 0x000007FF 0x

00c09200, this section base address is 0x0000, and the segment is limited to 0x7FFF.

Head.s starts to reset the IDT table and GDT table, establish method and in setup.s, and take a look at the re-established GDT table. The command line is as follows:

U / 10

00000000: (): MOV Eax, 0x10; B810000000

00000005: (): MOV DS, AX; 8ed8

00000007: (): MOV ES, AX; 8ec0

00000009: (): MOV FS, AX; 8EE0

0000000B: (): MOV GS, AX; 8EE8

0000000d: (): LSS DS: 0X

192A4; 0FB

225A4920100

00000014: (): Call. 0x6f; E856000000

00000019: (): Call. 0x

9f; E881000000

0000001E: (): MOV Eax, 0x10; B810000000

00000023: (): MOV DS, AX; 8ed8

B 0x1e

C

(0) BreakPoint 6, 0x1e in ?? ()

Next At t = 16752168

(0) [0x0000001e] 0008: 0000001E (UNK. CTXT): MOV Eax, 0x10; B8100000

00

Dump_CPU

......

GDTR: Base = 0x5cb8, limited = 0x7ff

IDTR: Base = 0x54b8, limited = 0x7ff

......

x / 10 0x5cb8

[bochs]:

0x00005cb8 : 0x00000000 0x00000000 0x00000FFF

0x

00C

09A00

0x00005cc8 : 0x00000FFF 0X

00c09200 0x00000000

0x00000000

0x00005cd8 : 0x00000000 0x00000000

The value of this GDT table is defined by constant data starting at the end of the 234 line at the end of the head .s:

_GDT: .quad 0x0000000000000000 / * Null Descriptor * /

.quad 0x

00C

09A0000000FFF / * 16MB * /

.quad 0x

00c0920000000FFF / * 16MB * /

.quad 0x000000000000000000 / * Temporary - don't use * /

.fill 252, 8, 0 / * Space for ldt's and tss's etc * /

It can be seen that the 0th item of this GDT table is undefined. The first item is the core code segment, and the second item is the kernel data segment, the third item is not defined, and the remaining 252 partial descriptions are used to place the creation task. The compament and task status section descriptor. The diagram of the descriptor table used by the Linux kernel is as follows:

Figure 2: Linux kernel usage descriptor representation (taken from "Linux core full notes)

postscript

After establishing the GDT table, the Head.s code will continue to make paging, and turn on the paging mechanism, Linux will convert the logical address into a linear address in a segmentation mechanism to convert the linear address into a physical address. At the subsequent multitasking job, 252 tails at the end of the GDT table will be filld.

Linus has a famous saying: "Read the f ** king code", in fact, debugging methods to assist reading is quite beneficial.

转载请注明原文地址:https://www.9cbs.com/read-42380.html

New Post(0)