ftp.nice.ch/pub/next/developer/languages/c/djgpp-NS.s.tar.gz#/djgpp/go32/ed/syms.c

This is syms.c in view mode; [Download] [Up]

/* This is file SYMS.C */
/*
** Copyright (C) 1993 DJ Delorie, 24 Kirsten Ave, Rochester NH 03867-2954
**
** This file is distributed under the terms listed in the document
** "copying.dj", available from DJ Delorie at the address above.
** A copy of "copying.dj" should accompany this file; if not, a copy
** should be available from where this file was obtained.  This file
** may not be distributed without a verbatim copy of "copying.dj".
**
** This file is distributed WITHOUT ANY WARRANTY; without even the implied
** warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/

#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <dos.h>
#include <io.h>

#include "ed.h"
#include "coff.h"
#include "syms.h"
#include "stab.h"

#define DO_SYMS 1

int undefined_symbol=0;

#define Ofs(n) ((int)&(((TSS *)0)->n))

struct {
  char *name;
  int size;
  int ofs;
  } regs[] = {
  "%eip", 4, Ofs(tss_eip),
  "%eflags", 4, Ofs(tss_eflags),
  "%eax", 4, Ofs(tss_eax),
  "%ebx", 4, Ofs(tss_ebx),
  "%ecx", 4, Ofs(tss_ecx),
  "%edx", 4, Ofs(tss_edx),
  "%esp", 4, Ofs(tss_esp),
  "%ebp", 4, Ofs(tss_ebp),
  "%esi", 4, Ofs(tss_esi),
  "%edi", 4, Ofs(tss_edi),
  "%ax", 2, Ofs(tss_eax),
  "%bx", 2, Ofs(tss_ebx),
  "%cx", 2, Ofs(tss_ecx),
  "%dx", 2, Ofs(tss_edx),
  "%ah", 1, Ofs(tss_eax)+1,
  "%bh", 1, Ofs(tss_ebx)+1,
  "%ch", 1, Ofs(tss_ecx)+1,
  "%dh", 1, Ofs(tss_edx)+1,
  "%al", 1, Ofs(tss_eax),
  "%bl", 1, Ofs(tss_ebx),
  "%cl", 1, Ofs(tss_ecx),
  "%dl", 1, Ofs(tss_edx),
  0, 0, 0
};

#if !DO_SYMS

void syms_init(char *fname) {}
void syms_list(int byval) {}
void syms_listwild(char *pattern) {}

word32 syms_name2val(char *name)
{
  if (isdigit(name[0]))
  {
    int v;
    if (strncmp(name, "0x", 2) == 0)
      sscanf(name+2, "%x", &v);
    else
      sscanf(name, "%d", &v);
    undefined_symbol = 0;
    return v;
  }
  else
  {
    undefined_symbol = 1;
    return 0;
  }
}

char *syms_val2name(word32 val, word32 *delta)
{
  static char noname_buf[20];
  sprintf(noname_buf, "%#lx", val);
  *delta = 0;
  return noname_buf;
}

char *syms_val2line(word32 val, int *lineret, int exact)
{
  return 0;
}

#else

/* From the file */

typedef struct SYM_ENTRY {
  word32 string_off;
  word8 type;
  word8 other;
  word16 desc;
  word32 val;
} SYM_ENTRY;

static FILHDR f_fh;
static AOUTHDR f_ah;
static SCNHDR *f_sh;
static SYMENT *f_symtab;
static SYM_ENTRY *f_aoutsyms;
static AUXENT *f_aux;
static LINENO **f_lnno;
static char *f_string_table;
static char *f_types;

/* built internally */

typedef struct {
  char *filename;
  word32 first_address;
  word32 last_address;
  LINENO *lines;
  int num_lines;
} FileNode;

static FileNode *files;
static int num_files;

typedef struct SymNode {
  char *name;
  word32 address;
  char type_c;
} SymNode;

static SymNode *syms;
static SymNode *syms_byname;
static int num_syms;

static int syms_sort_bn(const void *a, const void *b)
{
  SymNode *sa = (SymNode *)a;
  SymNode *sb = (SymNode *)b;
  return strcmp(sa->name, sb->name);
}

static int syms_sort_bv(const void *a, const void *b)
{
  SymNode *sa = (SymNode *)a;
  SymNode *sb = (SymNode *)b;
  return sa->address - sb->address;
}

static char *symndup(char *s, int len)
{
  char c = s[len], *rv;
  s[len] = 0;
  rv = strdup(s);
  s[len] = c;
  return rv;
}

static int valid_symbol(int i)
{
  char *sn;
  if (f_symtab[i].e.e.e_zeroes)
    sn = f_symtab[i].e.e_name; 
  else
    sn = f_string_table + f_symtab[i].e.e.e_offset;
  if (sn[0] != '_')
    return 0;
  if (strncmp(sn, "___gnu_compiled", 15) == 0)
    return 0;
  if (strcmp(sn, "__DYNAMIC") == 0)
    return 0;
  return 1;
}

static void process_coff(FILE *fd, long ofs)
{
  int i, f, s, f_pending;
  LINENO *l;
  int l_pending;
  word32 strsize;
  char *name;

  fseek(fd, ofs, 0);
  fread(&f_fh, 1, FILHSZ, fd);
  fread(&f_ah, 1, AOUTSZ, fd);
  f_sh = (SCNHDR *)malloc(f_fh.f_nscns * SCNHSZ);
  f_types = (char *)malloc(f_fh.f_nscns);
  f_lnno = (LINENO **)malloc(f_fh.f_nscns * sizeof(LINENO *));
  fread(f_sh, f_fh.f_nscns, SCNHSZ, fd);

  for (i=0; i<f_fh.f_nscns; i++)
  {
    if (f_sh[i].s_flags & STYP_TEXT)
      f_types[i] = 'T';
    if (f_sh[i].s_flags & STYP_DATA)
      f_types[i] = 'D';
    if (f_sh[i].s_flags & STYP_BSS)
      f_types[i] = 'B';
    if (f_sh[i].s_nlnno)
    {
      fseek(fd, ofs + f_sh[i].s_lnnoptr, 0L);
      f_lnno[i] = (LINENO *)malloc(f_sh[i].s_nlnno * LINESZ);
      fread(f_lnno[i], LINESZ, f_sh[i].s_nlnno, fd);
    }
    else
      f_lnno[i] = 0;
  }

  fseek(fd, ofs + f_fh.f_symptr + f_fh.f_nsyms * SYMESZ, 0);
  fread(&strsize, 1, 4, fd);
  f_string_table = (char *)malloc(strsize);
  fread(f_string_table+4, 1, strsize-4, fd);
  f_string_table[0] = 0;

  fseek(fd, ofs+f_fh.f_symptr, 0);
  f_symtab = (SYMENT *)malloc(f_fh.f_nsyms * SYMESZ);
  fread(f_symtab, SYMESZ, f_fh.f_nsyms, fd);
  f_aux = (AUXENT *)f_symtab;

  num_syms = num_files = 0;
  for (i=0; i<f_fh.f_nsyms; i++)
  {
    switch (f_symtab[i].e_sclass)
    {
      case C_FILE:
        num_files++;
        break;
      case C_EXT:
      case C_STAT:
        if (!valid_symbol(i))
          break;
        num_syms++;
        break;
    }
    i += f_symtab[i].e_numaux;
  }

  files = (FileNode *)malloc(num_files * sizeof(FileNode));

  syms = (SymNode *)malloc(num_syms * sizeof(SymNode));

  f = s = f_pending = l_pending = 0;
  for (i=0; i<f_fh.f_nsyms; i++)
  {
    switch (f_symtab[i].e_sclass)
    {
      case C_FILE:
        if (f_aux[i+1].x_file.x_n.x_zeroes)
          files[f].filename = symndup(f_aux[i+1].x_file.x_fname, 16);
        else
          files[f].filename = f_string_table + f_aux[i+1].x_file.x_n.x_offset;
        files[f].lines = 0;
        f_pending = 1;
	f++;
        break;
      case C_EXT:
      case C_STAT:

        if (f_symtab[i].e.e.e_zeroes)
          name = f_symtab[i].e.e_name;
        else
          name = f_string_table + f_symtab[i].e.e.e_offset;

        if (f_pending && strcmp(name, ".text") == 0)
        {
          files[f-1].first_address = f_symtab[i].e_value;
          files[f-1].last_address = f_symtab[i].e_value + f_aux[i+1].x_scn.x_scnlen - 1;
          files[f-1].num_lines = f_aux[i+1].x_scn.x_nlinno;
          f_pending = 0;
        }

        if (ISFCN(f_symtab[i].e_type))
        {
          int scn = f_symtab[i].e_scnum - 1;
          l = f_lnno[scn] + ((f_aux[i+1].x_sym.x_fcnary.x_fcn.x_lnnoptr - f_sh[scn].s_lnnoptr)/LINESZ);
          l_pending = 1;
          l->l_addr.l_paddr = f_symtab[i].e_value;
        }

        if (!valid_symbol(i))
          break;

        syms[s].address = f_symtab[i].e_value;
        if (f_symtab[i].e.e.e_zeroes)
          syms[s].name = symndup(f_symtab[i].e.e_name, 8);
        else
          syms[s].name = f_string_table + f_symtab[i].e.e.e_offset;
        
        switch (f_symtab[i].e_scnum)
        {
          case 1 ... 10:
            syms[s].type_c = f_types[f_symtab[i].e_scnum-1];
            break;
          case N_UNDEF:
            syms[s].type_c = 'U';
            break;
          case N_ABS:
            syms[s].type_c = 'A';
            break;
          case N_DEBUG:
            syms[s].type_c = 'D';
            break;
        }
        if (f_symtab[i].e_sclass == C_STAT)
          syms[s].type_c += 'a' - 'A';

        s++;
        break;
      case C_FCN:
        if (f_pending && files[f-1].lines == 0)
        {
          files[f-1].lines = l;
        }
        if (l_pending)
        {
          int lbase = f_aux[i+1].x_sym.x_misc.x_lnsz.x_lnno - 1;
          int i2;
          l->l_lnno = lbase;
          l++;
          for (i2=0; l[i2].l_lnno; i2++)
            l[i2].l_lnno += lbase;
          l_pending = 0;
        }
        break;
    }
    i += f_symtab[i].e_numaux;
  }
}

static void process_aout(FILE *fd, long ofs)
{
  GNU_AOUT header;
  word32 string_table_length;
  int nsyms, i, f, s, l;

  fseek(fd, ofs, 0);
  fread(&header, 1, sizeof(header), fd);

  fseek(fd, ofs + sizeof(header) + header.tsize + header.dsize + header.txrel + header.dtrel, 0);
  nsyms = header.symsize / sizeof(SYM_ENTRY);
  f_aoutsyms = (SYM_ENTRY *)malloc(header.symsize);
  fread(f_aoutsyms, 1, header.symsize, fd);

  fread(&string_table_length, 1, 4, fd);
  f_string_table = (char *)malloc(string_table_length);
  fread(f_string_table+4, 1, string_table_length-4, fd);
  f_string_table[0] = 0;

  num_files = num_syms = 0;
  for (i=0; i<nsyms; i++)
  {
    char *symn = f_string_table + f_aoutsyms[i].string_off;
    char *cp;
    switch (f_aoutsyms[i].type & ~N_EXT)
    {
      case N_SO:
        if (symn[strlen(symn)-1] == '/')
          break;
        num_files++;
        break;
      case N_TEXT:
        cp = symn + strlen(symn) - 2;
	if (strncmp(symn, "___gnu", 6) == 0 ||
	    strcmp(cp, "d.") == 0 /* as in gcc_compiled. */ ||
	    strcmp(cp, ".o") == 0)
	  break;
      case N_DATA:
      case N_ABS:
      case N_BSS:
      case N_FN:
      case N_SETV:
      case N_SETA:
      case N_SETT:
      case N_SETD:
      case N_SETB:
      case N_INDR:
        num_syms ++;
        break;
    }
  }
  
  syms = (SymNode *)malloc(num_syms * sizeof(SymNode));
  memset(syms, num_syms * sizeof(SymNode), 0);
  files = (FileNode *)malloc(num_files * sizeof(FileNode));
  memset(files, num_files * sizeof(FileNode), 0);

  f = s = 0;
  for (i=0; i<nsyms; i++)
  {
    char c, *cp;
    char *symn = f_string_table + f_aoutsyms[i].string_off;
    switch (f_aoutsyms[i].type & ~N_EXT)
    {
      case N_SO:
        if (symn[strlen(symn)-1] == '/')
          break;
        if (f && files[f-1].last_address == 0)
          files[f-1].last_address = f_aoutsyms[i].val - 1;
        files[f].filename = symn;
        files[f].first_address = f_aoutsyms[i].val;
        f ++;
        break;
      case N_SLINE:
        files[f-1].num_lines++;
        break;
      case N_TEXT:
        cp = symn + strlen(symn) - 2;
	if (strncmp(symn, "___gnu", 6) == 0 ||
	    strcmp(cp, "d.") == 0 /* as in gcc_compiled. */ ||
	    strcmp(cp, ".o") == 0)
        {
          if (f && files[f-1].last_address == 0)
            files[f-1].last_address = f_aoutsyms[i].val - 1;
          break;
        }
	c = 't';
	goto sym_c;
      case N_DATA:
	c = 'd';
	goto sym_c;
      case N_ABS:
	c = 'a';
	goto sym_c;
      case N_BSS:
	c = 'b';
	goto sym_c;
      case N_FN:
	c = 'f';
	goto sym_c;
      case N_SETV:
	c = 'v';
	goto sym_c;
      case N_SETA:
	c = 'v';
	goto sym_c;
      case N_SETT:
	c = 'v';
	goto sym_c;
      case N_SETD:
	c = 'v';
	goto sym_c;
      case N_SETB:
	c = 'v';
	goto sym_c;
      case N_INDR:
	c = 'i';
	sym_c:
        syms[s].name = symn;
        syms[s].address = f_aoutsyms[i].val;
        syms[s].type_c = (f_aoutsyms[i].type & N_EXT) ? (c+'A'-'a') : c;
        s ++;
        break;
    }
  }
  
  l = f = 0;
  for (i=0; i<nsyms; i++)
  {
    char c, *cp;
    char *symn = f_string_table + f_aoutsyms[i].string_off;
    switch (f_aoutsyms[i].type & ~N_EXT)
    {
      case N_SO:
        if (symn[strlen(symn)-1] == '/')
          break;
        files[f].lines = (LINENO *)malloc(files[f].num_lines * sizeof(LINENO));
        f++;
        l = 0;
        break;
      case N_SLINE:
        files[f-1].lines[l].l_addr.l_paddr = f_aoutsyms[i].val;
        files[f-1].lines[l].l_lnno = f_aoutsyms[i].desc;
        l ++;
        break;
    }
  }
  
}

static void process_file(FILE *fd, long ofs)
{
  short s, exe[2];
  fseek(fd, ofs, 0);
  fread(&s, 1, 2, fd);
  switch (s)
  {
    case 0x5a4d:	/* .exe */
      fread(exe, 2, 2, fd);
      ofs += (long)exe[1] * 512L;
      if (exe[0])
        ofs += (long)exe[0] - 512L;
      process_file(fd, ofs);
      break;
    case 0x014c:	/* .coff */
      process_coff(fd, ofs);
      break;
    case 0x010b:	/* a.out ZMAGIC */
    case 0x0107:	/* a.out object */
      process_aout(fd, ofs);
      break;
  }
}

void syms_init(char *fname)
{
  FILE *fd = fopen(fname, "rb");
  if (fd == 0)
  {
    perror(fname);
  }
  else
  {
    process_file(fd, 0);

    syms_byname = (SymNode *)malloc(num_syms * sizeof(SymNode));
    memcpy(syms_byname, syms, num_syms * sizeof(SymNode));
    qsort(syms_byname, num_syms, sizeof(SymNode), syms_sort_bn);
    qsort(syms, num_syms, sizeof(SymNode), syms_sort_bv);

    fclose(fd);
  }
}

int lookup_sym_byname(char *name, int idx, int ofs)
{
  int below, above;
  char ch = name[idx];
  name[idx] = 0;
  
  below = -1;
  above = num_syms;
  while (above - below > 1)
  {
    int mid = (above + below) / 2;
    int c = 0;
    if (ofs)
      c = '_' - syms_byname[mid].name[0];
    if (c == 0)
      c = strcmp(name, syms_byname[mid].name+ofs);
    if (c == 0)
    {
      name[idx] = ch;
      return mid;
    }
    if (c < 0)
      above = mid;
    else
      below = mid;
  }
  name[idx] = ch;
  return -1;
}

word32 syms_name2val(char *name)
{
  int cval, idx, sign=1, i;
  word32 v;
  char *cp;

  undefined_symbol = 0;

  idx = 0;
  sscanf(name, "%s", name);

  if (name[0] == 0)
    return 0;

  if (name[0] == '-')
  {
    sign = -1;
    name++;
  }
  else if (name[0] == '+')
  {
    name++;
  }
  if (isdigit(name[0]))
  {
    if (sign == -1)
      return -strtol(name, 0, 0);
    return strtol(name, 0, 0);
  }

  cp = strpbrk(name, "+-");
  if (cp)
    idx = cp-name;
  else
    idx = strlen(name);

  if (name[0] == '%') /* register */
  {
    for (i=0; regs[i].name; i++)
      if (strncmp(name, regs[i].name, idx) == 0)
      {
	switch (regs[i].size)
	{
	  case 1:
	    v = *(word8 *)((word8 *)(&a_tss) + regs[i].ofs);
	    break;
	  case 2:
	    v = *(word16 *)((word8 *)(&a_tss) + regs[i].ofs);
	    break;
	  case 4:
	    v = *(word32 *)((word8 *)(&a_tss) + regs[i].ofs);
	    break;
	}
	return v + syms_name2val(name+idx);
      }
  }

  for (i=0; i<idx; i++)
    if (name[i] == '#')
    {
      int f;
      int lnum, l;
      sscanf(name+i+1, "%d", &lnum);
      for (f=0; f<num_files; f++)
      {
	if ((strncmp(name, files[f].filename, i) == 0) && (files[f].filename[i] == 0))
	{
	  for (l=0; l<files[f].num_lines; l++)
	  {
	    if (files[f].lines[l].l_lnno == lnum)
	      return files[f].lines[l].l_addr.l_paddr + syms_name2val(name+idx);
	  }
	  printf("undefined line number %.*s\n", idx, name);
	  undefined_symbol = 1;
	  return 0;
	}
      }
      printf("Undefined file name %.*s\n", i, name);
      undefined_symbol = 1;
      return 0;
    }

  i = lookup_sym_byname(name, idx, 0);
  if (i == -1)
    i = lookup_sym_byname(name, idx, 1);
  if (i != -1)
    return syms_byname[i].address * sign + syms_name2val(name+idx);
  printf("Undefined symbol %.*s\n", idx, name);
  undefined_symbol = 1;
  return 0;
}

static char noname_buf[11];

char *syms_val2name(word32 val, word32 *delta)
{
  static char buf[1000];
  int above, below, mid;

  if (delta)
    *delta = 0;

  if (num_syms <= 0)
    goto noname;  
  above = num_syms;
  below = -1;
  while (above-below > 1)
  {
    mid = (above+below)/2;
    if (syms[mid].address == val)
      break;
    if (syms[mid].address > val)
      above = mid;
    else
      below = mid;
  }
  if (syms[mid].address > val)
  {
    if (mid == 0)
      goto noname;
    mid--; /* the last below was it */
  }
  if (mid < 0)
    goto noname;
  if (strcmp(syms[mid].name, "_end") == 0)
    goto noname;
  if (strcmp(syms[mid].name, "__end") == 0)
    goto noname;
  if (strcmp(syms[mid].name, "_etext") == 0)
    goto noname;
  if (delta)
    *delta = val - syms[mid].address;
  return syms[mid].name;
noname:
  sprintf(noname_buf, "%#lx", val);
  return noname_buf;
}

char *syms_val2line(word32 val, int *lineret, int exact)
{
  int f, l;
  for (f=0; f<num_files; f++)
  {
    if (val >= files[f].first_address && val <= files[f].last_address && files[f].lines)
    {
      for (l=files[f].num_lines-1; l >= 0 && files[f].lines[l].l_addr.l_paddr > val; l--);
      if ((files[f].lines[l].l_addr.l_paddr != val) && exact)
        return 0;
      *lineret = files[f].lines[l].l_lnno;
      return files[f].filename;
    }
  }
  return 0;
}

void syms_listwild(char *pattern)
{
  int linecnt = 0;
  int lnum;
  char *name;
  int i, key;

  for (i=0; i<num_syms; i++)
    if (wild(pattern, syms_byname[i]))
    {
      if (++linecnt > 20)
      {
        printf("--- More ---");
        fflush(stdout);
        key = getkey();
        printf("\r            \r");
        switch (key)
        {
	  case ' ':
  	    linecnt = 0;
	    break;
	  case 13:
	    linecnt--;
	    break;
	  case 'q':
	  case 27:
	    return;
        }
      }
      printf("0x%08lx %c %s", syms_byname[i].address, syms_byname[i].type_c, syms_byname[i].name);
      name = syms_val2line(syms_byname[i].address, &lnum, 0);
      if (name)
        printf(", line %d of %s", lnum, name);
      putchar('\n');
    }
}

#endif

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.