From 0fc2365b57dffc01d194a97d9e11c245e3db5718 Mon Sep 17 00:00:00 2001 From: Anton Kochkov Date: Sat, 4 Jul 2026 06:25:17 +0000 Subject: [PATCH] bin/coff: apply TMS320C6000 (c6x) relocations --- librz/bin/format/coff/coff.h | 3 + librz/bin/format/coff/coff_reloc.c | 201 +++++++++++++++++++++++++++++ librz/bin/format/coff/coff_specs.h | 33 +++++ test/db/formats/coff | 20 +++ 4 files changed, 257 insertions(+) diff --git a/librz/bin/format/coff/coff.h b/librz/bin/format/coff/coff.h index b439e4e65a..d0a0d9c1d6 100644 --- a/librz/bin/format/coff/coff.h +++ b/librz/bin/format/coff/coff.h @@ -52,4 +52,7 @@ RZ_API RZ_BORROW RzBuffer *rz_coff_get_patched_buf(struct rz_bin_coff_obj *bin); #define RZ_COFF_RELOC_TARGET_SIZE 8 +/// on-disk size of a TI COFF2 relocation entry (r_vaddr, r_symndx, r_disp, r_type) +#define RZ_COFF_TI_RELOC_SIZE 12 + #endif /* COFF_H */ diff --git a/librz/bin/format/coff/coff_reloc.c b/librz/bin/format/coff/coff_reloc.c index 5a56e21949..847219eba1 100644 --- a/librz/bin/format/coff/coff_reloc.c +++ b/librz/bin/format/coff/coff_reloc.c @@ -144,10 +144,211 @@ static ut8 handle_arm64_relocs(struct rz_bin_coff_obj *bin, RzBinReloc *reloc, u return 0; } +// Store `value` into the field of the little-endian word at `paddr` that a TI +// C6000 field spec describes: bits [start, start + size) of a `container`-bit +// word, other bits preserved. The patched word is written to patch_buf; returns +// the byte count (container / 8), or 0 if the spec or read is invalid. +static size_t ti_c6000_store_field(struct rz_bin_coff_obj *bin, ut64 paddr, ut32 fldspec, ut64 value, ut8 *patch_buf) { + ut8 start = fldspec & 0xff; + ut8 size = (fldspec >> 8) & 0xff; + ut8 container = (fldspec >> 16) & 0xff; + if (!size || size > container || start + size > container) { + return 0; + } + ut64 mask = size >= 64 ? UT64_MAX : (((ut64)1 << size) - 1); + ut64 field = (value & mask) << start; + ut64 clear = mask << start; + switch (container) { + case 32: { + ut32 word; + if (!rz_buf_read_le32_at(bin->b, paddr, &word)) { + return 0; + } + word = (word & ~(ut32)clear) | (ut32)field; + rz_write_le32(patch_buf, word); + return 4; + } + case 16: { + ut16 hw; + if (!rz_buf_read_le16_at(bin->b, paddr, &hw)) { + return 0; + } + hw = (hw & ~(ut16)clear) | (ut16)field; + rz_write_le16(patch_buf, hw); + return 2; + } + case 8: { + ut8 b; + if (rz_buf_read_at(bin->b, paddr, &b, 1) != 1) { + return 0; + } + patch_buf[0] = (b & ~(ut8)clear) | (ut8)field; + return 1; + } + default: + return 0; + } +} + +// Evaluate the TI TMS320C6000 relocation expressions of every section. Each +// field is patched by a contiguous RPN sequence (operators in coff_specs.h) +// that ends in a store; the running stack is reset at each store so the next +// field starts clean. One RzBinReloc is emitted per store, carrying the field +// width and the symbol the expression referenced. +static void ti_c6000_relocs_foreach(struct rz_bin_coff_obj *bin, RelocsForeachCb cb, void *user) { + size_t i = 0; + CoffScnHdr *scn_hdr = NULL; + rz_vector_enumerate (bin->scn_hdrs, scn_hdr, i) { + if (!scn_hdr->s_nreloc || !bin->scn_va) { + continue; + } + if (scn_hdr->s_relptr > bin->size || + scn_hdr->s_relptr + (ut64)scn_hdr->s_nreloc * RZ_COFF_TI_RELOC_SIZE > bin->size) { + continue; + } + ut64 off = scn_hdr->s_relptr; + ut64 stack[16]; + int sp = 0; + RzBinSymbol *cur_sym = NULL; + for (ut32 j = 0; j < scn_hdr->s_nreloc; j++) { + ut32 r_vaddr, r_symndx; + ut16 r_disp, r_type; + if (!rz_buf_read_le32_offset(bin->b, &off, &r_vaddr) || + !rz_buf_read_le32_offset(bin->b, &off, &r_symndx) || + !rz_buf_read_le16_offset(bin->b, &off, &r_disp) || + !rz_buf_read_le16_offset(bin->b, &off, &r_type)) { + break; + } +#define C6000_BINOP(expr) \ + do { \ + if (sp >= 2) { \ + ut64 b = stack[--sp], a = stack[sp - 1]; \ + stack[sp - 1] = (expr); \ + } \ + } while (0) + switch (r_type) { + case COFF_REL_C6000_PUSH: { + RzBinSymbol *sym = ht_up_find(bin->sym_ht, (ut64)r_symndx, NULL); + if (sp < 16) { + stack[sp++] = sym ? sym->vaddr : 0; + } + if (sym) { + cur_sym = sym; + } + break; + } + case COFF_REL_C6000_PUSHUK: + if (sp < 16) { + stack[sp++] = r_symndx; + } + break; + case COFF_REL_C6000_PUSHSK: + if (sp < 16) { + stack[sp++] = (ut64)(st64)(st32)r_symndx; + } + break; + case COFF_REL_C6000_ADD: C6000_BINOP(a + b); break; + case COFF_REL_C6000_SUB: C6000_BINOP(a - b); break; + case COFF_REL_C6000_MPY: C6000_BINOP(a * b); break; + case COFF_REL_C6000_DIV: C6000_BINOP(b ? a / b : 0); break; + case COFF_REL_C6000_MOD: C6000_BINOP(b ? a % b : 0); break; + case COFF_REL_C6000_SR: C6000_BINOP(b < 64 ? a >> b : 0); break; + case COFF_REL_C6000_ASR: C6000_BINOP(b < 64 ? (ut64)((st64)a >> b) : 0); break; + case COFF_REL_C6000_SL: C6000_BINOP(b < 64 ? a << b : 0); break; + case COFF_REL_C6000_AND: C6000_BINOP(a & b); break; + case COFF_REL_C6000_OR: C6000_BINOP(a | b); break; + case COFF_REL_C6000_XOR: C6000_BINOP(a ^ b); break; + case COFF_REL_C6000_NEG: + if (sp >= 1) { + stack[sp - 1] = (ut64)(-(st64)stack[sp - 1]); + } + break; + case COFF_REL_C6000_NOTB: + if (sp >= 1) { + stack[sp - 1] = ~stack[sp - 1]; + } + break; + case COFF_REL_C6000_USTFLD: + case COFF_REL_C6000_SSTFLD: + case COFF_REL_C6000_XSTFLD: { + if (sp < 1) { + break; + } + ut64 value = stack[--sp]; + ut8 patch_buf[8]; + ut8 container = (r_symndx >> 16) & 0xff; + RzBinReloc reloc = { 0 }; + reloc.paddr = scn_hdr->s_scnptr + r_vaddr; + reloc.vaddr = bin->scn_va[i] + r_vaddr; + reloc.symbol = cur_sym; + reloc.target_vaddr = cur_sym ? cur_sym->vaddr : value; + reloc.type = container == 16 ? RZ_BIN_RELOC_16 : (container == 8 ? RZ_BIN_RELOC_8 : RZ_BIN_RELOC_32); + reloc.print_name = "R_C6000_STFLD"; + size_t plen = ti_c6000_store_field(bin, reloc.paddr, r_symndx, value, patch_buf); + cb(&reloc, plen ? patch_buf : NULL, plen, user); + sp = 0; + cur_sym = NULL; + break; + } + case COFF_REL_C6000_RELBYTE: + case COFF_REL_C6000_RELWORD: + case COFF_REL_C6000_RELLONG: { + // classic direct relocations: add the symbol value to the + // in-place field, no stack involved + RzBinSymbol *sym = ht_up_find(bin->sym_ht, (ut64)r_symndx, NULL); + sp = 0; + cur_sym = NULL; + if (!sym) { + break; + } + ut8 width = r_type == COFF_REL_C6000_RELLONG ? 32 : (r_type == COFF_REL_C6000_RELWORD ? 16 : 8); + ut8 patch_buf[8]; + RzBinReloc reloc = { 0 }; + reloc.paddr = scn_hdr->s_scnptr + r_vaddr; + reloc.vaddr = bin->scn_va[i] + r_vaddr; + reloc.symbol = sym; + reloc.additive = 1; + reloc.type = width == 32 ? RZ_BIN_RELOC_32 : (width == 16 ? RZ_BIN_RELOC_16 : RZ_BIN_RELOC_8); + reloc.print_name = r_type == COFF_REL_C6000_RELLONG ? "R_C6000_RELLONG" : (r_type == COFF_REL_C6000_RELWORD ? "R_C6000_RELWORD" : "R_C6000_RELBYTE"); + ut32 fldspec = ((ut32)width << 16) | ((ut32)width << 8); + ut64 addend = 0; + if (width == 32) { + ut32 v; + addend = rz_buf_read_le32_at(bin->b, reloc.paddr, &v) ? v : 0; + } else if (width == 16) { + ut16 v; + addend = rz_buf_read_le16_at(bin->b, reloc.paddr, &v) ? v : 0; + } else { + ut8 v; + addend = rz_buf_read_at(bin->b, reloc.paddr, &v, 1) == 1 ? v : 0; + } + reloc.addend = addend; + reloc.target_vaddr = sym->vaddr; + size_t plen = ti_c6000_store_field(bin, reloc.paddr, fldspec, sym->vaddr + addend, patch_buf); + cb(&reloc, plen ? patch_buf : NULL, plen, user); + break; + } + default: + // framing/attribute relocations (alignment, fetch-packet + // header, no-compact): no operand, and they terminate any + // field expression, so drop the stack + sp = 0; + cur_sym = NULL; + break; + } +#undef C6000_BINOP + } + } +} + static void relocs_foreach(struct rz_bin_coff_obj *bin, RelocsForeachCb cb, void *user) { if (!bin->scn_hdrs) { return; } + if (bin->target_id == COFF_FILE_TARGET_TI_TMS320C6000) { + ti_c6000_relocs_foreach(bin, cb, user); + return; + } size_t i = 0; CoffScnHdr *scn_hdr = NULL; diff --git a/librz/bin/format/coff/coff_specs.h b/librz/bin/format/coff/coff_specs.h index 3c92a5b81b..b9b696dc8e 100644 --- a/librz/bin/format/coff/coff_specs.h +++ b/librz/bin/format/coff/coff_specs.h @@ -184,6 +184,39 @@ #define COFF_REL_ARM64_ADDR32NB 2 #define COFF_REL_ARM64_BRANCH26 3 +/* TI TMS320C6000 COFF relocations are RPN expressions evaluated by a small + * stack machine (TI "Common Object File Format Specification", SPRAAO8): the + * RE_PUSH* operators load operands, the arithmetic operators fold the two top + * stack entries, and the RE_*STFLD operators store the result into a field the + * reloc's symbol-index word describes as (container_bits << 16) | + * (field_bits << 8) | start_bit. Framing relocations (type < 0x4000, e.g. the + * no-compact marker) carry no operand and patch nothing. */ +#define COFF_REL_C6000_ADD 0x4000 /* pop b, a; push a + b */ +#define COFF_REL_C6000_SUB 0x4001 /* pop b, a; push a - b */ +#define COFF_REL_C6000_NEG 0x4002 /* pop a; push -a */ +#define COFF_REL_C6000_MPY 0x4003 /* pop b, a; push a * b */ +#define COFF_REL_C6000_DIV 0x4004 /* pop b, a; push a / b */ +#define COFF_REL_C6000_MOD 0x4005 /* pop b, a; push a % b */ +#define COFF_REL_C6000_SR 0x4006 /* pop b, a; push a >> b (unsigned) */ +#define COFF_REL_C6000_ASR 0x4007 /* pop b, a; push a >> b (arithmetic) */ +#define COFF_REL_C6000_SL 0x4008 /* pop b, a; push a << b */ +#define COFF_REL_C6000_AND 0x4009 /* pop b, a; push a & b */ +#define COFF_REL_C6000_OR 0x400a /* pop b, a; push a | b */ +#define COFF_REL_C6000_XOR 0x400b /* pop b, a; push a ^ b */ +#define COFF_REL_C6000_NOTB 0x400c /* pop a; push ~a */ +#define COFF_REL_C6000_USTFLD 0x400f /* pop v; store unsigned v into field */ +#define COFF_REL_C6000_SSTFLD 0x4010 /* pop v; store signed v into field */ +#define COFF_REL_C6000_PUSH 0x4011 /* push value of symbol symndx */ +#define COFF_REL_C6000_PUSHSK 0x4012 /* push signed constant symndx */ +#define COFF_REL_C6000_PUSHUK 0x4013 /* push unsigned constant symndx */ +#define COFF_REL_C6000_XSTFLD 0x4016 /* pop v; store v into a whole-word field */ + +/* Classic COFF direct data relocations reused by TI (not stack-machine ops): + * the referenced symbol value is added to the in-place field. */ +#define COFF_REL_C6000_RELBYTE 0x000f /* symbol value added to an 8-bit field */ +#define COFF_REL_C6000_RELWORD 0x0010 /* symbol value added to a 16-bit field */ +#define COFF_REL_C6000_RELLONG 0x0011 /* symbol value added to a 32-bit field */ + typedef struct coff_hdr { ut16 f_magic; /* Magic number */ ut16 f_nscns; /* Number of Sections */ diff --git a/test/db/formats/coff b/test/db/formats/coff index 564b9ef267..1b16ab13a2 100644 --- a/test/db/formats/coff +++ b/test/db/formats/coff @@ -277,3 +277,23 @@ bcc 0x2F, tc1 mov xar0, ac3 EOF RUN + +NAME=TMS320C6000 COFF relocation application +FILE=bins/tms320/coff2/c6x_reloc.ticoff2.obj +CMDS=<