Compiling a kernel module on 32-Bit Linux kernel results in
"__udivdi3" [mymodule.ko] undefined!
"__umoddi3" [mymodule.ko] undefined!
Everything is fine on 64-bit systems. As far as I know, the reason for this is that 64-bit integer division and modulo are not supported inside a 32-bit Linux kernel.
How can I find the code issuing the 64-bit operations. They are hard to find manually because I cannot easily check if an "/" is 32-bit wide or 64-bit wide. If "normal" functions are undefined, I can grep them, but this is not possible here. Is there another good way to search the references? Some kind of "machine code grep"?
The module consists of some thousand lines of code. I can really not check every line manually.
First, you can do 64 bit division by using the do_div macro. (note the prototype is uint32_t do_div(uint64_t dividend, uint32_t divisor) and that "dividend" may be evaluated multiple times.
{
unsigned long long int x = 6;
unsigned long int y = 4;
unsigned long int rem;
rem = do_div(x, y);
/* x now contains the result of x/y */
}
Additionally, you should be able to either find usage of long long int (or uint64_t) types in your code, or alternately, you can build your module with the -g flag and use objdump -S to get a source annotated disassembly.
note: this applies to 2.6 kernels, I have not checked usage for anything lower
Actually, 64-bit integer divison and modulo are supported within a 32-bit Linux kernel; however, you must use the correct macros to do so (which ones depend on your kernel version, since recently new better ones were created IIRC). The macros will do the correct thing in the most efficient way for whichever architecture you are compiling for.
The easiest way to find where they are being used is (as mentioned in #shodanex's answer) to generate the assembly code; IIRC, the way to do so is something like make directory/module.s (together with whatever parameters you already have to pass to make). The next easiest way is to disassemble the .o file (with something like objdump --disassemble). Both ways will give you the functions where the calls are being generated (and, if you know how to read assembly, a general idea of where within the function the division is taking place).
After compilation stage, you should be able to get some documented assembly, and see were those function are called. Try to mess with CFLAGS and add the -S flags.
Compilation should stop at the assembly stage. You can then grep for the offending function call in the assembly file.
Related
I'm working on an integrated SDK for powerpc microprocessor which has some kernel modules.
The kernel(3.12.37) and toolchain(powerpc gcc 4.9.2) provided by Yocto project.
When i make SDK in native machine(x86_64bit Ubuntu 14) by mentioned toolchain and kernel source, I see these warnings during compilation of kernel module:
WARNING: "__udivdi3" [modules/foo.ko] undefined!
WARNING: "__umoddi3" [modules/foo.ko] undefined!
These warnings appear when i insmod kernel module.
I realized that this functions are used for 64bit operations and libgcc has these functions.
this is compilation flags:
"-Wall -Werror -gdwarf-3 -Wframe-larger-than=16384 -mcpu=powerpc"
also, i gave this flag to compile statically "-Wl,-static-libgcc" and didn't work.
my questions are:
why does linker is going to use 64 bit functions which powerpc arch($KERNELDIR/arch/powerpc) doesn't have? (Linker knows that ARCH is powerpc)
"in the kernel space, compiler does not use libgcc or other userspace functions and libs", is this right? if not, please explain to me.
and finally how can i solve this problem?
Thanks.
The compiler will generate code that calls functions in libgcc in order to perform certain operations. In this case, they are being called to perform the regular division (/) and modulo (%) operations on 64-bit quantities.
Correct, the kernel doesn't use libgcc. Kernel code needs to be written to prevent the compiler making calls to functions in libgcc (in this case for division and module operations on 64-bit quantities).
Any code that does division or modulo operations on 64-bit quantities (apart from the native signed and unsigned long types on 64-bit architectures) needs to be rewritten to use special 64-bit division functions and macros provided by the Linux kernel instead of the regular / and % operators.
In the old days, this was done by calling the do_div(n,base) macro defined by #include <asm/div64.h>, but that has an unusual, non-functionlike interface in that it modifies the caller's n parameter in place to become the dividend n / base in addition to returning the remainder n % base.
For new code, it is better to avoid the old do_div(n,base) macro and make use of the functions declared by #include <linux/math64.h>. There are various functions defined there for signed and unsigned arguments and for both 32-bit and 64-bit remainders. You need to pick the most appropriate function to match the requirements of your code.
I'm building musl-libc statically for a project on an aarch64 (ARM 64-bit) platform. I'd like to avoid any soft floating point libraries such as GCC's soft float library routines. However, these are still appearing in the library archives even when I use -mfloat-abi=hard. As best I can tell, this is because ARM 64-bit platforms define a long double to be 128 bits.
Is there any way to change this behavior? For example, could I force long double to be defined as the same size as a double? I know this is allowed by the C standard, but I'm unsure if there's any way to force Clang (I'm specifically using Clang for this) to compile with such a definition.
I ultimately found a solution, though I can't necessarily recommend it for everyone. It likely will induce bugs elsewhere, but it was good enough for what I needed. It also involves building Clang from scratch (thanks for the suggestion #Art!). Furthermore, the project I'm working on is using LLVM/Clang 3.7.1, so I make no claims for other versions.
As near as I can tell, the definition of a long double for the AArch64 target occurs in clang/lib/Basic/Targets.cpp:
...
MaxAtomicInlineWidth = 128;
MaxAtomicPromoteWidth = 128;
LongDoubleWidth = LongDoubleAlign = SuitableAlign = 128;
LongDoubleFormat = &llvm::APFloat::IEEEquad;
// {} in inline assembly are neon specifiers, not assembly variant
// specifiers.
...
By modifying the inner 2 lines, I removed any references to the soft-FP routines I mentioned in my question:
LongDoubleWidth = LongDoubleAlign = SuitableAlign = 64;
LongDoubleFormat = &llvm::APFloat::IEEEdouble;
My test programs -- SNU's version of the NASA Parallel Benchmarks -- still validate correctly, so I'm assuming I didn't break anything too badly. Still, this is a non-trivial modification -- I don't recommend it for most individuals (it could lead to breakage elsewhere).
I've had to do something similar before, fluffing around with types (particularly longs). Your best bet is to just manually replace the types by hand, as that's the simplest and most straightforward way to get what you want. You can try playing tricks with macros or massaging the compiler, but from my experience, you just create more problems than you solve, and it usually is a fragile solution that breaks later on.
Luckily, the sources you are working with look well maintained, and the changes you are looking for are rather blanket. You can knock this out quite simply. Assuming you are running a Unix-like system, you can run the following command from the base directory of musl:
$ grep -Rl 'long double' * | xargs -tn1 sed -i '' -e 's/long double/double/g'
This command:
Looks for the string long double in all files, recursively, and returns the filenames which contain that string.
Which gets passed into xargs, who invokes the sed command per each filename, printing as it goes along.
When sed runs, it modifies the file in-place, and substitutes long double with double.
When I tried out this command, it "just worked". I would peruse the diff more carefully to make sure it hit everything proper and didn't alter the behavior of the library.
I'm writing a bare metal application for an ARM device (no OS). I need 32-bit enums, so I compiled the application with the -fno-short-enums compiler flag. Without this flag, I get variable enums (and enforcing the size by adding an additional 0xFFFFFFFF value to each enum is not an option).
Now I get the following linker warning for every object:
c:/gcc-arm-none-eabi/bin/../lib/gcc/arm-none-eabi/6.2.1/../../../../arm-none-eabi/bin/ld.exe: warning: ./src/test.o uses 32-bit enums yet the output is to use variable-size enums; use of enum values across objects may fail
It's just a warning, no error. But what does it mean exactly? How can I specify the "output"?
I tried to recompile the newlib with the above flag to ensure that all objects use the same enum size, but I'm still getting the warning. Is there something I missed?
After a while I got it working.
I rebuilt the whole toolchain including the compiler with this flag. Here is the way I did:
Get the toolchain source from https://developer.arm.com/open-source/gnu-toolchain/gnu-rm/downloads
Add 3 lines to some sections of the buildscript build-toolchain.sh:
saveenv
saveenvvar CFLAGS_FOR_TARGET '-fno-short-enums'
[...build commands...]
restoreenv
Modified sections:
Task [III-1] /$HOST_NATIVE/gcc-first/
Task [III-2] /$HOST_NATIVE/newlib/
Task [III-4] /$HOST_NATIVE/gcc-final/
Task [IV-3] /$HOST_MINGW/gcc-final/
I Skipped building newlib-nano and gcc-size-libstdcxx.
Run the modified Scripts build-prerequisites.sh and build-toolchain.sh to build everything.
After that, the compiler is using the large-enum-mode and the linker is fine with my objects. But now, I get the opposit warning for some objects of the newlib (lib_a-mbtowc_r.o, lib_a-svfiprintf.o, lib_a-svfprintf.o, lib_a-vfprintf.o and lib_a-vfiprintf.o):
c:/gcc-arm-none-eabi/bin/../lib/gcc/arm-none-eabi/6.2.1/../../../../arm-none-eabi/bin/ld.exe: warning: c:/gcc-arm-none-eabi/bin/../lib/gcc/arm-none-eabi/6.2.1/../../../../arm-none-eabi/lib\libc.a(lib_a-mbtowc_r.o) uses variable-size enums yet the output is to use 32-bit enums; use of enum values across objects may fail
I looked into the makefiles of these object and they are explicitly set to variable-size-enums, sadly. The only "solution" for this, was to add a linker flag to mute this warning:
-Xlinker -no-enum-size-warning
That's it.
I ran into the same problem since I have a lot of enums in data structures that map to hardware. In my case, my fix is to do the following:
struct x {
union {
enum my_enum eval;
uint32_t :32;
};
uint32_t some_val;
...
}
This works and is transparent outside of the data structure, though it means that every time an enum is used in a data structure this union wrapper is required. I suppose a macro might be possible as well. I agree it's a pain. Every other 32-bit and 64-bit environment I've worked with treats enums as 32-bits and don't get me started with the decision to make a uint32_t an unsigned long in the ARM embedded ABI (every other one I've worked with it's an unsigned int, making portability with 64-bit ABIs easy).
I have a partial answer as I had the same question. The problem is a warning message from the linker that will appear with the use of -fno-short-enums. The message indicates that the target object is not compatible. So I spent a world of time looking for now to change the target to be compatible.
But that wasn't the problem. The gcc compiler will build 32-bit enums by default!! So this option is no necessary unless you DONT want 32-bit enums. However, if you do specify the -fno-short-enums you will receive the warning message. Unfortunately I don't know why.
So the bottom line is that the no-short-enums flag is not necessary to achieve 32-bit enums. If you do specify it then you will receive the warning message.
I am running into integer overflow using the standard ftell and fseek options inside of G++, but I guess I was mistaken because it seems that ftell64 and fseek64 are not available. I have been searching and many websites seem to reference using lseek with the off64_t datatype, but I have not found any examples referencing something equal to fseek. Right now the files that I am reading in are 16GB+ CSV files with the expectation of at least double that.
Without any external libraries what is the most straightforward method for achieving a similar structure as with the fseek/ftell pair? My application right now works using the standard GCC/G++ libraries for 4.x.
fseek64 is a C function. To make it available you'll have to define _FILE_OFFSET_BITS=64 before including the system headers That will more or less define fseek to be actually fseek64. Or do it in the compiler arguments e.g.
gcc -D_FILE_OFFSET_BITS=64 ....
http://www.suse.de/~aj/linux_lfs.html has a great overviw of large file support on linux:
Compile your programs with "gcc -D_FILE_OFFSET_BITS=64". This forces all file access calls to use the 64 bit variants. Several types change also, e.g. off_t becomes off64_t. It's therefore important to always use the correct types and to not use e.g. int instead of off_t. For portability with other platforms you should use getconf LFS_CFLAGS which will return -D_FILE_OFFSET_BITS=64 on Linux platforms but might return something else on e.g. Solaris. For linking, you should use the link flags that are reported via getconf LFS_LDFLAGS. On Linux systems, you do not need special link flags.
Define _LARGEFILE_SOURCE and _LARGEFILE64_SOURCE. With these defines you can use the LFS functions like open64 directly.
Use the O_LARGEFILE flag with open to operate on large files.
If you want to stick to ISO C standard interfaces, use fgetpos() and fsetpos(). However, these functions are only useful for saving a file position and going back to the same position later. They represent the position using the type fpos_t, which is not required to be an integer data type. For example, on a record-based system it could be a struct containing a record number and offset within the record. This may be too limiting.
POSIX defines the functions ftello() and fseeko(), which represent the position using the off_t type. This is required to be an integer type, and the value is a byte offset from the beginning of the file. You can perform arithmetic on it, and can use fseeko() to perform relative seeks. This will work on Linux and other POSIX systems.
In addition, compile with -D_FILE_OFFSET_BITS=64 (Linux/Solaris). This will define off_t to be a 64-bit type (i.e. off64_t) instead of long, and will redefine the functions that use file offsets to be the versions that take 64-bit offsets. This is the default when you are compiling for 64-bit, so is not needed in that case.
fseek64() isn't standard, the compiler docs should tell you where to find it.
Have you tried fgetpos and fsetpos? They're designed for large files and the implementation typically uses a 64-bit type as the base for fpos_t.
Have you tried fseeko() with the _FILE_OFFSET_BITS preprocessor symbol set to 64?
This will give you an fseek()-like interface but with an offset parameter of type off_t instead of long. Setting _FILE_OFFSET_BITS=64 will make off_t a 64-bit type.
The same for goes for ftello().
Use fsetpos(3) and fgetpos(3). They use the fpos_t datatype , which I believe is guaranteed to be able to hold at least 64 bits.
I seem to be having a problem with a macro that I have defined in a C program.
I compile this software and run it sucessfully with the MIPS compiler.
It builds OK but throws the error "Segmentation fault" at runtime when using icc.
I compiled both of these on 64 bit architectures (MIPS on SGI, with -64 flag and icc on an intel platform).
Is there some magic switch I need to use to make this work correctly on both system? I turned on warnings for the intel compiler, and EVERY one of the places in my program where a macro is invoked throws a warning. Usually something along the lines of mismatched types on the macro's parameters (int to char *) or some such thing.
Here is the offending macro
#define DEBUG_ENTER(name) {tdepth++;
if(tnames[tdepth] == NULL) tnames[tdepth] = memalign(8, sizeof(char)*MAXLEN);
strcopy(tnames[tdepth],name);
FU_DEBUG("Entering \n");}
This basically is used for debugging - printing to a log file with a set number of tabs in based on how many function calls there are. (tdepth = tab depth)
I did some checking around in man pages. it seems like memalign is only supported on IRIX. This may be my problem. I am going to track it down.
This might have to do with the system's "endianness." Looking here it seems that MIPS has switchable endianness. I'm not sure if you are using the correct endianness already, but if you aren't, you will DEFINATELY have problems.
This might be a byte order issue. MIPS can be big endian but intel is little endian.
It sounds like the array tnames is an array of int. If you're assigning pointers to it, it should be an array of a pointer type - in this case probably char * is appropriate.
(Also, strcopy() isn't a standard function - are you sure you don't mean strcpy()?)