为什么中断不是由C代码产生,而是由汇编指令产生?
Why Interrupts not generates by C code but easy generates by assembly instructions?
我正在编写一些内核,并实现 idt 和中断。
我的小内核中的这段 C 代码没有产生任何中断:
int x = 5/0;
int f[4];
f[5] = 8;
但是这段汇编代码可以产生任何中断:
asm("int [=11=]");
(处理程序正常工作)。
帮助我理解为什么会发生这种情况。
我也试过这个:
int a = 3;
int b = 3;
int c = a-b;
int x = a/c;
我在 C 代码中尝试的任何事情都不会为我生成异常。
即使这样也行不通:
int div_by_0(int a, int b){return a/b;}
int x = div_by_0(5, 0);
void fun ( void )
{
int a = 3;
int b = 3;
int c = a-b;
int x = a/c;
}
Disassembly of section .text:
0000000000000000 <fun>:
0: f3 c3 repz retq
没有触发被零除的除法。都是死代码。
其中none与int指令有任何关系,这些是完全不同的话题。
如评论中所述,在不使用死代码的情况下对其进行测试。
int fun0 ( int x )
{
return(5/x);
}
int fun1 ( void )
{
return(fun0(0));
}
但明白还是达不到预期的效果:
Disassembly of section .text:
0000000000000000 <fun0>:
0: b8 05 00 00 00 mov [=12=]x5,%eax
5: 99 cltd
6: f7 ff idiv %edi
8: c3 retq
9: 0f 1f 80 00 00 00 00 nopl 0x0(%rax)
0000000000000010 <fun1>:
10: 0f 0b ud2
因为 fun1 的优化器可以看到 fun0 函数。您希望在单独的优化域中测试代码。在上面的这种情况下,idiv 将生成除以零。然后它就变成了一个操作系统问题,关于如何处理它以及它是否对您可见。
您看到的问题是因为除以 0 在 C/C++ 中是未定义的行为。编译器已设法在编译时进行足够的优化以实现除以零。编译器可以自由地做任何事情,从停止和着火到使结果为 0。一些编译器会发出 ud2
指令来引发 CPU 异常。结果未定义。
你有几个选择。在汇编中编写除法并从 C/C++ 调用该函数。由于您使用的是 GCC(也适用于 CLANG),您还可以使用内联汇编生成除以零,例如:
#include <stdint.h> /* or replace uint16_t with unsigned short int */
void div_by_0 (void)
{
asm ("div %b0" :: "a"((uint16_t)0));
return;
}
这会将 AX 设置为 0,然后将 AX 除以 AL 并得到 DIV
指令。 0/0 未定义,将引发除法异常 (#DE)。此内联汇编应适用于 16、32 和 64 位代码。
在保护模式或长模式下使用 int $#
(其中 #
是向量编号)触发异常并不总是与获得 CPU 生成的异常相同。 CPU 生成的 Some exceptions 在需要由中断处理程序清除的 return 地址之后将错误代码压入堆栈。如果您要使用环 0 中的 int [=15=]x0d
导致#GP 异常,中断处理程序可能会出错,因为它来自中断 returns,因为使用 int
生成异常永远不会放置堆栈上的错误代码。这不是 int [=17=]
的问题,因为 #DE 没有由 CPU.
放置在堆栈上的错误代码
原来是优化标志的问题。由于 Makefiles 的一些混乱,-O2 标志起作用了。如果启用 -O0 标志,异常直接从 C 工作。甚至这个简单的代码也会抛出异常:
int x = 5/0;
我正在编写一些内核,并实现 idt 和中断。
我的小内核中的这段 C 代码没有产生任何中断:
int x = 5/0;
int f[4];
f[5] = 8;
但是这段汇编代码可以产生任何中断:
asm("int [=11=]");
(处理程序正常工作)。
帮助我理解为什么会发生这种情况。
我也试过这个:
int a = 3;
int b = 3;
int c = a-b;
int x = a/c;
我在 C 代码中尝试的任何事情都不会为我生成异常。
即使这样也行不通:
int div_by_0(int a, int b){return a/b;}
int x = div_by_0(5, 0);
void fun ( void )
{
int a = 3;
int b = 3;
int c = a-b;
int x = a/c;
}
Disassembly of section .text:
0000000000000000 <fun>:
0: f3 c3 repz retq
没有触发被零除的除法。都是死代码。
其中none与int指令有任何关系,这些是完全不同的话题。
如评论中所述,在不使用死代码的情况下对其进行测试。
int fun0 ( int x )
{
return(5/x);
}
int fun1 ( void )
{
return(fun0(0));
}
但明白还是达不到预期的效果:
Disassembly of section .text:
0000000000000000 <fun0>:
0: b8 05 00 00 00 mov [=12=]x5,%eax
5: 99 cltd
6: f7 ff idiv %edi
8: c3 retq
9: 0f 1f 80 00 00 00 00 nopl 0x0(%rax)
0000000000000010 <fun1>:
10: 0f 0b ud2
因为 fun1 的优化器可以看到 fun0 函数。您希望在单独的优化域中测试代码。在上面的这种情况下,idiv 将生成除以零。然后它就变成了一个操作系统问题,关于如何处理它以及它是否对您可见。
您看到的问题是因为除以 0 在 C/C++ 中是未定义的行为。编译器已设法在编译时进行足够的优化以实现除以零。编译器可以自由地做任何事情,从停止和着火到使结果为 0。一些编译器会发出 ud2
指令来引发 CPU 异常。结果未定义。
你有几个选择。在汇编中编写除法并从 C/C++ 调用该函数。由于您使用的是 GCC(也适用于 CLANG),您还可以使用内联汇编生成除以零,例如:
#include <stdint.h> /* or replace uint16_t with unsigned short int */
void div_by_0 (void)
{
asm ("div %b0" :: "a"((uint16_t)0));
return;
}
这会将 AX 设置为 0,然后将 AX 除以 AL 并得到 DIV
指令。 0/0 未定义,将引发除法异常 (#DE)。此内联汇编应适用于 16、32 和 64 位代码。
在保护模式或长模式下使用 int $#
(其中 #
是向量编号)触发异常并不总是与获得 CPU 生成的异常相同。 CPU 生成的 Some exceptions 在需要由中断处理程序清除的 return 地址之后将错误代码压入堆栈。如果您要使用环 0 中的 int [=15=]x0d
导致#GP 异常,中断处理程序可能会出错,因为它来自中断 returns,因为使用 int
生成异常永远不会放置堆栈上的错误代码。这不是 int [=17=]
的问题,因为 #DE 没有由 CPU.
原来是优化标志的问题。由于 Makefiles 的一些混乱,-O2 标志起作用了。如果启用 -O0 标志,异常直接从 C 工作。甚至这个简单的代码也会抛出异常:
int x = 5/0;