Auxiliary Vectors 辅助向量 auvx

原页面

http://articles.manugarg.com/aboutelfauxiliaryvectors.html

 

ELF auxiliary vectors are a mechanism to transfer certain kernel level information to the user processes. An example of such an information is the pointer to the system call entry point in the memory (AT_SYSINFO); this information is dynamic in nature and is only known after kernel has finished up loading.

The information is passed on to the user processes by binary loaders which are part of the kernel subsystem itself; either built-in the kernel or a kernel module. Binary loaders convert a binary file, a program, into a process on the system. Usually there is a different loader for each binary format; thankfully there are not many binary formats - most of the linux based systems now use ELF binaries. ELF binary loader is defined in the following file /usr/src/linux/fs/binfmt_elf.c.

The ELF loader parses the ELF file, maps the various program segments in the memory, sets up the entry point and initializes the process stack. It puts ELF auxiliary vectors on the process stack along with other information like argc, argv, envp. After initialization, a process' stack looks something like this:

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 strings ]   >= 0
                    [ environment ASCIIZ str. ]   >= 0

  (0xbffffffc)      [ end marker ]                4   (= NULL)

  (0xc0000000)      < bottom of stack >           0   (virtual)
  ------------------------------------------------------------------------

ELF loader puts an array (auxv) of ELF auxiliary vectors at the bottom of the stack. The structure of an auxiliary vector is defined in /usr/include/elf.h as:

typedef struct
{
  uint32_t a_type;              /* Entry type */
  union
    {
      uint32_t a_val;           /* Integer value */
      /* We use to have pointer elements added here.  We cannot do that,
         though, since it does not work when using 32-bit definitions
         on 64-bit platforms and vice versa.  */
    } a_un;
} Elf32_auxv_t;

a_type defines the entry type and union a_un defines the entry value. Legal values for a_type are defined in elf.h. To give you an idea, here are some of the vectors:

 

/* 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() */
/* Pointer to the global system page used for system calls and other nice things.  */
#define AT_SYSINFO      32
#define AT_SYSINFO_EHDR 33

The whole list is defined in the header files: /usr/include/linux/auxvec.h and asm/auxvec.h. (Since all entry types (a_type) start with AT_, ELF auxiliary vectors are also called AT_ elf parameters.)

Example of adding AT_SYSINFO auxiliary vector:

arch/ia64/include/asm/elf.h:

#define ARCH_DLINFO								\
do {										\
	extern char __kernel_syscall_via_epc[];					\
	NEW_AUX_ENT(AT_SYSINFO, (unsigned long) __kernel_syscall_via_epc);	\
	NEW_AUX_ENT(AT_SYSINFO_EHDR, (unsigned long) GATE_EHDR);		\
} while (0)

fs/binfmt_elf.c:

#define NEW_AUX_ENT(id, val) \
	do { \
		elf_info[ei_index++] = id; \
		elf_info[ei_index++] = val; \
	} while (0)

 

Spying On ELF Auxiliary Vectors:

ELF auxiliary vectors are mostly used by the program interpreter and hence are not discussed much by the programmers. The ELF auxiliary vectors being passed to a program can be seen by setting environment variable LD_SHOW_AUXV to 1.

[root@localhost ~]# LD_SHOW_AUXV=1 /bin/true
AT_SYSINFO:      0x9ff400
AT_SYSINFO_EHDR: 0x9ff000
AT_HWCAP:    fpu vme de pse tsc msr pae mce cx8 apic mtrr pge mca cmov pat clflush dts acpi mmx fxsr sse sse2 ss
AT_PAGESZ:       4096
AT_CLKTCK:       100
..........

Programmers can also access these parameters inside their programs by reaching out to the auxv array on the stack. Following program snippet shows a way to find out the value of AT_SYSINFO parameter:

#include <stdio.h>
#include <elf.h>

main(int argc, char* argv[], char* envp[])
{
        Elf32_auxv_t *auxv;
        while(*envp++ != NULL); /*from stack diagram above: *envp = NULL marks end of envp*/

        for (auxv = (Elf32_auxv_t *)envp; auxv->a_type != AT_NULL; auxv++)
      /* auxv->a_type = AT_NULL marks the end of auxv */
        {
                if( auxv->a_type == AT_SYSINFO)
                        printf("AT_SYSINFO is: 0x%x\n", auxv->a_un.a_val);
        }
}

[root@localhost ~]# gcc -o ats ats.c
[root@localhost ~]# ./ats
AT_SYSINFO: 0xc24400


We can verify that our program is working properly by using LD_SHOW_AUXV environment variable:

[root@localhost ~]# LD_SHOW_AUXV=1 ./ats | grep AT_SYSINFO
AT_SYSINFO:      0xdd9400
AT_SYSINFO_EHDR: 0xdd9000
AT_SYSINFO is: 0xdd9400

 

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------

ELF辅助向量是将某些内核级信息传输到用户进程的机制。这种信息的一个例子是指向存储器中系统调用 入口点的指针(AT_SYSINFO); 这些信息本质上是动态的,只有在内核完成加载后才会知道。

信息通过二进制加载器传递给用户进程,二进制加载器是内核子系统本身的一部分; 内置内核或内核模块。二进制加载器将二进制文件程序转换为系统上的进程。通常每种二进制格式都有不同的加载器; 幸运的是,没有太多的二进制格式 - 大多数基于Linux的系统现在使用ELF二进制文件。ELF二进制加载器在以下文件 /usr/src/linux/fs/binfmt_elf.c中定义。

ELF加载程序解析ELF文件,映射内存中的各个程序段,设置入口点并初始化进程堆栈。它将ELF辅助向量与argc,argv,envp等其他信息一起放在进程堆栈上。初始化后,进程堆栈看起来像这样:

位置内容大小(字节)+评论
  -------------------------------------------------- ----------------------
  堆栈指针 - > [argc = args的数量] 4
                    [argv [0](指针)] 4(程序名称)
                    [argv [1](指针)] 4
                    [argv [..](指针)] 4 * x
                    [argv [n  -  1](指针)] 4
                    [argv [n](指针)] 4(= NULL)

                    [envp [0](指针)] 4
                    [envp [1](指针)] 4
                    [envp [..](指针)] 4
                    [envp [term](指针)] 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向量)

                    [填充] 0  -  16

                    [参数ASCIIZ字符串]> = 0
                    [环境ASCIIZ str。]> = 0

  (0xbffffffc)[结束标记] 4(= NULL)

  (0xc0000000)<堆栈底部> 0(虚拟)
  -------------------------------------------------- ----------------------

ELF加载器将ELF辅助向量的数组(auxv)放在堆栈的底部。辅助向量的结构在/usr/include/elf.h中定义为:

typedef结构
{
  uint32_t a_type; / *输入类型* /
  联盟
    {
      uint32_t a_val; / *整数值* /
      / *我们用来在这里添加指针元素。我们做不到,
         但是,因为它在使用32位定义时不起作用
         在64位平台上,反之亦然。* /
    } a_un;
} Elf32_auxv_t;

a_type定义条目类型,union a_un定义条目值。a_type的合法值 在elf.h中定义。为了给你一个想法,这里有一些向量:

 

/ * a_type(条目类型)的合法值。* /
#define AT_NULL 0 / *向量结束* /
#define AT_IGNORE 1 / *应忽略条目* /
#define AT_EXECFD 2 / *程序的文件描述符* /
#define AT_PHDR 3 / *程序的程序头* /
#define AT_PHENT 4 / *程序头条目的大小* /
#define AT_PHNUM 5 / *程序头数* /
#define AT_PAGESZ 6 / *系统页面大小* /
#define AT_BASE 7 / *解释器的基地址* /
#define AT_FLAGS 8 / *标志* /
#define AT_ENTRY 9 / *程序入口点* /
#define AT_NOTELF 10 / *程序不是ELF * /
#define AT_UID 11 / * Real uid * /
#define AT_EUID 12 / *有效的uid * /
#define AT_GID 13 / *真正的gid * /
#define AT_EGID 14 / *有效的gid * /
#define AT_CLKTCK 17 / *次数()* /
/ *指向用于系统调用和其他好东西的全局系统页面的指针。* /
#define AT_SYSINFO 32
#define AT_SYSINFO_EHDR 33

整个列表在头文件中定义: /usr/include/linux/auxvec.hasm / auxvec.h。(由于所有条目类型(a_type)都以AT_开头,因此ELF辅助向量也称为AT_ elf参数。)

添加AT_SYSINFO辅助向量的示例:

拱/ IA64 /包括/ ASM / elf.h中:

#define ARCH_DLINFO \
做{\
	extern char __kernel_syscall_via_epc []; \
	NEW_AUX_ENT(AT_SYSINFO,(unsigned long)__ kernel_yscall_via_epc); \
	NEW_AUX_ENT(AT_SYSINFO_EHDR,(无符号长)GATE_EHDR); \
} while(0)

FS / binfmt_elf.c:

#define NEW_AUX_ENT(id,val)\
	做{\
		elf_info [ei_index ++] = id; \
		elf_info [ei_index ++] = val; \
	} while(0)

 

监视ELF辅助载体:

ELF辅助向量主要由程序解释器使用,因此程序员不会对其进行太多讨论。通过将环境变量LD_SHOW_AUXV设置为1,可以看到传递给程序的ELF辅助向量。

[root @ localhost~] #LD_SHOW_AUXV = 1 / bin / true
AT_SYSINFO:0x9ff400
AT_SYSINFO_EHDR:0x9ff000
AT_HWCAP:fpu vme de pse tsc msr pae mce cx8 apic mtrr pge mca cmov pat clflush dts acpi mmx fxsr sse sse2 ss
AT_PAGESZ:4096
AT_CLKTCK:100
..........

程序员也可以通过扩展到堆栈上的auxv数组来访问程序中的这些参数。以下程序片段显示了一种查找AT_SYSINFO参数值的方法:

#include <stdio.h>
#include <elf.h>

main(int argc,char * argv [],char * envp [])
{
        Elf32_auxv_t * auxv;
        while(* envp ++!= NULL); / *来自上面的堆栈图:* envp = NULL标记envp结束* /

        for(auxv =(Elf32_auxv_t *)envp; auxv-> a_type!= AT_NULL; auxv ++)
      / * auxv-> a_type = AT_NULL标志着auxv * /的结束
        {
                if(auxv-> a_type == AT_SYSINFO)
                        printf(“AT_SYSINFO是:0x%x \ n”,auxv-> a_un.a_val);
        }
}

[root @ localhost~] #gcc -o ats ats.c
[root @ localhost~]#。/ats
AT_SYSINFO:0xc24400


我们可以通过使用LD_SHOW_AUXV环境变量验证我们的程序是否正常工作:

[root @ localhost~] #LD_SHOW_AUXV = 1 ./ats | grep AT_SYSINFO
AT_SYSINFO:0xdd9400
AT_SYSINFO_EHDR:0xdd9000
AT_SYSINFO是:0xdd9400

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值