starnix_core/vdso/
vdso_loader.rs1use crate::arch::vdso::VDSO_SIGRETURN_NAME;
6use crate::mm::memory::MemoryObject;
7use fidl_fuchsia_io as fio;
8use process_builder::elf_parse;
9use starnix_uapi::errors::Errno;
10use starnix_uapi::{errno, from_status_like_fdio};
11use std::sync::{Arc, LazyLock};
12
13pub static ZX_TIME_VALUES_MEMORY: LazyLock<Arc<MemoryObject>> = LazyLock::new(|| {
14 load_time_values_memory().expect(
15 "Could not find time values VMO! Please ensure /boot/kernel was routed to the starnix kernel.",
16 )
17});
18
19pub struct Vdso {
20 pub memory: Arc<MemoryObject>,
21 pub sigreturn_offset: u64,
22}
23
24impl Vdso {
25 pub fn new() -> Self {
26 let memory = load_vdso_from_file().expect("Couldn't read vDSO from disk");
27 let sigreturn_offset = match VDSO_SIGRETURN_NAME {
28 Some(name) => get_sigreturn_offset(&memory, name)
29 .expect("Couldn't find sigreturn trampoline code in vDSO"),
30 None => 0,
31 };
32
33 Self { memory, sigreturn_offset }
34 }
35
36 pub fn new_arch32() -> Option<Self> {
37 let maybe_memory = load_vdso_arch32_from_file();
38 if maybe_memory.is_err() {
39 return None;
40 }
41 let memory = maybe_memory.unwrap();
42 let sigreturn_offset = match VDSO_SIGRETURN_NAME {
43 Some(name) => get_sigreturn32_offset(&memory, name)
44 .expect("Couldn't find sigreturn trampoline code in arch32 vDSO"),
45 None => 0,
46 };
47
48 Some(Self { memory, sigreturn_offset })
49 }
50}
51
52fn sync_open_in_namespace(
53 path: &str,
54 flags: fio::Flags,
55) -> Result<fio::DirectorySynchronousProxy, Errno> {
56 let (client, server) = fidl::Channel::create();
57 let dir_proxy = fio::DirectorySynchronousProxy::new(client);
58
59 let namespace = fdio::Namespace::installed().map_err(|_| errno!(EINVAL))?;
60 namespace.open(path, flags, server).map_err(|_| errno!(ENOENT))?;
61 Ok(dir_proxy)
62}
63
64fn load_vdso_from_file() -> Result<Arc<MemoryObject>, Errno> {
66 const VDSO_FILENAME: &str = "libvdso.so";
67 const VDSO_LOCATION: &str = "/pkg/data";
68
69 let dir_proxy = sync_open_in_namespace(VDSO_LOCATION, fio::PERM_READABLE)?;
70 let vdso_vmo = syncio::directory_open_vmo(
71 &dir_proxy,
72 VDSO_FILENAME,
73 fio::VmoFlags::READ,
74 zx::MonotonicInstant::INFINITE,
75 )
76 .map_err(|status| from_status_like_fdio!(status))?;
77
78 Ok(Arc::new(MemoryObject::from(vdso_vmo)))
79}
80
81fn load_vdso_arch32_from_file() -> Result<Arc<MemoryObject>, Errno> {
83 const VDSO_FILENAME: &str = "libvdso_arch32.so";
84 const VDSO_LOCATION: &str = "/pkg/data";
85
86 let dir_proxy = sync_open_in_namespace(VDSO_LOCATION, fio::PERM_READABLE)?;
87 let vdso_vmo = syncio::directory_open_vmo(
88 &dir_proxy,
89 VDSO_FILENAME,
90 fio::VmoFlags::READ,
91 zx::MonotonicInstant::INFINITE,
92 )
93 .map_err(|status| from_status_like_fdio!(status))?;
94
95 Ok(Arc::new(MemoryObject::from(vdso_vmo)))
96}
97
98fn load_time_values_memory() -> Result<Arc<MemoryObject>, Errno> {
99 const FILENAME: &str = "time_values";
100 const DIR: &str = "/boot/kernel";
101
102 let (client, server) = fidl::Channel::create();
103 let dir_proxy = fio::DirectorySynchronousProxy::new(client);
104
105 let namespace = fdio::Namespace::installed().map_err(|_| errno!(EINVAL))?;
106 namespace.open(DIR, fuchsia_fs::PERM_READABLE, server).map_err(|_| errno!(ENOENT))?;
107
108 let vmo = syncio::directory_open_vmo(
109 &dir_proxy,
110 FILENAME,
111 fio::VmoFlags::READ,
112 zx::MonotonicInstant::INFINITE,
113 )
114 .map_err(|status| from_status_like_fdio!(status))?;
115
116 let vmo_size = vmo.get_size().expect("failed to get time values VMO size");
121 let expected_size = 0x1000u64;
122 if vmo_size != expected_size {
123 panic!(
124 "time values VMO has unexpected size; got {:?}, expected {:?}",
125 vmo_size, expected_size
126 );
127 }
128 Ok(Arc::new(MemoryObject::from(vmo)))
129}
130
131fn get_string_index(string_table: &[u8], value: &[u8]) -> Option<usize> {
132 for (position, window) in string_table.windows(value.len()).enumerate() {
133 if window == value {
134 return Some(position);
135 }
136 }
137 None
138}
139
140fn get_sigreturn_offset(vdso_memory: &MemoryObject, sigreturn_name: &[u8]) -> Result<u64, Errno> {
141 let vdso_vmo = vdso_memory.as_vmo().ok_or_else(|| errno!(EINVAL))?;
142 let dyn_section = elf_parse::Elf64DynSection::from_vmo(vdso_vmo).map_err(|_| errno!(EINVAL))?;
143 let symtab = dyn_section
144 .dynamic_entry_with_tag(elf_parse::Elf64DynTag::Symtab)
145 .ok_or_else(|| errno!(EINVAL))?;
146 let strtab = dyn_section
147 .dynamic_entry_with_tag(elf_parse::Elf64DynTag::Strtab)
148 .ok_or_else(|| errno!(EINVAL))?;
149 let strsz = dyn_section
150 .dynamic_entry_with_tag(elf_parse::Elf64DynTag::Strsz)
151 .ok_or_else(|| errno!(EINVAL))?;
152
153 let strtab_bytes = vdso_vmo
155 .read_to_vec(strtab.value, strsz.value)
156 .map_err(|status| from_status_like_fdio!(status))?;
157 let strtab_idx =
158 get_string_index(&strtab_bytes, sigreturn_name).ok_or_else(|| errno!(ENOENT))?;
159
160 const SYM_ENTRY_SIZE: usize = std::mem::size_of::<elf_parse::Elf64Sym>();
161
162 let mut symtab_offset = symtab.value;
164 loop {
165 let sym_entry = vdso_vmo
166 .read_to_object::<elf_parse::Elf64Sym>(symtab_offset)
167 .map_err(|status| from_status_like_fdio!(status))?;
168 if sym_entry.st_name as usize == strtab_idx {
169 return Ok(sym_entry.st_value);
170 }
171 symtab_offset += SYM_ENTRY_SIZE as u64;
172 }
173}
174
175fn get_sigreturn32_offset(vdso_memory: &MemoryObject, sigreturn_name: &[u8]) -> Result<u64, Errno> {
176 let vdso_vmo = vdso_memory.as_vmo().ok_or_else(|| errno!(EINVAL))?;
177 let dyn_section =
178 elf_parse::Elf64DynSection::from_vmo_with_arch32(vdso_vmo).map_err(|_| errno!(EINVAL))?;
179 let symtab = dyn_section
180 .dynamic_entry_with_tag(elf_parse::Elf64DynTag::Symtab)
181 .ok_or_else(|| errno!(EINVAL))?;
182 let strtab = dyn_section
183 .dynamic_entry_with_tag(elf_parse::Elf64DynTag::Strtab)
184 .ok_or_else(|| errno!(EINVAL))?;
185 let strsz = dyn_section
186 .dynamic_entry_with_tag(elf_parse::Elf64DynTag::Strsz)
187 .ok_or_else(|| errno!(EINVAL))?;
188
189 let strtab_bytes = vdso_vmo
191 .read_to_vec(strtab.value, strsz.value)
192 .map_err(|status| from_status_like_fdio!(status))?;
193 let strtab_idx =
194 get_string_index(&strtab_bytes, sigreturn_name).ok_or_else(|| errno!(ENOENT))?;
195
196 const SYM_ENTRY_SIZE: usize = std::mem::size_of::<elf_parse::Elf32Sym>();
197
198 let mut symtab_offset = symtab.value;
200 loop {
201 let sym_entry = vdso_vmo
202 .read_to_object::<elf_parse::Elf32Sym>(symtab_offset)
203 .map_err(|status| from_status_like_fdio!(status))?;
204 if sym_entry.st_name as usize == strtab_idx {
205 return Ok(sym_entry.st_value as u64);
206 }
207 symtab_offset += SYM_ENTRY_SIZE as u64;
208 }
209}