如果我有以下内容strlen.c:
int call_strlen(char *s) {
return __builtin_strlen(s);
}
Run Code Online (Sandbox Code Playgroud)
然后像这样用gcc和clang编译它:
gcc -c -o strlen-gcc.o strlen.c
clang -c -o strlen-clang.o strlen.c
Run Code Online (Sandbox Code Playgroud)
我很惊讶地看到strlen-clang.o包含对"strlen"的引用,而gcc预计会内联函数并且没有这样的引用.(见下面的objdumps).这是clang中的错误吗?我已经在clang编译器的几个版本中测试了它,包括3.8.
编辑:这对我来说很重要的原因是我正在链接-nostdlib,并且clang编译的版本给我一个链接错误,找不到strlen.
铛
@> objdump -d strlen-clang.o
strlen-clang.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <call_strlen>:
0: 55 push %rbp
1: 48 89 e5 mov %rsp,%rbp
4: 48 83 ec 10 sub $0x10,%rsp
8: 48 89 7d f8 mov %rdi,-0x8(%rbp)
c: 48 8b 7d f8 mov -0x8(%rbp),%rdi
10: e8 00 00 00 00 callq 15 <call_strlen+0x15>
15: 89 c1 mov %eax,%ecx
17: 89 c8 mov %ecx,%eax
19: 48 83 c4 10 add $0x10,%rsp
1d: 5d pop %rbp
1e: c3 retq
@> objdump -t strlen-clang.o
strlen-clang.o: file format elf64-x86-64
SYMBOL TABLE:
0000000000000000 l df *ABS* 0000000000000000 strlen.c
0000000000000000 l d .text 0000000000000000 .text
0000000000000000 l d .data 0000000000000000 .data
0000000000000000 l d .bss 0000000000000000 .bss
0000000000000000 l d .comment 0000000000000000 .comment
0000000000000000 l d .note.GNU-stack 0000000000000000 .note.GNU-stack
0000000000000000 l d .eh_frame 0000000000000000 .eh_frame
0000000000000000 g F .text 000000000000001f call_strlen
0000000000000000 *UND* 0000000000000000 strlen
Run Code Online (Sandbox Code Playgroud)
GCC
@> objdump -d strlen-gcc.o
strlen-gcc.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <call_strlen>:
0: 55 push %rbp
1: 48 89 e5 mov %rsp,%rbp
4: 48 89 7d f8 mov %rdi,-0x8(%rbp)
8: 48 8b 45 f8 mov -0x8(%rbp),%rax
c: 48 c7 c1 ff ff ff ff mov $0xffffffffffffffff,%rcx
13: 48 89 c2 mov %rax,%rdx
16: b8 00 00 00 00 mov $0x0,%eax
1b: 48 89 d7 mov %rdx,%rdi
1e: f2 ae repnz scas %es:(%rdi),%al
20: 48 89 c8 mov %rcx,%rax
23: 48 f7 d0 not %rax
26: 48 83 e8 01 sub $0x1,%rax
2a: 5d pop %rbp
2b: c3 retq
@> objdump -t strlen-gcc.o
strlen-gcc.o: file format elf64-x86-64
SYMBOL TABLE:
0000000000000000 l df *ABS* 0000000000000000 strlen.c
0000000000000000 l d .text 0000000000000000 .text
0000000000000000 l d .data 0000000000000000 .data
0000000000000000 l d .bss 0000000000000000 .bss
0000000000000000 l d .note.GNU-stack 0000000000000000 .note.GNU-stack
0000000000000000 l d .eh_frame 0000000000000000 .eh_frame
0000000000000000 l d .comment 0000000000000000 .comment
0000000000000000 g F .text 000000000000002c call_strlen
Run Code Online (Sandbox Code Playgroud)
只是为了避免优化:
与clang -O0:
t.o:
(__TEXT,__text) section
_call_strlen:
0000000000000000 pushq %rbp
0000000000000001 movq %rsp, %rbp
0000000000000004 subq $0x10, %rsp
0000000000000008 movq %rdi, -0x8(%rbp)
000000000000000c movq -0x8(%rbp), %rdi
0000000000000010 callq _strlen
0000000000000015 movl %eax, %ecx
0000000000000017 movl %ecx, %eax
0000000000000019 addq $0x10, %rsp
000000000000001d popq %rbp
000000000000001e retq
Run Code Online (Sandbox Code Playgroud)
用 clang -O3
t.o:
(__TEXT,__text) section
_call_strlen:
0000000000000000 pushq %rbp
0000000000000001 movq %rsp, %rbp
0000000000000004 popq %rbp
0000000000000005 jmp _strlen
Run Code Online (Sandbox Code Playgroud)
现在,解决问题:
clang文档声称clang支持所有GCC支持的内置程序。
但是,GCC文档似乎将内置函数及其库等效项的名称视为同义词:
两种形式都具有与C库函数相同的类型(包括原型),相同的地址(采用地址时)和相同的含义。
此外,它也不保证具有等效库的内置函数(如的情况strlen)确实可以得到优化:
其中许多功能仅在某些情况下进行了优化。如果在特定情况下未对它们进行优化,则会发出对库函数的调用。
此外,clang内部手册__builtin_strlen仅提及一次:
__builtin_strlen和strlen:如果参数为字符串文字,则将它们折叠为整数常量表达式。
除此之外,他们似乎没有任何承诺。
由于在您的情况下,to的参数__builtin_strlen不是字符串文字,并且由于GCC文档允许将对内置函数的调用转换为库函数调用,因此clang的行为似乎是完全有效的。
一个铛开发人员邮件列表上的“补丁审查”也说:
[...]如果无法/不需要编译时评估,它仍然可以在运行时使用strlen库。
那是在2012年,但文字表明至少在那时,仅支持编译时评估。
现在,我看到两个选择:
我强烈建议不要滚动自己的标准库函数实现,即使在看似简单的情况下(如果您不同意,请尝试编写自己的strlen实现,然后将其与glibc比较)。
| 归档时间: |
|
| 查看次数: |
1237 次 |
| 最近记录: |