-
Notifications
You must be signed in to change notification settings - Fork 245
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Symbol migration tool for functions and global data. #175
- Loading branch information
1 parent
f1c2120
commit 8748b4b
Showing
3 changed files
with
265 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
|
||
UNWRAPPED=../firmware/unwrapped | ||
GOLD=../firmware/unwrapped/D002.032.img | ||
GOLDSYM=../applet/src/symbols_2_032 | ||
|
||
all: symgrate run | ||
|
||
clean: | ||
rm -f symgrate symbols_* | ||
images: | ||
cd ../firmware && make all | ||
|
||
run: all images | ||
./symgrate $(GOLD) $(UNWRAPPED)/D002.034.img <$(GOLDSYM) >symbols_d02_034 | ||
./symgrate $(GOLD) $(UNWRAPPED)/D003.008.img <$(GOLDSYM) >symbols_d03_008 | ||
./symgrate $(GOLD) $(UNWRAPPED)/D013.009.img <$(GOLDSYM) >symbols_d13_009 | ||
./symgrate $(GOLD) $(UNWRAPPED)/S003.012.img <$(GOLDSYM) >symbols_s03_012 | ||
./symgrate $(GOLD) $(UNWRAPPED)/S013.012.img <$(GOLDSYM) >symbols_s13_012 | ||
./symgrate $(GOLD) $(UNWRAPPED)/D013.014.img <$(GOLDSYM) >symbols_d13_014 | ||
wc -l symbols_* | ||
@echo "These are temporary symbol files that will be overwritten." | ||
@echo "Move them elsewhere before editing." | ||
|
||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
This directory is used to auto-generate symbols for unknown MD380 | ||
firmware versions from the present gold-standard version, which will | ||
likely be 2.032 for quite some time. | ||
|
||
The tool's parser is too primitive to understand addition, so please | ||
ensure that all input lines are either (1) just a comment, (2) just | ||
whitespace, or (3) a symbol name and value with no arithmetic. | ||
|
||
'make clean all' ought to produce symbol files for all versions, for | ||
all functions that match. | ||
|
||
Cheers, | ||
--Travis | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,226 @@ | ||
/* \file symgrate.c | ||
\author Travis Goodspeed <travis@radiantmachines.com | ||
\brief Symbol migration tool. | ||
This is a quick and dirty tool for migrating function symbols in | ||
Thumb(2) machine code on ARM Cortex M4, and more specifically, for | ||
migrating function symbols between various firmware images of the | ||
Tytera MD380. I hope someday to target related radios, such as | ||
those from Connect Systems. | ||
The general theory of operation is pretty simple. We have a | ||
known-good firmware images (2.032) with a number of identified | ||
functions, and we wish to hook, patch, or call those functions on a | ||
new firmware image. Since Thumb uses constant pools for its | ||
immediates, rather than putting them inside the instructions, the | ||
related function ought to be the closes match for the starting | ||
code. | ||
Where possible, this tool matches each function in the old image to | ||
an address in the new image. This will succeed where the function | ||
and optimizations haven't changed between revisions, hopefully | ||
leaving just a few remaining functions to search for manually in | ||
IDA+Bindiff or Radare. The goal is not to be sophisticated, but to | ||
catch as much of a break for free as possible. | ||
A separate tool will be needed to find branches and RAM addresses. | ||
Also, this parser sucks. | ||
*/ | ||
|
||
#include <stdio.h> | ||
#include <stdlib.h> | ||
#include <string.h> | ||
|
||
//Image begins just after the bootloader. | ||
const int BASE=0x0800C000; | ||
const int MAXLEN=1024*1024; | ||
|
||
//These refer to the actual buffers. | ||
char *Src; | ||
char *Dst; | ||
|
||
//These macros grab a byte from the device. | ||
#define src8(x) (*(Src+x-BASE)) | ||
#define dst8(x) (*(Src+x-BASE)) | ||
|
||
//These macros grab a word from the device. | ||
#define src32(x) (*((int*)(Src+x-BASE))) | ||
#define dst32(x) (*((int*)(Dst+x-BASE))) | ||
|
||
//These macros grab a short from the device. | ||
#define src16(x) (*((short*)(Src+x-BASE))) | ||
#define dst16(x) (*((short*)(Dst+x-BASE))) | ||
|
||
|
||
|
||
//! Loads a disk image into a buffer. | ||
int loadbuffer(char *buf, const char *filename){ | ||
FILE *f; | ||
int len; | ||
|
||
f=fopen(filename,"r"); | ||
if(!f){ | ||
printf("File %s not found!\n", filename); | ||
exit(1); | ||
} | ||
|
||
//FIXME hardcoded size | ||
len=fread(buf, 1024, 1024, f); | ||
|
||
return 0; | ||
} | ||
|
||
//! Allocate the buffers. | ||
int mallocbuffers(){ | ||
Src=(char*) malloc(MAXLEN); | ||
Dst=(char*) malloc(MAXLEN); | ||
return 1;//success | ||
} | ||
|
||
//! Scores a match between the src and destination buffer by length. | ||
int scorematch(int sadr, int dadr){ | ||
int i=0; | ||
|
||
/* Compare up to the first 1024 bytes, one pair at a time. Usually | ||
the best score is the same function if that function hasn't been | ||
modified between versions. | ||
*/ | ||
do{ | ||
i+=2; | ||
}while(src32(sadr+i)==dst32(dadr+i) && i<1024); | ||
|
||
return i; | ||
} | ||
|
||
//!Locates a symbol by name and address. | ||
int findsymbol(const char *name, int adr){ | ||
int isfunction=adr&1; | ||
adr=adr&~1; | ||
|
||
if(adr&0x20000000){ //RAM | ||
printf("/* %s is a RAM address and cannot be converted. */\n", | ||
name); | ||
return adr|isfunction; | ||
}else if(adr&0x40000000){ //IO | ||
printf("%s = 0x%08x;\n", | ||
name,adr|isfunction); | ||
return adr|isfunction; | ||
} | ||
|
||
|
||
/* Here we run through the destination image, trying to find | ||
anything that matches a few bytes. The decision of whether to | ||
accept the match comes later. | ||
*/ | ||
short tofind=src16(adr); | ||
int dadr=0, dscore=0; | ||
for(int i=0;i<(MAXLEN);i+=2){ | ||
if(src16(adr)==dst16(BASE+i)){ | ||
int score=scorematch(adr,BASE+i); | ||
|
||
if(score>dscore){ | ||
dscore=score; | ||
dadr=adr; | ||
} | ||
} | ||
} | ||
|
||
if(dadr && dscore>8) | ||
printf("%-20s = 0x%08x; /* %i byte match */\n", | ||
name, | ||
adr+isfunction, | ||
dscore); | ||
else if(dadr) | ||
printf("/* %s has bad match of %i points at 0x%08x*/\n", | ||
name, dscore, adr+isfunction); | ||
else | ||
printf("/* %s not found. */\n", | ||
name); | ||
|
||
return dadr; | ||
} | ||
|
||
//! Ugly shotgun parser for one record of the file. | ||
int parseline(char *line, | ||
char *retname, int *retadr){ | ||
//Assumes a form like this: | ||
//md380_spiflash_read = 0x802fd83; | ||
|
||
|
||
char *start=line; | ||
char *eq=strstr(line,"="); | ||
char *semicolon=strstr(line,";"); | ||
char *name=start; | ||
char *adr=strstr(line,"0x"); | ||
char *plus=strstr(line,"+"); | ||
|
||
//We can't handle arithmetic | ||
if(plus){ | ||
return 0; | ||
} | ||
|
||
//We need all these fields. | ||
if(name && semicolon && eq && adr){ | ||
sscanf(name,"%s ",retname); | ||
sscanf(adr, "0x%08x",retadr); | ||
return 1; | ||
} | ||
|
||
return 0; | ||
} | ||
|
||
//! Ugly puntgun parser loop, terribly unsafe. | ||
int parseloop(){ | ||
static char line[10240]; | ||
//FIXME This should really be rewritten with a better parsing library. | ||
|
||
static char name[1024]; | ||
int adr; | ||
memset(name,1024,0); | ||
|
||
while(!feof(stdin)){ | ||
//Read the line. | ||
fgets(line,1024,stdin); | ||
|
||
if(strlen(line)==1){ //empty line. | ||
printf("\n"); | ||
}else if(line[0]=='/' && line[1]=='*'){ //commenty line | ||
printf("%s",line); | ||
}else if(parseline(line,name,&adr)){ | ||
//Look for a symbol match. | ||
//This prints its own result. | ||
findsymbol(name,adr); | ||
}else{ | ||
printf("/* Unparsed line: %s */\n", | ||
line); | ||
} | ||
} | ||
return 0; | ||
} | ||
|
||
//! Main method. Maybe abstract this a bit? | ||
int main(int argc, char **argv){ | ||
//Allocate the buffers. | ||
mallocbuffers(); | ||
|
||
if(argc<3){ | ||
printf("Usage: %s D002.032.img D013.014.img " | ||
"<symbols_D002.032 >symbols_D013.014\n", | ||
argv[0]); | ||
return 1; | ||
} | ||
|
||
//Load the source and destination buffers. | ||
loadbuffer(Src,argv[1]); | ||
loadbuffer(Dst,argv[2]); | ||
|
||
|
||
printf("/* Symbols for %s imported from %s. */\n", | ||
argv[2], | ||
argv[1]); | ||
|
||
parseloop(); | ||
} |