PSP decompiler

Discuss the development of new homebrew software, tools and libraries.

Moderators: cheriff, TyRaNiD

hnaves
Posts: 30
Joined: Tue Feb 03, 2009 3:01 am

PSP decompiler

Post by hnaves »

While trying to decompile the usb.prx (FW 1.5) by hand, I realized that it was a very long and boring process... So I made this little tool to help us while doing some prx reverse engineering...

http://ifile.it/93pthb0

The instructions to compile and use are in the README file.The output of the program is a low-level "C" program (with a lot of imperfections), but it is almost understandable. There is also a DOT mode, where the output is a collection of graphviz dot files with the control flow graph of the program.

I know that this tool has several bugs and improvements to be made, and I hope that more people contribute to its development. Please use with caution. I would like to thank Tyranid for the development of prxtool.
hlide
Posts: 739
Joined: Sun Sep 10, 2006 2:31 am

Post by hlide »

quite impressive

do you think we can put it through SVN in google code ? so some of use can contribute ?
jimparis
Posts: 1145
Joined: Fri Jun 10, 2005 4:21 am
Location: Boston

Post by jimparis »

Agreed, very impressive!
TyRaNiD
Posts: 907
Joined: Sun Jan 18, 2004 12:23 am

Post by TyRaNiD »

Nice work :)
hnaves
Posts: 30
Joined: Tue Feb 03, 2009 3:01 am

Post by hnaves »

Does anybody here uses GIT? My project is already under version control at http://repo.or.cz/w/pspdecompiler.git. I believe GIT is better and easier than SVN or CVS, and it is used in the Linux Kernel project :-)

I have also another project, that is to build an OPEN EDITION USB driver for PSP. I have already started, and decompiled (by hand :-) about 20% of the original PRX. Using the tool now will greatly improve the speed of the development. Anybody interested?
User avatar
ghost_gluck
Posts: 7
Joined: Fri Apr 04, 2008 5:09 am

Post by ghost_gluck »

hnaves wrote:I have also another project, that is to build an OPEN EDITION USB driver for PSP. I have already started, and decompiled (by hand :-) about 20% of the original PRX. Using the tool now will greatly improve the speed of the development. Anybody interested?
Nice work!
I find some bugs with firmware prxses decompilation (usb.prx and other) and fix it, see below

Code: Select all

diff -urN pspdecompiler/lists.c pspdecompiler.patched/lists.c
--- pspdecompiler/lists.c	2009-05-12 13:29:26.000000000 +0300
+++ pspdecompiler.patched/lists.c	2009-05-13 11:24:32.812500000 +0300
@@ -85,7 +85,7 @@
 
 element list_head (list l)
 {
-  return l->head;
+  return (l != NULL) ? l->head : NULL;
 }
 
 void *list_headvalue (list l)
@@ -200,22 +200,23 @@
 
 void *element_getvalue (element el)
 {
-  return el->value;
+  return (el != NULL) ? el->value : NULL;
 }
 
 void element_setvalue (element el, void *val)
 {
-  el->value = val;
+  if(el != NULL )
+    el->value = val;
 }
 
 element element_next (element el)
 {
-  return el->next;
+  return (el != NULL) ? el->next : NULL;
 }
 
 element element_previous (element el)
 {
-  return el->prev;
+  return (el != NULL) ? el->prev : NULL;
 }
 
 
diff -urN pspdecompiler/operations.c pspdecompiler.patched/operations.c
--- pspdecompiler/operations.c	2009-05-12 13:29:26.000000000 +0300
+++ pspdecompiler.patched/operations.c	2009-05-13 13:37:12.140625000 +0300
@@ -161,7 +161,7 @@
     }
   case I_MOVN:
     val = element_getvalue (element_next (list_head (op->operands)));
-    if (val->val.intval == 0) {
+    if (val != NULL && val->val.intval == 0) {
       reset_operation (op);
       op->type = OP_NOP;
     }
diff -urN pspdecompiler/output.c pspdecompiler.patched/output.c
--- pspdecompiler/output.c	2009-05-12 13:29:26.000000000 +0300
+++ pspdecompiler.patched/output.c	2009-05-13 13:43:22.296875000 +0300
@@ -462,7 +462,7 @@
   val = list_headvalue (op->operands);
   if (val->type == VAL_SSAVAR) {
     if (val->val.variable->type == SSAVAR_CONSTANT) {
-      address = val->val.variable->type;
+      address = val->val.variable->info;
       val = list_tailvalue (op->operands);
       address += val->val.intval;
       fprintf (out, "*((%s) 0x%08X)", type, address);
Problem with access to the structure member when pointer to val or el struct is NULL.
Mabye you are already fix this problem. I don't check git source.

[offtopic]
About OPEN EDITION USB driver for PSP:
So I started working on USB driver for PSP. I want to add to the driver functionality which allow create more than Block Devices, for example: HID devices (joysticks etc), RNDIS devices and additional host functionality (PSP as USB Host - maybe this is a dream).
I want try to connect my GPS (PSP-290) module to PC for examine communication protocol. If GPS module connected to PC and it is works as device, then PSP works as host when GPS module was connected to PSP.(IMHO) It's correct?
[/offtopic]

EDIT:
I find bug with translation addresses, see below:
lui $t0, 0xbc10
lw $t1, 0x4c($t0)
is interpreted as var = *((int*) 0x00000050, but right translation is var = *((int*) 0x0xbc10004c.

EDIT:
previous bug was fixed. see diff. problem in address = val->val.variable->type, but base address value contains in val->val.variable->info

New problem. Procedure calls through registers (jal rX) doesn't reverses correctly. Maybe its possible because subroutine address calculation mechanism doesn't implemented/works incorrectly.
Sorry for terrible english. My native language is C++...
sauron_le_noir
Posts: 203
Joined: Sat Jul 05, 2008 8:03 am

Post by sauron_le_noir »

Great tool Where can we found nidsfiles ? are this files compatible with the nids file found on http://silverspring.lan.st/
User avatar
ghost_gluck
Posts: 7
Joined: Fri Apr 04, 2008 5:09 am

Post by ghost_gluck »

sauron_le_noir wrote:Great tool Where can we found nidsfiles ? are this files compatible with the nids file found on http://silverspring.lan.st/
compatible.
Sorry for terrible english. My native language is C++...
hlide
Posts: 739
Joined: Sun Sep 10, 2006 2:31 am

Post by hlide »

some suggestions :

1) prepend "int " to "varN = ..." :

in "static void print_binaryop (FILE *out, struct operation *op, const char *opsymbol, int options)",
"void print_complexop (FILE *out, struct operation *op, const char *opsymbol, int options)", etc. transform every "print_value (out, list_headvalue (op->results));" into "print_result (out, list_headvalue (op->results));"

and add :

Code: Select all

void print_result(FILE *out, struct value *val)
{
    fprintf(out, "int ");
    print_element (out, value, 0);
}
2) add ';' to each output asm string instruction :

Code: Select all

static
void print_asm (FILE *out, struct operation *op, int identsize, int options)
{
	struct location *loc;

	ident_line (out, identsize);
	fprintf (out, "__asm__ (");
	for (loc = op->info.asmop.begin; ; loc++) {
		if (loc != op->info.asmop.begin) {
			fprintf (out, "\n");
			ident_line (out, identsize);
			fprintf (out, "         ");
		}
		fprintf (out, "\"%s;\"", allegrex_disassemble (loc->opc, loc->address, FALSE));
		if (loc == op->info.asmop.end) break;
	}
	if (list_size (op->results) != 0 || list_size (op->operands) != 0) {
		print_asm_reglist (out, op->results, identsize, options);
		if (list_size (op->operands) != 0) {
			print_asm_reglist (out, op->operands, identsize, options);
		}
	}

	fprintf (out, ");\n");
}
i'm not sure there is a good reason to handle $sp and load/store operations with $sp as base as they are prolog and epilog, so you may probably drop them or rather make them into comment. By the way, having varN in epilog is ackward : wouldn't it better not to ssa them ?.
hnaves
Posts: 30
Joined: Tue Feb 03, 2009 3:01 am

Post by hnaves »

Hi,

I have corrected several bugs including those posted by ghost_gluck, and among them are:
- Invalid detection of nested ifs gotos;
- Better detection of constants
- Detection of callbacks

To checkout the lastest snapshot, please refer to
http://repo.or.cz/w/pspdecompiler.git
and click on the first snapshot right below the shortlog (on the very right side of the page)

The next step is to improve the detection of arguments and return values of subroutines
willow :--)
Posts: 107
Joined: Sat Jan 13, 2007 11:50 am

Post by willow :--) »

This looks great!
You might want to remove the pdf documents from the repository though, I believe you can't freely distribute those ;)

Also, a very minor point, but could it be possible to have the list of options in alphabetical order in the "usage" text? (because there are lots of options)

Code: Select all

    "  -c    output code\n"
    "  -d    print the dominator\n"
    "  -e    print edge types\n"
    "  -f    print the frontier\n"
    "  -g    output graphviz dot\n"
    "  -i    print prx info\n"
    "  -n    specify nids xml file\n"
    "  -q    print code into nodes\n"
    "  -r    print the reverse depth first search number\n"
    "  -s    print structures\n"
    "  -t    print depth first search number\n"
    "  -v    increase verbosity\n"
    "  -x    print the reverse dominator\n"
    "  -z    print the reverse frontier\n",
SilverSpring
Posts: 110
Joined: Tue Feb 27, 2007 9:43 pm
Contact:

Post by SilverSpring »

Very nice, you worked out the new-style prxs? Good work.
hlide
Posts: 739
Joined: Sun Sep 10, 2006 2:31 am

Post by hlide »

hlide
Posts: 739
Joined: Sun Sep 10, 2006 2:31 am

Post by hlide »

@hnaves
There is a problem : in a loop starting with "while (1)" with a register counter, i have : "varN = varN;" instead of "varJ = varI + CONSTANT;" where varI is containing a constant. It seems the issue is in output.c :

Code: Select all

void print_operation (FILE *out, struct operation *op, int identsize, int options)
{
  ...
  if ((op->status & (OP_STAT_CONSTANT | OP_STAT_DEFERRED)) == OP_STAT_CONSTANT) {
    struct value *val = list_headvalue (op->results);
    if (!(options & OPTS_NORESULT)) {
      print_value (out, val, OPTS_RESULT);
      fprintf (out, " = ");
    }
    print_value (out, val, 0);
  } else {
  ...
}
I may be wrong but it looks as if when an SSA register simplified to a constant" the operation status is erroneously marked as OP_STAT_CONSTANT because it doesn't take into account the fact that this operation is inside a loop and is not invariant.

If not, it means an I_ADDIU is wrongly transformed into a I_MOVE.
D_Street
Posts: 22
Joined: Thu Jun 12, 2008 2:09 pm
Location: Berkeley, CA

Post by D_Street »

really nice work!

i remember last year i also did a little decompiler, but that was a bit lame, i just did the decompile thing out of the output from prxtool! i guess that way i can just copy & paste a piece of the disassembly output and decompile it without any problems.
hlide
Posts: 739
Joined: Sun Sep 10, 2006 2:31 am

Post by hlide »

there is a regression on the last version, pspdecompiler crashes on some prx files whereas the first public version did not.
hnaves
Posts: 30
Joined: Tue Feb 03, 2009 3:01 am

Post by hnaves »

Could you provide me the prx name (and version)? There were several critical bugs fixed (in the constant propagation algorithm) in the last revision, maybe the incorrect output for loops with constants will not exist anymore.
hlide
Posts: 739
Joined: Sun Sep 10, 2006 2:31 am

Post by hlide »

I tried your last revision : two comile-time errors about value_append function missing the last argument in ssa.c; I appended FALSE as last argument to build pspdecompiler fine.

And yes, I have no crash now with all the prx which crashed.
Last edited by hlide on Sun May 17, 2009 10:04 pm, edited 1 time in total.
hlide
Posts: 739
Joined: Sun Sep 10, 2006 2:31 am

Post by hlide »

do you detect some functions not terminating with a JA $RA but with a J ?

the rule is to check if a subroutine has no forward conditional branch when you meet a J to conclude this is subroutine with a tail call to another subroutine :

Code: Select all

void X&#40;&#41; &#123; ...; Y&#40;&#41;; &#125; <=> ...; J sub_Y; ...; 
void Y&#40;&#41; &#123; ... &#125;       <=> ...&#58; JR $RA; ...;

int Z&#40;&#41; &#123; ...; return W&#40;&#41;; &#125; <=> ...; J sub_Y; ...; 
int W&#40;&#41; &#123; ... &#125;              <=> ...; JR $RA; ...;
hlide
Posts: 739
Joined: Sun Sep 10, 2006 2:31 am

Post by hlide »

bug with counters in loops is still unresolved :

Code: Select all

  while &#40;1&#41; &#123;
    var17 = var17; <-- it should be &#58; "var17 = var17 + 0x00000001;"
    InterruptManagerForKernel_7527A9BA &#40;0x00000019, 0x00000000, 0x00000001&#41;;
    if &#40;&#40;&#40;var17 < 0x00000010&#41;&#41; != 0x00000000&#41;
      continue;
    break;
  &#125;
"var17 = var17;" is output when executing output.c:593 :

Code: Select all

   if &#40;&#40;op->status & &#40;OP_STAT_CONSTANT | OP_STAT_DEFERRED&#41;&#41; == OP_STAT_CONSTANT&#41; &#123;
    struct value *val = list_headvalue &#40;op->results&#41;;
    if &#40;!&#40;options & OPTS_NORESULT&#41;&#41; &#123;
      print_value &#40;out, val, OPTS_RESULT&#41;;
	  fprintf &#40;out, " = "&#41;;
    &#125;
    print_value &#40;out, val, 0&#41;;
  &#125; else &#123;
first, i think "print_value (out, val, 0);" should be something like : "print_expression (out, <operands_expression>, 0, 0);"

second, i tracked OP_STAT_CONSTANT and found out they are set in constant.c:110 (I_MOV), 118(I_ADDI), 124(I_OR). They are trying to make a constant value having the result. The issue var17 is initially set to 0 and so it is a constant. When it tries to evaluate "var17 = I_ADDIU(var17, 1)" it will set var17 as being a constant 1. So var17 is wrongly set as a constant operation :/ and we get "var17 = var17".

so when I try to remove lines constant.c:83-88, I get :

Code: Select all

  while &#40;1&#41; &#123;
    var17 = 0x00000000 + 0x00000001;
    InterruptManagerForKernel_7527A9BA &#40;0x00000019, 0x00000000, 0x00000001&#41;;
    if &#40;0x00000001 != 0x00000000&#41;
      continue;
    break;
  &#125;
as I feared, still not good.
hnaves
Posts: 30
Joined: Tue Feb 03, 2009 3:01 am

Post by hnaves »

Hi hlide,

I believe that the problem is fixed now.
hlide
Posts: 739
Joined: Sun Sep 10, 2006 2:31 am

Post by hlide »

ackonwledged. I'll try when i have time and when I'm getting familiar with GIT :/ (which action you should do like a Update SVN ?)
hnaves
Posts: 30
Joined: Tue Feb 03, 2009 3:01 am

Post by hnaves »

Some useful links for GIT

http://www.kernel.org/pub/software/scm/ ... orial.html

http://www.kernel.org/pub/software/scm/ ... ryday.html

http://www.kernel.org/pub/software/scm/ ... anual.html

To "update" (in the svn sense) you must perform a "git pull"

Hlide, as soon as you get acquainted to GIT, I will give you commit permissions to the repository.
hlide
Posts: 739
Joined: Sun Sep 10, 2006 2:31 am

Post by hlide »

When doinga "pull", I got this :

Code: Select all

git.exe pull "origin" 

remote&#58; Compressing objects&#58; 100% &#40;17/17&#41;   &#91;K
remote&#58; Compressing objects&#58; 100% &#40;17/17&#41;, done.&#91;K
remote&#58; Total 26 &#40;delta 13&#41;, reused 22 &#40;delta 9&#41;&#91;K
From git&#58;//repo.or.cz/pspdecompiler
   9857781..52f17f2  master     -> origin/master
Updating 9857781..52f17f2
constants.c&#58; needs update
output.c&#58; needs update
ssa.c&#58; needs update
fatal&#58; Entry 'constants.c' not uptodate. Cannot merge.
hlide
Posts: 739
Joined: Sun Sep 10, 2006 2:31 am

Post by hlide »

hnaves wrote:I believe that the problem is fixed now.
I think so as I cannot see the bugs I used to see any longer :)
hlide
Posts: 739
Joined: Sun Sep 10, 2006 2:31 am

Post by hlide »

pspdecompiler has trouble with tail call.

int f(int a) { return g(a); }

f will call g through a J instead of JAL, and it seems f is not reckoned properly because of this.

J can be considered as a call if it has the same arguments and result registers and the callee is defined as a function. So this J can be handled as a return.
KickinAezz
Posts: 328
Joined: Sun Jun 03, 2007 10:05 pm

Post by KickinAezz »

Awesome project.

Hopefully once PSP GO! is hacked, we can play with bluetooth protocols unrestrictedly.
Intrigued by PSP system Since December 2006.
Use it more for Development than for Gaming.
User avatar
Wally
Posts: 663
Joined: Mon Sep 26, 2005 11:25 am

Post by Wally »

KickinAezz wrote:Awesome project.

Hopefully once PSP GO! is hacked, we can play with bluetooth protocols unrestrictedly.
that is if anyone buys that damn ugly thing
hnaves
Posts: 30
Joined: Tue Feb 03, 2009 3:01 am

Post by hnaves »

hlide wrote:pspdecompiler has trouble with tail call.

int f(int a) { return g(a); }

f will call g through a J instead of JAL, and it seems f is not reckoned properly because of this.

J can be considered as a call if it has the same arguments and result registers and the callee is defined as a function. So this J can be handled as a return.
Did you manage to solve the problem?
hlide
Posts: 739
Joined: Sun Sep 10, 2006 2:31 am

Post by hlide »

no, i didn't. I don't know all the details about pspdecompiler source enough to solve those problems.

I also noticed that constant return values are not always treated (I was forced to use prxtool to retrieve some return values).

Have you any plan to handle float as well ? their handling is quite similar to int but don't know if it is easily feasible.
Post Reply