7

我正在开发一个对启动时间有严格要求的项目。目标架构是在 32 位保护模式下运行的基于 IA-32 的处理器。确定可以改进的领域之一是当前系统动态初始化处理器的 IDT(中断描述符表)。由于我们没有任何即插即用的设备并且系统是相对静态的,我希望能够使用静态构建的 IDT。

然而,这对于 IA-32 架构来说是很麻烦的,因为 8 字节的中断门描述符拆分了 ISR 地址。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)},

等等

显然这不起作用,因为在 C 中这样做是非法的,因为 myIsr 不是常量。它的值由链接器解析(它只能做有限的数学运算)而不是编译器。

关于如何做到这一点的任何建议或其他想法?

谢谢,

4

1 回答 1

3

您遇到了一个众所周知的 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

第 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 地址作为正常重定位插入。因此,在启动时,软件必须构建这种拆分格式,这会减慢启动时间。

于 2012-10-13T00:47:12.567 回答