简体   繁体   English

printf和memcpy链接到标准C库

[英]printf and memcpy linkage to standard C library

It is my understanding that if I call printf in a program, by default (if the program isn't statically compiled) it makes a call to printf in the standard C library. 据我了解,如果我在程序中调用printf ,则默认情况下(如果程序不是静态编译的)会在标准C库中调用printf However, if I were to call say memcpy , I'd hope the code would be inlined, as a function call is very expensive if memcpy is only copying a few bytes. 但是,如果我要说memcpy ,我希望代码可以内联,因为如果memcpy仅复制几个字节,函数调用将非常昂贵。 If you're inlining sometimes and calling out others, the behaviour of your program after a libc upgrade is implementation dependent. 如果您有时内联并调出其他人,则libc升级后程序的行为取决于实现。

What actually occurs in both of these cases and generally? 在这两种情况下以及通常情况下,实际发生了什么?

An implementation is allowed by the C standard to behave "as if" the actual standard library function were called. C标准允许实现以“好像”调用了实际的标准库函数的方式运行。 This is indeed a common optimization: small memcpy calls can be unrolled/inlined, and much more. 这确实是一个常见的优化:小型memcpy调用可以展开/内联,甚至更多。

You're right that in some cases you could upgrade your libc and not see any change in function calls which were optimized out. 没错,在某些情况下,您可以升级libc ,而看不到已优化的函数调用中的任何更改。

You can always attempt to drive the compiler behavior. 您始终可以尝试驱动编译器行为。 For instance, with gcc : 例如,使用gcc

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

You should check the different results with nm or directly the interrupt calls in the assembly source code. 您应该使用nm检查不同的结果,或者直接在汇编源代码中检查中断调用。

First of all the function is never truly "inlined" - that applies to functions that you've written that are visible in the same compilation unit. 首先,该函数永远不会真正地“内联”-适用于您编写的在同一编译单元中可见的函数。

If you're inlining sometimes and calling out others, the behaviour of your program after a libc upgrade is implementation dependent. 如果您有时内联并调出其他人,则libc升级后程序的行为取决于实现。

This is not the case. 不是这种情况。 The memcpy might be "inlined" at compile time . memcpy可能在编译时 “内联”。 Once compiled, your libc version makes no difference. 编译后,您的libc版本没有任何区别。

In GCC, memcpy is recognized as a builtin . 在GCC中, memcpy被认为是内置函数。 That means if GCC decides it, the call to memcpy will be replaced with a suitable implementation. 这意味着,如果GCC做出决定,则对memcpy的调用将替换为合适的实现。 On x86, this will usually be a rep movsb or similar instruction - depending on the size of the copy, and if it is of a constant size or not. 在x86上,这通常是rep movsb或类似的指令-取决于副本的大小,以及大小是否恒定。

It's going to depend on a lot of things, here's how you can find out. 这将取决于很多事情,这是您可以找到的方法。 GNU Binutils comes with a utility objdump that gives all sorts of details on what's in a binary. GNU Binutils带有一个实用程序objdump ,它提供了有关二进制文件中内容的各种详细信息。

On my system (an ARM Chromebook), compiling test.c: 在我的系统(ARM Chromebook)上,编译test.c:

#include <stdio.h>

int main(void) {
  printf("Hello, world!\n");
}

with gcc test.c -o test and then running objdump -R test gives 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

These are the dynamic relocation entries that are in the file, all the stuff that will be linked in from libraries external to the binary. 这些是文件中的动态重定位条目,所有东西都将从二进制外部的库中链接进来。 Here it seems that the printf has been entirely optimized out, since it is only giving a constant string, and thus puts is sufficient. 在这里,似乎printf已被完全优化,因为它只给出一个恒定的字符串,因此puts就足够了。 If we modify this to 如果我们将其修改为

printf("Hello world #%d\n", 1);

then we get the expected 然后我们得到了期望

000105e0 R_ARM_JUMP_SLOT   printf

To get memcpy to be explicitly linked to, we have to prevent gcc from using its own builtin version with -fno-buildin-memcpy . 为了使memcpy与之显式链接,我们必须防止gcc将其内置版本与-fno-buildin-memcpy

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

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