VMS actually attacked this problem. The VAX Procedure Calling standard
not only specifies how registers are to be used, how the call is to be
made (with much of the stuff enforced by the hardware), how values are to
be returned, and such, but it even includes standard data representations
so that you can, for example, look at a descriptor value passed to you and
tell that it's an array of 32-bit floats with FORTRAN-style ordering.
There's even a defined way to represent all this stuff in the object
language, so that a Linker could check calls for type conformance.

In practice, VMS's attack failed. Too many languages specify too much at
too low a level. While there's a general descriptor format for arrays
that includes the index ordering information, it's way to expensive to
even CHECK that at run time, much less generate references based on the
information. In practice, compilers generate open code that assumes
everything is right.

Mainly because many languages play fast and loose with types, the Linker
doesn't bother to check types.

The standard says that the argument list is owned by the caller, so it can
be (and is, in the FORTRAN compiler) re-used. But C programmers assume
they can take the address of an argument, then walk the list - and even
modify it. When a field test version of the C compiler "followed the
rules", so many people complained that the compiler had to get a variance.
As a result, if you call C from FORTRAN you can get into trouble.

It's easier to build multi-language programs under VMS than on any other
system I've ever used, but there are plenty of rough spots - and they've
shown no signs of getting better over the years.

The VAX Procedure Calling Standard, and the CALL instructions it's
inherently based on, have been held up as a primary example of "what's
wrong with CISC". Compilers can do better by generating special-case
code. Unfortunately, this moves you even further from really having a
consistent, safe, multi-language separate compilation facility.