Files
pspsdk/tools/psp-build-exports.c
2020-06-24 18:02:19 +02:00

1058 lines
20 KiB
C

/*
* PSP Software Development Kit - https://github.com/pspdev
* -----------------------------------------------------------------------
* Licensed under the BSD license, see LICENSE in PSPSDK root for details.
*
* psp-build-exports.c - Simple program to build an export file.
*
* Copyright (c) 2005 James Forshaw <tyranid@gmail.com>
*
*/
#include <stdio.h>
#include <getopt.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <ctype.h>
#include "sha1.h"
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#define MAX_LIB_NAME 27
#define MAX_LIB_FUNCS 65535
#define MAX_LIB_VARS 255
#define MAX_LIB_ENTRY_NAME 127
#define MAX_ERROR 1024
#define MAX_LINE 1024
#define SYSTEM_LIB_NAME "syslib"
enum PspConfigMode
{
PSP_BUILD_UNKNOWN,
PSP_BUILD_EXPORTS,
PSP_BUILD_STUBS,
PSP_BUILD_STUBS_NEW,
};
struct psp_export
{
struct psp_export *pNext;
unsigned int nid;
char name[MAX_LIB_ENTRY_NAME+1];
};
struct psp_alias
{
struct psp_alias *pNext;
char name[MAX_LIB_ENTRY_NAME+1];
char alias[MAX_LIB_ENTRY_NAME+1];
};
struct psp_lib
{
struct psp_lib *pNext;
char name[MAX_LIB_NAME+1];
unsigned short ver;
unsigned short attr;
int funcCount;
struct psp_export *pFuncHead;
int varCount;
struct psp_export *pVarHead;
struct psp_alias *pAliasHead;
};
struct export_cmd
{
const char *name;
int params;
int (*handler)(char ** params);
};
/* Specifies that the current usage is to the print the pspsdk path */
static enum PspConfigMode g_outputmode;
static int g_verbose = 0;
static const char *g_infile;
static char g_errstring[MAX_ERROR] = "No Error";
static struct psp_lib *g_libhead = NULL;
static struct psp_lib *g_currlib = NULL;
static int g_libcount = 0;
void free_export_chain(struct psp_export *pHead)
{
struct psp_export *pNext;
pNext = pHead;
while(pNext != NULL)
{
struct psp_export *pTemp;
pTemp = pNext->pNext;
free(pNext);
pNext = pTemp;
}
}
void free_alias_chain(struct psp_alias *pHead)
{
struct psp_alias *pNext;
pNext = pHead;
while(pNext != NULL)
{
struct psp_alias *pTemp;
pTemp = pNext->pNext;
free(pNext);
pNext = pTemp;
}
}
void free_lib_data(void)
{
struct psp_lib *pNext;
if(g_currlib != NULL)
{
free_export_chain(g_currlib->pFuncHead);
free_export_chain(g_currlib->pVarHead);
free_alias_chain(g_currlib->pAliasHead);
free(g_currlib);
g_currlib = NULL;
}
pNext = g_libhead;
while(pNext != NULL)
{
struct psp_lib *pTemp;
pTemp = pNext->pNext;
free_export_chain(pNext->pFuncHead);
free_export_chain(pNext->pVarHead);
free_alias_chain(pNext->pAliasHead);
free(pNext);
pNext = pTemp;
}
g_libhead = NULL;
}
const char *find_alias(struct psp_alias *pHead, const char *name)
{
struct psp_alias *pNext;
const char *szAlias = NULL;
pNext = pHead;
while(pNext)
{
if(strcmp(name, pNext->name) == 0)
{
szAlias = pNext->alias;
break;
}
pNext = pNext->pNext;
}
return szAlias;
}
static struct option arg_opts[] =
{
{"build-exports", no_argument, NULL, 'b'},
{"build-stubs", no_argument, NULL, 's'},
{"build-stubs-new", no_argument, NULL, 'k'},
{"verbose", no_argument, NULL, 'v'},
{ NULL, 0, NULL, 0 }
};
/* Process the arguments */
int process_args(int argc, char **argv)
{
int ret = 0;
int ch;
g_outputmode = PSP_BUILD_UNKNOWN;
ch = getopt_long(argc, argv, "bsvk", arg_opts, NULL);
while(ch != -1)
{
switch(ch)
{
case 'b' : g_outputmode = PSP_BUILD_EXPORTS;
ret = 1;
break;
case 's' : g_outputmode = PSP_BUILD_STUBS;
ret = 1;
break;
case 'v' : g_verbose = 1;
break;
case 'k' : g_outputmode = PSP_BUILD_STUBS_NEW;
ret = 1;
break;
default : //fprintf(stderr, "Invalid option '%c'\n", ch);
break;
};
ch = getopt_long(argc, argv, "bsvk", arg_opts, NULL);
}
argc -= optind;
argv += optind;
if(argc < 1)
{
return 0;
}
g_infile = argv[0];
return ret;
}
void print_help(void)
{
fprintf(stderr, "Usage: psp-build-exports -b|-s|-k [-v] export.exp\n");
fprintf(stderr, "Options:\n");
fprintf(stderr, "-b, --build-exports : Build an export file to stdout\n");
fprintf(stderr, "-s, --build-stubs : Build a batch of stub files for the exports\n");
fprintf(stderr, "-k, --build-stubs-new : Build a batch of stub files (in new format)\n");
fprintf(stderr, "-v, --verbose : Verbose output\n");
}
void strip_whitespace(char *dest, const char *src)
{
assert(dest != NULL);
assert(src != NULL);
while(*src != 0)
{
if(!isspace(*src))
{
*dest = *src;
dest++;
}
src++;
}
*dest = 0;
}
/*
Parse a string of the form FUNC(arg1,arg2,...)
Returns the number of parameters or -1 on error.
*/
int parse_string(char *line, char **params, int max_params)
{
int pos;
int param_start;
char *next_param;
int param_count;
assert(line != NULL);
assert(params != NULL);
pos = 0;
/* Parse off "function" name */
while((isalnum(line[pos])) || (line[pos] == '_'))
{
pos++;
}
/* If we don't have any thing else than this is just the function */
if(line[pos] == 0)
{
return 0;
}
if(line[pos] != '(')
{
snprintf(g_errstring, MAX_ERROR, "Invalid character %c, expected (", line[pos]);
return -1;
}
/* Terminate */
line[pos++] = 0;
param_start = pos;
/* Scan parameters */
while((isalnum(line[pos])) || (line[pos] == '_') || (line[pos] == ','))
{
pos++;
}
if(line[pos] == 0)
{
snprintf(g_errstring, MAX_ERROR, "Missing closing brace ')'");
return -1;
}
if(line[pos] != ')')
{
snprintf(g_errstring, MAX_ERROR, "Invalid character %c, expected ')'", line[pos]);
return -1;
}
if(line[pos+1] != 0)
{
snprintf(g_errstring, MAX_ERROR, "Trailing characters after bracket close %d", line[pos+1]);
return -1;
}
line[pos] = 0;
param_count = 0;
next_param = strtok(&line[param_start], ",");
while(next_param != NULL)
{
if(param_count == max_params)
{
snprintf(g_errstring, MAX_ERROR, "Run out of space for parameters");
return -1;
}
params[param_count++] = next_param;
next_param = strtok(NULL, ",");
}
return param_count;
}
void build_exports_output_extern(struct psp_export *pHead)
{
struct psp_export *pExp;
pExp = pHead;
while(pExp != NULL)
{
fprintf(stdout, "extern int %s;\n", pExp->name);
pExp = pExp->pNext;
}
}
void build_exports_output_nids(struct psp_export *pHead)
{
struct psp_export *pExp;
pExp = pHead;
while(pExp != NULL)
{
fprintf(stdout, "\t0x%08X,\n", pExp->nid);
pExp = pExp->pNext;
}
}
void build_exports_output_names(struct psp_export *pHead)
{
struct psp_export *pExp;
pExp = pHead;
while(pExp != NULL)
{
fprintf(stdout, "\t(unsigned int) &%s,\n", pExp->name);
pExp = pExp->pNext;
}
}
void build_exports(void)
{
struct psp_lib *pLib;
fprintf(stdout, "#include <pspmoduleexport.h>\n");
fprintf(stdout, "#define NULL ((void *) 0)\n\n");
/* Write out the nid tables */
pLib = g_libhead;
while(pLib != NULL)
{
/* Print extern */
build_exports_output_extern(pLib->pFuncHead);
build_exports_output_extern(pLib->pVarHead);
fprintf(stdout, "static const unsigned int __%s_exports[%d] __attribute__((section(\".rodata.sceResident\"))) = {\n",
pLib->name, (pLib->funcCount + pLib->varCount) * 2);
/* Print nid */
build_exports_output_nids(pLib->pFuncHead);
build_exports_output_nids(pLib->pVarHead);
/* Print names */
build_exports_output_names(pLib->pFuncHead);
build_exports_output_names(pLib->pVarHead);
fprintf(stdout, "};\n\n");
pLib = pLib->pNext;
}
fprintf(stdout, "const struct _PspLibraryEntry __library_exports[%d] __attribute__((section(\".lib.ent\"), used)) = {\n", g_libcount);
pLib = g_libhead;
while(pLib != NULL)
{
if(strcmp(pLib->name, SYSTEM_LIB_NAME) == 0)
{
fprintf(stdout, "\t{ NULL, ");
}
else
{
fprintf(stdout, "\t{ \"%s\", ", pLib->name);
}
fprintf(stdout, "0x%04X, 0x%04X, 4, %d, %d, (unsigned int *) &__%s_exports },\n", pLib->ver, pLib->attr,
pLib->varCount, pLib->funcCount, pLib->name);
pLib = pLib->pNext;
}
fprintf(stdout, "};\n");
}
void build_stubs_output_lib(struct psp_lib *pLib)
{
FILE *fp;
char filename[256];
snprintf(filename, 256, "%s.S", pLib->name);
if(g_verbose)
{
fprintf(stderr, "Writing output file %s\n", filename);
}
fp = fopen(filename, "w");
if(fp != NULL)
{
struct psp_export *pExp;
fprintf(fp, "\t.set noreorder\n\n");
fprintf(fp, "#include \"pspstub.s\"\n\n");
fprintf(fp, "\tSTUB_START \"%s\",0x%08X,0x%08X\n", pLib->name, ((pLib->attr | 0x8) << 16) | pLib->ver,
(pLib->funcCount << 16) | 5);
pExp = pLib->pFuncHead;
while(pExp != NULL)
{
const char *alias;
alias = find_alias(pLib->pAliasHead, pExp->name);
if(alias)
{
fprintf(fp, "\tSTUB_FUNC_WITH_ALIAS 0x%08X,%s,%s\n", pExp->nid, pExp->name,alias);
}
else
{
fprintf(fp, "\tSTUB_FUNC 0x%08X,%s\n", pExp->nid, pExp->name);
}
pExp = pExp->pNext;
}
fprintf(fp, "\tSTUB_END\n");
fclose(fp);
}
else
{
fprintf(stderr, "Error, couldn't open file %s for writing\n", filename);
}
}
void build_stubs_output_lib_new(struct psp_lib *pLib)
{
FILE *fp;
char filename[256];
int i;
snprintf(filename, 256, "%s.S", pLib->name);
if(g_verbose)
{
fprintf(stderr, "Writing output file %s\n", filename);
}
fp = fopen(filename, "w");
if(fp != NULL)
{
struct psp_export *pExp;
fprintf(fp, "\t.set noreorder\n\n");
fprintf(fp, "#include \"pspimport.s\"\n\n");
fprintf(fp, "// Build files\n");
fprintf(fp, "// %s_0000.o ", pLib->name);
pExp = pLib->pFuncHead;
i = 1;
while(pExp != NULL)
{
fprintf(fp, "%s_%04d.o ", pLib->name, i++);
pExp = pExp->pNext;
}
fprintf(fp, "\n\n");
fprintf(fp, "#ifdef F_%s_0000\n", pLib->name);
fprintf(fp, "\tIMPORT_START \"%s\",0x%08X\n", pLib->name, ((pLib->attr | 0x8) << 16) | pLib->ver);
fprintf(fp, "#endif\n");
pExp = pLib->pFuncHead;
i = 1;
while(pExp != NULL)
{
const char *alias;
fprintf(fp, "#ifdef F_%s_%04d\n", pLib->name, i++);
alias = find_alias(pLib->pAliasHead, pExp->name);
if(alias)
{
fprintf(fp, "\tIMPORT_FUNC_WITH_ALIAS \"%s\",0x%08X,%s,%s\n", pLib->name, pExp->nid, pExp->name, alias);
}
else
{
fprintf(fp, "\tIMPORT_FUNC \"%s\",0x%08X,%s\n", pLib->name, pExp->nid, pExp->name);
}
fprintf(fp, "#endif\n");
pExp = pExp->pNext;
}
fclose(fp);
}
else
{
fprintf(stderr, "Error, couldn't open file %s for writing\n", filename);
}
}
void build_stubs(void)
{
struct psp_lib *pLib;
pLib = g_libhead;
while(pLib != NULL)
{
if(strcmp(pLib->name, SYSTEM_LIB_NAME))
{
if(g_outputmode == PSP_BUILD_STUBS)
{
build_stubs_output_lib(pLib);
}
else
{
build_stubs_output_lib_new(pLib);
}
}
pLib = pLib->pNext;
}
}
int validate_number(const char *str, unsigned int *num)
{
char *endp;
assert(str != NULL);
assert(num != NULL);
if(!isxdigit(*str))
{
snprintf(g_errstring, MAX_ERROR, "'%s' is an invalid number", str);
return 0;
}
*num = strtoul(str, &endp, 0);
if(*endp != 0)
{
snprintf(g_errstring, MAX_ERROR, "'%s' has trailing non-numeric characters", str);
return 0;
}
return 1;
}
int psp_begin_exports(char **params)
{
/* Do nothing */
return 1;
}
int psp_end_exports(char **params)
{
if(g_currlib != NULL)
{
/* Error, missing end of exports */
}
return 1;
}
int psp_export_start(char **params)
{
unsigned int ver;
unsigned int attr;
if(g_currlib != NULL)
{
snprintf(g_errstring, MAX_ERROR, "Previous library '%s' not ended", g_currlib->name);
return 0;
}
if(validate_number(params[1], &ver) == 0)
{
return 0;
}
if(validate_number(params[2], &attr) == 0)
{
return 0;
}
g_currlib = (struct psp_lib *) malloc(sizeof(struct psp_lib));
if(g_currlib == NULL)
{
snprintf(g_errstring, MAX_ERROR, "Could not allocate memory for library");
return 0;
}
memset(g_currlib, 0, sizeof(struct psp_lib));
g_libcount++;
strncpy(g_currlib->name, params[0], MAX_LIB_NAME);
g_currlib->ver = ver;
g_currlib->attr = attr;
return 1;
}
int psp_export_end(char **params)
{
struct psp_lib *pNext;
if(g_currlib == NULL)
{
snprintf(g_errstring, MAX_ERROR, "No matching start command for library end");
return 1;
}
/* Add to the end of the chain */
if(g_libhead == NULL)
{
g_libhead = g_currlib;
}
else
{
pNext = g_libhead;
while(pNext->pNext != NULL)
{
pNext = pNext->pNext;
}
pNext->pNext = g_currlib;
}
g_currlib = NULL;
return 1;
}
int internal_do_export(const char *name, unsigned int nid, struct psp_export **pHead)
{
struct psp_export *pNew;
assert(name != NULL);
assert(pHead != NULL);
pNew = (struct psp_export *) malloc(sizeof(struct psp_export));
if(pNew != NULL)
{
struct psp_export *pNext;
memset(pNew, 0, sizeof(struct psp_export));
strncpy(pNew->name, name, MAX_LIB_ENTRY_NAME);
pNew->nid = nid;
if(*pHead == NULL)
{
*pHead = pNew;
}
else
{
pNext = *pHead;
while(pNext->pNext != NULL)
{
pNext = pNext->pNext;
}
pNext->pNext = pNew;
}
}
else
{
snprintf(g_errstring, MAX_ERROR, "Could not allocate memory for export %s", name);
return 0;
}
return 1;
}
int psp_export_func_nid(char **params)
{
unsigned int nid;
if(g_currlib == NULL)
{
snprintf(g_errstring, MAX_ERROR, "Cannot export function, not in a library definition");
return 0;
}
if(validate_number(params[1], &nid) == 0)
{
return 0;
}
if(g_currlib->funcCount < MAX_LIB_FUNCS)
{
if(internal_do_export(params[0], nid, &g_currlib->pFuncHead))
{
g_currlib->funcCount++;
return 1;
}
}
else
{
snprintf(g_errstring, MAX_ERROR, "Too many functions defined, cannot export another");
}
return 0;
}
int psp_export_func_hash(char **params)
{
unsigned int nid;
unsigned char hash[SHA1_DIGEST_SIZE];
if(g_currlib == NULL)
{
snprintf(g_errstring, MAX_ERROR, "Cannot export function, not in a library definition");
return 0;
}
sha1(hash, (unsigned char *) params[0], strlen(params[0]));
nid = hash[0] | (hash[1] << 8) | (hash[2] << 16) | (hash[3] << 24);
if(g_currlib->funcCount < MAX_LIB_FUNCS)
{
if(internal_do_export(params[0], nid, &g_currlib->pFuncHead))
{
g_currlib->funcCount++;
return 1;
}
}
else
{
snprintf(g_errstring, MAX_ERROR, "Too many functions defined, cannot export another");
}
return 0;
}
int psp_export_var_nid(char **params)
{
unsigned int nid;
if(g_currlib == NULL)
{
snprintf(g_errstring, MAX_ERROR, "Cannot export variable, not in a library definition");
return 0;
}
if(validate_number(params[1], &nid) == 0)
{
return 0;
}
if(g_currlib->varCount < MAX_LIB_VARS)
{
if(internal_do_export(params[0], nid, &g_currlib->pVarHead))
{
g_currlib->varCount++;
return 1;
}
}
else
{
snprintf(g_errstring, MAX_ERROR, "Too many variables defined, cannot export another");
}
return 0;
}
int psp_export_var_hash(char **params)
{
unsigned int nid;
unsigned char hash[SHA1_DIGEST_SIZE];
if(g_currlib == NULL)
{
snprintf(g_errstring, MAX_ERROR, "Cannot export variable, not in a library definition");
return 0;
}
sha1(hash, (unsigned char *) params[0], strlen(params[0]));
nid = hash[0] | (hash[1] << 8) | (hash[2] << 16) | (hash[3] << 24);
if(g_currlib->varCount < MAX_LIB_VARS)
{
if(internal_do_export(params[0], nid, &g_currlib->pVarHead))
{
g_currlib->varCount++;
return 1;
}
}
else
{
snprintf(g_errstring, MAX_ERROR, "Too many variables defined, cannot export another");
}
return 0;
}
int psp_export_alias(char **params)
{
struct psp_alias *pAlias;
if(g_currlib == NULL)
{
snprintf(g_errstring, MAX_ERROR, "Cannot alias name, not in a library definition");
return 0;
}
pAlias = (struct psp_alias *) malloc(sizeof(struct psp_alias));
if(pAlias == NULL)
{
snprintf(g_errstring, MAX_ERROR, "Cannot allocate memory for alias");
return 0;
}
memset(pAlias, 0, sizeof(*pAlias));
strncpy(pAlias->name, params[0], MAX_LIB_ENTRY_NAME);
strncpy(pAlias->alias, params[1], MAX_LIB_ENTRY_NAME);
if(g_currlib->pAliasHead == NULL)
{
g_currlib->pAliasHead = pAlias;
}
else
{
struct psp_alias *pNext = g_currlib->pAliasHead;
while(pNext->pNext)
{
pNext = pNext->pNext;
}
pNext->pNext = pAlias;
}
fprintf(stderr, "Alias %s->%s\n", params[0], params[1]);
return 1;
}
struct export_cmd commands[] =
{
{ "PSP_BEGIN_EXPORTS", 0, psp_begin_exports },
{ "PSP_END_EXPORTS", 0, psp_end_exports },
{ "PSP_EXPORT_START", 3, psp_export_start },
{ "PSP_EXPORT_END", 0, psp_export_end },
{ "PSP_EXPORT_FUNC_NID", 2, psp_export_func_nid },
{ "PSP_EXPORT_FUNC_HASH", 1, psp_export_func_hash },
{ "PSP_EXPORT_FUNC", 1, psp_export_func_hash },
{ "PSP_EXPORT_VAR_NID", 2, psp_export_var_nid },
{ "PSP_EXPORT_VAR", 1, psp_export_var_hash },
{ "PSP_EXPORT_VAR_HASH", 1, psp_export_var_hash },
{ "PSP_EXPORT_ALIAS", 2, psp_export_alias },
{ NULL, 0, NULL }
};
int process_command(const char *cmd, char ** params, int param_count)
{
int i;
i = 0;
while(commands[i].name != NULL)
{
if(strcmp(cmd, commands[i].name) == 0)
{
assert(commands[i].handler != NULL);
if(param_count != commands[i].params)
{
snprintf(g_errstring, MAX_ERROR, "Incorrect number of parameters, have %d, expect %d\n",
param_count, commands[i].params);
break;
}
else
{
return commands[i].handler(params);
}
}
i++;
}
if(commands[i].name == NULL)
{
snprintf(g_errstring, MAX_ERROR, "Unknown command '%s'", cmd);
}
return 0;
}
int load_exports(void)
{
FILE *fp;
int line_no = 1;
fp = fopen(g_infile, "r");
if(fp != NULL)
{
char curr_line[MAX_LINE];
char strip_line[MAX_LINE];
while(fgets(curr_line, MAX_LINE, fp))
{
strip_whitespace(strip_line, curr_line);
if((strip_line[0] != 0) && (strip_line[0] != '#'))
{
int ret;
char *params[16];
ret = parse_string(strip_line, params, 16);
if(ret >= 0)
{
if(g_verbose)
{
int i;
fprintf(stderr, "Line %d, Name: %s ", line_no, strip_line);
for(i = 0; i < ret; i++)
{
fprintf(stderr, "Arg %d: %s ", i, params[i]);
}
fprintf(stderr, "\n");
}
if(process_command(strip_line, params, ret) == 0)
{
fprintf(stderr, "Error, line %d: %s\n", line_no, g_errstring);
return 0;
}
}
else
{
fprintf(stderr, "Error, line %d: %s\n", line_no, g_errstring);
return 0;
}
}
line_no++;
}
if(g_currlib)
{
fprintf(stderr, "Error, reached end of file while still exporting a library\n");
return 0;
}
}
else
{
fprintf(stderr, "Could not open file %s\n", g_infile);
return 0;
}
return 1;
}
void dump_exports(void)
{
struct psp_lib *pLib;
int count;
pLib = g_libhead;
count = 1;
while(pLib != NULL)
{
fprintf(stderr, "Export %d '%s', ver %04X, attr %04X\n", count, pLib->name, pLib->ver, pLib->attr);
fprintf(stderr, "Funcs %d-%p, Vars %d-%p\n", pLib->funcCount, pLib->pFuncHead, pLib->varCount, pLib->pVarHead);
if(pLib->pFuncHead != NULL)
{
int func;
struct psp_export *pFunc;
func = 0;
pFunc = pLib->pFuncHead;
while(pFunc != NULL)
{
fprintf(stderr, "Function %d - '%s', nid %08X\n", func++, pFunc->name, pFunc->nid);
pFunc = pFunc->pNext;
}
}
if(pLib->pVarHead != NULL)
{
int var;
struct psp_export *pVar;
var = 0;
pVar = pLib->pVarHead;
while(pVar != NULL)
{
fprintf(stderr, "Variable %d - '%s', nid %08X\n", var++, pVar->name, pVar->nid);
pVar = pVar->pNext;
}
}
count++;
pLib = pLib->pNext;
}
}
int main(int argc, char **argv)
{
if(process_args(argc, argv))
{
if(load_exports())
{
if(g_verbose)
{
dump_exports();
}
switch(g_outputmode)
{
case PSP_BUILD_EXPORTS: build_exports();
break;
/* Do the same for both */
case PSP_BUILD_STUBS_NEW:
case PSP_BUILD_STUBS : build_stubs();
break;
default : /* Argh */
break;
};
}
free_lib_data();
}
else
{
print_help();
}
return 0;
}