はじめに
実用性があまりないネタ的な投稿ですのでご了承を。Linux-4.6あたりを見てます。
初心者向け
[rarul@tina ~]$ cat /etc/lsb-release
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=16.04
DISTRIB_CODENAME=xenial
DISTRIB_DESCRIPTION="Ubuntu 16.04.1 LTS"
ふつうの人向け
[rarul@tina ~]$ uname -a
Linux tina 4.4.0-59-generic #80-Ubuntu SMP Fri Jan 6 17:47:47 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux
システム屋さん
[rarul@tina ~]$ strace uname -a
execve("/bin/uname", ["uname", "-a"], [/* 22 vars */]) = 0
brk(NULL) = 0x1240000
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f0c65fd1000
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=33482, ...}) = 0
mmap(NULL, 33482, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f0c65fc8000
(---SNIP---)
ユーザランドが貧弱
[rarul@tina ~]$ more /proc/version
Linux version 4.4.0-59-generic (buildd@lgw01-11) (gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.4) ) #80-Ubuntu SMP Fri Jan 6 17:47:47 UTC 2017
ログ解析班
[rarul@tina ~]$ dmesg |grep "Linux version"
[ 0.000000] Linux version 4.4.0-59-generic (buildd@lgw01-11) (gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.4) ) #80-Ubuntu SMP Fri Jan 6 17:47:47 UTC 2017 (Ubuntu 4.4.0-59.80-generic 4.4.35)
バイナリファイルしかねぇ
[rarul@tina ~]$ wget https://raw.githubusercontent.com/torvalds/linux/master/scripts/extract-vmlinux
[rarul@tina ~]$ sudo sh ./extract-vmlinux /boot/vmlinuz-4.4.0-59-generic |strings |grep "Linux version"
Linux version 4.4.0-59-generic (buildd@lgw01-11) (gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.4) ) #80-Ubuntu SMP Fri Jan 6 17:47:47 UTC 2017 (Ubuntu 4.4.0-59.80-generic 4.4.35)
ソースコードしかねぇ
[rarul@tina ~]$ cd src/linux-stable/
[rarul@tina linux-stable]$ head -n 5 Makefile
VERSION = 4
PATCHLEVEL = 6
SUBLEVEL = 0
EXTRAVERSION =
NAME = Charred Weasel
ちなみに、「NAME = Charred Weasel」がウワサのコードネームです。
gdbが好きな人
[rarul@tina out-linux]$ gdb
GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.04) 7.11.1
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word".
(gdb) file vmlinux
Reading symbols from vmlinux...done.
(gdb) p linux_banner
$1 = "Linux version 4.6.0 (rarul@tina) (gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.4) ) #2 SMP Sat Feb 11 19:03:03 JST 2017\n"
(gdb) p linux_proc_banner
$2 = "%s version %s (rarul@tina) (gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.4) ) %s\n"
...Qiita記法のコード挿入の対応言語に.gdbinitはなかった模様...
gdb用のデバッグ情報をvmlinuxに埋め込むにはCONFIG_DEBUG_INFOが必要。
考古学者
[rarul@tina ~]$ git clone git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git
[rarul@tina ~]$ cd linux-stable
[rarul@tina linux-stable]$ git log Makefile
歴史の教科書
マジメにプログラミング(kernel編)
例えばlinux/fs/ext4/ext4.hにマクロを使った#ifの書き方がある。
825 #if LINUX_VERSION_CODE < KERNEL_VERSION(4,20,0)
LINUX_VERSION_CODEは、今ビルドしようとしているkernelのソースのバージョン、KERNEL_VERSIONはマクロで、「4.20.0」に相当するintegerに変換される。
マジメにプログラミング(ユーザランド編)
まず、kernel編と同じく、LINUX_VERSION_CODEとKERNEL_VERSIONを使った比較ができる。
次に、integerじゃなくて文字列としてほしい場合のサンプルがこんな感じとなる。
#include <stdio.h>
#include <linux/version.h>
int main (int argc, char *argv[]){
#define LINUX_VERSION_CODE_RELEASE (((LINUX_VERSION_CODE)>>16)&0xff)
#define LINUX_VERSION_CODE_MAJOR (((LINUX_VERSION_CODE)>>8)&0xff)
#define LINUX_VERSION_CODE_MINOR (((LINUX_VERSION_CODE)>>0)&0xff)
printf ("Linux-%d.%d.%d\n",
LINUX_VERSION_CODE_RELEASE,
LINUX_VERSION_CODE_MAJOR,
LINUX_VERSION_CODE_MINOR);
return 0;
}
[rarul@tina version_string]$ ./version_string
Linux-4.4.35
最後に、ヘッダ(include/linux.version.h)もない場合、uname(2)システムコールを使うことになる。バージョン互換性とかいろいろ書かれているけど、私あまり詳しくないので、詳細はman見てね。
#define _GNU_SOURCE
#include <stdio.h>
#include <sys/utsname.h>
int main(int argc, char *argv[]){
struct utsname me;
if (uname(&me) != 0){
perror("uname");
} else {
printf("sysname = %s\n" "nodename = %s\n" "release = %s\n"
"version = %s\n" "machine = %s\n" "domainname = %s\n",
me.sysname, me.nodename, me.release,
me.version, me.machine, me.domainname);
}
return 0;
}
[rarul@tina utsname]$ ./utsname
sysname = Linux
nodename = tina
release = 4.4.0-59-generic
version = #80-Ubuntu SMP Fri Jan 6 17:47:47 UTC 2017
machine = x86_64
domainname = (none)
kernel hacker
/proc/versionはlinux/proc/version.c:version_proc_show()より、
8 static int version_proc_show(struct seq_file *m, void *v)
9 {
10 seq_printf(m, linux_proc_banner,
11 utsname()->sysname,
12 utsname()->release,
13 utsname()->version);
14 return 0;
15 }
linux_banner[], **linux_proc_banner[]**の定義はlinux/init/version.cより、
45 /* FIXED STRINGS! Don't touch! */
46 const char linux_banner[] =
47 "Linux version " UTS_RELEASE " (" LINUX_COMPILE_BY "@"
48 LINUX_COMPILE_HOST ") (" LINUX_COMPILER ") " UTS_VERSION "\n";
49
50 const char linux_proc_banner[] =
51 "%s version %s"
52 " (" LINUX_COMPILE_BY "@" LINUX_COMPILE_HOST ")"
53 " (" LINUX_COMPILER ") %s\n";
54
マクロ名が気になるけど、あとでまとめて出てくるのでおいといて、先に**linux_proc_banner[]の%sから。これはutsname()**からくるが、デフォルトのutsname()は、linux/init/version.cで作られる。
25 struct uts_namespace init_uts_ns = {
26 .kref = {
27 .refcount = ATOMIC_INIT(2),
28 },
29 .name = {
30 .sysname = UTS_SYSNAME,
31 .nodename = UTS_NODENAME,
32 .release = UTS_RELEASE,
33 .version = UTS_VERSION,
34 .machine = UTS_MACHINE,
35 .domainname = UTS_DOMAINNAME,
36 },
37 .user_ns = &init_user_ns,
38 .ns.inum = PROC_UTS_INIT_INO,
39 #ifdef CONFIG_UTS_NS
40 .ns.ops = &utsns_operations,
41 #endif
42 };
「デフォルトのutsname()」と書いたのは、実はutsname()はタスクごとに持てるため(namespace,utsnameが入ったLinux-3.0より)。このへんは「struct nsproxy」などでググればそれなりに見つかるのでそちらに任せる。
で、ここから先に飛ばしたマクロもまとめて。LINUX_COMPILE_BY, LINUX_COMPILE_HOST, LINUX_COMPILER, UTS_VERSION, UTS_MACHINEはlinux/scripts/mkcompile_hより、
29 if [ -z "$KBUILD_BUILD_VERSION" ]; then
30 if [ -r .version ]; then
31 VERSION=`cat .version`
32 else
33 VERSION=0
34 echo 0 > .version
35 fi
36 else
37 VERSION=$KBUILD_BUILD_VERSION
38 fi
39
40 if [ -z "$KBUILD_BUILD_TIMESTAMP" ]; then
41 TIMESTAMP=`date`
42 else
43 TIMESTAMP=$KBUILD_BUILD_TIMESTAMP
44 fi
45 if test -z "$KBUILD_BUILD_USER"; then
46 LINUX_COMPILE_BY=$(whoami | sed 's/\\/\\\\/')
47 else
48 LINUX_COMPILE_BY=$KBUILD_BUILD_USER
49 fi
50 if test -z "$KBUILD_BUILD_HOST"; then
51 LINUX_COMPILE_HOST=`hostname`
52 else
53 LINUX_COMPILE_HOST=$KBUILD_BUILD_HOST
54 fi
55
56 UTS_VERSION="#$VERSION"
57 CONFIG_FLAGS=""
58 if [ -n "$SMP" ] ; then CONFIG_FLAGS="SMP"; fi
59 if [ -n "$PREEMPT" ] ; then CONFIG_FLAGS="$CONFIG_FLAGS PREEMPT"; fi
60 UTS_VERSION="$UTS_VERSION $CONFIG_FLAGS $TIMESTAMP"
61
62 # Truncate to maximum length
63
64 UTS_LEN=64
65 UTS_TRUNCATE="cut -b -$UTS_LEN"
66
67 # Generate a temporary compile.h
68
69 ( echo /\* This file is auto generated, version $VERSION \*/
70 if [ -n "$CONFIG_FLAGS" ] ; then echo "/* $CONFIG_FLAGS */"; fi
71
72 echo \#define UTS_MACHINE \"$ARCH\"
73
74 echo \#define UTS_VERSION \"`echo $UTS_VERSION | $UTS_TRUNCATE`\"
75
76 echo \#define LINUX_COMPILE_BY \"`echo $LINUX_COMPILE_BY | $UTS_TRUNCATE`\"
77 echo \#define LINUX_COMPILE_HOST \"`echo $LINUX_COMPILE_HOST | $UTS_TRUNCATE`\"
78
79 echo \#define LINUX_COMPILER \"`$CC -v 2>&1 | grep ' version '`\"
80 ) > .tmpcompile
UTS_SYSNAME, UTS_NODENAME, UTS_DOMAINNAMEのマクロは、linux/include/linux/uts.hより、
4 /*
5 * Defines for what uname() should return
6 */
7 #ifndef UTS_SYSNAME
8 #define UTS_SYSNAME "Linux"
9 #endif
10
11 #ifndef UTS_NODENAME
12 #define UTS_NODENAME CONFIG_DEFAULT_HOSTNAME /* set by sethostname() */
13 #endif
14
15 #ifndef UTS_DOMAINNAME
16 #define UTS_DOMAINNAME "(none)" /* set by setdomainname() */
17 #endif
UTS_RELEASEはlinux/Makefile
404 # Read KERNELRELEASE from include/config/kernel.release (if it exists)
405 KERNELRELEASE = $(shell cat include/config/kernel.release 2> /dev/null)
406 KERNELVERSION = $(VERSION)$(if $(PATCHLEVEL),.$(PATCHLEVEL)$(if $(SUBLEVEL),.$(SUBLEVEL)))$(EXTRAVERSION)
1031 uts_len := 64
1032 define filechk_utsrelease.h
1033 if [ `echo -n "$(KERNELRELEASE)" | wc -c ` -gt $(uts_len) ]; then \
1034 echo '"$(KERNELRELEASE)" exceeds $(uts_len) characters' >&2; \
1035 exit 1; \
1036 fi; \
1037 (echo \#define UTS_RELEASE \"$(KERNELRELEASE)\";)
1038 endef
自動生成される順などまで含めると若干不正確だがこんな感じとなっている。
scripts/gcc-version.shにもそれっぽいのがあってscripts/Kbuild.includeから使われているが、ここで作られるcc-name, cc-versionなどは、開発者向けには解放されているものの(Documentation/kbuild/makefiles.txtに言及がある)、直接これを使ってどうというのはないように読めた。
終わりに
ネタでさらっと書いて終わりにしようと思ってたのに、kernel hacker編どうしてこうなった...