10    Programming Examples

This chapter provides complete examples of programs that access object file and symbol table structures. These examples are meant to reinforce the descriptions of these structures and their use. In many cases APIs exist that could be used to simplify these examples. Use of these APIs is strongly encouraged, but they are not employed in these programming examples, because they would hide the details of the structure access and data interpretation.

10.1    Packed Line Numbers

This example illustrates the use of structures described in Section 5.3.2.2.1. The following program will read packed line numbers and display them in expanded form.

Source Listing

readline.c:
 
/* Expand packed line numbers and display ranges of addresses 
 * and line numbers.  For simplicity, file and procedure names are
 * omitted.
 */
 
#include <filehdr.h>
#include <scnhdr.h>
#include <sym.h>
#include <stdio.h>
 
main(int argc, char **argv){
    FILE                *fd;    /* fopen handle */
    FILHDR              fhead;  /* object file header */
    HDRR                hdrr;   /* symbol table header */
    unsigned char       *pline; /* buffer for packed lines */
    FDR                 *fdr;   /* buffer for FDRs */
    PDR                 *pdr;   /* buffer for PDRs */
 
    if (argc < 2) {
        printf("Usage: readline <OBJECT>\n"); 
        exit(1);
    }
 
    /* Open file argument */
 
    if ((fd = fopen(argv[1], "r")) == (FILE *)NULL) {
        printf("Bad file %s!\n", argv[1]); 
        exit(1);
    }
 
    /* Read file header and test magic id */
 
    if (fread(&fhead, FILHSZ, 1, fd) != 1) {
        printf("fread filheader!\n"); 
        exit(1);
    } else if (fhead.f_magic != ALPHAMAGIC) {
        if (fhead.f_magic == ALPHAUMAGIC)
            printf("Compressed object not supported\n");
        else
            printf("%s is not an object file\n", argv[1]);
        exit(1);
    }
 
    /* Read symbolic header */
 
    if (fhead.f_symptr == 0) {
        printf("no syms!\n"); 
        exit(1);
    }
    fseek(fd, fhead.f_symptr, 0);
    if (fread(&hdrr, sizeof(HDRR), 1, fd) != 1) {
        printf("symheader read failed!\n"); 
        exit(1); 
    }
 
    /* Test for FDRs, PDRs, and packed line numbers */
 
    if (!hdrr.ifdMax) {
        printf("No file descriptors!\n"); 
        exit(1);
    } else if (!hdrr.ipdMax) {
        printf("No procedure descriptors!\n"); 
        exit(1);
    } else if (hdrr.cbLine == 0) {
        printf("No lines!\n"); 
        exit(1);
    }
 
    /* Read FDRs */
 
    fseek(fd, hdrr.cbFdOffset, 0);
    if (!(fdr = (FDR *)malloc(hdrr.ifdMax * sizeof(FDR)))) {
        printf("FDR malloc failed\n"); 
        exit(1); 
    }
    if (fread(fdr, sizeof(FDR), hdrr.ifdMax, fd) != hdrr.ifdMax) {
        printf("FDR read failed\n"); 
        exit(1);
    }
 
    /* Read PDRs */
 
    fseek(fd, hdrr.cbPdOffset, 0);
    if (!(pdr = (PDR *)malloc(hdrr.ipdMax * sizeof(PDR)))) {
        printf("PDR malloc failed\n"); 
        exit(1); 
    }
    if (fread(pdr, sizeof(PDR), hdrr.ipdMax, fd) != hdrr.ipdMax) {
        printf("PDR read failed\n"); 
        exit(1);
    }
 
    /* Read packed lines */
 
    fseek(fd, hdrr.cbLineOffset, 0);
    if (!(pline = (unsigned char *)malloc(hdrr.cbLine))) {
        printf("pline malloc failed\n"); 
        exit(1); 
    }
    if (fread(pline, 1, hdrr.cbLine, fd) != hdrr.cbLine) {
        printf("pline read failed\n"); 
        exit(1);
    }
 
    /* Dump expanded packed lines */
 
    expand_lines(fdr, hdrr.ifdMax, pdr, pline);
}
 
expand_lines(FDR *fdr, int ifdmax,    /* FDRs and count */
             PDR *pdr,                /* PDRs */
             unsigned char *pline) {  /* Packed lines */
 
    int ifd;
 
    /* Iterate through FDRs */
 
    for (ifd = 0; ifd < ifdmax; ifd++) {
 
        /* Ignore FDRs without line numbers */
 
        if (fdr[ifd].cbLine == 0)
            continue;
 
        printf("File %d:\n", ifd);
 
        /* Dump expanded lines for this FDR */
 
        expand_file_lines(&fdr[ifd],
                          &pdr[fdr[ifd].ipdFirst],
                          fdr[ifd].cpd,
                          &pline[fdr[ifd].cbLineOffset],
                          fdr[ifd].cbLine);
    }
}
 
 
proc_pline_count(FDR *fdr,              /* FDR */
                 PDR *pdr,              /* First PDR for FDR */
                 int ipd) {             /* Index of current PDR */
 
    int nextipd;   /* Index of next PDR with line numbers */
    int i;         /* Index to iterate through PDRs */
 
    /* Return the number of packed line entries for a PDR. 
     * To simplify processing, a procedure with alternate
     * entries is treated as a set of contiguous procedures.
     * In this program the calling procedure does not need
     * to know that the packed lines associated with the
     * alternate entry actually belong to the containing 
     * procedure.
     */
 
 
    /* Test for no lines */
 
    if (pdr[ipd].iline == ilineNil)
        return(0);
 
    nextipd = -1;       /* Next PDR not found yet. */
 
    /* Iterate through all PDRs for this FDR */
 
    for (i=0; i < fdr->cpd; i++) {
 
        /* Find PDRs with packed line offsets the same or
         * greater than the current PDR's.
         */
 
        if (i != ipd &&                 
            pdr[i].iline != ilineNil &&
            pdr[i].cbLineOffset >= pdr[ipd].cbLineOffset) {
 
            /* Save PDR index of closest offset found so far.  
             * Do not assume the PDRs are arranged with 
             * ascending packed line offsets.
             */
 
            if (nextipd == -1 ||
                pdr[i].cbLineOffset < pdr[nextipd].cbLineOffset)
                nextipd = i;
        }
    }
 
    if (nextipd == -1)
 
        /* Current PDR is the last one in the file with line
         * numbers.  Use the file's packed line count to compute
         * the PDRs packed line count.
         */
 
        return (fdr->cbLine - pdr[ipd].cbLineOffset);
 
    else
 
        return (pdr[nextipd].cbLineOffset - pdr[ipd].cbLineOffset);
}
 
 
expand_file_lines(FDR *fdr,             /* FDR */
                  PDR *pdr,             /* First PDR for FDR */
                  int npdr,             /* PDR count for FDR */
                  unsigned char *pline, /* First packed line for FDR */
                  int numline) {        /* Packed line count for FDR */
 
    int  ipd;                   /* PDR index */
    int  pli, next_pli;         /* Packed line index */
    int  plcount;               /* Packed line count for PDR */
    long curline;               /* Current source line number */
    long start_address;         /* First address for curline */
    long end_address;           /* First address of next source line */
 
    /* Iterate through procedures and alternate entries */
 
    for (ipd=0; ipd < npdr; ipd++) {
 
        /* Ignore procedures without line numbers */
 
        if (pdr[ipd].iline == ilineNil)
            continue;
 
        /* Identify Procedure or Alternate entry */
 
        if (pdr[ipd].lnHigh != -1) {
            printf("  Proc %d:\n", ipd);
        } else {
            printf("  Alt Ent %d:\n", ipd);
        }
 
        start_address = pdr[ipd].adr;      /* 1st address of proc */
        curline = pdr[ipd].lnLow;          /* 1st line number of proc */
 
        /* Compute packed line count for this PDR */
 
        plcount = proc_pline_count(fdr, pdr, ipd);
 
        pli = pdr[ipd].cbLineOffset;       /* Packed line index */
        next_pli = pli + plcount;          /* End index */
 
        /* Iterate through packed line numbers */
 
        for (; pli < next_pli; pli++) {
 
            long delta;         /* temp for computing line delta */
 
            /* Use the instruction count to compute the first
             * address of the next line number.
             */
 
            end_address = start_address + 
                              (((pline[pli] & 0xfU) + 1) << 2);
            /* Use the line delta to compute the current
             * line number.  Test for extended deltas that
             * use two additional packed line bytes.
             */
 
            if ((pline[pli] & 0xf0U) == 0x80U) {
 
                /* extended delta */
 
                pli++;
                delta = ((signed char)pline[pli]) << 8;
                pli++;
                delta |= pline[pli];
 
            } else {
                delta = (signed char)pline[pli] >> 4; 
            }
 
            curline += delta;
 
            /* Display current address range and source line */
 
            printf("    0x%lx - 0x%lx :  Line %ld\n", 
                   start_address, end_address - 4, curline);
 
            /* Prepare for next iteration */
 
            start_address = end_address;
        }
    }
}

Sample Output

% cc -g -o readline readline.c
% ./readline readline
File 1:
  Proc 0:
    0x120001290 - 0x1200012b4 :  Line 11
    0x1200012b8 - 0x1200012c0 :  Line 19
    0x1200012c4 - 0x1200012d8 :  Line 20
    0x1200012dc - 0x1200012e8 :  Line 21
    0x1200012f0 - 0x120001310 :  Line 26
    0x120001314 - 0x120001330 :  Line 27
...

10.2    Extended Source Location Information

This example illustrates the use of structures described in Section 5.3.2.2.2. The following program will read extended source location information and display the intrepreted line numbers. This example includes a few lines of source from a header file in order to illustrate a typical use of ESLI.

Source Listing

usage.h:
 
if (argc < 2) {
  printf("Usage: readesli <OBJECT>\n"); 
  exit(1);
}
 
 
readesli.c:
 
/* readesli.c:  Interpret ESLI and display ranges of addresses with
 * file, line, and column numbers.  
 *
 * Omissions for simplification purposes:
 *     - file and procedure names.  These can be found by following
 *       the file or procedure's first local symbol entry.
 *     - alternate entries.  These can be included in the output by
 *       comparing the current PC address (maintained in the ESLI
 *       computation) to the address of the next successive
 *       alternate entry procedure descriptor.
 *     - selecting between ESLI and packed line numbers.  If PDRs
 *       have both, ESLI should be prefered.
 *     - relative file interpretation.  File numbers within ESLI
 *       can be converted to actual FDR indexes using the relative
 *       file descriptor table.
 */
 
#include <stdio.h>
#include <filehdr.h>
#include <scnhdr.h>
#include <sym.h>
#include <symconst.h>
#include <linenum.h>
 
main(int argc, char **argv){
  FILE          *fd;            /* fopen handle */
  FILHDR        fhead;          /* object file header */
  HDRR          hdrr;           /* symbol table header */
  char          *optbfr;        /* buffer for optimization symbols */
  FDR           *fdr;           /* buffer for FDRs */
  PDR           *pdr;           /* buffer for PDRs */
 
#include "usage.h"
 
  /* Open file argument */
 
  if ((fd = fopen(argv[1], "r")) == (FILE *)NULL) {
    printf("Bad file %s!\n", argv[1]); 
    exit(1);
  }
 
  /* Read file header and test magic id */
 
  if (fread(&fhead, FILHSZ, 1, fd) != 1) {
    printf("fread filheader!\n"); 
    exit(1);
  } else if (fhead.f_magic != ALPHAMAGIC) {
    if (fhead.f_magic == ALPHAUMAGIC)
      printf("Compressed object not supported\n");
    else
      printf("%s is not an object file\n", argv[1]);
    exit(1);
  }
 
  /* Read symbolic header */
 
  if (fhead.f_symptr == 0) {
    printf("no syms!\n"); 
    exit(1);
  }
  fseek(fd, fhead.f_symptr, 0);
  if (fread(&hdrr, sizeof(HDRR), 1, fd) != 1) {
    printf("symheader read failed!\n"); 
    exit(1); 
  }
 
  /* Test for FDRs, PDRs, and optimization symbols */
 
  if (!hdrr.ifdMax) {
    printf("No file descriptors!\n"); 
    exit(1);
  } else if (!hdrr.ipdMax) {
    printf("No procedure descriptors!\n"); 
    exit(1);
  } else if (hdrr.ioptMax == 0) {
    printf("No ESLI!\n"); 
    exit(1);
  }
 
  /* Read FDRs */
 
  fseek(fd, hdrr.cbFdOffset, 0);
  if (!(fdr = (FDR *)malloc(hdrr.ifdMax * sizeof(FDR)))) {
    printf("FDR malloc failed\n"); 
    exit(1); 
  }
  if (fread(fdr, sizeof(FDR), hdrr.ifdMax, fd) != hdrr.ifdMax) {
    printf("FDR read failed\n"); 
    exit(1);
  }
 
  /* Read PDRs */
 
  fseek(fd, hdrr.cbPdOffset, 0);
  if (!(pdr = (PDR *)malloc(hdrr.ipdMax * sizeof(PDR)))) {
    printf("PDR malloc failed\n"); 
    exit(1); 
  }
  if (fread(pdr, sizeof(PDR), hdrr.ipdMax, fd) != hdrr.ipdMax) {
    printf("PDR read failed\n"); 
    exit(1);
  }
 
  /* Read optimization symbols */
 
  fseek(fd, hdrr.cbOptOffset, 0);
  if (!(optbfr = (char *)malloc(hdrr.ioptMax))) {
    printf("opt malloc failed\n"); 
    exit(1); 
  }
  if (fread(optbfr, 1, hdrr.ioptMax, fd) != hdrr.ioptMax) {
    printf("opt read failed\n"); 
    exit(1);
  }
 
  /* Dump ESLI for all procedures */
 
  dump_esli(fdr, hdrr.ifdMax, pdr, optbfr);
}
 
 
dump_esli(FDR *fdr, int ifdmax,    /* FDRs and count */
          PDR *pdr,                /* PDRs */
          char *optbfr) {          /* optimization symbols */
 
  int ifd;
 
  /* Iterate through FDRs */
 
  for (ifd = 0; ifd < ifdmax; ifd++) {
 
    /* Ignore FDRs without optimization symbols */
 
    if (fdr[ifd].copt == 0)
      continue;
 
    printf("File %d:\n", ifd);
 
    /* Dump ESLI for PDRs in this FDR */
 
    dump_esli_for_file(&fdr[ifd],
                       &pdr[fdr[ifd].ipdFirst],
                       fdr[ifd].cpd,
                       optbfr + fdr[ifd].ioptBase);
  }
}
 
 
dump_esli_for_file(FDR *fdr,            /* FDR */
                   PDR *pdr,            /* First PDR for FDR */
                   int npdr,            /* PDR count for FDR */
                   char *optbfr) {      /* Optimization symbols for FDR */
 
  int           ipd;            /* PDR index */
  char          *pdr_optbfr;    /* Optimization symbols for PDR */
  PPODHDR       *ppod;          /* PPOD headers */
 
  /* Iterate through procedures and dump ESLI */
 
  for (ipd=0; ipd < npdr; ipd++) {
 
    /* Ignore procedures without optimization symbols */
 
    if (pdr[ipd].iopt == ioptNil)
      continue;
 
    /* Set PPOD header pointer and verify content */
 
    pdr_optbfr = optbfr + pdr[ipd].iopt;
    ppod = (PPODHDR *)pdr_optbfr;
 
    if (ppod->ppode_tag != PPODE_STAMP || 
        ppod->ppode_val > PPOD_VERSION) {
      continue;
    }
 
    /* Search for ESLI PPOD in optimization symbols */
 
    for (ppod++; ppod->ppode_tag != PPODE_END; ppod++) {
      if (ppod->ppode_tag == PPODE_EXT_SRC) {
 
        char    *esli_data;     /* ESLI data for procedure */
        int     esli_count;     /* Number of bytes of data */
 
        if (ppod->ppode_len == 0) {
            /* Immediate data */
            esli_data = (char *)&ppod->ppode_val;
            esli_count = 8;
        } else {
            esli_data = pdr_optbfr + ppod->ppode_val;
            esli_count = ppod->ppode_len;
        }
 
        printf("  Proc %d:\n", ipd);
 
        dump_esli_for_proc(esli_data,
                           esli_count,
                           pdr[ipd].adr,
                           pdr[ipd].lnLow);
        break;
      }
    }
  }
}
 
 
unsigned long
read_uleb(unsigned char **uleb) {  /* Pointer to LEB pointer */
 
  /* Read an unsigned LEB value and advance the 
   * LEB pointer past the LEB bytes.
   */
 
  unsigned char *byte;          /* ULEB byte pointer */
  unsigned long value;          /* Return value */
  int           shift;          /* Accumulated bit shift */
  int           morebits;       /* Loop control */
 
  value = 0;
  shift = 0;
 
  byte = *uleb;
 
  for (morebits=1; morebits; byte++) {
 
    /* Get 7 bits */
    value |= ((*byte) & 0x7f) << shift;
 
    /* Increment shift count */
    shift += 7;
 
    /* Test continue bit */
    morebits = (*byte) & 0x80;
  }
 
  /* Advance data pointer past ULEB bytes */
  *uleb = byte;
 
  return(value);
}
 
 
long
read_sleb(unsigned char **sleb) {  /* Pointer to SLEB pointer */
 
  /* Read a signed LEB value and advance the 
   * LEB pointer past the LEB bytes.
   */
 
  unsigned char *byte;          /* SLEB byte pointer */
  long          value;          /* Return value */
  int           shift;          /* Accumulated bit shift */
  int           morebits;       /* Loop control */
 
  value = 0;
  shift = 0;
 
  byte = *sleb;
 
  for (morebits=1; morebits; byte++) {
 
    /* Get 7 bits */
    value |= ((*byte) & 0x7f) << shift;
 
    /* Increment shift count */
    shift += 7;
 
    /* Test continue bit */
    morebits = (*byte) & 0x80;
  }
 
  /* Extend sign bit if set */
  if ((*byte) & 0x40) 
    value |= (-1L << shift);
 
  /* Advance data pointer past SLEB bytes */
  *sleb = byte;
 
  return(value);
}
 
 
dump_esli_for_proc(char *esli_data,     /* Raw ESLI data */
                   int esli_count,      /* Byte size of ESLI data */
                   long pdr_address,    /* Start address from PDR */
                   long pdr_lnLow) {    /* First source line from PDR */
 
  /* Read ESLI data for a procedure and display address
   * ranges with file, line, and column information.
   */
 
  unsigned char *edp;           /* ESLI data pointer */
  unsigned char  cmd;           /* ESLI command */
  int  data_mode = 1;           /* Data mode 1 or 2 */
  int  cmd_mode = 0;            /* Command mode flag */
  long cur_file = 0;            /* Current fileno (not fdr index) */
  long cur_column = 0;          /* Current column number */
  long cur_line = pdr_lnLow;    /* Current line number */
  long start_address;           /* Start of PC address range */
  long end_address;             /* End of PC address range */
 
  /* Just like packed-line data, ESLI assumes a starting 
   * address and computes the end of the PC range along
   * with the source line information that applies to that
   * range.
   */
 
  start_address = pdr_address;
  end_address = start_address;
 
  /* Iterate through ESLI data.  Loop pointer is incremented
   * within loop and LEB reading subroutines.
   */
 
  for (edp = (unsigned char *)esli_data; 
       edp < ((unsigned char *)esli_data + esli_count); ) {
 
    /* Data Modes */
 
    if (!cmd_mode) {
 
      /* Test for escape to command mode */
 
      if (( (*edp) & 0xf0U) == 0x80U) {
        cmd_mode = 1;
        edp++;
        continue;
      }
 
      /* Use the instruction count to compute the first
       * address of the next line number.
       */
 
      end_address = start_address + 
                    ((( (*edp) & 0xfU) + 1) << 2);
      cur_line += (signed char)(*edp) >> 4; 
 
      if (data_mode == 2)
        cur_column = *(++edp);
 
      /* Display current address range and source line */
 
      printf("    0x%lx - 0x%lx :  File %ld Line %ld Col %ld\n", 
             start_address, end_address - 4, 
             cur_file, cur_line, cur_column);
 
      /* Prepare for next iteration */
 
      edp++;
      start_address = end_address;
 
    } else {
 
      /* Command Mode */
 
      cmd = *edp++;
 
      /* Do command (CMD_MASK is 0x3F) */
 
      switch(cmd & CMD_MASK) {
 
        case ADD_PC: /* PC delta */
          end_address += read_sleb(&edp) << 2;
          break;
 
        case ADD_LINE: /* Line delta */
          cur_line += read_sleb(&edp);
          break;
 
        case SET_COL: /* Column */
          cur_column = read_uleb(&edp);
          break;
 
        case SET_FILE: /* File number */
          cur_file = read_uleb(&edp);
          break;
 
        case SET_DATA_MODE: /* Mode */
          data_mode = read_uleb(&edp);
          break;
 
        case ADD_LINE_PC: /* Line and PC delta */
          cur_line += read_sleb(&edp);
          end_address += read_sleb(&edp) << 2;
          break;
 
        case ADD_LINE_PC_COL: /* Line/PC delta, column */
          cur_line += read_sleb(&edp);
          end_address += read_sleb(&edp) << 2;
          cur_column = read_uleb(&edp);
          break;
 
        case SET_LINE: /* Line */
          cur_line = read_uleb(&edp);
          break;
 
        case SET_LINE_COL: /* Line and column */
          cur_line = read_uleb(&edp);
          cur_column = read_uleb(&edp);
          break;
 
        case SEQUENCE_BREAK: /* PC gap */
          end_address += read_sleb(&edp) << 2;
          start_address = end_address;
          break;
 
        default:
          fprintf(stderr,"Unkown ESLI command\n");
          exit(1);
      }
 
      /* check mark (0x80) flag */
 
      if ((cmd & MARKb) && end_address > start_address) {
        printf("    0x%lx - 0x%lx :  File %ld Line %ld Col %ld\n", 
               start_address, end_address - 4, 
               cur_file, cur_line, cur_column);
      }
 
      /* Check resume (0x40) flags */
 
      if (cmd & RESUMEb) {
        cmd_mode = 0;
      }
    }
  }
}

Sample Output

% cc -g -o readesli readesli.c
% ./readesli readesli
File 1:
  Proc 0:
    0x1200013b0 - 0x1200013d4 :  File 0 Line 25 Col 0
    0x1200013d8 - 0x1200013e0 :  File 13 Line 1 Col 0
    0x1200013e4 - 0x1200013f8 :  File 13 Line 2 Col 0
    0x1200013fc - 0x12000140c :  File 13 Line 3 Col 0
    0x120001410 - 0x120001430 :  File 0 Line 37 Col 0
    0x120001434 - 0x120001450 :  File 0 Line 38 Col 0
...