问题
I've been learning assembly language by disassembling some C code. When I disassemble this basic C code with GDB:
#include <stdio.h>
void main(void) {
printf("Hello World\n");
}
Among assembly code, it gives this line:
0x08048424 <+25>: call 0x80482e0 <puts@plt>
However, when I disassemble below code which has an integer in printf function:
#include <stdio.h>
void main(void) {
int a = 1;
printf("Hello Word %d\n", a);
}
It gives this line:
0x0804842e <+35>: call 0x80482e0 <printf@plt>
What is the difference between printf@plt and puts@plt?
Why disassembler does not recognize printf function without an integer parameter?
回答1:
In GCC printf
and puts
are built-in functions. Which means that the compiler has full knowledge of their semantics. In such cases the compiler is free to replace a call to one function with an equivalent call to another, if it thinks that it will produce better (faster and/or more compact) code.
puts
is a generally more efficient function since it does not have to parse and interpret a format string.
This is exactly what happened in your case. Your first call to printf
does not really need any printf
-specific features. The format string you supplied to printf
is trivial: it has no conversion specifiers in it. The compiler thought that your first call to printf
is better served with an equivalent call to puts
.
Meanwhile, your second call to printf
makes non-trivial use of printf
format string, i.e. it relies on printf
-specific features.
(Some rather thorough research of this specific matter from 2005: http://www.ciselant.de/projects/gcc_printf/gcc_printf.html)
回答2:
I don't know about the @plt
part, but printf
and puts
are simply two different standard library functions. printf
takes a format string and zero or more other parameters, possibly of different types. puts
takes just a string and prints it, followed by a newline. Consult any C reference for more information, or type
man 3 printf
man 3 puts
assuming you're on a Unix-like system with man pages installed. (man printf
without the 3
will show you the printf
command; you want the printf
function.)
Your compiler is able to optimize the call
printf("Hello, world\n");
to the equivalent of:
puts("Hello, world");
because it knows what both functions do, so it can determine that they do exactly the same thing.
It can't optimize
printf("Hello Word %d\n", a);
because the value of a
is unknown at compile time, so it doesn't print a fixed string. (It might figure it out at higher optimization levels by observing that a
is never modified after its initialization).
The disassembler is merely showing you the code generated by the compiler.
(Incidentally, void main(void)
is incorrect; use int main(void)
.)
回答3:
The puts
and printf
functions appear to have the same address because you are looking at stubs, and not the real functions. These stubs load an address from the procedure link table (what the @plt
suffix refers to) and then call it.
I'm disassembling a program, and see that it has stubs for both printf
and 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>
As you can see, the real functions are somewhere else, and these stubs are only generated for the functions that your program actually uses. If you have a program which calls only one function and then change it from printf
to puts
, it's not suprising that the one and only stub is at the same address. The program I just disassembled calls both printf
and puts
, and so has stubs for both, and consequently they have different addresses.
来源:https://stackoverflow.com/questions/39007002/difference-between-printfplt-and-putsplt