解決CentOS 6.10安裝VirtualBox增強插件報redefinition of set_nlink錯誤

CentOS 6早已退休,沒人維護了,但最近需要測試一個很老的服務器程序,要跑在CentOS 6上,那就在VirtualBox虛擬機上裝一個吧。由於測試環境是不能上網的,因此我特意下載了一個CentOS-6.10-x86_64-bin-DVD1.iso鏡像,這樣不僅安裝系統不需要連網,安裝一些基本的軟件也不需要連網了。

要測試的是服務器程序,因此是不需要桌面系統的,安裝的時候選擇了個Database Serve,系統安裝過程很順利,這裏不再多說。測試這個程序,需要把win下的一個目錄掛載到虛擬機的CentoS 6裏,最合適的方式當然是使用VirtualBox的目錄共享功能,這個功能需要安裝VirtualBox的增強功能(VirtualBox Guest Additions)。

和win下不一樣,VirtualBox的增強功能在Linux下是沒有預編譯二進制文件的,只能通過源碼編譯的方式安裝,因此需要預先安裝編譯環境和內核依賴。由於是離線環境,需要使用CentOS-6.10-x86_64-bin-DVD1.iso鏡像作爲源來安裝軟件。

  1. CentOS-6.10-x86_64-bin-DVD1.iso插入虛擬機光驅作爲安裝源

  2. 加載光驅

[root@localhost ~]# mkdir /media/cdrom
[root@localhost ~]# mount /dev/cdrom /media/cdrom
mount: block device /dev/sr0 is write-protected, mounting read-only
  1. 關閉其他源,使用cdrom作爲源安裝編譯軟件
[root@localhost ~]# yum --disablerepo=\* --enablerepo=c6-media install gcc gcc-c++ cmake
  1. 安裝編譯VirtualBox Guest Additions所需要的內核源碼
[root@localhost ~]# yum --disablerepo=\* --enablerepo=c6-media install "kernel-devel-uname-r == $(uname -r)"
  1. 卸載光驅
umount /media/cdrom

現在準備工作已經完畢,準備安裝增強功能。點擊菜單“設備/分配光驅/移除虛擬”盤來移除CentOS-6.10-x86_64-bin-DVD1.iso鏡像,點擊 “設備/安裝增強功能”來加載VBoxGuestAdditions.iso鏡像。

  1. 重新掛載光驅
[root@localhost ~]# mount /dev/cdrom /media/cdrom
mount: block device /dev/sr0 is write-protected, mounting read-only
  1. 執行VirtualBox Guest Additions安裝程序(由於沒有安裝桌面,因此這裏需要--nox11參數)
[root@localhost ~]# cd /media/cdrom
[root@localhost cdrom]# sh VBoxLinuxAdditions.run --nox11
Verifying archive integrity... All good.
Uncompressing VirtualBox 6.1.16 Guest Additions for Linux........
VirtualBox Guest Additions installer
Copying additional installer modules ...
Installing additional modules ...
VirtualBox Guest Additions: Starting.
VirtualBox Guest Additions: Building the VirtualBox Guest Additions kernel 
modules.  This may take a while.
VirtualBox Guest Additions: To build modules for other installed kernels, run
VirtualBox Guest Additions:   /sbin/rcvboxadd quicksetup <version>
VirtualBox Guest Additions: or
VirtualBox Guest Additions:   /sbin/rcvboxadd quicksetup all
VirtualBox Guest Additions: Building the modules for kernel 
2.6.32-754.el6.x86_64.

VirtualBox Guest Additions: Look at /var/log/vboxadd-setup.log to find out what 
went wrong
VirtualBox Guest Additions: modprobe vboxsf failed
Building the VirtualBox Guest Additions kernel modules.  This may take a while.
To build modules for other installed kernels, run
  /sbin/rcvboxadd quicksetup <version>
or
  /sbin/rcvboxadd quicksetup all
Running kernel modules will not be replaced until the system is restarted
vboxadd-service.sh: Starting VirtualBox Guest Addition service.

出乎我的意料,居然沒有安裝成功。按提示查看/var/log/vboxadd-setup.log

[root@localhost cdrom]# cat /var/log/vboxadd-setup.log
Could not find the X.Org or XFree86 Window System, skipping.

由於沒裝桌面,當然沒有X.Org,這是一個正常的提示,不會導致編譯失敗。但是在/var/log目錄下,我還注意到還有一個vboxadd-setup.log.1,程序員的直覺告訴我要看下里面有啥。

[root@localhost cdrom]# cat /var/log/vboxadd-setup.log.1 
Building the main Guest Additions 6.1.16 module for kernel 2.6.32-754.el6.x86_64.
Building the shared folder support module.
Error building the module.  Build output follows.
make V=1 CONFIG_MODULE_SIG= CONFIG_MODULE_SIG_ALL= -C /lib/modules/2.6.32-754.el6.x86_64/build M=/tmp/vbox.0 SRCROOT=/tmp/vbox.0 -j1 modules
test -e include/linux/autoconf.h -a -e include/config/auto.conf || (            \
        echo;                                                           \
        echo "  ERROR: Kernel configuration is invalid.";               \
        echo "         include/linux/autoconf.h or include/config/auto.conf are missing.";      \
        echo "         Run 'make oldconfig && make prepare' on kernel src to fix it.";  \
        echo;                                                           \
        /bin/false)
mkdir -p /tmp/vbox.0/.tmp_versions ; rm -f /tmp/vbox.0/.tmp_versions/*
make -f scripts/Makefile.build obj=/tmp/vbox.0
  gcc -Wp,-MD,/tmp/vbox.0/.vfsmod.o.d  -nostdinc -isystem /usr/lib/gcc/x86_64-redhat-linux/4.4.7/include -Iinclude  -I/usr/src/kernels/2.6.32-754.el6.x86_64/include/uapi -I/usr/src/kernels/2.6.32-754.el6.x86_64/arch/x86/include -Iarch/include/generated -Iinclude -include /usr/src/kernels/2.6.32-754.el6.x86_64/include/linux/kconfig.h -D__KERNEL__ -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs -fno-strict-aliasing -fno-common -Werror-implicit-function-declaration -Wno-format-security -fno-delete-null-pointer-checks -O2 -m64 -mtune=generic -mno-red-zone -mcmodel=kernel -funit-at-a-time -maccumulate-outgoing-args -fstack-protector -DCONFIG_AS_CFI=1 -DCONFIG_AS_CFI_SIGNAL_FRAME=1 -DCONFIG_AS_CFI_SECTIONS=1 -DCONFIG_AS_AVX=1 -pipe -Wno-sign-compare -fno-asynchronous-unwind-tables -mno-sse -mno-mmx -mno-sse2 -mno-3dnow -mindirect-branch=thunk-extern -mindirect-branch-register -DRETPOLINE -Wframe-larger-than=2048 -Wno-unused-but-set-variable -fno-omit-frame-pointer -fno-optimize-sibling-calls -g -pg -Wdeclaration-after-statement -Wno-pointer-sign -fno-strict-overflow -fno-dwarf2-cfi-asm -fconserve-stack -Wno-declaration-after-statement -fno-pie -include /tmp/vbox.0//include/VBox/VBoxGuestMangling.h -fshort-wchar -I/usr/src/kernels/2.6.32-754.el6.x86_64/include -I/tmp/vbox.0/ -I/tmp/vbox.0/include -I/tmp/vbox.0/r0drv/linux -D__KERNEL__ -DMODULE -DRT_WITHOUT_PRAGMA_ONCE -DRT_OS_LINUX -DIN_RING0 -DIN_RT_R0 -DIN_SUP_R0 -DVBOX -DVBOX_WITH_HGCM -DIN_MODULE -DIN_GUEST -DIN_GUEST_R0 -DRT_NO_EXPORT_SYMBOL -DVBOX_WITH_64_BITS_GUESTS -DRT_ARCH_AMD64  -DMODULE -D"KBUILD_STR(s)=#s" -D"KBUILD_BASENAME=KBUILD_STR(vfsmod)"  -D"KBUILD_MODNAME=KBUILD_STR(vboxsf)" -D"DEBUG_HASH=41" -D"DEBUG_HASH2=24" -c -o /tmp/vbox.0/.tmp_vfsmod.o /tmp/vbox.0/vfsmod.c
In file included from /tmp/vbox.0/vfsmod.c:44:
/tmp/vbox.0/vfsmod.h:102: error: redefinition of ‘set_nlink’
include/linux/fs.h:1892: note: previous definition of ‘set_nlink’ was here
make[2]: *** [/tmp/vbox.0/vfsmod.o] Error 1
make[1]: *** [_module_/tmp/vbox.0] Error 2
make: *** [vboxsf] Error 2
Could not find the X.Org or XFree86 Window System, skipping.

對於寫C++的我,這個提示很熟悉了,就是VirtualBox的源代碼和CentOS 6.10的內核源代碼版本沒對上,不兼容,set_nlink這個符號重複定義。除非是改內核源代碼或者VirtualBox的源代碼,不然基本上沒救了。我用VirtualBox好多年了,之前也遇到過這種情況,多半是舊版本的VirtualBox安裝新版本的Linux,由於新的內核源代碼做了一些改動,與舊的VirtualBox不兼容,只能等VirtualBox更新。但CentOS 6已經停止支持,應該是沒有更新了,想要自己改的話,要對Linux內核和VirtualBox的源代碼比較熟悉纔行,我基本是放棄了,這事就擱下了。

過了好幾天,剛好有點空閒時間,又想起來這事。我定了幾個方案,1是使用CentOS 7來測試這個程序,不過這個最終測出來的結果可能差比較遠;2是使用其他方式掛載目錄或者不掛載,使用sftp來同步目錄,這樣整個測試過程比較難受;3是刪掉當前的虛擬機和VirtualBox,再安裝一個低版本的VirtualBox試試,這個比較費時。這有一個令我非常好奇的點,那就是CentOS 6.10雖然是一個比較老的系統,但距離停止支持也才幾年,這個系統的使用這麼廣泛,VirtualBox沒有理由不再支持這個系統的。我的NAS上還用VirtualBox跑着一個更老的xp,也沒見VirtualBox放棄支持,我決定還是查一下原因,看看能不能搶救一下。

先是google半天“VirtualBox set_nlink”關鍵字,啥有用的東西都沒找着,然後查“CentOS modprobe vboxsf failed”,這個倒是很多,但翻了半天沒有和我這個相同的。反正我也是寫C++的,於是想看一下源碼。由於VBoxLinuxAdditions.run是一個自解壓腳本,報錯的文件/tmp/vbox.0/vfsmod.h只是一個臨時文件,程序結束後就沒了,看不了,需要手動解壓VBoxLinuxAdditions.run

sh VBoxLinuxAdditions.run --target /tmp/VBoxLinuxAdditions
[root@localhost cdrom]# ls /tmp/VBoxLinuxAdditions/
deffiles   install.sh   VBoxGuestAdditions-amd64.tar.bz2
installer  routines.sh  VBoxGuestAdditions-x86.tar.bz2

這個網上查的解壓指令好像不太對,會執行一次安裝操作(後來意識到好像還要加一個extra-only之類的參數,記不太清了,sh VBoxLinuxAdditions.run --help應該會列出所有參數),不過文件確實是解壓到了對應的目錄。從目錄文件名來看,解壓後應該是執行了install.sh,64位的源碼應該是在VBoxGuestAdditions-amd64.tar.bz2裏。

[root@localhost cdrom]# cd /tmp/VBoxLinuxAdditions/
[root@localhost cdrom]# mkdir VBoxGuestAdditions-amd64
[root@localhost VBoxLinuxAdditions]# tar -xf VBoxGuestAdditions-amd64.tar.bz2 -C VBoxGuestAdditions-amd64

搜索一下,就能定位到出錯的文件VBoxGuestAdditions-amd64/src/vboxguest-6.1.16/vboxsf/vfsmod.h,查看102行

#if RTLNX_VER_MAX(3,2,0) && !RTLNX_RHEL_MIN(6, 10)
DECLINLINE(void) set_nlink(struct inode *pInode, unsigned int cLinks)
{
    pInode->i_nlink = cLinks;
}
#endif

從代碼來看,這明顯是有做版本兼容的,而且是以6.10爲分界。我嘗試直接去掉這個定義,然後重新打包一個新的VBoxGuestAdditions-amd64.tar.bz2到當前目錄,再執行sh install.sh,這次報錯不一樣了,但沒安裝成功,懷疑是參數沒傳對,但install.sh這個腳本很大,一步步跟下去太花時間,想重新再打一個run腳本,這個又不會弄,放棄了。決定還是再看看源碼的問題,感覺像是哪個環境變量設置不對導致的。再找一下RTLNX_VER_MAX``RTLNX_RHEL_MIN的定義,發現它們在VBoxGuestAdditions-amd64/src/vboxguest-6.1.16/vboxsf/include/iprt/linux/version.h

/** @def RTLNX_VER_MAX
 * Evaluates to true if the linux kernel version is less to the one specfied
 * (exclusive). */
#define RTLNX_VER_MAX(a_Major, a_Minor, a_Patch) \
    (LINUX_VERSION_CODE < KERNEL_VERSION(a_Major, a_Minor, a_Patch))

/** @def RTLNX_RHEL_MIN
 * Require a minium RedHat release.
 * @param a_iMajor      The major release number (RHEL_MAJOR).
 * @param a_iMinor      The minor release number (RHEL_MINOR).
 * @sa RTLNX_RHEL_MAX, RTLNX_RHEL_RANGE, RTLNX_RHEL_MAJ_PREREQ
 */
#if defined(RHEL_MAJOR) && defined(RHEL_MINOR)
# define RTLNX_RHEL_MIN(a_iMajor, a_iMinor) \
     ((RHEL_MAJOR) > (a_iMajor) || ((RHEL_MAJOR) == (a_iMajor) && (RHEL_MINOR) >= (a_iMinor)))
#else
# define RTLNX_RHEL_MIN(a_iMajor, a_iMinor) (0)
#endif

根據編譯時的日誌,當前內核版本爲2.6.32-754.el6.x86_64,猜測一下,CentOS 6.10這個版本RHEL_MAJOR爲6,RHEL_MINOR爲10,那就不應該啓用這個set_nlink的結構體定義纔對。但是既然啓用了,那就是引用錯誤的頭文件,或者是我裝錯了內核開發庫版本。但是這是從DVD裝的,不是從網絡裝的,版本錯了就會找不到安裝包,也不太可能裝錯啊。
使用yum再次檢查

[root@localhost VBoxLinuxAdditions]# yum list installed | grep kernel
abrt-addon-kerneloops.x86_64
dracut-kernel.noarch    004-411.el6    @anaconda-CentOS-201806291108.x86_64/6.10
kernel.x86_64           2.6.32-754.el6 @anaconda-CentOS-201806291108.x86_64/6.10
kernel-devel.x86_64     2.6.32-754.el6 @c6-media                                
kernel-firmware.noarch  2.6.32-754.el6 @anaconda-CentOS-201806291108.x86_64/6.10
kernel-headers.x86_64   2.6.32-754.el6 @anaconda-CentOS-201806291108.x86_64/6.10
libreport-plugin-kerneloops.x86_64

kernel-devel.x86_64 2.6.32-754.el6 @c6-media這個應該是我安裝的,從版本號上來看是沒錯的,那再查下是不是引用了錯誤的頭文件。這個文件裏引用了#include <linux/version.h>,於是找到/usr/include/linux/version.h,發現有點不太對

[root@localhost VBoxLinuxAdditions]# cat /usr/include/linux/version.h
#define LINUX_VERSION_CODE 132640
#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
#define RHEL_MAJOR 6
#define RHEL_MINOR 9
#define RHEL_RELEASE_VERSION(a,b) (((a) << 8) + (b))
#define RHEL_RELEASE_CODE 1545
#define RHEL_RELEASE 753
#define RHEL_16KSTACK_BUILD 520

這裏的RHEL_MINOR居然是9而不是10,我懷疑我下錯了鏡像,但看了下release,好像沒錯。

[root@localhost VBoxLinuxAdditions]# cat /etc/centos-release 
CentOS release 6.10 (Final)

現在懷疑我下載了被修改的鏡像,到官網覈對一下checksum。官網(https://vault.centos.org/6.10/)給出的鏡像列表中有mirror.nsc.liu,進去後有checksum。

可以看到,sha1的值是對得上的。不管那麼多了,我直接手動修改/usr/include/linux/version.h中的RHEL_MINOR爲10,重新編譯,結果發現還是同樣的錯誤。再仔細查,它引用的其實是/usr/src/kernels/2.6.32-754.el6.x86_64/include/linux/version.h。把這裏也改掉後,編譯成功,VirtualBox的增強功能也沒問題。

由於CentOS 6已停止支持,這個我也不打算到CentOS社區那邊去問了,或者已經有人提了issue我沒搜索到,或者是這個問題已修復,但沒更新DVD,需要聯網更新,這個就不再深究。這裏只是做個筆記。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章