#358 trampoline_r test1 fails on i386 running Mac OS

build problems
closed-out-of-date
Bruno Haible
ffcall (13)
5
2008-01-22
2006-08-06
Edward Carrel
No

trampoline_r fails to configure on a MacBook running Mac OS 10.4.7.
Get the following output from configure:

cd trampoline_r; make all
make[1]: Nothing to be done for `all'.
/bin/sh ./libtool --mode=link gcc -o libcallback.la -rpath /usr/local/
lib vacall_r/vacall.lo vacall_r/misc.lo vacall_r/structcpy.lo trampoline_r/
*.lo
rm -fr .libs/libcallback.a .libs/libcallback.la .libs/libcallback.lai
ar cru .libs/libcallback.a vacall_r/vacall-i386.o vacall_r/misc.o
vacall_r/structcpy.o trampoline_r/trampoline.o
ranlib .libs/libcallback.a
creating libcallback.la
(cd .libs && rm -f libcallback.la && ln -s ../libcallback.la libcallback.la)
./minitests > minitests.out
LC_ALL=C uniq -u < minitests.out > minitests.output.i386-apple-
darwin8.7.1
test '!' -s minitests.output.i386-apple-darwin8.7.1
cd vacall_r; make all
make[1]: Nothing to be done for `all'.
cd trampoline_r; make all
make[1]: Nothing to be done for `all'.
cd vacall_r; make check
make[1]: Nothing to be done for `check'.
cd trampoline_r; make check
gcc -g -O2 -I. -I../../../ffcall/callback/trampoline_r -c ../../../ffcall/
callback/trampoline_r/test1.c
/bin/sh ./libtool --mode=link gcc -g -O2 -x none test1.o
trampoline.lo -o test1
gcc -g -O2 -x none test1.o trampoline.o -o test1
gcc -g -O2 -I. -I../../../ffcall/callback/trampoline_r -c ../../../ffcall/
callback/trampoline_r/test2.c
/bin/sh ./libtool --mode=link gcc -g -O2 -x none test2.o
trampoline.lo -o test2
gcc -g -O2 -x none test2.o trampoline.o -o test2
./test1
make[1]: *** [check] Illegal instruction (core dumped)

The core provides nothing really useful:

$ gdb ~/clisp-2.39/src/callback/trampoline_r/test1 /cores/core.
18128
GNU gdb 6.3.50-20050815 (Apple version gdb-477) (Sun Apr 30
20:01:44 GMT 2006)
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and
you are
welcome to change it and/or distribute copies of it under certain
conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for
details.
This GDB was configured as "i386-apple-darwin"...Reading symbols for
shared libraries .. done

Core was generated by `./test1'.
#0 0x8fe13914 in __dyld_stub_binding_helper_interface ()
(gdb) bt
#0 0x8fe13914 in __dyld_stub_binding_helper_interface ()
#1 0x00000000 in ?? ()
(gdb)

I realize this isn't a lot to go on. Let me know if I can provide anything
else that might make diagnosing this any easier.

Discussion

  • Edward Carrel
    Edward Carrel
    2006-08-06

    Logged In: YES
    user_id=635878

    I stepped through the execution of test1 in GDB and found that the illegal
    instruction is occuring in a call to free(3). I've included a trace of the program
    execution following a few variables of interest below.

    Breakpoint 2 at 0x1e08: file ./test1.c, line 96.
    Starting program: *snip*/clisp-2.39/ffcall/callback/trampoline_r/test1

    Breakpoint 2, main () at ./test1.c:96
    96 function cf = alloc_trampoline_r((function)&f, (void*)MAGIC1, (void*)
    MAGIC2);
    (gdb) s
    alloc_trampoline_r (address=0x1de6 <f>, data0=0x9db9af42,
    data1=0x614a13c9) at ./trampoline.c:394
    394 { char* room = (char*) malloc(sizeof(void*) + TRAMP_TOTAL_LENGTH
    + TRAMP_ALIGN-1);
    (gdb) s
    395 if (!room)
    (gdb) s
    397 function = (char*)(((long)room + sizeof(void*) + TRAMP_ALIGN-1) &
    -TRAMP_ALIGN);
    (gdb) s
    398 ((char**)function)[-1] = room; /* backpointer for free_trampoline()
    */
    (gdb) s
    403 data = function + TRAMP_LENGTH;
    (gdb) print function
    $4 = 0x300100 ""
    (gdb) s
    420 *(short *) (function + 0) = 0x6859;
    (gdb) s
    421 *(long *) (function + 2) = (long) data;
    (gdb) s
    422 *(short *) (function + 6) = 0xE951;
    (gdb) s
    423 *(long *) (function + 8) = (long) address - (long) (function + 12);
    (gdb) s
    424 *(long *) (function +12) = 0x90909090; /* nop nop nop nop, for
    alignment */
    (gdb) s
    1061 *(long *) (data + 0*sizeof(void*)) = (long) data0;
    (gdb) s
    1062 *(long *) (data + 1*sizeof(void*)) = (long) data1;
    (gdb) s
    1193 }
    (gdb) print function
    $5 = 0x300100 "Yh\020\0010"
    (gdb) s
    main () at ./test1.c:98
    98 if ((*cf)(MAGIC4) == MAGIC1+MAGIC2+MAGIC3+MAGIC4)
    (gdb) print cf
    $6 = (function) 0x300100
    (gdb) s
    102 { free_trampoline_r(cf); printf("Works, test1 passed.\n"); exit(0); }
    (gdb) s
    free_trampoline_r (function=0x300100) at ./trampoline.c:1204
    1204 free(((char**)function)[-1]);
    (gdb) s
    1206 }
    (gdb) s
    free_trampoline_r (function=0x1) at ./trampoline.c:1204
    1204 free(((char**)function)[-1]);
    (gdb) s
    0x00003033 in dyld_stub_free ()
    (gdb) s
    Single stepping until exit from function dyld_stub_free,
    which has no line number information.
    0x8fe13900 in __dyld_fast_stub_binding_helper_interface ()
    (gdb) s
    Single stepping until exit from function
    __dyld_fast_stub_binding_helper_interface,
    which has no line number information.
    0x8fe13902 in __dyld_stub_binding_helper_interface ()
    (gdb) s
    Single stepping until exit from function __dyld_stub_binding_helper_interface,
    which has no line number information.

    Program received signal EXC_BAD_INSTRUCTION, Illegal instruction/operand.
    0x8fe13914 in __dyld_stub_binding_helper_interface ()
    (gdb)

    So, freeing the space allocated eariler and coerced into being a function
    pointer is causing an illegal instruction to be executed. Odd.

    I also tested out putting a printout of where the pointer was just before it's
    freed, and got the same execution path until the print:

    1204 printf("%p\n", (char**)function);
    (gdb) s

    Program received signal EXC_BAD_INSTRUCTION, Illegal instruction/operand.
    0x8fe13914 in __dyld_stub_binding_helper_interface ()
    (gdb) s
    Single stepping until exit from function __dyld_stub_binding_helper_interface,

    It looks like the dynamic linker in OS X is getting confused about what it's
    being passed. Thoughts? Other tests to run to try to pin this down?

     
  • Logged In: YES
    user_id=346809
    Originator: NO

    The problem here is an unaligned vector store:

    (gdb) disass
    Dump of assembler code for function __dyld_stub_binding_helper_interface:
    0x8fe12f72 <__dyld_stub_binding_helper_interface+0>: push %ebp
    0x8fe12f73 <__dyld_stub_binding_helper_interface+1>: mov %esp,%ebp
    0x8fe12f75 <__dyld_stub_binding_helper_interface+3>: sub $0x60,%esp
    0x8fe12f78 <__dyld_stub_binding_helper_interface+6>: mov %eax,8(%esp)
    0x8fe12f7c <__dyld_stub_binding_helper_interface+10>: mov %ecx,12(%esp)
    0x8fe12f80 <__dyld_stub_binding_helper_interface+14>: mov %edx,16(%esp)
    0x8fe12f84 <__dyld_stub_binding_helper_interface+18>: movdqa %xmm0,32(%esp)
    0x8fe12f8a <__dyld_stub_binding_helper_interface+24>: movdqa %xmm1,48(%esp)
    0x8fe12f90 <__dyld_stub_binding_helper_interface+30>: movdqa %xmm2,64(%esp)
    0x8fe12f96 <__dyld_stub_binding_helper_interface+36>: movdqa %xmm3,80(%esp)
    0x8fe12f9c <__dyld_stub_binding_helper_interface+42>: mov 4(%ebp),%eax
    0x8fe12f9f <__dyld_stub_binding_helper_interface+45>: mov %eax,0(%esp)
    0x8fe12fa3 <__dyld_stub_binding_helper_interface+49>: mov 8(%ebp),%eax
    0x8fe12fa6 <__dyld_stub_binding_helper_interface+52>: mov %eax,4(%esp)
    0x8fe12faa <__dyld_stub_binding_helper_interface+56>: call 0x8fe05276 <__dyld__ZN4dyld14bindLazySymbolEPK11mach_headerPm>
    0x8fe12faf <__dyld_stub_binding_helper_interface+61>: mov %eax,8(%ebp)
    0x8fe12fb2 <__dyld_stub_binding_helper_interface+64>: movdqa 32(%esp),%xmm0
    0x8fe12fb8 <__dyld_stub_binding_helper_interface+70>: movdqa 48(%esp),%xmm1
    0x8fe12fbe <__dyld_stub_binding_helper_interface+76>: movdqa 64(%esp),%xmm2
    0x8fe12fc4 <__dyld_stub_binding_helper_interface+82>: movdqa 80(%esp),%xmm3
    0x8fe12fca <__dyld_stub_binding_helper_interface+88>: mov 8(%esp),%eax
    0x8fe12fce <__dyld_stub_binding_helper_interface+92>: mov 12(%esp),%ecx
    0x8fe12fd2 <__dyld_stub_binding_helper_interface+96>: mov 16(%esp),%edx
    0x8fe12fd6 <__dyld_stub_binding_helper_interface+100>: add $0x60,%esp
    0x8fe12fd9 <__dyld_stub_binding_helper_interface+103>: pop %ebp
    0x8fe12fda <__dyld_stub_binding_helper_interface+104>: add $0x4,%esp
    0x8fe12fdd <__dyld_stub_binding_helper_interface+107>: ret
    0x8fe12fde <__dyld_stub_binding_helper_interface+108>: add %al,(%eax)
    End of assembler dump.
    (gdb) p/x $pc
    $1 = 0x8fe12f84
    (gdb) p/x $esp
    $2 = 0xbffff56c
    (gdb)

    The OS X ABI requires the stack to be aligned to 16 bytes.

     
  • Logged In: YES
    user_id=346809
    Originator: NO

    Some more hints:

    $esp gets messed after returning from the trampoline, and the offending instruction is inside printf.

    (gdb) p/x $esp # After alloc_trampoline_r()
    $4 = 0xbffff5e0
    (gdb) n
    (gdb) p/x $esp # After calling trampoline
    $5 = 0xbffff5dc
    # calling printf will segfault now

     
  • Raph Levien
    Raph Levien
    2007-02-09

    Logged In: YES
    user_id=379
    Originator: NO

    It's failing because Apple made a tweak to the ABI, always expecting the stack pointer to be 16-byte aligned (they use some SSE2 instructions to manipulate the stack inside the dyld helper functions). I've made a patch which pads out the stack with 3 extra 4-byte pushes when adding the env. Hopefully I'll be able to attach it to this bug, if I can figure out the attachment mechanism.

     
  • Raph Levien
    Raph Levien
    2007-02-09

    Logged In: YES
    user_id=379
    Originator: NO

    Ok, I can't find the attach button, so I'll just post it inline. However, this patch still doesn't bring joy to the entire compile - it fixes test1 and test2, but not the minitest, which exercises the vacall mechanism. I think there are nontrivial changes needed to that function as well.

    *** dist-trampoline.c Thu Feb 8 22:13:51 2007
    --- trampoline.c Fri Feb 9 00:38:51 2007
    ***************
    *** 235,239 ****
    --- 235,243 ----
    #ifdef __i386__
    #ifdef __APPLE__
    + /* Must align esp to 16-byte boundaries, so use longer sequence */
    + #define TRAMP_LENGTH 32
    + #else
    #define TRAMP_LENGTH 16
    + #endif
    #define TRAMP_ALIGN 16 /* 4 for a i386, 16 for a i486 */
    #endif
    ***************
    *** 408,411 ****
    --- 412,449 ----
    */
    #ifdef __i386__
    + #if TRAMP_LENGTH == 32
    + /* function:
    + * popl %ecx 59
    + * mov (%esp), %edx 8b 14 24
    + * and $0xfffffff0, %esp 83 e4 f0
    + * sub $8, %esp 83 ec 08
    + * pushl %edx 52
    + * pushl $<data> 68 <data>
    + * pushl %ecx 51
    + * jmp <address> E9 <address>-<here>
    + * here:
    + * nop 90
    + * nop 90
    + * nop 90
    + * nop 90
    + */
    + *(long *) (function + 0) = 0x24148b59;
    + *(long *) (function + 4) = 0x83f0e483;
    + *(long *) (function + 8) = 0x685208ec;
    + *(long *) (function + 12) = (long) data;
    + *(short *) (function + 16) = 0xE951;
    + *(long *) (function + 18) = (long) address - (long) (function + 22);
    + *(short *) (function + 22) = 0x90909090; /* nop nop nop nop, for alignment */
    + *(long *) (function + 24) = 0x90909090; /* nop nop nop nop, for alignment */
    + *(long *) (function + 28) = 0x90909090; /* nop nop nop nop, for alignment */
    + #define is_tramp(function) \ + *(long *) (function + 0) == 0x24148b59 && \ + *(long *) (function + 4) == 0x83f0e483 && \ + *(unsigned short *) (function + 16) == 0xE951
    + #define tramp_address(function) \ + *(long *) (function + 18) + (long) (function + 22)
    + #define tramp_data(function) \ + *(long *) (function + 12)
    + #else
    /* function:
    * popl %ecx 59
    ***************
    *** 442,445 ****
    --- 480,484 ----
    *(long *) (function + 2)
    #endif
    + #endif
    #ifdef __m68k__
    #ifdef __NetBSD__

     
  • Logged In: YES
    user_id=1977958
    Originator: NO

    The following modifications fix the minitests in ffcall/callback on MAC OS on i386.
    With this patch clisp compiles on a Macbook Pro (intel core duo) providing :ffi and passing all ffi tests.

    It seems that the arguments have an offset by 12 bytes after the modification of trampoline_r where 12 extra bytes are pushed on the stack, whereas for the modification in vacall-i386-macro.S I do not know why the last "ret $4" instruction has to be replaced by "ret".

    =======================================
    diff clisp-2.40/ffcall/callback/vacall_r/vacall_r.h.in clisp-2.40-mod/ffcall/callback/vacall_r/vacall_r.h.in

    408a409,415
    > #if defined(__i386__) && defined (__APPLE__)
    > #define __va_start(LIST,RETTYPE) \ > ((LIST)->flags = __VA_START_FLAGS, \ > (LIST)->rtype = (RETTYPE), \ > (LIST)->aptr += 12 \ > )
    > #else
    412a420
    > #endif

    =========================================
    diff clisp-2.40/ffcall/callback/vacall_r/vacall-i386-macro.S
    clisp-2.40-mod/ffcall/callback/vacall_r/vacall-i386-macro.S

    127a128
    > #ifndef __APPLE__
    129a131
    > #endif

     
  • Sam Steingold
    Sam Steingold
    2008-01-22

    • status: open --> closed-out-of-date
     
  • Sam Steingold
    Sam Steingold
    2008-01-22

    Logged In: YES
    user_id=5735
    Originator: NO

    moved to ffcall savannah
    https://savannah.gnu.org/bugs/index.php?22084
    please attach your patch there,
    as a unified diff (diff -u)
    thanks!