home *** CD-ROM | disk | FTP | other *** search
/ vsiftp.vmssoftware.com / VSIPUBLIC@vsiftp.vmssoftware.com.tar / FREEWARE / FREEWARE40.ZIP / callmon / src / callmon$branch.c < prev    next >
C/C++ Source or Header  |  1996-08-06  |  16KB  |  467 lines

  1. /*  CALLMON - A Call Monitor for OpenVMS Alpha
  2.  *
  3.  *  File:     CALLMON$BRANCH.C
  4.  *  Author:   Thierry Lelegard
  5.  *  Version:  1.0
  6.  *  Date:     24-JUL-1996
  7.  *
  8.  *  Abstract: This module processes the BSR instructions (branch to
  9.  *            subroutine) inside an image.
  10.  */
  11.  
  12.  
  13. #include "callmon$private.h"
  14.  
  15.  
  16. /*******************************************************************************
  17.  *
  18.  *  This module analyzes code sections. During the analysis of the code
  19.  *  stream of a section, the following structure describes the state of
  20.  *  the section.
  21.  */
  22.  
  23. typedef struct {
  24.     inst_t* start;          /* Starting address of the section */
  25.     inst_t* end;            /* End address (+1) of the section */
  26.     uint32* targets;        /* Bit array: if set, inst is target of a branch */
  27.     size_t targets_size;    /* Allocated size of targets (in bytes) */
  28.     int targets_count;      /* Number of bits set in targets */
  29.     inst_t* last_nop;       /* Last NOP instruction */
  30.     inst_t* last_load_r27;  /* Last inst which loads something into R27 */
  31.     inst_t* last_use_r27;   /* Last inst which uses the content of R27 */
  32. } code_state_t;
  33.  
  34.  
  35. /*******************************************************************************
  36.  *
  37.  *  Output routine for callmon$disassemble.
  38.  *  Used when instruction tracing is enabled.
  39.  */
  40.  
  41. typedef struct {
  42.     FILE* fp;
  43.     enum {DISPLAY_NONE, DISPLAY_CHANGE, DISPLAY_ALL} display;
  44.     code_state_t previous_state;
  45.     code_state_t state;
  46. } trace_output_data_t;
  47.  
  48. static void trace_output (
  49.     void* pc,
  50.     char* opcode,
  51.     char* operands,
  52.     void* user_data)
  53. {
  54.     trace_output_data_t* trdata = user_data;
  55.     routine_t* routine;
  56.     int offset;
  57.     int is_target;
  58.  
  59.     /* Check if the PC is at the entry of a known procedure */
  60.  
  61.     routine = callmon$$get_routine_by_entry ((int64) (pc));
  62.  
  63.     if (routine != NULL)
  64.         fprintf (trdata->fp, "%s:\n", routine->name);
  65.  
  66.     /* Check if the instruction is the target of a branch */
  67.  
  68.     offset = (inst_t*) pc - trdata->state.start;
  69.     is_target = trdata->state.targets [offset / 32] & (1 << (offset % 32));
  70.  
  71.     /* Display the instruction */
  72.  
  73.     fprintf (trdata->fp, "%08X:%08X%s%-7s %s", pc, *(uint32*)pc,
  74.         is_target ? "->" : "  ", opcode, operands);
  75.  
  76.     /* Display the change of state */
  77.  
  78.     if (trdata->display != DISPLAY_NONE) {
  79.  
  80.         if (trdata->display == DISPLAY_ALL ||
  81.             trdata->state.last_nop != trdata->previous_state.last_nop)
  82.             fprintf (trdata->fp, " ** NOP:%08X", trdata->state.last_nop);
  83.  
  84.         if (trdata->display == DISPLAY_ALL ||
  85.             trdata->state.last_load_r27 != trdata->previous_state.last_load_r27)
  86.             fprintf (trdata->fp, " ** LDxR27:%08X",trdata->state.last_load_r27);
  87.  
  88.         if (trdata->display == DISPLAY_ALL ||
  89.             trdata->state.last_use_r27 != trdata->previous_state.last_use_r27)
  90.             fprintf (trdata->fp, " ** STxR27:%08X", trdata->state.last_use_r27);
  91.     }
  92.  
  93.     fprintf (trdata->fp, "\n");
  94. }
  95.  
  96.  
  97. /*******************************************************************************
  98.  *
  99.  *  This routine attempts to transform all BSR instructions in the
  100.  *  specified image into JSR instructions.
  101.  */
  102.  
  103. void callmon$$remove_bsr (image_t* image)
  104. {
  105.     uint32 status;
  106.     EISD* eisd;
  107.     char* end_of_header;
  108.     int section_count;
  109.     int section_max_size;
  110.     routine_t* routine;
  111.     int is_bsr_r26;
  112.     int ok_to_replace;
  113.     int offset;
  114.     inst_t ldq_r26, jsr, nop;
  115.     uint32 input_regs, output_regs;
  116.     inst_t *pc, *branch_target, *first;
  117.     code_state_t state;
  118.     trace_output_data_t trace;
  119.     char trace_file_name [FILE_SPEC_SIZE];
  120.  
  121.     /* Do this only once per image */
  122.  
  123.     if (!image->replace_bsr)
  124.         return;
  125.  
  126.     image->replace_bsr = 0;
  127.  
  128.     /* Build an instruction to load the entry code of a procedure:
  129.      * LDQ R26, PDSC$Q_ENTRY (R27) */
  130.  
  131.     ldq_r26.u.mem.opcode = EVX$OPC_LDQ;
  132.     ldq_r26.u.mem.ra = 26;
  133.     ldq_r26.u.mem.rb = 27;
  134.     ldq_r26.u.mem.disp = OFFSET (pdsc_t, pdsc$l_entry);
  135.  
  136.     /* Build a standard JSR instruction: JSR R26, (R26) */
  137.  
  138.     jsr.u.instr = EVX$OPC32_JSR;
  139.     jsr.u.mem.ra = jsr.u.mem.rb = 26;
  140.  
  141.     /* Build a NOP instruction: BIS R31, R31, R31 */
  142.  
  143.     nop.u.instr = EVX$OPC32_BIS;
  144.     nop.u.op.ra = nop.u.op.rb = nop.u.op.rc = 31;
  145.  
  146.     /* The table of ISD (image section descriptors) is at the end of the image
  147.      * header. The last ISD is found when the end of header is reached. */
  148.  
  149.     end_of_header = (char*) image->eihd + image->eihd->eihd$l_size;
  150.  
  151.     /* Find the biggest code section in the image. We loop on all sections
  152.      * with the EXE attributes and find the biggest one. */
  153.  
  154.     section_max_size = 0;
  155.  
  156.     for (eisd = image->eisd;
  157.          (char*) eisd < end_of_header &&
  158.          (char*) eisd + eisd->eisd$l_eisdsize <= end_of_header;
  159.          eisd = (EISD*) ((char*)eisd + eisd->eisd$l_eisdsize)) {
  160.  
  161.         if (eisd->eisd$v_exe && eisd->eisd$l_secsize > section_max_size)
  162.             section_max_size = eisd->eisd$l_secsize;
  163.     }
  164.  
  165.     /* Allocate on stack an array of bits. There is one bit for each
  166.      * instruction in the current code section. If the bit is set,
  167.      * the instruction is the target of a branch. Notes:
  168.      *   - instructions are 32-bits => divide section size by 4
  169.      *   - bits are grouped in uint32 => add 31 to the number of instructions
  170.      *   - divide by 8 gives the number of bytes to allocate */
  171.  
  172.     state.targets_size = ((section_max_size / 4) + 31) / 8;
  173.     state.targets = __ALLOCA (state.targets_size);
  174.  
  175.     /* Now, examine all code sections, looking for BSR instructions pointing
  176.      * to known routines. */
  177.  
  178.     section_count = 0;
  179.  
  180.     for (eisd = image->eisd;
  181.          (char*) eisd < end_of_header &&
  182.          (char*) eisd + eisd->eisd$l_eisdsize <= end_of_header;
  183.          eisd = (EISD*) ((char*)eisd + eisd->eisd$l_eisdsize)) {
  184.  
  185.         section_count++;
  186.  
  187.         /* If the section does not contain code, no interest */
  188.  
  189.         if (!eisd->eisd$v_exe)
  190.             continue;
  191.  
  192.         /* Compute the starting and ending addresses of the section.
  193.          * For shareable images, the section address is relative to
  194.          * the image base address. */
  195.  
  196.         if (image->imcb->imcb$b_act_code == IMCB$K_MAIN_PROGRAM)
  197.             state.start = (inst_t*) eisd->eisd$l_virt_addr;
  198.         else
  199.             state.start = (inst_t*) ((char*) image->imcb->imcb$l_base_address +
  200.                 (int32) eisd->eisd$l_virt_addr);
  201.  
  202.         state.end = (inst_t*) ((char*) state.start + eisd->eisd$l_secsize);
  203.  
  204.         /* If instruction tracing is enabled, creates a text file
  205.          * for each code section */
  206.  
  207.         trace.fp = NULL;
  208.  
  209.         if (callmon$$own.trace_flags & TRACE_INSTRUCTIONS) {
  210.  
  211.             sprintf (trace_file_name, "%s_%03d.LIS",
  212.                 image->logname, section_count);
  213.  
  214.             callmon$$putmsg (CALLMON$_DOISD, 4, section_count, image->logname,
  215.                 state.start, state.end, CALLMON$_CREFLW, 1, trace_file_name);
  216.  
  217.             trace.fp = fopen (trace_file_name, "w",
  218.                 "dna=CALLMON$TRACE_DIRECTORY:");
  219.  
  220.             if (trace.fp != NULL) {
  221.                 fprintf (trace.fp, "Image file %s\n"
  222.                     "Image image section number %d "
  223.                     "(%d bytes, %d instructions)\n"
  224.                     "Image base address: %08X\n"
  225.                     "Section base address: %08X, end: %08X\n",
  226.                     image->file_spec, section_count,
  227.                     eisd->eisd$l_secsize, state.end - state.start,
  228.                     image->imcb->imcb$l_base_address,
  229.                     state.start, state.end);
  230.             }
  231.             else if (errno == EVMSERR) {
  232.                 callmon$$putmsg (CALLMON$_OPEN, 1, trace_file_name, vaxc$errno);
  233.             }
  234.             else {
  235.                 callmon$$putmsg (CALLMON$_OPEN, 1, trace_file_name);
  236.                 perror ("fopen");
  237.             }
  238.         }
  239.  
  240.         /* First pass: find all instructions which are targets of branches */
  241.  
  242.         memset (state.targets, 0, state.targets_size);
  243.         state.targets_count = 0;
  244.  
  245.         for (pc = state.start; pc < state.end; pc++) {
  246.             if ((branch_target = callmon$$branch_target (pc)) != NULL) {
  247.                 offset = branch_target - state.start;
  248.                 if ((state.targets [offset / 32] & (1 << (offset % 32))) == 0) {
  249.                     state.targets [offset / 32] |= 1 << (offset % 32);
  250.                     state.targets_count++;
  251.                 }
  252.             }
  253.         }
  254.  
  255.         if (trace.fp != NULL) {
  256.             callmon$$putmsg (CALLMON$_PASS1, 2, state.end - state.start,
  257.                 state.targets_count);
  258.             fprintf (trace.fp, "Number of branch targets: %d\n\n",
  259.                 state.targets_count);
  260.         }
  261.  
  262.         /* Second pass: Process BSR instructions */
  263.  
  264.         state.last_nop = state.last_load_r27 = state.last_use_r27 = NULL;
  265.  
  266.         for (pc = state.start; pc < state.end; pc++) {
  267.  
  268.             /* Save current state to trace state change */
  269.  
  270.             if (trace.fp != NULL) {
  271.                 trace.previous_state = state;
  272.                 if (pc > state.start && (pc - state.start) % 10000 == 0)
  273.                     callmon$$putmsg (CALLMON$_INSDUMP, 1, pc - state.start);
  274.             }
  275.  
  276.             /* If the current instruction is the target of a branch,
  277.              * we cannot hack instructions backwards, Reset */
  278.  
  279.             offset = pc - state.start;
  280.  
  281.             if (state.targets [offset / 32] & (1 << (offset % 32))) {
  282.                 state.last_nop = NULL;
  283.                 state.last_load_r27 = NULL;
  284.                 state.last_use_r27 = NULL;
  285.             }
  286.  
  287.             /* Keep track of last NOP instruction. A NOP can be later
  288.              * replaced by a new instruction. */
  289.  
  290.             if (pc->u.instr == nop.u.instr)
  291.                 state.last_nop = pc;
  292.  
  293.             /* Check if the instruction is a standard call using BSR.
  294.              * In a standard call, the R26 register must point to the
  295.              * return address. */
  296.  
  297.             is_bsr_r26 = pc->u.branch.opcode == EVX$OPC_BSR &&
  298.                 pc->u.branch.ra == 26;
  299.  
  300.             /* Get the registers which are used by the instruction */
  301.  
  302.             input_regs = callmon$$read_register_mask (pc);
  303.             output_regs = callmon$$write_register_mask (pc);
  304.  
  305.             /* Keep track of last instruction loading something into R27.
  306.              * R27 is loaded with the procedure value (address of PDSC)
  307.              * before a standard call. */
  308.  
  309.             if (output_regs & (1 << 27))
  310.                 state.last_load_r27 = pc;
  311.  
  312.             /* Keep track of last instruction using the content of R27.
  313.              * If we later relocate the instruction loading into R27,
  314.              * we cannot move it before this one. */
  315.  
  316.             if (input_regs & (1 << 27))
  317.                 state.last_use_r27 = pc;
  318.  
  319.             /* If an instruction uses R26, we cannot insert a LDQ R26
  320.              * before it without breaking the code. Consequently, we
  321.              * cannot patch the last NOP location. Note however that
  322.              * the BSR R26 is a special case (see below). */
  323.  
  324.             if (!is_bsr_r26 &&
  325.                 ((input_regs & (1 << 26)) || (output_regs & (1 << 26))))
  326.                 state.last_nop = NULL;
  327.  
  328.             /* Branch instructions reset the last NOP and load R27
  329.              * because we lose track of register usage. Note however
  330.              * that the BSR R26 is a special case (see below). */
  331.  
  332.             if (!is_bsr_r26 && callmon$$branch_instruction (pc)) {
  333.                 state.last_nop = NULL;
  334.                 state.last_load_r27 = NULL;
  335.                 state.last_use_r27 = NULL;
  336.             }
  337.  
  338.             /* Dump instruction in file if trace is enabled */
  339.  
  340.             if (trace.fp != NULL) {
  341.                 trace.state = state;
  342.                 trace.display = is_bsr_r26 ? DISPLAY_ALL : DISPLAY_CHANGE;
  343.                 callmon$disassemble (pc, 4, pc, trace_output, &trace);
  344.             }
  345.  
  346.             /* The rest of the loop is the processing of a BSR instruction
  347.              * for standard call, ie a BSR R26. If the instruction is not
  348.              * a BSR R26, loop on next instruction. */
  349.  
  350.             if (!is_bsr_r26)
  351.                 continue;
  352.  
  353.             /* Check if the target of the BSR is a known routine. If the
  354.              * target is a routine without universal symbol, we don't care.
  355.              * If the routine is not relocatable, don't care as well. */
  356.  
  357.             routine = callmon$$get_routine_by_entry (
  358.                 (int64) callmon$$branch_target (pc));
  359.  
  360.             if (routine == NULL || routine->unrelocatable) {
  361.                 state.last_nop = NULL;
  362.                 state.last_load_r27 = NULL;
  363.                 state.last_use_r27 = NULL;
  364.                 continue;
  365.             }
  366.  
  367.             /* Check if we are allowed to perform BSR replacement */
  368.  
  369.             switch (callmon$$own.bsr_replacement) {
  370.                 case BSR_OFF:
  371.                     ok_to_replace = 0;
  372.                     break;
  373.                 case BSR_IF_R27:
  374.                     ok_to_replace = state.last_load_r27 != NULL;
  375.                     break;
  376.                 case BSR_ALL:
  377.                     ok_to_replace = 1;
  378.                     break;
  379.                 default:
  380.                     ok_to_replace = 0; /* should not be there */
  381.             }
  382.  
  383.             /* Check we can modify the BSR into a JSR.
  384.              * The input sequence is something like:
  385.              *
  386.              *     NOP                      LDx R27, offset(Rx)
  387.              *     ....                     ....
  388.              *     LDx R27, offset(Rx)      NOP
  389.              *     ....                     ....
  390.              *     BSR R26, displacement    BSR R26, displacement
  391.              *
  392.              * We modify the code sequence into a traditional call:
  393.              *
  394.              *     LDx R27, offset(Rx)
  395.              *     ....
  396.              *     LDQ R26, PSDC$Q_ENTRY(R27)
  397.              *     ....
  398.              *     JSR R26, (R26)
  399.              */
  400.  
  401.             first = state.last_load_r27 == NULL ||
  402.                 state.last_load_r27 > state.last_nop ?
  403.                 state.last_nop : state.last_load_r27;
  404.  
  405.             if (ok_to_replace &&
  406.                 first >= pc - callmon$$own.bsr_depth &&
  407.                 (state.last_nop != first ||
  408.                     state.last_use_r27 < state.last_nop) &&
  409.                 callmon$$writeable (first,
  410.                     (pc + 1 - first) * sizeof (inst_t))) {
  411.  
  412.                 /* Modify the code sequence with a JSR */
  413.  
  414.                 if (state.last_nop == first && state.last_load_r27 != NULL) {
  415.                     /* Move the LDQ R27 first */
  416.                     *state.last_nop = *state.last_load_r27;
  417.                     state.last_nop = state.last_load_r27;
  418.                 }
  419.                 *state.last_nop = ldq_r26;
  420.                 *pc = jsr;
  421.  
  422.                 /* Invalidate instruction stream */
  423.  
  424.                 __MB ();
  425.                 __PAL_IMB ();
  426.  
  427.                 /* Trace this modification */
  428.  
  429.                 if (trace.fp != NULL) {
  430.                     fprintf (trace.fp, "========  "
  431.                         "Instruction stream modified to:\n");
  432.                     trace.display = DISPLAY_NONE;
  433.                     callmon$disassemble (first, (pc + 1 - first) * 4, first,
  434.                         trace_output, &trace);
  435.                     fprintf (trace.fp, "===================================\n");
  436.                 }
  437.             }
  438.  
  439.             else {
  440.  
  441.                 /* We cannot convert the BSR into a JSR. Consequently, we
  442.                  * cannot safely hook the relocation of the target routine. */
  443.  
  444.                 callmon$$set_unrelocatable (routine);
  445.  
  446.                 if ((callmon$$own.trace_flags & TRACE_UNRELOCATABLE))
  447.                     callmon$$putmsg (CALLMON$_NOTREL, 1, routine->name);
  448.  
  449.                 if (trace.fp != NULL)
  450.                     fprintf (trace.fp, "========  "
  451.                         "Routine %s becomes unrelocatable\n", routine->name);
  452.             }
  453.  
  454.             /* Reset NOP and LDx R27 after the BSR */
  455.  
  456.             state.last_nop = state.last_load_r27 = NULL;
  457.         }
  458.  
  459.         /* If instruction tracing is enabled, close the trace file */
  460.  
  461.         if (trace.fp != NULL) {
  462.             fclose (trace.fp);
  463.             callmon$$putmsg (CALLMON$_FILDONE, 1, trace_file_name);
  464.         }
  465.     }
  466. }