- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
切换到保护模式后,我试图从我的引导加载程序跳转到我加载的内核。
内核加载正常并且位于正确的位置,但是当加载程序的第二阶段调用内核的主函数时,它调用了错误的地址。
这是加载器的第二阶段(loader.asm
):
global load_kern
extern main
[bits 32]
load_kern:
call main
cli
hlt
然后我将其与一个 c 目标文件进行汇编并链接以创建一个 elf 可执行文件:
nasm -f elf32 loader.asm -o loader.o
ld -melf_i386 -n -Tos.lds loader.o os.o -o kernel.elf
我使用这个文件来链接它们:
ENTRY(load_kern);
PHDRS
{
headers PT_PHDR FILEHDR PHDRS;
code PT_LOAD;
}
SECTIONS
{
.text 0x500: ALIGN(0x100) { *(.text) } :code
.data : { *(.data) }
.bss : { *(.bss) }
/DISCARD/ : { *(.eh_frame) }
}
然后我将这个 kernel.elf
放在我的软盘镜像的第二个扇区(在引导扇区之后)。
当我 objdump kernel.elf
输出是正确的:
os/kernel.elf: file format elf32-i386
Disassembly of section .text:
00000500 <load_kern>:
500: e8 43 00 00 00 call 548 <main>
505: fa cli
506: f4 hlt
...
00000548 <main>:
548: 55 push %ebp
549: 89 e5 mov %esp,%ebp
54b: 68 5c 05 00 00 push $0x55c
550: 6a 05 push $0x5
552: e8 b0 ff ff ff call 507 <write_string>
557: 83 c4 08 add $0x8,%esp
55a: eb fe jmp 55a <main+0x12>
main 的地址似乎映射得很好,但是当我加载我的内核扇区并跳转到它时,这就是 gdb 显示的内容:
┌─────────────────────────────────────────────────────────────────┐
>│0x600 call 0x646 │
│0x603 add BYTE PTR [bx+si],al │
│0x605 cli │
│0x606 hlt
...
│0x646 leave │
│0x647 ret │
│0x648 push bp │
│0x649 mov bp,sp │
│0x64b push 0x55c |
内核在 0x500 处加载,但文件的文本部分的对齐方式为 0x100,因此代码从 0x600(在 elf header 之后)而不是 0x500 开始。代码加载正常,但 loader.asm
中的调用使用 0x646 而不是 main 开始处的 0x648。
请注意,在 0x603 处有一个不应该存在的额外汇编行。它看起来像是一条垃圾指令,我认为这可能是导致它失败的原因。看起来像调用指令
500: e8 43 00 00 00 call 548 <main>
被正确解释,但零以某种方式携带到下一条指令以创建额外的意外指令。不确定这是否可能。
我不明白为什么它使用了 2 off 的地址,这也发生在内核代码的其他部分。
请随时指出我犯的任何其他错误,我正在学习。我不确定这是否与我链接它们的方式、我使用的格式或不同的东西有关。
编辑:
所以,看来这实际上是我的引导加载程序的问题:
global start
[bits 16]
[org 0x7c00]
start: jmp boot
boot:
cli ; clear interrupts
cld ; clear direction flag
mov ax, 0
mov es, ax
mov bx, 0x500 ; set bx to where we load the kernel
mov al, 0x12 ; set lower byte of ax to read 18 sectors
mov ch, 0 ; set higher byte of cx to read track 0 (first track)
mov cl, 2 ; set lower byte of cx to read sector 2 (bootloader is sec1)
mov dh, 0 ; set higher byte of dx to read head 0 (top side of disk)
mov dl, 0 ; set lower byte of dx to read drive 0 (floppy drive)
mov ah, 0x02 ; read
int 0x13 ; call BIOS interrupt 13 to read drive
int 0x10 ; clear screen
jmp pm_switch
hlt ; this instruction should not execute
pm_switch:
xor ax, ax ; clear ds (used by lgdt)
mov ds, ax
cli
lgdt [gdt_desc]
mov eax, cr0
or eax, 1 ; switch to protected mode
mov cr0, eax
jmp 08h:select_jump
select_jump:
xor eax, eax
mov ax, 0x10 ; set data segments to data selector (0x10)
mov ds, ax
mov ss, ax
mov esp, 09000h
mov ax, 0
mov es, ax
; do a far jump to set cs and go to kernel code
jmp 08h:0x600
gdt: ; Address for the GDT
gdt_null: ; Null Segment
dd 0
dd 0
;KERNEL_CODE equ $-gdt
gdt_kernel_code:
dw 0FFFFh ; Limit 0xFFFF
dw 0 ; Base 0:15
db 0 ; Base 16:23
db 09Ah ; Present, Ring 0, Code, Non-conforming, Readable
db 00Fh ; Page-granular
db 0 ; Base 24:31
;KERNEL_DATA equ $-gdt
gdt_kernel_data:
dw 0FFFFh ; Limit 0xFFFF
dw 0 ; Base 0:15
db 0 ; Base 16:23
db 092h ; Present, Ring 0, Data, Expand-up, Writable
db 00Fh ; Page-granular
db 0 ; Base 24:32
gdt_end: ; Used to calculate the size of the GDT
gdt_desc: ; The GDT descriptor
dw gdt_end - gdt - 1 ; Limit (size)
dd gdt ; Address of the GDT
; pad the file to file the rest of the sector (512 bytes) and add boot sig
times 510 - ($-$$) db 0
dw 0xAA55
解决方案:
原来我的 GDT 代码段被设置为 16 位。我将其更改为 32,并在切换到 protected 后立即添加了一条 [bits 32]
指令(就在 select jump
之前)。
最佳答案
您正在使用[bits 32]
指示汇编程序为保护模式(32 位)生成代码,但您的处理器正在以实模式(16 位)运行。
您最终运行的代码是无稽之谈。许多指令在实模式和保护模式下的解释不同——例如,jmp
在实模式下只需要两个立即字节. (例如,这是 0x603 处意外的 add
的来源——它是错误的 32 位立即值的后半部分。)
关于c - 程序集调用映射到错误的地址,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42466427/
#include using namespace std; class C{ private: int value; public: C(){ value = 0;
这个问题已经有答案了: What is the difference between char a[] = ?string?; and char *p = ?string?;? (8 个回答) 已关闭
关闭。此题需要details or clarity 。目前不接受答案。 想要改进这个问题吗?通过 editing this post 添加详细信息并澄清问题. 已关闭 7 年前。 此帖子已于 8 个月
除了调试之外,是否有任何针对 c、c++ 或 c# 的测试工具,其工作原理类似于将独立函数复制粘贴到某个文本框,然后在其他文本框中输入参数? 最佳答案 也许您会考虑单元测试。我推荐你谷歌测试和谷歌模拟
我想在第二台显示器中移动一个窗口 (HWND)。问题是我尝试了很多方法,例如将分辨率加倍或输入负值,但它永远无法将窗口放在我的第二台显示器上。 关于如何在 C/C++/c# 中执行此操作的任何线索 最
我正在寻找 C/C++/C## 中不同类型 DES 的现有实现。我的运行平台是Windows XP/Vista/7。 我正在尝试编写一个 C# 程序,它将使用 DES 算法进行加密和解密。我需要一些实
很难说出这里要问什么。这个问题模棱两可、含糊不清、不完整、过于宽泛或夸夸其谈,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开,visit the help center . 关闭 1
有没有办法强制将另一个 窗口置于顶部? 不是应用程序的窗口,而是另一个已经在系统上运行的窗口。 (Windows, C/C++/C#) 最佳答案 SetWindowPos(that_window_ha
假设您可以在 C/C++ 或 Csharp 之间做出选择,并且您打算在 Windows 和 Linux 服务器上运行同一服务器的多个实例,那么构建套接字服务器应用程序的最明智选择是什么? 最佳答案 如
你们能告诉我它们之间的区别吗? 顺便问一下,有什么叫C++库或C库的吗? 最佳答案 C++ 标准库 和 C 标准库 是 C++ 和 C 标准定义的库,提供给 C++ 和 C 程序使用。那是那些词的共同
下面的测试代码,我将输出信息放在注释中。我使用的是 gcc 4.8.5 和 Centos 7.2。 #include #include class C { public:
很难说出这里问的是什么。这个问题是含糊的、模糊的、不完整的、过于宽泛的或修辞性的,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开它,visit the help center 。 已关
我的客户将使用名为 annoucement 的结构/类与客户通信。我想我会用 C++ 编写服务器。会有很多不同的类继承annoucement。我的问题是通过网络将这些类发送给客户端 我想也许我应该使用
我在 C# 中有以下函数: public Matrix ConcatDescriptors(IList> descriptors) { int cols = descriptors[0].Co
我有一个项目要编写一个函数来对某些数据执行某些操作。我可以用 C/C++ 编写代码,但我不想与雇主共享该函数的代码。相反,我只想让他有权在他自己的代码中调用该函数。是否可以?我想到了这两种方法 - 在
我使用的是编写糟糕的第 3 方 (C/C++) Api。我从托管代码(C++/CLI)中使用它。有时会出现“访问冲突错误”。这使整个应用程序崩溃。我知道我无法处理这些错误[如果指针访问非法内存位置等,
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 我们不允许提问寻求书籍、工具、软件库等的推荐。您可以编辑问题,以便用事实和引用来回答。 关闭 7 年前。
已关闭。此问题不符合Stack Overflow guidelines 。目前不接受答案。 要求我们推荐或查找工具、库或最喜欢的场外资源的问题对于 Stack Overflow 来说是偏离主题的,因为
我有一些 C 代码,将使用 P/Invoke 从 C# 调用。我正在尝试为这个 C 函数定义一个 C# 等效项。 SomeData* DoSomething(); struct SomeData {
这个问题已经有答案了: Why are these constructs using pre and post-increment undefined behavior? (14 个回答) 已关闭 6
我是一名优秀的程序员,十分优秀!