Cross Compilation kernel and kernel modules problems - linux-kernel

I've been spending the last week trying to reach one objective that i still haven't reached.
My final goal would be to cross-compile for some architectures (in this question i will take as an example only one which is MIPSLE) a couple of kernel modules that i'd like to use on my home router, by loading them at runtime with INSMOD.
My router is missing of some iptables functionalities and that's why during the kernel compilation, who compiled it decided to get rid of them.
The current kernel version is quite obsolete: Linux version 2.6.36+ by doing /proc/version
I read a lot of documentations and still i'm not sure if it possible to compile just some modules or if it's necessary to compile the whole kernel everytime (some people said that for some modules is possible and for others with more dependencies is not, is that true ?).
In this case the kernel modules i need are located in /net/ipv4/netfilter/
Since i didn't get how to compile just kernel modules in a standalone way, i decided to cross-compile the entire kernel and take the modules i needed.
So i've downloaded the proper toolchain (uclibc mipsle toolchain) and i was successfull in compiling a simple working hello world that i've executed on my router where i have an ssh shell.
So i'm sure that the toolchain i'm using it's the correct one.
Now, since i wanted to test with a recent version of kernel i've downloaded the latest ubuntu 20 with correspective kernel and i've successfully compiled it and the kernel modules that i was speaking about before, were there inside the netfilter folder in .ko format , PERFECT !
I clearly used the /boot/config_file of my host machine as .config
So, after I've downloaded the exact same kernel version that my router has (2.6.36) and I've tried to compile it with a .config file that i've found on GitHub related to a mipsle device with my same kernel version.
Here i think we have the first problem, I tried to find the .config of my device in order to have a smooth configuration but i don't have the /boot folder and in also was not in /proc.
How can i get it?
Anyway, I've used it and the compilation didn't look like failed, but these are the last lines:
CC drivers/usb/storage/usual-tables.o
LD drivers/usb/storage/usb-storage.o
LD drivers/usb/storage/built-in.o
LD drivers/usb/built-in.o
LD drivers/video/built-in.o
LD drivers/built-in.o
LD vmlinux.o
MODPOST vmlinux.o
GEN .version
CHK include/generated/compile.h
UPD include/generated/compile.h
CC init/version.o
LD init/built-in.o
LD .tmp_vmlinux1
KSYM .tmp_kallsyms1.S
AS .tmp_kallsyms1.o
LD .tmp_vmlinux2
KSYM .tmp_kallsyms2.S
AS .tmp_kallsyms2.o
LD vmlinux
SYSMAP System.map
SYSMAP .tmp_System.map
OBJCOPY arch/mips/boot/vmlinux.bin
Building modules, stage 2.
MODPOST 1 modules
CC drivers/scsi/scsi_wait_scan.mod.o
LD [M] drivers/scsi/scsi_wait_scan.ko
It says "1 Modules" but I don't understand why and inside the /net/ipv4/netfilter folder now I have .o files instead of .ko like if they've not been linked.
Since i was getting mad and I didn't know what to try more after several attempts, I've decided to use the config file of my host machine (ubuntu 2020, kernel 5.4.0.26) and the compilation was successful, with .ko files inside the folder.
The problem was that obviously they were modules compiled for x86-64 instead of MIPS and other clear "problems" related to the configuration of my x86-64 machine.
So, what i think now is that the .config I've found could be broken somehow, I also tried to disable, by adding a comment, that SCSI module, but nothing, I always get the same stuff.
And obviously between each try I always did a make distclean and make clean.
What do you suggest me to do? I won't even post all the references i've read about this stuff because i could sigsegv StackOverflow's server with that amount of data.
Thanks to everyone and sorry for the wall-post.

i'm not sure if it possible to compile just some modules or if it's necessary to compile the whole kernel every time
Well, you can compile just single modules, but compiling a module requires the kernel to be already built. Once you do that one time though, you should be able to compile other modules singularly. That is, of course, if you do not wish to embed them in the kernel itself (CONFIG_XXX=y instead of CONFIG_XXX=m). You should be able to compile only the module you want like this (assuming /path/to/linux is the directory where your already built kernel source resides):
$ cd /path/to/linux
$ cd path/to/module/folder
$ make -C /path/to/linux M=$(pwd) modules
I tried to find the .config of my device in order to have a smooth configuration but i don't have the /boot folder and it also was not in /proc. How can i get it?
Where did you look precisely? The presence of /proc/config.gz depends on CONFIG_IKCONFIG_PROC (see also here). If you cannot find the file then it's most likely because that configuration option was disabled when the kernel was built. You may try look under /boot (as you already did), or under /lib/modules/$(uname -r)/build/.config, but unfortunately there's not much else to do otherwise.
I've seen people suggest trying to run modprobe configs and then check /proc/config.gz, but that seems strange since as far as I know the kernel config shouldn't be configurable to be available as a loadable module.
What do you suggest me to do?
Well, the most important thing you want right now is to find the configuration file for your router (or a compatible one). If you cannot find that, it will be pretty hard to get everything right. You might want to search for OpenWRT versions available for your router (if any), or really anywhere else on the internet as long as you can find a suitable configuration. Include your router brand and/or model in your searches. StackOverflow can't really help you that much about this though.
You can try cross-compiling a 5.4 kernel with default config plus the module you want. For example, assuming you have the right cross-compilation toolchain ready:
cd /path/to/linux
make ARCH=mips CROSS_COMPILE=your-cross-toolchain-prefix- defconfig
make ARCH=mips CROSS_COMPILE=your-cross-toolchain-prefix- menuconfig
# ... enable the module, tune the config ...
make -j ARCH=mips CROSS_COMPILE=your-cross-toolchain-prefix-
In any case, consider the fact that jumping from a 2.6 to a 5.4 kernel is a pretty big change, and it's likely to end up breaking everything, so be sure to make a backup of your router's firmware before trying anything.

Related

Why does my kernel module Makefile build a .ko with kernel 4.14 but not 5.6?

I have a Makefile made by following this example:
cross compile kernel module
I built a 4.14 Linux kernel from an older Xilinx source, and then built a out-of-kernel module with that script, pointing it to the said 4.14 kernel sources, and filling in the blanks for my particular platform architecture.
That worked.
(It's based on this code, if that matters: dma-proxy.c)
Now I need a newer version, and got Xilinx sources with a kernel named 5.6.0-rc1.
(--branch "zynqmp-soc-for-v5.7" from here)
Building that kernel also worked fine.
If I now use a scrubbed clean directory (incl. hidden files) with my module source code and that Makefile again, pointing to the newer kernel sources, it does neither produce a .ko file nor an error message.
All I get is:
make ARCH=arm64 CROSS_COMPILE="aarch64-linux-gnu-" -C /home/sk/src/XILINX/linux-xlnx SUBDIRS=/home/sk/src/XILINX/dma-proxy/driver modules
make[1]: Entering directory '/home/sk/src/XILINX/linux-xlnx'
CALL scripts/checksyscalls.sh
CALL scripts/atomic/check-atomics.sh
MODPOST 28 modules
make[1]: Leaving directory '/home/sk/src/XILINX/linux-xlnx'
No .ko file in my folder as it was before when building with 4.14, and it doesn't list actually compiling anything.
I find it curious that it says "MODPOST 28 modules", whereas with pointing it to kernel 4.14, it expectedly says "1 modules"
Has anything changed between 4.14 and 5.x that would cause this?
Mkay, here is the suggested makefile template by the tutorial I referenced in the question:
PWD := $(shell pwd)
obj-m += hello.o
all:
make ARCH=arm CROSS_COMPILE=$(CROSS) -C $(KERNEL) SUBDIRS=$(PWD) modules
clean:
make -C $(KERNEL) SUBDIRS=$(PWD) clean
Turns out that if I replace SUBDIRS=$(PWD) with M=$(PWD), it works. Now if I google explicitly for that M variable in conjunction with building kernel modules, I do find text that show it that way. But the net is also littered with examples using SUBDIRS, and it worked for me with a fairly recent kernel (4.14).
Then I did find references hinting at this being an old way of doing it, like from here:
make -C $KDIR SUBDIRS=$PWD
Same as M=. The SUBDIRS= syntax is kept for backwards compatibility.
In fact, this seems to be really old, like, kernel 2.6.7 old. Unfortunately, fairly recent tutorials show the old way.

Cross-compiling for a specific platform -- linking libc

I have to compile a simple binary for a very old Linux system. I have no gcc/build libs available on the target machine but I do have access to the machine.
I am having trouble compiling the code on my machine and having it execute on target machine.
I am copying libc.so to my local machine and trying to compile and link the program so that it will execute on the target machine.
I have copied the libc from the target machine to mine and tried compiling it with my target executable.
this has gotten closes to a successful execution:
gcc -nostdlib ./libc-[version].so myFile.c -emain -o outfile.out
upon execution a very simple PoC test program runs, and then seg faults upon exit. the actual program simply seg faults.
It seems I have somewhat of a lack of understanding of linking. Any help?
It seems I have somewhat of a lack of understanding of linking.
You do. A "normal" user-level program doesn't start executing at main, it starts at _start.
The _start symbol typically comes from crt0.o file (part of libc), and knows how to "interface" between the way the kernel supplies arguments, and the way main expects to find them. It also initializes various data that must be initialized before main runs (e.g. stdio streams).
What you want to do then is:
Find out the actual link command that gcc main.o performs. You can do so by adding the -v flag.
Replicate such command, providing crt0.o and other input files, appropriate for your version of target libc.
It might be easier to spin up a VM with the OS matching your target (and with old tools that target it), and build your program inside of that VM.
Otherwise you'll likely have to set up a full cross-compiler environment (which includes libc and all other libraries you need). This is not a trivial proposition, and is certainly not accomplished by copying libc.so from the target machine. But it is well-documented and is certainly doable (with some skill).

GCC built from source in different location is incorrectly using same shared libs as native GCC

I'm a student doing research involving extending the TM capabilities of gcc. My goal is to make changes to gcc source, build gcc from the modified source, and, use the new executable the same way I'd use my distro's vanilla gcc.
I built and installed gcc in a different location (not /usr/bin/gcc), specifically because the modified gcc will be unstable, and because our project goal is to compare transactional programs compiled with the two different versions.
Our changes to gcc source impact both /gcc and /libitm. This means we are making a change to libitm.so, one of the shared libraries that get built.
My expectation:
when compiling myprogram.cpp with /usr/bin/g++, the version of libitm.so that will get linked should be the one that came with my distro;
when compiling it with ~/project/install-dir/bin/g++, the version of libitm.so that will get linked should be the one that just got built when I built my modified gcc.
But in reality it seems both native gcc and mine are using the same libitm, /usr/lib/x86_64-linux-gnu/libitm.so.1.
I only have a rough grasp of gcc internals as they apply to our project, but this is my understanding:
Our changes tell one compiler pass to conditionally insert our own "function builtin" instead of one it would normally use, and this is / becomes a "symbol" which needs to link to libitm.
When I use the new gcc to compile my program, that pass detects those conditions and successfully inserts the symbol, but then at runtime my program gives a "relocation error" indicating the symbol is not defined in the file it is searching in: ./test: relocation error: ./test: symbol _ITM_S1RU4, version LIBITM_1.0 not defined in file libitm.so.1 with link time reference
readelf shows me that /usr/lib/x86_64-linux-gnu/libitm.so.1 does not contain our new symbols while ~/project/install-dir/lib64/libitm.so.1 does; if I re-run my program after simply copying the latter libitm over the former (backing it up first, of course), it does not produce the relocation error anymore. But naturally this is not a permanent solution.
So I want the gcc I built to use the shared libs that were built along with it when linking. And I don't want to have to tell it where they are every time - my feeling is that it should know where to look for them since I deliberately built it somewhere else to behave differently.
This sounds like the kind of problem any amateur gcc developer would have when trying to make a dev environment and still be able to use both versions of gcc, but I had difficulty finding similar questions. I am thinking this is a matter of lacking certain config options when I configure gcc before building it. What is the right configuration to do this?
My small understanding of the instructions for building and installing gcc led me to do the following:
cd ~/project/
mkdir objdir
cd objdir
../source-dir/configure --enable-languages=c,c++ --prefix=/home/myusername/project/install-dir
make -j2
make install
I only have those config options because they seemed like the ones closest related to "only building the parts I need" and "not overwriting native gcc", but I could be wrong. After the initial config step I just re-run make -j2 and make install every time I change the code. All these steps do complete without errors, and they produce the ~/project/install-dir/bin/ folder, containing the gcc and g++ which behave as described.
I use ~/project/install-dir/bin/g++ -fgnu-tm -o myprogram myprogram.cpp to compile a transactional program, possibly with other options for programs with threads.
(I am using Xubuntu 16.04.3 (64 bit), within VirtualBox on Windows. The installed /usr/bin/gcc is version 5.4.0. Our source at ~/project/source-dir/ is a modified version of 5.3.0.)
You’re running into build- versus run-time linking differences. When you build with -fgnu-tm, the compiler knows where the library it needs is found, and it tells the linker where to find it; you can see this by adding -v to your g++ command. However when you run the resulting program, the dynamic linker doesn’t know it should look somewhere special for the ITM library, so it uses the default library in /usr/lib/x86_64-linux-gnu.
Things get even more confusing with ITM on Ubuntu because the library is installed system-wide, but the link script is installed in a GCC-private directory. This doesn’t happen with the default GCC build, so your own GCC build doesn’t do this, and you’ll see libitm.so in ~/project/install-dir/lib64.
To fix this at run-time, you need to tell the dynamic linker where to find the right library. You can do this either by setting LD_LIBRARY_PATH (to /home/.../project/install-dir/lib64), or by storing the path in the binary using -Wl,-rpath=/home/.../project/install-dir/lib64 when you build it.

Booting custom kernel on xeon-phi

I am trying to boot a custom kernel on Xeon-phi instead of the default Linux kernel. At this link, I found a way to cross compile my kernel which compiles successfully using k1om-mpss-linux-gcc cross compiler. Is cross compiling enough ? I get the error
mykernel.img is not a k1om Linux bzImage
Edit:
So, I used /usr/linux-k1om-4.7/bin/x86_64-k1om-linux-gcc compiler to compile a simple helloworld.c program and the kernel source. I get two different types of results for objdump -f on the executables.
for helloworld.c:
hello: file format elf64-k1om
architecture: k1om, flags 0x00000112:
EXEC_P, HAS_SYMS, D_PAGED
start address 0x0000000000400400
for mykernel:
mykernel: file format elf32-i386
architecture: i386, flags 0x00000112:
EXEC_P, HAS_SYMS, D_PAGED
start address 0x0010000c
I compiled using the same compiler, yet they show different architectures. What is the reason for this ?
The first thing to do is figure out what mykernel.img is. Try running file on it.
$ file /opt/mpss/3.4/sysroots/k1om-mpss-linux/boot/vmlinux-2.6.38.8+mpss3.4
/opt/mpss/3.4/sysroots/k1om-mpss-linux/boot/vmlinux-2.6.38.8+mpss3.4: ELF 64-bit LSB executable, version 1 (SYSV), statically linked, BuildID[sha1]=0xa4c16ee85c11aca4e78dc4ae46d3827fb74289c1, not stripped
$ objdump -f /opt/mpss/3.4/sysroots/k1om-mpss-linux/boot/vmlinux-2.6.38.8+mpss3.4
/opt/mpss/3.4/sysroots/k1om-mpss-linux/boot/vmlinux-2.6.38.8+mpss3.4: file format elf64-k1om
architecture: k1om, flags 0x00000112:
EXEC_P, HAS_SYMS, D_PAGED
start address 0x0000000001000000
The answer to your original question - no, unfortunately, it is not as simple as just cross-compiling. There were a number of changes made to the kernel that comes with the MPSS. I don't know all the changes but a big one that I do know is that they had to add support for the larger register set on the coprocessor in order to be able to save state on a context switch.
As to why the file format is elf32-i386 instead of elf32-k1om -
The web site you referenced referred to recompiling the kernel that came with the MPSS after possibly make a few changes in the files. You'll notice that they also copied over a configuration file for the installed version of the kernel. So they had all the files to remake the kernel exactly as it had been made.
I suspect that, in your case, either a) there was a configuration script of some sort in your source directory that picked up the architecture you were running on and caused confusion when the makefile ran or b) your makefile had no idea what k1om was. In either case, it fell back to what it believed to the the lowest common denominator i386. As I say, this is just a suspicion on my part but a careful reading of your makefiles should lead to the answer.

UHD produces linker error in FreeBSD

I'm on freebsd and trying to run uhd from: http://www.ettus.com/download This should normally run under freebsd, but I encounter problems when I try to build it like specified here:
http://www.ettus.com/uhd_docs/manual/html/build.html#build-instructions-unix
I get:
[ 73%] Building CXX object examples/CMakeFiles/benchmark_rx_rate.dir/benchmark_rx_rate.cpp.o
Linking CXX executable benchmark_rx_rate
../lib/libuhd.so.003.000: undefined reference to `uhd::set_thread_priority(float, bool)'
collect2: ld returned 1 exit status
*** Error code 1
Now this seems to be a linker error, but how to get rid of it? I tried setting the path using ldconfig but this hasn't changed a thing. Maybe some of you have an idea where I could continue searching.
If i get it right, libuhd get's built earlier by this CMake script. If it's true, this error indicates bug in their CMakeLists.txt files.
It looks like you are building examples of using libuhd. I doubt you are need them. So, either look for switches in your CMakeCache.txt and regenerate Makefiles or hack CMakeLists.txt to not include examples dir.
Another thing you may try - set CMAKE_EXE_LINKER_FLAGS to -L/usr/local/include.
This is a perfect situation for creating a FreeBSD port. Since FreeBSD has its own consistent filesystem layout, compiler and linker flags often need to be modified (as noted by arrowdodger) slightly.
The ports system is a framework for persistently capturing those configuration changes and making any software package as easy to install as typing the commands:
cd /usr/ports/category/application-name && make install clean
In a perfect world, you would create the UHD port, submit it for review and addition to the tree. If you need help learning how to do that, the FreeBSD Porter's Handbook and the freebsd-ports mailing list are excellent resources.

Resources