我一直在通过分解一些C代码来学习汇编语言。当我用GDB反汇编这个基本C代码时:
#include <stdio.h>
void main(void) {
printf("Hello World\n");
}在汇编代码中,它给出了这条线:
0x08048424 <+25>: call 0x80482e0 <puts@plt>但是,当我在下面的代码中反汇编它的printf函数中有一个整数时:
#include <stdio.h>
void main(void) {
int a = 1;
printf("Hello Word %d\n", a);
}它给出了这一行:
0x0804842e <+35>: call 0x80482e0 <printf@plt>printf@plt和put@plt之间有什么区别?
为什么反汇编程序不识别没有整数参数的printf函数?
发布于 2016-08-17 22:10:26
在GCC中,printf和puts都是内置函数.这意味着编译器对它们的语义有充分的了解。在这种情况下,如果编译器认为它将产生更好(更快和/或更紧凑)的代码,那么编译器可以将对一个函数的调用替换为对另一个函数的等效调用。
puts通常是一个更有效的函数,因为它不需要解析和解释格式字符串。
你的案子就是这样的。第一次调用printf并不需要任何printf-specific特性。您向printf提供的格式字符串非常简单:其中没有转换说明符。编译器认为,对printf的第一次调用与对puts的等效调用提供更好的服务。
同时,您对printf的第二次调用使用了printf格式字符串,即它依赖于printf-specific特性。
(从2005年开始,对这一具体问题进行了相当深入的研究:printf.html)
发布于 2016-08-17 22:13:09
我不知道@plt部分,但是printf和puts只是两个不同的标准库函数。printf采用格式字符串和零或多个其他参数,可能是不同类型的参数。puts只需要一个字符串并打印它,然后是一个换行符。有关更多信息或类型,请参阅任何C引用
man 3 printf
man 3 puts假设您在安装了手册页的类似Unix的系统上。(没有man printf的3将显示printf命令;您需要printf函数。)
您的编译器能够优化调用。
printf("Hello, world\n");相当于:
puts("Hello, world");因为它知道这两个函数都做什么,所以它可以确定它们所做的事情完全相同。
它不能优化
printf("Hello Word %d\n", a);因为a的值在编译时是未知的,所以它不会打印固定的字符串。(通过观察a在初始化后从未被修改过,可以在更高的优化级别上解决这个问题)。
反汇编程序只是向您展示编译器生成的代码。
(顺便说一句,void main(void)是不正确的;使用int main(void)。)
发布于 2016-08-17 22:43:51
puts和printf函数似乎具有相同的地址,因为您查看的是存根,而不是真正的函数。这些存根从过程链接表( @plt后缀所指的内容)加载一个地址,然后调用它。
我正在拆卸一个程序,并看到它有printf和puts的存根
08048370 <printf@plt>:
8048370: ff 25 04 a0 04 08 jmp *0x804a004
8048376: 68 08 00 00 00 push $0x8
804837b: e9 d0 ff ff ff jmp 8048350 <_init+0x3c>
08048380 <puts@plt>:
8048380: ff 25 08 a0 04 08 jmp *0x804a008
8048386: 68 10 00 00 00 push $0x10
804838b: e9 c0 ff ff ff jmp 8048350 <_init+0x3c>正如您所看到的,真正的函数在其他地方,这些存根只为您的程序实际使用的函数生成。如果您有一个程序只调用一个函数,然后将它从printf更改为puts,那么一个存根和唯一存根位于同一个地址并不奇怪。我刚刚分解的程序同时调用了printf和puts,因此两者都有存根,因此它们有不同的地址。
https://stackoverflow.com/questions/39007002
复制相似问题