Repository: bytecodealliance/wasm-tools
Pull request #1543 (2024-05-09 15:09:08)

Make fields of `BinaryReader` private
Don't allow arbitrary modifications/access throughout the crate to be sure arbitrate through the methods on this type. This is a pretty core type to the crate so it's best to not spread implementation details too much.


Patch:
diff --git a/crates/wasm-encoder/src/core/dump.rs b/crates/wasm-encoder/src/core/dump.rs
index ee3d2299..f9c01d9e 100644
--- a/crates/wasm-encoder/src/core/dump.rs
+++ b/crates/wasm-encoder/src/core/dump.rs
@@ -336,7 +336,7 @@ impl Encode for CoreDumpValue {
 mod tests {
     use super::*;
     use crate::Module;
-    use wasmparser::{BinaryReader, FromReader, Parser, Payload};
+    use wasmparser::{KnownCustom, Parser, Payload};
 
     // Create new core dump section and test whether it is properly encoded and
     // parsed back out by wasmparser
@@ -361,10 +361,10 @@ mod tests {
         match payload {
             Payload::CustomSection(section) => {
                 assert_eq!(section.name(), "core");
-                let core = wasmparser::CoreDumpSection::from_reader(&mut BinaryReader::new(
-                    section.data(),
-                ))
-                .expect("data is readable into a core dump section");
+                let core = match section.as_known() {
+                    KnownCustom::CoreDump(s) => s,
+                    _ => panic!("not coredump"),
+                };
                 assert_eq!(core.name, "test.wasm");
             }
             _ => panic!("unexpected payload"),
@@ -394,10 +394,10 @@ mod tests {
         match payload {
             Payload::CustomSection(section) => {
                 assert_eq!(section.name(), "coremodules");
-                let modules = wasmparser::CoreDumpModulesSection::from_reader(
-                    &mut BinaryReader::new(section.data()),
-                )
-                .expect("data is readable into a core dump modules section");
+                let modules = match section.as_known() {
+                    KnownCustom::CoreDumpModules(s) => s,
+                    _ => panic!("not coremodules"),
+                };
                 assert_eq!(modules.modules[0], "test_module");
             }
             _ => panic!("unexpected payload"),
@@ -429,10 +429,10 @@ mod tests {
         match payload {
             Payload::CustomSection(section) => {
                 assert_eq!(section.name(), "coreinstances");
-                let coreinstances = wasmparser::CoreDumpInstancesSection::from_reader(
-                    &mut BinaryReader::new(section.data()),
-                )
-                .expect("data is readable into a core dump instances section");
+                let coreinstances = match section.as_known() {
+                    KnownCustom::CoreDumpInstances(s) => s,
+                    _ => panic!("not coreinstances"),
+                };
                 assert_eq!(coreinstances.instances.len(), 1);
                 let instance = coreinstances
                     .instances
@@ -475,10 +475,10 @@ mod tests {
         match payload {
             Payload::CustomSection(section) => {
                 assert_eq!(section.name(), "corestack");
-                let corestack = wasmparser::CoreDumpStackSection::from_reader(
-                    &mut BinaryReader::new(section.data()),
-                )
-                .expect("data is readable into a core dump stack section");
+                let corestack = match section.as_known() {
+                    KnownCustom::CoreDumpStack(s) => s,
+                    _ => panic!("not a corestack section"),
+                };
                 assert_eq!(corestack.name, "main");
                 assert_eq!(corestack.frames.len(), 1);
                 let frame = corestack
@@ -570,7 +570,7 @@ mod tests {
             // 0x0, module_idx
             0x0, 0,
             // memories count, memories
-            1, 42, 
+            1, 42,
             // globals count, globals
             1, 17
         ]);
@@ -595,7 +595,7 @@ mod tests {
             encoded,
             vec![
                 // section length
-                27, 
+                27,
                 // length of name.
                 9,
                 // section name (corestack)
diff --git a/crates/wasm-encoder/src/core/producers.rs b/crates/wasm-encoder/src/core/producers.rs
index af03ecbb..00b3853f 100644
--- a/crates/wasm-encoder/src/core/producers.rs
+++ b/crates/wasm-encoder/src/core/producers.rs
@@ -121,7 +121,7 @@ mod test {
     #[test]
     fn roundtrip_example() {
         use crate::{Module, ProducersField, ProducersSection};
-        use wasmparser::{Parser, Payload, ProducersSectionReader};
+        use wasmparser::{KnownCustom, Parser, Payload};
 
         // Create a new producers section.
         let mut field = ProducersField::new();
@@ -151,9 +151,10 @@ mod test {
         match payload {
             Payload::CustomSection(c) => {
                 assert_eq!(c.name(), "producers");
-                let mut section = ProducersSectionReader::new(c.data(), c.data_offset())
-                    .expect("readable as a producers section")
-                    .into_iter();
+                let mut section = match c.as_known() {
+                    KnownCustom::Producers(s) => s.into_iter(),
+                    _ => panic!("unknown custom section"),
+                };
                 let field = section
                     .next()
                     .expect("section has an element")
diff --git a/crates/wasm-metadata/src/lib.rs b/crates/wasm-metadata/src/lib.rs
index 8a3626e0..0f9e39f0 100644
--- a/crates/wasm-metadata/src/lib.rs
+++ b/crates/wasm-metadata/src/lib.rs
@@ -9,7 +9,8 @@ use std::mem;
 use std::ops::Range;
 use wasm_encoder::{ComponentSection as _, ComponentSectionId, Encode, Section};
 use wasmparser::{
-    ComponentNameSectionReader, NameSectionReader, Parser, Payload::*, ProducersSectionReader,
+    ComponentNameSectionReader, KnownCustom, NameSectionReader, Parser, Payload::*,
+    ProducersSectionReader,
 };
 
 /// A representation of a WebAssembly producers section.
@@ -50,9 +51,11 @@ impl Producers {
             match payload {
                 ModuleSection { .. } | ComponentSection { .. } => depth += 1,
                 End { .. } => depth -= 1,
-                CustomSection(c) if c.name() == "producers" && depth == 0 => {
-                    let producers = Self::from_bytes(c.data(), c.data_offset())?;
-                    return Ok(Some(producers));
+                CustomSection(c) if depth == 0 => {
+                    if let KnownCustom::Producers(_) = c.as_known() {
+                        let producers = Self::from_bytes(c.data(), c.data_offset())?;
+                        return Ok(Some(producers));
+                    }
                 }
                 _ => {}
             }
@@ -290,57 +293,60 @@ fn rewrite_wasm(
             _ => {}
         }
 
-        // Process the wasm sections:
-        match payload {
-            // Only rewrite the outermost producers section:
-            CustomSection(c) if c.name() == "producers" && stack.len() == 0 => {
-                producers_found = true;
-                let mut producers = Producers::from_bytes(c.data(), c.data_offset())?;
-                // Add to the section according to the command line flags:
-                producers.merge(&add_producers);
-                // Encode into output:
-                producers.section().append_to(&mut output);
-            }
-
-            CustomSection(c) if c.name() == "name" && stack.len() == 0 => {
-                names_found = true;
-                let mut names = ModuleNames::from_bytes(c.data(), c.data_offset())?;
-                names.merge(&ModuleNames::from_name(add_name));
-
-                names.section()?.as_custom().append_to(&mut output);
-            }
-
-            CustomSection(c) if c.name() == "component-name" && stack.len() == 0 => {
-                names_found = true;
-                let mut names = ComponentNames::from_bytes(c.data(), c.data_offset())?;
-                names.merge(&ComponentNames::from_name(add_name));
-                names.section()?.as_custom().append_to(&mut output);
-            }
-
-            CustomSection(c) if c.name() == "registry-metadata" && stack.len() == 0 => {
-                // Pass section through if a new registry metadata isn't provided, otherwise ignore and overwrite with new
-                if add_registry_metadata.is_none() {
-                    let registry: RegistryMetadata = RegistryMetadata::from_bytes(&c.data(), 0)?;
-
-                    let registry_metadata = wasm_encoder::CustomSection {
-                        name: Cow::Borrowed("registry-metadata"),
-                        data: Cow::Owned(serde_json::to_vec(&registry)?),
-                    };
-                    registry_metadata.append_to(&mut output);
-                }
-            }
+        // Only rewrite the outermost custom sections
+        if let CustomSection(c) = &payload {
+            if stack.len() == 0 {
+                match c.as_known() {
+                    KnownCustom::Producers(_) => {
+                        producers_found = true;
+                        let mut producers = Producers::from_bytes(c.data(), c.data_offset())?;
+                        // Add to the section according to the command line flags:
+                        producers.merge(&add_producers);
+                        // Encode into output:
+                        producers.section().append_to(&mut output);
+                        continue;
+                    }
+                    KnownCustom::Name(_) => {
+                        names_found = true;
+                        let mut names = ModuleNames::from_bytes(c.data(), c.data_offset())?;
+                        names.merge(&ModuleNames::from_name(add_name));
 
-            // All other sections get passed through unmodified:
-            _ => {
-                if let Some((id, range)) = payload.as_section() {
-                    wasm_encoder::RawSection {
-                        id,
-                        data: &input[range],
+                        names.section()?.as_custom().append_to(&mut output);
+                        continue;
+                    }
+                    KnownCustom::ComponentName(_) => {
+                        names_found = true;
+                        let mut names = ComponentNames::from_bytes(c.data(), c.data_offset())?;
+                        names.merge(&ComponentNames::from_name(add_name));
+                        names.section()?.as_custom().append_to(&mut output);
+                        continue;
                     }
-                    .append_to(&mut output);
+                    KnownCustom::Unknown if c.name() == "registry-metadata" => {
+                        // Pass section through if a new registry metadata isn't provided, otherwise ignore and overwrite with new
+                        if add_registry_metadata.is_none() {
+                            let registry: RegistryMetadata =
+                                RegistryMetadata::from_bytes(&c.data(), 0)?;
+
+                            let registry_metadata = wasm_encoder::CustomSection {
+                                name: Cow::Borrowed("registry-metadata"),
+                                data: Cow::Owned(serde_json::to_vec(&registry)?),
+                            };
+                            registry_metadata.append_to(&mut output);
+                            continue;
+                        }
+                    }
+                    _ => {}
                 }
             }
         }
+        // All other sections get passed through unmodified:
+        if let Some((id, range)) = payload.as_section() {
+            wasm_encoder::RawSection {
+                id,
+                data: &input[range],
+            }
+            .append_to(&mut output);
+        }
     }
     if !names_found && add_name.is_some() {
         if output.starts_with(&wasm_encoder::Component::HEADER) {
@@ -434,39 +440,42 @@ impl Metadata {
                         metadata.last_mut().unwrap().push_child(finished);
                     }
                 }
-                CustomSection(c) if c.name() == "name" => {
-                    let names = ModuleNames::from_bytes(c.data(), c.data_offset())?;
-                    if let Some(name) = names.get_name() {
+                CustomSection(c) => match c.as_known() {
+                    KnownCustom::Name(_) => {
+                        let names = ModuleNames::from_bytes(c.data(), c.data_offset())?;
+                        if let Some(name) = names.get_name() {
+                            metadata
+                                .last_mut()
+                                .expect("non-empty metadata stack")
+                                .set_name(&name);
+                        }
+                    }
+                    KnownCustom::ComponentName(_) => {
+                        let names = ComponentNames::from_bytes(c.data(), c.data_offset())?;
+                        if let Some(name) = names.get_name() {
+                            metadata
+                                .last_mut()
+                                .expect("non-empty metadata stack")
+                                .set_name(name);
+                        }
+                    }
+                    KnownCustom::Producers(_) => {
+                        let producers = Producers::from_bytes(c.data(), c.data_offset())?;
                         metadata
                             .last_mut()
                             .expect("non-empty metadata stack")
-                            .set_name(&name);
+                            .set_producers(producers);
                     }
-                }
-                CustomSection(c) if c.name() == "component-name" => {
-                    let names = ComponentNames::from_bytes(c.data(), c.data_offset())?;
-                    if let Some(name) = names.get_name() {
+                    KnownCustom::Unknown if c.name() == "registry-metadata" => {
+                        let registry: RegistryMetadata =
+                            RegistryMetadata::from_bytes(&c.data(), 0)?;
                         metadata
                             .last_mut()
                             .expect("non-empty metadata stack")
-                            .set_name(name);
+                            .set_registry_metadata(registry);
                     }
-                }
-                CustomSection(c) if c.name() == "producers" => {
-                    let producers = Producers::from_bytes(c.data(), c.data_offset())?;
-                    metadata
-                        .last_mut()
-                        .expect("non-empty metadata stack")
-                        .set_producers(producers);
-                }
-                CustomSection(c) if c.name() == "registry-metadata" => {
-                    let registry: RegistryMetadata = RegistryMetadata::from_bytes(&c.data(), 0)?;
-                    metadata
-                        .last_mut()
-                        .expect("non-empty metadata stack")
-                        .set_registry_metadata(registry);
-                }
-
+                    _ => {}
+                },
                 _ => {}
             }
         }
diff --git a/crates/wasmparser/src/binary_reader.rs b/crates/wasmparser/src/binary_reader.rs
index 00a417fa..ab122f7f 100644
--- a/crates/wasmparser/src/binary_reader.rs
+++ b/crates/wasmparser/src/binary_reader.rs
@@ -103,8 +103,8 @@ impl BinaryReaderError {
 /// A binary reader of the WebAssembly structures and types.
 #[derive(Clone, Debug, Hash)]
 pub struct BinaryReader<'a> {
-    pub(crate) buffer: &'a [u8],
-    pub(crate) position: usize,
+    buffer: &'a [u8],
+    position: usize,
     original_offset: usize,
     allow_memarg64: bool,
 }
diff --git a/crates/wasmparser/src/parser.rs b/crates/wasmparser/src/parser.rs
index 75afed42..5c5f5f20 100644
--- a/crates/wasmparser/src/parser.rs
+++ b/crates/wasmparser/src/parser.rs
@@ -533,15 +533,17 @@ impl Parser {
         };
         // TODO: thread through `offset: u64` to `BinaryReader`, remove
         // the cast here.
-        let mut reader = BinaryReader::new_with_offset(data, self.offset as usize);
+        let starting_offset = self.offset as usize;
+        let mut reader = BinaryReader::new_with_offset(data, starting_offset);
         match self.parse_reader(&mut reader, eof) {
             Ok(payload) => {
                 // Be sure to update our offset with how far we got in the
                 // reader
-                self.offset += usize_to_u64(reader.position);
-                self.max_size -= usize_to_u64(reader.position);
+                let consumed = reader.original_position() - starting_offset;
+                self.offset += usize_to_u64(consumed);
+                self.max_size -= usize_to_u64(consumed);
                 Ok(Chunk::Parsed {
-                    consumed: reader.position,
+                    consumed: consumed,
                     payload,
                 })
             }
@@ -595,7 +597,7 @@ impl Parser {
                     return Ok(Payload::End(reader.original_position()));
                 }
 
-                let id_pos = reader.position;
+                let id_pos = reader.original_position();
                 let id = reader.read_u8()?;
                 if id & 0x80 != 0 {
                     return Err(BinaryReaderError::new("malformed section id", id_pos));
@@ -608,9 +610,10 @@ impl Parser {
                 // but it is required for nested modules/components to correctly ensure
                 // that all sections live entirely within their section of the
                 // file.
+                let consumed = reader.original_position() - id_pos;
                 let section_overflow = self
                     .max_size
-                    .checked_sub(usize_to_u64(reader.position))
+                    .checked_sub(usize_to_u64(consumed))
                     .and_then(|s| s.checked_sub(len.into()))
                     .is_none();
                 if section_overflow {
@@ -1057,9 +1060,9 @@ fn delimited<'a, T>(
     len: &mut u32,
     f: impl FnOnce(&mut BinaryReader<'a>) -> Result<T>,
 ) -> Result<T> {
-    let start = reader.position;
+    let start = reader.original_position();
     let ret = f(reader)?;
-    *len = match (reader.position - start)
+    *len = match (reader.original_position() - start)
         .try_into()
         .ok()
         .and_then(|i| len.checked_sub(i))
diff --git a/crates/wasmparser/src/readers/component/names.rs b/crates/wasmparser/src/readers/component/names.rs
index 32906423..86f91018 100644
--- a/crates/wasmparser/src/readers/component/names.rs
+++ b/crates/wasmparser/src/readers/component/names.rs
@@ -53,7 +53,7 @@ impl<'a> Subsection<'a> for ComponentName<'a> {
                 }
                 ComponentName::Component {
                     name,
-                    name_range: offset..offset + reader.position,
+                    name_range: offset..reader.original_position(),
                 }
             }
             1 => {
diff --git a/crates/wasmparser/src/readers/component/types.rs b/crates/wasmparser/src/readers/component/types.rs
index 989171da..bcd412e5 100644
--- a/crates/wasmparser/src/readers/component/types.rs
+++ b/crates/wasmparser/src/readers/component/types.rs
@@ -125,7 +125,7 @@ pub enum ComponentValType {
 impl<'a> FromReader<'a> for ComponentValType {
     fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
         if let Some(ty) = PrimitiveValType::from_byte(reader.peek()?) {
-            reader.position += 1;
+            reader.read_u8()?;
             return Ok(ComponentValType::Primitive(ty));
         }
 
@@ -319,7 +319,7 @@ impl<'a> FromReader<'a> for ComponentTypeDeclaration<'a> {
         // variant of imports; check for imports here or delegate to
         // `InstanceTypeDeclaration` with the appropriate conversions.
         if reader.peek()? == 0x03 {
-            reader.position += 1;
+            reader.read_u8()?;
             return Ok(ComponentTypeDeclaration::Import(reader.read()?));
         }
 
diff --git a/crates/wasmparser/src/readers/core/coredumps.rs b/crates/wasmparser/src/readers/core/coredumps.rs
index 8fd29859..3a90b4b9 100644
--- a/crates/wasmparser/src/readers/core/coredumps.rs
+++ b/crates/wasmparser/src/readers/core/coredumps.rs
@@ -13,7 +13,7 @@ use crate::{BinaryReader, FromReader, Result};
 /// let data: &[u8] = &[0x00, 0x09, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x77, 0x61,
 ///      0x73, 0x6d];
 /// let mut reader = BinaryReader::new(data);
-/// let core = CoreDumpSection::from_reader(&mut reader).unwrap();
+/// let core = CoreDumpSection::new(reader).unwrap();
 /// assert!(core.name == "test.wasm")
 /// ```
 pub struct CoreDumpSection<'a> {
@@ -21,13 +21,21 @@ pub struct CoreDumpSection<'a> {
     pub name: &'a str,
 }
 
-impl<'a> FromReader<'a> for CoreDumpSection<'a> {
-    fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
+impl<'a> CoreDumpSection<'a> {
+    /// Parses this section from the provided `reader`, derived from a custom
+    /// section.
+    pub fn new(mut reader: BinaryReader<'a>) -> Result<CoreDumpSection<'a>> {
         let pos = reader.original_position();
         if reader.read_u8()? != 0 {
             bail!(pos, "invalid start byte for core dump name");
         }
         let name = reader.read_string()?;
+        if !reader.eof() {
+            bail!(
+                reader.original_position(),
+                "trailing bytes at end of custom section"
+            );
+        }
         Ok(CoreDumpSection { name })
     }
 }
@@ -40,8 +48,8 @@ impl<'a> FromReader<'a> for CoreDumpSection<'a> {
 /// ```
 /// use wasmparser::{ BinaryReader, CoreDumpModulesSection, FromReader, Result };
 /// let data: &[u8] = &[0x01, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74];
-/// let mut reader = BinaryReader::new(data);
-/// let modules_section = CoreDumpModulesSection::from_reader(&mut reader).unwrap();
+/// let reader = BinaryReader::new(data);
+/// let modules_section = CoreDumpModulesSection::new(reader).unwrap();
 /// assert!(modules_section.modules[0] == "test")
 /// ```
 #[derive(Debug)]
@@ -51,8 +59,10 @@ pub struct CoreDumpModulesSection<'a> {
     pub modules: Vec<&'a str>,
 }
 
-impl<'a> FromReader<'a> for CoreDumpModulesSection<'a> {
-    fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
+impl<'a> CoreDumpModulesSection<'a> {
+    /// Parses this section from the provided `reader`, derived from a custom
+    /// section.
+    pub fn new(mut reader: BinaryReader<'a>) -> Result<CoreDumpModulesSection<'a>> {
         let pos = reader.original_position();
         let mut modules = vec![];
         for _ in 0..reader.read_var_u32()? {
@@ -61,6 +71,12 @@ impl<'a> FromReader<'a> for CoreDumpModulesSection<'a> {
             }
             modules.push(reader.read_string()?);
         }
+        if !reader.eof() {
+            bail!(
+                reader.original_position(),
+                "trailing bytes at end of custom section"
+            );
+        }
         Ok(CoreDumpModulesSection { modules })
     }
 }
@@ -70,17 +86,26 @@ pub struct CoreDumpInstancesSection {
     pub instances: Vec<CoreDumpInstance>,
 }
 
-impl<'a> FromReader<'a> for CoreDumpInstancesSection {
-    fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
+impl CoreDumpInstancesSection {
+    /// Parses this section from the provided `reader`, derived from a custom
+    /// section.
+    pub fn new(mut reader: BinaryReader<'_>) -> Result<CoreDumpInstancesSection> {
         let mut instances = vec![];
         for _ in 0..reader.read_var_u32()? {
-            instances.push(CoreDumpInstance::from_reader(reader)?);
+            instances.push(CoreDumpInstance::from_reader(&mut reader)?);
+        }
+        if !reader.eof() {
+            bail!(
+                reader.original_position(),
+                "trailing bytes at end of custom section"
+            );
         }
         Ok(CoreDumpInstancesSection { instances })
     }
 }
 
 /// A single instance from a coredump instances section
+#[derive(Debug)]
 pub struct CoreDumpInstance {
     /// The module that this is an instance of, as an index into a "coremodules"
     /// section.
@@ -129,8 +154,8 @@ impl<'a> FromReader<'a> for CoreDumpInstance {
 /// let data: &[u8] = &[0x00, 0x04, 0x6d, 0x61, 0x69, 0x6e, 0x01, 0x00, 0x04,
 ///     0x2a, 0x33, 0x01, 0x7f, 0x01, 0x01, 0x7f, 0x02];
 /// use wasmparser::{ BinaryReader, CoreDumpStackSection, FromReader };
-/// let mut reader = BinaryReader::new(data);
-/// let corestack = CoreDumpStackSection::from_reader(&mut reader).unwrap();
+/// let reader = BinaryReader::new(data);
+/// let corestack = CoreDumpStackSection::new(reader).unwrap();
 /// assert!(corestack.name == "main");
 /// assert!(corestack.frames.len() == 1);
 /// let frame = &corestack.frames[0];
@@ -147,8 +172,10 @@ pub struct CoreDumpStackSection<'a> {
     pub frames: Vec<CoreDumpStackFrame>,
 }
 
-impl<'a> FromReader<'a> for CoreDumpStackSection<'a> {
-    fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
+impl<'a> CoreDumpStackSection<'a> {
+    /// Parses this section from the provided `reader`, derived from a custom
+    /// section.
+    pub fn new(mut reader: BinaryReader<'a>) -> Result<CoreDumpStackSection<'a>> {
         let pos = reader.original_position();
         if reader.read_u8()? != 0 {
             bail!(pos, "invalid start byte for core dump stack name");
@@ -156,7 +183,13 @@ impl<'a> FromReader<'a> for CoreDumpStackSection<'a> {
         let name = reader.read_string()?;
         let mut frames = vec![];
         for _ in 0..reader.read_var_u32()? {
-            frames.push(CoreDumpStackFrame::from_reader(reader)?);
+            frames.push(CoreDumpStackFrame::from_reader(&mut reader)?);
+        }
+        if !reader.eof() {
+            bail!(
+                reader.original_position(),
+                "trailing bytes at end of custom section"
+            );
         }
         Ok(CoreDumpStackSection {
             name: name,
diff --git a/crates/wasmparser/src/readers/core/custom.rs b/crates/wasmparser/src/readers/core/custom.rs
index 71ec8087..6e90563a 100644
--- a/crates/wasmparser/src/readers/core/custom.rs
+++ b/crates/wasmparser/src/readers/core/custom.rs
@@ -50,6 +50,94 @@ impl<'a> CustomSectionReader<'a> {
     pub fn range(&self) -> Range<usize> {
         self.range.clone()
     }
+
+    /// Attempts to match and see if this custom section is statically known to
+    /// `wasmparser` with any known section reader.
+    ///
+    /// This will inspect `self.name()` and return a [`KnownCustom`] if the name
+    /// matches a known custom section where there is a parser available for it.
+    /// This can also be used as a convenience function for creating such
+    /// parsers.
+    ///
+    /// If the custom section name is not known, or if a reader could not be
+    /// created, then `KnownCustom::Unknown` is returned.
+    pub fn as_known(&self) -> KnownCustom<'a> {
+        match self.name() {
+            "name" => KnownCustom::Name(crate::NameSectionReader::new(self.data, self.data_offset)),
+            "component-name" => KnownCustom::ComponentName(crate::ComponentNameSectionReader::new(
+                self.data,
+                self.data_offset,
+            )),
+            "metadata.code.branch_hint" => {
+                match crate::BranchHintSectionReader::new(self.data, self.data_offset) {
+                    Ok(s) => KnownCustom::BranchHints(s),
+                    Err(_) => KnownCustom::Unknown,
+                }
+            }
+            "producers" => match crate::ProducersSectionReader::new(self.data, self.data_offset) {
+                Ok(s) => KnownCustom::Producers(s),
+                Err(_) => KnownCustom::Unknown,
+            },
+            "dylink.0" => KnownCustom::Dylink0(crate::Dylink0SectionReader::new(
+                self.data,
+                self.data_offset,
+            )),
+            "core" => match crate::CoreDumpSection::new(BinaryReader::new_with_offset(
+                self.data,
+                self.data_offset,
+            )) {
+                Ok(s) => KnownCustom::CoreDump(s),
+                Err(_) => KnownCustom::Unknown,
+            },
+            "coremodules" => match crate::CoreDumpModulesSection::new(
+                BinaryReader::new_with_offset(self.data, self.data_offset),
+            ) {
+                Ok(s) => KnownCustom::CoreDumpModules(s),
+                Err(_) => KnownCustom::Unknown,
+            },
+            "coreinstances" => match crate::CoreDumpInstancesSection::new(
+                BinaryReader::new_with_offset(self.data, self.data_offset),
+            ) {
+                Ok(s) => KnownCustom::CoreDumpInstances(s),
+                Err(_) => KnownCustom::Unknown,
+            },
+            "corestack" => match crate::CoreDumpStackSection::new(BinaryReader::new_with_offset(
+                self.data,
+                self.data_offset,
+            )) {
+                Ok(s) => KnownCustom::CoreDumpStack(s),
+                Err(_) => KnownCustom::Unknown,
+            },
+            "linking" => match crate::LinkingSectionReader::new(self.data, self.data_offset) {
+                Ok(s) => KnownCustom::Linking(s),
+                Err(_) => KnownCustom::Unknown,
+            },
+            s if s.starts_with("reloc.") => {
+                match crate::RelocSectionReader::new(self.data, self.data_offset) {
+                    Ok(s) => KnownCustom::Reloc(s),
+                    Err(_) => KnownCustom::Unknown,
+                }
+            }
+            _ => KnownCustom::Unknown,
+        }
+    }
+}
+
+/// Return value of [`CustomSectionReader::as_known`].
+#[allow(missing_docs)]
+pub enum KnownCustom<'a> {
+    Name(crate::NameSectionReader<'a>),
+    ComponentName(crate::ComponentNameSectionReader<'a>),
+    BranchHints(crate::BranchHintSectionReader<'a>),
+    Producers(crate::ProducersSectionReader<'a>),
+    Dylink0(crate::Dylink0SectionReader<'a>),
+    CoreDump(crate::CoreDumpSection<'a>),
+    CoreDumpStack(crate::CoreDumpStackSection<'a>),
+    CoreDumpInstances(crate::CoreDumpInstancesSection),
+    CoreDumpModules(crate::CoreDumpModulesSection<'a>),
+    Linking(crate::LinkingSectionReader<'a>),
+    Reloc(crate::RelocSectionReader<'a>),
+    Unknown,
 }
 
 impl<'a> fmt::Debug for CustomSectionReader<'a> {
diff --git a/crates/wasmparser/src/readers/core/names.rs b/crates/wasmparser/src/readers/core/names.rs
index d5278b8e..549338bb 100644
--- a/crates/wasmparser/src/readers/core/names.rs
+++ b/crates/wasmparser/src/readers/core/names.rs
@@ -135,7 +135,7 @@ impl<'a> Subsection<'a> for Name<'a> {
                 }
                 Name::Module {
                     name,
-                    name_range: offset..offset + reader.position,
+                    name_range: offset..reader.original_position(),
                 }
             }
             1 => Name::Function(NameMap::new(data, offset)?),
diff --git a/crates/wasmparser/src/readers/core/types.rs b/crates/wasmparser/src/readers/core/types.rs
index 96fc4b3a..d015f035 100644
--- a/crates/wasmparser/src/readers/core/types.rs
+++ b/crates/wasmparser/src/readers/core/types.rs
@@ -1368,11 +1368,11 @@ impl<'a> FromReader<'a> for StorageType {
     fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
         match reader.peek()? {
             0x78 => {
-                reader.position += 1;
+                reader.read_u8()?;
                 Ok(StorageType::I8)
             }
             0x77 => {
-                reader.position += 1;
+                reader.read_u8()?;
                 Ok(StorageType::I16)
             }
             _ => Ok(StorageType::Val(reader.read()?)),
@@ -1384,23 +1384,23 @@ impl<'a> FromReader<'a> for ValType {
     fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
         match reader.peek()? {
             0x7F => {
-                reader.position += 1;
+                reader.read_u8()?;
                 Ok(ValType::I32)
             }
             0x7E => {
-                reader.position += 1;
+                reader.read_u8()?;
                 Ok(ValType::I64)
             }
             0x7D => {
-                reader.position += 1;
+                reader.read_u8()?;
                 Ok(ValType::F32)
             }
             0x7C => {
-                reader.position += 1;
+                reader.read_u8()?;
                 Ok(ValType::F64)
             }
             0x7B => {
-                reader.position += 1;
+                reader.read_u8()?;
                 Ok(ValType::V128)
             }
             0x70 | 0x6F | 0x64 | 0x63 | 0x6E | 0x71 | 0x72 | 0x73 | 0x74 | 0x6D | 0x6B | 0x6A
@@ -1440,51 +1440,51 @@ impl<'a> FromReader<'a> for HeapType {
     fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
         match reader.peek()? {
             0x70 => {
-                reader.position += 1;
+                reader.read_u8()?;
                 Ok(HeapType::Func)
             }
             0x6F => {
-                reader.position += 1;
+                reader.read_u8()?;
                 Ok(HeapType::Extern)
             }
             0x6E => {
-                reader.position += 1;
+                reader.read_u8()?;
                 Ok(HeapType::Any)
             }
             0x71 => {
-                reader.position += 1;
+                reader.read_u8()?;
                 Ok(HeapType::None)
             }
             0x72 => {
-                reader.position += 1;
+                reader.read_u8()?;
                 Ok(HeapType::NoExtern)
             }
             0x73 => {
-                reader.position += 1;
+                reader.read_u8()?;
                 Ok(HeapType::NoFunc)
             }
             0x6D => {
-                reader.position += 1;
+                reader.read_u8()?;
                 Ok(HeapType::Eq)
             }
             0x6B => {
-                reader.position += 1;
+                reader.read_u8()?;
                 Ok(HeapType::Struct)
             }
             0x6A => {
-                reader.position += 1;
+                reader.read_u8()?;
                 Ok(HeapType::Array)
             }
             0x6C => {
-                reader.position += 1;
+                reader.read_u8()?;
                 Ok(HeapType::I31)
             }
             0x69 => {
-                reader.position += 1;
+                reader.read_u8()?;
                 Ok(HeapType::Exn)
             }
             0x74 => {
-                reader.position += 1;
+                reader.read_u8()?;
                 Ok(HeapType::NoExn)
             }
             _ => {
diff --git a/crates/wasmprinter/src/lib.rs b/crates/wasmprinter/src/lib.rs
index edde2985..22f57673 100644
--- a/crates/wasmprinter/src/lib.rs
+++ b/crates/wasmprinter/src/lib.rs
@@ -283,23 +283,20 @@ impl Printer {
                     bytes = &bytes[offset..];
                 }
 
-                // Ignore any error associated with the name sections.
-                Payload::CustomSection(c) if c.name() == "name" => {
-                    let reader = NameSectionReader::new(c.data(), c.data_offset());
-                    drop(self.register_names(state, reader));
-                }
-                Payload::CustomSection(c) if c.name() == "component-name" => {
-                    let reader = ComponentNameSectionReader::new(c.data(), c.data_offset());
-                    drop(self.register_component_names(state, reader));
-                }
-
-                Payload::CustomSection(c) if c.name() == "metadata.code.branch_hint" => {
-                    drop(
-                        self.register_branch_hint_section(BranchHintSectionReader::new(
-                            c.data(),
-                            c.data_offset(),
-                        )?),
-                    );
+                Payload::CustomSection(c) => {
+                    // Ignore any error associated with the name sections.
+                    match c.as_known() {
+                        KnownCustom::Name(reader) => {
+                            drop(self.register_names(state, reader));
+                        }
+                        KnownCustom::ComponentName(reader) => {
+                            drop(self.register_component_names(state, reader));
+                        }
+                        KnownCustom::BranchHints(reader) => {
+                            drop(self.register_branch_hint_section(reader));
+                        }
+                        _ => {}
+                    }
                 }
 
                 Payload::End(_) => break,
@@ -2716,31 +2713,34 @@ impl Printer {
         state: &State,
         section: CustomSectionReader<'_>,
     ) -> Result<()> {
-        match section.name() {
+        match section.as_known() {
             // For now `wasmprinter` has invented syntax for `producers` and
             // `dylink.0` below to use in tests. Note that this syntax is not
             // official at this time.
-            "producers" => {
+            KnownCustom::Producers(s) => {
                 self.newline(section.range().start);
-                self.print_producers_section(ProducersSectionReader::new(
-                    section.data(),
-                    section.data_offset(),
-                )?)
+                self.print_producers_section(s)
             }
-            "dylink.0" => {
+            KnownCustom::Dylink0(s) => {
                 self.newline(section.range().start);
-                self.print_dylink0_section(Dylink0SectionReader::new(
-                    section.data(),
-                    section.data_offset(),
-                ))
+                self.print_dylink0_section(s)
             }
 
             // These are parsed during `read_names_and_code` and are part of
             // printing elsewhere, so don't print them.
-            "name" | "component-name" | "metadata.code.branch_hint" => Ok(()),
-
-            // Unknown custom sections get a `@custom` annotation printed.
-            _ => self.print_raw_custom_section(state, section),
+            KnownCustom::Name(_) | KnownCustom::ComponentName(_) | KnownCustom::BranchHints(_) => {
+                Ok(())
+            }
+
+            // Custom sections without a text format at this time and unknown
+            // custom sections get a `@custom` annotation printed.
+            KnownCustom::CoreDump(_)
+            | KnownCustom::CoreDumpModules(_)
+            | KnownCustom::CoreDumpStack(_)
+            | KnownCustom::CoreDumpInstances(_)
+            | KnownCustom::Linking(_)
+            | KnownCustom::Reloc(_)
+            | KnownCustom::Unknown => self.print_raw_custom_section(state, section),
         }
     }
 
diff --git a/crates/wit-component/src/gc.rs b/crates/wit-component/src/gc.rs
index 5244f37b..8c1ffaa1 100644
--- a/crates/wit-component/src/gc.rs
+++ b/crates/wit-component/src/gc.rs
@@ -329,14 +329,11 @@ impl<'a> Module<'a> {
 
                 // Ignore all custom sections except for the `name` and
                 // `producers` sections which we parse, but ignore errors within.
-                Payload::CustomSection(s) => {
-                    if s.name() == "name" {
-                        drop(self.parse_name_section(&s));
-                    }
-                    if s.name() == "producers" {
-                        drop(self.parse_producers_section(&s));
-                    }
-                }
+                Payload::CustomSection(s) => match s.as_known() {
+                    KnownCustom::Name(s) => drop(self.parse_name_section(s)),
+                    KnownCustom::Producers(_) => drop(self.parse_producers_section(&s)),
+                    _ => {}
+                },
 
                 // sections that shouldn't appear in the specially-crafted core wasm
                 // adapter self we're processing
@@ -369,8 +366,7 @@ impl<'a> Module<'a> {
         Ok(())
     }
 
-    fn parse_name_section(&mut self, section: &CustomSectionReader<'a>) -> Result<()> {
-        let section = NameSectionReader::new(section.data(), section.data_offset());
+    fn parse_name_section(&mut self, section: NameSectionReader<'a>) -> Result<()> {
         for s in section {
             match s? {
                 Name::Function(map) => {
diff --git a/crates/wit-component/src/linking/metadata.rs b/crates/wit-component/src/linking/metadata.rs
index ca0b4644..5dca719c 100644
--- a/crates/wit-component/src/linking/metadata.rs
+++ b/crates/wit-component/src/linking/metadata.rs
@@ -8,8 +8,8 @@ use {
         fmt,
     },
     wasmparser::{
-        BinaryReader, BinaryReaderError, ExternalKind, FuncType, Parser, Payload, RefType,
-        Subsection, Subsections, SymbolFlags, TableType, TypeRef, ValType,
+        BinaryReader, BinaryReaderError, ExternalKind, FuncType, KnownCustom, Parser, Payload,
+        RefType, Subsection, Subsections, SymbolFlags, TableType, TypeRef, ValType,
     },
 };
 
@@ -333,23 +333,29 @@ impl<'a> Metadata<'a> {
 
         for payload in Parser::new(0).parse_all(module) {
             match payload? {
-                Payload::CustomSection(section) if section.name() == "dylink.0" => {
-                    let reader = DylinkSectionReader::new(section.data(), section.data_offset());
-                    for subsection in reader {
-                        match subsection.context("failed to parse `dylink.0` subsection")? {
-                            DylinkSubsection::MemInfo(info) => result.mem_info = info,
-                            DylinkSubsection::Needed(needed) => result.needed_libs = needed.clone(),
-                            DylinkSubsection::ExportInfo(info) => {
-                                export_info.extend(info.iter().map(|info| (info.name, info.flags)));
-                            }
-                            DylinkSubsection::ImportInfo(info) => {
-                                import_info.extend(
-                                    info.iter()
-                                        .map(|info| ((info.module, info.field), info.flags)),
-                                );
-                            }
-                            DylinkSubsection::Unknown(index) => {
-                                bail!("unrecognized `dylink.0` subsection: {index}")
+                Payload::CustomSection(section) => {
+                    if let KnownCustom::Dylink0(_) = section.as_known() {
+                        let reader =
+                            DylinkSectionReader::new(section.data(), section.data_offset());
+                        for subsection in reader {
+                            match subsection.context("failed to parse `dylink.0` subsection")? {
+                                DylinkSubsection::MemInfo(info) => result.mem_info = info,
+                                DylinkSubsection::Needed(needed) => {
+                                    result.needed_libs = needed.clone()
+                                }
+                                DylinkSubsection::ExportInfo(info) => {
+                                    export_info
+                                        .extend(info.iter().map(|info| (info.name, info.flags)));
+                                }
+                                DylinkSubsection::ImportInfo(info) => {
+                                    import_info.extend(
+                                        info.iter()
+                                            .map(|info| ((info.module, info.field), info.flags)),
+                                    );
+                                }
+                                DylinkSubsection::Unknown(index) => {
+                                    bail!("unrecognized `dylink.0` subsection: {index}")
+                                }
                             }
                         }
                     }
diff --git a/src/bin/wasm-tools/demangle.rs b/src/bin/wasm-tools/demangle.rs
index 613177e3..fad848a8 100644
--- a/src/bin/wasm-tools/demangle.rs
+++ b/src/bin/wasm-tools/demangle.rs
@@ -1,6 +1,6 @@
 use anyhow::{bail, Result};
 use wasm_encoder::{IndirectNameMap, NameMap, NameSection, RawSection};
-use wasmparser::{Name, NameSectionReader, Parser, Payload::*};
+use wasmparser::{KnownCustom, Name, NameSectionReader, Parser, Payload::*};
 
 /// Demangle Rust and C++ symbol names in the `name` section.
 ///
@@ -30,13 +30,15 @@ impl Opts {
         for payload in Parser::new(0).parse_all(&input) {
             let payload = payload?;
             match &payload {
-                CustomSection(c) if c.name() == "name" => {
-                    match self.demangle(c.data(), c.data_offset()) {
-                        Ok(new_section) => {
-                            module.section(&new_section);
-                            continue;
+                CustomSection(c) => {
+                    if let KnownCustom::Name(s) = c.as_known() {
+                        match self.demangle(s) {
+                            Ok(new_section) => {
+                                module.section(&new_section);
+                                continue;
+                            }
+                            Err(e) => log::debug!("error parsing name section {e:?}"),
                         }
-                        Err(e) => log::debug!("error parsing name section {e:?}"),
                     }
                 }
                 Version { encoding, .. } if *encoding == wasmparser::Encoding::Component => {
@@ -59,9 +61,9 @@ impl Opts {
         Ok(())
     }
 
-    fn demangle(&self, section: &[u8], offset: usize) -> Result<NameSection> {
+    fn demangle(&self, section: NameSectionReader<'_>) -> Result<NameSection> {
         let mut new_section = NameSection::new();
-        for section in NameSectionReader::new(section, offset) {
+        for section in section {
             match section? {
                 Name::Module { name, .. } => new_section.module(name),
                 Name::Memory(names) => new_section.memories(&self.name_map(names)?),
diff --git a/src/bin/wasm-tools/dump.rs b/src/bin/wasm-tools/dump.rs
index 71ccb91b..7c3539ec 100644
--- a/src/bin/wasm-tools/dump.rs
+++ b/src/bin/wasm-tools/dump.rs
@@ -446,21 +446,18 @@ impl<'a> Dump<'a> {
                     self.color_print(c.range().start)?;
                     write!(self.state, "name: {:?}", c.name())?;
                     self.print(c.data_offset())?;
-                    match c.name() {
-                        "name" => {
-                            let iter = NameSectionReader::new(c.data(), c.data_offset());
+                    match c.as_known() {
+                        KnownCustom::Name(iter) => {
                             self.print_subsections(iter, |me, item, pos| {
                                 me.print_core_name(item, pos)
                             })?;
                         }
-                        "component-name" => {
-                            let iter = ComponentNameSectionReader::new(c.data(), c.data_offset());
+                        KnownCustom::ComponentName(iter) => {
                             self.print_subsections(iter, |me, item, pos| {
                                 me.print_component_name(item, pos)
                             })?;
                         }
-                        "producers" => {
-                            let iter = ProducersSectionReader::new(c.data(), c.data_offset())?;
+                        KnownCustom::Producers(iter) => {
                             self.print_iter(iter, |me, _pos, item| {
                                 write!(me.state, "field: {}", item.name)?;
                                 me.print(item.values.range().start)?;
@@ -471,15 +468,13 @@ impl<'a> Dump<'a> {
                                 })
                             })?;
                         }
-                        "dylink.0" => {
-                            let iter = Dylink0SectionReader::new(c.data(), c.data_offset());
+                        KnownCustom::Dylink0(iter) => {
                             self.print_subsections(iter, |me, item, pos| {
                                 write!(me.state, "{item:?}")?;
                                 me.print(pos)
                             })?;
                         }
-                        "metadata.code.branch_hint" => {
-                            let iter = BranchHintSectionReader::new(c.data(), c.data_offset())?;
+                        KnownCustom::BranchHints(iter) => {
                             self.print_iter(iter, |me, _pos, item| {
                                 write!(me.state, "func: {}", item.func)?;
                                 me.print(item.hints.range().start)?;
@@ -490,7 +485,40 @@ impl<'a> Dump<'a> {
                                 })
                             })?;
                         }
-                        _ => {
+                        KnownCustom::CoreDump(s) => {
+                            write!(self.state, "name: {}", s.name)?;
+                            self.print(c.range().end)?;
+                        }
+                        KnownCustom::CoreDumpModules(s) => {
+                            write!(self.state, "modules: {:?}", s.modules)?;
+                            self.print(c.range().end)?;
+                        }
+                        KnownCustom::CoreDumpInstances(s) => {
+                            write!(self.state, "instances: {:?}", s.instances)?;
+                            self.print(c.range().end)?;
+                        }
+                        KnownCustom::CoreDumpStack(s) => {
+                            write!(self.state, "stacks: {} / {:?}", s.name, s.frames)?;
+                            self.print(c.range().end)?;
+                        }
+                        KnownCustom::Linking(s) => {
+                            let subsections = s.subsections();
+                            write!(self.state, "linking version {}", s.version())?;
+                            self.print(subsections.range().start)?;
+                            self.print_subsections(subsections, |me, item, pos| {
+                                me.print_linking_subsection(item, pos)
+                            })?;
+                        }
+                        KnownCustom::Reloc(s) => {
+                            let entries = s.entries();
+                            write!(self.state, "section {}", s.section_index())?;
+                            self.print(entries.range().start)?;
+                            self.print_iter(entries, |me, pos, item| {
+                                write!(me.state, "{item:?}")?;
+                                me.print(pos)
+                            })?;
+                        }
+                        KnownCustom::Unknown => {
                             self.print_byte_header()?;
                             for _ in 0..NBYTES {
                                 write!(self.dst, "---")?;
@@ -641,6 +669,32 @@ impl<'a> Dump<'a> {
         Ok(())
     }
 
+    fn print_linking_subsection(&mut self, s: Linking<'_>, end: usize) -> Result<()> {
+        match s {
+            Linking::SegmentInfo(map) => self.section(map, "segment info", |me, pos, item| {
+                write!(me.state, "{item:?}")?;
+                me.print(pos)
+            }),
+            Linking::InitFuncs(map) => self.section(map, "init funcs", |me, pos, item| {
+                write!(me.state, "{item:?}")?;
+                me.print(pos)
+            }),
+            Linking::ComdatInfo(map) => self.section(map, "comdat info", |me, pos, item| {
+                write!(me.state, "{item:?}")?;
+                me.print(pos)
+            }),
+            Linking::SymbolTable(map) => self.section(map, "symbol table", |me, pos, item| {
+                write!(me.state, "{item:?}")?;
+                me.print(pos)
+            }),
+            Linking::Unknown { ty, range, .. } => {
+                write!(self.state, "unknown subsection: {}", ty)?;
+                self.print(range.start)?;
+                self.print(end)
+            }
+        }
+    }
+
     fn section<'b, T>(
         &mut self,
         iter: SectionLimited<'b, T>,


Test patch:
diff --git a/crates/wast/tests/annotations.rs b/crates/wast/tests/annotations.rs
index 706a392c..49bb34e5 100644
--- a/crates/wast/tests/annotations.rs
+++ b/crates/wast/tests/annotations.rs
@@ -99,8 +99,8 @@ fn assert_local_name(name: &str, wat: &str) -> anyhow::Result<()> {
 fn get_name_section(wasm: &[u8]) -> anyhow::Result<NameSectionReader<'_>> {
     for payload in Parser::new(0).parse_all(&wasm) {
         if let Payload::CustomSection(c) = payload? {
-            if c.name() == "name" {
-                return Ok(NameSectionReader::new(c.data(), c.data_offset()));
+            if let KnownCustom::Name(s) = c.as_known() {
+                return Ok(s);
             }
         }
     }
diff --git a/tests/cli/dump-llvm-object.wat b/tests/cli/dump-llvm-object.wat
new file mode 100644
index 00000000..be5463b1
--- /dev/null
+++ b/tests/cli/dump-llvm-object.wat
@@ -0,0 +1,263 @@
+;; RUN: dump %
+
+;; This test case was generated with this Rust source file:
+;;
+;;    fn main() {}
+;;
+;; compiled with Rust 1.78.0 with:
+;;
+;;    rustc foo.rs --emit obj --target wasm32-wasi
+;;
+;; and then the output of `wasm-tools print` at the time over `foo.o` was
+;; pasted below. This works around how the linking and reloc custom sections do
+;; not have a text format at this time.
+
+(module
+  (type (;0;) (func (param i32)))
+  (type (;1;) (func (param i32 i32 i32 i32) (result i32)))
+  (type (;2;) (func (param i32 i32 i32 i32 i32) (result i32)))
+  (type (;3;) (func (param i32) (result i32)))
+  (type (;4;) (func (result i32)))
+  (type (;5;) (func))
+  (import "env" "__linear_memory" (memory (;0;) 1))
+  (import "env" "__stack_pointer" (global (;0;) (mut i32)))
+  (import "env" "_ZN3std2rt19lang_start_internal17h8f61396649e22e0eE" (func (;0;) (type 2)))
+  (import "env" "__indirect_function_table" (table (;0;) 4 funcref))
+  (func (;1;) (type 0) (param i32)
+    local.get 0
+    call 2
+    return
+  )
+  (func (;2;) (type 0) (param i32)
+    (local i32 i32 i32 i32 i32)
+    global.get 0
+    local.set 1
+    i32.const 16
+    local.set 2
+    local.get 1
+    local.get 2
+    i32.sub
+    local.set 3
+    local.get 3
+    global.set 0
+    local.get 0
+    call_indirect (type 5)
+    i32.const 16
+    local.set 4
+    local.get 3
+    local.get 4
+    i32.add
+    local.set 5
+    local.get 5
+    global.set 0
+    return
+  )
+  (func (;3;) (type 1) (param i32 i32 i32 i32) (result i32)
+    (local i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32)
+    global.get 0
+    local.set 4
+    i32.const 16
+    local.set 5
+    local.get 4
+    local.get 5
+    i32.sub
+    local.set 6
+    local.get 6
+    global.set 0
+    local.get 6
+    local.get 0
+    i32.store offset=12
+    i32.const 12
+    local.set 7
+    local.get 6
+    local.get 7
+    i32.add
+    local.set 8
+    local.get 8
+    local.set 9
+    i32.const 0
+    local.set 10
+    local.get 9
+    local.get 10
+    local.get 1
+    local.get 2
+    local.get 3
+    call 0
+    local.set 11
+    local.get 6
+    local.get 11
+    i32.store offset=8
+    local.get 6
+    i32.load offset=8
+    local.set 12
+    i32.const 16
+    local.set 13
+    local.get 6
+    local.get 13
+    i32.add
+    local.set 14
+    local.get 14
+    global.set 0
+    local.get 12
+    return
+  )
+  (func (;4;) (type 3) (param i32) (result i32)
+    (local i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32)
+    global.get 0
+    local.set 1
+    i32.const 16
+    local.set 2
+    local.get 1
+    local.get 2
+    i32.sub
+    local.set 3
+    local.get 3
+    global.set 0
+    local.get 0
+    i32.load
+    local.set 4
+    local.get 4
+    call 1
+    call 5
+    local.set 5
+    i32.const 1
+    local.set 6
+    local.get 5
+    local.get 6
+    i32.and
+    local.set 7
+    local.get 3
+    local.get 7
+    i32.store8 offset=15
+    local.get 3
+    i32.load8_u offset=15
+    local.set 8
+    i32.const 1
+    local.set 9
+    local.get 8
+    local.get 9
+    i32.and
+    local.set 10
+    i32.const 16
+    local.set 11
+    local.get 3
+    local.get 11
+    i32.add
+    local.set 12
+    local.get 12
+    global.set 0
+    local.get 10
+    return
+  )
+  (func (;5;) (type 4) (result i32)
+    (local i32 i32 i32)
+    i32.const 0
+    local.set 0
+    i32.const 1
+    local.set 1
+    local.get 0
+    local.get 1
+    i32.and
+    local.set 2
+    local.get 2
+    return
+  )
+  (func (;6;) (type 3) (param i32) (result i32)
+    (local i32 i32 i32 i32 i32 i32 i32)
+    global.get 0
+    local.set 1
+    i32.const 16
+    local.set 2
+    local.get 1
+    local.get 2
+    i32.sub
+    local.set 3
+    local.get 3
+    global.set 0
+    local.get 0
+    i32.load
+    local.set 4
+    local.get 4
+    call 7
+    local.set 5
+    i32.const 16
+    local.set 6
+    local.get 3
+    local.get 6
+    i32.add
+    local.set 7
+    local.get 7
+    global.set 0
+    local.get 5
+    return
+  )
+  (func (;7;) (type 3) (param i32) (result i32)
+    (local i32 i32 i32 i32 i32 i32 i32 i32 i32)
+    global.get 0
+    local.set 1
+    i32.const 16
+    local.set 2
+    local.get 1
+    local.get 2
+    i32.sub
+    local.set 3
+    local.get 3
+    global.set 0
+    local.get 3
+    local.get 0
+    i32.store offset=8
+    i32.const 8
+    local.set 4
+    local.get 3
+    local.get 4
+    i32.add
+    local.set 5
+    local.get 5
+    local.set 6
+    local.get 6
+    call 4
+    local.set 7
+    i32.const 16
+    local.set 8
+    local.get 3
+    local.get 8
+    i32.add
+    local.set 9
+    local.get 9
+    global.set 0
+    local.get 7
+    return
+  )
+  (func (;8;) (type 0) (param i32)
+    return
+  )
+  (func (;9;) (type 5)
+    return
+  )
+  (func (;10;) (type 4) (result i32)
+    (local i32 i32 i32 i32)
+    i32.const 1
+    local.set 0
+    i32.const 0
+    local.set 1
+    i32.const 0
+    local.set 2
+    local.get 0
+    local.get 1
+    local.get 1
+    local.get 2
+    call 3
+    local.set 3
+    local.get 3
+    return
+  )
+  (elem (;0;) (i32.const 1) func 9 8 6 4)
+  (data (;0;) (i32.const 0) "\02\00\00\00\04\00\00\00\04\00\00\00\03\00\00\00\04\00\00\00\04\00\00\00")
+  (@custom "linking" (after data) "\02\08\c9\85\80\80\00\0d\00\02\01O_ZN3std10sys_common9backtrace28__rust_begin_short_backtrace17hf1ecc3b2b910465aE\00\02\02:_ZN4core3ops8function6FnOnce9call_once17h06fc313c2ecf3d24E\00\04\03*_ZN3std2rt10lang_start17h385f18abde4350daE\02\10\00\01\02\0d.L__unnamed_1\00\00\18\00\10\00\00\02\04H_ZN3std2rt10lang_start28_$u7b$$u7b$closure$u7d$$u7d$17h24681ec0ade90745E\00\02\05V_ZN54_$LT$$LP$$RP$$u20$as$u20$std..process..Termination$GT$6report17haa5bf2f79896d941E\00\02\06Z_ZN4core3ops8function6FnOnce40call_once$u7b$$u7b$vtable.shim$u7d$$u7d$17hc804a9e31dd3115cE\00\02\07:_ZN4core3ops8function6FnOnce9call_once17h20fe7bf2b7692f9bE\00\02\08w_ZN4core3ptr85drop_in_place$LT$std..rt..lang_start$LT$$LP$$RP$$GT$..$u7b$$u7b$closure$u7d$$u7d$$GT$17h26d303f198cad6c2E\00\02\09 _ZN3foo4main17h694cf023eeafda77E\00\04\0a\0b__main_void\05\99\80\80\80\00\01\15.rodata..L__unnamed_1\02\00")
+  (@custom "reloc.CODE" (after data) "\05\19\00\06\01\07\12\03\07'\03\06/\05\07C\03\07O\03\07d\03\04\80\01\04\00\00\92\01\05\07\b5\01\03\07\c3\01\03\07\d8\01\03\00\e7\01\00\00\ed\01\07\07\a6\02\03\07\cb\02\03\07\e0\02\03\00\ef\02\09\07\84\03\03\07\92\03\03\07\a7\03\03\00\c5\03\06\07\da\03\03\01\f0\03\0b\00\88\04\02")
+  (@custom "reloc.DATA" (after data) "\06\04\02\06\0a\02\12\08\02\16\06\02\1a\06")
+  (@producers
+    (processed-by "rustc" "1.78.0 (9b00956e5 2024-04-29)")
+  )
+  (@custom "target_features" (after data) "\02+\0fmutable-globals+\08sign-ext")
+)
diff --git a/tests/cli/dump-llvm-object.wat.stdout b/tests/cli/dump-llvm-object.wat.stdout
new file mode 100644
index 00000000..fbf6d9cf
--- /dev/null
+++ b/tests/cli/dump-llvm-object.wat.stdout
@@ -0,0 +1,601 @@
+   0x0 | 00 61 73 6d | version 1 (Module)
+       | 01 00 00 00
+   0x8 | 01 22       | type section
+   0xa | 06          | 6 count
+--- rec group 0 (implicit) ---
+   0xb | 60 01 7f 00 | [type 0] SubType { is_final: true, supertype_idx: None, composite_type: Func(FuncType { params: [I32], results: [] }) }
+--- rec group 1 (implicit) ---
+   0xf | 60 04 7f 7f | [type 1] SubType { is_final: true, supertype_idx: None, composite_type: Func(FuncType { params: [I32, I32, I32, I32], results: [I32] }) }
+       | 7f 7f 01 7f
+--- rec group 2 (implicit) ---
+  0x17 | 60 05 7f 7f | [type 2] SubType { is_final: true, supertype_idx: None, composite_type: Func(FuncType { params: [I32, I32, I32, I32, I32], results: [I32] }) }
+       | 7f 7f 7f 01
+       | 7f         
+--- rec group 3 (implicit) ---
+  0x20 | 60 01 7f 01 | [type 3] SubType { is_final: true, supertype_idx: None, composite_type: Func(FuncType { params: [I32], results: [I32] }) }
+       | 7f         
+--- rec group 4 (implicit) ---
+  0x25 | 60 00 01 7f | [type 4] SubType { is_final: true, supertype_idx: None, composite_type: Func(FuncType { params: [], results: [I32] }) }
+--- rec group 5 (implicit) ---
+  0x29 | 60 00 00    | [type 5] SubType { is_final: true, supertype_idx: None, composite_type: Func(FuncType { params: [], results: [] }) }
+  0x2c | 02 8b 01    | import section
+  0x2f | 04          | 4 count
+  0x30 | 03 65 6e 76 | import [memory 0] Import { module: "env", name: "__linear_memory", ty: Memory(MemoryType { memory64: false, shared: false, initial: 1, maximum: None, page_size_log2: None }) }
+       | 0f 5f 5f 6c
+       | 69 6e 65 61
+       | 72 5f 6d 65
+       | 6d 6f 72 79
+       | 02 00 01   
+  0x47 | 03 65 6e 76 | import [global 0] Import { module: "env", name: "__stack_pointer", ty: Global(GlobalType { content_type: I32, mutable: true, shared: false }) }
+       | 0f 5f 5f 73
+       | 74 61 63 6b
+       | 5f 70 6f 69
+       | 6e 74 65 72
+       | 03 7f 01   
+  0x5e | 03 65 6e 76 | import [func 0] Import { module: "env", name: "_ZN3std2rt19lang_start_internal17h8f61396649e22e0eE", ty: Func(2) }
+       | 33 5f 5a 4e
+       | 33 73 74 64
+       | 32 72 74 31
+       | 39 6c 61 6e
+       | 67 5f 73 74
+       | 61 72 74 5f
+       | 69 6e 74 65
+       | 72 6e 61 6c
+       | 31 37 68 38
+       | 66 36 31 33
+       | 39 36 36 34
+       | 39 65 32 32
+       | 65 30 65 45
+       | 00 02      
+  0x98 | 03 65 6e 76 | import [table 0] Import { module: "env", name: "__indirect_function_table", ty: Table(TableType { element_type: funcref, table64: false, initial: 4, maximum: None }) }
+       | 19 5f 5f 69
+       | 6e 64 69 72
+       | 65 63 74 5f
+       | 66 75 6e 63
+       | 74 69 6f 6e
+       | 5f 74 61 62
+       | 6c 65 01 70
+       | 00 04      
+  0xba | 03 0b       | func section
+  0xbc | 0a          | 10 count
+  0xbd | 00          | [func 1] type 0
+  0xbe | 00          | [func 2] type 0
+  0xbf | 01          | [func 3] type 1
+  0xc0 | 03          | [func 4] type 3
+  0xc1 | 04          | [func 5] type 4
+  0xc2 | 03          | [func 6] type 3
+  0xc3 | 03          | [func 7] type 3
+  0xc4 | 00          | [func 8] type 0
+  0xc5 | 05          | [func 9] type 5
+  0xc6 | 04          | [func 10] type 4
+  0xc7 | 09 0a       | element section
+  0xc9 | 01          | 1 count
+  0xca | 00          | element table[None]
+  0xcb | 41 01       | i32_const value:1
+  0xcd | 0b          | end
+  0xce | 04          | 4 items [indices]
+  0xcf | 09          | item 9
+  0xd0 | 08          | item 8
+  0xd1 | 06          | item 6
+  0xd2 | 04          | item 4
+  0xd3 | 0a af 03    | code section
+  0xd6 | 0a          | 10 count
+============== func 1 ====================
+  0xd7 | 07          | size of function
+  0xd8 | 00          | 0 local blocks
+  0xd9 | 20 00       | local_get local_index:0
+  0xdb | 10 02       | call function_index:2
+  0xdd | 0f          | return
+  0xde | 0b          | end
+============== func 2 ====================
+  0xdf | 2c          | size of function
+  0xe0 | 01          | 1 local blocks
+  0xe1 | 05 7f       | 5 locals of type I32
+  0xe3 | 23 00       | global_get global_index:0
+  0xe5 | 21 01       | local_set local_index:1
+  0xe7 | 41 10       | i32_const value:16
+  0xe9 | 21 02       | local_set local_index:2
+  0xeb | 20 01       | local_get local_index:1
+  0xed | 20 02       | local_get local_index:2
+  0xef | 6b          | i32_sub
+  0xf0 | 21 03       | local_set local_index:3
+  0xf2 | 20 03       | local_get local_index:3
+  0xf4 | 24 00       | global_set global_index:0
+  0xf6 | 20 00       | local_get local_index:0
+  0xf8 | 11 05 00    | call_indirect type_index:5 table_index:0 table_byte:0
+  0xfb | 41 10       | i32_const value:16
+  0xfd | 21 04       | local_set local_index:4
+  0xff | 20 03       | local_get local_index:3
+ 0x101 | 20 04       | local_get local_index:4
+ 0x103 | 6a          | i32_add
+ 0x104 | 21 05       | local_set local_index:5
+ 0x106 | 20 05       | local_get local_index:5
+ 0x108 | 24 00       | global_set global_index:0
+ 0x10a | 0f          | return
+ 0x10b | 0b          | end
+============== func 3 ====================
+ 0x10c | 5f          | size of function
+ 0x10d | 01          | 1 local blocks
+ 0x10e | 0b 7f       | 11 locals of type I32
+ 0x110 | 23 00       | global_get global_index:0
+ 0x112 | 21 04       | local_set local_index:4
+ 0x114 | 41 10       | i32_const value:16
+ 0x116 | 21 05       | local_set local_index:5
+ 0x118 | 20 04       | local_get local_index:4
+ 0x11a | 20 05       | local_get local_index:5
+ 0x11c | 6b          | i32_sub
+ 0x11d | 21 06       | local_set local_index:6
+ 0x11f | 20 06       | local_get local_index:6
+ 0x121 | 24 00       | global_set global_index:0
+ 0x123 | 20 06       | local_get local_index:6
+ 0x125 | 20 00       | local_get local_index:0
+ 0x127 | 36 02 0c    | i32_store memarg:MemArg { align: 2, max_align: 2, offset: 12, memory: 0 }
+ 0x12a | 41 0c       | i32_const value:12
+ 0x12c | 21 07       | local_set local_index:7
+ 0x12e | 20 06       | local_get local_index:6
+ 0x130 | 20 07       | local_get local_index:7
+ 0x132 | 6a          | i32_add
+ 0x133 | 21 08       | local_set local_index:8
+ 0x135 | 20 08       | local_get local_index:8
+ 0x137 | 21 09       | local_set local_index:9
+ 0x139 | 41 00       | i32_const value:0
+ 0x13b | 21 0a       | local_set local_index:10
+ 0x13d | 20 09       | local_get local_index:9
+ 0x13f | 20 0a       | local_get local_index:10
+ 0x141 | 20 01       | local_get local_index:1
+ 0x143 | 20 02       | local_get local_index:2
+ 0x145 | 20 03       | local_get local_index:3
+ 0x147 | 10 00       | call function_index:0
+ 0x149 | 21 0b       | local_set local_index:11
+ 0x14b | 20 06       | local_get local_index:6
+ 0x14d | 20 0b       | local_get local_index:11
+ 0x14f | 36 02 08    | i32_store memarg:MemArg { align: 2, max_align: 2, offset: 8, memory: 0 }
+ 0x152 | 20 06       | local_get local_index:6
+ 0x154 | 28 02 08    | i32_load memarg:MemArg { align: 2, max_align: 2, offset: 8, memory: 0 }
+ 0x157 | 21 0c       | local_set local_index:12
+ 0x159 | 41 10       | i32_const value:16
+ 0x15b | 21 0d       | local_set local_index:13
+ 0x15d | 20 06       | local_get local_index:6
+ 0x15f | 20 0d       | local_get local_index:13
+ 0x161 | 6a          | i32_add
+ 0x162 | 21 0e       | local_set local_index:14
+ 0x164 | 20 0e       | local_get local_index:14
+ 0x166 | 24 00       | global_set global_index:0
+ 0x168 | 20 0c       | local_get local_index:12
+ 0x16a | 0f          | return
+ 0x16b | 0b          | end
+============== func 4 ====================
+ 0x16c | 5c          | size of function
+ 0x16d | 01          | 1 local blocks
+ 0x16e | 0c 7f       | 12 locals of type I32
+ 0x170 | 23 00       | global_get global_index:0
+ 0x172 | 21 01       | local_set local_index:1
+ 0x174 | 41 10       | i32_const value:16
+ 0x176 | 21 02       | local_set local_index:2
+ 0x178 | 20 01       | local_get local_index:1
+ 0x17a | 20 02       | local_get local_index:2
+ 0x17c | 6b          | i32_sub
+ 0x17d | 21 03       | local_set local_index:3
+ 0x17f | 20 03       | local_get local_index:3
+ 0x181 | 24 00       | global_set global_index:0
+ 0x183 | 20 00       | local_get local_index:0
+ 0x185 | 28 02 00    | i32_load memarg:MemArg { align: 2, max_align: 2, offset: 0, memory: 0 }
+ 0x188 | 21 04       | local_set local_index:4
+ 0x18a | 20 04       | local_get local_index:4
+ 0x18c | 10 01       | call function_index:1
+ 0x18e | 10 05       | call function_index:5
+ 0x190 | 21 05       | local_set local_index:5
+ 0x192 | 41 01       | i32_const value:1
+ 0x194 | 21 06       | local_set local_index:6
+ 0x196 | 20 05       | local_get local_index:5
+ 0x198 | 20 06       | local_get local_index:6
+ 0x19a | 71          | i32_and
+ 0x19b | 21 07       | local_set local_index:7
+ 0x19d | 20 03       | local_get local_index:3
+ 0x19f | 20 07       | local_get local_index:7
+ 0x1a1 | 3a 00 0f    | i32_store8 memarg:MemArg { align: 0, max_align: 0, offset: 15, memory: 0 }
+ 0x1a4 | 20 03       | local_get local_index:3
+ 0x1a6 | 2d 00 0f    | i32_load8_u memarg:MemArg { align: 0, max_align: 0, offset: 15, memory: 0 }
+ 0x1a9 | 21 08       | local_set local_index:8
+ 0x1ab | 41 01       | i32_const value:1
+ 0x1ad | 21 09       | local_set local_index:9
+ 0x1af | 20 08       | local_get local_index:8
+ 0x1b1 | 20 09       | local_get local_index:9
+ 0x1b3 | 71          | i32_and
+ 0x1b4 | 21 0a       | local_set local_index:10
+ 0x1b6 | 41 10       | i32_const value:16
+ 0x1b8 | 21 0b       | local_set local_index:11
+ 0x1ba | 20 03       | local_get local_index:3
+ 0x1bc | 20 0b       | local_get local_index:11
+ 0x1be | 6a          | i32_add
+ 0x1bf | 21 0c       | local_set local_index:12
+ 0x1c1 | 20 0c       | local_get local_index:12
+ 0x1c3 | 24 00       | global_set global_index:0
+ 0x1c5 | 20 0a       | local_get local_index:10
+ 0x1c7 | 0f          | return
+ 0x1c8 | 0b          | end
+============== func 5 ====================
+ 0x1c9 | 16          | size of function
+ 0x1ca | 01          | 1 local blocks
+ 0x1cb | 03 7f       | 3 locals of type I32
+ 0x1cd | 41 00       | i32_const value:0
+ 0x1cf | 21 00       | local_set local_index:0
+ 0x1d1 | 41 01       | i32_const value:1
+ 0x1d3 | 21 01       | local_set local_index:1
+ 0x1d5 | 20 00       | local_get local_index:0
+ 0x1d7 | 20 01       | local_get local_index:1
+ 0x1d9 | 71          | i32_and
+ 0x1da | 21 02       | local_set local_index:2
+ 0x1dc | 20 02       | local_get local_index:2
+ 0x1de | 0f          | return
+ 0x1df | 0b          | end
+============== func 6 ====================
+ 0x1e0 | 36          | size of function
+ 0x1e1 | 01          | 1 local blocks
+ 0x1e2 | 07 7f       | 7 locals of type I32
+ 0x1e4 | 23 00       | global_get global_index:0
+ 0x1e6 | 21 01       | local_set local_index:1
+ 0x1e8 | 41 10       | i32_const value:16
+ 0x1ea | 21 02       | local_set local_index:2
+ 0x1ec | 20 01       | local_get local_index:1
+ 0x1ee | 20 02       | local_get local_index:2
+ 0x1f0 | 6b          | i32_sub
+ 0x1f1 | 21 03       | local_set local_index:3
+ 0x1f3 | 20 03       | local_get local_index:3
+ 0x1f5 | 24 00       | global_set global_index:0
+ 0x1f7 | 20 00       | local_get local_index:0
+ 0x1f9 | 28 02 00    | i32_load memarg:MemArg { align: 2, max_align: 2, offset: 0, memory: 0 }
+ 0x1fc | 21 04       | local_set local_index:4
+ 0x1fe | 20 04       | local_get local_index:4
+ 0x200 | 10 07       | call function_index:7
+ 0x202 | 21 05       | local_set local_index:5
+ 0x204 | 41 10       | i32_const value:16
+ 0x206 | 21 06       | local_set local_index:6
+ 0x208 | 20 03       | local_get local_index:3
+ 0x20a | 20 06       | local_get local_index:6
+ 0x20c | 6a          | i32_add
+ 0x20d | 21 07       | local_set local_index:7
+ 0x20f | 20 07       | local_get local_index:7
+ 0x211 | 24 00       | global_set global_index:0
+ 0x213 | 20 05       | local_get local_index:5
+ 0x215 | 0f          | return
+ 0x216 | 0b          | end
+============== func 7 ====================
+ 0x217 | 45          | size of function
+ 0x218 | 01          | 1 local blocks
+ 0x219 | 09 7f       | 9 locals of type I32
+ 0x21b | 23 00       | global_get global_index:0
+ 0x21d | 21 01       | local_set local_index:1
+ 0x21f | 41 10       | i32_const value:16
+ 0x221 | 21 02       | local_set local_index:2
+ 0x223 | 20 01       | local_get local_index:1
+ 0x225 | 20 02       | local_get local_index:2
+ 0x227 | 6b          | i32_sub
+ 0x228 | 21 03       | local_set local_index:3
+ 0x22a | 20 03       | local_get local_index:3
+ 0x22c | 24 00       | global_set global_index:0
+ 0x22e | 20 03       | local_get local_index:3
+ 0x230 | 20 00       | local_get local_index:0
+ 0x232 | 36 02 08    | i32_store memarg:MemArg { align: 2, max_align: 2, offset: 8, memory: 0 }
+ 0x235 | 41 08       | i32_const value:8
+ 0x237 | 21 04       | local_set local_index:4
+ 0x239 | 20 03       | local_get local_index:3
+ 0x23b | 20 04       | local_get local_index:4
+ 0x23d | 6a          | i32_add
+ 0x23e | 21 05       | local_set local_index:5
+ 0x240 | 20 05       | local_get local_index:5
+ 0x242 | 21 06       | local_set local_index:6
+ 0x244 | 20 06       | local_get local_index:6
+ 0x246 | 10 04       | call function_index:4
+ 0x248 | 21 07       | local_set local_index:7
+ 0x24a | 41 10       | i32_const value:16
+ 0x24c | 21 08       | local_set local_index:8
+ 0x24e | 20 03       | local_get local_index:3
+ 0x250 | 20 08       | local_get local_index:8
+ 0x252 | 6a          | i32_add
+ 0x253 | 21 09       | local_set local_index:9
+ 0x255 | 20 09       | local_get local_index:9
+ 0x257 | 24 00       | global_set global_index:0
+ 0x259 | 20 07       | local_get local_index:7
+ 0x25b | 0f          | return
+ 0x25c | 0b          | end
+============== func 8 ====================
+ 0x25d | 03          | size of function
+ 0x25e | 00          | 0 local blocks
+ 0x25f | 0f          | return
+ 0x260 | 0b          | end
+============== func 9 ====================
+ 0x261 | 03          | size of function
+ 0x262 | 00          | 0 local blocks
+ 0x263 | 0f          | return
+ 0x264 | 0b          | end
+============== func 10 ====================
+ 0x265 | 1f          | size of function
+ 0x266 | 01          | 1 local blocks
+ 0x267 | 04 7f       | 4 locals of type I32
+ 0x269 | 41 01       | i32_const value:1
+ 0x26b | 21 00       | local_set local_index:0
+ 0x26d | 41 00       | i32_const value:0
+ 0x26f | 21 01       | local_set local_index:1
+ 0x271 | 41 00       | i32_const value:0
+ 0x273 | 21 02       | local_set local_index:2
+ 0x275 | 20 00       | local_get local_index:0
+ 0x277 | 20 01       | local_get local_index:1
+ 0x279 | 20 01       | local_get local_index:1
+ 0x27b | 20 02       | local_get local_index:2
+ 0x27d | 10 03       | call function_index:3
+ 0x27f | 21 03       | local_set local_index:3
+ 0x281 | 20 03       | local_get local_index:3
+ 0x283 | 0f          | return
+ 0x284 | 0b          | end
+ 0x285 | 0b 1e       | data section
+ 0x287 | 01          | 1 count
+ 0x288 | 00          | data memory[0]
+ 0x289 | 41 00       | i32_const value:0
+ 0x28b | 0b          | end
+ 0x28c |-------------| ... 24 bytes of data
+ 0x2a5 | 00 f7 05    | custom section
+ 0x2a8 | 07 6c 69 6e | name: "linking"
+       | 6b 69 6e 67
+ 0x2b0 | 02          | linking version 2
+ 0x2b1 | 08 c9 85 80 | symbol table section
+       | 80 00      
+ 0x2b7 | 0d          | 13 count
+ 0x2b8 | 00 02 01 4f | Func { flags: SymbolFlags(BINDING_LOCAL), index: 1, name: Some("_ZN3std10sys_common9backtrace28__rust_begin_short_backtrace17hf1ecc3b2b910465aE") }
+       | 5f 5a 4e 33
+       | 73 74 64 31
+       | 30 73 79 73
+       | 5f 63 6f 6d
+       | 6d 6f 6e 39
+       | 62 61 63 6b
+       | 74 72 61 63
+       | 65 32 38 5f
+       | 5f 72 75 73
+       | 74 5f 62 65
+       | 67 69 6e 5f
+       | 73 68 6f 72
+       | 74 5f 62 61
+       | 63 6b 74 72
+       | 61 63 65 31
+       | 37 68 66 31
+       | 65 63 63 33
+       | 62 32 62 39
+       | 31 30 34 36
+       | 35 61 45   
+ 0x30b | 00 02 02 3a | Func { flags: SymbolFlags(BINDING_LOCAL), index: 2, name: Some("_ZN4core3ops8function6FnOnce9call_once17h06fc313c2ecf3d24E") }
+       | 5f 5a 4e 34
+       | 63 6f 72 65
+       | 33 6f 70 73
+       | 38 66 75 6e
+       | 63 74 69 6f
+       | 6e 36 46 6e
+       | 4f 6e 63 65
+       | 39 63 61 6c
+       | 6c 5f 6f 6e
+       | 63 65 31 37
+       | 68 30 36 66
+       | 63 33 31 33
+       | 63 32 65 63
+       | 66 33 64 32
+       | 34 45      
+ 0x349 | 00 04 03 2a | Func { flags: SymbolFlags(VISIBILITY_HIDDEN), index: 3, name: Some("_ZN3std2rt10lang_start17h385f18abde4350daE") }
+       | 5f 5a 4e 33
+       | 73 74 64 32
+       | 72 74 31 30
+       | 6c 61 6e 67
+       | 5f 73 74 61
+       | 72 74 31 37
+       | 68 33 38 35
+       | 66 31 38 61
+       | 62 64 65 34
+       | 33 35 30 64
+       | 61 45      
+ 0x377 | 02 10 00    | Global { flags: SymbolFlags(UNDEFINED), index: 0, name: None }
+ 0x37a | 01 02 0d 2e | Data { flags: SymbolFlags(BINDING_LOCAL), name: ".L__unnamed_1", symbol: Some(DefinedDataSymbol { index: 0, offset: 0, size: 24 }) }
+       | 4c 5f 5f 75
+       | 6e 6e 61 6d
+       | 65 64 5f 31
+       | 00 00 18   
+ 0x38d | 00 10 00    | Func { flags: SymbolFlags(UNDEFINED), index: 0, name: None }
+ 0x390 | 00 02 04 48 | Func { flags: SymbolFlags(BINDING_LOCAL), index: 4, name: Some("_ZN3std2rt10lang_start28_$u7b$$u7b$closure$u7d$$u7d$17h24681ec0ade90745E") }
+       | 5f 5a 4e 33
+       | 73 74 64 32
+       | 72 74 31 30
+       | 6c 61 6e 67
+       | 5f 73 74 61
+       | 72 74 32 38
+       | 5f 24 75 37
+       | 62 24 24 75
+       | 37 62 24 63
+       | 6c 6f 73 75
+       | 72 65 24 75
+       | 37 64 24 24
+       | 75 37 64 24
+       | 31 37 68 32
+       | 34 36 38 31
+       | 65 63 30 61
+       | 64 65 39 30
+       | 37 34 35 45
+ 0x3dc | 00 02 05 56 | Func { flags: SymbolFlags(BINDING_LOCAL), index: 5, name: Some("_ZN54_$LT$$LP$$RP$$u20$as$u20$std..process..Termination$GT$6report17haa5bf2f79896d941E") }
+       | 5f 5a 4e 35
+       | 34 5f 24 4c
+       | 54 24 24 4c
+       | 50 24 24 52
+       | 50 24 24 75
+       | 32 30 24 61
+       | 73 24 75 32
+       | 30 24 73 74
+       | 64 2e 2e 70
+       | 72 6f 63 65
+       | 73 73 2e 2e
+       | 54 65 72 6d
+       | 69 6e 61 74
+       | 69 6f 6e 24
+       | 47 54 24 36
+       | 72 65 70 6f
+       | 72 74 31 37
+       | 68 61 61 35
+       | 62 66 32 66
+       | 37 39 38 39
+       | 36 64 39 34
+       | 31 45      
+ 0x436 | 00 02 06 5a | Func { flags: SymbolFlags(BINDING_LOCAL), index: 6, name: Some("_ZN4core3ops8function6FnOnce40call_once$u7b$$u7b$vtable.shim$u7d$$u7d$17hc804a9e31dd3115cE") }
+       | 5f 5a 4e 34
+       | 63 6f 72 65
+       | 33 6f 70 73
+       | 38 66 75 6e
+       | 63 74 69 6f
+       | 6e 36 46 6e
+       | 4f 6e 63 65
+       | 34 30 63 61
+       | 6c 6c 5f 6f
+       | 6e 63 65 24
+       | 75 37 62 24
+       | 24 75 37 62
+       | 24 76 74 61
+       | 62 6c 65 2e
+       | 73 68 69 6d
+       | 24 75 37 64
+       | 24 24 75 37
+       | 64 24 31 37
+       | 68 63 38 30
+       | 34 61 39 65
+       | 33 31 64 64
+       | 33 31 31 35
+       | 63 45      
+ 0x494 | 00 02 07 3a | Func { flags: SymbolFlags(BINDING_LOCAL), index: 7, name: Some("_ZN4core3ops8function6FnOnce9call_once17h20fe7bf2b7692f9bE") }
+       | 5f 5a 4e 34
+       | 63 6f 72 65
+       | 33 6f 70 73
+       | 38 66 75 6e
+       | 63 74 69 6f
+       | 6e 36 46 6e
+       | 4f 6e 63 65
+       | 39 63 61 6c
+       | 6c 5f 6f 6e
+       | 63 65 31 37
+       | 68 32 30 66
+       | 65 37 62 66
+       | 32 62 37 36
+       | 39 32 66 39
+       | 62 45      
+ 0x4d2 | 00 02 08 77 | Func { flags: SymbolFlags(BINDING_LOCAL), index: 8, name: Some("_ZN4core3ptr85drop_in_place$LT$std..rt..lang_start$LT$$LP$$RP$$GT$..$u7b$$u7b$closure$u7d$$u7d$$GT$17h26d303f198cad6c2E") }
+       | 5f 5a 4e 34
+       | 63 6f 72 65
+       | 33 70 74 72
+       | 38 35 64 72
+       | 6f 70 5f 69
+       | 6e 5f 70 6c
+       | 61 63 65 24
+       | 4c 54 24 73
+       | 74 64 2e 2e
+       | 72 74 2e 2e
+       | 6c 61 6e 67
+       | 5f 73 74 61
+       | 72 74 24 4c
+       | 54 24 24 4c
+       | 50 24 24 52
+       | 50 24 24 47
+       | 54 24 2e 2e
+       | 24 75 37 62
+       | 24 24 75 37
+       | 62 24 63 6c
+       | 6f 73 75 72
+       | 65 24 75 37
+       | 64 24 24 75
+       | 37 64 24 24
+       | 47 54 24 31
+       | 37 68 32 36
+       | 64 33 30 33
+       | 66 31 39 38
+       | 63 61 64 36
+       | 63 32 45   
+ 0x54d | 00 02 09 20 | Func { flags: SymbolFlags(BINDING_LOCAL), index: 9, name: Some("_ZN3foo4main17h694cf023eeafda77E") }
+       | 5f 5a 4e 33
+       | 66 6f 6f 34
+       | 6d 61 69 6e
+       | 31 37 68 36
+       | 39 34 63 66
+       | 30 32 33 65
+       | 65 61 66 64
+       | 61 37 37 45
+ 0x571 | 00 04 0a 0b | Func { flags: SymbolFlags(VISIBILITY_HIDDEN), index: 10, name: Some("__main_void") }
+       | 5f 5f 6d 61
+       | 69 6e 5f 76
+       | 6f 69 64   
+ 0x580 | 05 99 80 80 | segment info section
+       | 80 00      
+ 0x586 | 01          | 1 count
+ 0x587 | 15 2e 72 6f | Segment { name: ".rodata..L__unnamed_1", alignment: 2, flags: SegmentFlags(0x0) }
+       | 64 61 74 61
+       | 2e 2e 4c 5f
+       | 5f 75 6e 6e
+       | 61 6d 65 64
+       | 5f 31 02 00
+ 0x59f | 00 6b       | custom section
+ 0x5a1 | 0a 72 65 6c | name: "reloc.CODE"
+       | 6f 63 2e 43
+       | 4f 44 45   
+ 0x5ac | 05          | section 5
+ 0x5ad | 19          | 25 count
+ 0x5ae | 00 06 01    | RelocationEntry { ty: FunctionIndexLeb, offset: 6, index: 1, addend: 0 }
+ 0x5b1 | 07 12 03    | RelocationEntry { ty: GlobalIndexLeb, offset: 18, index: 3, addend: 0 }
+ 0x5b4 | 07 27 03    | RelocationEntry { ty: GlobalIndexLeb, offset: 39, index: 3, addend: 0 }
+ 0x5b7 | 06 2f 05    | RelocationEntry { ty: TypeIndexLeb, offset: 47, index: 5, addend: 0 }
+ 0x5ba | 07 43 03    | RelocationEntry { ty: GlobalIndexLeb, offset: 67, index: 3, addend: 0 }
+ 0x5bd | 07 4f 03    | RelocationEntry { ty: GlobalIndexLeb, offset: 79, index: 3, addend: 0 }
+ 0x5c0 | 07 64 03    | RelocationEntry { ty: GlobalIndexLeb, offset: 100, index: 3, addend: 0 }
+ 0x5c3 | 04 80 01 04 | RelocationEntry { ty: MemoryAddrSleb, offset: 128, index: 4, addend: 0 }
+       | 00         
+ 0x5c8 | 00 92 01 05 | RelocationEntry { ty: FunctionIndexLeb, offset: 146, index: 5, addend: 0 }
+ 0x5cc | 07 b5 01 03 | RelocationEntry { ty: GlobalIndexLeb, offset: 181, index: 3, addend: 0 }
+ 0x5d0 | 07 c3 01 03 | RelocationEntry { ty: GlobalIndexLeb, offset: 195, index: 3, addend: 0 }
+ 0x5d4 | 07 d8 01 03 | RelocationEntry { ty: GlobalIndexLeb, offset: 216, index: 3, addend: 0 }
+ 0x5d8 | 00 e7 01 00 | RelocationEntry { ty: FunctionIndexLeb, offset: 231, index: 0, addend: 0 }
+ 0x5dc | 00 ed 01 07 | RelocationEntry { ty: FunctionIndexLeb, offset: 237, index: 7, addend: 0 }
+ 0x5e0 | 07 a6 02 03 | RelocationEntry { ty: GlobalIndexLeb, offset: 294, index: 3, addend: 0 }
+ 0x5e4 | 07 cb 02 03 | RelocationEntry { ty: GlobalIndexLeb, offset: 331, index: 3, addend: 0 }
+ 0x5e8 | 07 e0 02 03 | RelocationEntry { ty: GlobalIndexLeb, offset: 352, index: 3, addend: 0 }
+ 0x5ec | 00 ef 02 09 | RelocationEntry { ty: FunctionIndexLeb, offset: 367, index: 9, addend: 0 }
+ 0x5f0 | 07 84 03 03 | RelocationEntry { ty: GlobalIndexLeb, offset: 388, index: 3, addend: 0 }
+ 0x5f4 | 07 92 03 03 | RelocationEntry { ty: GlobalIndexLeb, offset: 402, index: 3, addend: 0 }
+ 0x5f8 | 07 a7 03 03 | RelocationEntry { ty: GlobalIndexLeb, offset: 423, index: 3, addend: 0 }
+ 0x5fc | 00 c5 03 06 | RelocationEntry { ty: FunctionIndexLeb, offset: 453, index: 6, addend: 0 }
+ 0x600 | 07 da 03 03 | RelocationEntry { ty: GlobalIndexLeb, offset: 474, index: 3, addend: 0 }
+ 0x604 | 01 f0 03 0b | RelocationEntry { ty: TableIndexSleb, offset: 496, index: 11, addend: 0 }
+ 0x608 | 00 88 04 02 | RelocationEntry { ty: FunctionIndexLeb, offset: 520, index: 2, addend: 0 }
+ 0x60c | 00 19       | custom section
+ 0x60e | 0a 72 65 6c | name: "reloc.DATA"
+       | 6f 63 2e 44
+       | 41 54 41   
+ 0x619 | 06          | section 6
+ 0x61a | 04          | 4 count
+ 0x61b | 02 06 0a    | RelocationEntry { ty: TableIndexI32, offset: 6, index: 10, addend: 0 }
+ 0x61e | 02 12 08    | RelocationEntry { ty: TableIndexI32, offset: 18, index: 8, addend: 0 }
+ 0x621 | 02 16 06    | RelocationEntry { ty: TableIndexI32, offset: 22, index: 6, addend: 0 }
+ 0x624 | 02 1a 06    | RelocationEntry { ty: TableIndexI32, offset: 26, index: 6, addend: 0 }
+ 0x627 | 00 2c       | custom section
+ 0x629 | 0f 74 61 72 | name: "target_features"
+       | 67 65 74 5f
+       | 66 65 61 74
+       | 75 72 65 73
+ 0x639 |-------------| ... 28 bytes of data
+ 0x655 | 00 3d       | custom section
+ 0x657 | 09 70 72 6f | name: "producers"
+       | 64 75 63 65
+       | 72 73      
+ 0x661 | 01          | 1 count
+ 0x662 | 0c 70 72 6f | field: processed-by
+       | 63 65 73 73
+       | 65 64 2d 62
+       | 79         
+ 0x66f | 01          | 1 count
+ 0x670 | 05 72 75 73 | ProducersFieldValue { name: "rustc", version: "1.78.0 (9b00956e5 2024-04-29)" }
+       | 74 63 1d 31
+       | 2e 37 38 2e
+       | 30 20 28 39
+       | 62 30 30 39
+       | 35 36 65 35
+       | 20 32 30 32
+       | 34 2d 30 34
+       | 2d 32 39 29