跟踪write()时为什么会得到与其他人不同的结果?

Jay*_*Jay -2 c linux x86 assembly system-calls

我正在做一些x86练习; 我的任务让我write()在调试器中逐步调试库调用的汇编代码,直到我们达到SYSENTER指令,但是我得到的结果与我的一些同学不同.我看到的SYSENTER是:

   ?0xf7fdf421 <__kernel_vsyscall+1>        push   %edx                                                                                            
   ?0xf7fdf422 <__kernel_vsyscall+2>        push   %ebp                                                                                            
   ?0xf7fdf423 <__kernel_vsyscall+3>        mov    %esp,%ebp                                                                                       
   ?0xf7fdf425 <__kernel_vsyscall+5>        sysenter    
Run Code Online (Sandbox Code Playgroud)

这是我应该看到的吗?如果是这样,为什么它与我的一些同学看到的不一样?在执行sysenter指令之前,%edx和%ebp寄存器也保存在堆栈中吗?(根据我得到的答案,或者我错了,它似乎不是这样吗?)

这是我作业的原始说明:

汇编代码:

.file    "A3Program2.c"
    .section    .rodata
.LC0:
    .string    "hello\n"
.LC1:
    .string    "xxxx\n"
    .text
.globl secondCall
    .type    secondCall, @function
secondCall:
    pushl    %ebp
    movl    %esp, %ebp
    subl    $40, %esp
    movl    $6, 8(%esp)
    movl    $.LC0, 4(%esp)
    movl    $1, (%esp)
    call    write
    movl    %eax, -12(%ebp)
    movl    $8, 8(%esp)
    movl    $.LC1, 4(%esp)
    movl    $1, (%esp)
    call    write
    addl    %eax, -12(%ebp)
    movl    12(%ebp), %eax
    movl    8(%ebp), %edx
    leal    (%edx,%eax), %eax
    addl    %eax, -12(%ebp)
    movl    -12(%ebp), %eax
    leave
    ret
    .size    secondCall, .-secondCall
.globl firstCall
    .type    firstCall, @function
firstCall:
    pushl    %ebp
    movl    %esp, %ebp
    subl    $40, %esp
    movl    $2, 4(%esp)
    movl    $4, (%esp)
    call    secondCall
    movl    %eax, -12(%ebp)
    movl    -12(%ebp), %eax
    leave
    ret
    .size    firstCall, .-firstCall
.globl main
    .type    main, @function
main:
    pushl    %ebp
    movl    %esp, %ebp
    andl    $-16, %esp
    subl    $16, %esp
    call    firstCall
    movl    %eax, 12(%esp)
    movl    $0, %eax
    leave
    ret
    .size    main, .-main
    .ident    "GCC: (Ubuntu/Linaro 4.4.4-14ubuntu5) 4.4.5"
    .section    .note.GNU-stack,"",@progbits
Run Code Online (Sandbox Code Playgroud)

以下说明适用于Linux:

在secondCall函数中找到第二次写入调用的行号"call write".在此行设置断点.根据我的说法,这是22.

在此行设置断点.

 break 22
Run Code Online (Sandbox Code Playgroud)

在调试器中运行程序.

 run
Run Code Online (Sandbox Code Playgroud)

程序将在您设置的断点处停止.进入没有调试信息的代码.

  si 
Run Code Online (Sandbox Code Playgroud)

您将在源布局中看到"[No Source Available]".所以你需要查看反汇编的指令.

 layout asm
Run Code Online (Sandbox Code Playgroud)

重复步入(si然后返回/输入将重复执行si命令),直到您看到"sysenter"出现在屏幕的asm布局部分中.我试图从asm布局部分的顶部复制指令(包括它们的地址),直到并包括sysenter指令.

提示:您可以通过键入Ctrl-x o将键盘的焦点更改为命令区域.这样,箭头键可以用来带回早期的命令(它只是节省一些打字).

bdo*_*lan 10

您正在追踪所谓的"虚拟动态共享对象"(VDSO) - 其内容是Linux内核的实现细节.有许多条件可能导致VDSO的内容发生变化; 因此,这里没有一个正确答案.

特别是,在32位x86系统上,至少有三种不同的机制可用于进行系统调用:

  • INT $0x80
  • SYSCALL (最近的AMD CPU)
  • SYSENTER (最近的英特尔CPU)

您会注意到只INT $0x80适用于所有CPU(实际上,内核使其可用于遗留应用程序,即使还有更现代的替代方案); 然而,它也很慢.内核将探测引导时支持哪些内核,并选择使用最有效机制的VDSO版本.

因此,根据您的CPU型号,您可能会在VDSO中看到不同的代码 - 特别是,如果您有AMD CPU,您可能会看到SYSCALL路径,如果您有一个非常老的CPU,您甚至可能会看到INT $0x80路径.如果你对其他人感到好奇,这里是源代码:

最有可能的是,你实验室中得到不同结果的其他人有一个AMD CPU并且正在寻找SYSCALL路径(或者他们有一台古董PC,正在寻找INT $0x80路径).

另请注意,在64位进程中,SYSCALL将直接使用,而无需通过VDSO.

  • @Jay,哇,bdonlan给出了我见过的这个功能的_best_写作之一 - 顺便提一下,为你提供了为什么你和你的同伴得到不同答案的原因 - 你是_unhappy_ ?? (3认同)