Skip to content

TriCore: ld.bu with base+offset pre/post-increment addressing sign-extends the byte (should zero-extend) #2348

Description

@ionutkep

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).

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions