用嵌入式汇编完成系统调用

这篇是网易云课堂《Linux内核分析》这门课的作业

看下面这段程序:

Else块中做的和IF块中做的是一回事,都是进行了了getuid这个系统调用取得了当前用户的uid。现在分行解释一下:

mov $0,%%ebx\n\t

这一行是把ebx寄存器的值清0,在系统调用时第一个参数从ebx中来,这相当于传递了NULL。

通过查询系统调用表我们知道getuid()的系统调用功能号是24,16进制为0x18。系统调用功能号是由eax寄存器传递的,那么下面这一行的意思就很明显了:

mov $0x18,%%eax\n\t

然后发送软中断信号给内核,系统调用的中断号是0x80,所以:

int $0x80\n\t

int命令会引发一个编程异常,这个异常会被内核trap住。还记得我们在start_kernel中有trap_init吗?

#ifdef CONFIG_IA32_EMULATION
    set_system_intr_gate(IA32_SYSCALL_VECTOR, ia32_syscall);
    set_bit(IA32_SYSCALL_VECTOR, used_vectors);
#endif

IA32_SYSCALL_VECTOR这个宏的定义在irq_vectors.h

#define IA32_SYSCALL_VECTOR        0x80
#ifdef CONFIG_X86_32
# define SYSCALL_VECTOR            0x80
#endif

中断让程序陷入内核态,内核通过中断向量表知道当前是要进行系统调用。然后进入系统调用入口system_call:

ENTRY(system_call)
    RING0_INT_FRAME            # can't unwind into user space anyway
    ASM_CLAC
    pushl_cfi %eax            # save orig_eax
    SAVE_ALL
    GET_THREAD_INFO(%ebp)
                    # system call tracing in operation / emulation
    testl $_TIF_WORK_SYSCALL_ENTRY,TI_flags(%ebp)
    jnz syscall_trace_entry
    cmpl $(NR_syscalls), %eax
    jae syscall_badsys
syscall_call:
    call *sys_call_table(,%eax,4)
syscall_after_call:
    movl %eax,PT_EAX(%esp)        # store the return value
syscall_exit:
    LOCKDEP_SYS_EXIT
    DISABLE_INTERRUPTS(CLBR_ANY)    # make sure we don't miss an interrupt
                    # setting need_resched or sigpending
                    # between sampling and the iret
    TRACE_IRQS_OFF
    movl TI_flags(%ebp), %ecx
    testl $_TIF_ALLWORK_MASK, %ecx    # current->work
    jne syscall_exit_work

restore_all:
    TRACE_IRQS_IRET
restore_all_notrace:
#ifdef CONFIG_X86_ESPFIX32
    movl PT_EFLAGS(%esp), %eax    # mix EFLAGS, SS and CS
    # Warning: PT_OLDSS(%esp) contains the wrong/random values if we
    # are returning to the kernel.
    # See comments in process.c:copy_thread() for details.
    movb PT_OLDSS(%esp), %ah
    movb PT_CS(%esp), %al
    andl $(X86_EFLAGS_VM | (SEGMENT_TI_MASK << 8) | SEGMENT_RPL_MASK), %eax
    cmpl $((SEGMENT_LDT << 8) | USER_RPL), %eax
    CFI_REMEMBER_STATE
    je ldt_ss            # returning to user-space with LDT SS
#endif
restore_nocheck:
    RESTORE_REGS 4            # skip orig_eax/error_code
irq_return:
    INTERRUPT_RETURN
.section .fixup,"ax"
ENTRY(iret_exc)
    pushl $0            # no error code
    pushl $do_iret_error
    jmp error_code
.previous
    _ASM_EXTABLE(irq_return,iret_exc)

#ifdef CONFIG_X86_ESPFIX32
    CFI_RESTORE_STATE
ldt_ss:
#ifdef CONFIG_PARAVIRT
    /*
     * The kernel can't run on a non-flat stack if paravirt mode
     * is active.  Rather than try to fixup the high bits of
     * ESP, bypass this code entirely.  This may break DOSemu
     * and/or Wine support in a paravirt VM, although the option
     * is still available to implement the setting of the high
     * 16-bits in the INTERRUPT_RETURN paravirt-op.
     */
    cmpl $0, pv_info+PARAVIRT_enabled
    jne restore_nocheck
#endif

 可以看到system_call中调用了SAVE_ALL保护现场,将当前的栈顶,cs:eip,状态号压栈。

.macro SAVE_ALL
    cld
    PUSH_GS
    pushl_cfi %fs
    /*CFI_REL_OFFSET fs, 0;*/
    pushl_cfi %es
    /*CFI_REL_OFFSET es, 0;*/
    pushl_cfi %ds
    /*CFI_REL_OFFSET ds, 0;*/
    pushl_cfi %eax
    CFI_REL_OFFSET eax, 0
    pushl_cfi %ebp
    CFI_REL_OFFSET ebp, 0
    pushl_cfi %edi
    CFI_REL_OFFSET edi, 0
    pushl_cfi %esi
    CFI_REL_OFFSET esi, 0
    pushl_cfi %edx
    CFI_REL_OFFSET edx, 0
    pushl_cfi %ecx
    CFI_REL_OFFSET ecx, 0
    pushl_cfi %ebx
    CFI_REL_OFFSET ebx, 0
    movl $(__USER_DS), %edx
    movl %edx, %ds
    movl %edx, %es
    movl $(__KERNEL_PERCPU), %edx
    movl %edx, %fs
    SET_KERNEL_GS %edx
.endm

然后查表找到到系统调用服务程序并执行:

syscall_call:
    call *sys_call_table(,%eax,4)

从eax中知道系统调用功能号是24,而24对应着getuid(在unistd32.h中定义)。

#define __NR_getuid 24
__SYSCALL(__NR_getuid, sys_getuid16)

执行完系统调用服务程序以后将返回值写回eax寄存器:

syscall_after_call:
    movl %eax,PT_EAX(%esp)        # store the return value

然后或者发生进程调度,或者返回用户态接着执行(这一段代码不是很懂)。

所以我们可以从eax寄存器拿到getuid的返回值。并且写到内存变量uid。

mov %%eax,%0

 

编译这个程序然后测试一下

得到的用户id和id这条命令得到的是一致的。

刘聪 + 原创作品转载请注明出处 + 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000

转载于:https://www.cnblogs.com/nmyh/p/4375387.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值