test_sin_no_return_value violates calling convention on x87

Issue #382 resolved
Michael Hudson-Doyle
created an issue

Hi,

As you can see here https://launchpadlibrarian.net/385898413/buildlog_ubuntu-cosmic-i386.python-cffi_1.11.5-2_BUILDING.txt.gz python-cffi fails tests in the development version of Ubuntu, like this:

    def test_dlopen_filename(self):
        path = ctypes.util.find_library(lib_m)
        if not path:
            py.test.skip("%s not found" % lib_m)
        ffi = FFI(backend=self.Backend())
        ffi.cdef("""
                double cos(double x);
            """)
        m = ffi.dlopen(path)
        x = m.cos(1.23)
>       assert x == math.cos(1.23)
E       assert nan == 0.3342377271245026
E        +  where 0.3342377271245026 = <built-in function cos>(1.23)
E        +    where <built-in function cos> = math.cos

What's going on here is that this runs after test_sin_no_return_value and that test lies about the return value of sin. This means that the x87 stack is not cleaned up after sin returns, in violation of the calling convention, and now for some reason the cos function in glibc fails if it is called with an unclean x87 stack.

I'm not sure how this can be fixed -- always clean the x87 stack after function return? -- but it seems a niche feature to me and perhaps the test can just be skipped on ia32 until x87 can be forgotten about entirely.

Comments (5)

  1. Adam Conrad

    The above commit fails with the following:

    =================================== FAILURES ===================================
    _____________________ TestFFI.test_getenv_no_return_value ______________________
    
    self = <testing.cffi0.test_ffi_backend.TestFFI object at 0x7fc9b122a470>
    
        def test_getenv_no_return_value(self):
            # check that 'void'-returning functions work too
            ffi = FFI(backend=self.Backend())
            ffi.cdef("""
                    void getenv(char *);
                """)
            needs_dlopen_none()
            m = ffi.dlopen(None)
    >       x = m.getenv("FOO")
    E       TypeError: initializer for ctype 'char *' must be a bytes or list or tuple, not str
    
    testing/cffi0/test_function.py:56: TypeError
    ___________________ TestFunction.test_getenv_no_return_value ___________________
    
    self = <testing.cffi0.test_function.TestFunction object at 0x7fc9b09333c8>
    
        def test_getenv_no_return_value(self):
            # check that 'void'-returning functions work too
            ffi = FFI(backend=self.Backend())
            ffi.cdef("""
                    void getenv(char *);
                """)
            needs_dlopen_none()
            m = ffi.dlopen(None)
    >       x = m.getenv("FOO")
    
    testing/cffi0/test_function.py:56: 
    _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
    cffi/backend_ctypes.py:924: in __call__
        ctypes_args.append(BArg._arg_to_ctypes(arg))
    cffi/backend_ctypes.py:567: in _arg_to_ctypes
        return super(CTypesPtr, cls)._arg_to_ctypes(*value)
    cffi/backend_ctypes.py:39: in _arg_to_ctypes
        res = cls._to_ctypes(*value)
    _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
    
    cls = <class 'ffi.CData<char *>'>, value = 'FOO'
    
        @classmethod
        def _to_ctypes(cls, value):
            if not isinstance(value, CTypesData):
    >           raise TypeError("unexpected %s object" % type(value).__name__)
    E           TypeError: unexpected str object
    
    cffi/backend_ctypes.py:216: TypeError
    
  2. Adam Conrad

    I opted for a much simpler fix that retains the spirit of the original test (forcing a non-void function from libm to a void return) and only touches 3 lines by just switching to lround, which returns int instead of float:

    --- python-cffi-1.11.5.orig/testing/cffi0/test_function.py
    +++ python-cffi-1.11.5/testing/cffi0/test_function.py
    @@ -45,14 +45,14 @@ class TestFunction(object):
             assert x != math.sin(1.23)    # rounding effects
             assert abs(x - math.sin(1.23)) < 1E-6
    
    -    def test_sin_no_return_value(self):
    +    def test_lround_no_return_value(self):
             # check that 'void'-returning functions work too
             ffi = FFI(backend=self.Backend())
             ffi.cdef("""
    -            void sin(double x);
    +            void lround(double x);
             """)
             m = ffi.dlopen(lib_m)
    -        x = m.sin(1.23)
    +        x = m.lround(1.23)
             assert x is None
    
         def test_dlopen_filename(self):
    

    This works great here.

  3. Armin Rigo

    My version fails because I didn't test it on CPython 3.x; it's as easy as saying getenv(b"FOO") instead of getenv("FOO"). But I agree that yours is a more minimal change. Thanks, accepted your version in 7a76a3815340 !

  4. Log in to comment