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:
- RPATH/RUNPATH baked into the binary
- Standard system paths like
/lib
,/usr/lib
- Paths in
/etc/ld.so.conf
(after runningldconfig
) - 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.