我正在尝试学习汇编,并且可以得到一些示例,但这是令人迷惑的。
内核如何知道抢占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寄存器上的数据/输出?
答案 0 :(得分:2)
参数的位置是ABI的一部分。根据{{3}}:
通过如下设置通用寄存器来传递参数:
Syscall # | Param 1 | Param 2 | Param 3 | Param 4 | Param 5 | Param 6 eax | ebx | ecx | edx | esi | edi | ebp Return value eax
答案 1 :(得分: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开发人员也可能会决定采用其他方式。