Skip to content

Fix relocation addend sign extension on 32-bit platforms#4846

Open
sumleo wants to merge 3 commits intobytecodealliance:mainfrom
sumleo:fix/relocation-addend-sign-extension
Open

Fix relocation addend sign extension on 32-bit platforms#4846
sumleo wants to merge 3 commits intobytecodealliance:mainfrom
sumleo:fix/relocation-addend-sign-extension

Conversation

@sumleo
Copy link
Contributor

@sumleo sumleo commented Feb 25, 2026

Summary

On 32-bit platforms, load_relocation_section reads the relocation addend as uint32 and zero-extends it to uint64. This corrupts negative addends — for example, an addend of -4 (0xFFFFFFFC as a 32-bit value) becomes 4294967292 instead of remaining -4 (0xFFFFFFFFFFFFFFFC as a 64-bit value).

The Windows code path (around line 3616) already handles this correctly with proper sign extension.

Changes

  • Added a (int64)(int32) double cast when widening addend32 to 64 bits, so negative values are sign-extended rather than zero-extended
  • addend32 remains uint32 as expected by read_uint32()
  • The offset32 variable remains uint32 since relocation offsets are unsigned

Test plan

  • Verified that the fix matches the existing Windows code path behavior
  • The change only affects 32-bit platforms (sizeof(void *) != 8)
  • Negative relocation addends (e.g., used for GOT-relative relocations) will now be preserved correctly

When loading relocations on 32-bit platforms, the addend is read
as uint32 and zero-extended to uint64, which corrupts negative
addends. For example, -4 (0xFFFFFFFC) becomes 4294967292 instead
of remaining -4. Use int32 with sign extension to int64, matching
the Windows code path which already handles this correctly.
else {
uint32 offset32, addend32;
uint32 offset32;
int32 addend32;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

L3873 read_uint32() expects addend32 as an unsigned. No need to change its type to int32.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Reverted addend32 back to uint32 and now sign-extend via a double cast (int64)(int32)addend32 instead. This keeps read_uint32() happy while still preserving proper sign extension for negative addends on 32-bit platforms.

relocation->relocation_offset = (uint64)offset32;
read_uint32(buf, buf_end, addend32);
relocation->relocation_addend = (uint64)addend32;
relocation->relocation_addend = (int64)addend32;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. there is a risk when casting uint64 to int64. But personally, I am curious that is it reported by a compiler or analysis tool?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was found during code review. On 32-bit platforms, the relocation addend is read as a uint32 and then cast to uint64. If the addend is negative (e.g., -4 stored as 0xFFFFFFFC), the unsigned widening produces 0x00000000FFFFFFFC instead of the correct 0xFFFFFFFFFFFFFFFC. The fix now uses (int64)(int32)addend32 to properly sign-extend while keeping addend32 as uint32 for read_uint32().

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants