How to compile longjmp using emcc? - c

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

Related

Linking EFI application with LLVM on MacOS

I'm attempting to cross-compile and link a very simple EFI application that is using the EFI headers from the Zircon kernel. Despite my best efforts, I am unable to link a working PE executable under macOS Montery (with apple silicon), due to the LLD flag -subsystem:efi_application not being valid. The full error is:
FAILED: test.efi: && /opt/local/bin/clang -target x86_64-none-elf -ffreestanding -nostdlib -fuse-ld=lld -dll -WX -Wl,-subsystem:efi_application -Wl,entry:efi_main src/main.c.obj -o test.efi && :
ld.lld: error: unknown argument '-subsystem:efi_application'
Several guides indicate that lld-link is required to compile this correctly, however adding -fuse-ld=lld-link results in errors stating:
clang: error: invalid linker name in argument '-fuse-ld=lld-link'
This occurs despite the fact that I have lld-link in my $PATH. If I, instead, pass the full lld-link path to -fuse-ld=, I get the following error:
FAILED: test.efi: && /opt/local/bin/clang -target x86_64-none-elf -ffreestanding -target x86_64-none-elf -nostdlib -dll -WX -Wl,-subsystem:efi_application -Wl,-entry:efi_main -fuse-ld=/opt/local/bin/lld-link src/main.c.obj -o test.efi && :
lld-link: warning: ignoring unknown argument '--eh-frame-hdr'
lld-link: warning: ignoring unknown argument '-m'
lld-link: warning: ignoring unknown argument '-dynamic-linker'
lld-link: warning: ignoring unknown argument '-o'
lld-link: warning: ignoring unknown argument '-L/opt/local/libexec/llvm-13/bin/../lib'
lld-link: warning: ignoring unknown argument '-L/usr/lib'
lld-link: error: could not open 'elf_x86_64': No such file or directory
lld-link: error: could not open '/lib64/ld-linux-x86-64.so.2': No such file or directory
lld-link: error: could not open 'test.efi': No such file or directory
lld-link: error: src/main.c.obj: unknown file type
clang: error: linker command failed with exit code 1 (use -v to see invocation)
I am using the MacPorts LLVM-13 package. The result of $ lld-link --version is LLD 13.0.0.
For reference, the code I'm attempting to compile and link is, simply:
#include "efi/protocol/graphics-output.h"
#include "efi/protocol/loaded-image.h"
#include "efi/system-table.h"
#include "efi/types.h"
#define ERR(x) if (EFI_ERROR((x))) { return (x); }
efi_status efi_main(efi_handle handle __attribute__((unused)), efi_system_table* st) {
efi_status status;
/* clear the screen */
status = st->ConOut->ClearScreen(st->ConOut);
ERR(status);
/* print 'Hello World' */
status = st->ConOut->OutputString(st->ConOut, u"Hello World");
ERR(status);
return EFI_SUCCESS;
}
What do I need to change above in order to build an EFI application using LLVM on macOS?
I'd also be curious to know any ideas as to why -fuse-ld=lld-link would fail when it exists in my PATH, and why using -fuse-ld=/opt/local/bin/lld-link would result in other implicit linker flags not succeeding.
System Details:
Compiler Suite: LLVM-13 (Macport Installation)
Host OS: macOS Monterey (Apple Silicon, M1 Pro)
Target Arch: x86_64
Build System: CMake using custom toolchain (which passes the -nostdlib, -target ... args, etc.)
You can try to use clang-cl to compile your code, using /c option to compile only: do not link yet. Use /Fo option to specify the output object file.
The clang-cl offers compatibility options from MSVC cl.exe. For further details, run clang-cl --help. The build target of clang-cl is default to windows, so you don't have to reset the build target in order to compile for EFI applications.
Be warned that clang-cl does not have full support to MSVC intrinsics. Intrinsics like __vmx_vmptrld, __svm_vmload, etc. are broken. clang-cl would generate a call instruction rather than the specific instruction. Define them in inline assembly in GCC's syntax, if you want to use these intrinsics.
After you compiled all source files into object files, run lld-link to link all output object files with /SUBSYSTEM:EFI_APPLICATION parameter into an EFI application.
I haven't used the header files from Zircon yet, but I tried compiling EDK II libraries with LLVM into .lib files on Windows.
With the help of these .lib files, I made some simple EFI applications. For instance, you can check this partition enumerator out.
The batch scripts might not work on MacOS, but, by reading them, you can definitely figure out the general ideas about how to specify the parameters.
If you want to use EDK II by virtue of its rich libraries, you might want to install Netwide Assembler as well, in that the assembly codes in EDK II libraries are NASM.

EM_JS can't export functions in emscripten

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.

`__heap_base` seems to be missing in clang 9.0.0, is there a replacement?

I'm trying to compile a C library of mine for WebAssembly, using clang (no emscripten), and it compiles fine with clang version 8.0.1, but fails with clang version 9.0.0. The reported error is wasm-ld: error: ….o: undefined symbol: __heap_base. Has __heap_base been replaced with some other symbol?
The library is open source and compilation instructions can be found here
Actually, I believe I found the culprit: the linker in 9.0.0 seems to require the --export=__heap_base (-Wl,--export=__heap_base for the clang). This works for my project.
This does seem to be bug in 9.0.0. It doesn't seem to occur with ToT or with 8.0.0.
Simple repro case:
extern void* __heap_base;
void* a = &__heap_base;
void _start() {
}
Build with:
$ clang --target=wasm32 test.c -nostdlib -Wl,-no-gc-sections
With 9.0.0:
wasm-ld: error: /tmp/test-551a5c.o: undefined symbol: __heap_base
clang-9: error: linker command failed with exit code 1 (use -v to see invocation)
This should be opened as bug in the llvm bug tracker.

Cross-compiled library not found by toolchain

I'm new to developing for embedded systems, but I have installed arm-linux-gnueabi-gcc via the Linux Mint package manager and managed to build a few programs successfully.
I'm currently struggling with getting a program to compile using libusb. I've done the following:
Downloaded and unpacked the libusb 1.0.20 sources from https://sourceforge.net/projects/libusb/.
Compiled and installed them using the following commands:
~/Downloads/libusb-1.0.20 $ ./configure --host=arm-linux-gnueabi --prefix=/opt/ --disable-udev
~/Downloads/libusb-1.0.20 $ sudo make
~/Downloads/libusb-1.0.20 $ sudo make install
(The reason for sudo-ing the make commands was because I encountered permission problems related to removing old files.)
Copied a small sample file from somewhere on the internet:
#include <libusb-1.0/libusb.h>
#include <stdio.h>
int main()
{
int i=0;
libusb_context **c = NULL;
i = libusb_init(c);
printf("\nusing libusb.h\n");
return 0;
}
Tried to build it and run it with gcc:
~/Desktop/libtest $ gcc libtest1.c -o libtest1 -lusb-1.0
~/Desktop/libtest $ ./libtest1
using libusb.h
However, when I try to do the same with arm-linux-gnueabi-gcc, it can't find the library:
~/Desktop/libtest $ arm-linux-gnueabi-gcc libtest1.c -o libtest1 -lusb-1.0
/usr/lib/gcc-cross/arm-linux-gnueabi/4.7/../../../../arm-linux-gnueabi/bin/ld: cannot find -lusb-1.0
collect2: error: ld returned 1 exit status
Where did I go wrong? Is there something else I need to do in order to use the library? Did I fail at compiling the library for the arm compiler? I didn't include the compiler output here since it's quite long, but there are no obvious errors. This might be a very stupid question, but I'm completely clueless.

Linking fails with gcc 4.8.2 / ld 2.24, succeeds with gcc 4.4.7 / ld 2.20

In a chroot based on CentOS 6.4 I'm working in, linking against ncurses with ld 2.20 succeeds, but linking with ld 2.24 fails. I don't directly invoke the linker, gcc is handling it -- gcc 4.4.7 is using ld 2.20, and gcc 4.8.2 is using ld 2.24.
Here is a minimal example that fails to link with gcc 4.8.2 / ld 2.24 in my particular environment.
#include <ncurses.h>
#include <stdio.h>
#include <stdlib.h>
int main() {
WINDOW* window = NULL;
if (!(window = initscr())) {
printf("Error initializing ncurses.");
exit(1);
}
halfdelay(50);
getch();
endwin();
}
Success (ld 2.20):
$ gcc main.c -lncurses -Wl,--verbose | grep "ncurses.*succeeded"
attempt to open /usr/lib/gcc/x86_64-redhat-linux/4.4.7/../../../../lib64/libncurses.so succeeded
$
Failure (ld 2.24):
$ /opt/gcc/4.8.2/bin/gcc48 main.c -lncurses -Wl,--verbose | grep "ncurses.*succeeded"
attempt to open /usr/lib/../lib64/libncurses.so succeeded
/opt/binutils/2.24/bin/ld24: /tmp/ccCxUFxl.o: undefined reference to symbol 'halfdelay'
/lib64/libtinfo.so.5: error adding symbols: DSO missing from command line
collect2: error: ld returned 1 exit status
$
Note that both commands appear to link against the same libncurses.so.
For what it's worth, in a different chroot based on CentOS 5.4, there is a different libncurses version, and it links fine with ld 2.24, but unfortunately, building in that chroot is not an option. The nm utility shows that the (static) libncurses.a here does have the required symbols (nm lists no symbols for libncurses.so -- so I'm just assuming they are similar).
In the CentOS 6.4 chroot, however, nm shows that all of the ncurses symbols I'm getting "undefined reference" messages for are indeed undefined or not present in libncurses.a in the Centos 6.4 chroot, which is confusing, because linking with gcc 4.4.7 works. Something is not right.
Also, I tried producing an object with gcc 4.4.7, and then linking with gcc 4.8.2, but that did not help.
I'm confused as to why the one compiler / linker would succeed while the other would fail. Is this an ABI issue? Does anyone know what is happening here? Are there any flags I can pass to gcc to make the new linker work?
Your libncurses library is itself linked to libtinfo, which causes your older toolchain to also look for symbols in libtinfo.
But newer toolchains normally runs the linker with the --as-needed, and the --no-copy-dt-needed-entries, the latter which probably causes the difference you're seeing.
Basically you'll need to link to libtinfo as well, which is where the halfdelay function resides.
gcc main.c -lncurses -ltinfo

Resources