How to use armv6 third party libraries in an armv7 app? - ios

I have two 3rd party libraries. One just has a build for armv6 and the other just has a build for armv7. I need to use both of them in my iOS enterprise application. I've asked the armv6 library vendors to supply a armv7 version, but they haven't been able to do so. (Note: I've already got the answer and will provide it. Someone else asked this in a comment and there wasn't enough room to answer, so I've created its own question, and will provide my answer.)

The answer is to hack the armv6 library into thinking it's an armv7 library. This will get you running until the vendor supplies the library. The reason this works is because the arm spec requires all arm architectures to be able to run code generated by previous architectures. So if an armv6 library told the linker it's an armv7, the processor should still be able to run the code. Of course, you can't go the other way. Use otool -h on both libraries to see the cputype and cpusubtype. On my libraries it was 12 for both cputypes, and 6 and 9 for the subtypes, indicating armv6 and armv7.
Using a hex editor, look for the hex string 0xcefaedfe which is the marker MH_MAGIC (0xfeedface) reversed due to big/little endian.
Following that, there's a whole word with just 0xC. That's the 12 for the cputype. Next is a word for 0x6. Change that to 0x9 for all cases.
Now ld will think your library is an armv7, and act accordingly.
You're probably not done, because the armv6 may have linked in some thumbs library routines. If you get link errors (I got some for switch8 and switch16), you need to find the Darwin code that has them. Look for the file lib1funcs.asm on the web. This will probably have your missing functions. They're probably conditionaled out, so adjust the #ifdefs and make sure they're being compiled. This file tries to open some includes at the end, but since they're at the end, they don't affect anything, so just comment them out.
Yes, it is a tremendous hack, but it gets you up and running. If you get your revised library, you just have to drop it in. No code change required. If it works for you, you're still using Apple's code, and it would be the same code you'd be running if you were armv6 only.

Related

Xcode 14 deprecates bitcode - but why?

The bounty expires in 7 days. Answers to this question are eligible for a +50 reputation bounty.
mfaani wants to reward an existing answer.
Xcode 14 Beta release notes are out, all thanks to the annual WWDC.
And alas, the Bitcode is now deprecated, and you'll get a warning message if you attempt to enable it.
And I was wondering, why has this happened? Was there any downside to using Bitcode? Was it somehow painful for Apple to maintain it? And how will per-iPhone-model compilation operate now?
Bitccode is actually just the LLVM intermediate language. When you compile source code using the LLVM toolchain, source code is translated into an intermediate language, named Bitcode. This Bitcode is then analyzed, optimized and finally translated to CPU instructions for the desired target CPU.
The advantage of doing it that way is that all LLVM based frontends (like clang) only need to translate source code to Bitcode, from there on it works the same regardless the source language as the LLVM toolchain doesn't care if the Bitcode was generated from C, C++, Obj-C, Rust, Swift or any other source language; once there is Bitcode, the rest of the workflow is always the same.
One benefit of Bitcode is that you can later on generate instructions for another CPU without having to re-compile the original source code. E.g. I may compile a C code to Bitcode and have LLVM generate a running binary for x86 CPUs in the end. If I save the Bitcode, however, I can later on tell LLVM to also create a running binary for an ARM CPU from that Bitcode, without having to compile anything and without access to the original C code. And the generated ARM code will be as good as if I had compiled to ARM from the very start.
Without the Bitcode, I would have to convert x86 code to ARM code and such a translation produces way worse code as the original intent of the code is often lost in the final compilation step to CPU code, which also involves CPU specific optimizations that make no sense for other CPUs, whereas Bitcode retains the original intent pretty well and only performs optimization that all CPUs will benefit from.
Having the Bitcode of all apps allowed Apple to re-compile that Bitcode for a specific CPU, either to make an App compatible with a different kind of CPU or an entirely different architecture or just to benefit from better optimizations of newer compiler versions. E.g. if Apple had tomorrow shiped an iPhone that uses a RISC-V instead of an ARM CPU, all apps with Bitcode could have been re-compiled to RISC-V and would natively support that new CPU architecture despite the author of the app having never even heard of RISC-V.
I think that was the idea why Apple wanted all Apps in Bitcode format. But that approach had issues to begin with. One issue is that Bitcode is not a frozen format, LLVM updates it with every release and they do not guarantee full backward compatibility. Bitcode has never been intended to be a stable representation for permanent storage or archival. Another problem is that you cannot use assembly code as no Bitcode is emitted for assembly code. Also you cannot use pre-built third party libraries that come without Bitcode.
And last but not least: AFAIK Apple has never used any of the Bitcode advantages so far. Despite requiring all apps to contain Bitcode in the past, the apps also had to contain pre-build fat binaries for all supported CPUs and Apple would always only just ship that pre-build code. E.g. for iPhones you used to once have a 32 Bit ARMv7 and a 64 Bit ARM64 version, as well as the Bitcode and during app thinning, Apple would remove either the 32 Bit or the 64 Bit version, as well as the Bitcode, and then ship whats left over. Fine, but they could have done so also if no Bitcode was there. Bitcode is not required to thin out architectures of a fat binary!
Bitcode would be required to re-build for a different architecture but Apple has never done that. No 32 Bit app magically became 64 bit by Apple re-compiling the Bitcode. And no 64 bit only app was magically available for 32 bit systems as Apple re-compiled the Bitcode on demand. As a developer, I can assure you, the iOS App Store always delivered exactly the binary code that you have built and signed yourself and never any code that Apple has themselves created from the Bitcode, so nothing was server side optimized. Even when Apple switched from Intel to M1, no macOS app magically got converted to native ARM, despite that would have been possible for all x86 apps in the app store for that Apple had the Bitcode. Instead Apple still shipped the x86 version and let it run in Rosetta 2.
So imposing various disadvantages onto developers by forcing all code to be available as Bitcode and then not using any of the advantages Bitcode would give you kinda makes the whole thing pointless. And now that all platforms migrated to ARM64 and in a couple of years there won't even be fat binaries anymore (once x86 support for Mac has been dropped), what's the point of continuing with that stuff? I guess Apple took the chance to bury that idea once and for all. Even if they one day add RISC-V to their platforms, developers can still ship fat binaries containing ARM64 and RISC-V code at the same time. This concept works well enough, is way simpler, and has no downsides other than "bigger binaries" and that's something server side app thinning can fix, as during download only the code for the current platform needs to be included.
Apple Watch Series 3 was the last device to not support 64-bit. (i.e. i386 or armv7)
Apple has now stopped supporting the Apple Watch Series 3. [1] They would have been happy to drop support for bitcode.
[1] https://www.xda-developers.com/watchos-9-not-coming-apple-watch-series-3
xcode remove armv7/armv7s/i386 targets support. bitcode use for build different cpu targets. but now all devices might be arm64 . and now no more developer use this tech. so deprecated maybe a wise choice
Bitcode was always pointless, as even if you compiled bitcode to another architecture, there's high chance it won't actually work because the ABI is different. For example, when you compile C program, the libc headers actually are different for every architecture. I'm glad they are finally getting rid of it, as it caused more problems than solved. At most they could've done is re-optimize the binary for the same architecture, or similar enough architecture. There is also the problem of unwanted symbols leaking in bitcode builds, so you either have to rename/obfuscate those or get hit by collisions (big problem if you are a library/framework vendor).

Swift, how to make a framework for both arm and i386

I need to use a framework for and old obj-c project. The problem is, before I used to compile again and again code in a binary and then lipo it. Now with the swift architecture, it generate some Apple specific files, and of course, it is too hard for (or unintuitive to) Xcode to generate the framework for the architecture I want.
I googled how to do it, but to my surprise, I just found old post who talk about the old way with the standard binary output.
My question is how to simply generate a swift framework for the wanted arch. For the moment the only solution to my problem is to compile a framework for the simulator and another one for the phone. But I have no idea how to fat it. Or if it is the right approach.
I can't believe there is no way to ask Xcode to build it. But since we are talking about Xcode...

BitCode and unexported_symbols_list cannot be used together?

For an iOS Framework, I'd like to be able to hide the symbols inside of it, in order to prevent collisions when the framework, and the application that uses the framework, include different versions of the same 3rd party library.
Until now this has been possible by specifying -unexported_symbols_list in the options.
However, in Xcode 7 Beta 5, the following error message...
-unexported_symbols_list and -bitcode_bundle (Xcode
setting ENABLE_BITCODE=YES) cannot be used together
...occurs when trying to compile the BitCode version of the framework.
So, I have three (3) questions:
1) Is there a straightforward way to hide your symbols when compiling BitCode?
2) If not, is there anything else that can be done to keep the Framework's 3rd-party libraries from colliding with the Application's 3rd-party libraries when they are the same lib, but different versions?
3) Why can't -unexported_symbols_list and -bitcode_bundle be used together?
Partial answer (since I am also looking for the complete answer)
When using -exported_symbols_list or -unexported_symbols_list in the options, the various functions symbols are turned into local symbols.
For some reason, the linker is unable use bitcode option with local symbols.
Hence the two cannot be used together.
Also note, strip keyword which is used for symbol hiding can hide global symbols but the library has to be dynamic.
This is an option I haven't explored completely yet. If a dynamic library works for you, then you should explore it.
If this helps someone get a different perspective as to how to resolve this issue, that would be awesome.

Cross-compiling FontForge to iOS

I am trying to cross compile FontForge's libs and binaries to armv7 but I seem to be running into several major issues.
Firstly, I am using SDK7.0 on Xcode 5.0.1.
On my first configure, I got several "error: cannot check for X while cross compiling"
Once I bypassed this, I realized the program was looking for Carbon (deprecated in the iOS SDK files for a long time now.)
Once I moved the old SDK files over, a TON of files were giving errors about unknown types (from _types.h, and all of its associated headers) most notable ssize and off_t. Even after tinkering with these, I eventually get an "unsupported architecture" being reported from the sdk.
Here is my configure command:
./configure '--with-programs=yes' '--without-spiro' '--with-x=no' '--enable-static' '--with-cairo=no' '--without-python' '--with-pango=no' '--prefix=/Users/thebertolet/fontforge-ios' '--host=arm-apple-darwin' 'CC=/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/usr/bin/gcc' 'CFLAGS=-mthumb -arch armv7 -miphoneos-version-min=5.1 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS7.0.sdk/ -I/Users/thebertolet/freetype-ios/include/freetype2/ -I/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS7.0.sdk/usr/include/libxml2/' 'LDFLAGS=-mthumb -arch armv7 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS7.0.sdk/ -miphoneos-version-min=5.1'
Also of note: all of the required libs were required and reported to the program. I guess my specific problem is the program trying to locate files that either don't exist or are somewhat unusable to armv7.
Alternatively, does anyone know of a great way (other than fontforge) to break ttc and combine ttf's?
This is very likely to be totally impossible. You can likely use the fontforge internals behind a Cocoa Touch front end, but fontforge is an X11 app in its native form with a custom widget set, and the only X servers for iOS seem to be for remote use of desktop Unix apps. You may have better luck on Android.

Is there a way to compile for ARM rather than Thumb in Xcode 4?

Apple is recommending to compiling for ARM rather than thumb if there are many floating point operations going on. My whole app is almost one big floating point operation.
Here's what they say in the iOS App Development Workflow Guide:
iOS devices support two instruction sets, ARM and Thumb. Xcode uses
Thumb instructions by default because using Thumb typically reduces
code size by about 35 percent relative to ARM. Applications that have
extensive floating-point code might perform better if they use ARM
instructions rather than Thumb. You can turn off Thumb for your
application, so it compiles for ARM, by setting the Compile for Thumb
build setting to No.
However, I cannot find any "Compile for Thumb" setting in my build settings. Did they rename it? Or is this unavailable now with Xcode 4?
First, the advice to not compile for the Thumb instruction set in order to improve floating point performance only really applies to the old ARMv6 devices.
ARMv7 hardware (iPhone 3G S and newer, including all iPads) uses the more efficient Thumb-2 instruction set, which does not suffer the same sort of floating point slowdowns. For ARMv7 builds, it is recommended in almost all cases that you build for Thumb. I provide a little more detail about this in my answer here.
This might be why this compiler setting is no longer exposed as a common option, because ARMv7 devices are the vast majority of iOS devices out there.
If you want to do this for just your ARMv6 builds, you can go to your build settings and mouse over the "Other C Flags" option. Click on the little plus button that appears to the right of this option and add a condition for the ARMv6 architecture. Do this again to create one for the ARMv7 architecture. Under the ARMv6 architecture, add the extra compiler flag of -mno-thumb (as Kevin suggests).
You should end up with something that looks like the following:
I do this in one of my applications, because I did see a performance boost on the older ARMv6 devices with that. However, another of my applications was slower when not building for Thumb on ARMv6, so you'll want to profile this first.
Additionally, there is currently a bug in the LLVM Compiler 3.0 that ships with Xcode 4.2 (which has since been fixed in 4.2.1, from what I hear) where floating point calculations are compiled wrong under Thumb for ARMv6. If you're using that particular version of Xcode, you'll need to do this for proper behavior on the older devices.
I don't know whether or not "Compile for Thumb" is supposed to exist in Xcode 4, but you can always add -mno-thumb to the Other C Flags build setting.
Regarding your original question: I have noticed that "Compile for Thumb" (under the section "Code Generation" of your "Project Build Settings") in Xcode 4.2.1 is only available if you are using LLVM GCC 4.2 (if set in "Compiler for C/C++/Objective-C")!
If compiling with Apple LLVM 3.0 then you will find no "Compile for Thumb" option. But - as Brad already said - you can still change the "Other C Flags" option to turn off Thumb mode.
Another interesting point: I am using the sqlite amalgamation source in my project (i need fts - full text search) and since compiling with LLVM 3.0 I had strange and rather random crashes on armv6 devices whenever accessing the database: as it turns out that was because of Thumb mode not disabled when compiling for armv6 devices.

Resources