Skip to content

EMSCRIPTEN_KEEPALIVE doesn't work for static functions #24010

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
hly2019 opened this issue Mar 28, 2025 · 1 comment
Open

EMSCRIPTEN_KEEPALIVE doesn't work for static functions #24010

hly2019 opened this issue Mar 28, 2025 · 1 comment

Comments

@hly2019
Copy link

hly2019 commented Mar 28, 2025

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))
@sbc100
Copy link
Collaborator

sbc100 commented Mar 31, 2025

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants