emscripten icon indicating copy to clipboard operation
emscripten copied to clipboard

`EMSCRIPTEN_KEEPALIVE` doesn't work for static functions

Open hly2019 opened this issue 11 months ago • 1 comments

Please include the following in your bug report:

Version of emscripten/emsdk: 3.1.74

Hi, I just met some problems when using the EMSCRIPTEN_KEEPALIVE flag with static function definitions. It seems that for static functions, adding EMSCRIPTEN_KEEPALIVE couldn't guarantee that the code would be preserved, as shown in the following example.

Thank you very much for your consideration!

Source code (test_keepalive.cpp):

#include <emscripten.h>
EMSCRIPTEN_KEEPALIVE static int test_keepalive() {
    return 1 + 1;
}

Compilation command:

$ emcc test_keepalive.cpp -o test.wasm -g --no-entry
$ wasm2wat test.wasm -o test.wat

Result:

(module $test.wasm
  (type (;0;) (func (result i32)))
  (type (;1;) (func))
  (type (;2;) (func (param i32)))
  (func $__wasm_call_ctors (type 1)
    call $emscripten_stack_init)
  (func $_initialize (type 1)
    block  ;; label = @1
      i32.const 1
      i32.eqz
      br_if 0 (;@1;)
      call $__wasm_call_ctors
    end)
  (func $emscripten_stack_init (type 1)
    i32.const 65536
    global.set $__stack_base
    i32.const 0
    i32.const 15
    i32.add
    i32.const -16
    i32.and
    global.set $__stack_end)
  (func $emscripten_stack_get_free (type 0) (result i32)
    global.get $__stack_pointer
    global.get $__stack_end
    i32.sub)
  (func $emscripten_stack_get_base (type 0) (result i32)
    global.get $__stack_base)
  (func $emscripten_stack_get_end (type 0) (result i32)
    global.get $__stack_end)
  (func $_emscripten_stack_restore (type 2) (param i32)
    local.get 0
    global.set $__stack_pointer)
  (func $emscripten_stack_get_current (type 0) (result i32)
    global.get $__stack_pointer)
  (table (;0;) 2 2 funcref)
  (memory (;0;) 257 257)
  (global $__stack_pointer (mut i32) (i32.const 65536))
  (global $__stack_end (mut i32) (i32.const 0))
  (global $__stack_base (mut i32) (i32.const 0))
  (export "memory" (memory 0))
  (export "__indirect_function_table" (table 0))
  (export "_initialize" (func $_initialize))
  (export "emscripten_stack_init" (func $emscripten_stack_init))
  (export "emscripten_stack_get_free" (func $emscripten_stack_get_free))
  (export "emscripten_stack_get_base" (func $emscripten_stack_get_base))
  (export "emscripten_stack_get_end" (func $emscripten_stack_get_end))
  (export "_emscripten_stack_restore" (func $_emscripten_stack_restore))
  (export "emscripten_stack_get_current" (func $emscripten_stack_get_current))
  (elem (;0;) (i32.const 1) func $__wasm_call_ctors))

hly2019 avatar Mar 28 '25 20:03 hly2019

Yes, this is mostly just a artifact of how EMSCRIPTEN_KEEPALIVE works. Currently its implemented as __attribute__((used)).

The core problem is that static functions are internal of the object file so don't even show up in the linker's symbol table. Marking a function as static and exporting it is kind of a contradiction so I don't think its is a huge issues, and is perhaps just something that should be documented somewhere.

sbc100 avatar Mar 31 '25 20:03 sbc100