We speak about the interface an operating system provides to userland. They are kernel and libc interface (syscalls, libc functions, system structures…).
With BSDs, the system is provided as a whole: kernel, libc, all system programs installed with the OS (like ls, mount, …). It is the whole that is targeted (but not third-parties programs or libraries, installed after the system).
Regarding Rust, the intented usage of OS or environment version is mostly for libc crate. It is where most of the OS depend code live, and the current code lackes a way to express breaking changes (as example, FreeBSD 12 will be published with a ino_tstruct size change - it is a low level structure used in syscalls - using the wrong struct size makes the program to crash).
For backported changes, I think it will depend of the OS policy, but breaking changes will not be backported (because it will break expectation of running binaries). Speaking about OpenBSD, the changes are published via erratas. There are security, interoperability or reliability fixes only. No new features. A particular version is supported one year only (the RFC mentions deprecating unsupported versions).
About runtime detection, it could be done by replacing the libc crate with binding generation (using rust-bindgen for example). The main drawback, as mentioned, will be cross-compilation, as the host system doesn’t know about the targeted system. I added this alternative to the RFC.
If a backported change would require some adjustment, it would be still possible to express it in straighful way (assuming it isn’t a breaking change): adding new code under conditional compilation directive would be still possible. If it is a breaking change which require change in libc crate, it will require a library major version bump (as it will be also a breaking change in the crate itself), but it isn’t different from what we have now.