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.
24 #include "parse_vdso.h"
26 /* And here's the code. */
28 # if ULONG_MAX > 0xffffffffUL
35 #define ELF_BITS_XFORM2(bits, x) Elf##bits##_##x
36 #define ELF_BITS_XFORM(bits, x) ELF_BITS_XFORM2(bits, x)
37 #define ELF(x) ELF_BITS_XFORM(ELF_BITS, x)
40 #define ELF_HASH_ENTRY ELF(Xword)
42 #define ELF_HASH_ENTRY ELF(Word)
45 static struct vdso_info
49 /* Load information */
51 uintptr_t load_offset
; /* load_addr - recorded vaddr */
55 const char *symstrings
;
56 ELF_HASH_ENTRY
*bucket
, *chain
;
57 ELF_HASH_ENTRY nbucket
, nchain
;
65 * Straight from the ELF specification...and then tweaked slightly, in order to
66 * avoid a few clang warnings.
68 static unsigned long elf_hash(const char *name
)
70 unsigned long h
= 0, g
;
71 const unsigned char *uch_name
= (const unsigned char *)name
;
75 h
= (h
<< 4) + *uch_name
++;
84 void vdso_init_from_sysinfo_ehdr(uintptr_t base
)
87 bool found_vaddr
= false;
89 vdso_info
.valid
= false;
91 vdso_info
.load_addr
= base
;
93 ELF(Ehdr
) *hdr
= (ELF(Ehdr
)*)base
;
94 if (hdr
->e_ident
[EI_CLASS
] !=
95 (ELF_BITS
== 32 ? ELFCLASS32
: ELFCLASS64
)) {
96 return; /* Wrong ELF class -- check ELF_BITS */
99 ELF(Phdr
) *pt
= (ELF(Phdr
)*)(vdso_info
.load_addr
+ hdr
->e_phoff
);
103 * We need two things from the segment table: the load offset
104 * and the dynamic table.
106 for (i
= 0; i
< hdr
->e_phnum
; i
++)
108 if (pt
[i
].p_type
== PT_LOAD
&& !found_vaddr
) {
110 vdso_info
.load_offset
= base
111 + (uintptr_t)pt
[i
].p_offset
112 - (uintptr_t)pt
[i
].p_vaddr
;
113 } else if (pt
[i
].p_type
== PT_DYNAMIC
) {
114 dyn
= (ELF(Dyn
)*)(base
+ pt
[i
].p_offset
);
118 if (!found_vaddr
|| !dyn
)
122 * Fish out the useful bits of the dynamic table.
124 ELF_HASH_ENTRY
*hash
= 0;
125 vdso_info
.symstrings
= 0;
126 vdso_info
.symtab
= 0;
127 vdso_info
.versym
= 0;
128 vdso_info
.verdef
= 0;
129 for (i
= 0; dyn
[i
].d_tag
!= DT_NULL
; i
++) {
130 switch (dyn
[i
].d_tag
) {
132 vdso_info
.symstrings
= (const char *)
133 ((uintptr_t)dyn
[i
].d_un
.d_ptr
134 + vdso_info
.load_offset
);
137 vdso_info
.symtab
= (ELF(Sym
) *)
138 ((uintptr_t)dyn
[i
].d_un
.d_ptr
139 + vdso_info
.load_offset
);
142 hash
= (ELF_HASH_ENTRY
*)
143 ((uintptr_t)dyn
[i
].d_un
.d_ptr
144 + vdso_info
.load_offset
);
147 vdso_info
.versym
= (ELF(Versym
) *)
148 ((uintptr_t)dyn
[i
].d_un
.d_ptr
149 + vdso_info
.load_offset
);
152 vdso_info
.verdef
= (ELF(Verdef
) *)
153 ((uintptr_t)dyn
[i
].d_un
.d_ptr
154 + vdso_info
.load_offset
);
158 if (!vdso_info
.symstrings
|| !vdso_info
.symtab
|| !hash
)
161 if (!vdso_info
.verdef
)
162 vdso_info
.versym
= 0;
164 /* Parse the hash table header. */
165 vdso_info
.nbucket
= hash
[0];
166 vdso_info
.nchain
= hash
[1];
167 vdso_info
.bucket
= &hash
[2];
168 vdso_info
.chain
= &hash
[vdso_info
.nbucket
+ 2];
170 /* That's all we need. */
171 vdso_info
.valid
= true;
174 static bool vdso_match_version(ELF(Versym
) ver
,
175 const char *name
, ELF(Word
) hash
)
178 * This is a helper function to check if the version indexed by
179 * ver matches name (which hashes to hash).
181 * The version definition table is a mess, and I don't know how
182 * to do this in better than linear time without allocating memory
183 * to build an index. I also don't know why the table has
184 * variable size entries in the first place.
186 * For added fun, I can't find a comprehensible specification of how
187 * to parse all the weird flags in the table.
189 * So I just parse the whole table every time.
192 /* First step: find the version definition */
193 ver
&= 0x7fff; /* Apparently bit 15 means "hidden" */
194 ELF(Verdef
) *def
= vdso_info
.verdef
;
196 if ((def
->vd_flags
& VER_FLG_BASE
) == 0
197 && (def
->vd_ndx
& 0x7fff) == ver
)
200 if (def
->vd_next
== 0)
201 return false; /* No definition. */
203 def
= (ELF(Verdef
) *)((char *)def
+ def
->vd_next
);
206 /* Now figure out whether it matches. */
207 ELF(Verdaux
) *aux
= (ELF(Verdaux
)*)((char *)def
+ def
->vd_aux
);
208 return def
->vd_hash
== hash
209 && !strcmp(name
, vdso_info
.symstrings
+ aux
->vda_name
);
212 void *vdso_sym(const char *version
, const char *name
)
214 unsigned long ver_hash
;
215 if (!vdso_info
.valid
)
218 ver_hash
= elf_hash(version
);
219 ELF(Word
) chain
= vdso_info
.bucket
[elf_hash(name
) % vdso_info
.nbucket
];
221 for (; chain
!= STN_UNDEF
; chain
= vdso_info
.chain
[chain
]) {
222 ELF(Sym
) *sym
= &vdso_info
.symtab
[chain
];
224 /* Check for a defined global or weak function w/ right name. */
225 if (ELF64_ST_TYPE(sym
->st_info
) != STT_FUNC
)
227 if (ELF64_ST_BIND(sym
->st_info
) != STB_GLOBAL
&&
228 ELF64_ST_BIND(sym
->st_info
) != STB_WEAK
)
230 if (sym
->st_shndx
== SHN_UNDEF
)
232 if (strcmp(name
, vdso_info
.symstrings
+ sym
->st_name
))
235 /* Check symbol version. */
237 && !vdso_match_version(vdso_info
.versym
[chain
],
241 return (void *)(vdso_info
.load_offset
+ sym
->st_value
);
247 void vdso_init_from_auxv(void *auxv
)
249 ELF(auxv_t
) *elf_auxv
= auxv
;
250 for (int i
= 0; elf_auxv
[i
].a_type
!= AT_NULL
; i
++)
252 if (elf_auxv
[i
].a_type
== AT_SYSINFO_EHDR
) {
253 vdso_init_from_sysinfo_ehdr(elf_auxv
[i
].a_un
.a_val
);
258 vdso_info
.valid
= false;