【问题标题】:seg fault on IN instruction, inline assembly GCCIN 指令的段错误,内联汇编 GCC
【发布时间】:2018-06-19 01:41:44
【问题描述】:

我正在尝试在 Linux (Ubuntu) 上通过内联汇编代码中的 IN 和 OUT 指令从 C 访问 I/O 端口。只要执行 IN 或 OUT 指令,就会产生 seg 错误。

例如,这段简单的代码会产生一个段错误:

#include <stdint.h>

int main() {

    uint8_t readvalue = 0;
    uint16_t port = 0xB3;

    asm volatile("in    %%dx, %%al\n\t"
         : "=a" (readvalue)
         : "d" (port)
    );

    return(0);

}

我编译:gcc -O2 -g

从 GDB 中,我看到这个程序被编译成以下简单的汇编代码序列:

mov    $0xb3,%edx
in     (%dx),%al
xor    %eax,%eax
retq

而且在 GDB 中,我看到只要执行 IN 指令,就会发生 seg 错误。

完整的 GDB 会话:

(gdb) list 
1   #include <stdint.h>
2   
3   int main() {
4       
5       uint8_t readvalue = 0;
6       uint16_t port = 0xB3;
7            
8       asm volatile("in    %%dx, %%al\n\t"
9            : "=a" (readvalue)
10           : "d" (port)
(gdb) list
11      );
12
13      return(0);
14
15  } 
16   
(gdb) break 7
Breakpoint 1 at 0x4003e0: file tcgsmi_inonly_pure.c, line 7.
(gdb) r
Starting program: /home/emerald/tcgsmi_inonly_pure

Breakpoint 1, main () at tcgsmi_inonly_pure.c:8
8       asm volatile("in    %%dx, %%al\n\t"
(gdb) disass
Dump of assembler code for function main:
=> 0x00000000004003e0 <+0>: mov    $0xb3,%edx
   0x00000000004003e5 <+5>: in     (%dx),%al
   0x00000000004003e6 <+6>: xor    %eax,%eax
   0x00000000004003e8 <+8>: retq
End of assembler dump.
(gdb) display/i $rip
1: x/i $rip
=> 0x4003e0 <main>: mov    $0xb3,%edx
(gdb) ni
0x00000000004003e5  8       asm volatile("in    %%dx, %%al\n\t"
1: x/i $rip
=> 0x4003e5 <main+5>:   in     (%dx),%al
(gdb) ni

Program received signal SIGSEGV, Segmentation fault.
0x00000000004003e5 in main () at tcgsmi_inonly_pure.c:8
8       asm volatile("in    %%dx, %%al\n\t"
1: x/i $rip
=> 0x4003e5 <main+5>:   in     (%dx),%al

【问题讨论】:

  • 你是在用户模式还是内核模式下运行?用户程序不能这样使用 IN 和 OUT。
  • 您可能会发现以下文档信息丰富:The Userspace I/O HOWTO
  • @GregHewgill:更具体地说,要在 x86 上允许来自用户空间的 in / out,请调用 the ioperm function(以 root 身份)以允许对特定端口的权限。或iopl 设置所有端口的 I/O 权限级别。
  • 另外请注意,I/O 端口受到保护是有充分理由的。如果您开始尝试它们,请为您的系统崩溃做好准备。

标签: linux gcc io x86 inline-assembly


【解决方案1】:

答案在 OP 上的 cmets 中。

IO 端口不能简单地在用户模式下访问。

  1. 该程序需要以 root 身份运行(例如使用 sudo)。
  2. 在访问端口之前,需要使用 ioperm() 将其解锁。

ioperm() 的原型是:

int ioperm(unsigned long from, unsigned long num, int turn_on);

from = 您要访问的端口范围的起始 ID
num = 范围的长度
turn_on = 非零启用访问,零禁用访问

因此,在上面的示例中,调用将是:

ioperm(0xB3,1,1);

【讨论】:

    猜你喜欢
    • 2016-09-06
    • 2019-10-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多