Tuesday, October 22, 2019

Linux: Solution Unknown symbol “__aeabi_ldivmod”

You might notice that while compiling for your 32bit platforms your kernel module compiles. However, when you are inserting it we see a failure (either at boot or while explicitly doing an insmod or modprobe).

The reason this "Unknown symbol in module" is seen is because you are in some instruction trying to do a 64bit division in the Linux kernel for an ARM platform (32bit).

Why is the symbol missing though if everything compiles. The compiler wants to do the 64bit div with slow library functions which Linux does not implement. So when the code is run, the symbol (__aeabi_ldivmod) is not found.

The solution to this problem is to use do_div() while including <asm/div64.h>.

do_div has its own side effects and if you search enough you will find that there are multiple cases where "do_div is considered harmful". But for now, if you are not doing this division frequently, it might be a good idea to just use do_div() and get your work done.

From div64.h:

// the semantics of do_div() macros are:
uint32_t do_div(uint64_t *n, uint32_t base) {
  uint32_t remainder = *n % base;
  *n = *n / base;
  return remainder;

Simple example:

void  _do_64bit_div(int64_t *operand_ptr, int64_t base)
    // do_div for older platforms.
    uint64_t operand, remainder;
    operand = *operand_ptr;
    remainder = do_div(operand, base);
    *operand_ptr = operand;
    // Direct div for newer platforms.
    unit64_t result;
    *operand_ptr = *operand_ptr / base;

Just call this function with the parameters (&operand / operator) and get the result in operand.