feverDream
feverDream

Reputation: 283

Linking with versioned shared library in Android NDK

I am trying to load two shared libraries in my Android application through the loadLibrary call:

System.loadLibrary("mywrapper");
System.loadLibrary("crypto");

I keep running catching the `UnsatisfiedLinkError. Here is a more detailed version of the error.

Caused by: java.lang.UnsatisfiedLinkError: Cannot load library: link_image[1969]:
  130 could not load needed library 'libcrypto.so.1.0.0' for 
  'libmywrapper.so' (load_library[1111]: Library 'libcrypto.so.1.0.0' not found)

Any ideas?

After spending some time I found out that Android doesn't support versioned libraries. Has any one faced the same issue?

Upvotes: 6

Views: 4122

Answers (4)

Billthekidz
Billthekidz

Reputation: 1

In 2024, Android 14 still doesn't support versioned objects in the form of *.so.x.x.x because it forces module_name+'.so'==object_name.

I was able to follow the most upvoted as of now except that rpl does not do it for binary file in 2024 and searching and replacing via a text editor messes up the ELF format.

I used patchelf to do it and it works great. github

Sample commands for openssl, libssl.so.1.1 and libcrypto.so.1.1 were:

 2078  objdump -p lib* | grep so
 2079  patchelf --set-soname libssl_1_1.so libssl.so.1.1
 2080  patchelf --set-soname libcrypto_1_1.so libcrypto.so.1.1
 2081  patchelf --replace-needed libcrypto.so.1.1 libcrypto_1_1.so 

Again, don't forget to rename the file to match the SONAME.

For other library, the shared object should be inspected before using patchelf to understand what needs to be changed.

Upvotes: 0

user996042
user996042

Reputation: 559

I had the same problem on building libwebsockets for Android, which needs to link with OpenSSL. I use libssl.so as example. You should do the same for related .so files.

Before:
huiying@huiying-PORTEGE-R835:~$ objdump -p libssl.so | grep so
libssl.so:     file format elf32-little
  NEEDED               libcrypto.so.1.0.0
  NEEDED               libdl.so
  NEEDED               libc.so
  SONAME               libssl.so.1.0.0

After 
huiying@huiying-PORTEGE-R835:~$ rpl -R -e .so.1.0.0 "_1_0_0.so" libssl.so 
Replacing ".so.1.0.0" with "_1_0_0.so" (case sensitive) (partial words matched)
.
A Total of 2 matches replaced in 1 file searched.
huiying@huiying-PORTEGE-R835:~$ objdump -p libssl.so | grep so
libssl.so:     file format elf32-little
  NEEDED               libcrypto_1_0_0.so
  NEEDED               libdl.so
  NEEDED               libc.so
  SONAME               libssl_1_0_0.so

And don't forget to change file name "libssl.so" to "libssl_1_0_0.so".

The hack works. I have running Android app to prove it. See my rant at http://computervisionandjava.blogspot.com/2015/05/trouble-with-versioned-shared-libraries.html.

Upvotes: 6

sviborg
sviborg

Reputation: 23

Year 2014 and still no support for versioned shared libs. So I made a script to patch SONAME. Just point the script to input dir where all versioned libs placed. Then check output dir "unver".

#!/bin/bash

DIR="$1"

if [ "$DIR" == "" ]; then
    echo "Usage: fix-soname.sh <target dir>"
    exit
fi

if [ ! -d $DIR ]; then
    echo "Not found: $DIR"
    exit
fi

OUT="$DIR/unver"
echo "Input=$DIR"
echo "Output=$OUT"

CWD=$(pwd)
cd $DIR

# prep dirs
mkdir -p $OUT
rm -f -R $OUT/*

# rename libs and copy to out dir
find "$DIR" -type f -name '*.so*' | while read FILE; do

    NAME=$(basename "$FILE")
    SONAME=$NAME

    while read SYMLINK; do
        X=$(basename "$SYMLINK")
        #echo "$X (${#X}) -> $NAME (${#NAME})"
        if [ "${#X}" -lt "${#SONAME}" ]; then
            SONAME=$X
        fi
done<<EOT
`find -L $DIR -samefile $FILE`
EOT

    #echo $SONAME
    cp -f $SONAME $OUT/
done

# patch libs in out dir
find "$OUT" -type f -name '*.so*' | while read FILE; do

    # get file name without path
    NAME=$(basename "$FILE")

    # extract SONAME from shared lib
    SONAME=`readelf -d $FILE | grep '(SONAME)' | grep -P '(?<=\[)(lib.*?)(?=\])' -o`

    #echo "$NAME [$SONAME]"

    # patch SONAME if required
    if [ "$NAME" != "$SONAME" ]; then
        L1=${#NAME}
        L2=${#SONAME}
        LDIFF=$((L2-L1))
        #echo "$NAME [$SONAME] ($LDIFF)"

        if [ "$LDIFF" -gt "0" ]; then
            SONEW=$NAME
            for (( c=1; c<=$LDIFF; c++ )); do
                SONEW+="\x00"
            done
            echo "$NAME [$SONAME] -> $SONEW ($LDIFF)"
            rpl -R -e "$SONAME" "$SONEW" $OUT
        fi
    fi
done

cd $CWD

Upvotes: 2

feverDream
feverDream

Reputation: 283

It seems android has an issue with loading versioned libraries.The issue at hand was because of library so-name in my case libcrypto.so.1.0.0. Even if you rename the library and try to load it as a prebuilt shared library in an android make file it fails.( It has to be because the library name is somehow embedded in the file. And any library that links with it expects to be linked with a library of with the same name )

I hope there are other ways out there when it comes to handling libraries with version names in android.

For now I am evading the problem all together by using static libraries of openssl and linking them with my own shared library.

Upvotes: 2

Related Questions