emscripten-core / emscripten

Emscripten: An LLVM-to-WebAssembly Compiler
Other
25.59k stars 3.28k forks source link

ALLOW_UNIMPLEMENTED_SYSCALLS doesn't exclude unsupported syscalls in autoconf #14824

Open RReverser opened 3 years ago

RReverser commented 3 years ago

A program I was cross-compiling relies on pipe2. I've noticed that it was always printing a warning about pipe2 being unsupported during runtime, and always falling back to pipe.

While it's good that they left in a runtime fallback, I was wondering if there's a way to avoid compiling that unsupported code in altogether. I've noticed that they detect pipe2 with configure.ac:

AC_CHECK_FUNCS([pipe2])

When executed with Emscripten, it prints:

checking for pipe2... yes

After looking through settings, I've assumed it's because ALLOW_UNIMPLEMENTED_SYSCALLS is enabled by default, so I went ahead and changed it, but, surprisingly, now configure continued to report

checking for pipe2... yes

but the actual program fails to link due to the missing syscall.

Looking at config.log, this is the only relevant bit which seems suspicious but I don't understand how it explains why configure still manages to find the pipe2 function:

configure:17216: checking for pipe2
configure:17216: /usr/local/google/home/rreverser/emsdk/upstream/emscripten/emcc -o conftest -g -O2  -s ALLOW_UNIMPLEMENTED_SYSCALLS=0 conftest.c  >&5
wasm-ld: warning: function signature mismatch: pipe2
>>> defined as () -> i32 in /tmp/emscripten_temp_nj0l58ag/conftest_0.o
>>> defined as (i32, i32) -> i32 in /usr/local/google/home/rreverser/emsdk/upstream/emscripten/cache/sysroot/lib/wasm32-emscripten/libc.a(pipe2.o)
configure:17216: $? = 0
configure:17216: result: yes

Does it try to link against wrong pipe2 function or something?

RReverser commented 3 years ago

Hm, looks like the problem is that autoconf (roughly) generates code like this:

char pipe2 ();

int
main (void)
{
return pipe2 ();
  ;
  return 0;
}

Due to function signature mismatch, it results in the following Wasm under -O -s ALLOW_UNIMPLEMENTED_SYSCALLS=0:

...
 (func $signature_mismatch:pipe2 (type 0) (result i32)
    unreachable)
  (func $__original_main (type 0) (result i32)
    call $signature_mismatch:pipe2)
  (func $main (type 4) (param i32 i32) (result i32)
    call $__original_main)
...

But succeeds to compile.

@sbc100 Can we enhance the check somehow to account for autoconf shenanigans here?

sbc100 commented 3 years ago

It looks like we do produce a linker warning:

wasm-ld: warning: function signature mismatch: pipe2
>>> defined as () -> i32 in /tmp/emscripten_temp_rrr3hslv/test_0.o
>>> defined as (i32, i32) -> i32 in /home/sbc/dev/wasm/emscripten/cache/sysroot/lib/wasm32-emscripten/libc.a(pipe2.o)

Unfortunately I think that linker behaviour exists specifically to allow cmake and autoconf to detect the existence of functions in this way (with incorrect signatures). If we turn those into errors then I fear everything will fail to be detected.

ALLOW_UNIMPLEMENTED_SYSCALLS is a relatively new feature... and I'm struggling to how we can make it work alongside this fuzzy signature matching here. I guess one way would be build a separate libc-strict.a that simply does not contain any of the unimplemented syscalls.

RReverser commented 3 years ago

If we turn those into errors then I fear everything will fail to be detected.

I was thinking about the opposite - matching pipe2 to the pipe2 with proper signature by reusing the mechanism from main -> original_main where we already wrap one with the other on signature mismatch.

I guess one way would be build a separate libc-strict.a that simply does not contain any of the unimplemented syscalls.

Sounds like it could work too yeah.

RReverser commented 3 years ago

Sounds like it could work too yeah.

Come to think of it, yeah, if this works, it's a much better option.