Search code examples
c++gccclanglinker-errorsns-3

Compiling ns3 using clang results in undefined symbol linker error


I've downloaded ns-3.39, unpacked and compiled it using

wget https://www.nsnam.org/releases/ns-allinone-3.39.tar.bz2
tar xfj ns-allinone-3.39.tar.bz2
cd ns-allinone-3.39/ns-3.39/
./ns3 configure --disable-examples --disable-tests -d default
./ns3 build

this works fine.

When I add an #include "ns3/ipv4-header.h" to ns-3.39/src/wifi/model/phy-entity.cc and try to use the Ipv4Header class from that header by instantiating an Ipv4Header ipHeader; inside phy-entity.cc:528 in its DropPreambleEvent function, I get the following compilation error:

ld: Undefined symbols:
  ns3::Ipv4Header::Ipv4Header(), referenced from:
      ns3::PhyEntity::DropPreambleEvent(ns3::Ptr<ns3::WifiPpdu const>, ns3::WifiPhyRxfailureReason, ns3::Time) in phy-entity.cc.o
clang: error: linker command failed with exit code 1 (use -v to see invocation)

I am compiling on a MacBook M2 Pro with clang targeted for ARM. When I do the exact same thing on a x86_64 Linux server with gcc, it works.
I would really like to get this working so that I can work locally. I've sunk some time into this, and found the following

  • since the unmodified ns3 simulator compiles on the Mac, it is not the ARM platform as such that causes problems
  • ns3 compiles a bunch of standalone dynamic libraries
    • my best guess right now is that the Ipv4Header definition is part of a different library than the PhyEntity and so it cannot call the constructor
    • probably gcc allows such undefined symbols in dynamic libraries, which should indeed be fine as long as the final executable links all symbols in, right?
    • I've tried to configure clang to allow the same by following this post
      • i.e. I've set the environment variable OTHER_LDFLAGS to -undefined dynamic_lookup and recompiled, but to no avail

I am out of ideas. What could I try?


Solution

  • I've got it running. For those that come after me that have similar troubles:

    I've learned that the ns3 core is structured module-wise (forgive me if you know this already, I am learning as I go), so there is no direct link between the wifi module and the internet module. Modules are compiled into dynamic libraries, so that you can pick and choose what you need. Makes total sense to me from a software architecture standpoint.

    In my project we've been changing the wifi module code (as stated in the original post), by including stuff such as the Ipv4Header that comes from the internet module.
    To make this work when compiling with clang/arm, you need to explicitly link the internet library with the wifi library by adding 1 line to the src/wifi/CMakeLists.txt, under build_lib(...) you must add

    build_lib(
        LIBNAME wifi
        ...
        LIBRARIES_TO_LINK
        ...
        ${libinternet}
        ...
    )
    

    This might be bad coding style as we break the isolation between the libraries; we'll accept it for the task at hand.

    What I still don't understand is why gcc/x86_64 on a Linux system DOES NOT REQUIRE THIS. Somehow it determines to link in this library on its own? If anyone could explain to me how/why this works, I'd love to hear it.