解除clock_gettime@@GLIBC_2.17的依赖

在x86_64的Debian Jessie上交叉编译一个mipsel架构的程序,能静态链接的都静态链接了。

程序编译好传送到mipsel架构的设备上时,ldd查看库的依赖,却提示

(⊙﹏⊙)b

mipsel设备上运行着Debian Wheezy,使用的是glibc 2.13:

尝试了更新到glibc 2.17,结果运行所有动态链接glibc的程序都提示Segmentation fault或Bus error,眼看要变砖!!!∑(゚Д゚ノ)ノ,最后用一个静态链接版本的busybox救回来( ̄▽ ̄)~*。

换到Wheezy再交叉编译?然而Debian的Wiki:

ヽ(ー_ー)ノ,还不想自己编译gcc。

查找了下资料,没找到让gcc链接到自定义libc的方法。

查找了下资料,根据此文章:http://blog.csdn.net/hbuxiaofei/article/details/46012833,发现我交叉编译的程序也是clock_gettime()函数导致的,但是这作者你只提供分析方法却不提供解决方案算什么意思ヽ(ー_ー)ノ。

找了下这个函数的资料,发现除了glibc外,动态链接库librt有独立提供这个函数: https://stackoverflow.com/questions/2418157/ubuntu-linux-c-error-undefined-reference-to-clock-gettime-and-clock-settim,于是在g++编译参数中加入了-lrt,编译完成后,把程序传送到mipsel架构的设备上,再次ldd,没有了”`GLIBC_2.17′ not found”的提示,程序运行正常,成功解除对glibc 2.17的依赖(o゚▽゚)o  :

探讨C语言返回struct的实现

C语言基本类型的返回通过寄存器实现。

寄存器容量不大,一个结构体的大小不定,多塞几个成员,很容易就会超过寄存器的容量,不太可能通过寄存器一次性返回一个结构体。

把以下代码转为汇编,探个究竟:

优化后的代码可能比较晦涩,64位寄存器数量是32位的一倍,为了简便理解,关闭代码优化,并且转为32位汇编,命令:

结果:

汇编代码已写上注释。

在get_struct()函数中,调用fun()之前,会首先把get_struct()函数中要接收返回值的地址压入栈中。

在fun()中,要返回值时,直接从get_struct()的栈帧取得保存结构体值的地址,直接对get_struct()的栈帧进行操作。

总的来说,一个函数要返回一个结构体,则从调用者函数的栈帧中取得接收结构体的地址(被赋值结构体的指针),直接对调用者函数的栈帧进行修改。

关于Visual Studio C LANG下pow()函数的重载

pow

当初看到Visual Studio 2010的这个提示,我真当VS实现了C LANG的函数重载。

今天即兴尝试VS下C LANG的函数重载,虽然IDE没报语法错误,但编译器编译时会报错。无奈之下只好打开长长的math.h一探究竟。

通过全文搜索很容易找到math.h声明的pow()原型:

声明的这些原型一眼看上去没什么问题,挺正常的重载方式。不过math.h内容有点多,不细心点还是看不出来,实际上pow()的重载使用了条件编译:

查找了一下资料,得知”__cplusplus”是CPP下才有的预定义宏,毕竟”C Plus Plus”……

也就是说,编译C LANG的情况下,仅有这个原型起作用:

其他的重载用的原型根本就不会被编译。

 

这下结论明确,C LANG根本没有函数重载,那就更加别谈对pow()的重载了,只是IDE对条件编译的检测不完善,才有本文首图的那种提示。

最后自己弄一个伪函数重载玩玩:

 

为何C语言函数可以返回struct

struct可由多个数据类型构成,初步看起来应该类似数组,变量本身是一个指向首字节的指针,无法直接通过此指针赋值struct,在函数中声明为自动变量时,也无法返回此指针所指向的值。

但事实上就并非如此,struct之间可以直接赋值,使用指针指向其地址时需要使用”&”,函数中声明为自动变量时可以直接return其值。

那struct变量本身到底是何?

写了这样一个东西,运行后瞬间明白:

struct变量就和基本类型,int, char等一样,直接指向其值,而且struct有多少字节,那个值就有多少字节。

也就是说,我们声明一个struct类型时,就是的的确确等于声明了一个确定大小的类型,就和int, char等基本类型一致。

既然是指向值,有确定大小,而且不是地址,那么就可以解释为何可以直接赋值以及可以被函数return了。

 

返回数组:

 

Linux chroot Shell —— 简单chroot指定用户

对于Linux服务器,有时需要开放登录权限给一部分用户,但并非所有用户都愿意安分守己,也无法知道谁是不安分守己的,给多一个这些用户开放登录权限,系统的威胁就多了一分。

目前来说比较好的方案是chroot,调用了chroot(),就能把的根目录改成指定的根目录。何为根目录?顾名思义,“根”就是所有目录的“根”,一切目录均从“根”而生,没有“根”就没有其他目录,换句话说,“根目录”就是最原始的目录。你无法访问“根”之前前的数据,没有“根”的时候,怎么可以发展数据呢?

二进制程序chroot乃GNU的核心程序中的一个,Linux发行版本一般都有Coreutils,不过二进制版的chroot在这种情况下帮不上忙,因为仅有特权用户(root)才能调用chroot(),why?想想如果任何人都能调用chroot,如果用户自建了一个自定义了root密码的根文件系统,那他岂不是可以chroot进去后把自己提升为root?

SSH自带chroot功能,不过也仅仅是帮你chroot,如果让所有用户共享一个chroot文件系统,一个用户的数据可能会泄露到其他用户手上,要让不同用户使用不同chroot文件系统,管理和配置起来又麻烦。不仅如此,每chroot一个用户,就需要更改SSH的配置文件,记录多了异常麻烦。

如何简单地管理chroot文件系统,还方便地chroot用户?

Linux-CHROOT-SHELL就是为了解决此问题而生的。

拥有了Linux-CHROOT-SHELL,你只需要建立且维护一个chroot根文件系统。

拥有了Linux-CHROOT-SHELL,你可以让所有用户使用的chroot环境相互隔离。

拥有了Linux-CHROOT-SHELL,你不再需要维护多余的配置文件,繁杂的配置记录。

多说没用,下面让我来告诉大家如何使用Linux-CHROOT-SHELL。

 

Linux-CHROOT-SHELL由C语言编写,代码托管在GIT服务器上,因此需要安装gcc与git:

从GIT服务器上获取源码,并进入源码目录:

编译Linux-CHROOT-SHELL:

一切无误后,编译完毕的二进制文件将会存放到/usr/bin/chroot_shell。

 

由于调用chroot(),内核的要求是特权用户身份,因此需要给予用户临时的root权限,不过不用担心,一旦chroot完毕后,本shell将会自动把身份更改为原用户的身份:

执行这条命令后,程序将会获得set uid和set gid属性,所有执行本程序的用户都可以取得文件所有者和所属群组的权限,所以必须确保本文件的所有者和所属群组为root。不过,既然你编译的时候能把文件输出到/usr/bin/,那你一定是以用root身份操作的,那么本文件的所有者和所属群组也必定是root。

 

接下来构建chroot文件系统:

为了简单起见,这里直接使用了OpenVZ的模板构建chroot文件系统。

构建完毕后,可以对根文件系统进行设置:

例如更改/etc/resolv.conf中所设置的DNS服务器。

设置完毕后,执行exit退出:

 

最后一步,创建用户:

创建用户的方法和平常差不多,只不过需要把用户的Shell指定为/usr/bin/chroot_shell:

添加用户成功后,更改用户密码:

如果要更改原有用户的Shell,使用usermod即可:

别忘了,还要同步passwd的记录到chroot文件系统:

可以把这个命令写到cron任务中,实现周期性同步。

 

接下来,你可以尝试使用指定了chroot_shell的用户登录:

user1

user2

可以看到,chroot_user和chroot_user2互相无法访问对方的目录,根据下图,可以看出两个chroot的用户无法访问原根存在的文件,说明chroot成功:

root

不仅如此,chroot后,用户还能访问自己家目录原有的数据,此外,除了用户自己的家目录和/tmp,所有其他目录均为只读模式,/tmp也实现了隔离,所有用户使用的/tmp不相干扰,相互独立。

 

由于没有挂载/proc和/dev,用户自然无法调用程序查看系统信息。

 

有了chroot shell,chroot用户变得简单起来,再也不用担心给了用户登录权限后,给服务器增加安全隐患了。