Skip to content

16.21 linux-gate.so.1是什么东西

https://scz.617.cn/unix/201205101859.txt

Q:

$ ldd which col linux-gate.so.1 => (0xffffe000) libc.so.6 => /lib/i686/cmov/libc.so.6 (0xb7e60000) /lib/ld-linux.so.2 (0x80000000) $ find /lib -name linux-gate.so.1 -type f -print $ find /usr/lib -name linux-gate.so.1 -type f -print $

用ldd时注意到有一个linux-gate.so.1,但在文件系统中找不到,这是神马东西?

A:

linux-gate.so.1是一个"Virtual Dynamic Shared Object",即vsdo,由内核导出, 被映射到每个进程的地址空间中。vsyscall位于该页。参看:

《Linux系统调用》

在没有ASLR机制的年代,vsyscall page被映射到固定地址(0xffffe000-0xffffefff)。 从Linux 2.6.18开始,该页被映射到一个随机地址:

$ cat /proc/self/maps | grep vdso b7eef000-b7ef0000 r-xp b7eef000 00:00 0 [vdso]

过去AT_SYSINFO_EHDR指向vsyscall page起始地址,引入ASLR之后,AT_SYSINFO_EHDR 的值不可信了。但AT_SYSINFO仍指向随机化后的__kernel_vsyscall()。


/ * gcc-3.3 -Wall -pipe -O3 -s -o get_vsyscall_page get_vsyscall_page.c /

include

include

include

include

include

include

include

include

unsigned int get_auxv ( Elf32_auxv_t *auxv, unsigned int type ) { unsigned int value = 0xffffffff;

for ( ; AT_NULL != auxv->a_type; auxv++ )
{
    if ( type == auxv->a_type )
    {
        value   = auxv->a_un.a_val;
        break;
    }
}
return( value );

} / end of get_auxv /

int main ( int argc, char * argv[], char * envp[] ) { Elf32_auxv_t auxv; Elf32_Ehdr so; Elf32_Shdr sh; unsigned int size; unsigned char buf = NULL; int f = -1;

while ( NULL != *envp++ );
auxv    = ( Elf32_auxv_t * )envp;
/*
 * 从AT_SYSINFO的值推测vsyscall page的起始地址,这是一个经验搞法,不可
 * 靠。
 */
so      = ( Elf32_Ehdr * )( get_auxv( auxv, AT_SYSINFO ) & ~0xFFF );
sh      = ( Elf32_Shdr * )( ( unsigned char * )so + so->e_shoff );
/*
 * 这是山寨搞法。假设Section header table位于最未尾。
 */
size    = so->e_shoff + ( so->e_shentsize * so->e_shnum );
buf     = ( unsigned char * )calloc( size, 1 );
if ( NULL == buf )
{
    perror( "calloc() failed" );
    goto main_exit;
}
memcpy( buf, so, size );
/*
 * write( 1, buf, size );
 *
 * $ ./get_vsyscall_page > ...
 */
f       = open( "/tmp/vsyscall_page.so", O_CREAT | O_WRONLY, S_IRWXU );
if ( -1 == f )
{
    perror( "open() failed" );
    goto main_exit;
}
write( f, buf, size );

main_exit:

if ( -1 != f )
{
    close( f );
    f   = -1;
}
if ( NULL != buf )
{
    free( buf );
    buf = NULL;
}
return( 0 );

} / end of main /

vsyscall page的内容是ELF格式的,可以调用getpagesize()获取页大小,转储该页。 get_vsyscall_page.c取了ELF文件的精确大小,这是出于演示目的,不是必要的。

$ ./get_vsyscall_page $ ls -l /tmp/vsyscall_page.so -rwx------ 1 root root 2216 05-10 17:49 /tmp/vsyscall_page.so* $ file -b /tmp/vsyscall_page.so ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), dynamically linked, stripped $ readelf -e /tmp/vsyscall_page.so ELF Header: Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 Class: ELF32 Data: 2's complement, little endian Version: 1 (current) OS/ABI: UNIX - System V ABI Version: 0 Type: DYN (Shared object file) Machine: Intel 80386 Version: 0x1 Entry point address: 0xffffe400 Start of program headers: 52 (bytes into file) Start of section headers: 1696 (bytes into file) Flags: 0x0 Size of this header: 52 (bytes) Size of program headers: 32 (bytes) Number of program headers: 4 Size of section headers: 40 (bytes) Number of section headers: 13 Section header string table index: 12

Section Headers: [Nr] Name Type Addr Off Size ES Flg Lk Inf Al [ 0] NULL 00000000 000000 000000 00 0 0 0 [ 1] .hash HASH ffffe0b4 0000b4 000038 04 A 2 0 4 [ 2] .dynsym DYNSYM ffffe0ec 0000ec 000090 10 A 3 5 4 [ 3] .dynstr STRTAB ffffe17c 00017c 000056 00 A 0 0 1 [ 4] .gnu.version VERSYM ffffe1d2 0001d2 000012 02 A 2 0 2 [ 5] .gnu.version_d VERDEF ffffe1e4 0001e4 000038 00 A 3 2 4 [ 6] .text PROGBITS ffffe400 000400 000060 00 AX 0 0 32 [ 7] .note NOTE ffffe460 000460 000018 00 A 0 0 4 [ 8] .eh_frame_hdr PROGBITS ffffe478 000478 000024 00 A 0 0 4 [ 9] .eh_frame PROGBITS ffffe49c 00049c 00010c 00 A 0 0 4 [10] .dynamic DYNAMIC ffffe5a8 0005a8 000078 08 WA 3 0 4 [11] .useless PROGBITS ffffe620 000620 00000c 04 WA 0 0 4 [12] .shstrtab STRTAB 00000000 00062c 000073 00 0 0 1 Key to Flags: W (write), A (alloc), X (execute), M (merge), S (strings) I (info), L (link order), G (group), x (unknown) O (extra OS processing required) o (OS specific), p (processor specific)

Program Headers: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align LOAD 0x000000 0xffffe000 0xffffe000 0x0062c 0x0062c R E 0x1000 DYNAMIC 0x0005a8 0xffffe5a8 0xffffe5a8 0x00078 0x00078 R 0x4 NOTE 0x000460 0xffffe460 0xffffe460 0x00018 0x00018 R 0x4 GNU_EH_FRAME 0x000478 0xffffe478 0xffffe478 0x00024 0x00024 R 0x4

Section to Segment mapping: Segment Sections... 00 .hash .dynsym .dynstr .gnu.version .gnu.version_d .text .note .eh_frame_hdr .eh_frame .dynamic .useless 01 .dynamic 02 .note 03 .eh_frame_hdr $ readelf -s /tmp/vsyscall_page.so

Symbol table '.dynsym' contains 9 entries: Num: Value Size Type Bind Vis Ndx Name 0: 00000000 0 NOTYPE LOCAL DEFAULT UND 1: ffffe400 0 SECTION LOCAL DEFAULT 6 2: ffffe478 0 SECTION LOCAL DEFAULT 8 3: ffffe49c 0 SECTION LOCAL DEFAULT 9 4: ffffe620 0 SECTION LOCAL DEFAULT 11 5: ffffe400 20 FUNC GLOBAL DEFAULT 6 __kernel_vsyscall@@LINUX_2.5 6: 00000000 0 OBJECT GLOBAL DEFAULT ABS LINUX_2.5 7: ffffe440 7 FUNC GLOBAL DEFAULT 6 __kernel_rt_sigreturn@@LINUX_2.5 8: ffffe420 8 FUNC GLOBAL DEFAULT 6 __kernel_sigreturn@@LINUX_2.5 $ objdump -d -j .text /tmp/vsyscall_page.so

/tmp/vsyscall_page.so: file format elf32-i386

Disassembly of section .text:

ffffe400 <__kernel_vsyscall>: ffffe400: 51 push %ecx ffffe401: 52 push %edx ffffe402: 55 push %ebp ffffe403: 89 e5 mov %esp,%ebp ffffe405: 0f 34 sysenter ffffe407: 90 nop ffffe408: 90 nop ffffe409: 90 nop ffffe40a: 90 nop ffffe40b: 90 nop ffffe40c: 90 nop ffffe40d: 90 nop ffffe40e: eb f3 jmp ffffe403 <__kernel_vsyscall+0x3> ffffe410: 5d pop %ebp ffffe411: 5a pop %edx ffffe412: 59 pop %ecx ffffe413: c3 ret ffffe414: 90 nop ... ffffe41f: 90 nop

ffffe420 <__kernel_sigreturn>: ffffe420: 58 pop %eax ffffe421: b8 77 00 00 00 mov $0x77,%eax ffffe426: cd 80 int $0x80 ffffe428: 90 nop ... ffffe43f: 90 nop

ffffe440 <__kernel_rt_sigreturn>: ffffe440: b8 ad 00 00 00 mov $0xad,%eax ffffe445: cd 80 int $0x80 ffffe447: 90 nop ... ffffe45f: 90 nop

可以不用get_vsyscall_page.c转储vsyscall page:

$ setarch uname -m -R cat /proc/self/maps | grep vdso b7fe4000-b7fe5000 r-xp b7fe4000 00:00 0 [vdso] $ echo 0xb7fe4000 | awk '{printf "%u\n", $1/4096;}' 753636 $ setarch uname -m -R dd if=/proc/self/mem of=/tmp/vsyscall_page.dd bs=4096 skip=753636 count=1 $ file -b /tmp/vsyscall_page.dd $ readelf -e /tmp/vsyscall_page.dd $ readelf -s /tmp/vsyscall_page.dd $ objdump -d -j .text /tmp/vsyscall_page.dd