C _Compilation_Linking - RicoJia/notes GitHub Wiki

========================================================================

Basic Components

========================================================================

Symbol

  1. symbol: During compilation, any non-static global variables? and functions will generate symbols in object files.
    • So linker can decide different modules (shared libs, executables, object files) share the same variables or funcs.
    • e.g, a.o and b.o will both have a symbol for shared_var, and their symbols will contain the same address after linker's resolution
      //A.c
      extern int shared_var;
      //B.c
      int shared_var
    • Some functions/classes in the shared lib can be linked to outside applications.
    • Nice explaination
    • static lib can call a shared lib's function, but that will be linked during linking.
    • fvisibility = hidden doesn't apply to static lib as it has static linkage, and static linkage has public visibility

Libs

  1. types of lib files

    1. .o files can be used as a "library"
    • Others can use it to link against their programs
      gcc -g -o output test.c libtest.o
    1. .so
    • aka dll on windows, .dylib for macOS
    • loads in runtime
    • compile needs -fPIC and -shared flags
    • link needs flag -L , meaning "look in the current directory"
    • -ltest means libtest
      • -lc means libc, where holds printf, malloc, etc
    • See SEARCH_DIR for default libraries
      ld --verbose | grep SEARCH_DIR
      • ld --verbose ld is link editor that generates object files.
      • You will see something like /usr/lib
    • .so has smaller code size
      • Because it's loaded at runtime, the other programs don't need to have this code during compile time
      • So this gives better modularity for bug fixes as well
      • If you don't use .so, then you use .a (static library)
    1. .a
    • static library will be part of your program
    • static library is made by ar (archive), having the files all bundled up
      ar rcs libtest.a littest1.o libtest2.o ...
      • r means replacing other files with the same name in the archive
      • c means to create if it's not there yet
      • s means to generate index for the library
    • linking looks the same as shared lib, you need -ltest
      • But this lib will be stuffed into your binary
      • Then you don't need the library anymore
  2. static lib

    • what happens in linking:

    Linker's job is to search for symbols
    • is just a lib file, archived with a collection of object files (.o), just like a tar/zip file.
    • so they're not linked. linker will still need to link them, but it will be easier for them.
    • So yes, a static lib can work with shared lib, and final linking will be done by making the executable
  3. How to write a shared lib

  4. when you see ‘SomeClass’ declared with greater visibility than the type of its field ‘SomeClass::member’ [-Wattributes]”

    • that means your code (visible to other modules) are using a shared' libs code that's not visible to other modules. 1. Make sure that code is truly visible to other modules 2. if not, try -fvisibility=hidden, the shared_lib must have this flag too.

========================================================================

Code

========================================================================

Storage Specifiers

  1. extern "C"
    1. Summary

      • tells the compiler go outside my scope, the variable is defined somewhere, but the compiler doesn't need to know where it is at the moment.
      • not definition
      • The .o files will be linked together, this happens at that stage.
    2. first make the .o from c, then link it to c++

    3. Uses

    • Canonical Use 1, in a header file
      //h file
      extern int i;     //extern means it's defined somewhere else. 
      // source_1.cpp
      int i = 3; 
      //sournce_2.cpp
      int main(){
          cout <<i; 
        }
    • Canonical Use 2: in two source files, which are linked thru a header file
      // source_1.cpp
      int i = 3; 
      // sournce_2.cpp
      extern i; 
      cout <<i;     
      • This was done in linking.
      • ** If you can't find i, you will get undefined reference.**
      • Multiple definition is not allowed, you can define i only once
    • Edge Use 3: **extern tell the compiler to go outside the current scope. **
      //source.cpp
      int a = 9; 
      {
        extern int a; 
        printf("%d", a);  
      }
      • extends variable visibility, only for declaring, not for defining.
        #include "def.h"    // has int i = 3; in its
        extern int i;     
        int main (){
            i = 1; 
          }
      • Exception is function declared with an initial value
        extern int i = 1;
        int main(){
            i = 3;
          }

Include

  1. Header and multiple definitions

    • a cpp file will become an .o file, which will become a translational unit of itself. To make a final executable, multiple translational units will be linked together.

    • if two cpp files include the same header file, the include guard will prevent each single cpp file from including the the header file twice, but it doesn't do anything for linking.

      • In hfile, void foo() will be linked twice, that's multiple definition error
      • In hfile, inline void foo() will be copied to the two translational units.
      • This is one definition rule: (ODR)
    • class member functions inside the function body, with the declaration, then compiler will see it as inline functions, which is okay. Also, you can declare your enum class with its definition as well.

      // header.hpp
      class Foo(){
          // this is ok
          void foo(){
          }
      }
      
  2. The multiple Definition Error

    1. check include guard
    2. check if your binary and you lib have included the same files together.
    3. For libs that must have an cpp, or cu, it's okay to have function definitions in .hpp files, but this case will introduce "multiple definitions" error.
      // a.hpp
      #ifndef __A__   //include guard will prevent multiple inclusion in a cpp file, but will not protect its functions will be included, and potentially defined in different translational units (e.g, lib src files and user src file)
      #define __A__
      void Foo(){}
      #endif
      //b.hpp
      #include "a.hpp"
    
      //lib.cpp
      #include "b.hpp"
    
      //user.cpp, using lib
      #include "b.cpp"
    1. To solve this issue, you can 1. define hpp functions, variables in a src, or 2. declare a function as static (so the functions defined in header will be only visible to the current transaltional unit)
⚠️ **GitHub.com Fallback** ⚠️