Normally, when correctly configured, the pseudo relocations should be in fields that are large enough to hold the full target offset/address. But if the relocations nevertheless end up truncated, error out clearly instead of running into a hard to diagnose crash at runtime.
The pseudo relocations can be applied both on absolute pointers and relative offsets, so when writing a N bit number, we don't know if the limits for it are unsigned or signed. Thus carefully allow values from -(2^(N-1)) to (2^N)-1, covering the full range for both signed and unsigned N bit numbers. This won't catch all cases where offsets are out of bounds, but should catch the vast majority, allowing a clearer error message in those situations. By default, GCC builds for x86_64 with the medium code model, which adds .refptr stubs when referencing addresses that might end up autoimported (i.e. when referencing addresses that can be out of range for a 32 bit offset). Some users, who don't expect to be autoimporting any data symbols, might be building with -mcmodel=small [1], which avoids this extra indirection - but which then silently breaks things if actually ending up autoimporting data symbols from another DLL. This can also happen if calling a function which is marked "DATA" in the def files as it's not meant to be called/used normally (because we provide a replacement in libmingwex or lib*crt* that we think should be used instead). If the function that is meant to be called is missing (this can happen in misconfigured builds where the libraries are lacking symbols that we expect to provide, see [2]), the linker can end up doing an autoimport of the function into a 32 bit RIP-relative offset. (This only happens with Clang; GCC creates a .refptr stub for the function in these cases, while Clang expects such stubs not to be needed for functions, only for data.) [1] https://code.videolan.org/videolan/dav1d/-/commit/8f7af99687533d15a9b5d16abc7b9d7b0cd4dcd0 [2] https://github.com/ziglang/zig/issues/9845 Signed-off-by: Martin Storsjö <[email protected]> --- mingw-w64-crt/crt/pseudo-reloc.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/mingw-w64-crt/crt/pseudo-reloc.c b/mingw-w64-crt/crt/pseudo-reloc.c index feab8720f..2452dff5a 100644 --- a/mingw-w64-crt/crt/pseudo-reloc.c +++ b/mingw-w64-crt/crt/pseudo-reloc.c @@ -311,6 +311,7 @@ do_pseudo_reloc (void * start, void * end, void * base) ptrdiff_t reloc_target = (ptrdiff_t) ((char *)end - (char*)start); runtime_pseudo_reloc_v2 *v2_hdr = (runtime_pseudo_reloc_v2 *) start; runtime_pseudo_reloc_item_v2 *r; + unsigned int bits; /* A valid relocation list will contain at least one entry, and * one v1 data structure (the smallest one) requires two DWORDs. @@ -440,6 +441,23 @@ do_pseudo_reloc (void * start, void * end, void * base) reldata -= ((ptrdiff_t) base + r->sym); reldata += addr_imp; + bits = r->flags & 0xff; + if (bits < sizeof(ptrdiff_t)*8) + { + /* Check for overflows. We don't know if the target address is + * interpreted as a relative offset or as a truncated absolute + * address - to avoid false positives, allow offsets within the + * whole range of signed and unsigned N bits numbers, but error + * out for anything outside of that. Thus for relative offsets, + * this won't catch offsets that are only barely too large. */ + ptrdiff_t max_unsigned = (1LL << bits) - 1; + ptrdiff_t min_signed = (~(ptrdiff_t)0) << (bits - 1); + if (reldata > max_unsigned || reldata < min_signed) + __report_error ("%d bit pseudo relocation at %p out of range, " + "targeting %p, yielding the value %p.\n", + bits, reloc_target, addr_imp, reldata); + } + /* Write the new relocation value back to *reloc_target */ switch ((r->flags & 0xff)) { -- 2.25.1 _______________________________________________ Mingw-w64-public mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/mingw-w64-public
