cffi.FFI.cdef and implicit function declarations

12 views
Skip to first unread message

Florian Weimer

unread,
Feb 8, 2023, 5:10:43 AM2/8/23
to pytho...@googlegroups.com, c-std-...@lists.linux.dev
We are trying to build Fedora with a compiler that does not support
implicit function declarations:

<https://fedoraproject.org/wiki/Changes/PortingToModernC>
<https://fedoraproject.org/wiki/Toolchain/PortingToModernC>

Gentoo is doing something simlar:

<https://wiki.gentoo.org/wiki/Modern_C_porting

While investigating a false positive from our tester triggered by the
python-cffi testsuite, I noticed a curiousity with cdef: it does not
inject the specified prototype into the generated C stub. This means
that unless Python.h happens to include a header with a function
declaration, an implicit declaration is the result.

For example, this snippet:


import cffi

ffi = cffi.FFI()
ffi.cdef("int puts(const char *);")
ffi.cdef("const char *getenv(const char *);")
ffi.cdef("const char *gnu_get_libc_release(void);")
lib = ffi.verify()
lib.puts(lib.getenv(b"TERM"))
lib.puts(lib.gnu_get_libc_release())


prints this on x86 32-bit:


vt100
stable


But the call to gnu_get_libc_release() crashes on x86-64 64-bit because
the implicit declaration has return type int, and the pointer is clipped
to 32 bits.

This not an issue if the function returns int, though. And to support
implicit declarations, many POSIX interfaces return int anyway, so it
just works, although and ugly warning is printed on first use.

This will stop working once the compiler no longer accepts such implicit
function declarations. How common is such use of cdef? Do we need to
worry about it? Is there something we need to do as part of the new
compiler rollout?

Thanks,
Florian

Armin Rigo

unread,
Feb 8, 2023, 7:13:21 AM2/8/23
to pytho...@googlegroups.com, c-std-...@lists.linux.dev
Hi Florian,

On Wed, 8 Feb 2023 at 11:10, Florian Weimer <fwe...@redhat.com> wrote:
> For example, this snippet:
>
> “
> import cffi
>
> ffi = cffi.FFI()
> ffi.cdef("int puts(const char *);")
> ffi.cdef("const char *getenv(const char *);")
> ffi.cdef("const char *gnu_get_libc_release(void);")
> lib = ffi.verify()
> lib.puts(lib.getenv(b"TERM"))
> lib.puts(lib.gnu_get_libc_release())

Yes, it's expected not to work. First, the ffi.verify() should not be
used any more and has been deprecated for many, many years. But if
you want to use it anyway, then: ffi.verify() must be called with an
argument which is the extra piece of C source code, and that *must*
make the declared functions available to the C code. A typical usage
is to say ffi.verify('#include <something.h>'). If you are getting an
"implicit function" warning from the C compiler, then this was not
done correctly.

A bientôt,
Armin Rigo

Armin Rigo

unread,
Feb 8, 2023, 7:18:52 AM2/8/23
to pytho...@googlegroups.com, c-std-...@lists.linux.dev
Hi again,

On Wed, 8 Feb 2023 at 13:12, Armin Rigo <armin...@gmail.com> wrote:
> make the declared functions available to the C code. A typical usage
> is to say ffi.verify('#include <something.h>'). If you are getting an
> "implicit function" warning from the C compiler, then this was not
> done correctly.

...In other words, if changes to the C compiler replaces these
warnings with errors, and this breaks old existing code, then that old
code was broken in the first place for relying on that feature. I
don't think there is something to say about that specifically for
CFFI's old verify() API. I think the problem is the same as people will
encounter with old, manually-written C code which was broken the same way.
The fix is to add the missing #include in both cases. For ffi.verify()
it is added in the first argument, which is copied into the generated C code.



Armin Rigo

Florian Weimer

unread,
Feb 8, 2023, 8:57:18 AM2/8/23
to Armin Rigo, pytho...@googlegroups.com, c-std-...@lists.linux.dev
* Armin Rigo:
Okay, this is good to know. Thank you for taking the time to explain
it.

Florian

Reply all
Reply to author
Forward
0 new messages