libffi / libffi

A portable foreign-function interface library.
http://sourceware.org/libffi
Other
3.17k stars 695 forks source link

Request for an ffi_get_trampoline_size() function #10

Open alexrp opened 12 years ago

alexrp commented 12 years ago

In trying to maintain a libffi binding for the D programming language, we have to do really fragile attempts at mirroring configuration detection in libffi. This has turned out to fail several times (human parsing of C preprocessor forests can be rather hard!). Would it be feasible to add an ffi_get_trampoline_size() (or so) function that just returns FFI_TRAMPOLINE_SIZE?

alexrp commented 12 years ago

Additionally, since FFI_TRAMPOLINE_SIZE is a macro and may change between libffi versions, keeping ABI compatibility is virtually impossible.

atgreen commented 12 years ago

On 2/23/2012 10:58 AM, Alex Rønne Petersen wrote:

In trying to maintain a libffi binding for the D programming language, we have to do really fragile attempts at mirroring configuration detection in libffi. This has turned out to fail several times (human parsing of C preprocessor forests can be rather hard!). Would it be feasible to add an ffi_get_trampoline_size() (or so) function that just returns FFI_TRAMPOLINE_SIZE?


Reply to this email directly or view it on GitHub: https://github.com/atgreen/libffi/issues/10

Hmm... I'd rather not add a new function for this. Is there some other way we can pass the info to you?

For instance, your build machinery could include a program like this...

include

include

int main() { printf ("%d\n", FFI_TRAMPOLINE_SIZE); return 0; }

..and compile like so: $ gcc -o trampsize trampsize.c pkg-config --cflags libffi

..and then somehow include the output of trampsize in a source file for the D runtime. Would that work?

AG

alexrp commented 12 years ago

Well, doing something like that works fine if you're compiling for the host only; it doesn't work if you're cross-compiling. The reason having a function in libffi to retrieve the size would be a Good Thing is two-fold: Cross-compilation is seamless, and libffi maintains ABI compatibility across versions.

As it stands, all programs compiled against some libffi version will break when you alter the FFI_TRAMPOLINE_SIZE macro; there's no ABI compatibility here at all, and there really should be.

Come to think of it, the tramp field in ffi_closure is the very first field... This isn't a problem from libffi's point of view, since as long as the consuming application over-commits the size of the allocated struct, things will Just Work. However, if the consuming application actually tries to access fields after the tramp field, all hell will break loose.

The way I see it, the only way to fix this situation is to:

The latter point means that you can alter FFI_TRAMPOLINE_SIZE at any point, to any value, without breaking ABI compatibility - provided, of course, that applications actually use the ffi_get_trampoline_size() function (but even if they don't, nothing will break more than FFI_TRAMPOLINE_SIZE changes in libffi already break things).

An alternative is of course to just set FFI_TRAMPOLINE_SIZE to a specific value for all platforms. This would be even easier to work with, and if some sensible value like 128 is chosen, it likely wouldn't have to change again, ever.

As an example, the change you did in 8360bf1cd0aba8db5582266da70467de7e89a57a pretty much broke ABI compatibility with any Windows application that uses libffi's closure API. This is the kind of stuff we really, really need to avoid; you can't reasonably expect everyone to recompile their programs - they may not even be aware of the change at all (in an ideal world, everyone would read change logs, but...).

atgreen commented 12 years ago

On 2/23/2012 2:27 PM, Alex Rønne Petersen wrote:

Well, doing something like that works fine if you're compiling for the host only; it doesn't work if you're cross-compiling. The reason having a function in libffi to retrieve the size would be a Good Thing (TM) is two-fold: Cross-compilation is seamless, and libffi maintains ABI compatibility across versions.

As it stands, all programs compiled against some libffi version will break when you alter the FFI_TRAMPOLINE_SIZE macro; there's no ABI compatibility here at all, and there really should be.

Come to think of it, the tramp field in ffi_closure is the very first field... This isn't a problem from libffi's point of view, since as long as the consuming application over-commits the size of the allocated struct, things will just work. However, if the consuming application actually tries to access fields after the tramp field, all hell will break loose. :/

The way I see it, the only way to fix this situation is to:

  • Expose an ffi_get_trampoline_size() function that consuming code uses when allocating.
  • Move the tramp field so that it is the last field in the ffi_closure struct.

The latter point means that you can alter FFI_TRAMPOLINE_SIZE at any point, to any value, without breaking ABI compatibility - provided, of course, that applications actually use the ffi_get_trampoline_size() function (but even if they don't, nothing will break more than FFI_TRAMPOLINE_SIZE changes in libffi already break things).

I was going to suggest a way to extract the FFI_TRAMPOLINE_SIZE from libffi.a (perhaps by stuffing it in a special section), but you raise an interesting point about ABI compatibility. Let me think about this.
I'll try to write something up for the libffi-discuss mailing list as well.

AG


Reply to this email directly or view it on GitHub: https://github.com/atgreen/libffi/issues/10#issuecomment-4143256

alexrp commented 12 years ago

Any news on this?

atgreen commented 12 years ago

I've had a chance to think about this some more.

It's convenient to have tramp at the start of the closure because otherwise the callers would have to know where in structure to jump to.

Can you point me at the D code that is using FFI_TRAMPOLINE_SIZE. I still need help understanding this issue.

Thanks!

alexrp commented 12 years ago

We no longer use it because of the related ABI issues (we over-commit its size instead). But: https://github.com/lycus/libffi-d/blob/master/ffi.d#L188

Consider this situation: libffi version A requires a trampoline size of 4. An application compiles against this version and works fine. Now the system's libffi is updated to version B. This version requires a trampoline size of 8. The application is now binary incompatible with libffi (and will most likely receive seg faults due to libffi reading beyond allocated memory).

alexrp commented 12 years ago

@atgreen It's been a while, what's the status of this issue?

atgreen commented 10 years ago

@alexrp, this scenario finally came home to bite us. See bug #113 .