2 * parse_vdso.c: Linux reference vDSO parser
3 * Written by Andrew Lutomirski, 2011-2014.
5 * This code is meant to be linked in to various programs that run on Linux.
6 * As such, it is available with as few restrictions as possible. This file
7 * is licensed under the Creative Commons Zero License, version 1.0,
8 * available at http://creativecommons.org/publicdomain/zero/1.0/legalcode
10 * The vDSO is a regular ELF DSO that the kernel maps into user space when
11 * it starts a program. It works equally well in statically and dynamically
14 * This code is tested on x86. In principle it should work on any
15 * architecture that has a vDSO.
25 * To use this vDSO parser, first call one of the vdso_init_* functions.
26 * If you've already parsed auxv, then pass the value of AT_SYSINFO_EHDR
27 * to vdso_init_from_sysinfo_ehdr. Otherwise pass auxv to vdso_init_from_auxv.
28 * Then call vdso_sym for each symbol you want. For example, to look up
29 * gettimeofday on x86_64, use:
31 * <some pointer> = vdso_sym("LINUX_2.6", "gettimeofday");
33 * <some pointer> = vdso_sym("LINUX_2.6", "__vdso_gettimeofday");
35 * vdso_sym will return 0 if the symbol doesn't exist or if the init function
36 * failed or was not called. vdso_sym is a little slow, so its return value
39 * vdso_sym is threadsafe; the init functions are not.
41 * These are the prototypes:
43 extern void vdso_init_from_auxv(void *auxv
);
44 extern void vdso_init_from_sysinfo_ehdr(uintptr_t base
);
45 extern void *vdso_sym(const char *version
, const char *name
);
48 /* And here's the code. */
50 # if ULONG_MAX > 0xffffffffUL
57 #define ELF_BITS_XFORM2(bits, x) Elf##bits##_##x
58 #define ELF_BITS_XFORM(bits, x) ELF_BITS_XFORM2(bits, x)
59 #define ELF(x) ELF_BITS_XFORM(ELF_BITS, x)
61 static struct vdso_info
65 /* Load information */
67 uintptr_t load_offset
; /* load_addr - recorded vaddr */
71 const char *symstrings
;
72 ELF(Word
) *bucket
, *chain
;
73 ELF(Word
) nbucket
, nchain
;
80 /* Straight from the ELF specification. */
81 static unsigned long elf_hash(const unsigned char *name
)
83 unsigned long h
= 0, g
;
86 h
= (h
<< 4) + *name
++;
87 if (g
= h
& 0xf0000000)
94 void vdso_init_from_sysinfo_ehdr(uintptr_t base
)
97 bool found_vaddr
= false;
99 vdso_info
.valid
= false;
101 vdso_info
.load_addr
= base
;
103 ELF(Ehdr
) *hdr
= (ELF(Ehdr
)*)base
;
104 if (hdr
->e_ident
[EI_CLASS
] !=
105 (ELF_BITS
== 32 ? ELFCLASS32
: ELFCLASS64
)) {
106 return; /* Wrong ELF class -- check ELF_BITS */
109 ELF(Phdr
) *pt
= (ELF(Phdr
)*)(vdso_info
.load_addr
+ hdr
->e_phoff
);
113 * We need two things from the segment table: the load offset
114 * and the dynamic table.
116 for (i
= 0; i
< hdr
->e_phnum
; i
++)
118 if (pt
[i
].p_type
== PT_LOAD
&& !found_vaddr
) {
120 vdso_info
.load_offset
= base
121 + (uintptr_t)pt
[i
].p_offset
122 - (uintptr_t)pt
[i
].p_vaddr
;
123 } else if (pt
[i
].p_type
== PT_DYNAMIC
) {
124 dyn
= (ELF(Dyn
)*)(base
+ pt
[i
].p_offset
);
128 if (!found_vaddr
|| !dyn
)
132 * Fish out the useful bits of the dynamic table.
135 vdso_info
.symstrings
= 0;
136 vdso_info
.symtab
= 0;
137 vdso_info
.versym
= 0;
138 vdso_info
.verdef
= 0;
139 for (i
= 0; dyn
[i
].d_tag
!= DT_NULL
; i
++) {
140 switch (dyn
[i
].d_tag
) {
142 vdso_info
.symstrings
= (const char *)
143 ((uintptr_t)dyn
[i
].d_un
.d_ptr
144 + vdso_info
.load_offset
);
147 vdso_info
.symtab
= (ELF(Sym
) *)
148 ((uintptr_t)dyn
[i
].d_un
.d_ptr
149 + vdso_info
.load_offset
);
153 ((uintptr_t)dyn
[i
].d_un
.d_ptr
154 + vdso_info
.load_offset
);
157 vdso_info
.versym
= (ELF(Versym
) *)
158 ((uintptr_t)dyn
[i
].d_un
.d_ptr
159 + vdso_info
.load_offset
);
162 vdso_info
.verdef
= (ELF(Verdef
) *)
163 ((uintptr_t)dyn
[i
].d_un
.d_ptr
164 + vdso_info
.load_offset
);
168 if (!vdso_info
.symstrings
|| !vdso_info
.symtab
|| !hash
)
171 if (!vdso_info
.verdef
)
172 vdso_info
.versym
= 0;
174 /* Parse the hash table header. */
175 vdso_info
.nbucket
= hash
[0];
176 vdso_info
.nchain
= hash
[1];
177 vdso_info
.bucket
= &hash
[2];
178 vdso_info
.chain
= &hash
[vdso_info
.nbucket
+ 2];
180 /* That's all we need. */
181 vdso_info
.valid
= true;
184 static bool vdso_match_version(ELF(Versym
) ver
,
185 const char *name
, ELF(Word
) hash
)
188 * This is a helper function to check if the version indexed by
189 * ver matches name (which hashes to hash).
191 * The version definition table is a mess, and I don't know how
192 * to do this in better than linear time without allocating memory
193 * to build an index. I also don't know why the table has
194 * variable size entries in the first place.
196 * For added fun, I can't find a comprehensible specification of how
197 * to parse all the weird flags in the table.
199 * So I just parse the whole table every time.
202 /* First step: find the version definition */
203 ver
&= 0x7fff; /* Apparently bit 15 means "hidden" */
204 ELF(Verdef
) *def
= vdso_info
.verdef
;
206 if ((def
->vd_flags
& VER_FLG_BASE
) == 0
207 && (def
->vd_ndx
& 0x7fff) == ver
)
210 if (def
->vd_next
== 0)
211 return false; /* No definition. */
213 def
= (ELF(Verdef
) *)((char *)def
+ def
->vd_next
);
216 /* Now figure out whether it matches. */
217 ELF(Verdaux
) *aux
= (ELF(Verdaux
)*)((char *)def
+ def
->vd_aux
);
218 return def
->vd_hash
== hash
219 && !strcmp(name
, vdso_info
.symstrings
+ aux
->vda_name
);
222 void *vdso_sym(const char *version
, const char *name
)
224 unsigned long ver_hash
;
225 if (!vdso_info
.valid
)
228 ver_hash
= elf_hash(version
);
229 ELF(Word
) chain
= vdso_info
.bucket
[elf_hash(name
) % vdso_info
.nbucket
];
231 for (; chain
!= STN_UNDEF
; chain
= vdso_info
.chain
[chain
]) {
232 ELF(Sym
) *sym
= &vdso_info
.symtab
[chain
];
234 /* Check for a defined global or weak function w/ right name. */
235 if (ELF64_ST_TYPE(sym
->st_info
) != STT_FUNC
)
237 if (ELF64_ST_BIND(sym
->st_info
) != STB_GLOBAL
&&
238 ELF64_ST_BIND(sym
->st_info
) != STB_WEAK
)
240 if (sym
->st_shndx
== SHN_UNDEF
)
242 if (strcmp(name
, vdso_info
.symstrings
+ sym
->st_name
))
245 /* Check symbol version. */
247 && !vdso_match_version(vdso_info
.versym
[chain
],
251 return (void *)(vdso_info
.load_offset
+ sym
->st_value
);
257 void vdso_init_from_auxv(void *auxv
)
259 ELF(auxv_t
) *elf_auxv
= auxv
;
260 for (int i
= 0; elf_auxv
[i
].a_type
!= AT_NULL
; i
++)
262 if (elf_auxv
[i
].a_type
== AT_SYSINFO_EHDR
) {
263 vdso_init_from_sysinfo_ehdr(elf_auxv
[i
].a_un
.a_val
);
268 vdso_info
.valid
= false;