wasmer_wasix/state/linker/instance_group/
exports.rs

1use tracing::trace;
2use wasmer::{AsStoreMut, Exportable, Extern, ExternType, Function, Global, Instance, Value};
3
4use super::{
5    DylinkInfo, InstanceGroupState, LinkerState, MAIN_MODULE_HANDLE, ModuleHandle,
6    PartiallyResolvedExport, ResolveError,
7};
8
9impl InstanceGroupState {
10    pub(in crate::state::linker) fn resolve_exported_symbol(
11        &self,
12        symbol: &str,
13    ) -> Option<(ModuleHandle, &Extern)> {
14        if let Some(export) = self
15            .main_instance()
16            .and_then(|instance| instance.exports.get_extern(symbol))
17        {
18            trace!(symbol, from = ?MAIN_MODULE_HANDLE, ?export, "Resolved exported symbol");
19            Some((MAIN_MODULE_HANDLE, export))
20        } else {
21            for (handle, dl_instance) in &self.side_instances {
22                if let Some(export) = dl_instance.instance.exports.get_extern(symbol) {
23                    trace!(symbol, from = ?handle, ?export, "Resolved exported symbol");
24                    return Some((*handle, export));
25                }
26            }
27
28            trace!(symbol, "Failed to resolve exported symbol");
29            None
30        }
31    }
32
33    // Resolve an export down to the "memory address" of the symbol. This is different from
34    // `resolve_symbol`, which resolves a WASM export but does not care about its type and
35    // does no further processing on the export itself.
36    pub(in crate::state::linker) fn resolve_export(
37        &self,
38        linker_state: &LinkerState,
39        store: &mut impl AsStoreMut,
40        module_handle: Option<ModuleHandle>,
41        symbol: &str,
42        allow_hidden: bool,
43    ) -> Result<(PartiallyResolvedExport, ModuleHandle), ResolveError> {
44        trace!(?module_handle, ?symbol, "Resolving export");
45        match module_handle {
46            Some(module_handle) => {
47                let instance = self
48                    .try_instance(module_handle)
49                    .ok_or(ResolveError::InvalidModuleHandle)?;
50                let tls_base = self.tls_base(module_handle);
51                let memory_base = linker_state.memory_base(module_handle);
52                let dylink_info = linker_state.dylink_info(module_handle);
53                Ok((
54                    self.resolve_export_from(
55                        store,
56                        module_handle,
57                        symbol,
58                        instance,
59                        dylink_info,
60                        memory_base,
61                        tls_base,
62                        allow_hidden,
63                    )?,
64                    module_handle,
65                ))
66            }
67
68            None => {
69                // TODO: this would be the place to support RTLD_NEXT
70                if let Some(instance) = self.main_instance() {
71                    match self.resolve_export_from(
72                        store,
73                        MAIN_MODULE_HANDLE,
74                        symbol,
75                        instance,
76                        &linker_state.main_module_dylink_info,
77                        linker_state.memory_base(MAIN_MODULE_HANDLE),
78                        self.main_instance_tls_base,
79                        allow_hidden,
80                    ) {
81                        Ok(export) => return Ok((export, MAIN_MODULE_HANDLE)),
82                        Err(ResolveError::MissingExport) => (),
83                        Err(e) => return Err(e),
84                    }
85                }
86
87                // Iterate over linker.side_modules to ensure we're going over the
88                // modules in increasing order of module_handle, A.K.A. the order
89                // in which modules were loaded. linker.side_modules is a BTreeMap
90                // whereas self.side_instances is a HashMap with undetermined
91                // iteration order.
92                for (handle, module) in &linker_state.side_modules {
93                    let instance = &self.side_instances[handle];
94                    match self.resolve_export_from(
95                        store,
96                        *handle,
97                        symbol,
98                        &instance.instance,
99                        &module.dylink_info,
100                        linker_state.memory_base(*handle),
101                        instance.tls_base,
102                        allow_hidden,
103                    ) {
104                        Ok(export) => return Ok((export, *handle)),
105                        Err(ResolveError::MissingExport) => (),
106                        Err(e) => return Err(e),
107                    }
108                }
109
110                trace!(
111                    ?module_handle,
112                    ?symbol,
113                    "Failed to locate symbol after searching all instances"
114                );
115                Err(ResolveError::MissingExport)
116            }
117        }
118    }
119
120    pub(in crate::state::linker) fn resolve_export_from(
121        &self,
122        store: &mut impl AsStoreMut,
123        module_handle: ModuleHandle,
124        symbol: &str,
125        instance: &Instance,
126        dylink_info: &DylinkInfo,
127        memory_base: u64,
128        tls_base: Option<u64>,
129        allow_hidden: bool,
130    ) -> Result<PartiallyResolvedExport, ResolveError> {
131        trace!(from = ?module_handle, symbol, "Resolving export from instance");
132        let export = instance.exports.get_extern(symbol).ok_or_else(|| {
133            trace!(from = ?module_handle, symbol, "Not found");
134            ResolveError::MissingExport
135        })?;
136
137        if !allow_hidden
138            && dylink_info
139                .export_metadata
140                .get(symbol)
141                .map(|flags| flags.contains(wasmparser::SymbolFlags::VISIBILITY_HIDDEN))
142                .unwrap_or(false)
143        {
144            return Err(ResolveError::MissingExport);
145        }
146
147        match export.ty(store) {
148            ExternType::Function(_) => {
149                trace!(from = ?module_handle, symbol, "Found function");
150                Ok(PartiallyResolvedExport::Function(
151                    Function::get_self_from_extern(export).unwrap().clone(),
152                ))
153            }
154            ty @ ExternType::Global(_) => {
155                let global = Global::get_self_from_extern(export).unwrap();
156                let value = match global.get(store) {
157                    Value::I32(value) => value as u64,
158                    Value::I64(value) => value as u64,
159                    _ => return Err(ResolveError::InvalidExportType(ty.clone())),
160                };
161
162                let is_tls = dylink_info
163                    .export_metadata
164                    .get(symbol)
165                    .map(|flags| flags.contains(wasmparser::SymbolFlags::TLS))
166                    .unwrap_or(false);
167
168                if is_tls {
169                    let Some(tls_base) = tls_base else {
170                        return Err(ResolveError::NoTlsBaseGlobalExport);
171                    };
172                    let final_value = value + tls_base;
173                    trace!(
174                        from = ?module_handle,
175                        symbol,
176                        value,
177                        offset = value,
178                        final_value,
179                        "Found TLS global"
180                    );
181                    Ok(PartiallyResolvedExport::Tls {
182                        offset: value,
183                        final_addr: final_value,
184                    })
185                } else {
186                    let final_value = value + memory_base;
187                    trace!(from = ?module_handle, symbol, value, final_value, "Found global");
188                    Ok(PartiallyResolvedExport::Global(final_value))
189                }
190            }
191            ty => Err(ResolveError::InvalidExportType(ty.clone())),
192        }
193    }
194}