Change:
"ARCH=${stdenv.hostPlatform.linuxArch}" ] ++ lib.optionals (stdenv.hostPlatform != stdenv.buildPlatform) [ "CROSS_COMPILE=${stdenv.cc.targetPrefix}" <del> ] ++ (kernelConf.makeFlags or []) </del> <ins> ] ++ (stdenv.hostPlatform.linux-kernel.makeFlags or []) </ins> ++ extraMakeFlags; karch = stdenv.hostPlatform.linuxArch; <del> buildFlags = [ "DTC_FLAGS=-@" "KBUILD_BUILD_VERSION=1-NixOS" # Set by default in the kernel since a73619a845d5, # replicated here to apply to older versions. # Makes __FILE__ relative to the build directory. "KCPPFLAGS=-fmacro-prefix-map=$(sourceRoot)/=" kernelConf.target ] ++ optional isModular "modules" ++ optional buildDTBs "dtbs" ++ extraMakeFlags; installFlags = [ "INSTALL_PATH=$(out)" ] ++ (optional isModular "INSTALL_MOD_PATH=$(out)") ++ optionals buildDTBs ["dtbs_install" "INSTALL_DTBS_PATH=$(out)/dtbs"]; preInstall = let # All we really need to do here is copy the final image and System.map to $out, # and use the kernel's modules_install, firmware_install, dtbs_install, etc. targets # for the rest. Easy, right? # # Unfortunately for us, the obvious way of getting the built image path, # make -s image_name, does not work correctly, because some architectures # (*cough* aarch64 *cough*) change KBUILD_IMAGE on the fly in their install targets, # so we end up attempting to install the thing we didn't actually build. # # Thankfully, there's a way out that doesn't involve just hardcoding everything. # # The kernel has an install target, which runs a pretty simple shell script # (located at scripts/install.sh or arch/$arch/boot/install.sh, depending on # which kernel version you're looking at) that tries to do something sensible. # # (it would be great to hijack this script immediately, as it has all the #   information we need passed to it and we don't need it to try and be smart, #   but unfortunately, the exact location of the scripts differs between kernel #   versions, and they're seemingly not considered to be public API at all) # # One of the ways it tries to discover what "something sensible" actually is # is by delegating to what's supposed to be a user-provided install script # located at ~/bin/installkernel. # # (the other options are: #   - a distribution-specific script at /sbin/installkernel, #        which we can't really create in the sandbox easily #   - an architecture-specific script at arch/$arch/boot/install.sh, #        which attempts to guess _something_ and usually guesses very wrong) # # More specifically, the install script exec's into ~/bin/installkernel, if one # exists, with the following arguments: # # $1: $KERNELRELEASE - full kernel version string # $2: $KBUILD_IMAGE - the final image path # $3: System.map - path to System.map file, seemingly hardcoded everywhere # $4: $INSTALL_PATH - path to the destination directory as specified in installFlags # # $2 is exactly what we want, so hijack the script and use the knowledge given to it # by the makefile overlords for our own nefarious ends. # # Note that the makefiles specifically look in ~/bin/installkernel, and # writeShellScriptBin writes the script to <store path>/bin/installkernel, # so HOME needs to be set to just the store path. # # FIXME: figure out a less roundabout way of doing this. installkernel = buildPackages.writeShellScriptBin "installkernel" '' cp -av $2 $4 cp -av $3 $4 ''; in '' installFlagsArray+=("-j$NIX_BUILD_CORES") export HOME=${installkernel} ''; # Some image types need special install targets (e.g. uImage is installed with make uinstall) installTargets = [ (kernelConf.installTarget or ( /**/ if target == "uImage" then "uinstall" else if target == "zImage" || target == "Image.gz" then "zinstall" else "install")) ]; postInstall = optionalString isModular '' if [ -z "''${dontStrip-}" ]; then installFlagsArray+=("INSTALL_MOD_STRIP=1") fi make modules_install $makeFlags "''${makeFlagsArray[@]}"  $installFlags "''${installFlagsArray[@]}" unlink $out/lib/modules/${modDirVersion}/build unlink $out/lib/modules/${modDirVersion}/source mkdir $dev/lib/modules/${modDirVersion}/build cd $dev/lib/modules/${modDirVersion}/source cp $buildRoot/{.config,Module.symvers} $dev/lib/modules/${modDirVersion}/build make modules_prepare $makeFlags "''${makeFlagsArray[@]}" O=$dev/lib/modules/${modDirVersion}/build # For reproducibility, removes accidental leftovers from a `cc1` call # from a `try-run` call from the Makefile rm -f $dev/lib/modules/${modDirVersion}/build/.[0-9]*.d # Keep some extra files for f in arch/powerpc/lib/crtsavres.o arch/arm64/kernel/ftrace-mod.o  scripts/gdb/linux vmlinux vmlinux-gdb.py do if [ -e "$buildRoot/$f" ]; then mkdir -p "$(dirname "$dev/lib/modules/${modDirVersion}/build/$f")" cp -HR $buildRoot/$f $dev/lib/modules/${modDirVersion}/build/$f fi done ln -s $dev/lib/modules/${modDirVersion}/build/vmlinux $dev # !!! No documentation on how much of the source tree must be kept # If/when kernel builds fail due to missing files, you can add # them here. Note that we may see packages requiring headers # from drivers/ in the future; it adds 50M to keep all of its # headers on 3.10 though. chmod u+w -R .. arch=$(cd $dev/lib/modules/${modDirVersion}/build/arch; ls) # Remove unused arches for d in $(cd arch/; ls); do if [ "$d" = "$arch" ]; then continue; fi if [ "$arch" = arm64 ] && [ "$d" = arm ]; then continue; fi rm -rf arch/$d done # Remove all driver-specific code (50M of which is headers) rm -fR drivers # Keep all headers find .  -type f -name '*.h' -print0 | xargs -0 -r chmod u-w # Keep linker scripts (they are required for out-of-tree modules on aarch64) find .  -type f -name '*.lds' -print0 | xargs -0 -r chmod u-w # Keep root and arch-specific Makefiles chmod u-w Makefile arch/"$arch"/Makefile* # Keep whole scripts dir chmod u-w -R scripts # Delete everything not kept find . -type f -perm -u=w -print0 | xargs -0 -r rm # Delete empty directories find -empty -type d -delete # Remove reference to kmod sed -i Makefile -e 's|= ${buildPackages.kmod}/bin/depmod|= depmod|' '' # unfortunately linux/arch/mips/Makefile does not understand installkernel # and simply copies to $(INSTALL_PATH)/vmlinux-$(KERNELRELEASE) + lib.optionalString stdenv.hostPlatform.isMips '' mv $out/vmlinux-* $out/vmlinux || true mv $out/vmlinuz-* $out/vmlinuz || true mv $out/System.map-* $out/System.map ''; preFixup = '' # Don't strip $dev/lib/modules/*/vmlinux stripDebugList="$(cd $dev && echo lib/modules/*/build/*/)" '' + lib.optionalString (stdenv.hostPlatform.isMips) '' $STRIP -s $out/vmlinux || true ''; enableParallelBuilding = true; passthru = rec { inherit version modDirVersion config kernelPatches configfile moduleBuildDependencies stdenv; inherit isZen isHardened isLibre; isXen = lib.warn "The isXen attribute is deprecated. All Nixpkgs kernels that support it now have Xen enabled." true; baseVersion = lib.head (lib.splitString "-rc" version); kernelOlder = lib.versionOlder baseVersion; kernelAtLeast = lib.versionAtLeast baseVersion; }; requiredSystemFeatures = [ "big-parallel" ]; meta = { description = "The Linux kernel" + (lib.optionalString (kernelPatches != []) ( " (with patches: " + lib.concatStringsSep ", " (map (x: x.name) kernelPatches) + ")" )); license = lib.licenses.gpl2Only; homepage = "https://www.kernel.org/"; maintainers = lib.teams.linux-kernel.members ++ [ maintainers.thoughtpolice ]; platforms = platforms.linux; badPlatforms = lib.optionals (lib.versionOlder version "4.15") [ "riscv32-linux" "riscv64-linux" ] ++ lib.optional (lib.versionOlder version "5.19") "loongarch64-linux"; timeout = 14400; # 4 hours } // extraMeta; } // optionalAttrs (pos != null) { inherit pos; } // optionalAttrs isModular { outputs = [ "out" "dev" ]; })) </del> <ins> } // (optionalAttrs (pos != null) { inherit pos; }))) </ins>

Discussion:
Building powerpc kernels (I only tested pkgsCross.powernv, but examination of the kernel source code suggests to me that 32-bit powerpc, and well as big-endian 64-bit will be the same) is broken since This happens because on powerpc, the kernel target we install is vmlinux, rather than any derivative of it like zImage. The kernel build system will strip when creating such a derivative image, but because we install vmlinux directly, we get an unstripped image. This means the kernel contains references to its build directory, which since that PR will be the $dev output. AIUI, 64-bit POWER boots via kexec, and a zImage is a self-extracting kernel ELF image, so hopefully the fix would just be to switch the kernel target for powerpc to zImage, and things will work again, and POWER users will get smaller kernel images as a bonus. I'm not sure about 32-bit PowerPC, but this might be helpful. If we can't use an *Image target for any powerpc targets, there is also a target called that we could fall back to, but let's take the opportunity to evaluate whether anything else is more suitable first, when we're changing this stuff anyway. Apart from powerpc, the only we have that uses vmlinux as its kernel target is a MIPS machine called the Fuloong 2.0. I don't know how it boots.
I'll handle this. I don't have big-endian POWER hardware, but pointed out that qemu can probably boot a BE kernel under emulation at full hardware speeds. So if that works I can take care of the 32-bit POWER (which is mostly BE) too.
For reference, I managed to get a working kernel derivation for Power9 with Just setting building a zImage without fully stripping it afterwards wasn't enough. There still was a reference to in the debug info somewhere.
Yep, very plausible that there are other references as well. We had for x86 and RISC-V, although that specific issue didn't apply to other architectures.
I'll dig into this this evening. thank you for the tip! Also putting this link here so I don't lose it:
Verified (after fixing a bunch of other things) that is affected too. Thanks again for the heads up Working on a fix.
Edit: I meant to post this .
Fixed by