view objgrep/frontend.c @ 389:623316d1ece7

compal/iMelody: capturing initial observations
author Mychaela Falconia <falcon@freecalypso.org>
date Thu, 31 Mar 2022 20:56:56 +0000
parents 928ed52930aa
children
line wrap: on
line source

/*
 * This program is a front-end to objgrep.  It takes an ASCII text file as
 * input that lists the modules and sections to be grepped for, and it
 * invokes objgrep via popen() for each listed section, all on the same
 * unknown binary.  We collect the symbol addresses printed by objgrep
 * via our pipe, and then we sort them to produce the final report.
 */

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>

#define	RAM_BOUNDARY	0x800000

#define	SYMFLAG_AMBIG		8
#define	SYMSRC_TEXTMATCH	4
#define	SYMSRC_EXTRELOC		2
#define	SYMSRC_INTRELOC		1

struct symbol {
	char		*name;
	unsigned	value;
	int		flags;
	struct symbol	*next;
};

struct symbol *flash_symbols, *ram_symbols;
unsigned num_flash_symbols, num_ram_symbols;
char *binfilename;
int verbose;

enter_symbol(name, valstr, symsrc)
	char *name, *valstr;
{
	struct symbol *newsym;

	newsym = malloc(sizeof(struct symbol) + strlen(name) + 1);
	if (!newsym) {
		perror("malloc");
		exit(1);
	}
	newsym->name = (char *)(newsym + 1);
	strcpy(newsym->name, name);
	newsym->value = strtoul(valstr, 0, 16);
	newsym->flags = symsrc;
	if (newsym->value < RAM_BOUNDARY) {
		newsym->next = flash_symbols;
		flash_symbols = newsym;
		num_flash_symbols++;
	} else {
		newsym->next = ram_symbols;
		ram_symbols = newsym;
		num_ram_symbols++;
	}
}

process_section(objfile, secname)
	char *objfile, *secname;
{
	char cmdline[1024], linebuf[512], *cp, *fields[3];
	FILE *f;
	int symsrc, i;
	static char headline_textmatch[] = "This source section's symbols:";
	static char headline_extreloc[] = "Externals deduced from relocs:";
	static char headline_intreloc[] =
			"This module's symbols deduced from section relocs:";

	sprintf(cmdline, "objgrep -rs %s %s %s", objfile, secname, binfilename);
	if (verbose)
		printf("Running %s\n", cmdline);
	f = popen(cmdline, "r");
	if (!f) {
		perror("popen");
		exit(1);
	}
	symsrc = 0;
	while (fgets(linebuf, sizeof linebuf, f)) {
		cp = index(linebuf, '\n');
		if (!cp) {
			fprintf(stderr,
		"error: objgrep output line has no terminating newline\n");
			exit(1);
		}
		*cp = '\0';
		if (!strcmp(linebuf, headline_textmatch)) {
			symsrc = SYMSRC_TEXTMATCH;
			continue;
		}
		if (!strcmp(linebuf, headline_extreloc)) {
			symsrc = SYMSRC_EXTRELOC;
			continue;
		}
		if (!strcmp(linebuf, headline_intreloc)) {
			symsrc = SYMSRC_INTRELOC;
			continue;
		}
		cp = linebuf;
		for (i = 0; ; i++) {
			while (isspace(*cp))
				cp++;
			if (!*cp)
				break;
			if (i == 3) {
				i++;
				break;
			}
			for (fields[i] = cp; *cp && !isspace(*cp); cp++)
				;
			if (*cp)
				*cp++ = '\0';
		}
		if (i != 3)
			continue;
		if (fields[1][0] != '=' || fields[1][1])
			continue;
		if (!symsrc) {
			fprintf(stderr,
		"error: symbol output from objgrep w/o source indication\n");
			exit(1);
		}
		enter_symbol(fields[0], fields[2], symsrc);
	}
	pclose(f);
}

process_list_file(listfilename)
	char *listfilename;
{
	FILE *f;
	char linebuf[512], *objname, *cp, *np;
	int lineno;

	f = fopen(listfilename, "r");
	if (!f) {
		perror(listfilename);
		exit(1);
	}
	for (lineno = 1; fgets(linebuf, sizeof linebuf, f); lineno++) {
		for (cp = linebuf; isspace(*cp); cp++)
			;
		if (*cp == '\0' || *cp == '#')
			continue;
		for (objname = cp; *cp && !isspace(*cp); cp++)
			;
		if (*cp)
			*cp++ = '\0';
		while (isspace(*cp))
			cp++;
		if (*cp == '\0' || *cp == '#') {
			process_section(objname, ".text");
			continue;
		}
		while (*cp && *cp != '#') {
			for (np = cp; *cp && !isspace(*cp); cp++)
				;
			if (*cp)
				*cp++ = '\0';
			process_section(objname, np);
			while (isspace(*cp))
				cp++;
		}
	}
	fclose(f);
}

sort_by_value(sym1, sym2)
	struct symbol **sym1, **sym2;
{
	if ((*sym1)->value < (*sym2)->value)
		return(-1);
	if ((*sym1)->value > (*sym2)->value)
		return(1);
	return(0);
}

sort_by_name(sym1, sym2)
	struct symbol **sym1, **sym2;
{
	int i;

	i = strcmp((*sym1)->name, (*sym2)->name);
	if (i)
		return i;
	else
		return sort_by_value(sym1, sym2);
}

fill_input_array(head, buf)
	struct symbol *head, **buf;
{
	struct symbol *sym, **p;

	p = buf;
	for (sym = head; sym; sym = sym->next)
		*p++ = sym;
}

unsigned
dedup_symbols(inbuf, outbuf, total)
	struct symbol **inbuf, **outbuf;
	unsigned total;
{
	struct symbol **ip, **op, **input_end, *nsym;

	op = outbuf;
	input_end = inbuf + total;
	for (ip = inbuf; ip < input_end; ip++) {
		nsym = *ip;
		if (op == outbuf || strcmp(nsym->name, op[-1]->name)) {
			*op++ = nsym;
			continue;
		}
		if (nsym->value == op[-1]->value) {
			op[-1]->flags |= nsym->flags;
			continue;
		}
		op[-1]->flags |= SYMFLAG_AMBIG;
		nsym->flags |= SYMFLAG_AMBIG;
		*op++ = nsym;
	}
	return(op - outbuf);
}

emit_final_symbols(buf, total)
	struct symbol **buf;
	unsigned total;
{
	struct symbol **p, **endbuf, *sym;

	endbuf = buf + total;
	for (p = buf; p < endbuf; p++) {
		sym = *p;
		printf("%08X %s (", sym->value, sym->name);
		if (sym->flags & SYMSRC_TEXTMATCH)
			putchar('S');
		if (sym->flags & SYMSRC_EXTRELOC)
			putchar('R');
		if (sym->flags & SYMSRC_INTRELOC)
			putchar('r');
		if (sym->flags & SYMFLAG_AMBIG)
			fputs(", ambiguous", stdout);
		putchar(')');
		putchar('\n');
	}
}

sort_and_emit_symbols(head, total)
	struct symbol *head;
	unsigned total;
{
	struct symbol **input, **output;
	unsigned final_count;

	if (verbose)
		printf("Total symbols to process: %u\n", total);
	input = malloc(sizeof(void *) * total);
	if (!input) {
		perror("malloc");
		exit(1);
	}
	if (verbose)
		printf("Turning linked list into linear array\n");
	fill_input_array(head, input);
	if (verbose)
		printf("Sorting by name\n");
	qsort(input, total, sizeof(void *), sort_by_name);
	if (verbose)
		printf("Allocating output array\n");
	output = malloc(sizeof(void *) * total);
	if (!output) {
		perror("malloc");
		exit(1);
	}
	if (verbose)
		printf("Processing duplicates\n");
	final_count = dedup_symbols(input, output, total);
	free(input);
	if (verbose)
		printf("%u symbols left, sorting by value\n", final_count);
	qsort(output, final_count, sizeof(void *), sort_by_value);
	emit_final_symbols(output, final_count);
	free(output);
	putchar('\n');
}

main(argc, argv)
	char **argv;
{
	int c;
	extern int optind;

	while ((c = getopt(argc, argv, "v")) != EOF)
		switch (c) {
		case 'v':
			verbose++;
			continue;
		default:
		usage:
			fprintf(stderr, "usage: %s [-v] listfile binfile\n",
				argv[0]);
			exit(1);
		}
	if (argc - optind != 2)
		goto usage;

	binfilename = argv[optind+1];
	process_list_file(argv[optind]);
	if (flash_symbols) {
		printf("Flash symbols:\n\n");
		sort_and_emit_symbols(flash_symbols, num_flash_symbols);
	}
	if (ram_symbols) {
		printf("RAM symbols:\n\n");
		sort_and_emit_symbols(ram_symbols, num_ram_symbols);
	}
	exit(0);
}