mcsema icon indicating copy to clipboard operation
mcsema copied to clipboard

__remill_fpu_exception_test_and_clear

Open jibanes opened this issue 5 years ago • 15 comments

/usr/bin/ld: llvm-link:(.text+0x1a7dd0): undefined reference to `__remill_fpu_exception_test_and_clear'
/usr/bin/ld: llvm-link:(.text+0x1a816a): undefined reference to `__remill_fpu_exception_test_and_clear'
/usr/bin/ld: llvm-link:(.text+0x1a817a): undefined reference to `__remill_fpu_exception_test_and_clear'
/usr/bin/ld: llvm-link:(.text+0x1a92ff): undefined reference to `__remill_fpu_exception_test_and_clear'
/usr/bin/ld: llvm-link:(.text+0x1a930f): undefined reference to `__remill_fpu_exception_test_and_clear'

What do those generally mean, and how to correct this? (when running remill-clang-9.0)

jibanes avatar Nov 13 '20 04:11 jibanes

It means you will need a runtime. I will add try to add some new bitcode-based runtimes tomorrow to the build and install so that this error goes away.

Basically, the bitcode is using a remill intrinsic to inspect floating point status state, and this function is not actually implemented anywhere (this is by design). When I add it in, I will copy the implementation from here: https://github.com/lifting-bits/remill/blob/master/tests/X86/Run.cpp#L309-L313

pgoodman avatar Nov 13 '20 04:11 pgoodman

Right, I see; thank you Peter.

jibanes avatar Nov 13 '20 04:11 jibanes

This should work for you in the meantime:

#include <cstdint>
#include <cstddef>
#include <fcntl.h>
#include <unistd.h>
#include <cstdarg>
#include <cstdio>
#include <cfenv>
#include <cmath>

#ifndef ADDRESS_SIZE_BITS
# if !defined(UINTPTR_MAX)
#   error "Please specify -DADDRESS_SIZE_BITS=<32 or 64>"
# endif
# if UINTPTR_MAX == 0xFFFFFFFFull
#  define ADDRESS_SIZE_BITS 32
# else
#  define ADDRESS_SIZE_BITS 64
# endif
#endif

#include <remill/Arch/Runtime/State.h>
#include <remill/Arch/X86/Runtime/State.h>

extern "C" {

NEVER_INLINE float64_t __remill_read_memory_f80(Memory *, addr_t addr) {
  return static_cast<double>(*reinterpret_cast<long double *>(static_cast<uintptr_t>(addr)));
}

NEVER_INLINE Memory *__remill_write_memory_f80(Memory *mem, addr_t addr, float64_t val) {
  *reinterpret_cast<long double *>(static_cast<uintptr_t>(addr)) = static_cast<long double>(val);
  return mem;
}

Memory *__remill_compare_exchange_memory_8(Memory *memory, addr_t addr,
                                           uint8_t &expected, uint8_t desired) {
  expected = __sync_val_compare_and_swap(reinterpret_cast<uint8_t *>(addr),
                                         expected, desired);
  return memory;
}

Memory *__remill_compare_exchange_memory_16(Memory *memory, addr_t addr,
                                            uint16_t &expected,
                                            uint16_t desired) {
  expected = __sync_val_compare_and_swap(reinterpret_cast<uint16_t *>(addr),
                                         expected, desired);
  return memory;
}

Memory *__remill_compare_exchange_memory_32(Memory *memory, addr_t addr,
                                            uint32_t &expected,
                                            uint32_t desired) {
  expected = __sync_val_compare_and_swap(reinterpret_cast<uint32_t *>(addr),
                                         expected, desired);
  return memory;
}

Memory *__remill_compare_exchange_memory_64(Memory *memory, addr_t addr,
                                            uint64_t &expected,
                                            uint64_t desired) {
  expected = __sync_val_compare_and_swap(reinterpret_cast<uint64_t *>(addr),
                                         expected, desired);
  return memory;
}

Memory *__remill_compare_exchange_memory_128(Memory *memory, addr_t addr,
                                             uint128_t &expected,
                                             uint128_t &desired) {
#ifdef _GXX_EXPERIMENTAL_CXX0X__
  expected = __sync_val_compare_and_swap(reinterpret_cast<uint128_t *>(addr),
                                         expected, desired);
#endif
  return memory;
}

#define MAKE_ATOMIC_INTRINSIC(intrinsic_name, type_prefix, size) \
  Memory *__remill_##intrinsic_name##_##size(Memory *memory, addr_t addr, \
                                             type_prefix##size##_t &value) { \
    value = __sync_##intrinsic_name( \
        reinterpret_cast<type_prefix##size##_t *>(addr), value); \
    return memory; \
  }

MAKE_ATOMIC_INTRINSIC(fetch_and_add, uint, 8)
MAKE_ATOMIC_INTRINSIC(fetch_and_add, uint, 16)
MAKE_ATOMIC_INTRINSIC(fetch_and_add, uint, 32)
MAKE_ATOMIC_INTRINSIC(fetch_and_add, uint, 64)
MAKE_ATOMIC_INTRINSIC(fetch_and_sub, uint, 8)
MAKE_ATOMIC_INTRINSIC(fetch_and_sub, uint, 16)
MAKE_ATOMIC_INTRINSIC(fetch_and_sub, uint, 32)
MAKE_ATOMIC_INTRINSIC(fetch_and_sub, uint, 64)
MAKE_ATOMIC_INTRINSIC(fetch_and_or, uint, 8)
MAKE_ATOMIC_INTRINSIC(fetch_and_or, uint, 16)
MAKE_ATOMIC_INTRINSIC(fetch_and_or, uint, 32)
MAKE_ATOMIC_INTRINSIC(fetch_and_or, uint, 64)
MAKE_ATOMIC_INTRINSIC(fetch_and_and, uint, 8)
MAKE_ATOMIC_INTRINSIC(fetch_and_and, uint, 16)
MAKE_ATOMIC_INTRINSIC(fetch_and_and, uint, 32)
MAKE_ATOMIC_INTRINSIC(fetch_and_and, uint, 64)
MAKE_ATOMIC_INTRINSIC(fetch_and_xor, uint, 8)
MAKE_ATOMIC_INTRINSIC(fetch_and_xor, uint, 16)
MAKE_ATOMIC_INTRINSIC(fetch_and_xor, uint, 32)
MAKE_ATOMIC_INTRINSIC(fetch_and_xor, uint, 64)

int __remill_fpu_exception_test_and_clear(int read_mask, int clear_mask) {
  auto except = std::fetestexcept(read_mask);
  std::feclearexcept(clear_mask);
  return except;
}

Memory *__remill_barrier_load_load(Memory *) {
  return nullptr;
}
Memory *__remill_barrier_load_store(Memory *) {
  return nullptr;
}
Memory *__remill_barrier_store_load(Memory *) {
  return nullptr;
}
Memory *__remill_barrier_store_store(Memory *) {
  return nullptr;
}
Memory *__remill_atomic_begin(Memory *) {
  return nullptr;
}
Memory *__remill_atomic_end(Memory *) {
  return nullptr;
}


Memory *__remill_sync_hyper_call(X86State &state, Memory *mem,
                                 SyncHyperCall::Name call) {
  switch (call) {
    case SyncHyperCall::kX86CPUID:
      asm volatile("cpuid"
                   : "=a"(state.gpr.rax.aword), "=b"(state.gpr.rbx.aword),
                     "=c"(state.gpr.rcx.aword), "=d"(state.gpr.rdx.aword)
                   : "a"(state.gpr.rax.aword), "b"(state.gpr.rbx.aword),
                     "c"(state.gpr.rcx.aword), "d"(state.gpr.rdx.aword));
      break;

    case SyncHyperCall::kX86ReadTSC:
      asm volatile("rdtsc"
                   : "=a"(state.gpr.rax.dword), "=d"(state.gpr.rdx.dword));
      break;

    case SyncHyperCall::kX86ReadTSCP:
      asm volatile("rdtscp"
                   : "=a"(state.gpr.rax.aword), "=c"(state.gpr.rcx.aword),
                     "=d"(state.gpr.rdx.aword)
                   : "a"(state.gpr.rax.aword), "c"(state.gpr.rcx.aword),
                     "d"(state.gpr.rdx.aword));
      break;

    default: abort();
  }

  return mem;
}

extern thread_local X86State __mcsema_reg_state;

int __fpclassifyd(double x) {
  return std::fpclassify(x);
}

Memory *__mcsema_printf(Memory *mem, const char *fmt, ...) {
  va_list ls;
  va_start(ls, fmt);
  vprintf(fmt, ls);
  va_end(ls);
  return mem;
}

void __mcsema_pc_tracer(addr_t pc) {
#if ADDRESS_SIZE_BITS == 32
  printf("pc=%08" PRIx32 "\n", pc);
#else
  printf("pc=%016" PRIx64 "\n", pc);
#endif
}

}  // extern C

pgoodman avatar Nov 13 '20 05:11 pgoodman

I will have to doublecheck but I think the issue persists (same missing object at link time); I'm running additional tests.

jibanes avatar Nov 13 '20 17:11 jibanes

Are you linking this file in? E.g. clang-9 lifted.bc runtime.cc ...?

pgoodman avatar Nov 13 '20 17:11 pgoodman

I just replaced Run.cc with the code above aforementioned, and recompiled.

jibanes avatar Nov 13 '20 18:11 jibanes

I don't understand; do you mean you replaced the test runner with the code I pasted above? What I meant is that you need to make a new file, lets say Runtime.cpp, copy this code into it, and then when you're trying to recompile the lifted bitcode, add in Runtime.cpp to your compile command.

pgoodman avatar Nov 13 '20 18:11 pgoodman

Sorry for the confusion Peter.

Here's my complete output:

$ remill-clang-9.0 -v --target=i386-linux-gnu -o /tmp/xxx.lifted /tmp/xxx.bc -lpthread -lm -ldl /usr/lib/i386-linux-gnu/libX11.so.6.3.0 -Wl,--section-start=.section_1ff00000=0x1ff00000 fpu.cc -I /usr/lib/llvm-9/lib/clang/9.0.1/include
clang version 9.0.0 (tags/RELEASE_900/final)
Target: i386-unknown-linux-gnu
Thread model: posix
InstalledDir: /usr/local/bin
Found candidate GCC installation: /usr/lib/gcc-cross/i686-linux-gnu/9
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
Candidate multilib: 32;@m32
Candidate multilib: x32;@mx32
Selected multilib: 32;@m32
 "/usr/local/bin/remill-clang-9.0" -cc1 -triple i386-unknown-linux-gnu -emit-obj -mrelax-all -disable-free -disable-llvm-verifier -discard-value-names -main-file-name xxx.bc -mrelocation-model static -mthread-model posix -mdisable-fp-elim -fmath-errno -masm-verbose -mconstructor-aliases -fuse-init-array -target-cpu pentium4 -dwarf-column-info -debugger-tuning=gdb -v -resource-dir /usr/local/lib/clang/9.0.0 -fdebug-compilation-dir /home/jibanes -ferror-limit 19 -fmessage-length 0 -fobjc-runtime=gcc -fdiagnostics-show-option -fcolor-diagnostics -faddrsig -o /tmp/xxx-dfa8bc.o -x ir /tmp/xxx.bc
clang -cc1 version 9.0.0 based upon LLVM 9.0.0 default target x86_64-unknown-linux-gnu
warning: overriding the module target triple with i386-unknown-linux-gnu [-Woverride-module]
1 warning generated.
 "/usr/local/bin/remill-clang-9.0" -cc1 -triple i386-unknown-linux-gnu -emit-obj -mrelax-all -disable-free -disable-llvm-verifier -discard-value-names -main-file-name fpu.cc -mrelocation-model static -mthread-model posix -mdisable-fp-elim -fmath-errno -masm-verbose -mconstructor-aliases -fuse-init-array -target-cpu pentium4 -dwarf-column-info -debugger-tuning=gdb -v -resource-dir /usr/local/lib/clang/9.0.0 -I /usr/lib/llvm-9/lib/clang/9.0.1/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/9/../../../../include/x86_64-linux-gnu/c++/9/32 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/9/../../../../include/i386-linux-gnu/c++/9 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/backward -internal-isystem /usr/local/include -internal-isystem /usr/local/lib/clang/9.0.0/include -internal-externc-isystem /usr/include/i386-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -fdeprecated-macro -fdebug-compilation-dir /home/jibanes -ferror-limit 19 -fmessage-length 0 -fobjc-runtime=gcc -fcxx-exceptions -fexceptions -fdiagnostics-show-option -fcolor-diagnostics -faddrsig -o /tmp/fpu-2095fb.o -x c++ fpu.cc
clang -cc1 version 9.0.0 based upon LLVM 9.0.0 default target x86_64-unknown-linux-gnu
ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/9/../../../../include/i386-linux-gnu/c++/9"
ignoring nonexistent directory "/usr/local/lib/clang/9.0.0/include"
ignoring nonexistent directory "/include"
#include "..." search starts here:
#include <...> search starts here:
 /usr/lib/llvm-9/lib/clang/9.0.1/include
 /usr/lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9
 /usr/lib/gcc/x86_64-linux-gnu/9/../../../../include/x86_64-linux-gnu/c++/9/32
 /usr/lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/backward
 /usr/local/include
 /usr/include/i386-linux-gnu
 /usr/include
End of search list.
fpu.cc:177:19: error: expected ')'
  printf("pc=%08" PRIx32 "\n", pc);
                  ^
fpu.cc:177:9: note: to match this '('
  printf("pc=%08" PRIx32 "\n", pc);
        ^
1 error generated.

jibanes avatar Nov 13 '20 19:11 jibanes

Ahh! This is one of those annoying issues where remill-clang has slightly different include search paths than your system clang/gcc. I'd recommend doing c++ -x c++ -v -P - </dev/null, looking at its include search order, and then if you spot something there that's missing from what you see in your previous comment, then add it into your compile command with a -isystem /path/to/blah.

pgoodman avatar Nov 13 '20 19:11 pgoodman

or perhaps it's just missing #include <inttypes.h>

llvm-link:(.text+0x1a7daa): undefined reference to `sqrt__original'

I must have had a function in the original binary that was called sqrt; which conflicts with modern versions of libm which define this, do you know how to rename this function at linking time?

jibanes avatar Nov 13 '20 19:11 jibanes

What happens if you comment out this line: https://github.com/lifting-bits/mcsema/blob/b0f7790f43567e3da5c4bf47235c1c53f73d00d8/mcsema/CFG/CFG.cpp#L1397 :trollface:

pgoodman avatar Nov 13 '20 19:11 pgoodman

It then produce a lifted binary, which unfortunately segfaults (i386->i386) What are your recommendations to troubleshoot? Original binary: https://eskimo.com/~jibanes/tmp/binary386

0x0812322c in sub_27f4e684.init_proc ()
(gdb) where
#0  0x0812322c in sub_27f4e684.init_proc ()
#1  0x08293e13 in sub_27fb86e0___libc_csu_init ()
#2  0x0804a9a6 in ?? ()
#3  0x083c9318 in __mcsema_constructor ()
#4  0x083ca553 in __libc_csu_init ()
#5  0xf7cafe74 in __libc_start_main () from /lib/i386-linux-gnu/libc.so.6
#6  0x0804a876 in _start ()

jibanes avatar Nov 13 '20 19:11 jibanes

Approach 1

  1. re-run mcsema-lift, with the additional flag --add_pc_tracer.
  2. re-compile, run it, and as additional output you will get a bunch of printouts.
  3. open up your original binary in gdb, lets say like this:
$ gdb ./binary386
(gdb) b *0
(gdb) section .text 0x1ff00000
(gdb) r
<error gets reported, don't worry>
(gdb) d 1
(gdb) while 1
  if ($eip & 0xfff00000) == 0x1ff00000
    printf "pc=%08x\n", $eip
  end
  si
end

Then that will print out a whole lot of stuff. Then I copy that output to somewhere, regex out all ^pc=.* lines, then do a diff between the trace outputted by the lifted bitcode and the trace from the native one.

Approach 2

Re-lift with --add_breakpoints. Re-compile, run under gdb. When it crashes, do:

(gdb) f 0
(gdb) disass

Then go looking for the => pointing out where it has crashed, and look nearby for calls to functions named breakpoint_, and that will help you localize the crash to a specific program counter in the original binary.

Approach 3

It probably has something to do with sqrt, as that was the last thing you changed. Uncomment the code that I asked you to comment, returning it to its original state. Go modify Runtime.cpp and add something like the following:

extern "C" double sqrt__original(double val) {
  return __builtin_sqrt(val);
}

pgoodman avatar Nov 13 '20 20:11 pgoodman

Approach 1:

(gdb) while 1
 >
 >  if ($eip & 0xfff00000) == 0x1ff00000
  >    printf "pc=%08x\n", $eip
  >  end
 >  si
 >end
Argument to arithmetic operation not a number or boolean.

Approach 2:

Program received signal SIGSEGV, Segmentation fault.
0x70b589ff in ?? ()
(gdb) f 0
#0  0x70b589ff in ?? ()
(gdb) disass
No function contains program counter for selected frame.

Approach 3:

/usr/bin/ld: /tmp/k-c9d513.o: in function `ext_28079ff8_tmpnam':
llvm-link:(.text+0x351bf4): warning: the use of `tmpnam' is dangerous, better use `mkstemp'
/usr/bin/ld: /tmp/k-c9d513.o: in function `ext_28079fa0_mktemp':
llvm-link:(.text+0x3522f4): warning: the use of `mktemp' is dangerous, better use `mkstemp' or `mkdtemp'
/usr/bin/ld: /tmp/k-c9d513.o: in function `sub_27f6cda9_dj':
llvm-link:(.text+0x10f28): undefined reference to `__remill_fpu_exception_test_and_clear'
/usr/bin/ld: llvm-link:(.text+0x10f54): undefined reference to `__remill_fpu_exception_test_and_clear'
/usr/bin/ld: /tmp/k-c9d513.o: in function `sub_27f4dec6_map':
llvm-link:(.text+0x12d61): undefined reference to `__remill_fpu_exception_test_and_clear'
/usr/bin/ld: llvm-link:(.text+0x12dad): undefined reference to `__remill_fpu_exception_test_and_clear'
/usr/bin/ld: /tmp/k-c9d513.o: in function `sub_27f4e203_gmT':
llvm-link:(.text+0x1f485): undefined reference to `__remill_fpu_exception_test_and_clear'
/usr/bin/ld: /tmp/k-c9d513.o:llvm-link:(.text+0x1f4a8): more undefined references to `__remill_fpu_exception_test_and_clear' follow
remill-clang-9: error: linker command failed with exit code 1 (use -v to see invocation)
make: *** [makefile:31: all] Error 1

jibanes avatar Nov 18 '20 06:11 jibanes

For approach 1, you can probably change $eip to ((uint32_t) $eip) or ((uint32_t) $pc).

pgoodman avatar Nov 18 '20 16:11 pgoodman