C 程序集代码与代码的gdb显示不同
我从《从0到1的操作系统》一书中学习了操作系统,我试图在我的内核main中显示代码,但是在GDB中显示的代码并不相同,尽管我跳到了作为入口点的地址 bootloader.asmC 程序集代码与代码的gdb显示不同,c,assembly,gdb,kernel,bootloader,C,Assembly,Gdb,Kernel,Bootloader,我从《从0到1的操作系统》一书中学习了操作系统,我试图在我的内核main中显示代码,但是在GDB中显示的代码并不相同,尽管我跳到了作为入口点的地址 bootloader.asm ;************************************************* ; bootloader.asm ; A Simple Bootloader ;************************************************* bits 16 start: jmp
;*************************************************
; bootloader.asm
; A Simple Bootloader
;*************************************************
bits 16
start: jmp boot
;; constants and variable definitions
msg db "Welcome to My Operating System!", 0ah, 0dh, 0h
boot:
cli ; no interrupts
cld ; all that we need to init
mov ax, 0x0000
;; set buffer
mov es, ax
mov bx, 0x0600
mov al, 1 ; read one sector
mov ch, 0 ; track 0
mov cl, 2 ; sector to read
mov dh, 0 ; head number
mov dl, 0 ; drive number
mov ah, 0x02 ; read sectors from disk
int 0x13 ; call the BIOS routine
jmp 0x0000:0x0600 ; jump and execute the sector!
hlt ; halt the system
; We have to be 512 bytes. Clear the rest of the bytes with 0
times 510 - ($-$$) db 0
dw 0xAA55 ; Boot Signature
readelf-l主
ELF Header:
Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
Class: ELF32
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: Intel 80386
Version: 0x1
Entry point address: 0x600
Start of program headers: 52 (bytes into file)
Start of section headers: 12888 (bytes into file)
Flags: 0x0
Size of this header: 52 (bytes)
Size of program headers: 32 (bytes)
Number of program headers: 3
Size of section headers: 40 (bytes)
Number of section headers: 12
Section header string table index: 11
Elf file type is EXEC (Executable file)
Entry point 0x600
There are 3 program headers, starting at offset 52
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
PHDR 0x000000 0x00000000 0x00000000 0x00094 0x00094 R 0x4
LOAD 0x000000 0x00000000 0x00000000 0x00094 0x00094 R 0x4
LOAD 0x000100 0x00000600 0x00000600 0x00006 0x00006 R E 0x100
Section to Segment mapping:
Segment Sections...
00
01
02 .text
readelf-l主
ELF Header:
Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
Class: ELF32
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: Intel 80386
Version: 0x1
Entry point address: 0x600
Start of program headers: 52 (bytes into file)
Start of section headers: 12888 (bytes into file)
Flags: 0x0
Size of this header: 52 (bytes)
Size of program headers: 32 (bytes)
Number of program headers: 3
Size of section headers: 40 (bytes)
Number of section headers: 12
Section header string table index: 11
Elf file type is EXEC (Executable file)
Entry point 0x600
There are 3 program headers, starting at offset 52
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
PHDR 0x000000 0x00000000 0x00000000 0x00094 0x00094 R 0x4
LOAD 0x000000 0x00000000 0x00000000 0x00094 0x00094 R 0x4
LOAD 0x000100 0x00000600 0x00000600 0x00006 0x00006 R E 0x100
Section to Segment mapping:
Segment Sections...
00
01
02 .text
main.c
void main(){}
objdump-z-M intel-S-D build/os/main
Disassembly of section .text:
00000600 <main>:
void main(){}
600: 55 push ebp
601: 89 e5 mov ebp,esp
603: 90 nop
604: 5d pop ebp
605: c3 ret
显示主代码的gdb代码
set architecture i8086
target remote localhost:26000
b *0x7c00
set disassembly-flavor intel
layout asm
layout reg
symbol-file build/os/main
b main
jg/dec esp/inc esi
是ELF幻数,而不是机器码!从ndissm-b32/bin/ls
的输出开始,您将看到相同的内容。(ndisam
始终将其输入视为一个简单的二进制文件;它不查找任何元数据。)
7F 45 4C 46是0x7F字节后的字符串“ELF”
,是将文件格式标识为ELF的ELF幻数。在main
的实际机器代码之前,后跟更多的ELF头字节objdump-D
反汇编所有ELF节,但它仍然解析ELF头,而不像ndisam
那样反汇编它们。因此,您最终还是看到了.text
部分的代码,因为其他部分都是空的(因为您链接了这个可执行文件,没有libc或CRT startfiles,并且以C main作为ELF入口点?!?)
您正在跳转到ELF文件的开头,就像它是一个平面二进制文件一样。事实并非如此,编写ELF程序加载器并不是那么简单。ELF程序头(可以解析的readelf
文件头)告诉您哪个文件偏移量位于哪个地址。.text
部分的开头将在文件中的某个偏移处,由于明显的原因,不会与ELF幻数重叠。(尽管如果您能找到合适的方法,它可能与ELF标题重叠:)
然后,一旦按照程序头中的指定将文件映射到内存中,就跳转到ELF入口点地址(本例中为0x600)。(这通常不是一个函数;在像Linux这样的真实操作系统中,你不能从入口点ret
。相反,你需要进行退出系统调用。)你也不能在这里,因为你可以jmp
调用它,而不是call
这就是为什么\u start
与main
分开的原因;使用编译器生成的main
作为其入口点构建程序是不可行的
当然,这项工作的大部分都是注定的,因为您要在CPU仍处于16位实模式的情况下跳转到main。但您的main是针对32位模式编译/组装的。您可以使用gcc-m16
来解决这一问题,以便为16位模式组装gcc输出,必要时使用操作数大小+地址大小前缀
不执行任何操作的主设备的机器代码实际上将在16位和32位模式下工作。如果未经优化就使用返回0
,情况就不会是这样:mov eax、imm32的操作码(不带前缀)意味着不同的指令长度,这取决于CPU在何种模式下解码,因此在16位模式下解码将写入AX,并留下2字节的零
最容易做的事情可能是将“内核”转换为平面二进制文件,而不是在引导加载程序中编写ELF程序加载程序。遵循osdev教程,因为很多东西都可能出错,例如,您必须注意静态数据 或者参见一个在切换到32位保护模式后调用C函数的引导加载程序示例
请参阅中的更多链接。您使用了哪些命令将源代码构建到可执行文件中,以及您是如何在任何东西上运行GDB的?在QEMU遥控器中?你有没有可能用ASCII文本覆盖代码?检查“代码”的hextump,查看数据是否与其他内容类似。(虽然这不太可能,但对于
addr/m32,r32
是01
)是的,我在qemu环境中运行了它。我通过本地主机将程序与gdb连接。我不知道你用ASCII文本覆盖代码是什么意思。你的命令(特别是gcc-m16
)与你的objdump
输出不匹配-m16
将使用66 55 push ebp
等等:16位模式下的32位操作数大小,使用66前缀。(objdump将其反汇编为push bp
,因为它位于32位ELF可执行文件中)。喜欢上了。您的objdump输出看起来像是使用-m32
编译的。这并不是说它改变了答案,除了关于需要-m16
的部分,但它意味着你的问题不是自相矛盾的。