Stream: wasmtime

Topic: Size of Precompiled Artifacts


view this post on Zulip Antoine Lavandier (Oct 28 2025 at 14:28):

Hi,
Is there somewhere that documents what goes into the precompiled artifacts made by Engine::precompile_module. The difference in sizes between before and after precompilation can be pretty big, some examples I have go from 35KiB to 100 KiB and I'm wondering if there are ways to reduce this gap.
For reference here's the config I'm using for precompilation

    let mut config = Config::new();
    // Tell how the memory is expected to behave when instantiated
    config.memory_reservation(0);
    config.wasm_custom_page_sizes(true);
    config.memory_may_move(false);
    config.memory_init_cow(false);

    // Optimize for Code size in the .cwasm
    config.cranelift_opt_level(wasmtime::OptLevel::SpeedAndSize);
    config.target("pulley32")?;

view this post on Zulip Alex Crichton (Oct 29 2025 at 08:21):

One other big one is likely disabling address map support

view this post on Zulip Alex Crichton (Oct 29 2025 at 08:22):

https://github.com/google/wasefire/issues/458#issuecomment-3189916340 is some dissection of the current size if you're curious for a similar/related project where binary sizes were important

Now that Wasmtime has no-std support, it becomes a possible alternative for the platform WASM runtime. This task should track the feasibility of using Wasmtime, since many roadblocks are expected (...

view this post on Zulip Alex Crichton (Oct 29 2025 at 08:22):

there's also https://github.com/bytecodealliance/wasmtime/issues/10286 which is an open issue for us

In #10285 it was shown that sections such as .symtab, .strtab, and .shstrtab account for a non-trivial portion of the size of an object file: $ objdump --section-headers target/wasm32-wasip1/releas...

view this post on Zulip Chris Fallin (Oct 29 2025 at 08:27):

FWIW, you should be able to see a breakdown of section sizes with a tool like bloaty; e.g. here's a run on a test.cwasm compiled from a test.wat with one tiny function:

% bloaty test.cwasm
    FILE SIZE        VM SIZE
 --------------  --------------
  62.6%  30.8Ki   0.0%       0    [Unmapped]
  32.6%  16.1Ki  93.0%  16.0Ki    .text
   2.0%    1016   5.4%     952    .wasmtime.engine
   0.5%     228   0.9%     164    .eh_frame
   0.4%     221   0.0%       0    .shstrtab
   0.4%     203   0.4%      75    .wasmtime.info
   0.3%     163   0.0%       0    .strtab
   0.3%     160   0.0%       0    .symtab
   0.3%     128   0.0%       0    [ELF Headers]
   0.2%      92   0.2%      28    .wasmtime.addrmap
   0.1%      72   0.0%       8    .wasmtime.exceptions
   0.1%      68   0.0%       4    .wasmtime.traps
   0.1%      65   0.0%       1    .wasmtime.bti
 100.0%  49.2Ki 100.0%  17.2Ki    TOTAL

view this post on Zulip Antoine Lavandier (Nov 20 2025 at 13:47):

I have looked deeper into optimizing this stuff. This is the configuration I ended up with

    config.memory_init_cow(false);
    config.generate_address_map(false);
    config.table_lazy_init(false);
    config.cranelift_opt_level(OptLevel::Speed);
    config.memory_may_move(false);
    config.memory_reservation(0);
    config.wasm_custom_page_sizes(true);

compared to an "unoptimized" precompilation this reduced the elf size by about 5, most of coming from generate_adress_map(false). Now I have ELFs that have the following sections :

llvm-readelf -S testing.cwasm
There are 11 section headers, starting at offset 0x2f28:

Section Headers:
  [Nr] Name              Type            Address          Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            0000000000000000 000000 000000 00      0   0  0
  [ 1] .wasmtime.engine  PROGBITS        0000000000000000 000040 000315 00   A  0   0  1
  [ 2] .wasmtime.bti     PROGBITS        0000000000000000 000355 000001 00   A  0   0  1
  [ 3] .text             PROGBITS        0000000000000000 000356 001d93 00   W  0   0  1
  [ 4] .wasmtime.traps   PROGBITS        0000000000000000 0020e9 000027 00   A  0   0  1
  [ 5] .wasmtime.exceptions PROGBITS     0000000000000000 002110 0000e0 00   A  0   0  1
  [ 6] .rodata.wasm      PROGBITS        0000000000000000 0021f0 000034 00   A  0   0  1
  [ 7] .wasmtime.info    PROGBITS        0000000000000000 002224 000596 00   A  0   0  1
  [ 8] .symtab           SYMTAB          0000000000000000 0027c0 000300 18      9  32  8
  [ 9] .strtab           STRTAB          0000000000000000 002ac0 0003e1 00      0   0  1
  [10] .shstrtab         STRTAB          0000000000000000 002ea1 000081 00      0   0  1

For some reasons, llvm-size -A and objdump --section-headers don't show the last 3 sections in their output.
From what I understand from this issue .symtab, .strtab and .shstrtab can be stripped and everything should work. What about the .wasmtime sections, especially traps and exceptions and info ? Can they be removed ?

In #10285 it was shown that sections such as .symtab, .strtab, and .shstrtab account for a non-trivial portion of the size of an object file: $ objdump --section-headers target/wasm32-wasip1/releas...

view this post on Zulip Antoine Lavandier (Nov 20 2025 at 14:03):

For a sense of scale, in this example, the sum of all 6 sections, accounts for 3853( 28.4 %) out of 12776 bytes (total measured with wc -c). In another bigger example I have lying around it accounts for 7339(19.6 %) out of 37448 bytes. So it's impact is decreasing but even constant gains in code size are desirable in the constrained environments I'm working in

view this post on Zulip bjorn3 (Nov 20 2025 at 15:06):

The traps section is necessary for wasmtime to know which locations are expected to trap and thus can be handled by returning a wasm trap to the host code rather than aborting the entire process. The exceptions section is necessary for wasm exceptions to function. The info section contains things like function types and the names and locations of all exports and imports, which is essential information for getting to run any wasm code at all.

view this post on Zulip bjorn3 (Nov 20 2025 at 15:07):

.shstrtab is necessary as it contains the names of all ELF sections, which are necessary to locate the sections in the first place.

view this post on Zulip Alex Crichton (Nov 20 2025 at 15:19):

it's theoretically possible to strip .wasmtime.traps, .wasmtime.exceptions, .wasmtime.engine, and .wasmtime.bti.

Otherwise, yeah, .symtab and .strtab are definitely strippable and aren't needed by Wasmtime at all.

Would you be interested in sending some PRs to help out here? For example stripping .wasmtime.exceptions shouldn't be too hard, and the next-easiest thing would probably be figuring out how to strip the sybols

view this post on Zulip Antoine Lavandier (Nov 20 2025 at 15:41):

.wamstime.engine in my examples is a flat 0x355 bytes and .wasmtime.bti is 0x1 byte regardless of the .textor .rodata.wasm section sizes so it's really not worth the removal I think. .symtab and .strtab are more interesting to remove. Regarding PRs, I'm not necessarily against it but I can't say that I'll find the time to contribute this. Also this kind of thing is not my domain of expertise. I'm fine llvm-striping the unnecessary sections for the time being. Regarding stripping the symbols, in the issue linked above, you mention [the need to request features to upstream projects like object] so I don't know how much I could do in that direction

view this post on Zulip Alex Crichton (Nov 20 2025 at 15:44):

it's true yeah I don't know how we'd strip the sections in Rust itself

view this post on Zulip Alex Crichton (Nov 20 2025 at 15:44):

in the meantime llvm-strip should work ok

view this post on Zulip Paul Osborne (Nov 20 2025 at 22:12):

It's been awhile, but I do think there would need to be updates to a few dependencies (gimli, IIRC) to be able to strip additional sections without external tooling. It would certainly be convenient to be able to do this without external tooling.


Last updated: Dec 06 2025 at 06:05 UTC