A kernel is “bare metal”. That means there are no dynamic link libraries. It’s code purely referencing itself. This, in turn, means you only need a cross compiler. There is no need for a full environment, e.g., no need for a cross linker, and no need for a sysroot (which is what contains the foreign architecture libraries). The instructions in the official NVIDIA documentation usually just has you install a cross linker via the apt
mechanism on the host PC. If the cross compiler is not available in a correct version, then the same download page for the documentation of a given L4T release also contain the download of the particular cross compiler (although it might be difficult to find).
Setting up the “KERNEL_OUT
” and then using “O=$KERNEL_OUT
” is a very good practice. Just make sure you start with an empty directory for $KERNEL_OUT
. If there is a “.config
” you produced in that location, then you can copy that to a safe spot if you wish to preserve it. Then, any time you delete that directory and recreate it, you know you are starting cleanly. If you then copy the “.config
” file you’ve restored all of your configuration (you would still need to “propagate” that config if not starting with the “Image
” build target; e.g., “make O=$KERNEL_OUT modules_prepare
” would propagate that config for building a module without first building Image
).
Running “make O=$KERNEL_OUT tegra_defconfig
” creates “$KERNEL_OUT/.config
”. It still needs CONFIG_LOCALVERSION
to be set up. If you then run something like “make O=$KERNEL_OUT nconfig
”, then any edits will be placed in “$KERNEL_OUT/.config
”. When satisfied, or just want to track progress, save “$KERNEL_OUT/.config
” somewhere else, perhaps renamed with a note in the name for what it is. Create with tegra_defconfig
, and edit with nconfig
(or menuconfig
, but nconfig
can find symbols). You can adjust CONFIG_LOCALVERSION
via nconfig
, just search for localversion
(hint: search already knows about the CONFIG_
prefix, and also is case-insensitive).
Incidentally, for menuconfig
or nconfig
to work, you need the ncurses
dev package on the host PC;
sudo apt-get install libncurses5-dev
I don’t use the nvbuild.sh
script, but it does the same thing manually running the commands would do. I prefer fine control of where everything goes, and it isn’t too difficult. One of the reasons why nvbuild.sh
exists is that people may not want to go through the details and learning curve for manually building on command line, but once you’ve done this, it’s actually quite easy.
It is very good to start with pristine source. That means running make mrproper
without the “O=...somewhere...
”. If that source is owned by root after the mrproper
, and not modifiable for anyone else, and if you always compile as a regular user with O=...somewhere
, you won’t hit any of the “old config sticking around in part and playing a joke on you 😈” issue.
In your case this might be a checklist (assuming you don’t use the nvbuild script):
- Unpack pristine source as root such that the regular user cannot modify it.
- If cross compiling be sure to set up any environment variables specific to that, e.g., you’d modify
ARCH
to be arm64
, but if native compiling, do not add ARCH
. The cross tools also need to be set up via an environment variable.
- Run “
sudo make mrproper
” to be certain it is clean.
- From then on use the “
O=...somewhere...
”, e.g., via the KERNEL_OUT
environment variable. Use a regular user for that and make KERNEL_OUT
a place your regular user has permissions to write to.
- Start with "
make O=$KERNEL_OUT tegra_defconfig
. This gives you a .config
at “$KERNEL_OUT/.config
”, but it is still missing CONFIG_LOCALVERSION
setup.
- If you are only building a module, then edit
CONFIG_LOCALVERSION
to “-tegra
”, the default. That’s a configuration which normally matches the running system. If the system has been modified, then you also have to account for that, e.g., using the “/proc/config.gz
” instead of tegra_defconfig
, and maybe altering the CONFIG_LOCALVERSION
.
- If you are building with a modified “integrated” feature, then use a modified
CONFIG_LOCALVERSION
, e.g., if you were adding the GFS2 integrated and not as a module, you might set CONFIG_LOCALVERSION
to “-gfs2
” as a reminder.
- Build using that
$KERNEL_OUT
. Any module with a matching CONFIG_LOCALVERSION
, and with integrated features matching, can then be a simple copy.
- Special note on building only modules: I advise building
Image
once as an acid test to see if it works. However, you could skip this, but you’d then need target modules_prepare
. Building Image
does this for you.
- When you go to copy a module, if the kernel source is at
$TOP
, note the subdirectory to that which the module ends up at, e.g., “$TOP/drivers/net/something.ko
”, and copy it to the Jetson at:
/lib/modules/$(uname -r)/kernel/drivers/net/something.ko
(to mirror the subdirectory)
One more tip: If your host PC has 8 cores, then you might want to build with the “-j 8
” option to use up to 8 cores during build. If you don’t have enough RAM and it starts to swap out, then you might be better of with a lower job server count (the -j
is the job server), e.g., “-j 4
” uses less RAM than “-j 8
”.