Skip to content

Instantly share code, notes, and snippets.

@ald2004
Forked from htfy96/static_inline_example.md
Created July 18, 2022 08:13
Show Gist options
  • Select an option

  • Save ald2004/7c84a1892750ddd4c6a15e22f7ee797f to your computer and use it in GitHub Desktop.

Select an option

Save ald2004/7c84a1892750ddd4c6a15e22f7ee797f to your computer and use it in GitHub Desktop.

Revisions

  1. @htfy96 htfy96 revised this gist Sep 2, 2017. 1 changed file with 32 additions and 1 deletion.
    33 changes: 32 additions & 1 deletion static_inline_example.md
    Original file line number Diff line number Diff line change
    @@ -41,7 +41,9 @@ int main()
    ## Compiled binaries
    ### a.o
    ```bash
    readelf -s a.o | c++filt -t
    readelf -sW a.o | c++filt -t
    # -s: symbol table
    # -W: display in wide format
    ```

    ```cpp
    @@ -172,6 +174,35 @@ Symbol table '.symtab' contains 71 entries:
    - `static` marks the symbol `LOCAL`, which restricts the symbol in current translation unit, and the linker may keep *multiple* instances of (possibly different) definition.
    - `static inline` generally works as `static`, but the `inline` keyword suggest compiler trying to inline this function.
    ## Extra credits
    In C++11 it is recommended to use function in anonymous namespace over `static` functions.
    What if we add the following code into `header.hpp`:
    ```cpp
    namespace {
    int anon_namespace() { return 42; }
    }
    ```

    ### a.o
    ```
    8: 0000000000000016 11 FUNC LOCAL DEFAULT 2 _ZN12_GLOBAL__N_114anon_namespaceEv # (anonymous namespace)::anon_namespace()
    ```

    ### b.o
    ```
    8: 0000000000000016 11 FUNC LOCAL DEFAULT 2 _ZN12_GLOBAL__N_114anon_namespaceEv # (anonymous namespace)::anon_namespace()
    ```

    ### a.out
    ```
    38: 0000000000000670 11 FUNC LOCAL DEFAULT 13 (anonymous namespace)::anon_namespace()
    42: 00000000000006c8 11 FUNC LOCAL DEFAULT 13 (anonymous namespace)::anon_namespace()
    ```

    You may notice that this is exactly the same as `static`, except for its mangled name. So why the commitee introduced anonymous namespace? Read https://stackoverflow.com/questions/4977252/why-an-unnamed-namespace-is-a-superior-alternative-to-static for explanation.


    ## Read more
    - https://stackoverflow.com/questions/10876930/should-one-never-use-static-inline-function
    - https://stackoverflow.com/questions/12836171/difference-between-an-inline-function-and-static-inline-function
  2. @htfy96 htfy96 revised this gist Sep 2, 2017. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion static_inline_example.md
    Original file line number Diff line number Diff line change
    @@ -1,7 +1,7 @@
    In this article we compared different behavior of `static`, `inline` and `static inline` free functions in compiled binary.
    All the following test was done under g++ 7.1.1 on Linux amd64, ELF64.

    ## Test source
    ## Test sources
    ### header.hpp
    ```cpp
    #pragma once
  3. @htfy96 htfy96 created this gist Sep 2, 2017.
    178 changes: 178 additions & 0 deletions static_inline_example.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,178 @@
    In this article we compared different behavior of `static`, `inline` and `static inline` free functions in compiled binary.
    All the following test was done under g++ 7.1.1 on Linux amd64, ELF64.

    ## Test source
    ### header.hpp
    ```cpp
    #pragma once

    inline int only_inline() { return 42; }
    static int only_static() { return 42; }
    static inline int static_inline() { return 42; }
    ```
    ### a.cpp
    ```cpp
    #include "header.hpp"
    int a()
    {
    return static_inline() + only_inline() + only_static();
    }
    ```

    ### b.cpp
    ```cpp
    #include "header.hpp"

    int a();

    int b()
    {
    return static_inline() + only_inline() + only_static();
    }

    int main()
    {
    return a() + b();
    }
    ```

    ## Compiled binaries
    ### a.o
    ```bash
    readelf -s a.o | c++filt -t
    ```

    ```cpp
    Symbol table '.symtab' contains 14 entries:
    Num: Value Size Type Bind Vis Ndx Name
    0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
    1: 0000000000000000 0 FILE LOCAL DEFAULT ABS a.cpp
    2: 0000000000000000 0 SECTION LOCAL DEFAULT 2
    3: 0000000000000000 0 SECTION LOCAL DEFAULT 4
    4: 0000000000000000 0 SECTION LOCAL DEFAULT 5
    5: 0000000000000000 0 SECTION LOCAL DEFAULT 6
    6: 0000000000000000 11 FUNC LOCAL DEFAULT 2 only_static() # local
    7: 000000000000000b 11 FUNC LOCAL DEFAULT 2 static_inline() # local
    8: 0000000000000000 0 SECTION LOCAL DEFAULT 8
    9: 0000000000000000 0 SECTION LOCAL DEFAULT 9
    10: 0000000000000000 0 SECTION LOCAL DEFAULT 7
    11: 0000000000000000 0 SECTION LOCAL DEFAULT 1
    12: 0000000000000000 11 FUNC WEAK DEFAULT 6 only_inline() # weak
    13: 0000000000000016 37 FUNC GLOBAL DEFAULT 2 a()
    ```

    ### b.o
    ```
    Symbol table '.symtab' contains 17 entries:
    Num: Value Size Type Bind Vis Ndx Name
    0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
    1: 0000000000000000 0 FILE LOCAL DEFAULT ABS b.cpp
    2: 0000000000000000 0 SECTION LOCAL DEFAULT 2
    3: 0000000000000000 0 SECTION LOCAL DEFAULT 4
    4: 0000000000000000 0 SECTION LOCAL DEFAULT 5
    5: 0000000000000000 0 SECTION LOCAL DEFAULT 6
    6: 0000000000000000 11 FUNC LOCAL DEFAULT 2 only_static() # LOCAL
    7: 000000000000000b 11 FUNC LOCAL DEFAULT 2 static_inline() # LOCAL
    8: 0000000000000000 0 SECTION LOCAL DEFAULT 8
    9: 0000000000000000 0 SECTION LOCAL DEFAULT 9
    10: 0000000000000000 0 SECTION LOCAL DEFAULT 7
    11: 0000000000000000 0 SECTION LOCAL DEFAULT 1
    12: 0000000000000000 11 FUNC WEAK DEFAULT 6 only_inline() # WEAK
    13: 0000000000000016 37 FUNC GLOBAL DEFAULT 2 b()
    14: 000000000000003b 30 FUNC GLOBAL DEFAULT 2 main
    15: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND _GLOBAL_OFFSET_TABLE_
    16: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND a()
    ```
    ### a.out
    As in compiled executable, every copies of symbol marked `LOCAL` will be preserved,
    while only one instance of `WEAK` symbols will only be preserved.
    ```
    Symbol table '.symtab' contains 71 entries:
    Num: Value Size Type Bind Vis Ndx Name
    0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
    1: 0000000000000238 0 SECTION LOCAL DEFAULT 1
    2: 0000000000000254 0 SECTION LOCAL DEFAULT 2
    3: 0000000000000274 0 SECTION LOCAL DEFAULT 3
    4: 0000000000000298 0 SECTION LOCAL DEFAULT 4
    5: c0 0 SECTION LOCAL DEFAULT 5
    6: 0000000000000368 0 SECTION LOCAL DEFAULT 6
    7: 0000000000000412 0 SECTION LOCAL DEFAULT 7
    8: 0000000000000420 0 SECTION LOCAL DEFAULT 8
    9: 0000000000000440 0 SECTION LOCAL DEFAULT 9
    10: 0000000000000518 0 SECTION LOCAL DEFAULT 10
    11: 0000000000000530 0 SECTION LOCAL DEFAULT 11
    12: 0000000000000540 0 SECTION LOCAL DEFAULT 12
    13: 0000000000000550 0 SECTION LOCAL DEFAULT 13
    14: 0000000000000774 0 SECTION LOCAL DEFAULT 14
    15: 0000000000000780 0 SECTION LOCAL DEFAULT 15
    16: 0000000000000784 0 SECTION LOCAL DEFAULT 16
    17: 00000000000007f8 0 SECTION LOCAL DEFAULT 17
    18: 0000000000200de0 0 SECTION LOCAL DEFAULT 18
    19: 0000000000200de8 0 SECTION LOCAL DEFAULT 19
    20: 0000000000200df0 0 SECTION LOCAL DEFAULT 20
    21: 0000000000200fd0 0 SECTION LOCAL DEFAULT 21
    22: 0000000000201000 0 SECTION LOCAL DEFAULT 22
    23: 0000000000201018 0 SECTION LOCAL DEFAULT 23
    24: 0000000000201028 0 SECTION LOCAL DEFAULT 24
    25: 0000000000000000 0 SECTION LOCAL DEFAULT 25
    26: 0000000000000000 0 FILE LOCAL DEFAULT ABS init.c
    27: 0000000000000000 0 FILE LOCAL DEFAULT ABS crtstuff.c
    28: 0000000000000580 0 FUNC LOCAL DEFAULT 13 deregister_tm_clones
    29: 00000000000005c0 0 FUNC LOCAL DEFAULT 13 register_tm_clones
    30: 0000000000000610 0 FUNC LOCAL DEFAULT 13 __do_global_dtors_aux
    31: 0000000000201028 1 OBJECT LOCAL DEFAULT 24 completed.6991
    32: 0000000000200de8 0 OBJECT LOCAL DEFAULT 19 __do_global_dtors_aux_fin
    33: 0000000000000650 0 FUNC LOCAL DEFAULT 13 frame_dummy
    34: 0000000000200de0 0 OBJECT LOCAL DEFAULT 18 __frame_dummy_init_array_
    35: 0000000000000000 0 FILE LOCAL DEFAULT ABS a.cpp
    36: 000000000000065a 11 FUNC LOCAL DEFAULT 13 only_static() # COPY 1
    37: 0000000000000665 11 FUNC LOCAL DEFAULT 13 static_inline() # COPY 1
    38: 0000000000000000 0 FILE LOCAL DEFAULT ABS b.cpp
    39: 00000000000006a0 11 FUNC LOCAL DEFAULT 13 only_static() # COPY 2
    40: 00000000000006ab 11 FUNC LOCAL DEFAULT 13 static_inline() # COPY 2
    41: 0000000000000000 0 FILE LOCAL DEFAULT ABS crtstuff.c
    42: 00000000000009e0 0 OBJECT LOCAL DEFAULT 17 __FRAME_END__
    43: 0000000000000000 0 FILE LOCAL DEFAULT ABS
    44: 0000000000000784 0 NOTYPE LOCAL DEFAULT 16 __GNU_EH_FRAME_HDR
    45: 0000000000201000 0 OBJECT LOCAL DEFAULT 22 _GLOBAL_OFFSET_TABLE_
    46: 0000000000200de8 0 NOTYPE LOCAL DEFAULT 18 __init_array_end
    47: 0000000000200de0 0 NOTYPE LOCAL DEFAULT 18 __init_array_start
    48: 0000000000200df0 0 OBJECT LOCAL DEFAULT 20 _DYNAMIC
    49: 0000000000201018 0 NOTYPE WEAK DEFAULT 23 data_start
    50: 0000000000000770 2 FUNC GLOBAL DEFAULT 13 __libc_csu_fini
    51: 0000000000000550 43 FUNC GLOBAL DEFAULT 13 _start
    52: 0000000000000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__
    53: 0000000000000670 37 FUNC GLOBAL DEFAULT 13 a()
    54: 0000000000000774 0 FUNC GLOBAL DEFAULT 14 _fini
    55: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@@GLIBC_
    56: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_deregisterTMCloneTab
    57: 0000000000000780 4 OBJECT GLOBAL DEFAULT 15 _IO_stdin_used
    58: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_registerTMCloneTable
    59: 0000000000201018 0 NOTYPE GLOBAL DEFAULT 23 __data_start
    60: 0000000000000000 0 FUNC WEAK DEFAULT UND __cxa_finalize@@GLIBC_2.2
    61: 0000000000201028 0 OBJECT GLOBAL HIDDEN 23 __TMC_END__
    62: 0000000000201020 0 OBJECT GLOBAL HIDDEN 23 __dso_handle
    63: 0000000000000700 101 FUNC GLOBAL DEFAULT 13 __libc_csu_init
    64: 0000000000201028 0 NOTYPE GLOBAL DEFAULT 24 __bss_start
    65: 0000000000000695 11 FUNC WEAK DEFAULT 13 only_inline() # only one
    66: 0000000000201030 0 NOTYPE GLOBAL DEFAULT 24 _end
    67: 00000000000006b6 37 FUNC GLOBAL DEFAULT 13 b()
    68: 0000000000201028 0 NOTYPE GLOBAL DEFAULT 23 _edata
    69: 00000000000006db 30 FUNC GLOBAL DEFAULT 13 main
    70: 0000000000000518 0 FUNC GLOBAL DEFAULT 10 _init
    ```
    ## Conclusion
    - `inline` marks the symbol `WEAK`, which hints linker to choose arbitary *one* of definition in object files
    - `static` marks the symbol `LOCAL`, which restricts the symbol in current translation unit, and the linker may keep *multiple* instances of (possibly different) definition.
    - `static inline` generally works as `static`, but the `inline` keyword suggest compiler trying to inline this function.
    ## Read more
    - https://stackoverflow.com/questions/10876930/should-one-never-use-static-inline-function
    - https://stackoverflow.com/questions/12836171/difference-between-an-inline-function-and-static-inline-function
    - https://stackoverflow.com/questions/22102919/static-vs-inline-for-functions-implemented-in-header-files