Home > Net >  Why `strlen` isn't included in elf's symbol table?
Why `strlen` isn't included in elf's symbol table?

Time:12-03

I'm compile 1.c with debug info:

gcc -O0 -g ./1.c
// 1.c
#include <stdio.h>
#include <string.h>
int main() {
  char c[222];
  scanf("%s", c);
  int a = strlen("dadw");
  return 0;
}

But I can't find strlen through readelf -a a.out or nm ./a.out. In my understanding, strlen is provided by libc.so and resolved by dynamic linking. So it should appear in the symbol table. Is it caused by some special tricks of gcc?

❯ nm ./a.out       
000000000000038c r __abi_tag
0000000000004010 B __bss_start
0000000000004010 b completed.0
                 w __cxa_finalize@GLIBC_2.2.5
0000000000004000 D __data_start
0000000000004000 W data_start
00000000000010b0 t deregister_tm_clones
0000000000001120 t __do_global_dtors_aux
0000000000003db8 d __do_global_dtors_aux_fini_array_entry
0000000000004008 D __dso_handle
0000000000003dc0 d _DYNAMIC
0000000000004010 D _edata
0000000000004018 B _end
00000000000011cc T _fini
0000000000001160 t frame_dummy
0000000000003db0 d __frame_dummy_init_array_entry
00000000000020e8 r __FRAME_END__
0000000000003fb0 d _GLOBAL_OFFSET_TABLE_
                 w __gmon_start__
0000000000002008 r __GNU_EH_FRAME_HDR
0000000000001000 T _init
0000000000002000 R _IO_stdin_used
                 U __isoc99_scanf@GLIBC_2.7
                 w _ITM_deregisterTMCloneTable
                 w _ITM_registerTMCloneTable
                 U __libc_start_main@GLIBC_2.34
0000000000001169 T main
00000000000010e0 t register_tm_clones
                 U __stack_chk_fail@GLIBC_2.4
0000000000001080 T _start
0000000000004010 D __TMC_END__

CodePudding user response:

Why strlen isn't present in the symbol table

Because GCC replaces the call to strlen with a constant 4. x86-64 GCC 12.2 with the options -O0 -g produces the following assembly code. Notice the line mov DWORD PTR [rbp-4], 4, which is the constant 4 for the length of the string.

.LC0:
        .string "%s"
main:
        push    rbp
        mov     rbp, rsp
        sub     rsp, 240
        lea     rax, [rbp-240]
        mov     rsi, rax
        mov     edi, OFFSET FLAT:.LC0
        mov     eax, 0
        call    __isoc99_scanf
        mov     DWORD PTR [rbp-4], 4
        mov     eax, 0
        leave
        ret

Try it on godbolt

How to make it actually call strlen

Without replacing the constant string "dadw" with a value read at runtime, one way to make GCC generate the call to strlen is to disable built-in functions by using the -fno-builtins option (link to documentation). According to the documentation:

GCC normally generates special code to handle certain built-in functions more efficiently; [...] the function calls no longer appear as such, [...]

With the options -O0 -g -fno-builtin, x86-64 GCC 12.2 produces the following assembly code, and strlen is present in the symbol table. Notice the line call strlen.

.LC0:
        .string "%s"
.LC1:
        .string "dadw"
main:
        push    rbp
        mov     rbp, rsp
        sub     rsp, 240
        lea     rax, [rbp-240]
        mov     rsi, rax
        mov     edi, OFFSET FLAT:.LC0
        mov     eax, 0
        call    __isoc99_scanf
        mov     edi, OFFSET FLAT:.LC1
        call    strlen
        mov     DWORD PTR [rbp-4], eax
        mov     eax, 0
        leave
        ret
  • Related