aboutsummaryrefslogtreecommitdiff
path: root/sim/common/sim-gx.c
diff options
context:
space:
mode:
Diffstat (limited to 'sim/common/sim-gx.c')
-rw-r--r--sim/common/sim-gx.c807
1 files changed, 807 insertions, 0 deletions
diff --git a/sim/common/sim-gx.c b/sim/common/sim-gx.c
new file mode 100644
index 0000000..413739d
--- /dev/null
+++ b/sim/common/sim-gx.c
@@ -0,0 +1,807 @@
+/* GX target-independent functions for block translation.
+ Copyright (C) 1998 Cygnus Solutions. */
+
+
+#include "sim-main.h"
+#include "sim-assert.h"
+#include "sim-gx.h"
+
+#include "config.h"
+#include "cconfig.h"
+
+/* shared object functions */
+#ifdef HAVE_DLFCN_H
+#include <dlfcn.h>
+#else
+#error "need dlfcn.h"
+#endif
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#else
+#error "need errno.h"
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#ifdef HAVE_TIME_H
+#include <time.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+
+#include "bfd.h"
+
+
+
+
+/* Load the object file with given gx block. Return pointer to GX
+ function or NULL on failure. */
+
+sim_gx_function
+sim_gx_compiled_block_f(sim_gx_compiled_block* gx)
+{
+ sim_gx_function f = gx->function_dlhandle;
+ SIM_DESC sd = current_state;
+ int rc;
+
+ if(f == NULL)
+ {
+ /* compile object */
+ if(gx->object_name == NULL && gx->source_name != NULL)
+ {
+ char compile_command[2000];
+
+ gx->object_name = strdup(gx->source_name);
+ /* turn *.c into *.o */
+ gx->object_name[strlen(gx->object_name)]='o';
+
+ /* compute command string to compile object */
+ sprintf(compile_command,
+ "make -f %s OBJ=%s SRC=%s gx",
+#define GX_MAKEFILE "--no-makefile-yet--"
+ GX_MAKEFILE,
+ gx->object_name,
+ gx->source_name);
+
+ rc = system(compile_command);
+ if(rc != 0)
+ {
+ sim_io_error(sd, "Compile error rc %d for GX source %s: %s",
+ rc,
+ gx->source_name,
+ strerror(errno));
+ }
+ }
+
+ /* load object */
+ if(gx->object_dlhandle == NULL && gx->object_name != NULL)
+ {
+ gx->object_dlhandle = dlopen(gx->object_name, RTLD_NOW);
+ if(gx->object_dlhandle == NULL)
+ {
+ sim_io_error(sd, "Load error for GX object %s: %s",
+ gx->object_name,
+ dlerror());
+ }
+ }
+
+ /* locate function */
+ if(gx->function_dlhandle == NULL && gx->object_dlhandle != NULL && gx->symbol_name != NULL)
+ {
+ f = gx->function_dlhandle = dlsym(gx->object_dlhandle, gx->symbol_name);
+ if(f == NULL)
+ {
+ sim_io_error(sd, "Resolve error for GX object %s symbol %s: %s",
+ gx->object_name,
+ gx->symbol_name,
+ dlerror());
+ }
+ }
+ } /* f == NULL */
+
+ return f;
+}
+
+
+
+/* Forget about given GX block. Remove its source/object; unload it
+ from memory. */
+void
+sim_gx_compiled_block_dispose(sim_gx_compiled_block* gx)
+{
+ SIM_DESC sd = current_state;
+ int rc;
+
+ /* forget dl information */
+ gx->function_dlhandle = NULL;
+
+ /* unload shared library */
+ if(gx->object_dlhandle != NULL)
+ {
+ rc = dlclose(gx->object_dlhandle);
+ if(rc != 0)
+ {
+ sim_io_error(sd, "dlclose() error for GX object %s: %s",
+ gx->object_name,
+ dlerror());
+ }
+ gx->object_dlhandle = NULL;
+ }
+
+ /* final gasps */
+ zfree(gx->source_name);
+ zfree(gx->object_name);
+ zfree(gx->symbol_name);
+ zfree(gx);
+}
+
+
+
+/* Translate a piece of the code segment around given PC, in given mode. */
+sim_gx_block*
+sim_gx_block_create(sim_cia cia)
+{
+ sim_gx_block* block;
+
+ /* allocate emtpy block */
+ block = zalloc(sizeof(sim_gx_block));
+
+ /* initialize block bounds, callback struct etc. */
+ tgx_block_ctor(block, cia);
+
+ /* create learning mode translation */
+ sim_gx_block_translate(block, 0 /* learning mode */);
+
+ /* add block to block list */
+ sim_gx_block_add(block);
+
+ return block;
+}
+
+
+
+/* Write the current block list to the state file */
+void
+sim_gx_write_block_list()
+{
+ int i;
+ SIM_DESC sd = current_state;
+ sim_gx_block_list* blocks = STATE_BLOCKS(sd);
+ FILE* f;
+ char state_file_name[PATH_MAX];
+ char *exec_name;
+
+ /* get base of executable name */
+ exec_name = bfd_get_filename(STATE_PROG_BFD(sd));
+ if(strrchr(exec_name, '/') != NULL)
+ exec_name = strrchr(exec_name, '/') + 1;
+
+ /* generate base name */
+ sprintf(state_file_name, "%s/%s.gx",
+ GX_DIR,
+ exec_name);
+
+ f = fopen(state_file_name, "w");
+ if(f == NULL)
+ {
+ sim_io_error(sd, "Error: cannot write to state file %s, errno %d",
+ state_file_name, errno);
+ }
+
+ fprintf(f, "# This file was automatically generated. Do not edit.\n");
+
+ /* write block descriptors into state file */
+ for(i=0; i<blocks->gx_blocks_used; i++)
+ {
+ sim_gx_block* gx = blocks->gx_blocks[i];
+ sim_gx_compiled_block* block;
+ int j;
+ int age;
+
+ age = time(NULL) - gx->learn_last_change; /* store interval */
+ fprintf(f, "BLOCK 0x%lx 0x%lx %u %u\n", gx->origin, gx->length, gx->divisor, age);
+ fprintf(f, "FLAGS ");
+ for(j=0; j<GX_PC_FLAGS_INDEX(gx, gx->origin + gx->length); j++)
+ {
+ fprintf(f, "%2x ", gx->pc_flags[j]);
+ }
+ fprintf(f, "\n");
+
+ /* write learning mode names */
+ block = gx->learning_block;
+ fprintf(f, "LEARNING %s %s %s %lu %u\n",
+ block->source_name, block->object_name, block->symbol_name,
+ gx->compile_time, gx->opt_compile_count);
+
+ /* write optimized mode names */
+ block = gx->optimized_block;
+ if(block)
+ fprintf(f, "OPTIMIZED %s %s %s\n",
+ block->source_name, block->object_name, block->symbol_name);
+
+ /* NB: other fields will be filled in with freshly guessed values */
+ }
+
+ (void) fclose(f);
+}
+
+
+
+void
+print_gx_blocks(sim_gx_block_list* blocks, char* where)
+{
+ printf("print_gx_blocks: %s\n", where);
+
+ if(blocks == NULL)
+ printf("(null)\n");
+ else
+ {
+ int i;
+ printf("size: %d, used: %d\n",
+ blocks->gx_blocks_size, blocks->gx_blocks_used);
+
+ /* linear search */
+ for(i=0; i<blocks->gx_blocks_used; i++)
+ {
+ sim_gx_block* gx = blocks->gx_blocks[i];
+ printf("block %d: %p\n", i, (void*) gx);
+ if(gx == NULL)
+ printf("** NULL!\n");
+ else
+ printf(" begin 0x%08x length 0x%08x [opt %d%s]\n",
+ (unsigned)gx->origin, (unsigned)gx->length,
+ gx->opt_compile_count,
+ (gx->optimized_block ? " loaded" : " discarded"));
+ }
+
+ }
+}
+
+
+
+/* Read the current block list from the cache */
+void
+sim_gx_read_block_list()
+{
+ SIM_DESC sd = current_state;
+ FILE* f;
+ char state_file_name[PATH_MAX];
+ char *exec_name;
+
+ /* check for block */
+ if(STATE_PROG_BFD(sd) == NULL)
+ return;
+
+ /* get base of executable name */
+ exec_name = bfd_get_filename(STATE_PROG_BFD(sd));
+ if(strrchr(exec_name, '/') != NULL)
+ exec_name = strrchr(exec_name, '/') + 1;
+
+ /* generate base name */
+ sprintf(state_file_name, "%s/%s.gx",
+ GX_DIR,
+ exec_name);
+
+ f = fopen(state_file_name, "r");
+ if(f == NULL)
+ {
+ /* XXX: print warning */
+ return;
+ }
+
+ fscanf(f, "#%*[^\n]\n"); /* swallow # comment line */
+
+ while(1)
+ {
+ unsigned_4 origin, length;
+ unsigned divisor;
+ sim_gx_block* gx;
+ int rc;
+ sim_gx_compiled_block* block;
+ unsigned age;
+ int j;
+
+ rc = fscanf(f, "BLOCK 0x%0lx 0x%lx %u %u\n", & origin, & length, & divisor, & age);
+ if(rc != 4) /* not all fields matched - assume EOF */
+ break;
+
+ gx = zalloc(sizeof(sim_gx_block));
+
+ /* initialize block bounds, callback struct etc. */
+ tgx_block_ctor2(gx, origin, length, divisor);
+
+ /* read flags */
+ fscanf(f, "FLAGS");
+ for(j=0; j<GX_PC_FLAGS_INDEX(gx, gx->origin + gx->length); j++)
+ {
+ unsigned value;
+ fscanf(f, "%2x ", & value);
+ gx->pc_flags[j] = (unsigned_1) value;
+ }
+ fscanf(f, "\n");
+
+ /* read learning mode info */
+ block = zalloc(sizeof(sim_gx_compiled_block));
+ gx->learning_block = block;
+ block->source_name = zalloc(PATH_MAX);
+ block->object_name = zalloc(PATH_MAX);
+ block->symbol_name = zalloc(PATH_MAX);
+ fscanf(f, "LEARNING %s %s %s %lu %u\n",
+ block->source_name, block->object_name, block->symbol_name,
+ & gx->compile_time, & gx->opt_compile_count);
+
+ /* read optimized mode info */
+ block = zalloc(sizeof(sim_gx_compiled_block));
+ gx->optimized_block = block;
+ block->source_name = zalloc(PATH_MAX);
+ block->object_name = zalloc(PATH_MAX);
+ block->symbol_name = zalloc(PATH_MAX);
+ rc = fscanf(f, "OPTIMIZED %s %s %s\n",
+ block->source_name, block->object_name, block->symbol_name);
+ if(rc != 3)
+ {
+ /* oops, not an optimized block */
+ zfree(block->source_name);
+ zfree(block->object_name);
+ zfree(block->symbol_name);
+ zfree(block);
+ gx->optimized_block = NULL;
+ }
+
+ /* fill in remaining fields */
+ gx->learn_last_change = time(NULL) - age; /* make absolute */
+
+ /* store it away */
+ sim_gx_block_add(gx);
+ }
+
+ print_gx_blocks(STATE_BLOCKS(sd), "after restoring state");
+}
+
+
+
+
+
+
+/* Add a gx block to list */
+void
+sim_gx_block_add(sim_gx_block* block)
+{
+ SIM_DESC sd = current_state;
+ sim_gx_block_list* blocks = STATE_BLOCKS(sd);
+ int i;
+
+ /* print_gx_blocks(blocks, "pre add"); */
+
+ if(blocks == NULL)
+ blocks = STATE_BLOCKS(sd) = zalloc(sizeof(sim_gx_block_list));
+
+ /* need to enlarge block vector? */
+ if(blocks->gx_blocks_used == blocks->gx_blocks_size)
+ {
+ sim_gx_block** new_blocks;
+ int j;
+
+ blocks->gx_blocks_size += 20;
+ new_blocks = zalloc(blocks->gx_blocks_size * sizeof(sim_gx_block*));
+ for(j=0; j<blocks->gx_blocks_used; j++)
+ new_blocks[j] = blocks->gx_blocks[j];
+ if(blocks->gx_blocks) zfree(blocks->gx_blocks);
+ blocks->gx_blocks = new_blocks;
+ }
+
+ /* insert new block */
+ for(i=0; i<blocks->gx_blocks_used; i++)
+ {
+ ASSERT(blocks->gx_blocks[i] != NULL);
+
+ /* insertion point reached? */
+ if(blocks->gx_blocks[i]->origin > block->origin)
+ {
+ int j;
+ for(j=blocks->gx_blocks_used; j>=i; j--)
+ blocks->gx_blocks[j] = blocks->gx_blocks[j-1];
+ blocks->gx_blocks[i] = block;
+ blocks->gx_blocks_used ++;
+ break;
+ }
+ }
+
+ /* end of block vector */
+ if(i == blocks->gx_blocks_used)
+ {
+ blocks->gx_blocks[blocks->gx_blocks_used ++] = block;
+ }
+
+ /* print_gx_blocks(blocks, "post add"); */
+}
+
+
+
+/* Remove a gx block from list */
+void
+sim_gx_block_remove(sim_gx_block* block)
+{
+ SIM_DESC sd = current_state;
+ sim_gx_block_list* blocks = STATE_BLOCKS(sd);
+ int i;
+
+ /* print_gx_blocks(blocks, "pre remove"); */
+
+ /* linear search */
+ for(i=0; i<blocks->gx_blocks_used; i++)
+ {
+ if(blocks->gx_blocks[i] == block)
+ {
+ /* found it */
+ while(i < blocks->gx_blocks_used - 1)
+ {
+ blocks->gx_blocks[i] = blocks->gx_blocks[i+1];
+ i++;
+ }
+ blocks->gx_blocks_used --;
+ break;
+ }
+ }
+
+ /* print_gx_blocks(blocks, "post remove"); */
+}
+
+
+/* Find a gx block from list */
+sim_gx_block*
+sim_gx_block_find(sim_cia cia)
+{
+ SIM_DESC sd = current_state;
+ sim_gx_block_list* blocks = STATE_BLOCKS(sd);
+ int i;
+
+ if(blocks == NULL) return NULL;
+
+ /* print_gx_blocks(blocks, "pre find"); */
+
+ /* linear search */
+ for(i=0; i<blocks->gx_blocks_used; i++)
+ {
+ sim_gx_block* gx = blocks->gx_blocks[i];
+ ASSERT(gx != NULL);
+
+ if(GX_PC_INCLUDES(gx,cia))
+ {
+ return gx;
+ }
+ }
+
+ return NULL;
+}
+
+
+
+/* generate */
+void
+sim_gx_block_translate(sim_gx_block* gx, int optimized)
+{
+ char pwd_name[PATH_MAX];
+ char dir_name[PATH_MAX];
+ char base_name[PATH_MAX];
+ char compile_command[PATH_MAX*4];
+ char* exec_name;
+ SIM_DESC sd = current_state;
+ int rc;
+ sim_cia gx_cia;
+ sim_gx_compiled_block* block = zalloc(sizeof(sim_gx_compiled_block));
+ unsigned time_begin, time_end;
+
+ time_begin = time(NULL);
+
+ if(optimized) gx->optimized_block = block;
+ else gx->learning_block = block;
+
+ /* get base of executable name */
+ exec_name = bfd_get_filename(STATE_PROG_BFD(sd));
+ if(strrchr(exec_name, '/') != NULL)
+ exec_name = strrchr(exec_name, '/') + 1;
+
+ /* generate base name */
+ sprintf(dir_name, "%s/%s",
+ GX_DIR,
+ exec_name);
+
+ /* generate base name */
+ getcwd(pwd_name, sizeof(pwd_name));
+
+ /* create work directory */
+ rc = mkdir(GX_DIR, 0777);
+ if(rc != 0 &&
+ errno != EEXIST)
+ {
+ sim_io_error(sd, "Error: cannot create directory %s, errno %d",
+ GX_DIR, errno);
+ }
+
+ rc = mkdir(dir_name, 0777);
+ if(rc != 0 &&
+ errno != EEXIST)
+ {
+ sim_io_error(sd, "Error: cannot create directory %s, errno %d",
+ dir_name, errno);
+ }
+
+ /* compute base name */
+ if(optimized)
+ sprintf(base_name, "%08lx_opt%d", gx->origin, gx->opt_compile_count);
+ else
+ sprintf(base_name, "%08lx", gx->origin);
+
+ /* generate source/object file names */
+ block->source_name = zalloc(PATH_MAX);
+ block->object_name = zalloc(PATH_MAX);
+ sprintf(block->source_name, "%s/%s.c", dir_name, base_name);
+
+ /* generate symbol name for gx function */
+ block->symbol_name = zalloc(PATH_MAX);
+ sprintf(block->symbol_name, "gx_%s", base_name);
+
+ /* open source file */
+ block->source_file = fopen(block->source_name, "w");
+ if(block->source_file == NULL)
+ {
+ sim_io_error(sd, "Error: cannot open file %s, errno %d",
+ block->source_name, errno);
+ }
+
+ /* front matter */
+ fprintf(block->source_file, "/* sim-gx version %d */\n", GX_VERSION);
+ fprintf(block->source_file, "/* gx block date stamp %lu */\n\n", time(NULL));
+
+ /* emit head end of source */
+ tgx_emit_pre_function(gx, optimized);
+
+ /* emit function header */
+ fprintf(block->source_file, "\n\n");
+ fprintf(block->source_file, "extern int\n");
+ fprintf(block->source_file, "%s", block->symbol_name);
+ fprintf(block->source_file, "(struct tgx_cpu_regs* regs, char* pc_flags, struct tgx_callbacks* callbacks)\n");
+ fprintf(block->source_file, "{\n");
+ fprintf(block->source_file, " int rc = 0;\n");
+ if(! optimized)
+ fprintf(block->source_file, " unsigned int insn_count = 0;\n");
+
+ /* pre-block gunk: register load */
+ tgx_emit_load_block(gx, optimized);
+
+ /* emit intra-block jump label */
+ fprintf(block->source_file, "\n");
+ fprintf(block->source_file, "shortjump:\n");
+ fprintf(block->source_file, " pc = npc;\n");
+
+ /* translate jumptarget table */
+ if(! optimized)
+ {
+ fprintf(block->source_file, " pc_flags[(pc - 0x%08x) / %u] |= %d;\n",
+ (unsigned)gx->origin, gx->divisor, GX_PCF_JUMPTARGET);
+ }
+
+ /* enforce learning mode run limit */
+ if(! optimized)
+ {
+ fprintf(block->source_file, " insn_count++;\n");
+ fprintf(block->source_file, " if (insn_count > %d)\n", GX_LEARN_RUN_LIMIT);
+ fprintf(block->source_file, " {\n");
+ fprintf(block->source_file, " rc = %d;\n", GX_F_YIELD);
+ fprintf(block->source_file, " npc = pc;\n");
+ fprintf(block->source_file, " goto save;\n");
+ fprintf(block->source_file, " }\n");
+ }
+
+ /* emit PC switch, use compressed case numbers */
+ fprintf(block->source_file, "\n");
+ fprintf(block->source_file, " switch((pc - 0x%08x) / %u)\n",
+ (unsigned)gx->origin, gx->divisor);
+ fprintf(block->source_file, " {\n");
+
+ /* handle bad-PC event */
+ fprintf(block->source_file, " /* handle unknown jump target */\n");
+ fprintf(block->source_file, " default:\n");
+ fprintf(block->source_file, " rc = %d;\n", GX_F_NONPC);
+ fprintf(block->source_file, " npc = pc;\n");
+ fprintf(block->source_file, " goto save;\n");
+
+ /* start translating at the origin */
+ gx_cia = gx->origin;
+
+ /* translate instructions in block */
+ while(GX_PC_INCLUDES(gx,gx_cia))
+ {
+ sim_cia next_gx_cia;
+
+ /* translate PC case statement */
+ fprintf(block->source_file, "\n");
+ fprintf(block->source_file, " /* PC: 0x%08x, flags %02x */\n",
+ gx_cia, (int) GX_PC_FLAGS(gx, gx_cia));
+
+
+ /* skip over this instruction if it is not executed */
+ if(optimized && !(GX_PC_FLAGS(gx, gx_cia) & GX_PCF_INSTRUCTION))
+ {
+ fprintf(block->source_file, " /* (not reached) */\n");
+
+ /* prevent fall-through from previous translated insn */
+ if(gx_cia > gx->origin &&
+ GX_PC_FLAGS(gx, (gx_cia - gx->divisor)) & GX_PCF_INSTRUCTION)
+ {
+ fprintf(block->source_file, " /* prevent fall-through */\n");
+ fprintf(block->source_file, " npc = 0x%08x;\n", gx_cia);
+ fprintf(block->source_file, " rc = %d;\n", GX_F_NONPC);
+ fprintf(block->source_file, " goto save;\n");
+ }
+
+ next_gx_cia = gx_cia + gx->divisor;
+ goto skip_instruction;
+ }
+
+ /* translate PC case statement */
+ if((! optimized) ||
+ (GX_PC_FLAGS(gx, gx_cia) & GX_PCF_JUMPTARGET))
+ {
+ fprintf(block->source_file, " case %ld:\n",
+ ((gx_cia - gx->origin) / gx->divisor));
+ }
+
+ /* translate breakpoint check & exit */
+ if(GX_PC_FLAGS(gx, gx_cia) & GX_PCF_COND_HALT)
+ {
+ fprintf(block->source_file, " if(pc_flags[%ld] & %d)\n",
+ GX_PC_FLAGS_INDEX(gx, gx_cia),
+ GX_PCF_HALT);
+ fprintf(block->source_file, " {\n");
+ fprintf(block->source_file, " rc = %d;\n", GX_F_HALT);
+ fprintf(block->source_file, " npc = pc;\n");
+ fprintf(block->source_file, " goto save;\n");
+ fprintf(block->source_file, " }\n");
+ }
+
+ /* [don't] emit PC-setting */
+ /* fprintf(block->source_file, " pc = 0x%08x;\n", gx_cia); */
+
+ /* mark traversed instructions */
+ if(! optimized)
+ {
+ fprintf(block->source_file, " pc_flags[%ld] |= %d;\n",
+ GX_PC_FLAGS_INDEX(gx, gx_cia),
+ GX_PCF_INSTRUCTION);
+ }
+
+
+ /* translate instruction semantics */
+ next_gx_cia = tgx_emit_insn(gx, gx_cia, optimized);
+
+ skip_instruction:
+
+ /* go to next instruction */
+ gx_cia = next_gx_cia;
+ }
+ fprintf(block->source_file, " }\n");
+
+ /* dropped through last instruction in switch block */
+ fprintf(block->source_file, "\n");
+ fprintf(block->source_file, " /* dropped through PC switch */\n");
+ fprintf(block->source_file, " npc = 0x%08x;\n", gx_cia);
+ fprintf(block->source_file, " rc = %d;\n", GX_F_RANGE);
+ fprintf(block->source_file, " goto save;\n");
+
+ /* unknown length jump */
+ fprintf(block->source_file, "\n");
+ fprintf(block->source_file, "unknownjump:\n");
+ fprintf(block->source_file, " if(npc >= 0x%08lx && npc < 0x%08lx)\n",
+ gx->origin, gx->origin + gx->length);
+ fprintf(block->source_file, " goto shortjump;\n");
+
+ /* long jump */
+ fprintf(block->source_file, "\n");
+ fprintf(block->source_file, "longjump:\n");
+ fprintf(block->source_file, " rc = %d;\n", GX_F_RANGE);
+
+ /* post-block gunk: SAVE etc. */
+ fprintf(block->source_file, "\n");
+ fprintf(block->source_file, "save:\n");
+
+ tgx_emit_save_block(gx, optimized);
+
+ /* emit tail end of function */
+ fprintf(block->source_file, "\n");
+ fprintf(block->source_file, " return rc;\n");
+ fprintf(block->source_file, "}\n");
+
+ /* emit tail end of source */
+ tgx_emit_post_function(gx, optimized);
+
+ /* close source file */
+ fclose(block->source_file);
+ block->source_file = NULL;
+
+ /* compile source & produce shared object */
+
+ sprintf(compile_command,
+ "gxtool --silent --mode=compile gcc -c -g %s %s",
+ (optimized ? "-O3" : "-O"), block->source_name);
+
+ rc = system(compile_command);
+ if(rc != 0)
+ {
+ sim_io_error(sd, "Error during compiling: `%s' rc %d",
+ compile_command, rc);
+ }
+
+ /* link source */
+
+ sprintf(compile_command,
+ "gxtool --silent --mode=link gcc -export-dynamic -rpath %s -g -o lib%s.la %s.lo",
+ dir_name, base_name, base_name);
+
+ rc = system(compile_command);
+ if(rc != 0)
+ {
+ sim_io_error(sd, "Error during linking: `%s' rc %d",
+ compile_command, rc);
+ }
+
+
+ /* install */
+
+ sprintf(compile_command,
+ "gxtool --silent --mode=install cp lib%s.la %s/%s >/dev/null 2>/dev/null",
+ base_name, pwd_name, dir_name);
+
+ rc = system(compile_command);
+ if(rc != 0)
+ {
+ sim_io_error(sd, "Error during install: `%s' rc %d",
+ compile_command, rc);
+ }
+
+
+ /* finish */
+
+ sprintf(compile_command,
+ "gxtool --silent --mode=finish %s >/dev/null 2>/dev/null",
+ dir_name);
+
+ rc = system(compile_command);
+ if(rc != 0)
+ {
+ sim_io_error(sd, "Error during finish: `%s' rc %d",
+ compile_command, rc);
+ }
+
+ /* clean up */
+
+ sprintf(compile_command, "rm -f lib%s.la %s.lo", base_name, base_name);
+ rc = system(compile_command);
+ if(rc != 0)
+ {
+ sim_io_error(sd, "Error during cleanup: `%s' rc %d",
+ compile_command, rc);
+ }
+
+ /* XXX: FILL IN block->object_name from .la file */
+ sprintf(block->object_name, "%s/%s/lib%s.so.0",
+ pwd_name, dir_name, base_name);
+
+ /* measure compile time */
+ time_end = time(NULL);
+
+ if(time_end == time_begin) time_end ++; /* clamp minimum duration to 1 */
+ gx->compile_time += time_end - time_begin;
+ /* fprintf(stderr, "*** compile time: %d\n", gx->compile_time); */
+}
+