繁体   English   中英

*地址(在printf中找到)在汇编中意味着什么?

[英]What does * address(found in printf) mean in assembly?

拆解printf没有提供太多信息:

(gdb) disas printf
Dump of assembler code for function printf:
0x00401b38 <printf+0>:  jmp    *0x405130
0x00401b3e <printf+6>:  nop
0x00401b3f <printf+7>:  nop
End of assembler dump.


(gdb) disas 0x405130
Dump of assembler code for function _imp__printf:
0x00405130 <_imp__printf+0>:    je     0x405184 <_imp__vfprintf+76>
0x00405132 <_imp__printf+2>:    add    %al,(%eax)

它是如何在引擎盖下实施的?

为什么拆卸无济于事?

0x405130之前*含义是什么?

*是用于间接内存引用的AT&T汇编语法。

jmp *<addr>

表示“跳转到存储在<addr>中的<addr> ”。

它等效于以下Intel语法:

jmp [addr]

使用寄存器或存储器操作数的分支寻址必须以'*'为前缀

资源

实际上,所有C编译器都为源提供了运行时库 - 而不仅仅是开源编译器。 不幸的是,它们通常以难以遵循的形式编写,并且通常不带有设计原理文档。

因此,处理该问题的一个非常好的资源是PJ Plauger的“标准C库” ,它不仅提供了库实现的源,而且还详细说明了它的设计方式以及这种库可能具有的特殊情况。考虑。

按照本书的某些“使用”版本的价格,这是一个窃取,应该在任何严肃的C程序员的书架上。

Plauger有类似的书籍,目标是我认为具有类似价值的C ++库:

我会说拆解工作在这里很好,并且printf是在引擎盖下使用vfprintf实现的,这几乎是你所期望的。 请注意,汇编程序通常比C更冗长,并且需要花费时间来理解您没有注释源的位置。 编译器输出也不是教你自己汇编器的好方法。

至于

在0x405130之前*的含义是什么?

我不熟悉gdb的反汇编程序,但看起来jmp *0x405130是间接跳过指针。 而不是拆解0x405130处的内容,你应该在那里转储4个字节的内存。 我愿意打赌你会在那里找到另一个地址,如果你拆开那个位置,你会发现printf()的代码(反汇编的可读性是另一个故事)。

换句话说, _imp__printf是指向printf()的指针,而不是printf()本身。


根据以下评论中的更多信息进行编辑:

在使用英特尔汇编语法时, jmp *0x405130jmp [0x405130]指令的GAS / AT&T汇编语法。

令你好奇的是你说gdb命令x/xw 0x405130显示该地址包含0x00005274 (这似乎与你在反汇编0x405130时得到的相匹配)。 但是,这意味着jmp [0x405130]会尝试跳转到地址0x00005274 ,这似乎不正确(当你试图反汇编该地址时,gdb 0x00005274说。

_imp_printf条目可能使用某种延迟绑定技术,其中第一次执行跳转到0x405130,它命中0x00005274地址,这导致操作系统记录陷阱并修复动态链接。 修复后,操作系统将使用0x405130中的正确链接地址重新启动执行。 但这只是我的猜测。 我不知道你使用的系统是否做了这样的事情(事实上,我甚至不知道你在运行什么系统),但这在技术上是可行的。 如果发生类似这样的事情,则在第一次调用printf()之后,才会在0x405130中看到正确的地址。

我认为你需要在程序集层面单步调用printf()来查看真正发生的事情。


使用GDB会话更新了信息:

这是您遇到的问题 - 您在系统加载DLL之前查看了该过程并修复了与DLL的链接。 这是使用GDB调试的MinGW编译的简单“hello world”程序的调试会话:

C:\temp>\mingw\bin\gdb test.exe
GNU gdb (GDB) 7.1
Copyright (C) 2010 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "mingw32".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from C:\temp/test.exe...done.

(gdb) disas main
Dump of assembler code for function main:
   0x004012f0 <+0>:     push   %ebp
   0x004012f1 <+1>:     mov    %esp,%ebp
   0x004012f3 <+3>:     sub    $0x8,%esp
   0x004012f6 <+6>:     and    $0xfffffff0,%esp
   0x004012f9 <+9>:     mov    $0x0,%eax
   0x004012fe <+14>:    add    $0xf,%eax
   0x00401301 <+17>:    add    $0xf,%eax
   0x00401304 <+20>:    shr    $0x4,%eax
   0x00401307 <+23>:    shl    $0x4,%eax
   0x0040130a <+26>:    mov    %eax,-0x4(%ebp)
   0x0040130d <+29>:    mov    -0x4(%ebp),%eax
   0x00401310 <+32>:    call   0x401850 <_alloca>
   0x00401315 <+37>:    call   0x4013d0 <__main>
   0x0040131a <+42>:    movl   $0x403000,(%esp)
   0x00401321 <+49>:    call   0x4018b0 <printf>
   0x00401326 <+54>:    mov    $0x0,%eax
   0x0040132b <+59>:    leave
   0x0040132c <+60>:    ret
End of assembler dump.

请注意,反汇编printf()会导致类似的间接跳转:

(gdb) disas printf
Dump of assembler code for function printf:
   0x004018b0 <+0>:     jmp    *0x4050f8     ; <<-- indirect jump
   0x004018b6 <+6>:     nop
   0x004018b7 <+7>:     nop
End of assembler dump.

并且_imp__printf symbiol作为代码毫无意义......

(gdb) disas 0x4050f8
Dump of assembler code for function _imp__printf:
   0x004050f8 <+0>:     clc                 ; <<-- how can this be printf()?
   0x004050f9 <+1>:     push   %ecx
   0x004050fa <+2>:     add    %al,(%eax)
End of assembler dump.

或作为指针......

(gdb) x/xw 0x4050f8
0x4050f8 <_imp__printf>:        0x000051f8  ; <<-- 0x000051f8 is an invalid pointer

现在,让我们在main()设置一个断点,然后运行它:

(gdb) break main
Breakpoint 1 at 0x40131a: file c:/temp/test.c, line 5.

(gdb) run
Starting program: C:\temp/test.exe
[New Thread 11204.0x2bc8]
Error while mapping shared library sections:
C:\WINDOWS\SysWOW64\ntdll32.dll: No such file or directory.

Breakpoint 1, main () at c:/temp/test.c:5
5           printf( "hello world\n");

printf()看起来一样:

(gdb) disas printf
Dump of assembler code for function printf:
   0x004018b0 <+0>:     jmp    *0x4050f8
   0x004018b6 <+6>:     nop
   0x004018b7 <+7>:     nop
End of assembler dump.

_imp__printf看起来不同 - 动态链接现已修复:

(gdb) x/xw 0x4050f8
0x4050f8 <_imp__printf>:        0x77bd27c2

如果我们反汇编_imp__printf现在指向的东西,它可能不是很可读,但现在显然它是代码。 这是在MSVCRT.DLL中实现的printf()

(gdb) disas _imp__printf
Dump of assembler code for function printf:
   0x77bd27c2 <+0>:     push   $0x10
   0x77bd27c4 <+2>:     push   $0x77ba4770
   0x77bd27c9 <+7>:     call   0x77bc84c4 <strerror+554>
   0x77bd27ce <+12>:    mov    $0x77bf1cc8,%esi
   0x77bd27d3 <+17>:    push   %esi
   0x77bd27d4 <+18>:    push   $0x1
   0x77bd27d6 <+20>:    call   0x77bcca49 <msvcrt!_lock+4816>
   0x77bd27db <+25>:    pop    %ecx
   0x77bd27dc <+26>:    pop    %ecx
   0x77bd27dd <+27>:    andl   $0x0,-0x4(%ebp)
   0x77bd27e1 <+31>:    push   %esi
   0x77bd27e2 <+32>:    call   0x77bd400d <wscanf+3544>
   0x77bd27e7 <+37>:    mov    %eax,-0x1c(%ebp)
   0x77bd27ea <+40>:    lea    0xc(%ebp),%eax
   0x77bd27ed <+43>:    push   %eax
   0x77bd27ee <+44>:    pushl  0x8(%ebp)
   0x77bd27f1 <+47>:    push   %esi
   0x77bd27f2 <+48>:    call   0x77bd3330 <wscanf+251>
   0x77bd27f7 <+53>:    mov    %eax,-0x20(%ebp)
   0x77bd27fa <+56>:    push   %esi
   0x77bd27fb <+57>:    pushl  -0x1c(%ebp)
   0x77bd27fe <+60>:    call   0x77bd4099 <wscanf+3684>
   0x77bd2803 <+65>:    add    $0x18,%esp
   0x77bd2806 <+68>:    orl    $0xffffffff,-0x4(%ebp)
   0x77bd280a <+72>:    call   0x77bd281d <printf+91>
   0x77bd280f <+77>:    mov    -0x20(%ebp),%eax
   0x77bd2812 <+80>:    call   0x77bc84ff <strerror+613>
   0x77bd2817 <+85>:    ret
   0x77bd2818 <+86>:    mov    $0x77bf1cc8,%esi
   0x77bd281d <+91>:    push   %esi
   0x77bd281e <+92>:    push   $0x1
   0x77bd2820 <+94>:    call   0x77bccab0 <msvcrt!_lock+4919>
   0x77bd2825 <+99>:    pop    %ecx
   0x77bd2826 <+100>:   pop    %ecx
   0x77bd2827 <+101>:   ret
   0x77bd2828 <+102>:   int3
   0x77bd2829 <+103>:   int3
   0x77bd282a <+104>:   int3
   0x77bd282b <+105>:   int3
   0x77bd282c <+106>:   int3
End of assembler dump.

它可能比您希望的更难阅读,因为我不确定它是否有适当的符号(或者GDB是否可以正确读取这些符号)。

但是, 正如我在另一个答案中提到的 ,您通常可以使用编译器获取C运行时例程的源代码,无论是否为开源代码。 MinGW没有附带MSVDRT.DLL的源代码,因为那是一个Windows的东西,但你可以在Visual Studio发行版中获得它的源代码(或者它非常接近它) - 我认为即使是免费的VC ++ Express随附运行时源(但我可能错了)。

printf()很可能位于动态共享库中。 动态链接器使用导入函数的地址填充表; 这就是你必须进行间接呼叫的原因。

我真的不记得这是如何运作的; 优化可能会使流程复杂化。 但是你明白了。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM