mirror of
https://github.com/rizinorg/rizin.git
synced 2026-10-12 02:21:57 +00:00
bin/coff: apply TMS320C6000 (c6x) relocations
This commit is contained in:
committed by
NOT XVilka
parent
58e1b45063
commit
0fc2365b57
@@ -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 */
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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=<<EOF
|
||||
ir
|
||||
s section..text
|
||||
pxw 12
|
||||
s section..data
|
||||
pxw 8
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
vaddr paddr target type name
|
||||
--------------------------------------------------------
|
||||
0x00000030 0x000000fd 0x00000050 R_C6000_STFLD _value
|
||||
0x00000034 0x00000101 0x00000050 R_C6000_STFLD _value
|
||||
0x00000054 0x00000121 0x00000050 R_C6000_RELLONG _value
|
||||
0x00000030 0x00002828 0x00000068 0x00800264 ((..h...d...
|
||||
0x00000050 0x0000600d 0x00000050 .`..P...
|
||||
EOF
|
||||
RUN
|
||||
|
||||
Reference in New Issue
Block a user