Wednesday, March 23, 2016

The Linker

<< Prev: The Working Subset       Next: Thin Air >>

Umm... No.

That is, yes, in the state I reached as of the last post, the code built. So I copied the kext to my test machine, held my breath, and loaded it. I was probably 70% expecting the machine to reboot, and happily it didn't to that. But the kext didn't load either:

kxld[AppleIntelWiFiMVM]: The following symbols are unresolved for this kext:
kxld[AppleIntelWiFiMVM]:     __Z10__iwl_infoP6devicePKcz
kxld[AppleIntelWiFiMVM]:     __Z9__iwl_errP6devicebbPKcz
Can't load kext AppleIntelWiFiMVM - link failed.
Failed to load executable for kext AppleIntelWiFiMVM.

That was a new one. Not a permissions error, not an ownership error, but a linker error. I first assumed I had left out an entry from the required frameworks in Info.plist, so I ran kextlibs again. But this time it had something new for me:

For x86_64:
    2 symbols not found in any library kext.

So then, not just a missing library. A little consultation of the man page suggested I add the -undef-symbols flag, and then I got more detail:

For x86_64:
    2 symbols not found in any library kext:
 __Z10__iwl_infoP6devicePKcz
 __Z9__iwl_errP6devicebbPKcz

Same as I got when I tried to load the kext. So I could have caught this before I even tried. Now, the question was, what did that mean?

I Googled for linker errors and what the symbol names meant. It looked like this was complaining about the functions __iwl_info and __iwl_err, which were declared (along with some friends) in iwl-debug.h:

void __iwl_err(struct device *dev, bool rfkill_prefix,
                bool only_trace, const char *fmt, ...) __printf(4, 5);
void __iwl_info(struct device *dev, const char *fmt, ...) __printf(2, 3);

Maybe the arguments were somehow wrong? I grepped the code for an implementation and didn't get any hits, but with "iwl" in the name it seemed unlikely they were supposed to come from an OS framework.

But on that last Google search, I did get a hit on "name mangling" for the C++ linker. It described how because C++ functions can be overloaded, every function gets a unique name, with those odd additional characters referring to the function parameters and etc. And it also said 'this is why you should add extern "C"'. Of course, I had removed 'extern "C"' because I had no idea what it was, and why do stuff that made no sense? Well, here's why, I guess.

I put 'extern "C"' back in, around the header files I was importing from the Linux driver source:
extern "C" {
#include "iwl-config.h"
#include "iwl-drv.h"
}

Then I built (no errors), and tried kextlib again:

For x86_64:
    2 symbols not found in any library kext:
 ___iwl_err
 ___iwl_info

Well, now I just felt like a script kiddie. I got an error, Googled it, and tried whatever it said without stopping to try to understand it. Surprise, surprise, it didn't work. It just changed the error, in a way that should have been obvious.

Instead of the C++ mangled name, I got the plain-C name in the error (with an extra underscore, but whatever). Because functions can't be overloaded in C, all the extra stuff to distinguish between overloaded functions isn't necessary, and the name itself is enough. So that's what I got.

Of course, since I was compiling all the Linux driver code together, either the function was there or it wasn't. If it was there, I wouldn't get errors either way. If it wasn't there, changing the naming strategy wouldn't make it suddenly appear. And I had already grepped the code and determined that it wasn't there.

I don't know what I was thinking. I needed to find the missing code.

<< Prev: The Working Subset       Next: Thin Air >>

No comments:

Post a Comment