printf和memcpy链接到标准C库
我的理解是,如果我在程序中调用printf
,默认情况下(如果程序没有静态编译),它会在标准C库中调用printf
。但是,如果我打电话给memcpy
,我希望代码将被内联,因为如果memcpy
仅复制几个字节,则函数调用非常昂贵。如果有时内联并调用其他方法,则libc升级后的程序行为取决于实现。printf和memcpy链接到标准C库
实际上在这两种情况下都会发生什么情况?
这将取决于很多事情,这里是你如何找出答案。 GNU Binutils附带一个实用程序objdump
,它提供关于二进制文件中各种细节的各种细节。
在我的系统(一个ARM的Chromebook),编译test.c的:
#include <stdio.h>
int main(void) {
printf("Hello, world!\n");
}
与gcc test.c -o test
,然后运行objdump -R test
给
test: file format elf32-littlearm
DYNAMIC RELOCATION RECORDS
OFFSET TYPE VALUE
000105e4 R_ARM_GLOB_DAT __gmon_start__
000105d4 R_ARM_JUMP_SLOT puts
000105d8 R_ARM_JUMP_SLOT __libc_start_main
000105dc R_ARM_JUMP_SLOT __gmon_start__
000105e0 R_ARM_JUMP_SLOT abort
这些是文件中的动态重定位项,将从二进制文件的外部库中链接到的所有东西。这里看起来printf
已经完全优化了,因为它只给出一个常量字符串,因此puts
就足够了。如果我们修改这
printf("Hello world #%d\n", 1);
那么我们得到预期的
000105e0 R_ARM_JUMP_SLOT printf
要获得memcpy
要明确挂钩,我们要防止gcc
使用它自己的内置版本-fno-buildin-memcpy
。
C标准允许一个实现来表现“好像”实际标准库函数被调用。这确实是一个常见的优化:小的memcpy
调用可以展开/内联,等等。
你是对的,在某些情况下,你可以升级你的libc
,而不会看到任何优化过的函数调用的变化。
您可以随时尝试驱动编译器行为。例如,对于gcc
:
gcc -fno-inline -fno-builtin-inline -fno-inline-functions -fno-builtin...
您应该检查与nm
或直接在汇编源代码的中断调用不同的结果。
首先,函数永远不会真正“内联” - 适用于您编写的功能在同一个编译单元中可见。
如果您有时正在内联并调用其他人,则在升级libc后程序的行为取决于实现。
事实并非如此。 memcpy
可能在编译时间处“内联”。编译完成后,您的libc版本没有任何区别。
在GCC中,memcpy
被识别为builtin。这意味着如果GCC决定它,对memcpy
的呼叫将被替换为合适的实现。在x86上,这通常是rep movsb
或类似的指令 - 取决于副本的大小,以及是否大小不变。