2.23 ELF Auxiliary Vectors https://scz.617.cn/unix/201205092043.txt D: Manu Garg 2006 参看Linux内核源码: fs/binfmt_elf.c 这里实现了ELF文件的加载。ELF文件加载时,栈被初始化成如下样子: position content size(bytes) + comment -------------------------------------------------------------------------- stack pointer -> [ argc = number of args ] 4 [ argv[0] (pointer) ] 4 (program name) [ argv[1] (pointer) ] 4 [ argv[...] (pointer) ] 4 * x [ argv[n - 1] (pointer) ] 4 [ argv[n] (pointer) ] 4 (=NULL) [ envp[0] (pointer) ] 4 [ envp[1] (pointer) ] 4 [ envp[...] (pointer) ] 4 [ envp[term] (pointer) ] 4 (=NULL) [ auxv[0] (Elf32_auxv_t) ] 8 [ auxv[1] (Elf32_auxv_t) ] 8 [ auxv[...] (Elf32_auxv_t) ] 8 [ auxv[term] (Elf32_auxv_t) ] 8 (=AT_NULL vector) [ padding ] 0~16 [ argument ASCIIZ str ] >=0 [ environment ASCIIZ str ] >=0 (0xbffffffc) [ end marker ] 4 (=NULL) (0xc0000000) < bottom of stack > 0 (virtual) -------------------------------------------------------------------------- 大多数时候C程序员这样写main(): int main ( int argc, char * argv[] ); 如果需要访问环境变量,就会这样写: int main ( int argc, char * argv[], char * envp[] ); 但在x86上没法直接访问auxv[],据说PowerPC上第4形参是auxv[]。 ------------------------------------------------------------------------ /* * /usr/include/elf.h */ typedef struct { /* * Entry type */ uint32_t a_type; union { uint32_t a_val; } a_un; } Elf32_auxv_t; /* * Legal values for a_type (entry type). */ #define AT_NULL 0 // End of vector #define AT_IGNORE 1 // Entry should be ignored #define AT_EXECFD 2 // File descriptor of program #define AT_PHDR 3 // Program headers for program #define AT_PHENT 4 // Size of program header entry #define AT_PHNUM 5 // Number of program headers #define AT_PAGESZ 6 // System page size #define AT_BASE 7 // Base address of interpreter #define AT_FLAGS 8 // Flags #define AT_ENTRY 9 // Entry point of program #define AT_NOTELF 10 // Program is not ELF #define AT_UID 11 // Real uid #define AT_EUID 12 // Effective uid #define AT_GID 13 // Real gid #define AT_EGID 14 // Effective gid #define AT_CLKTCK 17 // Frequency of times() /* * Some more special a_type values describing the hardware. */ #define AT_PLATFORM 15 // String identifying platform. #define AT_HWCAP 16 // Machine dependent hints about processor capabilities. /* * This entry gives some information about the FPU initialization performed by the kernel. */ #define AT_FPUCW 18 // Used FPU control word. /* * Cache block sizes. */ #define AT_DCACHEBSIZE 19 // Data cache block size. #define AT_ICACHEBSIZE 20 // Instruction cache block size. #define AT_UCACHEBSIZE 21 // Unified cache block size. /* * A special ignored value for PPC, used by the kernel to control the * interpretation of the AUXV. Must be > 16. */ #define AT_IGNOREPPC 22 // Entry should be ignored. #define AT_SECURE 23 // Boolean, was exec setuid-like? #define AT_BASE_PLATFORM 24 // String identifying real platforms. #define AT_RANDOM 25 // Address of 16 random bytes. #define AT_EXECFN 31 // Filename of executable. /* * Pointer to the global system page used for system calls and other nice things. */ #define AT_SYSINFO 32 #define AT_SYSINFO_EHDR 33 /* * Shapes of the caches. Bits 0-3 contains associativity; bits 4-7 contains * log2 of line size; mask those to get cache size. */ #define AT_L1I_CACHESHAPE 34 #define AT_L1D_CACHESHAPE 35 #define AT_L2_CACHESHAPE 36 #define AT_L3_CACHESHAPE 37 ------------------------------------------------------------------------ 绝大多数时候,只有ELF加载器需要关心ELF Auxiliary Vectors,程序员并不怎么关 心。但如果你非常好奇,有一个简便方法让你查看ELF Auxiliary Vectors: $ LD_SHOW_AUXV=1 /bin/true AT_SYSINFO: 0xb7fc8400 AT_SYSINFO_EHDR: 0xffffe000 AT_HWCAP: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss AT_PAGESZ: 4096 AT_CLKTCK: 100 AT_PHDR: 0x8048034 AT_PHENT: 32 AT_PHNUM: 7 AT_BASE: 0xb7fc9000 AT_FLAGS: 0x0 AT_ENTRY: 0x80489e0 AT_UID: 0 AT_EUID: 0 AT_GID: 0 AT_EGID: 0 AT_SECURE: 0 AT_PLATFORM: i686 下面演示如何编程寻找AT_SYSINFO、AT_SYSINFO_EHDR: -------------------------------------------------------------------------- /* * gcc-3.3 -Wall -pipe -O3 -s -o elf_auxv_demo elf_auxv_demo.c */ #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; unsigned int value; while ( NULL != *envp++ ); auxv = ( Elf32_auxv_t * )envp; if ( 0xffffffff != ( value = get_auxv( auxv, AT_SYSINFO ) ) ) { printf( "AT_SYSINFO = 0x%x\n", value ); } if ( 0xffffffff != ( value = get_auxv( auxv, AT_SYSINFO_EHDR ) ) ) { printf( "AT_SYSINFO_EHDR = 0x%x\n", value ); } return( 0 ); } /* end of main */ -------------------------------------------------------------------------- $ for i in `seq 0 1 2`;do ./elf_auxv_demo;done AT_SYSINFO = 0xb7fcc400 AT_SYSINFO_EHDR = 0xffffe000 AT_SYSINFO = 0xb7f0d400 AT_SYSINFO_EHDR = 0xffffe000 AT_SYSINFO = 0xb7ee8400 AT_SYSINFO_EHDR = 0xffffe000 可以看出,AT_SYSINFO被随机化了,AT_SYSINFO_EHDR好像是无效的。禁用ASLR之后 的效果: $ for i in `seq 0 1 2`;do setarch `uname -m` -R ./elf_auxv_demo | grep "AT_SYSINFO ";done AT_SYSINFO = 0xb7fe4400 AT_SYSINFO = 0xb7fe4400 AT_SYSINFO = 0xb7fe4400 利用LD_SHOW_AUXV环境变量时的效果: $ for((i=0;i<3;i++));do LD_SHOW_AUXV=1 ./elf_auxv_demo | grep "AT_SYSINFO[ :]";done AT_SYSINFO: 0xb7f04400 AT_SYSINFO = 0xb7f04400 AT_SYSINFO: 0xb7f48400 AT_SYSINFO = 0xb7f48400 AT_SYSINFO: 0xb7f71400 AT_SYSINFO = 0xb7f71400 D: scz@nsfocus elf_auxv_demo.c演示了一种比较繁琐的寻找auxv[]的办法,其实有更省事的办法: $ setarch `uname -m` -R cat /proc/self/auxv | xxd -g 1 | grep "20 00 00 00" 0000000: 20 00 00 00 00 44 fe b7 21 00 00 00 00 e0 ff ff ....D..!....... 0000030: 04 00 00 00 20 00 00 00 05 00 00 00 07 00 00 00 .... ........... 可以看出AT_SYSINFO等于0xb7fe4400。