Home > Net >  Is it safe to pass a uint64_t containing a 32-bit value to an external function whose parameter is a
Is it safe to pass a uint64_t containing a 32-bit value to an external function whose parameter is a

Time:05-04

I'm working on a cross-platform program that calls a function from a dynamic library with C linkage. I need to support multiple versions of this dynamic library, but between two of the versions I need to support, there is a function parameter that has changed from uint32_t to uint64_t.

If I pass this function a uint64_t that contains value which is still representable as a uint32_t, is that safe to do even when the function's parameter is actually a uint32_t?

Put more specifically:

If the source of the function as compiled into the dynamic library is:

extern "C" void foo(uint32_t param) {
    ...
}

Is it safe for me to use the function like so:

extern "C" void foo(uint64_t);

uint32_t value32 = 10; // Ensure value can be represented by uint32_t
uint64_t value64 = value32;
foo(value64);

If yes, is it safe to do this across different platforms? This program of mine supports 32-bit and 64-bit Windows (compiled as x86 for both), x86_64 macOS, arm64 macOS, x86 Linux, and x86_64 Linux.

CodePudding user response:

No, this is illegal. C 20 [basic.link] p11:

After all adjustments of types (during which typedefs (9.2.3) are replaced by their definitions), the types specified by all declarations referring to a given variable or function shall be identical.

Moreover, it will actually fail on 32-bit x86 systems using the usual stack-based calling conventions. The function defined with uint32_t will be looking for one dword on the stack, but two will have been pushed. Any arguments above it will then be in the wrong place. It's even worse with the stdcall convention, in which the called function pops its own arguments; it will pop the wrong amount, unbalance the stack, and cause all sorts of mayhem after returning.

CodePudding user response:

I'm sure someone will pipe in with some contrived example of a system where this won't work, but on Intel systems remember that larger registers are built on top of smaller ones (rax is built over eax, which is built over ax and al), and so what you're trying to do will work.

  • Related