#ifndef USE_NM
#include "link.h"
#include <stdlib.h>
#include <string.h>
#ifdef USE_BFD
#ifdef __alpha__
#include <ansidecl.h>
#else
#ifndef PARAMS
#define PARAMS(paramlist)              	paramlist
#endif
#endif
#include <bfd.h>
#else /* USE_BFD */
#include <a.out.h>
#include <linux/elf.h>
#endif /* USE_BFD */

/*
 * "mini-nm" support by Bjorn Ekwall  <bj0rn@blox.se>
 *
 * Support for BFD (esp. for Alpha) by:
 *	Manabe Takashi <manabe@Roy.dsl.tutics.tut.ac.jp>
 */
static void setupmod(MODULE *mod, SYMBOL *tbpub[], int nbpub,
	SYMBOL *tbext[], int nbext)
{
	mod->is_load = 1;
	mod->pub.nb = nbpub;
	mod->ext.nb = nbext;
	if (nbpub > 0){
		int size = nbpub*sizeof(SYMBOL*);
		mod->pub.tb = (SYMBOL**)malloc_err(size,1);
		memcpy (mod->pub.tb,tbpub,size);
	}else{
		mod->pub.tb = NULL;
	}
	if (nbext > 0){
		int size = nbext*sizeof(SYMBOL*);
		mod->ext.tb = (SYMBOL**)malloc_err(size,1);
		memcpy (mod->ext.tb,tbext,size);
		SYMBOL **ptext = mod->ext.tb;
		for (int i=0; i<nbext; i++,ptext++)
			(*ptext)->requis = 1;
	}else{
		mod->ext.tb = NULL;
	}
}

#ifdef USE_BFD

static
const char *load_bfd(bfd *bfdp, SYMBOLS &syms, MODULE *mod)
{
	SYMBOL *tbext[5000];
	int nbext = 0;
	SYMBOL *tbpub[5000];
	int nbpub = 0;
	int mr = 0;
	long size;
	asymbol **symp, **sp;

	if (!bfd_check_format(bfdp, bfd_object)) {
	    bfd_close(bfdp);
	    return bfd_errmsg(bfd_get_error());
	}
	if ((size = bfd_get_symtab_upper_bound(bfdp)) <= 0) {
	    bfd_close(bfdp);
	    return bfd_errmsg(bfd_get_error());
	}
	if (!(symp = (asymbol **)malloc(size))) {
	    bfd_close(bfdp);
	    return "Memory allocation error";
	}
	if (!bfd_canonicalize_symtab(bfdp, symp))
	    return bfd_errmsg(bfd_get_error());
	for (sp = symp; *sp != NULL; sp ++) {
	    const char *name=bfd_asymbol_name(*sp);
	    symbol_info syminfo;
	    flagword flags=(*sp)->flags;

	    bfd_get_symbol_info(bfdp, *sp, &syminfo);
	    switch(syminfo.type) {
	    case 'U':
	      tbext[nbext++] = syms.add(name,NULL, SYM_REQUIS, mr, 0);
	      break;
	    case 't':
	    case 'd':
	      if (flags & BSF_DEBUGGING) break;
	    case 'T':
	    case 'D':
	      tbpub[nbpub++] = syms.add(name,mod, SYM_DEFINI, mr, 0);
	      break;
	    }
	}
	setupmod(mod, tbpub, nbpub, tbext, nbext);
	free(symp);
	return (char *)0;
}

#else /* USE_BFD */

static char *load_aout(FILE *fp, struct exec *aouthdr, SYMBOLS &syms, MODULE *mod)
{
	SYMBOL *tbext[5000];
	int nbext = 0;
	SYMBOL *tbpub[5000];
	int nbpub = 0;
	int mr = 0;
	struct nlist *symtab;
	struct nlist *sp;
	long filesize;
	char *stringtab;
	int i;
	int len;
	int nsymbols;

	fseek(fp, 0L, SEEK_END);
	filesize = ftell(fp);

	/* read in the symbol table */
	fseek(fp, N_SYMOFF((*aouthdr)), SEEK_SET);
	nsymbols = aouthdr->a_syms / sizeof (struct nlist);
	symtab = (struct nlist*) malloc_err(nsymbols * sizeof (*symtab), 1);
	fread(symtab, nsymbols * sizeof (*symtab), 1, fp);
	if (feof(fp) || ferror(fp)) {
		free(symtab);
		return "Error reading symbol table";
	}

	/* Now get the symbols strings */
	len = filesize - N_STROFF((*aouthdr));
	stringtab = (char*) malloc_err(len, 1);
	fread(stringtab, len, 1, fp);
	if (feof(fp) || ferror(fp)) {
		free(symtab);
		free(stringtab);
		return "Error reading stringtab";
	}

	for (i = nsymbols, sp = symtab ; --i >= 0 ; sp++) {
		char *name = stringtab + sp->n_un.n_strx;

		if (*name == '_') /* Ignore leading '_' */
			++name;
		if ((sp->n_type == N_EXT) && (sp->n_value == 0))
			tbext[nbext++] = syms.add(name,NULL, SYM_REQUIS, mr, 0);
		else
			tbpub[nbpub++] = syms.add(name,mod, SYM_DEFINI, mr, 0/*buf[9] == 'C'*/);
	}

	setupmod(mod, tbpub, nbpub, tbext, nbext);
	free(symtab);
	free(stringtab);
	return (char *)0;
}

static char *load_elf(FILE *fp, Elf32_Ehdr *epnt, SYMBOLS &syms, MODULE *mod)
{
	SYMBOL *tbext[5000];
	int nbext = 0;
	SYMBOL *tbpub[5000];
	int nbpub = 0;
	int mr = 0;
	Elf32_Shdr *sections;
	Elf32_Shdr *spnt;
	struct elf32_sym *sp;
	struct elf32_sym *symtab = (struct elf32_sym *)0;
	long filesize;
	char *stringtab = (char *)0;
	int i;
	int n;
	int nsymbols = 0;

	if (epnt->e_phnum != 0)
		return "can't handle program headers...";

	fseek(fp, 0L, SEEK_END);
	filesize = ftell(fp);

	/* load the section headers */
	sections = (Elf32_Shdr *)malloc_err(epnt->e_shentsize * epnt->e_shnum, 1);

	fseek(fp, epnt->e_shoff, SEEK_SET);
	fread((char *)sections, epnt->e_shentsize * epnt->e_shnum, 1, fp);
	if (feof(fp) || ferror(fp)) {
		free(sections);
		return "Error reading ELF section headers";
	}

	for (spnt = sections, i = 0; i < epnt->e_shnum; ++i, ++spnt) {
		switch (spnt->sh_type) {
		default:
			break; /* IGNORE */

		case SHT_SYMTAB:
			if (nsymbols) {
				free(sections);
				if (stringtab)
					free(stringtab);
			        return "Can't handle >1 symbol section";
			}
			nsymbols = spnt->sh_size / spnt->sh_entsize;
			symtab = (struct elf32_sym*)malloc_err(nsymbols * sizeof (*symtab), 1);
			fseek(fp, spnt->sh_offset, SEEK_SET);
			fread(symtab, nsymbols * sizeof (*symtab), 1, fp);
			if (feof(fp) || ferror(fp)) {
				free(sections);
				free(symtab);
				if (stringtab)
					free(stringtab);
				return "Error reading ELF SYMTAB section";
			}
			break;

		case SHT_STRTAB:
			stringtab = (char *)malloc_err(spnt->sh_size, 1);
			fseek(fp, spnt->sh_offset, SEEK_SET);
			fread(stringtab, spnt->sh_size, 1, fp);
			if (feof(fp) || ferror(fp)) {
				free(sections);
				free(stringtab);
				if (symtab)
					free(symtab);
				return "Error reading ELF STRTAB section";
			}
			break;
		}
	}

	for (n = nsymbols, sp = symtab ; --n >= 0 ; sp++) {
		char *name = stringtab + sp->st_name;

		if (*name == '\0')
			continue;
		if (sp->st_shndx == SHN_UNDEF)
			tbext[nbext++] = syms.add(name,NULL, SYM_REQUIS, mr, 0);
		else
			tbpub[nbpub++] = syms.add(name,mod, SYM_DEFINI,
				mr, (sp->st_shndx == SHN_COMMON));
	}

	setupmod(mod, tbpub, nbpub, tbext, nbext);
	if (stringtab)
		free(stringtab);
	if (symtab)
		free(symtab);
	free(sections);
	return (char *)0;
}

#endif /* USE_BFD */

int load_obj(SYMBOLS &syms, const char *module, MODULE *mod)
{
#ifdef	USE_BFD
	bfd *bfdp;
#else
	FILE *fp;
	Elf32_Ehdr header;
	struct exec *aouthdr = (struct exec *)&header;
#endif
	const char *errstr = (char *)0;

#ifdef USE_BFD
	bfd_init();
	if ((bfdp = bfd_openr(module, 0)) != NULL) {
	    errstr = load_bfd(bfdp, syms, mod);
	    bfd_close(bfdp);
	} else return 1;
#else /* USE_BFD */
	/* open file and read header */
	if ((fp = fopen(module, "r")) == NULL)
		return 1;

	/* sizeof(struct elfhdr) > sizeof(struct exec) */
	fread(&header, sizeof(Elf32_Ehdr), 1, fp);
	if (feof(fp) || ferror(fp))
		return 1;

	if (N_MAGIC((*aouthdr)) == OMAGIC)
		errstr = load_aout(fp, aouthdr, syms, mod);
	else if ((header.e_ident[0] == 0x7f) &&
		 (strncmp((const char *)&header.e_ident[1], "ELF",3) == 0) &&
		 (header.e_type == ET_REL))
		errstr = load_elf(fp, &header, syms, mod);
#endif /* USE_BFD */
	if (errstr != (char *)0)
		return 1;
	return 0;
}
#endif
