Why the target x86_64-unknown-none is pie by default?

The x86_64-unknown-none target is developed for kernel and firmwares, as its documentation states.

However, after scanning the exact settings by rustc --target x86_64-unknown-none -Z unstable-options --print target-spec-json

{
  "arch": "x86_64",
  "code-model": "kernel",
  "cpu": "x86-64",
  "crt-objects-fallback": "false",
  "data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128",
  "disable-redzone": true,
  "features": "-mmx,-sse,-sse2,-sse3,-ssse3,-sse4.1,-sse4.2,-3dnow,-3dnowa,-avx,-avx2,+soft-float",
  "is-builtin": true,
  "linker": "rust-lld",
  "linker-flavor": "gnu-lld",
  "llvm-target": "x86_64-unknown-none-elf",
  "max-atomic-width": 64,
  "panic-strategy": "abort",
  "plt-by-default": false,
  "position-independent-executables": true,
  "relro-level": "full",
  "stack-probes": {
    "kind": "inline"
  },
  "static-position-independent-executables": true,
  "supported-sanitizers": [
    "kcfi",
    "kernel-address"
  ],
  "target-pointer-width": "64"
}

In the default setting, the position-independent-executables option is ture.

However, for kernel code like boot code, they must be put on specific address, so the target cannot be compiled as pie.

So I wonder why the target is pie by default and how can I built to non-pie x86_64-unknown-none target? Using custom target is one alternative method, but I wonder is there any other possible method?

Why not? isn't the point of PIE that the code can run regardless of where it's put?

It is common for kernels to remap themself into the upper half of the virtual memory. If the kernel was compiled for a fixed location, this wouldn't be possible. Many bootloader protocols mandate that the kernel gets an identity page mapping matching, which means that if the kernel hard codes the upper half of the virtual memory as load address, the bootloader wouldn't be able to load the kernel as there is no physical memory in the upper half of the address space at all.

2 Likes