Illegal instruction error with openssl - macos

Due to OSX having out of date openssl versions, i need to bundle more up to date copies of libssl and libcrypto with my application.
The bundled versions i distribute do appear to work on very recent systems (My own system and the system i built these libraries on is a 2015 MBP) - but on some other systems I get an 'illegal instruction' error using those bundled libraries.
My questions are:
(1) Are the illegal instructions happening because an advanced instruction (such as AVX-512) are being used by the binary, and this instructions doesn't exist on some systems?
(2) How do i build versions of libssl and libcrypto that can be bundled and used by the vast majority of relatively recent apple systems? (without causing illegal instructions..)

(1) Are the illegal instructions happening because an advanced
instruction (such as AVX-512) are being used by the binary, and this
instructions doesn't exist on some systems?
It depends, and you probably need to show the code that's causing them. In the past OpenSSL used CPU feature probes to see what was available on all (nearly all?) platforms. Also see questions like SSL_library_init cause SIGILL when running under gdb.
In the latest sources OpenSSL does not perform CPU feature probes on Apple platforms because a SIGILL trashes memory. It is some sort of Apple bug and it affects Botan, Crypto++, OpenSSL and others probing the cpu. (All the listed libraries moved away from Apple feature probes). That's a recent change, however. Also see OpenSSL PR 3108, SIGILL-free processor capabilities detection on MacOS X.
(2) How do i build versions of libssl and libcrypto that can be
bundled and used by the vast majority of relatively recent apple
systems?
If you are not doing so, use the latest OpenSSL. That should avoid the cpu feature probes on Apple platforms.
The library also uses -force_cpusubtype_ALL, so the compile should target the least capable machine in a class of cpu's. That should be enough to avoid instructions not available on later cpu's.
If the project is using AVX-512, then it's use is certainly guarded at runtime. My guess is the guard likely checks the result of CPUID. We would need to see the code in question that is using AVX-512 instructions and causing the SIGILL to say more. But like I said, it is only a guess until we see the code.

Related

Replace static symbols in MacOS Mach-O binary with external symbols

I have the following scenario:
A proprietary MacOS game that statically links a graphics framework MoltenVK into its main Mach-O x86-64 binary.
The version of MoltenVK linked in is very old.
I have a newer version of MoltenVK in the form of a .dylib (along with newer versions of the SPIRV compiler, libVulkan, etc. if they are needed, also in dylib form).
The older and newer version of MoltenVK are ABI compatible, meaning the exported symbol names and function signatures should be identical from the old to the new version of MoltenVK.
And the root cause of this journey into MacOS linkage:
The game does not run correctly on my version of macOS (10.15 Catalina Beta 3). I have isolated the problem to MoltenVK due to a crash backtrace.
I want to test whether updating MoltenVK will solve the problem, both as a temporary workaround and to help the developers isolate the problem.
Is it possible to force the binary to use a version of the symbols defined in a dynamically loaded .dylib instead of the version defined within the binary itself? I want to patch all symbols available in each of the .dylibs I have, because it would probably break if I only patched some symbols but not others (presumably MoltenVK only works if the code of each symbol in the framework is from the same version of MoltenVK).
Note: I am unable to recompile the main Mach-O binary of the game because I do not have source code. I am willing to bypass security safeguards on my local system to do this if it is possible at all; I accept the risk of doing dangerous things while running a Beta (non-production) OS anyway.
I'd prefer if answers and comments focus on the technical solution to the question asked, but if further justification is needed, I am trying to isolate this problem as quickly as possible to give the game's developers as much time as possible to fix it before the final release of macOS 10.15. If I stay silent, the problem has a chance that it won't be detected; then folks will upgrade to the final macOS 10.15 and notice the game doesn't work. That's not fun for anyone, because then we have to either stay on Mojave and wait for the game developer to update their game, or go without the game for possibly weeks or months.
Static linking implies the library gets effectively baked into your final executable binary. So there's no easy technical way of hooking the calls and redirecting them somewhere else (like DYLD_INTERPOSE or DYLD_INSERT_LIBRARIES allows for external dylibs).
Patching the binary would require going through each MoltenVK call the game makes and doing quite cumbersome post processing.
By post processing I mean either:
writing a dyld call using dlopen & dlsym tandem. You would still
need dlopen & dlsym symbols already used in the binary (they're part of
libSystem aka C std lib, but you still need the dedicated dyld opcodes
to be able to actually use them). Eventually you'd need to put the assembly opcodes somewhere in binary to make everything work. This will be quite hard.
firing lldb debugger, preparing the dlsym addresses to call by hand and patching the binary on the fly for each call (you'd probably need write permissions in __TEXT segment to do it, but that's the easy part). If you know what you're doing this is probably the most feasible approach. The main drawback is it's volatile, if you break something you'd start from scratch.
Add a LC_LOAD_DYLIB command to the binary and dyld opcodes referenced by LC_DYLD_INFO_ONLY , that would be super hard
In any case your best friends are Hopper dissassembler and MachOView to inspect the binary.
Elementary knowledge of x86 (and/or x86-64) assembly is a must to follow. I think playing with the original source code could be a way more viable option.

Compiler output on different OS Versions

As far as I understand(Correct me if i'm wrong), the output of a compiler depends on the Architecture version used, Compiler, and operating system.
Lets say Im using ubuntu release 16.04 x84-64 and compiling a c file with gcc version 5.4(or any other mix of OS,arch,compiler for the example) .
As I understood it until now, if I were to compile the same c file but with a different ubuntu release, with the same arch and compiler version it should have produced the same assembly code.
After a few tries I have got the impression that this is incorrect, how is this possible?
Does the output of a compiler depend on the release of the specific OS?
One of the examples is compiling https://github.com/tbuktu/libntru on 2 different ubuntu versions and receiving different assembly.
The different OS's may have different versions of the default libraries installed (which get linked into your final application). Thus the end result may be slightly different.
If you are just doing a few ubuntu versions the odds of differences goes down as the overall architecture differences may not be reflected either in your test or may not change on the same os family with the same compiler family for long periods of time. Where you are more likely to see differences in a test like that is as you get older versions of the same distro, newer/newest versions of the compiler are not ported/supported directly as an apt-get. maybe you can get them to work by hand building but gcc in particular is really bad about that their code only builds with relatively recent prior or following versions get too far apart and gcc cant build gcc. What I would first expect to see is strictly due to gcc version differences you start to see differences in the compiler.
A better test is take a simple .c file and build for windows any version (using the same version of gcc built for that system) and ubuntu/linux any version. Should more quickly see differences.
Two different compilers should show differences for reasonably sized projects, or knowledge based targeted small code samples, llvm/clang vs gcc for example. Different versions of the same compiler or compiler family will somewhat by definition show differences over time, does 6.x vs 6.x+1 gcc show differences well yes if you know where to look but often not, but gcc 3.x vs gcc 7.x should and then depending on the test you can narrow in from there.
You have compiler to compiler differences on the same os and system that are expected to show differences.
You have various reasons why system to system differences with the same compiler will show differences.
And then combinations of the above would naturally also show differences.
The bigger question is why do you care, the educational information is that you shouldn't expect the same C source code to build the same way if you change the compiler, compiler settings, or operating system. It can have anywhere from no differences to huge differences based on any of the above. Starting quite simply with optimization and other tuning settings and going from there.

Alternative to Newlib?

I'm an embedded software engineer working with IA-32 type processors. We are looking for a compiler tool chain - preferable free.
We used to use Mentor Graphics CodeBench Lite but it's no longer available.
We have looked at other GCC distributions but none of them have a bare metal implementation of glibc. None except newlib but we can't use it because of the GPL and LGPL licencing issues. We're an OEM and our customers (and us) have proprietary code.
Any suggestions welcome.
Sourcery's "lite" gpl tools are still available, it's just that Mentor likes to play hide-the-link.
If you want a lightweight C library with non-GPL licensing, you might look at Bionic from Android.
However, you concern may be mistaken. IANAL but most C library licenses have a linking exception of some sort which you may want to research with the help of your lawyers - their utility as system libraries would be extremely limited without.
And actually, a quick search of the newlib licensing page (which is complicated) seems to show that more of it is under BSD-style licenses than under GPL-style ones, though care would be needed to sort it all out.
Mentor may no longer be providing a Lite edition of the IA-32 bare-metal toolchain, but I'm pretty sure it's still supported in the commercial editions, and a basic license is not that expensive.
As Chris says, the Newlib licensing page is a bit complicated -- but the gist of it is that basically all of it that you need for a bare-metal system is BSD licensed; IIRC, the parts that are GPL-licensed are clearly-delineated system-specific pieces that reference things in the Linux kernel or the like (and thus have to be GPL-licensed), and those aren't included in the bare-metal builds. I think they're even all in one or two distinct directories that you can just delete. Obviously you should do the analysis for yourself, but that's the result you should expect to find.
A shortcut that may be useful: The download page for the most recent version of CodeBench Lite for IA-32 ELF that was produced is on this page. If you download the source tarball from there, you'll get the Newlib sources that were used to build that, and there's also a .sh file in the package indicating how it was configured and built. You'll note that in the documentation (licenses are in the back of the Getting Started Guide) the Newlib binaries are simply listed as BSD-licensed, so this should show you how Mentor got a compiled library that fits that licensing description.
(Disclaimer: I used to work for Mentor until recently.)

how to write cross-version/platform Linux kernel modules?

I'm new to programming Linux kernel modules, and many getting started guides on the topic include little information about how to build a kernel module which will run on many versions and CPU platforms of Linux. Most of the guides I've seen simply state things like, "Linux doesn't ensure any ABI/API compatibility between versions." However, other OSes do provide these guarantees for major versions, and the guides are mostly targeting 2.7 (which is a bit old now).
I was wondering if there is any kind of ABI/API compatibility now, or if there are any standard ways to deal with versioning other than isolating the kernel-dependent bits of my code into files with a ton of preprocessor directives. (Also, are there any standard preprocessor symbols I should be using in the second case?)
There isn't a stable ABI for the kernel and most likely never will be because it'd make Linux suck. The reasons for not having one are all pretty much documented in that link.
The best way to deal with this is to get your driver merged upstream where it'll be maintained by other kernel developers.
As to being cross-platform, that pretty much comes free with the Linux kernel as long as you only use the standard, platform-independent functions provided in the API.
Linux, the ying and the yang. Tangrs answer is good; it answers your question. However, there is the linux compat projects. See the backports wiki. Basically, there are libraries that provide shim functionality for newer Linux ABI's which you can use to link your code. The KERNEL_VERSION macro that Eugene notes is inspected in a compat.h, and appropriate compat-2.6.38.h, etc are included where each version has either macros and/or library functions to provide a forward API.
This lets the Linux Wifi group write code for the bleeding edge kernel, while still making it possible to compile on older kernel versions.
I guess this answers the question,
if there are any standard ways to deal with versioning?
The compat library is not a panacea, but at least it is there and under development.
Open source - There are many mutations. They all have a different plan.

How can I compile object code for the wrong system and cross compiling question?

Reference this question about compiling. I don't understand how my program for Mac can use the right -arch, compile with those -arch flags, the -arch flags be for the system I am on (a ppc64 g5), and still produce the wrong object code.
Also, if I used a cross compiler and was on Linux, produced 10.5 code for mac, how would this be any different than what I described above?
Background is that I have tried to compile various apache modules. They compile with the -arch ppc, ppc64, etc. I get no errors and I get my mod_whatever.so. But, apache will always complain that some symbol isn't found. Apparently, it has to do with what the compiler produces, even though the file type says it is for ppc, ppc64, i386, x_64 (universal binary) and seems to match all the other .so mods I have.
I guess I don't understand how it could compile for my system with no problem and then say my system can't use it. Maybe I do not understand what a compiler is actually giving me.
EDIT: All error messages and the complete process can be seen here.
Thank you.
Looking at the other thread and elsewhere and without a G5 or OSX Server installation, I can only make a few comments and suggestions but perhaps they will help.
It's generally not a good idea to be modifying the o/s vendor's installed software. Installing a new Apache module is less problematic than, say, overwriting an existing library but you're still at the mercy of the vendor in that a Software Update could delete your modifications and, beyond that you have to figure out how the vendor's version was built in the first place. A common practice in the OS X world is to avoid this by making a completely separate installation of an open source product, like Apache, using, for instance, MacPorts. That has its cons, too: to achieve a high-level of independence, MacPorts will often download and build a lot of dependent packages for things which are already in OS X but there's no harm in that other than some extra build cycles and disk space.
That said, it should be possible to build and install apache modules to supplement those supplied by Apple. Apple does publish the changes it makes to open source products here; you can drill down in the various versions there to find the apache directory which contains the source, Makefile and applied patches. That might be of help.
Make sure that the mod_*.so you build are truly 64-bit and don't depend on any non-64 bit libraries. Use otool -L mod_*.so to see the dynamic libraries that each references and then use file on those libraries to ensure they all have ppc64 variants.
Make sure you are using up-to-date developer tools (Xcode 3.1.3 is current).
While the developer tool chain uses many open source components, Apple has enhanced many of them and there are big differences in OS X's ABIs, universal binary support, dynamic libraries, etc. The bottom line is that cross-compilation of OS X-targeted object code on Linux (or any other non-OS X platform) is neither supported nor practical.

Resources