I'm making an Android app with help of the NDK. One of the shared libraries I'm using depends on ICU, which is another library that I'm trying to explicitly include too.
The problem is that my device (like apparently many others) has an old version of ICU pre-installed. This means that whenever I try to load my shared library, the system tries to load the system version instead of my own, more recent version.
Android seems to ignore any RPATHs, which would otherwise let libraries specific where to look for dependencies. As far as I understand RPATH is essentially hard-coded to /vendor/lib and /system/lib on Android.
I've seen a few workarounds for the issue, but none of them seem to work:
Explicitly load all the libraries (including dependencies) in dependency order.
Although the ICU libraries appear to load fine (via their absolute paths), I still get a cannot locate symbol error when attempting to load my shared library. I've quadruple-checked (via nm and readelf) that the missing symbol does exist in my (newly-compiled) ICU .so files.
Change the SONAME of the ICU dynamic libs to something project-specific and depend on them instead.
This would possibly work, but is kind of a last result because it would involve recompiling a lot of library code with non-trivial dependencies. It also feels more like a hack. A simple name change of the libraries doesn't work for the same reason as 1. – although the ICU symbols get loaded, they are not recognised when the dependent library tries to access them.
My question is to anyone who has tried something like this before or knows their way around linking a bit more than I do: how did you get it working? Is there a way to force Android to get its proverbial hotsauce together and actually load the correct libraries, or otherwise use the symbols which I've already successfully loaded?
Related
I have a native Windows library I'd like to include as part of Windows build/package in my MAUI app. Is there a way to do this where it won't be included in the builds/packages of the other platforms?
I found a way to do this with Android native libraries: I simply place them in (ProjectDir)\Resources\lib\(Architecture), where (Architecture) is, for example, 'arm64-v8a'. Then I can simply flag them in the .csproj file as an AndroidNativeLibrary and all's well; they show up in my Android build and don't appear in any of the other builds (Windows, IOS, etc.).
Things I've tried:
Manually copying the Windows native lib (a DLL) to the generated AppX folder via a postbuild script. Not ideal. For one, the AppX folder isn't technically generated until after the build is finished; it's part of a packaging step (I believe). So, this kind of works, but isn't really the proper solution. I want the DLL to automatically be included by the packager.
Adding the DLL as an item to the .csproj. This means it gets automatically included in the AppX package, but is still not ideal as it subsequently winds up in every platform's build.
The most promising: referenced the DLL via a 'file' element in (ProjectDir)\Platforms\Windows\app.manifest. However, it doesn't seem like this manifest file plays any role in the build/packaging. Rather Package.appxmanifest seems to be the file that matters. If I throw intentional typos into app.manifest, building and packaging still succeed. I also added app.manifest to the csproj explicitly via ; it doesn't seem to care.
Anyway, any ideas/insight would be much appreciated.
Ok, I found a fantastic article that solves my problem: https://learn.microsoft.com/en-us/xamarin/cross-platform/cpp/
Not sure how I didn’t find this before; I was looking forever.
The title was tough to get right, so let me explain my situation:
Another team develops a library. They ship a header file and a *.so file. The header file is available to us, and we can include it in our own code and use it if we wish. The *.so, however, is shipped with the platform we run on. We do not have access to this *.so at build time for our software. Because of this, we can't really use the header file either, since the linker will expect the *.so to be available at some point.
Right now what I do is create a wrapper class that loads the *.so file at runtime, then uses dlsym() to find functions by name, and I map them to function pointers.
Is this the only option? Is there a way I can use the header file but tell the linker to not resolve the symbols at build time, but instead try to resolve them at runtime after we have a chance to load the *.so file?
Note the real platform here is Android (via NDK), but hoping general linux advice will work as well in this case since we have POSIX APIs available.
You have a few options, in order of preference:
Get the libraries from the maintainer. Providing the header but not the library (at least a stub library like we do for libraries in the NDK) just won't work.
Build your own stub library. It's pretty straightforward if you have a list of symbols to expose. Put int foo; void bar() {} in a C file for all the variables and functions you need to expose and build it as a shared lib. If you have the list of symbols in a version script, you might be able to use Android's gen_stub_libs.py to do it for you.
Mark all the symbols with __attribute__((weak)) in the header file. The linker won't complain that they are missing. If they're missing at runtime, the library will still load but each function's address will be nullptr. Not really what you want in most cases because if your definition of the library is wrong, you turn build time failures into runtime failures, but in some cases this can be handy because it's easier to check for function availability with if (foo) { foo(); } then to do similar with dlsym.
Add -Wl,--allow-shlib-undefined to your ldflags. This is even worse than 3 because it affects all the libraries you link, but it wouldn't require you to meddle with the header.
On Windows this is solved by requiring export library (.lib) to be present for linker instead of real dynamic library (.dll). I think you can try making something similar, that is making a fake .so containing stubs of all the methods exported from real .so and linking against it. This hopefully will make linker happy and at the same time at runtime application will load real .so.
We are building an app that imports, via Artifactory, a collection of libraries that are then referenced from the main app.
We are in the process of localizing our app. We can localize our libraries and verify it works by directly using the libraries in a test app. When the libraries are pushed to our artifactory, we can then import, via gradle, the libraries for use in our app.
We can verify that making code changes or even changes to English strings in the libraries all work, and when pulled into the main app, any such changes are reflected correctly. However, while we have localized our libraries (into Spanish), when pulling the localized libraries into the main, the Spanish strings are not used. Instead, it uses the default (English) strings. We have looked at the libraries on our artifactory, downloaded the AARs and verified that the snapshots do in fact contain the localized strings but for reasons we can't figure out when imported into the main app, it does not use them.
We're confused why this is the case - wether we are doing something wrong or if there is a bug in gradle or Android.
Does anyone have any insight? I apologise for the vagueness but the issue is rather esoteric so I'm not sure what code if any might be relevant to solving the issue. Feel free to ask for further clarification.
UPDATE:
We decided to import the .AAR directly, by placing it in a libs/ directory and referencing it in our Gradle build. It now works fine, the library in question is correctly localized.
So it would seem the issue comes from when the snapshot is downloaded from the repository.
The app is looking at the wrong version of the library: the previously released version rather than the latest SNAPSHOT.
Full disclosure: James and I work together.
What I want to achieve:
In sonar it is possible to track third party dependencies used throughout Projects by setting the property "sonar.libraries" and perhaps there are more benefits (such as detecting which violations are caused by external libraries?)
What I tried to do:
I set the value to build/intermediates/pre-dexed/debug/*.jar but this seems to have little effect.
Question:
Since it is no longer needed to use the "libs" folder for third party dependencies, what is the recommendation for the property called "sonar.libraries"?
We need to implement special processing when an Android project is detected. Correctly setting sonar.java.libraries is one of the requirements. A ticket already exists, feel free to vote or provide a pull request.
https://jira.sonarsource.com/browse/SONARGRADL-6
Update: we have released version 2.1 of the plugin (currently RC2) that natively supports Android projects. Properties sonar.java.[test.]binaries and sonar.java.[test.]libraries will be automatically populated.
The pre-dex folder also seemed to me to be the perfect candidate. Unfortunately, if you look into the jars in this folder, you'll see they contain no compiled class, but dex files. A dex file being a "dalvik executable", it's Android material, and Sonar can't do anything of it.
I managed to reference some of my dependencies, by declaring build/intermediates/exploded-aar/**/*.jar in the sonar.libraries property.
With this line you'll see all the "android librairies" (aar) that your project depends on. I've not yet managed to track all the other plain java libraires (jar)
I'm porting a big chunk of native code with several interdependent libraries. Loading the code fails since Android linker only looks dependencies in /system/lib/ and not in the application install directory at /data/data/com.packagename.nnn/lib.
As a first workaround I loaded all .so's from Java with System.load() in correct dependency order. This solution isn't satisfactory for me mostly because the set of libraries changes from build to build due to plugin architecture. Also the UI shouldn't need to know that much about native libs.
I've found that Android does not support RPATH or setting LD_LIBRARY_PATH for an app. The only workaround I found is building the libraries with fully qualified SONAMEs.
However some of the dependencies come pre-built.
Is it possible compile fully qualified NEEDED tag into my own library even when the needed .so does not have fully qualified SONAME?
Or is it possible to modify existing .so and replace its SONAME or NEEDED with fully qualified one without recompiling?
Mozilla Fennec loads a bunch of shared objects from a custom cache directory under their /data/data/package/ directory, you might take a look at their source.