EM_JS can't export functions in emscripten - c

If I use the emscripten interface, EM_JS the symbols don't seem to be exported or anything.
If I use the following C code:
#include <emscripten.h>
#include <stdio.h>
//Function in Javascript Land
extern void writeout( int o );
EM_JS(void, console_logger, (const char* str), {
console.log(UTF8ToString(str));
} );
void testcallback( int z )
{
char ct[100];
sprintf( ct, "Calling back: %d\n", z );
console_logger(ct);
writeout( z+10000 );
}
If I try compiling with the following line, like this, here are my issues.
/home/cnlohr/git/emsdk/upstream/emscripten/emcc -o add2.wasm add2.c -s EXPORTED_FUNCTIONS='["_add2","_testcallback","_console_logger"]' -s EXPORTED_RUNTIME_METHODS='["ccall", "cwrap"]' -s ERROR_ON_UNDEFINED_SYMBOLS=0
warning: undefined symbol: writeout (referenced by top-level compiled C/C++ code)
emcc: error: undefined exported function: "_console_logger" [-Wundefined] [-Werror]
I can try compiling without it, like this:
/home/cnlohr/git/emsdk/upstream/emscripten/emcc -o add2.wasm add2.c -s EXPORTED_FUNCTIONS='["_add2","_testcallback"]' -s EXPORTED_RUNTIME_METHODS='["ccall", "cwrap"]' -s ERROR_ON_UNDEFINED_SYMBOLS=0
warning: undefined symbol: writeout (referenced by top-level compiled C/C++ code)
cat add2.wasm | base64 | ./strencode > add2.wasm.b64
When I load it, I get
Uncaught (in promise) LinkError: import object field 'console_logger' is not a Function
How do I get emcc to actually make the function I generated using EM_JS?
Just FYI, this is the version of emcc I'm using:
emcc (Emscripten gcc/clang-like replacement + linker emulating GNU ld) 2.0.0
clang version 12.0.0 (/b/s/w/ir/cache/git/chromium.googlesource.com-external-github.com-llvm-llvm--project a3036b386383f1c1e9d32c2c8dba995087959da3)
Target: x86_64-unknown-linux-gnu
Thread model: posix
InstalledDir: /home/cnlohr/git/emsdk/upstream/bin
Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/9
Selected GCC installation: /usr/lib/gcc/x86_64-linux-gnu/9
Candidate multilib: .;#m64
Selected multilib: .;#m64
shared:INFO: (Emscripten: Running sanity checks)

So, the answer to this is that if you target a .js file, these functions (among others) are put in that separate js file. You can examine that and copy things out willy-nilly if you don't want to use the whole file.

Related

How to compile longjmp using emcc?

I'm trying to diagnose a linker issue with emcc. I've got it down to the following file:
#include <setjmp.h>
jmp_buf error;
int main(int argc, char *argv[]) {
int n = setjmp(error);
if (n)
return n;
longjmp(error, 33);
return 0;
}
When compiled with gcc, this file produces an a.out that exits with code 33 as expected. But when compiled with emcc, it fails at the linking step, with this error:
nr#homedog ~/s/c [1]> emcc -s LLD_REPORT_UNDEFINED longjmp.c
error: undefined symbol: __invoke_void_$struct___jmp_buf_tag*_i32 (referenced by top-level compiled C/C++ code)
warning: To disable errors for undefined symbols use `-s ERROR_ON_UNDEFINED_SYMBOLS=0`
warning: ___invoke_void_$struct___jmp_buf_tag*_i32 may need to be added to EXPORTED_FUNCTIONS if it arrives from a system library
error: undefined symbol: emscripten_longjmp_jmpbuf (referenced by top-level compiled C/C++ code)
warning: _emscripten_longjmp_jmpbuf may need to be added to EXPORTED_FUNCTIONS if it arrives from a system library
Adding it to EXPORTED_FUNCTIONS results instead in this error:
wasm-ld: error: symbol exported via --export not found: emscripten_longjmp_jmpbuf
What do I need to do to compile this file?
I am running Debian stable (bullseye) with Emscripten version 2.0.12.
ETA: emcc reports versions as follows:
nr#homedog ~/s/c [1]> emcc -v
emcc (Emscripten gcc/clang-like replacement + linker emulating GNU ld) 2.0.12
Debian clang version 11.0.1-2
Target: wasm32-unknown-emscripten
Thread model: posix
InstalledDir: /usr/bin
It looks like you have a mismatched llvm (clang) and emscripten versions. We recommend using emsdk to install all correct dependencies (how are you install them?). If you don't want use emsdk you will need to build llvm from source at the correct revision.
Your example works find with the latest version of emscripten:
$ emcc -v
emcc (Emscripten gcc/clang-like replacement + linker emulating GNU ld) 2.0.29-git (6e9e10d3f78eb249bf09922a23e19c3f81a86d0f)
...
$ emcc -s LLD_REPORT_UNDEFINED setjmp.c

Unable to access own custom global function of shared library in linux from other module in C

I have downloaded libgcrypt library source code and
added my own customize function inside one particular file.
Although compilation/build process of customized shared library is successful, and both nm and objdump show
the customized function is global, it nonetheless shows an error (undefined reference) at linking time.
Here is what I have done:
inside /src/visibility.c file, I have added my custom function,
void __attribute__((visibility("default"))) MyFunction(void)
{
printf("This is added just for testing purpose");
}
build process
./configure --prefix=/usr/local --disable-ld-version-script
sudo make install
nm and objdump command find this custom function as global inside shared library.
nm /usr/local/lib/libgcrypt.so | grep MyFunction
000000000000fbf0 T MyFunction
objdump -t /usr/local/lib/libgcrypt.so | grep MyFunction
000000000000fbf0 g F .text 0000000000000013 MyFunction
Here is my sample code to access my custom function.
//gcrypt_example_test.c
#include <stdio.h>
#include <gcrypt.h>
#include <assert.h>
int main()
{
MyFunction();
return 0;
}
export LD_RUN_PATH=/usr/local/lib
gcc gcrypt_example_test.c -o test -lgcrypt
/tmp/ccA0qgAB.o: In function `main':
gcrypt_example_test.c:(.text+0x3a2): undefined reference to `MyFunction'
collect2: error: ld returned 1 exit status
Edit 1:
I tried all possible way to include function prototype declaration inside header file (/src/gcrypt.h) as follows:
void __attribute__((visibility("default"))) MyFunction(void);
... or:
extern void __attribute__((visibility("default"))) MyFunction(void);
... or:
extern void MyFunction(void);
... or:
void MyFunction(void);
I am still getting the same error (undefined reference) although no build error results in all above cases.
Why is this happening, and what mistake am I making?
Although other global functions which are part of standard shared library and defined inside visibility.c (nm also shows T for those functions) are accessible, why is my customized global function (MyFunction) of the shared library still inaccessible? Thanks!
Any link or explanation to resolve this error will be highly appreciable.
From the GCC documentation (emphasis mine):
Some linkers allow you to specify the path to the library by setting LD_RUN_PATH in your environment when linking.
But, from the GNU ld man page:
-rpath=dir
Add a directory to the runtime library search path. This is used
when linking an ELF executable with shared objects. All -rpath
arguments are concatenated and passed to the runtime linker,
which uses them to locate shared objects at runtime. The -rpath
option is also used when locating shared objects which are needed
by shared objects explicitly included in the link; see the
description of the -rpath-link option. If -rpath is not used
when linking an ELF executable, the contents of the environment
variable "LD_RUN_PATH" will be used if it is defined.
Note that there is no mention at all of the link time library search path.
You need to compile/link with /usr/local/lib in the link time library search path:
gcc gcrypt_example_test.c -o test -L/usr/local/lib -lgcrypt
most likely cause of the problem:
The header file for the library has not been updated to include the prototype for the new function
I don't understand the reason behind why it is working now, but not before. Anyway, I found the way to make the code working after adding customized function inside standard library. This post may help others in future.
I first locate libgcrypt.so and then remove all versions of libgcrypt.so
locate libgcrypt.so
sudo rm /usr/local/lib/libgcrypt.so
sudo rm /usr/local/lib/libgcrypt.so.20
sudo rm /usr/local/lib/libgcrypt.so.20.2.2
then I delete the libgcrypt folder (which I had extracted for building library) to start fresh.
Again, I follow these steps
Step 0 : extract libgcrypt source code
Step 1 : add my custom function, inside /src/visibility.c file
void __attribute__((visibility("default"))) MyFunction(void)
{
printf("This is added just for testing purpose");
}
Step 2 : build library
export LD_RUN_PATH=/usr/local/lib
./configure --prefix=/usr/local --disable-ld-version-script
sudo make install
Step 3: Open another terminal to compile
export LD_RUN_PATH=/usr/local/lib
gcc gcrypt_example_test.c -o test -lgcrypt
Step 4 : run
./test
This is added just for testing purpose
This is working fine now as expected.
What I noticed that __attribute__((visibility("default"))) in function definition and --disable-ld-version-script during build process is very important to make the customized function global, elimination of any makes the customized function local inside shared library(.so) file.
Below changes are working at my end
visibility.h
#include <cstdio>
void __attribute__((visibility("default"))) MyFunction(void);
visibility.cpp
#include "visibility.h"
void MyFunction(void)
{
printf("This is added just for testing purpose");
}
library build command
gcc -shared -o libtest.so -Wall -Werror -fpic -I. visibility.cpp
test.cpp
#include <stdio.h>
#include <gcrypt.h>
#include <assert.h>
#include "visibility.h"
extern void MyFunction();
int main()
{
MyFunction();
return 0;
}
exe build command
gcc test.cpp -o test -I. -L. -ltest -lstdc++
My gcc version is 4.4.7
And of-course I did not try and install the lib under /usr/local/lib but kept it local for quick testing.

Error when linking C / Rust "Hello world": undefined reference to `std::io::stdio::_print

I'd like to build and link the two files:
hello.rs
#![crate_type="staticlib"]
#[no_mangle]
pub extern "C" fn print_hello() {
println!("hello, world");
}
hi.c
extern void print_hello();
int main()
{
print_hello();
}
Here are the commands I used and the error:
> rustc hello.rs --emit=obj
> gcc hi.c hello.o -o hi
hello.o: In function `print_hello':
hello.0.rs:(.text.print_hello+0xa8): undefined reference to `std::io::stdio::_print::h3f2263e4b2c9891b'
collect2: error: ld returned 1 exit status
How can I include Rust's stdlib while linking those two?
It's unclear to me why the code specifies #![crate_type="staticlib"] and then the compilation goes out of its way to ignore that via --emit=obj. Compiling to a static library will include the necessary parts of the Rust standard library:
$ rustc hello.rs
note: link against the following native artifacts when linking against this static library
note: the order and any duplication can be significant on some platforms, and so may need to be preserved
note: library: System
note: library: c
note: library: m
$ gcc hi.c libhello.a -lSystem -lc -lm -o hi
$ ./hi
hello, world
This output is from macOS 10.12; check the appropriate libraries for your own system.
Note that you can also specify the crate type on the command line: --crate-type staticlib; it doesn't need to be in the source.

Clang static analyzer can't find stdio.h

I'm trying to use Clang static analyzer on a very simple program:
#include <stdio.h>
main ()
{
printf("Hello, world !");
}
When i do
clang helloworld.c
It compiles the program successfully.
When i do
clang -cc1 -analyze -analyzer-checker=unix helloworld.c
it raises an error:
helloworld.c:1:10: fatal error: 'stdio.h' file not found
#include <stdio.h>
^
1 error generated.
clang --analyze -Xanalyzer -analyzer-checker=unix helloworld.c
doesn't print anything.
What is the problem and how can i fix it?
I assume static analyzer doesn't see the header files though the compiler can use them.
Please, help me.
Sometimes the checker is not able to read the default include path. So you might want to pass it as an argument.
You can find the exact include path clang looks at using this command:
clang -E -x c - -v < /dev/null
and then your final query will become:
clang -I<path to include> --analyze -Xanalyzer -analyzer-checker=unix helloworld.c
Solution using -cc1 flag:
See what include paths the clang is receiving. The flag -v is the key option. The quick way of using it is the following (as given by #Nishant) along with the sample include paths it prints,
$ clang -E -x c - -v < /dev/null
...
#include <...> search starts here:
/usr/local/include
/home/codeman/.itsoflife/local/packages-live/llvm-clang6/build/lib/clang/6.0.1/include
/usr/include/x86_64-linux-gnu
/usr/include
...
On my machine, the simple use of the following command works seamlessly,
$ clang --analyze -Xanalyzer -analyzer-checker=debug.DumpCFG main.c
however the following form fails, as you pointed,
$ clang -cc1 -analyze -analyzer-checker=debug.DumpCFG main.c
For this second command (with -cc1) you can create an environment variable say MY_INCLUDES with the necessary includes. Paste the code below (with necessary include paths as per your system) into ~/.bashrc or ~/.zshrc depending on if you are using bash or zsh. (don't forget to source ~/.bashrc or source ~/.zshrc)
export MY_INCLUDES="-I/usr/local/include -I/home/codeman/.itsoflife/local/packages-live/llvm-clang6/build/lib/clang/6.0.1/include -I/usr/include/x86_64-linux-gnu -I/usr/include"
Now on bash use,
$ clang -cc1 $MY_INCLUDES -analyze -analyzer-checker=debug.DumpCFG main.c
on zsh use,
$ clang -cc1 ${=MY_INCLUDES} -analyze -analyzer-checker=debug.DumpCFG main.c
Note the use of MY_INCLUDES after -cc1 but before the main.c file. Moreover, on zsh one has to use the = prefix with the env variable or else its considered a single string (for details see this answer).

GCC looks for headers in /usr/local/include when compiling, but not for libraries in /usr/local/lib when linking. Why?

I have installed in /usr/ the distribution provided version of SQLite - version 3.4.2.
I have installed in /usr/local/ SQLite version 3.7.4.
/usr/include/sqlite3.h defines SQLITE_VERSION_NUMBER as 3004002
/usr/local/include/sqlite3.h defines SQLITE_VERSION_NUMBER as 3007004
Version 3007004 has the function sqlite3_initialize(), version 3004002 does not.
$ nm -D /usr/local/lib/libsqlite3.so | grep sqlite3_initialize
00018e20 T sqlite3_initialize
When I compile the following example program:
#include <stdio.h>
#include <sqlite3.h>
// This should fail if including /usr/include/sqlite3.h
#if SQLITE_VERSION_NUMBER != 3007004
#error "SQLite version is not 3.7.4"
#endif
int main() {
printf( "%d\n", SQLITE_VERSION_NUMBER );
sqlite3_initialize();
return 0;
}
When compiled and linked (with gcc 4.2.4) like this the preprocessor finds the sqlite3.h header for version 3.7.4 in /usr/local/include/, but the linker fails as it's looking in /usr/lib/libsqlite3.so for the symbols.
$ gcc -Wall test.c -o cpp -lsqlite3
/tmp/cc4iSSN6.o: In function `main':
test.c:(.text+0x26): undefined reference to `sqlite3_initialize'
test.c:(.text+0x2b): undefined reference to `sqlite3_shutdown'
collect2: ld returned 1 exit status
Of course I can specify the lib directory and it links the correct version of the library.
$ gcc -Wall test.c -o cpp -L/usr/local/lib -lsqlite3
$ ./cpp
3007004
$
It seems by default gcc looks in /usr/local/include/ before /usr/include/ for headers, but not for libraries when linking. Why?
Edit 1: As suggested by Tim Post:
$ sudo ldconfig -n /usr/local/lib
$ ldconfig -p | grep sqlite3
libsqlite3.so.0 (libc6) => /usr/local/lib/libsqlite3.so.0
libsqlite3.so.0 (libc6) => /usr/lib/libsqlite3.so.0
libsqlite3.so (libc6) => /usr/local/lib/libsqlite3.so
libsqlite3.so (libc6) => /usr/lib/libsqlite3.so
$ gcc -Wall cpp.c -o cpp -lsqlite3
/tmp/ccwPT9o0.o: In function `main':
cpp.c:(.text+0x26): undefined reference to `sqlite3_initialize'
cpp.c:(.text+0x2b): undefined reference to `sqlite3_shutdown'
collect2: ld returned 1 exit status
The include file search path is defined by gcc, but the library search path is encoded into ld, which is from a separate project; these are not necessarily synchronized.
One thing you could do is patch the specs file, which, if it exists, can be found in the same directory as libgcc; you can get the path to the latter using
gcc -print-libgcc-file-name
If there is no specs file there, create one using
gcc -dumpspecs >specs
and verify that gcc is reading it, by calling
gcc -v
Look for a line containing %{L*}, and add -L/usr/local/lib behind it (separated by spaces). Gcc will then pass this argument following any -L options from the command line to ld when linking.
In order to restore the defaults, just revert the specs file to its initial state (i.e. delete it if it didn't exist before).
This may be symptom of using the gold linker, which does not search /usr/local/lib, at least in some versions. Try removing the package binutils-gold.

Resources