让 GCC 编译而不插入对 memcpy 的调用

发布于 2024-11-16 15:00:54 字数 546 浏览 7 评论 0原文

我目前正在使用 GCC 4.5.3,为 PowerPC 440 编译,并且正在编译一些不需要 libc 的代码。我没有对 memcpy() 进行任何直接调用,但编译器似乎在构建过程中插入了一个。

有诸如 -nostdlib、-nostartfiles、-nodefaultlibs 之类的链接器选项,但我无法使用它们,因为我没有进行链接阶段。我只是编译。有了这样的内容:

$ powerpc-440-eabi-gcc -O2 -g -c -o output.o input.c

如果我用 nm 检查 output.o,我会看到对 memcpy 的引用:

$ powerpc-440-eabi-nm output.o | grep memcpy
     U memcpy
$ 

GCC 手册页清楚地说明了如何使用链接器删除对 memcpy 的调用和其他 libc 调用,但我不希望编译器首先将它们插入,因为我使用的是完全不同的链接器(不是 GNU 的 ld,而且它不知道 libc)。

感谢您提供的任何帮助。

I'm currently using GCC 4.5.3, compiled for PowerPC 440, and am compiling some code that doesn't require libc. I don't have any direct calls to memcpy(), but the compiler seems to be inserting one during the build.

There are linker options like -nostdlib, -nostartfiles, -nodefaultlibs but I'm unable to use them as I'm not doing the linking phase. I'm only compiling. With something like this:

$ powerpc-440-eabi-gcc -O2 -g -c -o output.o input.c

If I check the output.o with nm, I see a reference to memcpy:

$ powerpc-440-eabi-nm output.o | grep memcpy
     U memcpy
$ 

The GCC man page makes it clear how to remove calls to memcpy and other libc calls with the linker, but I don't want the compiler to insert them in the first place, as I'm using a completely different linker (not GNU's ld, and it doesn't know about libc).

Thanks for any help you can provide.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

夜声 2024-11-23 15:00:54

不需要 -fno-builtins-ffreestand 因为它们会不必要地禁用许多重要的优化

这实际上是由 gcc 的 tree-loop-distribute-patterns“优化”的,因此,要在保留有用的内置功能的同时禁用不需要的行为,您只需使用:

-fno-tree-loop-distribute-patterns

Musl-libc使用此标志进行构建,并在其配置脚本中包含以下注释(我查看了源代码,没有找到任何宏,所以这应该就足够了)

# 检查可能需要阻止编译器的选项
# 生成 memcpy,, memmove, memcmp,
的自引用版本
# 和内存集。确实,我们应该添加一个检查来确定这是否
# 选项就足够了,如果不够,添加一个宏来削弱这些
# 具有易失性的函数...
# tryflag CFLAGS_MEMOPS -fno-tree-loop-distribute-patterns

您还可以使用其优化属性将其作为属性添加到 gcc 中的各个函数中,以便其他函数可以从调用 mem*() 中受益

__attribute__((optimize("no-tree-loop-distribute-patterns")))
size_t strlen(const char *s){ //without attribute, gcc compiles to jmp strlen
  size_t i = -1ull;
  do { ++i; } while (s[i]);
  return i;
}

或者,(至少现在)您可以在循环中添加一个令人困惑的 null asm 语句来阻止模式识别。

size_t strlen(const char *s){
    size_t i = -1ull;
    do {
        ++i;
        asm("");
    } while (s[i]) ;
    return i;
}

There is no need to -fno-builtins or -ffreestanding as they will unnecessarily disable many important optimizations

This is actually "optimized" by gcc's tree-loop-distribute-patterns, so to disable the unwanted behavior while keeping the useful builtin capabilities, you can just use:

-fno-tree-loop-distribute-patterns

Musl-libc uses this flag for its build and has the following note in their configure script (I looked through the source and didn't find any macros, so this should be enough)

# Check for options that may be needed to prevent the compiler from
# generating self-referential versions of memcpy,, memmove, memcmp,
# and memset. Really, we should add a check to determine if this
# option is sufficient, and if not, add a macro to cripple these
# functions with volatile...
# tryflag CFLAGS_MEMOPS -fno-tree-loop-distribute-patterns

You can also add this as an attribute to individual functions in gcc using its optimize attribute, so that other functions can benefit from calling mem*()

__attribute__((optimize("no-tree-loop-distribute-patterns")))
size_t strlen(const char *s){ //without attribute, gcc compiles to jmp strlen
  size_t i = -1ull;
  do { ++i; } while (s[i]);
  return i;
}

Alternatively, (at least for now) you may add a confounding null asm statement into your loop to thwart the pattern recognition.

size_t strlen(const char *s){
    size_t i = -1ull;
    do {
        ++i;
        asm("");
    } while (s[i]) ;
    return i;
}
忆离笙 2024-11-23 15:00:54

在某些情况下,Gcc 会发出对 memcpy 的调用,例如,如果您正在复制结构。
无法更改 GCC 行为,但您可以尝试通过修改代码来避免这种复制。最好的办法是查看程序集,找出 gcc 发出 memcpy 的原因并尝试解决它。但这会很烦人,因为您基本上需要了解 gcc 的工作原理。

摘自http://gcc.gnu.org/onlinedocs/gcc/Standards.html:

GCC 使用的大多数编译器支持例程都存在于 libgcc 中,但也有一些例外。 GCC 要求独立环境提供 memcpy、memmove、memset 和 memcmp。最后,如果使用 __builtin_trap,并且目标未实现陷阱模式,则 GCC 将发出中止调用。

Gcc emits call to memcpy in some circumstance, for example if you are copying a structure.
There is no way to change GCC behaviour but you can try to avoid this by modifying your code to avoid such copy. Best bet is to look at the assembly to figure out why gcc emitted the memcpy and try to work around it. This is going to be annoying though, since you basically need to understand how gcc works.

Extract from http://gcc.gnu.org/onlinedocs/gcc/Standards.html:

Most of the compiler support routines used by GCC are present in libgcc, but there are a few exceptions. GCC requires the freestanding environment provide memcpy, memmove, memset and memcmp. Finally, if __builtin_trap is used, and the target does not implement the trap pattern, then GCC will emit a call to abort.

调妓 2024-11-23 15:00:54

您需要使用 -fno-builtin 禁用该优化。我在尝试为 C 库编译 memcpy 时遇到过这个问题。它自称。哎呀!

You need to disable a that optimization with -fno-builtin. I had this problem once when trying to compile memcpy for a C library. It called itself. Oops!

对风讲故事 2024-11-23 15:00:54

您还可以使您的二进制文件成为“独立”二进制文件:

ISO C 标准(在第 4 条中)定义了两类一致性实现。一致的托管实施支持整个标准[...];仅需要一个符合要求的独立实现来提供某些图书馆设施: 、 、 和 中的设施;自 AMD1 以来,也包括 ;在 C99 中,还有 和 中的那些。 [...]。

该标准还为程序定义了两种环境,一个独立的环境,所有实现都需要,并且可能没有超出独立实现所需的库设施,其中程序启动和终止的处理是实现定义的,并且托管环境,这不是必需的,其中提供了所有库设施,并且通过函数 int main (void) 或 int main (int, char *[]) 启动。

操作系统内核将是一个独立的环境;使用操作系统设施的程序通常是在托管实现中。

(我添加的段落)

更多信息此处。相应的 gcc 选项(关键字 -ffreestand-fno-builtin)可以在 此处

You can also make your binary a "freestanding" one:

The ISO C standard defines (in clause 4) two classes of conforming implementation. A conforming hosted implementation supports the whole standard [...]; a conforming freestanding implementation is only required to provide certain library facilities: those in , , , and ; since AMD1, also those in ; and in C99, also those in and . [...].

The standard also defines two environments for programs, a freestanding environment, required of all implementations and which may not have library facilities beyond those required of freestanding implementations, where the handling of program startup and termination are implementation-defined, and a hosted environment, which is not required, in which all the library facilities are provided and startup is through a function int main (void) or int main (int, char *[]).

An OS kernel would be a freestanding environment; a program using the facilities of an operating system would normally be in a hosted implementation.

(paragraph added by me)

More here. And the corresponding gcc option/s (keywords -ffreestanding or -fno-builtin) can be found here.

凉墨 2024-11-23 15:00:54

这是一个相当老的问题,但我遇到了同样的问题,并且这里的解决方案都不起作用。

所以我定义了这个函数:

static __attribute__((always_inline)) inline void* imemcpy (void *dest, const void *src, size_t len) {
  char *d = dest;
  const char *s = src;
  while (len--)
    *d++ = *s++;
  return dest;
}

然后用它代替memcpy。这已经永久解决了我的内联问题。如果您正在编译某种库,则不是很有用。

This is quite an old question, but I've hit the same issue, and none of the solutions here worked.

So I defined this function:

static __attribute__((always_inline)) inline void* imemcpy (void *dest, const void *src, size_t len) {
  char *d = dest;
  const char *s = src;
  while (len--)
    *d++ = *s++;
  return dest;
}

And then used it instead of memcpy. This has solved the inlining issue for me permanently. Not very useful if you are compiling some sort of library though.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文