2014-10-30 51 views
1

我的理解是,如果我在程序中调用printf,默认情况下(如果程序没有静态编译),它会在标准C库中调用printf。但是,如果我打电话给memcpy,我希望代码将被内联,因为如果memcpy仅复制几个字节,则函数调用非常昂贵。如果有时内联并调用其他方法,则libc升级后的程序行为取决于实现。printf和memcpy链接到标准C库

实际上在这两种情况下都会发生什么情况?

回答

1

这将取决于很多事情,这里是你如何找出答案。 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

2

C标准允许一个实现来表现“好像”实际标准库函数被调用。这确实是一个常见的优化:小的memcpy调用可以展开/内联,等等。

你是对的,在某些情况下,你可以升级你的libc,而不会看到任何优化过的函数调用的变化。

1

您可以随时尝试驱动编译器行为。例如,对于gcc

gcc -fno-inline -fno-builtin-inline -fno-inline-functions -fno-builtin... 

您应该检查与nm或直接在汇编源代码的中断调用不同的结果。

1

首先,函数永远不会真正“内联” - 适用于您编写的功能在同一个编译单元中可见。

如果您有时正在内联并调用其他人,则在升级libc后程序的行为取决于实现。

事实并非如此。 memcpy可能在编译时间处“内联”。编译完成后,您的libc版本没有任何区别。

在GCC中,memcpy被识别为builtin。这意味着如果GCC决定它,对memcpy的呼叫将被替换为合适的实现。在x86上,这通常是rep movsb或类似的指令 - 取决于副本的大小,以及是否大小不变。