Some history and limitations of uname(1) fields

June 4, 2024

Uname(1) is a command that hypothetically prints some potentially useful information about your system. In practice what information it prints, how useful that information is, and what command line options it supports varies widely between both different sorts of Unixes and between different versions of Linux (due to using different versions of GNU Coreutils, and different patches for it). I was asked recently if this situation ever made any sense and the general answer is 'maybe'.

In POSIX, uname(1) is more or less a program version of the uname() function. It supports only '-m', '-n', '-r', '-s', and '-v', and as a result of all of these arguments being required by POSIX, they are widely supported by the various versions of uname that are out there in various Unixes. All other arguments are non-standard and were added well after uname(1) initially came into being, which is one reason they are so divergent in presence and meaning; there is no enhanced ancestral 'uname' command for things to descend from.

The uname command itself comes from the System V side of Unix; it was first added as far back as at least System III, where the System III uname.c accepts -n, -r, -s, and -v with the modern meanings. System III gets the information from the kernel, in a utssys() system call. I believe that System V added the 'machine' information ('-m'), which then was copied straight into POSIX. On the BSD side, a uname command first appeared in 4.4 BSD, and the 4.4 BSD uname(1) manual page says that it also had the POSIX arguments, including -m. The actual implementation didn't use a uname() system call but instead extracted the information with sysctl() calls.

The modern versions of uname that I can find manual pages for are rather divergent; contrast Linux uname(1) (also), FreeBSD uname(1), OpenBSD uname(1), NetBSD uname(1), and Illumos uname(1) (manual pages for other Unixes are left as an exercise). For instance, take the '-i' argument, supported in Linux and Illumos to print a theoretical hardware platform and FreeBSD to print the 'kernel ident'. On top of that difference, on Linux distributions that use an unpatched build of Coreutils, I believe that 'uname -i' and 'uname -p' will both report 'unknown'.

(Based on how everyone has the -p argument for processor type, I suspect that it was one of the earliest additions to POSIX uname. How 'uname -m' differs from 'uname -p' in practice is something I don't know, but apparently people felt a need to distinguish the two at some point. Some Internet searches suggest that on Unixes such as Solaris, the processor type might be 'sparc' while the machine hardware name might be more specific, like 'sun4m'.)

On Linux and several other Unixes, much of the core information for uname comes from the kernel, which means that options like 'uname -r' and 'uname -v' have traditionally reported about the kernel version and build string, not anything to do with the general release of Linux (or Unix). On FreeBSD, the kernel release is usually fairly tightly connected to the userland version (although FreeBSD uname can tell you about the latter too), but on Linux it is not, and the Linux uname has no option to report a 'distribution' name or version.

In general, I suspect that the only useful fields you can count on from uname(1) are '-n' (some version of the hostname), '-s' (the broad operating system), and perhaps '-m', although you probably want to be wary about that. One of the cautions with 'uname -m' is that there is no agreement between Unixes about what the same hardware platform should be called; for example, OpenBSD uses 'amd64' for 64-bit x86 while Linux uses 'x86_64'. Illumos recommends using 'uname -p' instead of 'uname -m'.

(This entry's topic was suggested to me by Hunter Matthews, although I suspect I haven't answered their questions about historical uname values.)

Sidebar: An options comparison for non-POSIX options

The common additional options are:

  • -i: semi-supported on Linux, supported on Illumos, and means something different on FreeBSD (it's the kernel identifier instead of the hardware platform).
  • -o: supported on Linux, where it is often different from 'uname -s', FreeBSD, where it is explicitly the same as 'uname -s', and Illumos, where I don't know how it relates to 'uname -s'.
  • -p: semi-supported on Linux and fully supported on FreeBSD, OpenBSD, NetBSD, and Illumos. Illumos specifically suggests using 'uname -p' instead of 'uname -m', which will generally make you sad on Linux.

FreeBSD has -b, -K, and -U as additional FreeBSD specific arguments.

How 'uname -i', 'uname -p', and 'uname -m' differ on Illumos is not something I know; they all report something about the hardware, but the Illumos uname manpage mostly doesn't illuminate the difference. It's possible that this is more or less covered in sysinfo(2).

(The moral is that you can't predict the result of these options without running uname on an applicable system, or at least having a lot of OS-specific knowledge.)


Comments on this page:

By Phil Pennock at 2024-06-05 00:40:11:

I looked into this a couple of years ago and the -m/-p thing becomes more prominent once you have 64-bit processors working in 32-bit mode.

                           uname -m    uname -p
Darwin/M1  . . . . . . . . arm64       arm
Darwin/x86                 x86_64      i386
Ubuntu/x86/64b . . . . . . x86_64      x86_64
Alpine/docker on above     x86_64      unknown
RPi 3B Linux/32b-kernel    armv7l      unknown
RPi 3B Linux/64b-kernel    aarch64     unknown     /boot/config.txt includes `arm_64bit=1`

FreeBSD:   sysctl hw.machine hw.machine_arch debug.adaptive_machine_arch
   # uname: -m for "current hardware platform", -p for "machine processor architecture"
   # uname(1) implements these as sysctl fetches, of hw.machine and hw.machine_arch
   # as long as debug.adaptive_machine_arch is 1 (the default), -p will switch to 32-bit value for 32-bit binaries

I made the mistake of going down the rabbit-hole of looking at uname vs arch vs packaging-systems vs go env GOHOSTARCH vs rustc --print cfg | sed -Ene 's/^target_arch="(.*)"/\1/p

By Nobody in particular at 2024-06-05 08:21:27:

To all the above it's probably worth adding: since the format of the data for each field is implementation-defined, it's a priori impossible to parse the result of a 'uname -a' or any multi-flag invocation. So to whatever extent it's valid to make decisions based on uname output in a program, separate invocations are necessary for each flag, and each output should be treated as an atomic token whose interpretation depends on the output of null-ary uname and perhaps -r or -v (depending on the null-ary token and assuming each implementation is likely to be upwardly compatible with itself).

Anyhow, even if the formats were specified and could be parsed, the info is still very coarse. ISTM that programmatic use of uname(1) is fine for producing informational text for human consumption, but only suitable to get "toehold" on what kind of system you're dealing with: no fine-grained inferences ought to be made from its output.

By Ian Z aka nobrowser at 2024-06-05 13:33:12:

This is very interesting, because I just happen to be dealing with

https://unix.stackexchange.com/questions/777665/change-linux-kernel-release-string

Coincidence? And maybe readers here can comment on that?

Written on 04 June 2024.
« CVEs are not what I'll call security reports
Maybe understanding uname(1)'s platform and machine fields »

Page tools: View Source, View Normal.
Search:
Login: Password:

Last modified: Tue Jun 4 22:16:12 2024
This dinky wiki is brought to you by the Insane Hackers Guild, Python sub-branch.