Building a .so to be compatible with apps built with different NDKs?

163 views
Skip to first unread message

jgdats...@gmail.com

unread,
Dec 18, 2017, 9:56:11 AM12/18/17
to android-ndk

My first venture in Android programming is not terribly simple. I need to port some mathematical modelling libraries, compiled from C and C++ code, to Android. The code is thoroughly portable, and runs on many platforms already but I have no experience with Android. I'm also partially sighted, which makes it very difficult for me to use IDEs, so I'm planning to do everything from the Linux command line. 


I am not going to be building an app for distribution. The product I work on is libraries, which are licensed to ISVs, who use them in applications programs. The libraries will run on-device, and have no web or cloud interfaces or APIs of any kind. Customers who want to run them in the cloud already do that, using the Linux or Windows builds. They're fairly heavyweight code, which needs a fair bit of memory and floating-point for serious work, so I'm hoping to only build for 64-bit Android, or if a 32-bit build is necessary, to confine it to AArch32. I will have to build an app to run tests, but this will be an Android wrapper round the existing test harness. 


So I seem to need to build my libraries into .so shared libraries, which is familiar from Linux. But since I'm not building for one specific app, and the testing process for these libraries is lengthy, even though it is fully automated, I'd like to build to be compatible with a range of levels of Android. 


My initial customer is using NDK 14b. I could use that, or I could use the latest NDK, 16b. If I compile C and C++ code with NDK 16b, the same compiler (Clang), a compatible instruction set and the same C++ run-time (libc++) and target API (21) version as my customer, will they be able to use my shared libraries in their NDK 14b app?


The other way around is also interesting: if I used NDK 14b, and another customer comes along who uses NDK 16b, will shared libraries I've built with 14b work in their 16b-built app? I'd be using Clang, targeting an equal or earlier API to them, and the same instruction set and C++ run-time. 


Thanks in advance,


John


Dan Albert

unread,
Dec 18, 2017, 1:30:21 PM12/18/17
to android-ndk
Yes, in general that works. There can be some incompatibilities if you are building static libraries, and this isn't true of every NDK version. Pre-r11 and r11+ libc++ are not compatible, and there may be another ABI change in the future.

AArch32 is not a supported Android ABI. You wouldn't gain much from it if you're building AArch64 already anyway (which you should, because it is a free a sizable performance improvement), since the reason you will also want to build for 32-bit is to support old or low end devices (of which there are still a large number, but if they aren't as prevalent in your user group, maybe 64-bit only is okay). The older devices have pre-armv8 chips, so they wouldn't be able to run AArch32 code anyway.

--
You received this message because you are subscribed to the Google Groups "android-ndk" group.
To unsubscribe from this group and stop receiving emails from it, send an email to android-ndk+unsubscribe@googlegroups.com.
To post to this group, send email to andro...@googlegroups.com.
Visit this group at https://groups.google.com/group/android-ndk.
To view this discussion on the web visit https://groups.google.com/d/msgid/android-ndk/d93eae76-10b3-4cd4-947b-c13545f6ff0a%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Alex Cohn

unread,
Dec 21, 2017, 7:57:13 AM12/21/17
to android-ndk
> will shared libraries I've built with 14b work in their 16b-built app?

The question is whether your libs expose only JNI (and communicate directly with Java), or a C API, or a C++ API.

If your customers have other own native libs, you must make sure that you use the same ABI: if they build for arm64, you should too; if they build for armeabi-v7a, you must provide your libs for that ABI even if the device is capable of running the 64-bit version: the Android app will only choose one ABI to be installed on the device.

C++ compatibility is not guaranteed between NDK versions (passing exceptions included); C API will work, but delicate things like tracing native crashes may be broken.

BR,
Alex

John Dallman

unread,
Feb 27, 2018, 10:17:23 AM2/27/18
to andro...@googlegroups.com
On Thu, Dec 21, 2017 at 12:57 PM, Alex Cohn <sasha...@gmail.com> wrote:
> will shared libraries I've built with 14b work in their 16b-built app?
The question is whether your libs expose only JNI (and communicate directly with Java), or a C API, or a C++ API.

All my APIs are pure C, even for the parts that are written in C++. We've never needed JNI to date, although that may have to change for Android. 
 
If your customers have other own native libs, you must make sure that you use the same ABI: if they build for arm64, you should too; if they build for armeabi-v7a, you must provide your libs for that ABI even if the device is capable of running the 64-bit version: the Android app will only choose one ABI to be installed on the device.

OK. 
 
C++ compatibility is not guaranteed between NDK versions (passing exceptions included); C API will work, but delicate things like tracing native crashes may be broken.

We do not pass C++ exceptions through APIs, although we use them within C++ code. 

However, applications that use our libraries sometimes throw C++ exceptions from functions of theirs that we have called, through out libraries, and catch them above us in the call stack. Obviously that has potential to go wrong if C++ is not guaranteed compatible: we plan to insist on libc++, and we will compile C with -fexceptions to let C++ exceptions propagate through C stack frames. 

Thanks,

John


Alex Cohn

unread,
Feb 28, 2018, 6:16:30 AM2/28/18
to android-ndk
On Tuesday, February 27, 2018 at 5:17:23 PM UTC+2, John Dallman wrote:
 
We do not pass C++ exceptions through APIs, although we use them within C++ code. 

However, applications that use our libraries sometimes throw C++ exceptions from functions of theirs that we have called, through out libraries, and catch them above us in the call stack. Obviously that has potential to go wrong if C++ is not guaranteed compatible: we plan to insist on libc++, and we will compile C with -fexceptions to let C++ exceptions propagate through C stack frames. 

I am afraid that you cannot compile C with -fexceptions. The rule of thumb is to use C++ compiler for all your sources.

BR,
Alex

John Dallman

unread,
Feb 28, 2018, 11:53:47 AM2/28/18
to andro...@googlegroups.com
Also, why can't you compile C with -fexceptions? Does the compiler refuse it, it not work, or something else? I'm quite happy to restrict the C++ compiler it would work with to Clang++ and libc++, if that helps. 

Thanks,

John


On Wed, Feb 28, 2018 at 1:17 PM, John Dallman <jgdats...@gmail.com> wrote:
> I am afraid that you cannot compile C with -fexceptions. The rule of thumb is to use C++ compiler for all your sources.

Seriously? That's a very worrying prospect. The C is auto-generated from a higher level language, which has no facility to generate C++, and I don't know what adaptations would have to be made to the files to make them compile as C++ and still have the APIs come out right. 

Can anyone else confirm this, before I have to start a major project? 

John

--
You received this message because you are subscribed to the Google Groups "android-ndk" group.
To unsubscribe from this group and stop receiving emails from it, send an email to android-ndk+unsubscribe@googlegroups.com.
To post to this group, send email to andro...@googlegroups.com.
Visit this group at https://groups.google.com/group/android-ndk.

John Dallman

unread,
Feb 28, 2018, 11:53:47 AM2/28/18
to andro...@googlegroups.com
> I am afraid that you cannot compile C with -fexceptions. The rule of thumb is to use C++ compiler for all your sources.

Seriously? That's a very worrying prospect. The C is auto-generated from a higher level language, which has no facility to generate C++, and I don't know what adaptations would have to be made to the files to make them compile as C++ and still have the APIs come out right. 

Can anyone else confirm this, before I have to start a major project? 

John
On Wed, Feb 28, 2018 at 11:16 AM, Alex Cohn <sasha...@gmail.com> wrote:

--
You received this message because you are subscribed to the Google Groups "android-ndk" group.
To unsubscribe from this group and stop receiving emails from it, send an email to android-ndk+unsubscribe@googlegroups.com.
To post to this group, send email to andro...@googlegroups.com.
Visit this group at https://groups.google.com/group/android-ndk.

Dan Albert

unread,
Feb 28, 2018, 1:23:00 PM2/28/18
to android-ndk
-fexceptions actually does have an effect on C code if you're using a GNU extension: __attribute__((cleanup)). It will allow cleanups to run if an exception unwinds through your C function.

Keep in mind that it is *not* safe to propagate exceptions past JNI though. Exceptions need to be caught and handled before you transition back to Java.

Ryan Prichard

unread,
Feb 28, 2018, 7:09:56 PM2/28/18
to andro...@googlegroups.com
Even if you aren't using __attribute__((cleanup)), using -fexceptions on C code enables generation of unwind tables. These tables can also be enabled with -funwind-tables.

(If you build with -funwind-tables but not -fexceptions, then __attribute__((cleanup)) functions aren't called, but C frames are still unwound.)

-Ryan


John Dallman

unread,
Mar 1, 2018, 12:13:26 PM3/1/18
to andro...@googlegroups.com
I am not using the GNU attribute. The circumstances I'm concerned about are thus: 
  1. I build my mathematical modelling library from C code, with the C compiler, using -fexceptions to generate unwind tables for it. 
  2. My customer's C++ code calls my C code, and supplies 'extern "C"' function pointers for me to call when I need the customer's code to make a decision about the evaluation (some of these can't be anticipated beforehand). 
  3. I call one of the said function pointers, and in the customer's code, he hits an error condition, and needs to abort the operation.
  4. He throws a C++ exception, from C++ code that's below me in the call stack. The exception handler needs to unwind the call stack, through the C stack frames, back to his C++ code that's above me in the call stack. 
  5. He catches the C++ exception there, and makes a call to my cleanup function to tidy data within my code.
This method works just fine on desktop Windows, Linux and macOS. If it does not work on Android, that's a major barrier to porting these libraries to the platform. Should it work? 

Note that no JNI is involved with my scenario. The JNI to C++ transition is my customer's problem, and he hasn't asked for a JNI binding for my C library. 

Thanks very much,

John


Dan Albert

unread,
Mar 1, 2018, 1:46:13 PM3/1/18
to android-ndk
Should be fine as long as your code and your customers' code remains compatible. The STL that you and your users use need to be the same. For modern NDKs this means both using libc++, and the same ABI version of libc++. We don't break the ABI often, but it's possible that we may in the future. Your best bet is to always use the exact same NDK to build every binary in the app.

Note that the above guidance isn't actually unique to your situation. This is true of every NDK app.

John Dallman

unread,
Mar 2, 2018, 2:04:05 PM3/2/18
to andro...@googlegroups.com
On Thu, Mar 1, 2018 at 6:45 PM, 'Dan Albert' via android-ndk <andro...@googlegroups.com> wrote:
The STL that you and your users use need to be the same. For modern NDKs this means both using libc++, and the same ABI version of libc++.  We don't break the ABI often, but it's possible that we may in the future.

Yup, understood. 

Your best bet is to always use the exact same NDK to build every binary in the app.

Sadly, this is not something that I can plan on. The libraries I'm building are licensed by over a hundred commercial customers, across the range of platforms that we support. The customers do not conspire to use different toolchains so as to make our lives difficult, but if they started to do so, it would not make much difference. 

So I'm under considerable pressure to build something that's as widely compatible as possible, within the constraints of being useful. Requiring several different builds for different NDKs is something that's going to take a lot of justifying. Our automated testing is sufficiently time-consuming that I'll need a separate build machine and test setup for each NDK. To get the budget for that, I need a hard statement that backwards compatibility to older NDKs does not work, or to prove that in action. 

Management are assuming that Android backward compatibility works as well as it does on iOS, where it's been flawless for us so far. 

Thanks,

John

Dan Albert

unread,
Mar 2, 2018, 3:05:45 PM3/2/18
to android-ndk
Your best bet is to always use the exact same NDK to build every binary in the app.

Sadly, this is not something that I can plan on.

Ack. Almost nobody does this. Keep to the other guidelines and incompatibility issues will be fairly unlikely (and are more likely to be build failures than run-time failures).

Alex Cohn

unread,
Mar 2, 2018, 5:38:15 PM3/2/18
to android-ndk
On Wednesday, February 28, 2018 at 8:23:00 PM UTC+2, Dan Albert wrote:
-fexceptions actually does have an effect on C code if you're using a GNU extension: __attribute__((cleanup)). It will allow cleanups to run if an exception unwinds through your C function.

Keep in mind that it is *not* safe to propagate exceptions past JNI though. Exceptions need to be caught and handled before you transition back to Java.
 
Cool. TIL that both clang and gcc accept -fexceptions. The latter at least issues a warning for -frtti.

Alex 

On Feb 28, 2018 08:53, "John Dallman" <jgdats...@gmail.com> wrote:
Also, why can't you compile C with -fexceptions? Does the compiler refuse it, it not work, or something else? I'm quite happy to restrict the C++ compiler it would work with to Clang++ and libc++, if that helps. 

Thanks,

John

On Wed, Feb 28, 2018 at 1:17 PM, John Dallman <jgdats...@gmail.com> wrote:
> I am afraid that you cannot compile C with -fexceptions. The rule of thumb is to use C++ compiler for all your sources.

Seriously? That's a very worrying prospect. The C is auto-generated from a higher level language, which has no facility to generate C++, and I don't know what adaptations would have to be made to the files to make them compile as C++ and still have the APIs come out right. 

Can anyone else confirm this, before I have to start a major project? 

John
On Wed, Feb 28, 2018 at 11:16 AM, Alex Cohn <sasha...@gmail.com> wrote:
On Tuesday, February 27, 2018 at 5:17:23 PM UTC+2, John Dallman wrote:
 
We do not pass C++ exceptions through APIs, although we use them within C++ code. 

However, applications that use our libraries sometimes throw C++ exceptions from functions of theirs that we have called, through out libraries, and catch them above us in the call stack. Obviously that has potential to go wrong if C++ is not guaranteed compatible: we plan to insist on libc++, and we will compile C with -fexceptions to let C++ exceptions propagate through C stack frames. 

I am afraid that you cannot compile C with -fexceptions. The rule of thumb is to use C++ compiler for all your sources.

BR,
Alex

--
You received this message because you are subscribed to the Google Groups "android-ndk" group.
To unsubscribe from this group and stop receiving emails from it, send an email to android-ndk...@googlegroups.com.

To post to this group, send email to andro...@googlegroups.com.
Visit this group at https://groups.google.com/group/android-ndk.

--
You received this message because you are subscribed to the Google Groups "android-ndk" group.
To unsubscribe from this group and stop receiving emails from it, send an email to android-ndk...@googlegroups.com.

John Dallman

unread,
Mar 5, 2018, 12:21:18 PM3/5/18
to andro...@googlegroups.com
Hum. It's something that appears to be designed into the NDK, especially since the NDK went down to one set of headers. It evidently isn't widely used; presumably it isn't heavily tested either? 

Thanks,

John


Dan Albert

unread,
Mar 5, 2018, 1:30:32 PM3/5/18
to android-ndk
What is "it"?

--
You received this message because you are subscribed to the Google Groups "android-ndk" group.
To unsubscribe from this group and stop receiving emails from it, send an email to android-ndk...@googlegroups.com.

To post to this group, send email to andro...@googlegroups.com.
Visit this group at https://groups.google.com/group/android-ndk.

John Dallman

unread,
Mar 6, 2018, 5:38:32 AM3/6/18
to andro...@googlegroups.com
On Mon, Mar 5, 2018 at 6:30 PM, 'Dan Albert' via android-ndk <andro...@googlegroups.com> wrote:
What is "it"?

The ability to compile for older API versions. 

Thanks,

John

 

On Mon, Mar 5, 2018, 09:21 John Dallman <jgdats...@gmail.com> wrote:
On Fri, Mar 2, 2018 at 8:05 PM, 'Dan Albert' via android-ndk <andro...@googlegroups.com> wrote:
Your best bet is to always use the exact same NDK to build every binary in the app.

Sadly, this is not something that I can plan on.

Ack. Almost nobody does this. Keep to the other guidelines and incompatibility issues will be fairly unlikely (and are more likely to be build failures than run-time failures).

Hum. It's something that appears to be designed into the NDK, especially since the NDK went down to one set of headers. It evidently isn't widely used; presumably it isn't heavily tested either? 

Thanks,

John


--
You received this message because you are subscribed to the Google Groups "android-ndk" group.
To unsubscribe from this group and stop receiving emails from it, send an email to android-ndk+unsubscribe@googlegroups.com.

To post to this group, send email to andro...@googlegroups.com.
Visit this group at https://groups.google.com/group/android-ndk.

--
You received this message because you are subscribed to the Google Groups "android-ndk" group.
To unsubscribe from this group and stop receiving emails from it, send an email to android-ndk+unsubscribe@googlegroups.com.

To post to this group, send email to andro...@googlegroups.com.
Visit this group at https://groups.google.com/group/android-ndk.

Alex Cohn

unread,
Mar 6, 2018, 8:07:15 AM3/6/18
to android-ndk
On Tuesday, March 6, 2018 at 12:38:32 PM UTC+2, John Dallman wrote:
On Mon, Mar 5, 2018 at 6:30 PM, 'Dan Albert' via android-ndk <andro...@googlegroups.com> wrote:
What is "it"?

The ability to compile for older API versions. 

Quite contrary. Any version of NDK can build "for older API" and this is well supported. There are some limitations, e.g. NDK r16 dropped platforms 9, 12 and 13. Moreover, your code compiled for android-21 will happily run on android-26. Also, a shared library built for android-21 will coexist with other libs built for android-26. There may be some issues if you have a static library for 21, and want to link it into a shared library for 26, but unless the linker complains about missing references, you are safe.

It is really preferable to have all code compiled with the same version of NDK, as Dan wrote before. Furthermore, use the same toolchain (note that since NDK 13, GCC is no longer supported and does not receive backports). Also, make sure that different STLs are not mixed. The guidelines that Dan mentioned emphasise that the public APIs should not expose C++ objects, to mitigate problems with STL incompatibilities.
 
Hope this clarifies the picture,
Alex

Thanks,

John

 

On Mon, Mar 5, 2018, 09:21 John Dallman <jgdats...@gmail.com> wrote:
On Fri, Mar 2, 2018 at 8:05 PM, 'Dan Albert' via android-ndk <andro...@googlegroups.com> wrote:
Your best bet is to always use the exact same NDK to build every binary in the app.

Sadly, this is not something that I can plan on.

Ack. Almost nobody does this. Keep to the other guidelines and incompatibility issues will be fairly unlikely (and are more likely to be build failures than run-time failures).

Hum. It's something that appears to be designed into the NDK, especially since the NDK went down to one set of headers. It evidently isn't widely used; presumably it isn't heavily tested either? 

Thanks,

John


--
You received this message because you are subscribed to the Google Groups "android-ndk" group.
To unsubscribe from this group and stop receiving emails from it, send an email to android-ndk...@googlegroups.com.

To post to this group, send email to andro...@googlegroups.com.
Visit this group at https://groups.google.com/group/android-ndk.

--
You received this message because you are subscribed to the Google Groups "android-ndk" group.
To unsubscribe from this group and stop receiving emails from it, send an email to android-ndk...@googlegroups.com.

To post to this group, send email to andro...@googlegroups.com.
Visit this group at https://groups.google.com/group/android-ndk.

John Dallman

unread,
Mar 6, 2018, 11:51:16 AM3/6/18
to andro...@googlegroups.com
On Tue, Mar 6, 2018 at 1:07 PM, Alex Cohn <sasha...@gmail.com> wrote:
Quite contrary. Any version of NDK can build "for older API" and this is well supported. There are some limitations, e.g. NDK r16 dropped platforms 9, 12 and 13. Moreover, your code compiled for android-21 will happily run on android-26. Also, a shared library built for android-21 will coexist with other libs built for android-26. There may be some issues if you have a static library for 21, and want to link it into a shared library for 26, but unless the linker complains about missing references, you are safe.

It is really preferable to have all code compiled with the same version of NDK, as Dan wrote before. Furthermore, use the same toolchain (note that since NDK 13, GCC is no longer supported and does not receive backports). Also, make sure that different STLs are not mixed. The guidelines that Dan mentioned emphasise that the public APIs should not expose C++ objects, to mitigate problems with STL incompatibilities.

This is where I see a disconnect, but experienced Android people clearly don't. 

I'm completely new to developing for this platform, and I am not starting out by developing conventional Java apps, because my employers have no need of them whatsoever. I am quite experienced at porting the code I'm working on, and I have a pretty good understanding of what facilities it needs. 

With iOS, the most nearly similar platform I have worked with, compiling for the API standard of an older iOS gives you something that works with the build tools for the older iOS, with no qualifications. The same works with macOS, seemingly because Apple regard macOS as a subsidiary of iOS. 

Whereas, if I'm following you correctly, I can use the latest NDK to build a complete app for an old API standard of Android, and that works fine. But I'm not in the app business. I'm in the component business. 

I want to make libraries, primarily from C code, that my customers can use on the widest reasonable range of standards of Android (within the arm64-v8a ABI). I'd prefer to distribute these libraries as .so files, but this does not mean that they're set up for calling from JNI. My customers will have to write C or C++ code to call them, and expect that. I can recommend that they use a specific NDK, but I can't force them to do so. I think I can force them to to use libc++ when we do the C++ libraries. We were able to manage that on Mac, under similar circumstances (Clang replacing GCC). 

What sort of things cause problems when you mix code compiled with different NDKs? Is it different levels of completeness of Bionic LibC? Different availability of graphics functions? Variations in object file format? I may be able to do things about some of those, or anything else that I know about. But "we recommend you don't do this", without much explanation as to why, puts me at risk of spending a lot of time on something that can't work, and I want to avoid that. 

Thanks,

John

Dan Albert

unread,
Mar 6, 2018, 2:06:21 PM3/6/18
to android-ndk
tl;dr: Cross-NDK version issues are rare, but not unheard of. A "For best results, use NDK r$BLAH." note is all you need.
 
Whereas, if I'm following you correctly, I can use the latest NDK to build a complete app for an old API standard of Android, and that works fine. But I'm not in the app business. I'm in the component business. 

Apps, libraries, whatever. The same rules apply. Build a shared library targeting whatever the minimum version of Android you want to support is and it will work.

I'd prefer to distribute these libraries as .so files, but this does not mean that they're set up for calling from JNI.

 At some level you will be called from JNI, unless your library is for a system service and not an app. If your users are the ones that have to deal with JNI, they are the ones that need to make sure exceptions are caught, not you.

I can recommend that they use a specific NDK, but I can't force them to do so.

As I said before, it's a helpful guideline, but very few people do it. The real reason for this guideline is because this path is entirely untested (by us; it's tested every day by users, but that's not really "testing"). NDK testing involves using the same NDK throughout (that's what we're trying to test, after all). The only problem related to this that we've ever discovered that I can remember is https://github.com/android-ndk/ndk/issues/23#issuecomment-197740200.

Is it different levels of completeness of Bionic LibC? Different availability of graphics functions?

These things vary by OS level, not by NDK level (they vary by NDK level only in that a newer NDK will support a different set of OS levels, but only insofar as we add new OS versions and remove incredibly old ones).

Variations in object file format?

I suppose this could vary compiler to compiler (even version to version of the same compiler), but I'm guessing such a change in Clang would be fairly unpopular with their users.

The only other things that I can think of that would go wrong would be considered NDK bugs (accidental ABI change or something similar). Like everything else, this is unlikely and we'd release a hotfix for this sort of thing ASAP.

--
You received this message because you are subscribed to the Google Groups "android-ndk" group.
To unsubscribe from this group and stop receiving emails from it, send an email to android-ndk+unsubscribe@googlegroups.com.

To post to this group, send email to andro...@googlegroups.com.
Visit this group at https://groups.google.com/group/android-ndk.

Alex Cohn

unread,
Mar 6, 2018, 3:38:27 PM3/6/18
to android-ndk


On Tuesday, March 6, 2018 at 6:51:16 PM UTC+2, John Dallman wrote:
 
With iOS, the most nearly similar platform I have worked with, compiling for the API standard of an older iOS gives you something that works with the build tools for the older iOS, with no qualifications.

This behavior is very similar to NDK. 

Mixing the versions of NDK is like what you get if you distribute a library built with Xcode 5. It may work for your customers with Xcode 8, but there may be problems, and they would ask you to rebuild it with the latest Xcode. Now, your library built with Xcode 9 may probably work for a customer who is stuck at Xcode 7, but I won't bet on this; instead, I would recommend all my customers to upgrade their Xcode.

BR,
Alex

John Dallman

unread,
Mar 8, 2018, 12:20:10 PM3/8/18
to andro...@googlegroups.com
OK, I'm happy. I think I understand the risks and issues, and what to do if we run into them.

Thanks, everybody.

John

Reply all
Reply to author
Forward
0 new messages