简体   繁体   中英

seg fault on IN instruction, inline assembly GCC

I'm trying to access I/O ports from C, on Linux (Ubuntu), via IN and OUT instructions in inlined assembly code. A seg fault is generated as soon as an IN or OUT instruction is executed.

For example, this simple piece of code generates a seg fault:

#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);

}

I compile with: gcc -O2 -g

From GDB, I see that this program was compiled into the following simple assembly code sequence:

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

And in GDB, I see that as soon as the IN instruction is executed, a seg fault occurs.

The full GDB session:

(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

Answer is in the comments on the OP.

IO ports cannot simply be accessed in user mode.

  1. The program needs to be run as root (eg with sudo).
  2. Before accessing the ports, they need to be unlocked with ioperm().

The prototype for ioperm() is:

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

from = start ID of range of ports to which you want access
num = length of range
turn_on = nonzero to enable access, zero to disable access

So, in the example above, the call would be:

ioperm(0xB3,1,1);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM