How to Solve a Linux Runtime Linker Error

I recently ran into one of those “works in my build directory but breaks everywhere else” moments while working through the First Steps tutorial on the POCO Project site. I had successfully built POCO under:

~/Development/POCO

The shared libraries were sitting in:

~/Development/POCO/lib/Linux/x86_64/lib

Everything compiled fine until I tried running my program outside the POCO lib folder. That’s when Linux hit me with this not-so-friendly message:

error while loading shared libraries: libPocoFoundation.so.6: cannot open shared object file: No such file or directory

What Happening

When I build my app, the compiler happily links against libPocoFoundation.so.6. But that’s only half the story. At runtime, the dynamic linker (ld-linux-x86-64.so.2) needs to find that .so file.

By default, it searches in:

  1. RPATH/RUNPATH baked into the binary
  2. Standard system paths like /lib, /usr/lib
  3. Paths in /etc/ld.so.conf (after running ldconfig)
  4. Anything in LD_LIBRARY_PATH

My binary had no RPATH/RUNPATH, and the POCO library was living in a non-system directory. When I ran it from another folder, ld.so shrugged and said, “never heard of it.”

ldd made it painfully clear:

libPocoFoundation.so.6 => not found

I even tried setting LD_LIBRARY_PATH, but it didn’t help because it wasn’t actually exported in the shell that launched my program.

Minimal Reproducible Example

I recreated the issue with a minimal POCO DateTime sample:

// file: datetime_min.cpp
#include <Poco/DateTime.h>
#include <Poco/DateTimeFormatter.h>
#include <Poco/Timespan.h>
#include <iostream>

int main() {
Poco::DateTime now;
std::string s = Poco::DateTimeFormatter::format(now, "%Y-%m-%d %H:%M:%S");
std::cout << "Now: " << s << "\n";
return 0;
}

Compile it like this:

g++ datetime_min.cpp -o DateTime \
-I ~/Development/POCO/Foundation/include \
-L ~/Development/POCO/lib/Linux/x86_64 \
-lPocoFoundation

Run from ~/Development/POCO/lib/Linux/x86_64 and it works. Run it anywhere else, and boom — runtime linker error.

The Error Define

Error:

error while loading shared libraries: libPocoFoundation.so.6: cannot open shared object file: No such file or directory

Meaning:
The dynamic linker couldn’t locate libPocoFoundation.so.6 in any of its search paths.

How I Fix It

Bake the Path into the Binary

g++ datetime_min.cpp -o DateTime \
-I ~/Development/POCO/Foundation/include \
-L ~/Development/POCO/lib/Linux/x86_64 \
-lPocoFoundation \
-Wl,-rpath,$HOME/Development/POCO/lib/Linux/x86_64 \
-Wl,--enable-new-dtags

Check with:

readelf -d DateTime | grep -E 'RUNPATH|RPATH'

Use a Relative Path with $ORIGIN

Organize project:

bin/DateTime
lib/libPocoFoundation.so.6

Compile:

g++ datetime_min.cpp -o DateTime \
-I ../include -L ../lib -lPocoFoundation \
-Wl,-rpath,'$ORIGIN/../lib' \
-Wl,--enable-new-dtags

System Wide Install

echo "$HOME/Development/POCO/lib/Linux/x86_64" | \
sudo tee /etc/ld.so.conf.d/poco.conf
sudo ldconfig

Quick Hack: LD_LIBRARY_PATH

export LD_LIBRARY_PATH="$HOME/Development/POCO/lib/Linux/x86_64:${LD_LIBRARY_PATH}"
./DateTime

Patch After Build

patchelf --set-rpath '$ORIGIN/../lib' ./DateTime

Adding More Functionality

I decided to turn my tiny DateTime program into something more interactive:

// file: datetime_plus.cpp
#include <Poco/DateTime.h>
#include <Poco/DateTimeFormatter.h>
#include <Poco/Logger.h>
#include <Poco/ConsoleChannel.h>
#include <Poco/FormattingChannel.h>
#include <Poco/PatternFormatter.h>
#include <Poco/Timespan.h>
#include <iostream>

static std::string fmt(const Poco::DateTime& dt) {
return Poco::DateTimeFormatter::format(dt, "%Y-%m-%d %H:%M:%S");
}

int main(int argc, char** argv) {
Poco::AutoPtr<Poco::ConsoleChannel> cc(new Poco::ConsoleChannel);
Poco::AutoPtr<Poco::PatternFormatter> pf(new Poco::PatternFormatter("%Y-%m-%d %H:%M:%S [%p] %t"));
Poco::AutoPtr<Poco::FormattingChannel> fc(new Poco::FormattingChannel(pf, cc));
Poco::Logger& log = Poco::Logger::create("app", fc, Poco::Message::PRIO_INFORMATION);

long offsetMin = (argc >= 2) ? std::stol(argv[1]) : 0;
Poco::DateTime now;
Poco::Timespan delta(0, offsetMin * 60, 0, 0, 0);
Poco::DateTime then = now + delta;

log.information("Now: " + fmt(now));
if (offsetMin != 0) {
log.information("Then: " + fmt(then) + " (offset " + std::to_string(offsetMin) + " min)");
} else {
log.information("Tip: pass minutes offset, e.g. './DateTime 90'");
}
}

Compile with portable rpath:

g++ datetime_plus.cpp -o DateTime \
-I ~/Development/POCO/Foundation/include \
-L ~/Development/POCO/lib/Linux/x86_64 \
-lPocoFoundation \
-Wl,-rpath,'$ORIGIN/../lib' \
-Wl,--enable-new-dtags

Final Thought

This bug taught me that in Linux, compilation and runtime are separate worlds the compiler finds libraries at build time, but the runtime linker needs them at program start. Embedding an RPATH/RUNPATH, using $ORIGIN, setting ldconfig, or exporting LD_LIBRARY_PATH ensures they’re found. Since using $ORIGIN for dev and system-wide config for production, my DateTime program runs anywhere without errors.

Related blog posts