如何获取linux-gate.so.1动态库


前面“Linux应用程序Helloworld入门”已经提到在Linux下每个可执行文件都依赖于几个最为基本的动态库,其中一个就是linux-gate.so.1。



从上面ldd给出的结果可以看出,这个linux-gate.so.1动态库有一些异样,libc.so.6的实际动态库路径在/lib/tls/i686/cmov/libc.so.6,而ld-linux.so.2是在/lib/ld-linux.so.2。那么不禁要问一个问题linux-gate.so.1这个动态库的路径是什么,是文件系统中那个文件呢?其实这个文件是内核映射上去的,并非存在实际的动态库文件,对于这个具体问题我们后续再做详细分析,这里仅仅做如何获取linux-gate.so.1动态库的方法。


通常情况下,比如在suse10, suse11系统上,linux-gate.so.1被映射到ffffe000-fffff000这个高端内存段里面。此时将这段内存导出到文件比较简单,可以使用下面脚本,帮助各位导出:

#!/bin/bash
VDSO_FILE_NAME=linux-gate.dso
cat /proc/self/maps|grep "vdso"
VDSO_ADDR=`cat /proc/self/maps|grep "vdso" |awk -F '-' '{print $1 }'`
echo "Current VDSO address is 0x$VDSO_ADDR"

VDSO_BLOCK=`echo |awk '{print substr("'${VDSO_ADDR}'",1,5)}'`
((SKIP_BLOCKS=16#$VDSO_BLOCK))
echo "We have $SKIP_BLOCKS blocks before VDSO library"

echo "Ready to generate $VDSO_FILE_NAME from block $SKIP_BLOCKS"
dd if=/proc/self/mem of=$VDSO_FILE_NAME bs=4096 skip=$SKIP_BLOCKS count=1

echo "Generate $VDSO_FILE_NAME Done"


在suse系统上执行的结果:

~> ./cat_linux_gate_so.sh
ffffe000-fffff000 ---p 00000000 00:00 0          [vdso]
Current VDSO address is 0xffffe000
We have 1048574 blocks before VDSO library
Ready to generate linux-gate.dso from block 1048574
1+0 records in
1+0 records out
4096 bytes (4.1 kB) copied, 4.2e-05 seconds, 97.5 MB/s
Generate linux-gate.dso Done

~> file -b linux-gate.dso
ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), stripped
~> objdump -T linux-gate.dso


linux-gate.dso:     文件格式 elf32-i386


DYNAMIC SYMBOL TABLE:
ffffe400 l    d  .text  00000000              .text
ffffe478 l    d  .eh_frame_hdr  00000000              .eh_frame_hdr
ffffe49c l    d  .eh_frame      00000000              .eh_frame
ffffe620 l    d  .useless       00000000              .useless
ffffe400 g    DF .text  00000014  LINUX_2.5   __kernel_vsyscall
00000000 g    DO *ABS*  00000000  LINUX_2.5   LINUX_2.5
ffffe440 g    DF .text  00000007  LINUX_2.5   __kernel_rt_sigreturn
ffffe420 g    DF .text  00000008  LINUX_2.5   __kernel_sigreturn

在ubuntu 上情况比较复杂,在此也耽误了不少时间,因为ubuntu映射到的内存地址是不固定的,每个进程映射的位置都是不同的。

多次执行:

cat /proc/self/maps|grep "vdso"

b7f47000-b7f48000 r-xp b7f47000 00:00 0          [vdso]

b7f5f000-b7f60000 r-xp b7f5f000 00:00 0          [vdso]

b7f54000-b7f55000 r-xp b7f54000 00:00 0          [vdso]


因此上面脚本无法实现同一个进程内映射内存导出。为了实现同一进程内导出,这里采用简单C编程的方法实现。

基本原理和上面脚本一致,首先通过/proc/self/maps找到vsdo映射内存的地址,然后导出映射内存到文件linux-gate.dso。

int main(int argc, char **argv)
{
	FILE *pFile = NULL;
	void *paddr = NULL;

	char buffer[BUFFER_SIZE];
	char address[BUFFER_SIZE];
	char block[BLOCK_SIZE];

	char c;
	int i, ineedreset, iblocks, iSize;
	
	pFile = fopen("/proc/self/maps", "r");
	if (NULL == pFile)
	{
		printf("/proc/self/maps fopen failed, error!\r\n");
		return 1;
	}

	/* 查找vdso动态库映射内存位置  */
	i = 0;
	ineedreset = 1;
	memset(buffer, 0, BUFFER_SIZE);
	while(1) 
	{
		c = fgetc (pFile);
		if (c != EOF) 
		{
			printf("%c", c);
			if (c == '\r' || c == '\n')
			{
				i = 0;
				ineedreset = 1;
			} else
			{
				if (ineedreset)
				{
					if (NULL != strstr(buffer, "vdso"))
					{
						printf("I have got vdso section.\r\n");
						break;
					}
					memset(buffer, 0, BUFFER_SIZE);
					ineedreset = 0;
				}			
				buffer[i++] = c;
			}
		}else
		{
			break;
		}
    }

	printf("vsdo line is:%s\r\n", buffer);
	fclose(pFile);
	pFile = NULL;
	
	/* 获取起始地址 */
	memset(address, 0, BUFFER_SIZE);
	for (i = 0; buffer[i] != '-'; i++)
	{
		address[i] = buffer[i];
		if (buffer[i] == '-')
			break;
	}

	paddr = (void *) Hex2Ulong(address);
	printf("Current VDSO address is 0x%x\r\n", paddr);
	iblocks = (unsigned long)paddr / BLOCK_SIZE;
	printf("We have %d blocks before VDSO library\r\n", iblocks);
	printf("Ready to generate linux-gate.dso from block %d\r\n", iblocks);

	/* 导出vdso动态文件 */
	pFile = fopen("./linux-gate.dso", "w");
	if (NULL == pFile)
	{
		printf("fopen linux-gate.dso failed, exit!\r\n");
		return 1;
	}

	printf("Head:0x%x-%c-%c-%c\r\n", *((char *)paddr + 0),*((char *)paddr + 1),*((char *)paddr + 2),*((char *)paddr + 3));
        memcpy(block, paddr, BLOCK_SIZE);
	iSize = fwrite(block, 1, BLOCK_SIZE, pFile);
	if (BLOCK_SIZE != iSize)
	{
		perror("fwrite error:\r\n");
	}
	printf("copy %d/%d bytes from 0x%x to the file\r\n", iSize, BLOCK_SIZE, paddr);

	fclose(pFile);
	printf("Generate linux-gate.dso Done\r\n");

	return 0;
}



 

然后看下导出的结果:

这样我们就能在动态映射的情况下也能导出动态库。app_linux_gate_so.c如果做适当修改就能导出所有映射的动态内存库,用于DEBUG和验证了。

参考资料:

【1】二进制,八进制,十进制和十六进制 之间的关系转换

注:这里使用的unsigned long Hex2Ulong(char s[])是简单实现,供参考,相见参考资料

#define YES 1
#define NO  0

unsigned long Hex2Ulong(char s[])
{
    int i;
    unsigned long n;
    int inhex, digital;

    i = 0;
    if (s[i] == '0')
    {
        i++;
        if (s[i] == 'x' || s[i] == 'X')
        {
            i++;
        }
    }

    n = 0;
    inhex = YES;
    for (; inhex == YES; i++)
    {
        if (s[i] >= '0' && s[i] <= '9')
            digital = s[i] - '0';
        else if (s[i] >= 'a' && s[i] <= 'f')
            digital = s[i] - 'a' + 10;
        else if (s[i] >= 'A' && s[i] <= 'F')
            digital = s[i] - 'A' + 10;
        else
        {
            inhex = NO;
            break;
        }
        n = 16 * n + digital;
    }

    return n;
}



评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值