Linux NASM OUTSB SEGFULT

Linux NASM OUTSB SEGFULT,linux,assembly,segmentation-fault,nasm,ioports,Linux,Assembly,Segmentation Fault,Nasm,Ioports,我正在尝试将字节0xff写入位于0x378的并行端口。它编译和链接时不会出现问题,但在OUTSB指令中会出现故障 section .text global _start _err_exit: mov eax, 1 mov ebx, 1 int 80h _start: mov eax, 101 ; ioperm mov ebx,

我正在尝试将字节
0xff
写入位于
0x378
的并行端口。它编译和链接时不会出现问题,但在
OUTSB
指令中会出现故障

section .text
        global _start

_err_exit:
        mov     eax,    1
        mov     ebx,    1
        int     80h

_start:
        mov     eax,    101     ; ioperm
        mov     ebx,    0x378   ; Parallel port addr
        mov     ecx,    2       ; number of bytes to 'unlock'
        mov     edx,    1       ; enable
        int     80h

        mov     esi,    0xff
        mov     dx,     0x378
        outsb

        mov     eax,    1       ; exit
        mov     ebx,    0
        int     80h
如果我使用GDB单步执行,并在
OUTSB
指令之前检查寄存器,那么DX寄存器中看起来没有任何内容?还是32位的
dx
=
edx

(gdb) info registers 
eax            0x0  0
ecx            0x2  2
edx            0x378    888
ebx            0x378    888
esp            0xffffd810   0xffffd810
ebp            0x0  0x0
esi            0xff 255
edi            0x0  0
eip            0x8048090    0x8048090 <_start+36>
eflags         0x246    [ PF ZF IF ]
cs             0x23 35
ss             0x2b 43
ds             0x2b 43
es             0x2b 43
fs             0x0  0
gs             0x0  0

该代码存在许多问题。首先,您似乎忘记了
OUTSB
是一条特权指令,即只有在调用进程具有环0访问权限时才能执行,即它是内核代码的一部分。据我所知,Linux中唯一可以访问特权指令的代码是内核本身及其加载的模块。当您试图从非特权代码段执行特权指令时,所有其他进程都会给您一个
分段故障
(实际上是CPU发出的
一般保护故障
)。不过,我不知道调用
ioperm
syscall如何影响这一点

其次,
OUTSB
将一个字节从
ESI
指定的内存位置写入
DX
中的I/O端口。在本例中,您告诉处理器从位置
0xff
将数据写入端口,而进程肯定无法访问该端口。您可以通过简单地将代码更改为使用
OUT
指令来简化这一过程,因为
OUTSB
REP
前缀一起使用。试试这个:

mov al, 0xff
out 0x378, al
这会将
al
中的字节输出到立即数操作数指定的I/O端口,在本例中为
0x378


让我知道结果如何。

该代码存在许多问题。首先,您似乎忘记了
OUTSB
是一条特权指令,即只有在调用进程具有环0访问权限时才能执行,即它是内核代码的一部分。据我所知,Linux中唯一可以访问特权指令的代码是内核本身及其加载的模块。当您试图从非特权代码段执行特权指令时,所有其他进程都会给您一个
分段故障
(实际上是CPU发出的
一般保护故障
)。不过,我不知道调用
ioperm
syscall如何影响这一点

其次,
OUTSB
将一个字节从
ESI
指定的内存位置写入
DX
中的I/O端口。在本例中,您告诉处理器从位置
0xff
将数据写入端口,而进程肯定无法访问该端口。您可以通过简单地将代码更改为使用
OUT
指令来简化这一过程,因为
OUTSB
REP
前缀一起使用。试试这个:

mov al, 0xff
out 0x378, al
这会将
al
中的字节输出到立即数操作数指定的I/O端口,在本例中为
0x378


让我知道结果如何。

另一个注意事项:我刚刚修订了一些英特尔手册,显然可以将各个I/O端口的权限授予各个任务。这需要在操作系统中使用TSS(任务状态段)结构,我不确定Linux是否在每个进程中都使用它。但是
OUTSB
在这种情况下使用不当,因为它会导致从进程地址空间之外的内存区域进行读取。@Daniel我编辑了我的帖子-程序的C版本可以工作(在userland中以root用户身份运行时)。
sys/io.h
中定义了
outb()
函数<代码>静态uuu内联void outb(无符号字符值,无符号短整数端口){uuuu asm_uuuuuvolatile_uuu(“outb%b0,%w1”)::“a”(uuu值),“Nd”(uuu端口))感谢更新!显然,
ioperm
以某种方式修改了任务的I/O权限端口映射,并允许它执行特权I/O指令。@tMC:use
out dx,al
。根据您自己的引用,on OUT::目标操作数可以是字节立即数或DX寄存器。使用字节立即数允许访问I/O端口地址0到255;使用DX寄存器作为源操作数,可以访问0到65535之间的I/O端口。另一方面,我刚刚修订了一些英特尔手册,显然可以将各个I/O端口的权限授予各个任务。这需要在操作系统中使用TSS(任务状态段)结构,我不确定Linux是否在每个进程中都使用它。但是
OUTSB
在这种情况下使用不当,因为它会导致从进程地址空间之外的内存区域进行读取。@Daniel我编辑了我的帖子-程序的C版本可以工作(在userland中以root用户身份运行时)。
sys/io.h
中定义了
outb()
函数<代码>静态uuu内联void outb(无符号字符值,无符号短整数端口){uuuu asm_uuuuuvolatile_uuu(“outb%b0,%w1”)::“a”(uuu值),“Nd”(uuu端口))感谢更新!显然,
ioperm
以某种方式修改了任务的I/O权限端口映射,并允许它执行特权I/O指令。@tMC:use
out dx,al
。根据您自己的引用,on OUT::目标操作数可以是字节立即数或DX寄存器。使用字节立即数允许访问I/O端口地址0到255;使用DX寄存器作为源操作数可以访问0到65535之间的I/O端口。