view leo-obj/tool/richsym.c @ 221:9f2e0c34fe33

ftmdump (C1xx factory data reverse eng) tool written
author Mychaela Falconia <falcon@freecalypso.org>
date Fri, 17 Nov 2017 19:58:07 +0000
parents f7943bdbb3ea
children
line wrap: on
line source

/*
 * Code for working with the "rich" symbolic info
 * present in TI's GPF libraries and L1 objects
 */

#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include "intstruct.h"
#include "coffconst.h"
#include "globals.h"

extern unsigned get_u16(), get_u32();
extern char *storage_class_to_string();

int richsym_print_bitsize;

static int
try_typedef_hack(struct_sym)
	struct internal_syment *struct_sym;
{
	unsigned tpdef_cand;
	struct internal_syment *tpdef_sym;

	if (!struct_sym->aux)
		return(0);
	tpdef_cand = get_u32(struct_sym->aux + 12);
	if (tpdef_cand >= nsymtab)
		return(0);
	tpdef_sym = symtab[tpdef_cand];
	if (!tpdef_sym)
		return(0);
	if (tpdef_sym->class != C_TPDEF)
		return(0);
	if (tpdef_sym->type != struct_sym->type)
		return(0);
	if (tpdef_sym->name[0] != '_')
		return(0);
	if (!tpdef_sym->aux)
		return(0);
	if (get_u32(tpdef_sym->aux) != struct_sym->number)
		return(0);
	struct_sym->struct_name = tpdef_sym->name + 1;
	return(1);
}

static void
preen_strtag_sym(sym, kw, expect_type)
	struct internal_syment *sym;
	char *kw;
{
	char *buf;
	int isund, len;

	isund = (sym->name[0] == '_');
	len = strlen(kw) + 1 + strlen(sym->name) + 1;
	if (isund)
		len--;
	else
		len += 2;
	buf = malloc(len);
	if (!buf) {
		perror("malloc");
		exit(1);
	}
	if (isund)
		sprintf(buf, "%s %s", kw, sym->name + 1);
	else
		sprintf(buf, "%s \"%s\"", kw, sym->name);
	sym->struct_name = buf;
	sym->struct_name_raw = buf;
	if (sym->type != expect_type) {
		fprintf(stderr,
			"warning: type/class mismatch on tag symbol #%u\n",
			sym->number);
		return;
	}
	if (isund)
		return;
	try_typedef_hack(sym);
}

richsym_initial_preen()
{
	unsigned n;
	struct internal_syment *sym;

	for (n = 0; n < nsymtab; n++) {
		sym = symtab[n];
		if (!sym)
			continue;
		switch (sym->class) {
		case C_STRTAG:
			preen_strtag_sym(sym, "struct", T_STRUCT);
			continue;
		case C_UNTAG:
			preen_strtag_sym(sym, "union", T_UNION);
			continue;
		case C_ENTAG:
			preen_strtag_sym(sym, "enum", T_ENUM);
			continue;
		}
	}
}

char *
get_struct_type_name(idx, expect_class, is_typedef)
	unsigned idx;
{
	struct internal_syment *sym;

	if (idx >= nsymtab) {
inv_idx:	fprintf(stderr,
			"error: ref to invalid syment #%u for struct tag\n",
			idx);
		exit(1);
	}
	sym = symtab[idx];
	if (!sym)
		goto inv_idx;
	if (sym->class != expect_class) {
		fprintf(stderr,
		"error: ref to syment #%u for struct tag, class != %s\n",
			idx, storage_class_to_string(expect_class, 0));
		exit(1);
	}
	if (is_typedef)
		return(sym->struct_name_raw);
	else
		return(sym->struct_name);
}

char *
get_base_type_of_syment(sym, is_typedef)
	struct internal_syment *sym;
{
	unsigned idx;

	switch (sym->type & 0xF) {
	case T_VOID:
		return("void");
	case T_CHAR:
		if (is_typedef)
			return("signed char");
		else
			return("char");
	case T_SHORT:
		return("short");
	case T_INT:
		return("int");
	case T_LONG:
		return("long");
	case T_FLOAT:
		return("float");
	case T_DOUBLE:
		return("double");
	case T_STRUCT:
		idx = get_u32(sym->aux);
		if (idx)
			return get_struct_type_name(idx, C_STRTAG, is_typedef);
		else
			return("struct ?");
	case T_UNION:
		idx = get_u32(sym->aux);
		if (idx)
			return get_struct_type_name(idx, C_UNTAG, is_typedef);
		else
			return("union ?");
	case T_ENUM:
		idx = get_u32(sym->aux);
		if (idx)
			return get_struct_type_name(idx, C_ENTAG, is_typedef);
		else
			return("enum ?");
	case T_UCHAR:
		if (is_typedef)
			return("unsigned char");
		else
			return("u_char");
	case T_USHORT:
		if (is_typedef)
			return("unsigned short");
		else
			return("u_short");
	case T_UINT:
		return("unsigned");
	case T_ULONG:
		if (is_typedef)
			return("unsigned long");
		else
			return("u_long");
	default:
		return("__unknown_base_type");
	}
}

static char *
cdecl_obj_name(sym)
	struct internal_syment *sym;
{
	char *buf;
	int isund, len;

	switch (sym->name[0]) {
	case '_':
		isund = 1;
		break;
	case '$':
		if ((sym->type & 0x30) == 0x20)
			isund = 1;
		else
			isund = 0;
		break;
	default:
		isund = 0;
	}
	len = strlen(sym->name) + 1;
	if (isund)
		len--;
	else
		len += 2;
	buf = malloc(len);
	if (!buf) {
		perror("malloc");
		exit(1);
	}
	if (isund)
		strcpy(buf, sym->name + 1);
	else
		sprintf(buf, "\"%s\"", sym->name);
	return(buf);
}

richsym_print_in_c(prefix, sym, is_typedef)
	char *prefix;
	struct internal_syment *sym;
{
	char *base_type, *s1, *s2;
	int dertype, last_ptr, narray;

	base_type = get_base_type_of_syment(sym, is_typedef);
	dertype = sym->type >> 4;
	s1 = cdecl_obj_name(sym);
	last_ptr = 0;
	narray = 0;
	for (; dertype; dertype >>= 2) {
		switch (dertype & 3) {
		case DT_NON:
			fprintf(stderr,
		"error: symbol #%u: DT_NON followed by more derived types\n",
				sym->number);
			exit(1);
		case DT_PTR:
			s2 = malloc(strlen(s1) + 2);
			if (!s2) {
				perror("malloc");
				exit(1);
			}
			sprintf(s2, "*%s", s1);
			free(s1);
			s1 = s2;
			last_ptr = 1;
			continue;
		}
		if (last_ptr) {
			s2 = malloc(strlen(s1) + 3);
			if (!s2) {
				perror("malloc");
				exit(1);
			}
			sprintf(s2, "(%s)", s1);
			free(s1);
			s1 = s2;
		}
		switch (dertype & 3) {
		case DT_FCN:
			s2 = malloc(strlen(s1) + 3);
			if (!s2) {
				perror("malloc");
				exit(1);
			}
			sprintf(s2, "%s()", s1);
			free(s1);
			s1 = s2;
			break;
		case DT_ARY:
			if (narray >= 4) {
				fprintf(stderr,
				"error: symbol #%u: too many [] types\n",
					sym->number);
				exit(1);
			}
			s2 = malloc(strlen(s1) + 8);
			if (!s2) {
				perror("malloc");
				exit(1);
			}
			sprintf(s2, "%s[%u]", s1,
				get_u16(sym->aux + 8 + narray * 2));
			free(s1);
			s1 = s2;
			narray++;
			break;
		default:
			fprintf(stderr,
			"BUG in richsym_print_in_c(): bad derived type\n");
			exit(1);
		}
		last_ptr = 0;
	}
	printf("%s%s %s;", prefix, base_type, s1);
	free(s1);
	if (richsym_print_bitsize && (sym->type & 0x30) != 0x20)
		printf("\t/* %u bits */", get_u32(sym->aux + 4));
	putchar('\n');
}

cmd_ctypes(argc, argv)
	char **argv;
{
	int c;
	unsigned n;
	struct internal_syment *sym;
	int print_offsets = 0;

	while ((c = getopt(argc, argv, "bo")) != EOF)
		switch (c) {
		case 'b':
			richsym_print_bitsize++;
			continue;
		case 'o':
			print_offsets++;
			continue;
		default:
			/* error msg already printed */
			exit(1);
		}

	get_int_section_table();
	get_int_symbol_table();
	richsym_initial_preen();
	for (n = 0; n < nsymtab; n++) {
		sym = symtab[n];
		if (!sym)
			continue;
		switch (sym->class) {
		case C_FILE:
			printf("/* from %s */\n", sym->name);
			continue;
		case C_STRTAG:
		case C_UNTAG:
		case C_ENTAG:
			printf("%s {", sym->struct_name_raw);
			if (richsym_print_bitsize && sym->aux)
				printf("\t/* %u bits */", get_u32(sym->aux+4));
			putchar('\n');
			continue;
		case C_EOS:
			fputs("};", stdout);
			if (richsym_print_bitsize && sym->aux)
				printf("\t/* %u bits */", get_u32(sym->aux+4));
			putchar('\n');
			continue;
		case C_MOS:
		case C_MOU:
		case C_MOE:
		case C_TPDEF:
			break;
		default:
			continue;
		}
		if (sym->name[0] != '_') {
			printf(
		"/* symbol #%u of class %s has no leading underscore */\n",
				sym->number,
				storage_class_to_string(sym->class, 0));
			continue;
		}
		if (!sym->aux && sym->class != C_MOE) {
			printf(
			"/* symbol #%u of class %s has no aux record */\n",
				sym->number,
				storage_class_to_string(sym->class, 0));
			continue;
		}
		switch (sym->class) {
		case C_MOS:
		case C_MOU:
			if (sym->scnum != -1)
				printf("\t/* MOS/MOU section != ABS! */\n");
			else if (print_offsets && !(sym->value & 7))
				printf("\t/* offset: 0x%x */\n",
					sym->value >> 3);
			else if (print_offsets || richsym_print_bitsize >= 2)
				printf("\t/* offset: %u bits */\n", sym->value);
			richsym_print_in_c("\t", sym, 0);
			continue;
		case C_MOE:
			if (sym->scnum != -1) {
				printf("\t/* MOE section != ABS! */\n");
				continue;
			}
			printf("\t%s = %u;", sym->name + 1, sym->value);
			if (sym->value >= 10)
				printf("\t/* 0x%x */", sym->value);
			putchar('\n');
			continue;
		case C_TPDEF:
			richsym_print_in_c("typedef ", sym,
						!(sym->type & 0xFFF0));
			continue;
		}
	}
	exit(0);
}

static int
localsym_sanity_check_aux(sym)
	struct internal_syment *sym;
{
	if (!sym->aux) {
		printf("; symbol #%u of class %s has no aux record\n",
			sym->number, storage_class_to_string(sym->class, 0));
		return(-1);
	}
	return(0);
}

static int
localsym_sanity_check_scnum(sym)
	struct internal_syment *sym;
{
	if (sym->scnum != -1) {
		printf("; symbol #%u of class %s: section != ABS\n",
			sym->number, storage_class_to_string(sym->class, 0));
		return(-1);
	}
	return(0);
}

static int
localsym_sanity_check(sym)
	struct internal_syment *sym;
{
	int rc;

	rc = localsym_sanity_check_aux(sym);
	if (rc)
		return(rc);
	rc = localsym_sanity_check_scnum(sym);
	if (rc)
		return(rc);
	return(0);
}

richsym_function_locals(headsym)
	struct internal_syment *headsym;
{
	unsigned n, count = 0;
	struct internal_syment *sym;
	char classbuf[8];

	for (n = headsym->number + 1; n < nsymtab; n++) {
		sym = symtab[n];
		if (!sym)
			continue;
		switch (sym->class) {
		case C_REG:
		case C_REGPARM:
			count++;
			if (localsym_sanity_check(sym) < 0)
				continue;
			if (sym->value > 0xF) {
				printf(
	"; symbol #%u of class %s: value is not a valid register number\n",
					sym->number,
					storage_class_to_string(sym->class, 0));
				continue;
			}
			printf("; %s r%u: ",
				storage_class_to_string(sym->class, 0),
				sym->value);
			richsym_print_in_c("", sym, 0);
			continue;
		case C_ARG:
		case C_AUTO:
			count++;
			if (localsym_sanity_check(sym) < 0)
				continue;
			printf("; %s at 0x%x: ",
				storage_class_to_string(sym->class, 0),
				sym->value);
			richsym_print_in_c("", sym, 0);
			continue;
		case C_STRTAG:
		case C_UNTAG:
		case C_ENTAG:
			count++;
			printf("; %s {", sym->struct_name_raw);
			if (richsym_print_bitsize && sym->aux)
				printf("\t/* %u bits */", get_u32(sym->aux+4));
			putchar('\n');
			continue;
		case C_EOS:
			count++;
			fputs("; };", stdout);
			if (richsym_print_bitsize && sym->aux)
				printf("\t/* %u bits */", get_u32(sym->aux+4));
			putchar('\n');
			continue;
		case C_MOS:
		case C_MOU:
			count++;
			if (localsym_sanity_check(sym) < 0)
				continue;
			richsym_print_in_c(";   ", sym, 0);
			continue;
		case C_MOE:
			count++;
			if (localsym_sanity_check_scnum(sym) < 0)
				continue;
			if (sym->name[0] == '_')
				printf(";   %s = %u;", sym->name + 1,
					sym->value);
			else
				printf(";   \"%s\" = %u;", sym->name,
					sym->value);
			if (sym->value >= 10)
				printf("\t/* 0x%x */", sym->value);
			putchar('\n');
			continue;
		case C_TPDEF:
			count++;
			if (localsym_sanity_check_aux(sym) < 0)
				continue;
			richsym_print_in_c("; typedef ", sym,
						!(sym->type & 0xFFF0));
			continue;
		case C_STAT:
			count++;
			if (localsym_sanity_check_aux(sym) < 0)
				continue;
			richsym_print_in_c("; static ", sym, 0);
			continue;
		case C_FCN:
			return(count);
		default:
			count++;
			printf(
			"; presumed local symbol #%u: unexpected class %s\n",
				sym->number,
				storage_class_to_string(sym->class, classbuf));
			return(count);
		}
	}
	return(count);
}