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