Summary
On UC_ARCH_TRICORE, ld.bu (load byte unsigned) zero-extends correctly in the plain base form
ld.bu d, [a], but sign-extends in the base+offset increment form ld.bu d, [+a]off. A .bu load
must always zero-extend.
Environment
- Unicorn 2.1.4 (also reproduced on earlier 2.x), Python bindings, Windows x64.
UC_ARCH_TRICORE,
UC_MODE_LITTLE_ENDIAN.
Minimal reproduction
from unicorn import *
import unicorn.tricore_const as t
uc = Uc(UC_ARCH_TRICORE, UC_MODE_LITTLE_ENDIAN)
uc.mem_map(0x80000000, 0x200000) # code
uc.mem_map(0xC0000000, 0x200000) # data
# correct case: plain base form ld.bu d0,[a15] (bytes 14 f0)
uc.mem_write(0x80000000, bytes.fromhex("14f0"))
uc.mem_write(0xC0000000, bytes([0x80]))
uc.reg_write(t.UC_TRICORE_REG_A15, 0xC0000000)
uc.emu_start(0x80000000, 0x80000002, count=1)
print("ld.bu d0,[a15] -> 0x%08X" % uc.reg_read(t.UC_TRICORE_REG_D0)) # 0x00000080 (correct)
# buggy case: base+offset pre-increment ld.bu d15,[+a2]0x1 (bytes 09 2f 41 04)
uc = Uc(UC_ARCH_TRICORE, UC_MODE_LITTLE_ENDIAN)
uc.mem_map(0x80000000, 0x200000); uc.mem_map(0xC0000000, 0x200000)
uc.mem_write(0x80000000, bytes.fromhex("092f4104"))
uc.mem_write(0xC0000001, bytes([0xBC]))
uc.reg_write(t.UC_TRICORE_REG_A2, 0xC0000000)
uc.emu_start(0x80000000, 0x80000004, count=1)
print("ld.bu d15,[+a2]0x1 -> 0x%08X" % uc.reg_read(t.UC_TRICORE_REG_D15)) # 0xFFFFFFBC (WRONG)
Expected vs actual
ld.bu d0,[a15] -> 0x00000080 (correct, zero-extended)
ld.bu d15,[+a2]0x1 -> 0x000000BC expected
-> 0xFFFFFFBC actual (BUG: sign-extended)
Likely root cause
In the vendored QEMU TriCore translate.c, the BO (base+offset, with pre/post-increment) decode for
LD.BU appears to use a sign-extending memory op (MO_SB) instead of the unsigned MO_UB used by the
plain-base path. The plain [a] form is correct; only the [+a]off / [a+]off increment forms are
affected.
Impact
Silently corrupts byte-load + combine code (LFSR/stream ciphers, checksum loops, table walks). Found
while reverse-engineering automotive TriCore (TC1796) firmware: a 48-bit shift register used
ld.bu [+a]1; sha #-7 for the inter-byte carry, and the sign-extended load flipped the carry to 0xFF,
corrupting the output. Verified against Ghidra-PCode (which is correct).
Summary
On
UC_ARCH_TRICORE,ld.bu(load byte unsigned) zero-extends correctly in the plain base formld.bu d, [a], but sign-extends in the base+offset increment formld.bu d, [+a]off. A.buloadmust always zero-extend.
Environment
UC_ARCH_TRICORE,UC_MODE_LITTLE_ENDIAN.Minimal reproduction
Expected vs actual
Likely root cause
In the vendored QEMU TriCore
translate.c, the BO (base+offset, with pre/post-increment) decode forLD.BUappears to use a sign-extending memory op (MO_SB) instead of the unsignedMO_UBused by theplain-base path. The plain
[a]form is correct; only the[+a]off/[a+]offincrement forms areaffected.
Impact
Silently corrupts byte-load + combine code (LFSR/stream ciphers, checksum loops, table walks). Found
while reverse-engineering automotive TriCore (TC1796) firmware: a 48-bit shift register used
ld.bu [+a]1; sha #-7for the inter-byte carry, and the sign-extended load flipped the carry to 0xFF,corrupting the output. Verified against Ghidra-PCode (which is correct).