为什么我在跟踪 write() 时得到的结果与其他人不同?
我正在做一些 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
这是我应该看到的吗?如果是的话,为什么和我一些同学看到的不一样? 在执行 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
以下说明适用于 Linux:
查找第二次调用 write,“call write”,在 secondaryCall 中 功能。在此行设置断点。据我所知是22。
在此行设置断点。
break 22
在调试器中运行程序。
run
程序将在您设置的断点处停止。 单步执行没有调试信息的代码。
si
您将在源布局中看到“[ 无可用源 ]”。所以你需要查看 拆解后的指令。
layout asm
反复单步进入(si然后return/enter就会执行si命令 重复),直到您看到“sysenter”出现在屏幕的 asm 布局部分。 我试图将指令(包括它们的地址)从 asm 布局部分的顶部复制到并包括 sysenter 指令。
提示:您可以通过键入 Ctrl-x o 将键盘焦点更改到命令区域。这 方向键可用于返回较早的命令(它只是节省了一些 打字)。
I am doing some x86 exercises; my assignment has me stepping through the assembly code for the write()
library call in a debugger until we reach a SYSENTER
instruction, but I get different results from that of some of my classmates. What I saw leading up to SYSENTER
was:
│0xf7fdf421 <__kernel_vsyscall+1> push %edx
│0xf7fdf422 <__kernel_vsyscall+2> push %ebp
│0xf7fdf423 <__kernel_vsyscall+3> mov %esp,%ebp
│0xf7fdf425 <__kernel_vsyscall+5> sysenter
Is this what I should see? If so, why is it different from what some of my classmates saw?
Also are %edx and %ebp registers saved on the stack before executing the sysenter
instruction? (Would it not seem so according to the answer I got or am I wrong?)
Here's my original instructions from my assignment:
The assembly code:
.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
The following instructions are for Linux:
Find the line number of the second call to write, “call write”, in the secondCall
function. Set a break point at this line. Which is 22 according to me.
Set a break point at this line.
break 22
Run the program inside the debugger.
run
The program will stop at the break point you set.
Step into the code which does not have the debugging information.
si
You will see “[ No Source Available ]” in the source layout. So you need to view
the disassembled instructions.
layout asm
Repeatedly step into (si and then return/enter will execute the si command
repeatedly) until you see “sysenter” appear in the asm layout section of the screen.
I am trying to copy the instructions (including their addresses) from the top of the asm layout section, down to and including the sysenter instruction.
Hint: You can change the focus of the keyboard to the command area by typing Ctrl-x o. This
way the arrow keys can be used to bring back earlier commands (it just saves some
typing).
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
您正在追踪所谓的“虚拟动态共享对象”(VDSO) - 其内容是 Linux 内核的实现细节。有多种情况会导致 VDSO 内容发生变化;因此这里没有单一的正确答案。
特别是,在 32 位 x86 系统上,至少有三种不同的机制可用于进行系统调用:
INT $0x80
SYSCALL
(最新的 AMD CPU)SYSENTER
(最新的 Intel CPU)您会注意到,只有
INT $0x80
适用于所有 CPU(事实上,即使有更现代的替代方案,内核也使其可用于遗留应用程序)也可用);然而,它也很慢。内核将在启动时探测支持哪些版本,并选择使用最有效机制的 VDSO 版本。因此,根据您的 CPU 型号,您可能会在 VDSO 中看到不同的代码 - 特别是,如果您有 AMD CPU,您可能会看到
SYSCALL
路径,如果您有对于非常旧的 CPU,您甚至可能会看到INT $0x80
路径。如果您对其他内容感到好奇,这里是源代码:INT $0x80
方法SYSCALL
方法SYSENTER
方法 - 阅读注释了解为什么%ecx
、%edx
和%ebp 被推送
最有可能的是,您实验室中获得不同结果的其他人拥有 AMD CPU,并且正在查看
SYSCALL
路径(或者他们有一台古董 PC,并且正在查看 <代码>INT $0x80路径)。另请注意,在 64 位进程中,将直接使用
SYSCALL
,根本不经过 VDSO。You are tracing into what is called the 'virtual dynamic shared object' (VDSO) - the contents of which are an implementation detail of the linux kernel. There are a number of conditions that can cause the contents of the VDSO to change; as such there is no single correct answer here.
In particular, on 32-bit x86 systems, there are at least three different mechanisms that can be used to make a system call:
INT $0x80
SYSCALL
(recent AMD CPUs)SYSENTER
(recent Intel CPUs)You'll note that only
INT $0x80
works on all CPUs (indeed, the kernel makes it available for legacy applications even when more modern alternatives are also available); however, it's also slow. The kernel will probe for which are supported at boot time, and select a version of the VDSO that uses the most efficient mechanism available.As such, depending on your CPU model, you may see different code in the VDSO - in particular, if you have an AMD CPU, you're likely to see the
SYSCALL
path, and if you have a really old CPU you might even see theINT $0x80
path. If you're curious about the others, here's the source code:INT $0x80
methodSYSCALL
methodSYSENTER
method - read the comments for why%ecx
,%edx
, and%ebp
are pushedMost likely, the other folks in your lab who got a different result had an AMD CPU and were looking at the
SYSCALL
path (or they had an antique PC, and were looking at theINT $0x80
path).Note also that in a 64-bit process,
SYSCALL
will be used directly, without going through the VDSO at all.