<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Abhirup Bhattacharyya</title>
    <description>The latest articles on DEV Community by Abhirup Bhattacharyya (@quantadude).</description>
    <link>https://dev.to/quantadude</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3966818%2F875e1140-f240-4720-a384-30c97c726cca.jpeg</url>
      <title>DEV Community: Abhirup Bhattacharyya</title>
      <link>https://dev.to/quantadude</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/quantadude"/>
    <language>en</language>
    <item>
      <title>The Two Things That Bit Me In Emscripten</title>
      <dc:creator>Abhirup Bhattacharyya</dc:creator>
      <pubDate>Wed, 03 Jun 2026 18:46:39 +0000</pubDate>
      <link>https://dev.to/quantadude/the-two-things-that-bit-me-in-emscripten-2iih</link>
      <guid>https://dev.to/quantadude/the-two-things-that-bit-me-in-emscripten-2iih</guid>
      <description>&lt;p&gt;While building a &lt;a href="https://github.com/QuantaDude/algo-visualizer/tree/master" rel="noopener noreferrer"&gt;web application&lt;/a&gt; using React, TypeScript, C++, Emscripten, and Raylib, I ran into two linker-related issues that took far longer to diagnose than they should have.&lt;/p&gt;

&lt;p&gt;This is a short article on two problems that I have faced, I am sharing this so that developers who are exploring Web Assembly using Emscripten can easily avoid these issues as I'll also cover the workarounds that solved them for me.&lt;/p&gt;

&lt;p&gt;I'll start with the major one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Link-Time Optimization and EM_JS
&lt;/h2&gt;

&lt;p&gt;The problem appears when Link Time Optimization &lt;code&gt;(-flto)&lt;/code&gt; is enabled and an &lt;code&gt;EM_JS(ret, name, params, ...)&lt;/code&gt; macro function is invoked in one translation unit but called from another.&lt;/p&gt;

&lt;p&gt;You'll find that the linker complains that the function symbol you're trying to call is undefined.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;EM_JS&lt;/code&gt; macro defines a C interface to call the JS function. So if you have your EM_JS macros in a &lt;code&gt;js_layer.cpp&lt;/code&gt; source file, then you need to wrap the macro invocation as&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;extern&lt;/span&gt; &lt;span class="s"&gt;"C"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="n"&gt;EM_JS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;//your JS code;&lt;/span&gt;
          &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same applies to the function declaration in &lt;code&gt;js_layer.hpp&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;I'll briefly go through the three workarounds or 'solutions' before bringing up the last quirk.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Three Workarounds
&lt;/h2&gt;

&lt;h3&gt;
  
  
  If performance isn't of any concern
&lt;/h3&gt;

&lt;p&gt;Well the first one is obvious, just don't use &lt;code&gt;-flto&lt;/code&gt; in your release builds. The downside is that your binaries (the .data and .wasm compiler outputs in this case) will be larger.&lt;br&gt;
Without LTO, the linker has fewer opportunities for whole-program optimization and dead-code elimination, which can increase the size of the generated .wasm and potentially reduce performance.&lt;/p&gt;
&lt;h3&gt;
  
  
  Write a wrapper
&lt;/h3&gt;

&lt;p&gt;You can simply have a wrapper function in the same translation unit which calls the C function interfacing with the JS function, &lt;code&gt;add()&lt;/code&gt; if you take the above example. The wrapper can simply be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// In js_layer.hpp&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;add_wrapper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// In js_layer.cpp&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;add_wrapper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
    &lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can call &lt;code&gt;add_wrapper()&lt;/code&gt; from any source file just by including &lt;code&gt;js_layer.hpp&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use the macro in a header or same source
&lt;/h3&gt;

&lt;p&gt;You won't get any undefined symbols or other linking errors if you declare the JS and C functions in the same source file, or use the &lt;code&gt;EM_JS&lt;/code&gt; macro in a header file.&lt;/p&gt;

&lt;p&gt;Since EM_JS emits a function definition rather than a declaration, placing it in a header can lead to multiple-definition problems if the header is included from more than one translation unit.&lt;/p&gt;

&lt;h2&gt;
  
  
  The other quirk
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;EXPORTED_FUNCTIONS&lt;/code&gt; linker flag in emscripten allows you to export your C/C++ functions so that your JavaScript code in the browser can invoke and run your C code.&lt;br&gt;
The issue arises when you try to expose an EM_JS function through Emscripten's export mechanism so that it can be called from JavaScript after the module has loaded.&lt;/p&gt;

&lt;p&gt;The linker will simply complain that it can't find the xyz symbol even though it should exist as the &lt;code&gt;EM_JS&lt;/code&gt; macro defines a C function which you can call.&lt;/p&gt;

&lt;p&gt;I don't really know why this is the case as I don't know about Emscripten's implementation deeply enough to explain why this happens. Maybe I just need to include some flag in my &lt;code&gt;CMakeLists.txt&lt;/code&gt; but ultimately it isn't much of a problem as you can simply do the following in your C function declaration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// In js_layer.hpp&lt;/span&gt;

&lt;span class="k"&gt;extern&lt;/span&gt; &lt;span class="s"&gt;"C"&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;__attribute__&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;used&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="n"&gt;__attribute__&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;visibility&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"default"&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
&lt;span class="c1"&gt;// or simply&lt;/span&gt;
&lt;span class="k"&gt;extern&lt;/span&gt; &lt;span class="s"&gt;"C"&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;EMSCRIPTEN_KEEPALIVE&lt;/span&gt; &lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After this you can call the JS function in your JavaScript/TypeScript code after you import the file.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing Thoughts and Takeaways
&lt;/h2&gt;

&lt;p&gt;The first linker issue bothered me for quite a while. I couldn't find any resources describing this particular problem, which made it especially frustrating to diagnose. In hindsight, I probably would have discovered the cause much sooner if I had tested a debug build instead of focusing exclusively on release builds.&lt;/p&gt;

&lt;p&gt;I had no idea that enabling Link Time Optimization (&lt;code&gt;-flto&lt;/code&gt;) could affect code using the &lt;code&gt;EM_JS&lt;/code&gt; macro in this way. I knew about the wrapper workaround for quite some time but recently came to know about the root cause.&lt;/p&gt;

&lt;p&gt;If there's one takeaway from this article, it's this: test your release build configuration early. Some issues only appear when optimization flags are enabled, and linker-related problems can be particularly difficult to track down because the source of the error is often far removed from the code that triggered it.&lt;/p&gt;

&lt;p&gt;WebAssembly development has a few other sharp edges as well. Running out of stack space, memory limits, or misconfigured build flags can all lead to confusing bugs. In my experience, however, those issues are generally easier to understand and solve than linker problems like the ones described here.&lt;/p&gt;

&lt;p&gt;Hopefully this saves someone else a few hours of debugging.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>webassembly</category>
      <category>cpp</category>
      <category>c</category>
    </item>
  </channel>
</rss>
