1570af302Sopenharmony_ci/*
2570af302Sopenharmony_ci * Copyright (c) 2023 Huawei Device Co., Ltd.
3570af302Sopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License");
4570af302Sopenharmony_ci * you may not use this file except in compliance with the License.
5570af302Sopenharmony_ci * You may obtain a copy of the License at
6570af302Sopenharmony_ci *
7570af302Sopenharmony_ci *     http://www.apache.org/licenses/LICENSE-2.0
8570af302Sopenharmony_ci *
9570af302Sopenharmony_ci * Unless required by applicable law or agreed to in writing, software
10570af302Sopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS,
11570af302Sopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12570af302Sopenharmony_ci * See the License for the specific language governing permissions and
13570af302Sopenharmony_ci * limitations under the License.
14570af302Sopenharmony_ci */
15570af302Sopenharmony_ci
16570af302Sopenharmony_ci#include <elf.h>
17570af302Sopenharmony_ci#include <link.h>
18570af302Sopenharmony_ci#include <limits.h>
19570af302Sopenharmony_ci#include <stdint.h>
20570af302Sopenharmony_ci#include <string.h>
21570af302Sopenharmony_ci#include "libc.h"
22570af302Sopenharmony_ci#include "syscall.h"
23570af302Sopenharmony_ci
24570af302Sopenharmony_ci#ifdef VDSO_USEFUL
25570af302Sopenharmony_ci
26570af302Sopenharmony_ci#if ULONG_MAX == 0xffffffff
27570af302Sopenharmony_citypedef Elf32_Ehdr Ehdr;
28570af302Sopenharmony_citypedef Elf32_Phdr Phdr;
29570af302Sopenharmony_citypedef Elf32_Sym Sym;
30570af302Sopenharmony_citypedef Elf32_Verdef Verdef;
31570af302Sopenharmony_citypedef Elf32_Verdaux Verdaux;
32570af302Sopenharmony_ci#else
33570af302Sopenharmony_citypedef Elf64_Ehdr Ehdr;
34570af302Sopenharmony_citypedef Elf64_Phdr Phdr;
35570af302Sopenharmony_citypedef Elf64_Sym Sym;
36570af302Sopenharmony_citypedef Elf64_Verdef Verdef;
37570af302Sopenharmony_citypedef Elf64_Verdaux Verdaux;
38570af302Sopenharmony_ci#endif
39570af302Sopenharmony_ci
40570af302Sopenharmony_cistatic int checkver(Verdef *def, int vsym, const char *vername, char *strings)
41570af302Sopenharmony_ci{
42570af302Sopenharmony_ci	vsym &= 0x7fff;
43570af302Sopenharmony_ci	for (;;) {
44570af302Sopenharmony_ci		if (!(def->vd_flags & VER_FLG_BASE)
45570af302Sopenharmony_ci		  && (def->vd_ndx & 0x7fff) == vsym)
46570af302Sopenharmony_ci			break;
47570af302Sopenharmony_ci		if (def->vd_next == 0)
48570af302Sopenharmony_ci			return 0;
49570af302Sopenharmony_ci		def = (Verdef *)((char *)def + def->vd_next);
50570af302Sopenharmony_ci	}
51570af302Sopenharmony_ci	Verdaux *aux = (Verdaux *)((char *)def + def->vd_aux);
52570af302Sopenharmony_ci	return !strcmp(vername, strings + aux->vda_name);
53570af302Sopenharmony_ci}
54570af302Sopenharmony_ci
55570af302Sopenharmony_ci#define OK_TYPES (1<<STT_NOTYPE | 1<<STT_OBJECT | 1<<STT_FUNC | 1<<STT_COMMON)
56570af302Sopenharmony_ci#define OK_BINDS (1<<STB_GLOBAL | 1<<STB_WEAK | 1<<STB_GNU_UNIQUE)
57570af302Sopenharmony_ci
58570af302Sopenharmony_cistruct __vdso_info {
59570af302Sopenharmony_ci	size_t base;
60570af302Sopenharmony_ci	char *strings;
61570af302Sopenharmony_ci	Sym *syms;
62570af302Sopenharmony_ci	Elf_Symndx *hashtab;
63570af302Sopenharmony_ci	uint16_t *versym;
64570af302Sopenharmony_ci	Verdef *verdef;
65570af302Sopenharmony_ci} vdso_info = {-1, 0, 0, 0, 0, 0};
66570af302Sopenharmony_ci
67570af302Sopenharmony_civoid __get_vdso_info()
68570af302Sopenharmony_ci{
69570af302Sopenharmony_ci	if (vdso_info.base != -1) {
70570af302Sopenharmony_ci		return ;
71570af302Sopenharmony_ci	}
72570af302Sopenharmony_ci	size_t i;
73570af302Sopenharmony_ci	for (i=0; libc.auxv[i] != AT_SYSINFO_EHDR; i+=2)
74570af302Sopenharmony_ci		if (!libc.auxv[i]) return ;
75570af302Sopenharmony_ci	if (!libc.auxv[i+1]) return ;
76570af302Sopenharmony_ci	Ehdr *eh = (void *)libc.auxv[i+1];
77570af302Sopenharmony_ci	Phdr *ph = (void *)((char *)eh + eh->e_phoff);
78570af302Sopenharmony_ci	size_t *dynv=0, base=-1;
79570af302Sopenharmony_ci	for (i=0; i<eh->e_phnum; i++, ph=(void *)((char *)ph+eh->e_phentsize)) {
80570af302Sopenharmony_ci		if (ph->p_type == PT_LOAD)
81570af302Sopenharmony_ci			base = (size_t)eh + ph->p_offset - ph->p_vaddr;
82570af302Sopenharmony_ci		else if (ph->p_type == PT_DYNAMIC)
83570af302Sopenharmony_ci			dynv = (void *)((char *)eh + ph->p_offset);
84570af302Sopenharmony_ci	}
85570af302Sopenharmony_ci	if (!dynv || base==(size_t)-1) return ;
86570af302Sopenharmony_ci
87570af302Sopenharmony_ci	for (i=0; dynv[i]; i+=2) {
88570af302Sopenharmony_ci		void *p = (void *)(base + dynv[i+1]);
89570af302Sopenharmony_ci		switch(dynv[i]) {
90570af302Sopenharmony_ci		case DT_STRTAB: vdso_info.strings = p; break;
91570af302Sopenharmony_ci		case DT_SYMTAB: vdso_info.syms = p; break;
92570af302Sopenharmony_ci		case DT_HASH: vdso_info.hashtab = p; break;
93570af302Sopenharmony_ci		case DT_VERSYM: vdso_info.versym = p; break;
94570af302Sopenharmony_ci		case DT_VERDEF: vdso_info.verdef = p; break;
95570af302Sopenharmony_ci		}
96570af302Sopenharmony_ci	}
97570af302Sopenharmony_ci    vdso_info.base = base;
98570af302Sopenharmony_ci    return ;
99570af302Sopenharmony_ci}
100570af302Sopenharmony_ci
101570af302Sopenharmony_civoid *__get_vdso_addr(const char *vername, const char *name)
102570af302Sopenharmony_ci{
103570af302Sopenharmony_ci	if (!vdso_info.strings || !vdso_info.syms || !vdso_info.hashtab) return 0;
104570af302Sopenharmony_ci	if (!vdso_info.verdef) vdso_info.versym = 0;
105570af302Sopenharmony_ci
106570af302Sopenharmony_ci    size_t i;
107570af302Sopenharmony_ci	for (i=0; i<vdso_info.hashtab[1]; i++) {
108570af302Sopenharmony_ci		if (!(1<<(vdso_info.syms[i].st_info&0xf) & OK_TYPES)) continue;
109570af302Sopenharmony_ci		if (!(1<<(vdso_info.syms[i].st_info>>4) & OK_BINDS)) continue;
110570af302Sopenharmony_ci		if (!vdso_info.syms[i].st_shndx) continue;
111570af302Sopenharmony_ci		if (strcmp(name, vdso_info.strings+vdso_info.syms[i].st_name)) continue;
112570af302Sopenharmony_ci		if (vdso_info.versym && !checkver(vdso_info.verdef, vdso_info.versym[i], vername, vdso_info.strings))
113570af302Sopenharmony_ci			continue;
114570af302Sopenharmony_ci		return (void *)(vdso_info.base + vdso_info.syms[i].st_value);
115570af302Sopenharmony_ci	}
116570af302Sopenharmony_ci
117570af302Sopenharmony_ci	return 0;
118570af302Sopenharmony_ci}
119570af302Sopenharmony_ci
120570af302Sopenharmony_ci#endif
121