#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <libelf.h>

SYMTAB *
Snarf_Symbols (lf)
     int	lf;
{
    SYMTAB	*tab = NULL;
    register SYM *sp, **nextp;
    Elf		*elf_ptr;
    Elf_Scn	*elf_scn_ptr = NULL, *symtab_scn_ptr = NULL;
    Elf_Data	*elf_data_ptr = NULL;
    Elf32_Ehdr	*elf_ehdr_ptr = NULL;
    Elf32_Shdr	*elf_shdr_ptr = NULL,
		*symtab_ptr = NULL;
    size_t	elf_str_index, shstrndx;
    char	*symbol_name, *section_name;

    if (elf_version(EV_CURRENT) == EV_NONE)
      Primitive_Error("a.out file Elf version out of date");
    if ((elf_ptr = elf_begin(lf, ELF_C_READ, (Elf *)NULL)) == NULL)
      Primitive_Error ("can't elf_begin() a.out file");

    /* 
     * get the elf header, so we'll know where to look for the section 
     * names.
     */
    if ((elf_ehdr_ptr = elf32_getehdr(elf_ptr)) == NULL) {
	Primitive_Error("no elf header in a.out file");
    }
    shstrndx = elf_ehdr_ptr->e_shstrndx;
    /* look for the symbol and string tables */
    while (elf_scn_ptr = elf_nextscn(elf_ptr, elf_scn_ptr)) {
	if ((elf_shdr_ptr = elf32_getshdr(elf_scn_ptr)) == NULL)
	  Primitive_Error("can't get section header in a.out file");
	if (elf_shdr_ptr->sh_type == SHT_STRTAB) {
	    /* 
	     * save the index to the string table for later use by 
	     * elf_strptr().
	     */
	    section_name = elf_strptr(elf_ptr, shstrndx,
				      (size_t)elf_shdr_ptr->sh_name);
	    if (strcmp(section_name, ".strtab") == 0) {
		elf_str_index = elf_ndxscn(elf_scn_ptr);
	    }
	}
	else if (elf_shdr_ptr->sh_type == SHT_SYMTAB) {
	    symtab_ptr = elf_shdr_ptr;
	    symtab_scn_ptr = elf_scn_ptr;
	}
    }
    if (!symtab_ptr)
      Primitive_Error("no symbol table in a.out file");
    if (!elf_str_index)
      Primitive_Error("no string table in a.out file");
    /* 
     * we've located the symbol table -- go through it and save the names 
     * of the interesting symbols.
     */
    while (elf_data_ptr = elf_getdata(symtab_scn_ptr, elf_data_ptr)) {
	char	*name = NULL;
	int	symbol_count;
	Elf32_Sym	*symbol_ptr = elf_data_ptr->d_buf;
	Elf32_Sym	*current_symbol;

	tab = (SYMTAB *)Safe_Malloc (sizeof (SYMTAB));
	tab->first = 0;
	tab->strings = 0;
	nextp = &tab->first;
	for (symbol_count = 1;
	     /* < was <= in the version I received from the author, but
	      * the last entry seems to be bogus:
	      */
	     symbol_count < symtab_ptr->sh_size / symtab_ptr->sh_entsize;
	     symbol_count++) {
	    current_symbol = symbol_ptr + symbol_count;
	    if (ELF32_ST_TYPE(current_symbol->st_info) != STT_FUNC &&
		ELF32_ST_BIND(current_symbol->st_info) != STB_GLOBAL) {
		continue;
	    }
	    if ((name = elf_strptr(elf_ptr, elf_str_index,
				   (size_t)current_symbol->st_name)) == NULL) {
		Free_Symbols(tab);
		(void)close(lf);
		Primitive_Error(elf_errmsg(elf_errno()));
	    }
	    sp = (SYM *)Safe_Malloc (sizeof (SYM));
	    sp->name = Safe_Malloc (strlen (name) + 1);
	    strcpy (sp->name, name);
	    sp->value = current_symbol->st_value;
	    *nextp = sp;
	    nextp = &sp->next;
	    *nextp = 0;
	}
    }
    return tab;
}
  
#ifdef INIT_OBJECTS
SYMTAB *
Open_File_And_Snarf_Symbols (name)
     char *name;
{
    int		f;
    SYMTAB	*tab;

    if ((f = open (name, O_RDONLY)) == -1)
	Primitive_Error ("can't open a.out file");
    tab = Snarf_Symbols (f);
    (void)close(f);
    return tab;
}
#endif /* INIT_OBJECTS */
