1

If I have a function that is in one Linux Kernel, but not others, how would I go about using the same source code to compile for different kernels?

For example, if functionA is an old function which both kernels support, but functionB is only supported in newer kernels and I want to use functionB when I can for the systems that support it, how do I have only one piece of code?

Is there a way to dynamically try to load the function by name functionB and then if it fails, go ahead and dynamically load functionA?

Matthew
  • 3,886
  • 7
  • 47
  • 84

2 Answers2

1

You can only compile a module for a specific kernel version, other kernels will refuse to load it. You will therefore need to compile it once for every kernel version that you need to support, thus you can simply use compile-time macros such as LINUX_VERSION_CODE and KERNEL_VERSION():

static void do_something(void)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,0,0)
        functionB();
#else
        functionA();
#endif
}

Furthermore, make sure to check whether functionB() is behind some CONFIG_ option: in such case you also want to wrap everything in a #ifdef CONFIG_THAT_ENABLES_FUNCTION_B.

Marco Bonelli
  • 63,369
  • 21
  • 118
  • 128
  • What about dlsym? Why won't that work? – Matthew Jul 20 '22 at 18:31
  • @Matthew you cannot possibly use `dlsym()`. You are writing a *kernel* module, you do not have access to any userspace library. The most you can do if you really wish to dynamically resolve symbols is use [some of these hacks](https://stackoverflow.com/q/70930059), but you should avoid it. – Marco Bonelli Jul 20 '22 at 18:46
0

The tact of Linux backports it to do this,

#if LINUX_VERSION_CODE < KERNEL_VERSION(5,0,0)
/* Declare stub or translation of functionB() to functonA(). */
void functionB(void)
{
      functionA();
}
#endif

This can be more complex when the parameters to the routines take an infra-structure pointer that is not present in the newer kernels.

Backports is useful to take newer drivers to older kernel releases. It is less useful to take infra-structure (a new filesystem, etc.) to an older kernel.

The backport method replaces header files and has a few stub methods. For example, firmware.h has code such as,

#if LINUX_VERSION_IS_LESS(4,18,0)
#define firmware_request_nowarn(fw, name, device) request_firmware(fw, name, device)
#endif

#if LINUX_VERSION_IS_LESS(4,17,0)
#define firmware_request_cache LINUX_BACKPORT(firmware_request_cache)
static inline int firmware_request_cache(struct device *device, const char *name)
{
    return 0;
}
#endif

The mechanics let the primary driver remain untouched, so you can still use git to maintain consistency as it updates. If you are authoring a driver yourself, this might not matter.

See: Backports wiki

artless noise
  • 21,212
  • 6
  • 68
  • 105