ELF Auxiliary Vector

Auxiliary vector, também conhecido por auxv, nada mais é que um vector auxiliar com informações pertinentes ao sistema e ao processo, que é colocada na imagem do processo para que possa ser utilizada pelo interpretador do ELF (ld), bem como pelo processo.

Podemos ver a montagem do vetor no source do kernel no link [1]. O objetivo do post é demonstrar como podemos acessar tal informação, que é acessível de várias formas.

O próprio ld disponibiliza uma variável especial que faz com que ele faça um dump de tal vetor no programa. Veja abaixo:

$ LD_SHOW_AUXV=1 ./foo
AT_SYSINFO_EHDR: 0x7fff4ddb5000
AT_HWCAP:        bfebfbff
AT_PAGESZ:       4096
AT_CLKTCK:       100
AT_PHDR:         0x400040
AT_PHENT:        56
AT_PHNUM:        8
AT_BASE:         0x7f468bc11000
AT_FLAGS:        0x0
AT_ENTRY:        0x4003a0
AT_UID:          1000
AT_EUID:         1000
AT_GID:          1000
AT_EGID:         1000
AT_SECURE:       0
AT_RANDOM:       0x7fff4dd22049
AT_EXECFN:       ./foo
AT_PLATFORM:     x86_64

Podemos acessar também via código no próprio programa, o auxv está localizado sempre após o vetor envp que é passado para a função main do programa (podemos ver na referência [1], no source do Kernel, como é compartilhado argc, argv e envp), [2] veja abaixo:

#include <stdio.h>
#include <link.h>     /* ElfW */
#include <elf.h>
#include <limits.h>   /* PATH_MAX */
#include <inttypes.h> /* PRIxPTR */
 
int main(int argc, char **argv, char **envp) {
	ElfW(auxv_t) *auxv;
	enum {AUXV_HEX, AUXV_INT, AUXV_STR} type;
	const char *name;
 
	while (*envp++ != NULL);
 
#define CASE(_name, _type) case _name: name = #_name; type = _type; break
 
	for (auxv = (ElfW(auxv_t) *) envp; auxv->a_type != AT_NULL; ++auxv) {
		switch (auxv->a_type) {
			CASE(AT_HWCAP,  AUXV_HEX);
			CASE(AT_PAGESZ, AUXV_INT);
			CASE(AT_CLKTCK, AUXV_INT);
			CASE(AT_PHNUM,  AUXV_INT);
			CASE(AT_PHENT,  AUXV_INT);
			CASE(AT_PHDR,   AUXV_HEX);
			CASE(AT_BASE,   AUXV_HEX);
			CASE(AT_FLAGS,  AUXV_HEX);
			CASE(AT_SECURE, AUXV_INT);
			CASE(AT_UID,    AUXV_INT);
			CASE(AT_GID,    AUXV_INT);
			CASE(AT_EUID,   AUXV_INT);
			CASE(AT_EGID,   AUXV_INT);
			CASE(AT_PLATFORM, AUXV_STR);
			CASE(AT_RANDOM,   AUXV_HEX);
			CASE(AT_ENTRY,    AUXV_HEX);
			CASE(AT_EXECFN,   AUXV_STR);
			CASE(AT_SYSINFO_EHDR, AUXV_HEX);
			default:
				name = NULL;
				break;
		}
 
		switch (type) {
			case AUXV_STR: {
				printf("%-15s: %s\n", name, (char*)auxv->a_un.a_val);
				}
				break;
			case AUXV_INT:
				printf("%-15s: %u\n", name, auxv->a_un.a_val);
				break;
			case AUXV_HEX:
				printf("%-15s: %#" PRIxPTR "\n", name, auxv->a_un.a_val);
				break;
		}
	}
 
	return 0;
}

Além desses meios, o auxv também está disponível por meio do procfs, basta lermos de /proc/
/auxv. Esta forma é util para quando estamos obtendo informação de um processo usando ptrace.

#include <stdio.h>
#include <link.h>     /* ElfW */
#include <elf.h>
#include <limits.h>   /* PATH_MAX */
#include <inttypes.h> /* PRIxPTR */
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
 
int main(int argc, char **argv, char **envp) {
	ElfW(auxv_t) auxv;
	enum {AUXV_HEX, AUXV_INT, AUXV_STR} type;
	const char *name;	
	char filename[PATH_MAX];
	int fd;
 
	snprintf(filename, PATH_MAX, "/proc/%d/auxv", getpid());
 
	if ((fd = open(filename, O_RDONLY)) == -1) {
		printf("open fail (%m)");
		exit(1);
	}
 
#define CASE(_name, _type) case _name: name = #_name; type = _type; break
 
	while (read(fd, &auxv, sizeof(auxv)) > 0) {
		switch (auxv.a_type) {
			CASE(AT_HWCAP,  AUXV_HEX);
			CASE(AT_PAGESZ, AUXV_INT);
			CASE(AT_CLKTCK, AUXV_INT);
			CASE(AT_PHNUM,  AUXV_INT);
			CASE(AT_PHENT,  AUXV_INT);
			CASE(AT_PHDR,   AUXV_HEX);
			CASE(AT_BASE,   AUXV_HEX);
			CASE(AT_FLAGS,  AUXV_HEX);
			CASE(AT_SECURE, AUXV_INT);
			CASE(AT_UID,    AUXV_INT);
			CASE(AT_GID,    AUXV_INT);
			CASE(AT_EUID,   AUXV_INT);
			CASE(AT_EGID,   AUXV_INT);
			CASE(AT_PLATFORM, AUXV_STR);
			CASE(AT_RANDOM,   AUXV_HEX);
			CASE(AT_ENTRY,    AUXV_HEX);
			CASE(AT_EXECFN,   AUXV_STR);
			CASE(AT_SYSINFO_EHDR, AUXV_HEX);
			default:
				name = NULL;
				break;
		}
		if (name == NULL) {
			continue;
		}
 
		switch (type) {
			case AUXV_STR: {
				printf("%-15s: %s\n", name, (char*)auxv.a_un.a_val);
				}
				break;
			case AUXV_INT:
				printf("%-15s: %u\n", name, auxv.a_un.a_val);
				break;
			case AUXV_HEX:
				printf("%-15s: %#" PRIxPTR "\n", name, auxv.a_un.a_val);
				break;
		}
	}
 
	close(fd);
 
	return 0;
}

Além disso uma nova função getauxval() tem sido adicionada na glibc, que provê acesso rápido a entradas específicas do auxv. [3]

Referências

[1] - http://lxr.linux.no/#linux+v3.5.3/fs/binfmt_elf.c#L202
[2] - http://articles.manugarg.com/aboutelfauxiliaryvectors.html
[3] - http://www.gnu.org/software/libc/manual/html_node/Auxiliary-Vector.html#...

Post new comment

The content of this field is kept private and will not be shown publicly.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Allowed HTML tags: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd>
  • Lines and paragraphs break automatically.

More information about formatting options