我正在开发一个具有严格启动时间要求的项目.目标体系结构是基于IA-32的处理器,以32位保护模式运行.确定可以改进的一个领域是当前系统动态初始化处理器的IDT(中断描述符表).由于我们没有任何即插即用设备且系统相对静态,我希望能够使用静态构建的IDT.
然而,由于8字节中断门描述符分割ISR地址,这证明对于IA-32拱是麻烦的.ISR的低16位出现在描述符的前2个字节中,其他一些位填充接下来的4个字节,最后ISR的最后16位出现在最后2个字节中.
我想使用const数组来定义IDT,然后简单地将IDT寄存器指向它,如下所示:
typedef struct s_myIdt {
unsigned short isrLobits;
unsigned short segSelector;
unsigned short otherBits;
unsigned short isrHibits;
} myIdtStruct;
myIdtStruct myIdt[256] = {
{ (unsigned short)myIsr0, 1, 2, (unsigned short)(myIsr0 >> 16)},
{ (unsigned short)myIsr1, 1, 2, (unsigned short)(myIsr1 >> 16)},
Run Code Online (Sandbox Code Playgroud)
等等
显然这不起作用,因为在C中执行此操作是非法的,因为myIsr不是常量.它的值由链接器解决(只能进行有限的数学运算)而不是编译器.
关于如何做到这一点的任何建议或其他想法?
谢谢,
您遇到了众所周知的 x86 问题。我不相信链接器可以将 isr 例程的地址填充为 IDT 条目所期望的混合形式。
如果您雄心勃勃,您可以创建一个 IDT 构建器脚本来执行类似此(基于 Linux)的方法。我还没有测试过这个方案,无论如何它可能都是一种令人讨厌的黑客行为,所以要小心行事。
步骤 1:编写一个脚本来运行“nm”并捕获标准输出。
步骤 2:在脚本中,解析 nm 输出以获取所有中断服务例程的内存地址。
步骤 3:输出一个二进制文件“idt.bin”,其中 IDT 字节已全部设置并准备好执行 LIDT 指令。您的脚本显然以正确的混合形式输出 isr 地址。
第 4 步:使用 objcopy 将其原始二进制文件转换为 elf 部分:
objcopy -I binary -O elf32-i386 idt.bin idt.elf
第 5 步:现在 idt.elf 文件包含 IDT 二进制文件,其符号如下:
> nm idt.elf
000000000000000a D _binary_idt_bin_end
000000000000000a A _binary_idt_bin_size
0000000000000000 D _binary_idt_bin_start
Run Code Online (Sandbox Code Playgroud)
第 6 步:重新链接您的二进制文件,包括 idt.elf。在程序集存根和链接器脚本中,您可以引用符号 _binary_idt_bin_start 作为 IDT 的基础。例如,您的链接器脚本可以将符号 _binary_idt_bin_start 放置在您喜欢的任何地址处。
请注意,重新链接 IDT 部分不会移动二进制文件中的任何其他内容,例如 isr 例程。通过将 IDT 放入其自己的专用部分,可以在链接器脚本(.ld 文件)中对此进行管理。
---编辑--- 从评论来看,这个问题似乎很混乱。32 位 x86 IDT 期望中断服务例程的地址被分成两个不同的 16 位字,如下所示:
31 16 15 0 +----------------+----------------+ | 地址 31-16 | | +----------------+----------------+ | | 地址 15-0 | +----------------+----------------+
因此,链接器无法像正常重定位那样插入 ISR 地址。因此,在启动时,软件必须构建这种分割格式,这会减慢启动时间。