使用 GDT 保护模式下的汇编器跳转

2023-11-26

我目前正在使用 x86 Assembler,以提高我的低级编程技能。目前,我在 32 位保护模式下的寻址方案遇到了一个小问题。

情况如下:

我在 0x7e0 加载了一个程序,它将 CPU 切换到保护模式并跳转到代码中的相应标签:

[...]
code to switch CPU in Protected Mode
[...]

jmp ProtectedMode


[...]

bits 32

ProtectedMode:
    .halt:
        hlt
        jmp .halt

到目前为止,这工作得非常好。 “jmp ProtectedMode”无需显式远跳转即可清除预取队列 - 因为该程序以偏移量 0(开头为 org 0)加载 - 导致代码段指向正确的位置。

我现在的问题是,在“ProtectedMode”标签中,我想跳转到在 0x8000 处加载的另一个程序(我用内存转储检查了这一点,加载函数确实工作正常,并且程序正确加载到 0x8000) 。

由于 CPU 现在处于保护模式而不是实模式,因此寻址模式有所不同。 ProtectedMode 使用描述符选择器在描述符表中查找基地址和限制,以添加给定的偏移量并检索物理地址(据我所知)。因此,在进入保护模式之前需要安装GDT。

我的看起来像下面这样:

%ifndef __GDT_INC_INCLUDED__
%define __GDT_INC_INCLUDED__

;*********************************
;* Global Descriptor Table (GDT) *
;*********************************
NULL_DESC:
    dd 0            ; null descriptor
    dd 0

CODE_DESC:
    dw 0xFFFF       ; limit low
    dw 0            ; base low
    db 0            ; base middle
    db 10011010b    ; access
    db 11001111b    ; granularity
    db 0            ; base high

DATA_DESC:
    dw 0xFFFF       ; data descriptor
    dw 0            ; limit low
    db 0            ; base low
    db 10010010b    ; access
    db 11001111b    ; granularity
    db 0            ; base high

gdtr:
    Limit dw 24         ; length of GDT
    Base dd NULL_DESC   ; base of GDT

%endif ;__GDT_INC_INCLUDED__

并通过以下方式加载到 GDT 寄存器

lgdt [gdtr]

到目前为止我不明白的是,我现在如何使用GPT在保护模式下跳转到物理地址0x8000?

我的第一个想法是选择代码描述符(CODE_DESC),它应该指向 0x7e00(当前程序已加载)并使用到达 0x8000(512 字节)所需的偏移量,从而产生跳转指令:

jmp CODE_DESC:0x200

但这是行不通的。

jmp 0x7e0:0x200 

也不起作用...

你知道我在这里缺少什么吗?也许我不明白 32 位保护模式寻址方案和 GDT 的用法中的一些基本内容。

[编辑]完整代码:

bits 16
org 0                       ; loaded with offset 0000 (phys addr: 0x7e00)

jmp Start

Start:
    xor ax, ax
    mov ax, cs
    mov ds, ax              ; update data segment

    cli                     ; clear interrupts

    lgdt [gdtr]             ; load GDT from GDTR (see gdt_32.inc)

    call OpenA20Gate        ; open the A20 gate 

    call EnablePMode        ; jumps to ProtectedMode

;******************
;* Opens A20 Gate *
;******************
OpenA20Gate:
    in al, 0x93         ; switch A20 gate via fast A20 port 92

    or al, 2            ; set A20 Gate bit 1
    and al, ~1          ; clear INIT_NOW bit
    out 0x92, al

    ret

;**************************
;* Enables Protected Mode *
;**************************
EnablePMode:
    mov eax, cr0
    or eax, 1
    mov cr0, eax

    jmp ProtectedMode ; this works (jumps to label and halts)
    ;jmp (CODE_DESC-NULL_DESC):ProtectedMode ; => does not work
    ;jmp 08h:ProtectedMode , => does not work

;***************
;* data fields *
;*  &includes  *
;***************
%include "gdt_32.inc"

;******************
;* Protected Mode *
;******************
bits 32

ProtectedMode:
    ;here I want to jump to physical addr 0x8000 (elf64 asm program)

    .halt:
        hlt
        jmp .halt

代码中存在多个问题。

首先,你的GDTR.Base包含的偏移量GDT从代码开头开始,因为您的代码被编译为从地址 0 开始(因为org 0)。基地址必须是物理地址,而不是相对地址。 IOW,如果你保留这个org 0,您必须添加CS*16 (=0x7e00) 至Base.

其次,因为同样的org 0,代码中的 32 位偏移量(之后bits 32 and ProtectedMode:) 不等于它们对应的物理地址,它们比物理地址小 0x7e00。 OTOH,GDT 中定义的段从物理地址 0 开始(因为 GDT 条目的基址部分是 0),而不是从 0x7e00 开始。这意味着当您尝试将这些段与代码/数据一起使用时,您将丢失 0x7e00 处的地址。如果你想保留org 0,GDT中的基地址必须设置为0x7e00。

或者你可以改变org 0 to org 0x7e00然后GDT中的基数应该为0。并且您不需要将GDTR.Base调整为0x7e00,0即可。

这应该有效:

bits 16
org 0x7e00                  ; loaded at phys addr 0x7e00
                            ; control must be transferred with jmp 0:0x7e00

    xor ax, ax
    mov ds, ax              ; update data segment

    cli                     ; clear interrupts

    lgdt [gdtr]             ; load GDT from GDTR (see gdt_32.inc)

    call OpenA20Gate        ; open the A20 gate 

    call EnablePMode        ; jumps to ProtectedMode

;******************
;* Opens A20 Gate *
;******************
OpenA20Gate:
    in al, 0x93         ; switch A20 gate via fast A20 port 92

    or al, 2            ; set A20 Gate bit 1
    and al, ~1          ; clear INIT_NOW bit
    out 0x92, al

    ret

;**************************
;* Enables Protected Mode *
;**************************
EnablePMode:
    mov eax, cr0
    or eax, 1
    mov cr0, eax

    jmp (CODE_DESC - NULL_DESC) : ProtectedMode

;***************
;* data fields *
;*  &includes  *
;***************
;%include "gdt_32.inc"
;*********************************
;* Global Descriptor Table (GDT) *
;*********************************
NULL_DESC:
    dd 0            ; null descriptor
    dd 0

CODE_DESC:
    dw 0xFFFF       ; limit low
    dw 0            ; base low
    db 0            ; base middle
    db 10011010b    ; access
    db 11001111b    ; granularity
    db 0            ; base high

DATA_DESC:
    dw 0xFFFF       ; limit low
    dw 0            ; base low
    db 0            ; base middle
    db 10010010b    ; access
    db 11001111b    ; granularity
    db 0            ; base high

gdtr:
    Limit dw gdtr - NULL_DESC - 1 ; length of GDT
    Base dd NULL_DESC   ; base of GDT

;******************
;* Protected Mode *
;******************
bits 32

ProtectedMode:
    mov     ax, DATA_DESC - NULL_DESC
    mov     ds, ax ; update data segment

    .halt:
        hlt
        jmp .halt

请注意,段限制等于段大小减 1。

还有几点...使用有效选择器或 0 加载所有段寄存器。此外,设置堆栈。如果那里有垃圾(或实模式中的旧值),当您开始使用中断/异常时,您会遇到更多崩溃。

最后,我不知道 elf64 是什么,但你必须关心org对于其他模块来说,并确保所有生成的地址都对应于加载地址。如果您打算启用 64 位模式,则需要做大量工作。我建议您不要急于进入 64 位模式,因为您会被相对简单的东西绊倒。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用 GDT 保护模式下的汇编器跳转 的相关文章

  • 如何获取 VESA BIOS 信息

    我正在跟踪Phil Opp 教程 https os phil opp com 关于用 Rust 编写一个操作系统 在稍微尝试了一下之后 我想在屏幕上显示真实的图形 我发现我应该从使用带有 VESA 的线性帧缓冲区开始 我在 osdev or
  • MASM 字符串反转

    好吧 我正在讨论这个问题 可能是一个非常复杂的解决方案 但这是我脑海中浮现的第一件事 我需要编写一个汇编语言程序来反转 源 字符串 而不使用 目标 字符串 临时变量 这是我的尝试 INCLUDE Irvine32 inc data sour
  • C/C++ 中的简单“Hello World”内联汇编语言程序

    我使用 devcpp 和 borland c 编译器 asm mov ax 4 I O Func mov bx 1 Output func mov cx name address of the string mov dx 6 length
  • 使用 gdb 调试反汇编库

    在Linux和Mac OS X中可以使用strapi和next来调试应用程序而无需调试信息 在 Mac OS X 上 gdb 显示在库内部调用的函数 尽管有时会在每个 stepi 指令中推进多个汇编程序指令 在 Linux 上 当我进入动态
  • 在汇编中显示两位数? [复制]

    这个问题在这里已经有答案了 我对汇编编程完全陌生 在课堂作业的示例中 需要将两个数字相加并显示总和 我发现神秘的是当其是两位数时显示总和 这是我的代码 mov al num1 mov bl num2 add al bl add ax 303
  • 为什么这个“std::atomic_thread_fence”起作用

    首先我想谈一下我对此的一些理解 如有错误请指正 a MFENCE在x86中可以保证全屏障 顺序一致性可防止 STORE STORE STORE LOAD LOAD STORE 和 LOAD LOAD 重新排序 这是根据维基百科 https
  • 为什么我可以使用 ret 退出 main?

    我即将弄清楚程序堆栈到底是如何设置的 我了解到用以下方式调用该函数 call pointer 实际上等同于 mov register pc programcounter add register 1 where 1 is one instr
  • 汇编指令陷阱有什么作用?

    当程序需要时 程序通常会发出软件陷阱 由操作系统提供服务 通用异常处理程序 操作系统确定陷阱的原因并做出响应 适当地 汇编指令 trap 和 BASIC 中的 TRAP 指令一样吗 答案似乎是肯定的 你能接受还是拒绝我的结论 不中断 的代码
  • 破坏/分解函数的函数

    我以前有过 here https stackoverflow com questions 4920610 c class function in assembly 已经表明 C 函数不容易用汇编表示 现在我有兴趣以一种或另一种方式阅读它们
  • 遍历内存编辑每个字节

    我正在编写汇编代码 提示用户输入一串小写字符 然后输出包含所有大写字符的相同字符串 我的想法是迭代从特定地址开始的字节 并从每个字节中减去 20H 将小写变为大写 直到到达具有特定值的字节 我对 Assembly 相当缺乏经验 所以我不确定
  • 如何让c代码执行hex机器代码?

    我想要一个简单的 C 方法能够在 Linux 64 位机器上运行十六进制字节码 这是我的 C 程序 char code x48 x31 xc0 include
  • orpd等SSE2指令有什么意义?

    The orpd指令是 压缩双精度浮点值的按位逻辑或 这不是做完 全相同的事情吗por 按位逻辑或 如果是这样 拥有它还有什么意义呢 请记住 SSE1orps https www felixcloutier com x86 orps首先 实
  • 在 x86 程序集中将整数打印到控制台

    当我在 16 位汇编中添加两个值时 将结果打印到控制台的最佳方法是什么 目前我有这个代码 CODE START mov ax 1 put 1 into ax add ax 2 add 2 to ax current value mov ah
  • 取消的分支与常规分支有何不同?

    特别是对于 SPARC Assembly 取消的分支与常规分支有何不同 我一直认为 当我需要填充分支指令的 nop 延迟槽时 需要取消分支指令 但是 我认为我在这一部分上是不正确的 因为您可以在不取消分支的情况下填充 nop 如果不采用分支
  • 内联 asm 中不支持的指令“mov”将控制寄存器移动到 uint32_t

    我在 C 函数中使用汇编代码 但海湾合作委员会给出unsupported instruction mov 以下代码的错误 uint32 t faulting address asm volatile mov cr2 0 r faulting
  • 在 x86 Intel VT-X 非根模式下,是否可以在每个指令边界传递中断?

    除了不将中断传送到虚拟处理器的某些正常指定条件 cli if 0 等 之外 客户机中的所有指令实际上都是可中断的吗 也就是说 当传入的硬件中断先传递给 LAPIC 然后传递给处理器时 据说会发生一些内部魔法 将其转换为虚拟中断给来宾 使用虚
  • 为什么 RISC-V S-B 和 U-J 指令类型以这种方式编码?

    我正在读一本书 计算机组织与设计RISC V版 我遇到了 S B 和 U J 指令类型的编码 我上面提到的那些类型有奇怪的编码立即字段 S B 类型将直接字段分为两部分 这是有道理的 因为所有指令编码都必须相似 但我无法理解为什么立即字段以
  • 为什么x86分页没有特权环的概念?

    早在 1982 年 当 Intel 发布 80286 时 他们在分段方案中添加了 4 个特权级别 环 0 3 由全局描述符表 GDT 和局部描述符表 LDT 中的 2 位指定 在 80386 处理器中 Intel 添加了分页功能 但令人惊讶
  • 为什么 GCC 不将 a*a*a*a*a*a 优化为 (a*a*a)*(a*a*a)?

    我正在对科学应用程序进行一些数值优化 我注意到的一件事是 GCC 会优化调用pow a 2 通过将其编译成a a 但是调用pow a 6 没有优化 实际会调用库函数pow 这大大降低了性能 相比之下 英特尔 C 编译器 http en wi
  • 用于预乘 ARGB 的 SSE alpha 混合

    我正在尝试编写一个支持 SSE 的 alpha 合成器 这就是我想出的 首先 混合两个 4 像素向量的代码 alpha blend two 128 bit 16 byte SSE vectors containing 4 pre multi

随机推荐

  • Scala 中缀类型的现实示例

    我发现了一种有趣的语法东西 它被称为Infix type Example class M T U new Int M String 现在我正在从一些流行的框架或库中寻找这种类型的示例 我在哪里可以找到它们 有什么建议么 无形图书馆 有一堆
  • 即使在 unsetenv("LD_PRELOAD") 之后,LD_PRELOAD 也会影响新的子进程

    我的代码如下 preload c 内容如下 include
  • 如何向 DropDownList 添加初始“选择”值

    如果我使用 DropDownList
  • datepicker选择星期几android

    我的应用程序中有日期选择器 它运行良好 但我需要选择星期几而不是日期 即 如果我选择日期 23 我需要获取像 星期五 这样的日期而不是数字 我用了OnDateSetListener对于点击事件 private DatePickerDialo
  • 显示 sweave 的错误

    我正在使用 Sweave 编写一些 R 笔记 并希望显示常见错误 例如 lt
  • WordPress 分页简码

    向 WordPress 大脑提出一个简单的问题 我有一个短代码 当前可以导入特定帖子类型的所有帖子 但是 我在向 wp query 添加分页时遇到问题 为了解释此代码的功能 我将 feed type attractions limit 2
  • 如何在 bazel/tensorflow 构建期间添加外部头文件

    我正在尝试添加外部头文件 如 OpenCL 头文件 以进行一些张量流实验 我尝试将其添加到 tensorflow core BUILD 文件下的 BUILD 文件中 This includes implementations of all
  • 使用 ContentCachingRequestWrapper 导致参数映射为空

    我已经实现了一个过滤器 在其中我想首先读取请求的内容进行一些检查 然后我想继续 但问题是 在过滤器链的以下过滤器中getParameters 方法来自class Request org eclipse jetty server Reques
  • 如何使用合并内存访问

    我有 N 个线程在设备上同时执行 它们需要全局内存中的 M N 个浮点数 访问合并的全局内存的正确方法是什么 在这件事上 共享内存能提供什么帮助呢 通常 当相邻线程访问内存中的相邻单元时 可以实现良好的合并访问 因此 如果tid保存线程的索
  • RecyclerView可扩展cardView

    我用 RecyclerView 制作了一个小项目 里面有 CardView 项目 我创建了可扩展卡 通过按卡内的小按钮进行扩展 每张卡片始终包含可见部分 id top layout 和可扩展部分 id expandable part lay
  • 如何使用 Java 创建非常大的布尔数组?

    当我尝试使用 Java 创建一个非常大的布尔数组时 例如 boolean isPrime1 new boolean 600851475144 我可能会遇到精度损失错误 是不是太大了 可存储600个billion位 您需要 75 的绝对最小地
  • 是否可以使用Java Reflection创建内部类的实例?

    代码示例 public class Foo public class Bar public void printMesg String body System out println body public static void main
  • 用于服务器端控制的 jQuery 选择器

    有什么区别 and id lblName 将找到一个元素id属性由提供ClientIDASP Net 中的属性 id lblName 会找到一个带有id以结尾的属性lblName 例如foo lblName
  • 在企业代理背后使用 Selenium RemoteWebDriver

    如何从公司代理后面通过 RemoteWebDriver 连接到 Selenium 网格 例如 BrowserStack 被测试的应用程序位于代理之外 可以从 BrowserStack 自由访问 This 在企业代理后面使用 Selenium
  • 如何设置 bootstrap-datepicker-rails?

    有人知道如何设置 gem bootstrap datepicker rails 吗 我按照以下步骤操作http rubydoc info gems bootstrap datepicker rails 0 6 21 frames 基本上 I
  • Windows Azure 内部端点的安全性如何?

    我有一个 Web 角色中的前端 MVC 应用程序 受 WIF 和 ACS 保护 我希望它成为我的 Azure 应用程序唯一公开的表面 它连接到许多后端服务 一些辅助角色和一些 为了方便在 VS 中添加服务引用 或者因为它们使用 WCF 数据
  • 使用 Swift 在 iOS 中远程控制事件

    试图弄清楚如何读取Apple耳机的音量按钮以用作相机快门的触发器 就像Apple相机应用程序那样 从文档上远程控制事件 随事件接收远程控制 and 这个 git 仓库 我拼凑出我可能需要一个AVAudioPlayer目的 beginRece
  • 如何从 Google Analytics 4 获取视图 ID?

    我尝试从 GA Google Analytics 检索有关我的网站的所有信息 我正在使用 laravel 和这个包https github com spatie laravel analytics让我的生活更轻松 然而 该软件包需要 GA
  • Pandas,groupby,其中列值大于 x

    我有一张这样的桌子 timestamp avg hr hr quality avg rr rr quality activity sleep summary id 1422404668 66 229 0 0 13 78 1422404670
  • 使用 GDT 保护模式下的汇编器跳转

    我目前正在使用 x86 Assembler 以提高我的低级编程技能 目前 我在 32 位保护模式下的寻址方案遇到了一个小问题 情况如下 我在 0x7e0 加载了一个程序 它将 CPU 切换到保护模式并跳转到代码中的相应标签 code to