我正在开发一个具有严格启动时间要求的项目。目标体系结构是基于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)},
等
显然这不起作用,因为在C中执行此操作是违法的,因为myIsr不是常量。它的值由链接器解决(它只能进行有限的数学运算)而不是编译器。
有关如何执行此操作的任何建议或其他想法?
谢谢,
答案 0 :(得分:2)
你遇到了一个众所周知的x86疣。我不相信链接器可以用IDT条目所期望的混合形式填充你的isr例程的地址。
如果您有野心,可以创建一个IDT构建器脚本来执行类似这样的操作(基于Linux)的方法。我没有测试过这个方案,无论如何它可能都是一个令人讨厌的黑客,所以小心翼翼。
步骤1:编写脚本以运行'nm'并捕获标准输出。
步骤2:在脚本中,解析nm输出以获取所有中断服务例程的内存地址。
步骤3:输出一个二进制文件'idt.bin',其IDT字节全部设置并准备好用于LIDT指令。您的脚本显然以正确的混合形式输出isr地址。
步骤4:使用objcopy将原始二进制文件转换为精灵部分:
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部分进行重新链接不会移动二进制文件中的任何其他内容,例如你的惯例。通过将IDT放入其自己的专用部分,在链接描述文件(.ld文件)中对此进行管理。
<强> --- --- EDIT 强> 从评论来看,似乎对这个问题感到困惑。 32位x86 IDT期望中断服务程序的地址被分成两个不同的16位字,如下所示:
31 16 15 0 +---------------+---------------+ | Address 31-16 | | +---------------+---------------+ | | Address 15-0 | +---------------+---------------+
因此,链接器无法将ISR地址作为正常重定位插入。因此,在启动时,软件必须构建这种分割格式,这会减慢启动时间。