如何防止生成__IO_putc?

时间:2015-11-22 16:31:07

标签: c++ c linux glibc

当我objdump我的a.out时,我发现__IO_putc被使用了。

我知道当putc的输入很简单时,gcc会使用printf来替换printf。但为什么gcc会将putc替换为__IO_putc?我可以使用-U_FORTYFY(禁用__printf_chk)或--fno-stack-protector(禁用__stack_chk_fail)等命令行来阻止此替换吗?

4 个答案:

答案 0 :(得分:4)

  

为什么gcc会将putc替换为__IO_putc

/usr/include/stdio.h中,您会看到以下这一行:

#define putc(_ch, _fp) _IO_putc (_ch, _fp)
  

我可以使用某些命令行阻止此替换

您可以在#undef putc之后将#include <stdio.h>放入您的来源,或将函数名称括在每个调用网站的括号中,如下所示:

(putc)('a', stdout);

一般来说,你应该混淆像这样的标准功能。

答案 1 :(得分:2)

您可以通过括起名称来避免扩展类似函数的宏:

(putc)('c', outfile);

这是C标准特别允许的,§7.1.4(“使用库函数”)(强调添加):

  

标头中声明的任何函数可以另外实现为标头中定义的类函数宏,因此如果在包含标头时显式声明了库函数,则可以使用下面显示的技术之一来确保声明不受这种宏的影响。 可以通过封闭在本地抑制函数的任何宏定义   括号中的函数名称,因为该名称后面没有左括号,表示宏函数名称的扩展。

在与gcc捆绑在一起的标准库实现的特定情况下,您可能会发现使用fputc代替putc会导致实际调用fputc。 (该标准要求fputc的基本功能与putc相同;它允许putc的宏实现 - 如果有的话 - 不止一次地评估第二个参数。)

对于它的价值,我使用gcc 5.2.0进行了检查,看来fprintf被转换为对fputc的调用,而不是putcputc的显式调用确实已扩展为调用__IO_putc,但如上所述可以避免这种情况。

示例程序:

#include <stdio.h>
int main() {
  FILE* outfile = stdout;
  fprintf(outfile, "c");
  putc('c', outfile);
  (putc)('c', outfile);
  return 0;
}

生成的代码(已添加评论):

main:
    pushq   %rbx
    movq    stdout(%rip), %rbx
    movl    $99, %edi
    movq    %rbx, %rsi
    call    fputc          # fprintf(outfile, "c")
    movq    %rbx, %rsi
    movl    $99, %edi
    call    _IO_putc       # putc('c', outfile)
    movq    %rbx, %rsi
    movl    $99, %edi
    call    putc           # (putc)('c', outfile);
    xorl    %eax, %eax
    popq    %rbx
    ret

答案 2 :(得分:2)

由于putc中宏的扩展,

__IO_putc在预处理阶段被翻译为<stdio.h>。您确实可以尝试在fputc之后使用#undef或黑客攻击头文件或putc #include <stdio.h>来阻止这种情况,但这不是正确的解决方法,您只是幸运的是,如果它有效,稍后会出现更多问题,因为你依赖更多的库功能。

gcc配置为针对给定目标进行编译,其中包含配置选项和一组头文件和库的组合。重要的是要理解头文件本质上链接到每个特定版本的相应库。您不能只使用泛型 <stdio.h>头文件进行编译,并期望与另一个目标的C库链接。交叉编译需要正确配置完整的工具链。如果您要链接到的C库的特定版本,请使用随附的头文件。

OTOH,如果您只是希望源文件使用您的标准I / O功能版本,那么首先阻止扩展和与您的库链接可能会完成这项工作。另一种方法是重新定义putc和您想要替换的所有其他函数,替换所有对替换调用的调用,但是您仍然在使用标准库标识符,因此任何事情都可能发生。

答案 3 :(得分:0)

与我有点不同的方法:

如果要用自己的库版本替换标准库函数。您可能想要编写自己的版本&#34; stdio.h&#34;,而不是使用编译器附带的版本。或者,如果您使用的是编译器提供的那个,您可能希望实现它实际使用的低级IO函数,而不是替换更高级别的函数(换句话说,找出tomcat: image: tomcat:8.0 ports: - "8080:8080" volumes: - base-0.2.war:/usr/local/tomcat/webapps/base.war links: - postgres postgres: image: clegge/postgres ports: - "5432:5432" environment: - DB_USER_NAME=test_crud - DB_PASSWORD=test_crud - DB_NAME=base_db 实际上,并替换任何东西,等等。)

To&#34; mess&#34;通过在代码中执行__IO_putc或类似的标准标头内容可能会在其他地方插入新问题,并且如果您想要导入其他人的大型项目代码,则很可能会导致问题。相反,如果您拥有自己的&#34; stdio.h&#34;版本,那么首先不会执行#undef,那么您就没有问题需要解决之后 - 只要您完成外部产品所需的一切,它就会起作用。

重要的是要了解&#34; stdio.h&#34;并且它的朋友与某个C运行时库(在大多数情况下与某个编译器结合使用)配对,因此如果要替换C运行时库,那么还应该将相应的头文件替换为一个适用于库(以及您选择的编译器)