当我objdump
我的a.out时,我发现__IO_putc
被使用了。
我知道当putc
的输入很简单时,gcc会使用printf
来替换printf
。但为什么gcc会将putc
替换为__IO_putc
?我可以使用-U_FORTYFY
(禁用__printf_chk
)或--fno-stack-protector
(禁用__stack_chk_fail)等命令行来阻止此替换吗?
答案 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
的调用,而不是putc
。 putc
的显式调用确实已扩展为调用__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运行时库,那么还应该将相应的头文件替换为一个适用于库(以及您选择的编译器)