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

问题描述 投票:0回答:5

我目前正在使用 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)。

感谢您提供的任何帮助。

gcc compiler-construction memcpy libc
5个回答
8
投票

无需

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

这实际上是由 gcc 的树循环分布模式“优化”的,因此要在保留有用的内置功能的同时禁用不需要的行为,您可以使用:

-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;
}

7
投票

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

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

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


4
投票

您需要使用

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


3
投票

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

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

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

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

(我添加的段落)

更多这里。相应的 gcc 选项(关键字

-ffreestanding
-fno-builtin
)可以在 here 找到。


1
投票

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

所以我定义了这个函数:

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。这已经永久解决了我的内联问题。如果您正在编译某种库,则不是很有用。

© www.soinside.com 2019 - 2024. All rights reserved.