linux – 确定特定进程是32位还是64位

给定2.6.x或更新的Linux内核以及能够运行ELF32和ELF64二进制文件的现有用户空间(即远超过How do I know that my CPU supports 64bit operating systems under Linux?),如何确定给定进程(通过PID)是以32位还是64位模式运行?

天真的解决方案是运行:

file -L /proc/pid/exe | grep -o 'ELF ..-bit [LM]SB'

但是这些信息是否直接暴露在/ proc中而不依赖于libmagic?

解决方法:

如果你想限制自己的ELF检测,你可以自己读取/ proc / $PID / exe的ELF header.这非常简单:如果文件中的第5个字节是1,那么它是32位二进制文​​件.如果它是2,那么它是64位.为了增加健全性检查:

>如果前5个字节是0x7f,“ELF”,则1:它是32位ELF二进制文件.
>如果前5个字节是0x7f,“ELF”,则2:它是64位ELF二进制文件.
>否则:它没有结果.

你也可以使用objdump,但这会夺走你的libmagic依赖,并用libelf替换它.

另一种方法:你也可以解析/ proc / $PID / auxv文件.根据proc(5):

This contains the contents of the ELF interpreter information
passed to the process at exec time. The format is one
unsigned long ID plus one unsigned long value for each entry.
The last entry contains two zeros.

无符号长键的含义在/usr/include/linux/auxvec.h中.你想要AT_PLATFORM,它是0x00000f.不要引用我,但它似乎应该被解释为char *来获取平台的字符串描述.

你可能会发现this * question很有用.

另一种方式:您可以指示动态链接器(man ld)转储有关可执行文件的信息.它打印出标准输出解码的AUXV结构.警告:这是一个黑客,但它的工作原理.

LD_SHOW_AUXV=1 ldd /proc/$SOME_PID/exe | grep AT_PLATFORM | tail -1

这将显示如下:

AT_PLATFORM:     x86_64

我在32位二进制文​​件上尝试了它而得到了i686.

工作原理:LD_SHOW_AUXV = 1指示动态链接器在运行可执行文件之前转储已解码的AUXV结构.除非你真的想让你的生活变得有趣,否则你要避免实际运行所述可执行文件.在不实际调用main()函数的情况下加载和动态链接它的一种方法是在其上运行ldd(1).缺点:shell启用了LD_SHOW_AUXV,因此您将获得AUXV结构的转储:subshel​​l,ldd和目标二进制文件.所以我们grep for AT_PLATFORM,但只保留最后一行.

解析auxv:如果你自己解析auxv结构(不依赖于动态加载器),那么就有一个难题:auxv结构遵循它描述的进程规则,因此sizeof(unsigned long)将为4表示32位进程和8位64位进程.我们可以为我们工作.为了使其在32位系统上工作,所有密钥代码必须为0xffffffff或更小.在64位系统上,最重要的32位将为零.英特尔机器是小端,所以这32位跟随内存中最不重要的.

因此,您需要做的就是:

1. Read 16 bytes from the `auxv` file.
2. Is this the end of the file?
3.     Then it's a 64-bit process.
4.     Done.
5. Is buf[4], buf[5], buf[6] or buf[7] non-zero?
6.     Then it's a 32-bit process.
7.     Done.
8. Go to 1.

解析地图文件:这是吉尔斯建议的,但不太有用.这是一个修改过的版本.它依赖于读取/ proc / $PID / maps文件.如果文件列出64位地址,则进程为64位.否则,它是32位.问题在于内核将通过从4个组中的十六进制地址中剥离前导零来简化输出,因此长度hack不能正常工作.拯救:

if ! [ -e /proc/$pid/maps ]; then
    echo "No such process"
else
    case $(awk </proc/$pid/maps -- 'END { print substr($1, 0, 9); }') in
    *-) echo "32 bit process";;
    *[0-9A-Fa-f]) echo "64 bit process";;
    *) echo "Insufficient permissions.";;
    esac
 fi

这通过检查进程的最后一个内存映射的起始地址来工作.它们被列为12345678-deadbeef.因此,如果进程是32位,那么该地址将是八位十六进制数字,第九位将是一个连字符.如果它是64位的,那么最高地址将比这长.第九个字符是十六进制数字.

请注意:除了第一个和最后一个方法之外的所有方法都需要Linux内核2.6.0或更新,因为之前没有auxv文件.

上一篇:高并发(二):通用设计方法


下一篇:linux – 如何以改变文件部分数据长度的方式修改ELF文件?