这个内核调用如何知道从计数器寄存器中获取

问题描述 投票:0回答:2

我正在努力学习装配,我可以举几个例子,但这很神秘。

内核如何知道将ecx寄存器中的内容作为指向用户空间内存的指针来显示在stdout

mov edx,9       ;message length
mov ecx, name   ;message to write
mov ebx,1       ;file descriptor (stdout)
mov eax,4       ;system call number (sys_write)
int 0x80        ;call kernel

如果edx是通用数据寄存器,并且eax是通用输入输出,为什么内核调用期望ecx寄存器上的数据/输出?

assembly x86 system-calls api-design calling-convention
2个回答
2
投票

参数的位置是ABI的一部分。每https://en.wikibooks.org/wiki/X86_Assembly/Interfacing_with_Linux#Making_a_syscall

通过将通用寄存器设置如下来传递参数:

Syscall # | Param 1 | Param 2 | Param 3 | Param 4 | Param 5 | Param 6
eax       | ebx     | ecx     | edx     | esi     | edi     | ebp

Return value
eax

1
投票

...为什么内核调用期望ecx寄存器上的数据/输出?

中断是子例程的一种特殊形式,其工作方式类似于使用call指令调用的子例程。

当输入中断时,首先要做的是push堆栈上的所有寄存器。这意味着所有寄存器都将存储在RAM存储器中(因为堆栈是RAM存储器)。

在Linux中,将使用汇编代码调用用C编程语言编写的函数。

在C编程语言中,如果知道数据是如何存储的,则可以使用struct来访问存储在RAM中的数据。因为我们知道我们在汇编代码中编写了push指令的顺序,我们可以定义一个struct,它可以用来访问堆栈上的数据:

struct registers {
    unsigned long ebx;
    unsigned long ecx;
    unsigned long edx;
    ...
    unsigned long eax;
    unsigned long eip;
    ...
}

在内核中的C编写函数中,我们现在可以访问此结构来读出寄存器值:

void systemCall_4(struct registers * regs)
{
    kernelFile * f;
    int (*pWrite)(kernelFile *,const void *,int);

    /* Get the file from the file handle */
    f = getFileFromHandle(regs->ebx);

    /* No such file */
    if(f == NULL)
    {
        regs->eax = ERROR_INVALID_HANDLE;
    }
    /* Call the device driver */
    else
    {
        pWrite = f->writeFunction;
        regs->eax = pWrite(f, (const void *)(regs->ecx), regs->edx);
    }
}

内核程序员决定定义ecx指向数据,edx指的是长度。

在MS-DOS(例如)中,它是另一种方式:ecx是长度,edx指向数据。所以你看到Linux开发人员也可能决定采用不同的方式。

© www.soinside.com 2019 - 2024. All rights reserved.