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)
39 static struct vdso_info
43 /* Load information */
45 uintptr_t load_offset
; /* load_addr - recorded vaddr */
49 const char *symstrings
;
50 ELF(Word
) *bucket
, *chain
;
51 ELF(Word
) nbucket
, nchain
;
58 /* Straight from the ELF specification. */
59 static unsigned long elf_hash(const unsigned char *name
)
61 unsigned long h
= 0, g
;
64 h
= (h
<< 4) + *name
++;
65 if (g
= h
& 0xf0000000)
72 void vdso_init_from_sysinfo_ehdr(uintptr_t base
)
75 bool found_vaddr
= false;
77 vdso_info
.valid
= false;
79 vdso_info
.load_addr
= base
;
81 ELF(Ehdr
) *hdr
= (ELF(Ehdr
)*)base
;
82 if (hdr
->e_ident
[EI_CLASS
] !=
83 (ELF_BITS
== 32 ? ELFCLASS32
: ELFCLASS64
)) {
84 return; /* Wrong ELF class -- check ELF_BITS */
87 ELF(Phdr
) *pt
= (ELF(Phdr
)*)(vdso_info
.load_addr
+ hdr
->e_phoff
);
91 * We need two things from the segment table: the load offset
92 * and the dynamic table.
94 for (i
= 0; i
< hdr
->e_phnum
; i
++)
96 if (pt
[i
].p_type
== PT_LOAD
&& !found_vaddr
) {
98 vdso_info
.load_offset
= base
99 + (uintptr_t)pt
[i
].p_offset
100 - (uintptr_t)pt
[i
].p_vaddr
;
101 } else if (pt
[i
].p_type
== PT_DYNAMIC
) {
102 dyn
= (ELF(Dyn
)*)(base
+ pt
[i
].p_offset
);
106 if (!found_vaddr
|| !dyn
)
110 * Fish out the useful bits of the dynamic table.
113 vdso_info
.symstrings
= 0;
114 vdso_info
.symtab
= 0;
115 vdso_info
.versym
= 0;
116 vdso_info
.verdef
= 0;
117 for (i
= 0; dyn
[i
].d_tag
!= DT_NULL
; i
++) {
118 switch (dyn
[i
].d_tag
) {
120 vdso_info
.symstrings
= (const char *)
121 ((uintptr_t)dyn
[i
].d_un
.d_ptr
122 + vdso_info
.load_offset
);
125 vdso_info
.symtab
= (ELF(Sym
) *)
126 ((uintptr_t)dyn
[i
].d_un
.d_ptr
127 + vdso_info
.load_offset
);
131 ((uintptr_t)dyn
[i
].d_un
.d_ptr
132 + vdso_info
.load_offset
);
135 vdso_info
.versym
= (ELF(Versym
) *)
136 ((uintptr_t)dyn
[i
].d_un
.d_ptr
137 + vdso_info
.load_offset
);
140 vdso_info
.verdef
= (ELF(Verdef
) *)
141 ((uintptr_t)dyn
[i
].d_un
.d_ptr
142 + vdso_info
.load_offset
);
146 if (!vdso_info
.symstrings
|| !vdso_info
.symtab
|| !hash
)
149 if (!vdso_info
.verdef
)
150 vdso_info
.versym
= 0;
152 /* Parse the hash table header. */
153 vdso_info
.nbucket
= hash
[0];
154 vdso_info
.nchain
= hash
[1];
155 vdso_info
.bucket
= &hash
[2];
156 vdso_info
.chain
= &hash
[vdso_info
.nbucket
+ 2];
158 /* That's all we need. */
159 vdso_info
.valid
= true;
162 static bool vdso_match_version(ELF(Versym
) ver
,
163 const char *name
, ELF(Word
) hash
)
166 * This is a helper function to check if the version indexed by
167 * ver matches name (which hashes to hash).
169 * The version definition table is a mess, and I don't know how
170 * to do this in better than linear time without allocating memory
171 * to build an index. I also don't know why the table has
172 * variable size entries in the first place.
174 * For added fun, I can't find a comprehensible specification of how
175 * to parse all the weird flags in the table.
177 * So I just parse the whole table every time.
180 /* First step: find the version definition */
181 ver
&= 0x7fff; /* Apparently bit 15 means "hidden" */
182 ELF(Verdef
) *def
= vdso_info
.verdef
;
184 if ((def
->vd_flags
& VER_FLG_BASE
) == 0
185 && (def
->vd_ndx
& 0x7fff) == ver
)
188 if (def
->vd_next
== 0)
189 return false; /* No definition. */
191 def
= (ELF(Verdef
) *)((char *)def
+ def
->vd_next
);
194 /* Now figure out whether it matches. */
195 ELF(Verdaux
) *aux
= (ELF(Verdaux
)*)((char *)def
+ def
->vd_aux
);
196 return def
->vd_hash
== hash
197 && !strcmp(name
, vdso_info
.symstrings
+ aux
->vda_name
);
200 void *vdso_sym(const char *version
, const char *name
)
202 unsigned long ver_hash
;
203 if (!vdso_info
.valid
)
206 ver_hash
= elf_hash(version
);
207 ELF(Word
) chain
= vdso_info
.bucket
[elf_hash(name
) % vdso_info
.nbucket
];
209 for (; chain
!= STN_UNDEF
; chain
= vdso_info
.chain
[chain
]) {
210 ELF(Sym
) *sym
= &vdso_info
.symtab
[chain
];
212 /* Check for a defined global or weak function w/ right name. */
213 if (ELF64_ST_TYPE(sym
->st_info
) != STT_FUNC
)
215 if (ELF64_ST_BIND(sym
->st_info
) != STB_GLOBAL
&&
216 ELF64_ST_BIND(sym
->st_info
) != STB_WEAK
)
218 if (sym
->st_shndx
== SHN_UNDEF
)
220 if (strcmp(name
, vdso_info
.symstrings
+ sym
->st_name
))
223 /* Check symbol version. */
225 && !vdso_match_version(vdso_info
.versym
[chain
],
229 return (void *)(vdso_info
.load_offset
+ sym
->st_value
);
235 void vdso_init_from_auxv(void *auxv
)
237 ELF(auxv_t
) *elf_auxv
= auxv
;
238 for (int i
= 0; elf_auxv
[i
].a_type
!= AT_NULL
; i
++)
240 if (elf_auxv
[i
].a_type
== AT_SYSINFO_EHDR
) {
241 vdso_init_from_sysinfo_ehdr(elf_auxv
[i
].a_un
.a_val
);
246 vdso_info
.valid
= false;