linux的64位操作系统对32位程序的兼容-全面分析

简介:
1.结构体ioctl_trans:
struct ioctl_trans {
    unsigned long cmd;
    ioctl_trans_handler_t handler;
    struct ioctl_trans *next;
};
该结构体提供了一个粘合层,用户可以动态注册一个ioctl_trans以便其提供64位和32位的粘合:
extern int register_ioctl32_conversion(unsigned int cmd,
                ioctl_trans_handler_t handler);
extern int unregister_ioctl32_conversion(unsigned int cmd);
整个系统的ioctl_trans连接成一个哈希表,放在ioctl32_hash_table变量中。每一个ioctl_trans的handler都是一个回调函数,在其中将64位的数据和32位的数据类型进行统一,统一成64位可以正确识别和处理的,以防在后续的64位代码中出错,比如一个32位的signed int为-1,需要将之转化成64个1而不是32个0加上32个1。
2.一套完整的系统调用:
如果不这样的话,32位程序的系统调用如何被路由到通过ioctl_trans们进行粘合的代码就成了问题,要知道x86-64已经不使用int 0x80作为触发系统调用的机制了,而使用syscall指令来触发。那么原来的32位程序都是用int 0x80来触发的,这下怎么办?办法就是仍然保留0x80号中断号,将其处理程序设置成ia32_syscall,它在ia32_sys_call_table中找具体的系统调用处理函数,具体在arch/x86_64/ia32/ia32entry.S中:
ENTRY(ia32_syscall)
        CFI_STARTPROC
        swapgs
        sti
        movl %eax,%eax
        pushq %rax
        cld
        SAVE_ARGS 0,0,1
        GET_THREAD_INFO(%r10)
        testl $(_TIF_SYSCALL_TRACE|_TIF_SYSCALL_AUDIT),threadinfo_flags(%r10)
        jnz ia32_tracesys
ia32_do_syscall:
        cmpl $(IA32_NR_syscalls),%eax
        jae  ia32_badsys
        IA32_ARG_FIXUP
        call *ia32_sys_call_table(,%rax,8) # xxx: rip relative
...
ia32_sys_call_table:
        .quad sys_restart_syscall
        .quad sys_exit
        .quad stub32_fork
        .quad sys_read
...
    .quad compat_sys_ioctl
...
在arch/x86_64/kernel/traps.c的trap_init函数中将ia32_syscall设置成0x80号中断的处理程序:
set_system_gate(IA32_SYSCALL_VECTOR, ia32_syscall);//#define IA32_SYSCALL_VECTOR 0x80
那么使用sysenter的怎么办呢? 这是通过在exec的时候由内核检测到其是32位程序是动态将处理代码map到gate处的,要知道x86-64也不使用sysenter机制进行系统调用。那64位的x86-64怎么系统调用呢?在arch/x86-64/kernel/entry.S中有ENTRY(system_call)这个标志,在arch/x86_64/kernel/setup64.c中的syscall_init函数中有以下一行:
wrmsrl(MSR_LSTAR, system_call);
可见64位的x86-64是通过一个MSR寄存器来保存系统调用处理地址的,而不再是通过中断。至于说机器如何处理这个信息以及这个寄存器如何影响系统运行,这已经到x86-64体系的cpu实现硬件问题了,和本文的linux系统的要旨无关,此处简略(再说不简略也不行啊,我也不会啊)。
3.总结
由于硬件指令的兼容,32位的程序在用户态不受任何影响的运行,由于内核保留了0x80号中断作为32位程序的系统调用服务,因此32位程序可以安全触发0x80号中断使用系统调用,由于内核为0x80中断安排了另一套全新的系统调用表,因此可以安全地转换数据类型成一致的64位类型,再加上应用级别提供了两套c库,可以使64位和32位程序链接不同的库。因此linux的64-32兼容搞得非常好。
     为了看一下在x86-64上64位程序和32位程序是如何执行系统调用的,写一个最简单的测试程序:
#include <sys/types.h>
#include <unistd.h>
int main()
{
        getpid();
}
之所以选择getpid是因为它没有参数,最简单,将之在Red Hat 32位机器上按照如下命令行编译:
gcc test.c -o test-32 -g
然后再将之在64位机器上同样方式编译,只是可执行文件名字变为test-64。接下来首先gdb test-32:
(gdb) b main
...
(gdb) r
...
(gdb) b getpid
Breakpoint 2 at 0xf7f3d430
(gdb) disassemble  0xf7f3d430 0xf7f3d43a
0xf7f3d430 <getpid+0>:  mov    $0x14,%eax    #0x14是20,正是getpid的系统调用号
0xf7f3d435 <getpid+5>:  int    $0x80         #32位程序以int 0x80触发系统调用
0xf7f3d437 <getpid+7>:  ret    
0xf7f3d438 <getpid+8>:  nop    
0xf7f3d439 <getpid+9>:  nop    
End of assembler dump.
(gdb)   
结果全部在,可见即使在64位机器上,32位程序仍然使用int 0x80触发系统调用,在内核中已经注册了0x80的中断处理函数。接下来再试一下64位的程序如何触发系统调用,执行gdb test-64:
(gdb) b main
...
(gdb) r
...
(gdb) b getpid
Breakpoint 2 at 0x32fbf90f40
(gdb) disassemble 0x32fbf90f40 0x32fbf90f70
Dump of assembler code from 0x32fbf90f40 to 0x32fbf90f70:
0x00000032fbf90f40 <getpid+0>:  mov    %fs:0x94,%edx
0x00000032fbf90f48 <getpid+8>:  test   %edx,%edx
0x00000032fbf90f4a <getpid+10>: mov    %edx,%eax
0x00000032fbf90f4c <getpid+12>: jle    0x32fbf90f50 <getpid+16>
0x00000032fbf90f4e <getpid+14>: repz retq 
0x00000032fbf90f50 <getpid+16>: jne    0x32fbf90f5e <getpid+30>
0x00000032fbf90f52 <getpid+18>: mov    %fs:0x90,%eax
0x00000032fbf90f5a <getpid+26>: test   %eax,%eax
0x00000032fbf90f5c <getpid+28>: jne    0x32fbf90f4e <getpid+14>
0x00000032fbf90f5e <getpid+30>: mov    $0x27,%eax #系统调用号装入eax
0x00000032fbf90f63 <getpid+35>: syscall         #执行系统调用
0x00000032fbf90f65 <getpid+37>: test   %edx,%edx
0x00000032fbf90f67 <getpid+39>: jne    0x32fbf90f4e <getpid+14>
0x00000032fbf90f69 <getpid+41>: mov    %eax,%fs:0x90
值得注意的是,在2.6.9内核的x86-64机器上,getpid和32位机器的getpid系统调用号有所不同,在64位上是39号,定义在include/asm-x86_64/unistd.h:
#define __NR_getpid                             39
__SYSCALL(__NR_getpid, sys_getpid)
而刚才看到过,32位兼容的getpid的系统调用号为20,定义在arch/x86_64/ia32/ia32entry.S中:
ia32_sys_call_table:
...
    .quad sys_getpid                /* 20 */
...

PS:千万不要觉得test.c很简单然后就stepi单指令跟踪哦,因为这会涉及到一大堆跳转,如果你不明白链接的知识,不了解GOT和PIC的话,那就麻烦大了,因此还是直接在getpid处下断比较直观,如果你想顺便把代码重定位和GOT等玩意儿搞了的话,也可以试一下,反正在调试器面前,整个地址空间都会暴露,想看什么都行,当然,要学会让/proc/<pid>/maps等文件帮忙哦。



 本文转自 dog250 51CTO博客,原文链接:http://blog.51cto.com/dog250/1271104

相关实践学习
阿里云图数据库GDB入门与应用
图数据库(Graph Database,简称GDB)是一种支持Property Graph图模型、用于处理高度连接数据查询与存储的实时、可靠的在线数据库服务。它支持Apache TinkerPop Gremlin查询语言,可以帮您快速构建基于高度连接的数据集的应用程序。GDB非常适合社交网络、欺诈检测、推荐引擎、实时图谱、网络/IT运营这类高度互连数据集的场景。 GDB由阿里云自主研发,具备如下优势: 标准图查询语言:支持属性图,高度兼容Gremlin图查询语言。 高度优化的自研引擎:高度优化的自研图计算层和存储层,云盘多副本保障数据超高可靠,支持ACID事务。 服务高可用:支持高可用实例,节点故障迅速转移,保障业务连续性。 易运维:提供备份恢复、自动升级、监控告警、故障切换等丰富的运维功能,大幅降低运维成本。 产品主页:https://www.aliyun.com/product/gdb
相关文章
|
8月前
|
安全 Linux iOS开发
Binary Ninja 5.1.8104 (macOS, Linux, Windows) - 反编译器、反汇编器、调试器和二进制分析平台
Binary Ninja 5.1.8104 (macOS, Linux, Windows) - 反编译器、反汇编器、调试器和二进制分析平台
730 53
Binary Ninja 5.1.8104 (macOS, Linux, Windows) - 反编译器、反汇编器、调试器和二进制分析平台
|
8月前
|
Linux API iOS开发
Binary Ninja 4.2.6455 (macOS, Linux, Windows) - 反编译器、反汇编器、调试器和二进制分析平台
Binary Ninja 4.2.6455 (macOS, Linux, Windows) - 反编译器、反汇编器、调试器和二进制分析平台
616 14
Binary Ninja 4.2.6455 (macOS, Linux, Windows) - 反编译器、反汇编器、调试器和二进制分析平台
|
9月前
|
数据管理 Linux iOS开发
Splunk Enterprise 9.4.5 (macOS, Linux, Windows) - 机器数据管理和分析
Splunk Enterprise 9.4.5 (macOS, Linux, Windows) - 机器数据管理和分析
302 0
|
10月前
|
Ubuntu Unix Linux
操作系统的最强入门科普(Unix/Linux篇)
下期文章,小枣君会重点聊聊Windows和macOS那条线。敬请关注! 如果大家觉得文章不错,还请帮忙多多转发!谢谢!
|
10月前
|
Web App开发 缓存 Rust
|
Unix Linux iOS开发
Splunk Enterprise 10.0.0 (macOS, Linux, Windows) - 搜索、分析和可视化,数据全面洞察平台
Splunk Enterprise 10.0.0 (macOS, Linux, Windows) - 搜索、分析和可视化,数据全面洞察平台
277 0
|
人工智能 运维 监控
基于操作系统控制平台-深入剖析CPUGPU Tracing分析
基于操作系统控制平台-深入剖析CPUGPU Tracing分析
基于操作系统控制平台-深入剖析CPUGPU Tracing分析
|
监控 Linux
Linux基础:文件和目录类命令分析。
总的来说,这些基础命令,像是Linux中藏匿的小矮人,每一次我们使用他们,他们就把我们的指令准确的传递给Linux,让我们的指令变为现实。所以,现在就开始你的Linux之旅,挥动你的命令之剑,探索这个充满神秘而又奇妙的世界吧!
258 19
|
缓存 网络协议 Linux
PCIe 以太网芯片 RTL8125B 的 spec 和 Linux driver 分析备忘
本文详细介绍了 Realtek RTL8125B PCIe 以太网芯片的规格以及在 Linux 中的驱动安装和配置方法。通过深入分析驱动源码,可以更好地理解其工作原理和优化方法。在实际应用中,合理配置和优化驱动程序可以显著提升网络性能和稳定性。希望本文能帮助您更好地使用和管理 RTL8125B,以满足各种网络应用需求。
1885 33
|
弹性计算 自然语言处理 Ubuntu
OS Copilot-操作系统智能助手-Linux新手小白的福音
OS Copilot是由阿里云推出的操作系统智能助手,专为Linux新手设计,支持自然语言问答、辅助命令执行等功能,极大提升了Linux系统的使用效率。用户只需通过简单的命令或自然语言描述问题,OS Copilot即可快速提供解决方案并执行相应操作。例如,查询磁盘使用量等常见任务变得轻松快捷。此外,它还支持从文件读取复杂任务定义,进一步简化了操作流程。虽然在某些模式下可能存在小问题,但总体上大大节省了学习和操作时间,提高了工作效率。
541 2
OS Copilot-操作系统智能助手-Linux新手小白的福音