diff --git a/cpplinker/10_debug_linker/build.sh b/cpplinker/10_debug_linker/build.sh new file mode 100755 index 0000000..4c2b0ca --- /dev/null +++ b/cpplinker/10_debug_linker/build.sh @@ -0,0 +1,107 @@ +#!/bin/bash +set -e + +# Colours +BOLD='\033[1m' +CYAN='\033[1;36m' +GREEN='\033[1;32m' +YELLOW='\033[1;33m' +RESET='\033[0m' + +header() { echo -e "\n${CYAN}══════════════════════════════════════════${RESET}"; \ + echo -e "${BOLD} $1${RESET}"; \ + echo -e "${CYAN}══════════════════════════════════════════${RESET}"; } + +run() { echo -e "${YELLOW}\$ $*${RESET}"; eval "$@"; } + +# ── 0. Clean up any previous artefacts ──────────────────────────────────────── +header "0. Cleaning previous build artefacts" +rm -f *.o *.a *.so app + +# ── 1. Compile to object files ──────────────────────────────────────────────── +header "1. Compile source files to object files" +run g++ -std=c++17 -c math_lib.cpp -o math_lib.o +run g++ -std=c++17 -c main.cpp -o main.o + +# ── 2. Create a static archive ──────────────────────────────────────────────── +header "2. Pack into a static library (archive)" +run ar rcs libmath.a math_lib.o +echo -e "${GREEN}Created libmath.a${RESET}" + +# ── 3. Create a shared library (with an embedded RPATH for ldd / chrpath) ───── +header "3. Build a shared library" +run g++ -std=c++17 -fPIC -c math_lib.cpp -o math_lib_pic.o +run g++ -shared math_lib_pic.o -o libmath.so +echo -e "${GREEN}Created libmath.so${RESET}" + +# ── 4. Link the final executable (dynamic, rpath set to current dir) ────────── +header "4. Link the executable against the shared library" +run g++ main.o -L. -lmath -Wl,-rpath,"\$(pwd)" -o app +echo -e "${GREEN}Created app${RESET}" + +# ══════════════════════════════════════════════════════════════════════════════ +# DEBUG COMMANDS +# ══════════════════════════════════════════════════════════════════════════════ + +# ── 5. nm — inspect symbols ─────────────────────────────────────────────────── +header "5a. nm -C -g libmath.a (demangled global symbols in the archive)" +run nm -C -g libmath.a + +header "5b. nm -u main.o (undefined / unresolved symbols in main.o)" +run nm -u main.o + +# ── 6. objdump — disassembly ────────────────────────────────────────────────── +header "6. objdump -d math_lib.o (disassembly of the library object)" +# Show only the first ~40 lines so the output stays readable +run "objdump -d math_lib.o | head -60" + +# ── 7. readelf — ELF symbol table ──────────────────────────────────────────── +header "7. readelf -s math_lib.o (full ELF symbol table)" +run readelf -s math_lib.o + +# ── 8. Linker verbose — trace linker decisions ──────────────────────────────── +header "8. g++ -Wl,--verbose (trace linker decisions, filtered to 'attempt')" +# Build a fresh app with --verbose so we can grep the attempt lines +run "g++ main.o -L. -lmath -Wl,--verbose -o app_verbose 2>&1 | grep attempt || true" +rm -f app_verbose + +# ── 9. ld --trace ───────────────────────────────────────────────────────────── +header "9. ld --trace (every file the linker considers for math_lib.o)" +# We feed just the object file; ld will error because main() isn't provided, +# but --trace already prints all the files it opens before that. +echo -e "${YELLOW}\$ ld --trace math_lib.o (errors below are expected — main is absent)${RESET}" +ld --trace math_lib.o 2>&1 || true + +# ── 10. ldd — shared library dependencies ───────────────────────────────────── +header "10. ldd ./app (runtime shared-library dependencies)" +run ldd ./app + +# ── 11. chrpath — inspect / show embedded RPATH ────────────────────────────── +header "11. chrpath -l ./app (embedded RPATH recorded in the ELF header)" +if command -v chrpath &>/dev/null; then + run chrpath -l ./app +else + echo "(chrpath not installed — falling back to readelf)" + run "readelf -d ./app | grep -E 'RPATH|RUNPATH'" +fi + +# ── 12. c++filt — demangle a mangled symbol ─────────────────────────────────── +header "12. c++filt — demangle mangled symbol names" + +# Show raw mangled names from the archive first so the demangling is tangible +echo -e "${YELLOW}Raw (mangled) global symbols in libmath.a:${RESET}" +nm -g libmath.a | grep " T " | awk '{print $NF}' + +echo "" +echo -e "${YELLOW}Demangled with c++filt:${RESET}" +nm -g libmath.a | grep " T " | awk '{print $NF}' | c++filt + +echo "" +echo -e "${YELLOW}Specific example from the lesson:${RESET}" +run "c++filt _ZN4Math4sqrtEd" + +# ── 13. Run the program ─────────────────────────────────────────────────────── +header "13. Run ./app" +run ./app + +echo -e "\n${GREEN}${BOLD}Done.${RESET}" diff --git a/cpplinker/10_debug_linker/main.cpp b/cpplinker/10_debug_linker/main.cpp new file mode 100644 index 0000000..87fc090 --- /dev/null +++ b/cpplinker/10_debug_linker/main.cpp @@ -0,0 +1,27 @@ +#include "math_lib.hpp" +#include + +int main() { + // --- Free functions --- + std::cout << "=== Free functions ===\n"; + std::cout << "Math::sqrt(2.0) = " << Math::sqrt(2.0) << "\n"; + std::cout << "Math::square(5.0) = " << Math::square(5.0) << "\n"; + std::cout << "Math::cube(3.0) = " << Math::cube(3.0) << "\n"; + + // --- Vector2D --- + std::cout << "\n=== Vector2D ===\n"; + Math::Vector2D v1(3.0, 4.0); + Math::Vector2D v2(1.0, 2.0); + + std::cout << "v1.length() = " << v1.length() << "\n"; + + Math::Vector2D sum = v1.add(v2); + std::cout << "v1.add(v2) = (" << sum.x << ", " << sum.y << ")\n"; + + Math::Vector2D scaled = v1.scale(2.0); + std::cout << "v1.scale(2.0) = (" << scaled.x << ", " << scaled.y << ")\n"; + + std::cout << "v1.dot(v2) = " << v1.dot(v2) << "\n"; + + return 0; +} \ No newline at end of file diff --git a/cpplinker/10_debug_linker/math_lib.cpp b/cpplinker/10_debug_linker/math_lib.cpp new file mode 100644 index 0000000..d5fc109 --- /dev/null +++ b/cpplinker/10_debug_linker/math_lib.cpp @@ -0,0 +1,40 @@ +#include "math_lib.hpp" +#include + +namespace Math { + +// --- Free functions --- + +double sqrt(double x) { + return std::sqrt(x); +} + +double square(double x) { + return x * x; +} + +double cube(double x) { + return x * x * x; +} + +// --- Vector2D --- + +Vector2D::Vector2D(double x, double y) : x(x), y(y) {} + +double Vector2D::length() const { + return std::sqrt(x * x + y * y); +} + +Vector2D Vector2D::add(const Vector2D& other) const { + return Vector2D(x + other.x, y + other.y); +} + +Vector2D Vector2D::scale(double factor) const { + return Vector2D(x * factor, y * factor); +} + +double Vector2D::dot(const Vector2D& other) const { + return x * other.x + y * other.y; +} + +} // namespace Math \ No newline at end of file diff --git a/cpplinker/10_debug_linker/math_lib.hpp b/cpplinker/10_debug_linker/math_lib.hpp new file mode 100644 index 0000000..454659a --- /dev/null +++ b/cpplinker/10_debug_linker/math_lib.hpp @@ -0,0 +1,26 @@ +#pragma once + +namespace Math { + + // Free functions — each becomes a mangled symbol, e.g. + // Math::sqrt(double) → _ZN4Math4sqrtEd + // Math::square(double) → _ZN4Math6squareEd + // Math::cube(double) → _ZN4Math4cubeEd + double sqrt(double x); + double square(double x); + double cube(double x); + + // Class — member functions add even richer mangling + class Vector2D { + public: + double x, y; + + Vector2D(double x, double y); + + double length() const; + Vector2D add(const Vector2D& other) const; + Vector2D scale(double factor) const; + double dot(const Vector2D& other) const; + }; + +} // namespace Math \ No newline at end of file diff --git a/cpplinker/cpp_linkers.md b/cpplinker/cpp_linkers.md index bdd5edf..b734cc0 100644 --- a/cpplinker/cpp_linkers.md +++ b/cpplinker/cpp_linkers.md @@ -239,21 +239,26 @@ g++ main.cpp wrapper.cpp -lsqlite3 -o app ```bash # Inspect symbols -nm -C -g lib.a # demangled, global symbols only -nm -u main.o # undefined (unresolved) symbols -objdump -d my.o # disassembly -readelf -s my.o # ELF symbol table +nm -C -g libmath.a # demangled, global symbols only +nm -u main.o # undefined (unresolved) symbols +objdump -d math_lib.o # disassembly +readelf -s math_lib.o # ELF symbol table # Trace linker decisions -g++ main.o -lmylib -Wl,--verbose 2>&1 | grep "attempt" -ld --trace my.o # shows each file the linker considers +g++ main.o -L. -lmath -Wl,--verbose 2>&1 | grep "attempt" +ld --trace math_lib.o # shows each file the linker considers # Check shared lib deps ldd ./app -chrpath -l ./app # show embedded RPATH +chrpath -l ./app # show embedded RPATH +# fallback if chrpath is not installed: +readelf -d ./app | grep -E 'RPATH|RUNPATH' # Demangle a mangled symbol -c++filt _ZN4Math4sqrtEd # → Math::sqrt(double) +c++filt _ZN4Math4sqrtEd # → Math::sqrt(double) + +# Pipe nm output through c++filt to demangle all symbols at once +nm -g libmath.a | grep " T " | awk '{print $NF}' | c++filt ``` **Useful flags:**