Skip to content

C++ compiling

Published:

C++ is a compiled language, which means that the code you write is translated into machine code before it is executed. While compiling a small program is usually quite painless, things can get complicated when the project grows.

Static and dynamic libraries

When you compile a program, you can link it to libraries. There are two types of libraries: static and dynamic.

Static libraries

A static library is a file that contains compiled code that can be linked to a program at compile time. The code from the library is copied into the program, and the program is then compiled into a single executable file.

To create a static library, you can use the ar command to create an archive of object files.

# Create a static library
ar rcs libmylib.a file1.o file2.o

Find what libraries are linked statically to a binary

nm -D binary

Dynamic libraries

A dynamic library is a file that contains compiled code that can be linked to a program at run time. The code from the library is not copied into the program; instead, the program is linked to the library at run time.

To create a dynamic library, you can use the gcc command with the -shared option.

# Create a dynamic library
gcc -shared -o libmylib.so file1.o file2.o

To check what libraries are linked to a binary, you can use the ldd command.

# Check what libraries are linked to a binary
ldd binary

On Windows, an alternative is to use the mingw-ldd utility, which can be installed via pip. Then, just run the command

mingw-ldd.exe binary --dll-lookup-dirs $env:GUROBI_HOME\bin 'C:\Program Files (x86)\Windows Kits\10\Redist\ucrt\DLLs\x64

where, with the --dll-lookup-dirs you can indicate which folders the software should look into to find the dlls.

Data in Elf files

RPATH

rpath designates the run-time search path hard-coded in an executable file or library. Dynamic linking loaders use the rpath to find required libraries.

# Get rpah of a library
readelf -d bazel-bin/dlinear/libdlinear.so | grep 'R.*PATH'

SONAME

soname is the name of the shared library. It usually considered by other binaries to link against the right version of the shared library at run time.

# Get the SONAME
objdump -p libdlinear.so | grep SONAME

NEEDED

When a binary is linked to any number of shared libraries, they are marked as NEEDED in the binary to ensure they will be loaded at runtime. The NEEDED field is usually taken from the SONAME of the shared library. If the SONAME is not present, the NEEDED field will be the path to the file.

# Get the NEEEDED
objdump -p libdlinear.so | grep NEEDED

Compilation flags

gcc

General flags

  • -c: Compile or assemble the source files, but do not link
  • -o: Place the output into the specified file
  • -L: Add the directory to the list of directories to be searched for libraries
  • -l: Link with the library

Preprocessor flags

  • -I: Add the directory to the list of directories to be searched for header files
  • -Iquote: Add the directory to the list of directories to be searched for header files, but only through #include "file"
    • This is useful when you want to include a header file from the current directory. #include <file> would not work in this case

Linker flags

  • -Wl,-rpath-link=.: Add the current directory to the link-time search path
  • -Wl,-rpath=.: Add the current directory to the runtime search path
  • -Wl,-gc-sections: Remove unused code when statically linking, thus reducing the size of the binary
  • -shared: Create a shared library
  • -fPIC: Generate position-independent code
    • This is required when creating shared libraries

Warning flags

  • -Wall: Enable all warnings
  • -Wextra: Enable extra warnings
  • -Werror: Treat warnings as errors
    • This flag will cause the compiler to stop if any warnings are generated

Optimization flags

  • -O0: Disable optimization
  • -O1: Optimize for size
  • -O2: Optimize for speed
  • -O3: Optimize for speed and size

Debugging flags

  • -g: Generate debugging information

Security flags

  • -U_FORTIFY_SOURCE: Disable the fortify source feature
    • This feature is used to detect some buffer overflow errors when using standard library functions
  • -fstack-protector: Enable stack protection
    • This feature adds a canary value to the stack to detect buffer overflow errors

Dependency flags

  • -MD: Generate dependency information
  • -MMD: Generate dependency information, but do not include system headers
  • -MF <file>: Write dependency information to the specified file

MSVC

General flags

  • /c: Compile without linking
  • /Fe: Specify the name of the output file
  • /link: Link the object files

Add system headers

  • /external:anglebrackets: Treat angle brackets headers as external headers
  • /external:W0: Disable warnings for external headers

Debugging

GDB

MSVC

The debugger utilised by Visual Studio code is decent enough for most debugging tasks. However, if you need more advanced features (e.g., an issue in linking an external library), you can use the Visual Studio debugger. To do so, open the Developer Command Prompt for Visual Studio (View > Terminal) and run the following command:

devenv /DebugExe <path_to_executable>

A new instance of Visual Studio will open, and you can debug the executable as you would in Visual Studio Code.