Integrate OpenCV using cmake - cannot open shared object file - c++

I am trying to integrate OpenCV (v4.1.2) into an existing project and I made below changes for that:
CMakeLists.txt
find_package(OpenCV REQUIRED core imgcodecs imgproc PATHS <path to all opencv shared object files>)
target_link_libraries(<exec name> ${OpenCV_LIBS})
Also added below to the config file:
#cmakedefine HAVE_LIBOPENCV 1
#cmakedefine HAVE_OPENCV_H 1
Copy the output executable at:
/usr/bin
Run the below command to set rpath:
patchelf --set-rpath path-to-opencv/opencv/lib:$ORIGIN /usr/bin/executablename
Below are the changes I added in the source code:
#include <opencv2/core.hpp>
#include <opencv2/imgcodecs.hpp>
cv::Mat img = cv::imread("sample.jpg", ZM_MAX_IMAGE_COLOURS);
cv::imwrite("sample-opencv.jpg", img);
However, when I run the the code, I see below message in logs:
error while loading shared libraries: libopencv_imgproc.so.4.1: cannot open shared object file: No such file or directory
Below are some sanity checks I performed on the executable file:
$readelf -d <executable name>
0x0000000000000001 (NEEDED) Shared library: [libopencv_imgcodecs.so.4.1]
0x0000000000000001 (NEEDED) Shared library: [libopencv_core.so.4.1]
0x000000000000001d (RUNPATH) Library runpath: [<path to opencv>/opencv/lib:]
$ ldd <executable name>
libopencv_imgcodecs.so.4.1 => <path to opencv>/opencv/lib/libopencv_imgcodecs.so.4.1 (0x00007f49cf6f9000)
libopencv_core.so.4.1 => <path to opencv>/opencv/lib/libopencv_core.so.4.1 (0x00007f49ce4fc000)
libopencv_imgproc.so.4.1 => <path to opencv>/opencv/lib/libopencv_imgproc.so.4.1 (0x00007f49c3b12000)
Even though there is a valid link in ldd output, libopencv_imgproc.so doesn't exist in readelf output. My assumption is that since there is no source code directly consuming imgproc, it doesn't show up in readelf output.
Can someone point out what is causing the "cannot open shared object file" error?
EDIT:
I changed my above code to include imgproc header and declare a class below as shown below:
#include <opencv2/core.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/imgproc.hpp>
cv::Subdiv2D div; // comes from imgproc
cv::Mat img = cv::imread("sample.jpg", ZM_MAX_IMAGE_COLOURS);
cv::imwrite("sample-opencv.jpg", img);
It worked after this change. Also, below is the new readelf output:
$ readelf -d <executable name>
0x0000000000000001 (NEEDED) Shared library: [libopencv_imgcodecs.so.4.1]
0x0000000000000001 (NEEDED) Shared library: [libopencv_imgproc.so.4.1]
0x0000000000000001 (NEEDED) Shared library: [libopencv_core.so.4.1]
0x000000000000001d (RUNPATH) Library runpath: [<path to opencv>/opencv/lib:]
However, it is just a workaround. One shouldn't need to write a placeholder code like this. Can someone help figure out a clean solution to include shared objects correctly?
FYI - Here are the internal dependecies between these opencv shared objects:
libopencv_core --> no dependency
libopencv_imgproc --> libopencv_core
libopencv_imcodecs --> libopencv_core and libopencv_imgproc

Related

Confustion about Android NDK libc++ libc++_shared, libstdc++

I am getting very confused trying to build a simple C++ library using Android NDK 23 (23.1.7779620). I am using CMake and this is a very simple program:
# CMakeLists.txt
cmake_minimum_required(VERSION 3.14)
project(mf)
add_library(mf lib.cpp)
// lib.hpp
#pragma once
#include <string>
std::string foo(std::string);
// lib.cpp
#include "lib.hpp"
std::string foo(std::string str) {
return std::string{"test"} + str;
}
This is the command line to build:
cmake -G Ninja -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=ON -DANDROID_STL=c++_shared -DANDROID_ABI=arm64-v8a -DANDROID_PLATFORM=android-29 -DCMAKE_TOOLCHAIN_FILE=${ANDROID_NDK}/build/cmake/android.toolchain.cmake ..
cmake --build . -v
The first problem is that I was expecting to link against libc++.so and not libc++_shared.so. What is the difference between them? I read this article. But still is not explained the difference betweend libc++ and libc++_shared
The second problem is even worst, it seems I am using libstdc++!
The 3rd point, I was thinking that the c++ implementation of clang was under the namespace std::__1 but I cannot find anything like that.
I know libc++_shared is used because of this command:
$ readelf -d libmf.so
Dynamic section at offset 0x76e0 contains 26 entries:
Tag Type Name/Value
0x0000000000000001 (NEEDED) Shared library: [libm.so]
0x0000000000000001 (NEEDED) Shared library: [libc++_shared.so]
0x0000000000000001 (NEEDED) Shared library: [libdl.so]
0x0000000000000001 (NEEDED) Shared library: [libc.so]
0x000000000000000e (SONAME) Library soname: [libmf.so]
Running nm it seem I am using symbols from libstdc++:
$ nm -gDC libmf.so | grep '__ndk1'
0000000000003af0 T foo(std::__ndk1::basic_string<char, std::__ndk1::char_traits<char>, std::__ndk1::allocator<char> >)
U std::__ndk1::basic_string<char, std::__ndk1::char_traits<char>, std::__ndk1::allocator<char> >::append(char const*, unsigned long)
$ nm -gDC libmf.so | grep '__1'
$
Update
In this post is explained the difference between libc++.so and libc++_shared.so
By passing -DANDROID_STL=c++_shared to the CMake invocation you explicitly asked for the shared runtime as opposed to the default runtime.
As explained in the documentation, the rules are simple:
if all your native code is in a single library, use the static libc++ (the default) such that unused code can be removed and you have the smallest possible application package.
As soon as you include an extra library – either because you include a precompiled library from somewhere else or you include an Android AAR file that happens to include native code – you must switch to the shared runtime.
The rationale for the rules is simple: the C++ runtime has certain global data structures that must be initialized once and must only exist once in memory. If you were accidentally to load two libraries that both link the C++ runtime statically, you have (for instance) two conflicting memory allocators.
This will result in crashes when you free or delete memory allocated by the other library, or if you pass a C++ STL object like std::string across library boundaries.
For completeness, in older NDKs libstdc++ (the GNU C++ runtime) was also included in the NDK, but as of NDK r18 that is no longer the case.

Integrating shared object in an application, ldd and readelf show different outputs

I am trying to integrate OpenCV into an application and facing some issues below.
error while loading shared libraries: libopencv_imgproc.so.4.1: cannot open shared object file: No such file or directory
However, when I check the output of ldd and readelf, below are the differences.
$readelf -d <app name>
0x0000000000000001 (NEEDED) Shared library: [libopencv_imgcodecs.so.4.1]
0x0000000000000001 (NEEDED) Shared library: [libopencv_core.so.4.1]
$ldd <app name>
libopencv_imgcodecs.so.4.1 => <path to opencv>/opencv/lib/libopencv_imgcodecs.so.4.1 (0x00007f04555b2000)
libopencv_imgproc.so.4.1 => <path to opencv>/opencv/lib/libopencv_imgproc.so.4.1 (0x00007f0453e8f000)
libopencv_core.so.4.1 => <path to opencv>/opencv/lib/libopencv_core.so.4.1 (0x00007f0452774000)
As shown above, libopencv_imgproc is missing from readelf output. Can someone point out what causes such behavior?

clang, linux, is there an option to change shared library name at linking?

I'm testing a somewhat non-conventional project layout and rake as make utility. There is a rule to compile binaries from source files in different directories and link them with a shared library. This rule is run from the root directory of the project. For instance the rule does this:
clang -I libs/ -o tests/sourcefile2 tests/sourcefile2.c shared_libs/libFoo.so
And as a result I get the full path shared_libs/libFoo.so in the binary:
readelf -d tests/sourcefile2
Tag Type Name/Value
0x0000000000000001 (NEEDED) Shared library: [shared_libs/libFoo.so]
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
...
I would like to change it to just 'libFoo.so' like this:
readelf -d tests/sourcefile2
Tag Type Name/Value
0x0000000000000001 (NEEDED) Shared library: [libFoo.so]
...
Then I could set RPATH for dynamic linker as I want and it would give some flexibility. But I cannot find the corresponding option or similar example. Could you suggest how to handle this? Should I just use a temporary directory for the build, copy everything and compile there?
Not sure if it will help you. But when I try compile some shit and I don't know what flags. I use pkg-config.
For example, to compile a program which uses Xlib
pkg-config -cflags -libs x11
and the output is the following
-I/usr/X11R7/include -D_REENTRANT -Wl,-rpath,/usr/X11R7/lib -L/usr/X11R7/lib -lX11
Note this vary on systems, for example NetBSD forces me to link it with rpath, and there are optional arguments in this output.
So I copy the output of pkg-config and it compiles.
if If you use 'ld' as your linker you should be able to use "-Wl,-soname ".

FMOD Debian libfmod.so.8: cannot open shared object file: No such file or directory

I'm trying to install and validate fmod on my raspberry pi 1 model b with debian by running the provided example program play_stream. The compilation works fine however when I try to run the built executable it fails with the error
error while loading shared libraries: libfmod.so.8: cannot open shared object file: No such file or directory
I'm unsure of what i'm doing wrong. Any help or advice you can provide would be great
My set up:
~/fmodstudioapi10813linux/api/lowlevel/examples/make $ make --file play_stream.makefile CONFIG=Debug CPU=arm
~/fmodstudioapi10813linux/api/lowlevel/examples/make $ ./play_stream
./play_stream: error while loading shared libraries: libfmodL.so.8: cannot open shared object file: No such file or directory
~/fmodstudioapi10813linux/api/lowlevel/lib/arm $ ls /usr/local/lib/
libfmodL.so libfmodL.so.8 libfmodL.so.8.13 libfmod.so libfmod.so.8 libfmod.so.8.13
~ $ ls /usr/local/include/
fmod_codec.h fmod_common.h fmod_dsp_effects.h fmod_dsp.h fmod_errors.h fmod.h fmod.hpp fmod_output.h node
~ $ env | grep '^LD_LIBRARY_PATH'
LD_LIBRARY_PATH=:/usr/local/lib:/home/pi/fmodstudioapi10813linux/api/lowlevel/lib/arm
~/fmodstudioapi10813linux/api/lowlevel/examples/make $ ldd play_stream
/usr/lib/arm-linux-gnueabihf/libarmmem.so (0xb6faf000)
libfmod.so.8 => not found
libstdc++.so.6 => /usr/lib/arm-linux-gnueabihf/libstdc++.so.6 (0xb6ec0000)
libm.so.6 => /lib/arm-linux-gnueabihf/libm.so.6 (0xb6e45000)
libgcc_s.so.1 => /lib/arm-linux-gnueabihf/libgcc_s.so.1 (0xb6e18000)
libpthread.so.0 => /lib/arm-linux-gnueabihf/libpthread.so.0 (0xb6df0000)
libc.so.6 => /lib/arm-linux-gnueabihf/libc.so.6 (0xb6caf000)
/lib/ld-linux-armhf.so.3 (0x7f56d000)
~/fmodstudioapi10813linux/api/lowlevel/examples/make $ echo $LD_LIBRARY_PATH
:/usr/local/lib:/home/pi/fmodstudioapi10813linux/api/lowlevel/lib/arm
**~/fmodstudioapi10813linux/api/lowlevel/examples/make $** readelf -d play_stream
Dynamic section at offset 0x420c contains 30 entries:
Tag Type Name/Value
0x00000001 (NEEDED) Shared library: [libfmod.so.8]
0x00000001 (NEEDED) Shared library: [libstdc++.so.6]
0x00000001 (NEEDED) Shared library: [libm.so.6]
0x00000001 (NEEDED) Shared library: [libgcc_s.so.1]
0x00000001 (NEEDED) Shared library: [libpthread.so.0]
0x00000001 (NEEDED) Shared library: [libc.so.6]
0x0000000f (RPATH) Library rpath: [$ORIGIN/../../../lowlevel/lib/arm/]
Run ldd play_stream and echo $LD_LIBRARY_PATH.

Valid ARM executable doesn't find libraries

I cross-compiled a small Hello World program for an ARM embedded device (an Asus RT-AC68U router running DD-WRT):
# arm-linux-gnueabi-g++ hello.cpp -mcpu=cortex-a9 -s -o hello
The file seems fine:
# file hello
hello: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV),
dynamically linked (uses shared libs), for GNU/Linux 2.6.32,
BuildID[sha1]=5099693c31154cdd7f04c16ced5b80b1e35e625b, stripped
It depends on a few libraries:
# readelf -d hello
Dynamic section at offset 0xf08 contains 26 entries:
Tag Type Name/Value
0x00000001 (NEEDED) Shared library: [libstdc++.so.6]
0x00000001 (NEEDED) Shared library: [libgcc_s.so.1]
0x00000001 (NEEDED) Shared library: [libc.so.6]
(...)
When I try to execute the file on the target system, I get the following:
# ./hello
-sh: ./hello: not found
If I launch execution from ld-linux.so.3 without any other parameters, everything works:
# /opt/lib/ld-linux.so.3 ./hello
Hello, world.
When listing the libraries, the file seems to be searching for libraries in the /lib folder (they are not there, they are in /opt/lib):
# /opt/lib/ld-linux.so.3 --list ./hello
libstdc++.so.6 => /lib/libstdc++.so.6 (0x76dfb000)
libgcc_s.so.1 => /lib/libgcc_s.so.1 (0x76de0000)
libc.so.6 => /opt/lib/libc.so.6 (0x76cb2000)
libc.so => /lib/libc.so (0x76c35000)
/lib/ld-linux.so.3 => /opt/lib/ld-linux.so.3 (0x76f20000)
I'm guessing the error is due to the fact that the executable when launched on its own is not finding the required shared libraries. All the required libraries are present in the /opt/lib folder and this folder is included in LD_LIBRARY_PATH environment variable:
# echo $LD_LIBRARY_PATH
/lib:/usr/lib:/jffs/lib:/jffs/usr/lib:/jffs/usr/local/lib
:/mmc/lib:/mmc/usr/lib:/opt/lib:/opt/usr/lib
How do I get the executable to find the libraries in the right place?
Edit: I tried linking the libraries statically in the executable and the program runs fine on the target platform. However that is not a workable solution for what I want to do.
Your ld-linux.so.3 appears to be in a nonstandard location (/opt/lib), and it's possible that your gcc is specifying a different program interpreter. You can use readelf -l <program> to check what program interpreter your binary is expecting (under PT_INTERP, "Requesting program interpreter").
If the binary's program interpreter is wrong for your platform, you can specify it manually by passing -Wl,--dynamic-linker=/opt/lib/ld-linux.so.3 to gcc.