/**
 * @file op_bfd.h
 * Encapsulation of bfd objects
 *
 * @remark Copyright 2002 OProfile authors
 * @remark Read the file COPYING
 *
 * @author Philippe Elie
 * @author John Levon
 */

#ifndef OP_BFD_H
#define OP_BFD_H

#include "config.h"

#include <vector>
#include <string>
#include <list>
#include <map>
#include <set>

#include "bfd_support.h"
#include "locate_images.h"
#include "utility.h"
#include "cached_value.h"
#include "op_types.h"

class op_bfd;
class string_filter;
class extra_images;

/// all symbol vector indexing uses this type
typedef size_t symbol_index_t;

/**
 * A symbol description from a bfd point of view. This duplicate
 * information pointed by an asymbol, we need this duplication in case
 * the symbol is an artificial symbol
 */
class op_bfd_symbol {
public:

	/// ctor for real symbols
	op_bfd_symbol(asymbol const * a);

	/// ctor for artificial symbols
	op_bfd_symbol(bfd_vma vma, size_t size, std::string const & name);

	bfd_vma vma() const { return symb_value + section_vma; }
	unsigned long value() const { return symb_value; }
	unsigned long filepos() const { return symb_value + section_filepos; }
	unsigned long symbol_endpos(void) const;
	asection const * section(void) const { return bfd_symbol->section; }
	std::string const & name() const { return symb_name; }
	asymbol const * symbol() const { return bfd_symbol; }
	size_t size() const { return symb_size; }
	void size(size_t s) { symb_size = s; }
	bool hidden() const { return symb_hidden; }
	bool weak() const { return symb_weak; }
	bool artificial() const { return symb_artificial; }

	/// compare two symbols by their filepos()
	bool operator<(op_bfd_symbol const & lhs) const;

private:
	/// the original bfd symbol, this can be null if the symbol is an
	/// artificial symbol
	asymbol const * bfd_symbol;
	/// the offset of this symbol relative to the begin of the section's
	/// symbol
	unsigned long symb_value;
	/// the section filepos for this symbol
	unsigned long section_filepos;
	/// the section vma for this symbol
	bfd_vma section_vma;
	/// the size of this symbol
	size_t symb_size;
	/// the name of the symbol
	std::string symb_name;
	/// normally not externally visible symbol
	bool symb_hidden;
	/// whether other symbols can override it
	bool symb_weak;
	/// symbol is artificially created
	bool symb_artificial;
	/// code bytes corresponding to symbol -- used for XML generation
	std::string symb_bytes;
};

/**
 * Encapsulation of a bfd object. Simplifies open/close of bfd, enumerating
 * symbols and retrieving informations for symbols or vma.
 *
 * Use of this class relies on a std::ostream cverb
 */
class op_bfd {
public:
	/**
	 * @param filename  the name of the image file
	 * @param symbol_filter  filter to apply to symbols
	 * @param extra_images container where all extra candidate filenames
	 *    are stored
	 * @param ok in-out parameter: on in, if not set, don't
	 * open the bfd (because it's not there or whatever). On out,
	 * it's set to false if the bfd couldn't be loaded.
	 */
	op_bfd(std::string const & filename,
	       string_filter const & symbol_filter,
	       extra_images const & extra_images,
	       bool & ok);

	/**
	 * This constructor is used when the /proc/kallsyms file is used
	 * to get the kernel symbols.
	 */
	op_bfd(std::string const & filename,
	       extra_images const & extra_images);

	/// close an opened bfd image and free all related resources
	~op_bfd();

	/**
	 * @param sym_idx index of the symbol
	 * @param offset fentry number
	 * @param filename output parameter to store filename
	 * @param linenr output parameter to store linenr.
	 *
	 * Retrieve the relevant finename:linenr information for the sym_idx
	 * at offset. If the lookup fails, return false. In some cases this
	 * function can retrieve the filename and return true but fail to
	 * retrieve the linenr and so can return zero in linenr
	 */
	bool get_linenr(symbol_index_t sym_idx, bfd_vma offset,
			std::string & filename, unsigned int & linenr) const;

	/**
	 * @param sym_idx symbol index
	 * @param start reference to start var
	 * @param end reference to end var
	 *
	 * Calculates the range of sample file entries covered by sym. start
	 * and end will be filled in appropriately. If index is the last entry
	 * in symbol table, all entries up to the end of the sample file will
	 * be used.  After calculating start and end they are sanitized
	 *
	 * All errors are fatal.
	 */
	void get_symbol_range(symbol_index_t sym_idx,
			      unsigned long long & start, unsigned long long & end) const;

	/**
	 * @param start reference to the start vma
	 * @param end reference to the end vma
	 *
	 * return in start, end the vma range for this binary object.
	 */
	void get_vma_range(bfd_vma & start, bfd_vma & end) const;

	/** return the relocated PC value for the given file offset */
	bfd_vma offset_to_pc(bfd_vma offset) const;

	/**
	 * If passed 0, return the file position of the .text section.
	 * Otherwise, return the filepos of a section with a matching
	 * vma.
	 */
	unsigned long get_start_offset(bfd_vma vma = 0) const;
 
	/**
	 * Return the image name of the underlying binary image. For an
	 * archive, this returns the path *within* the archive, not the
	 * full path of the file.
	 */
	std::string get_filename() const;

	/// sorted vector by vma of interesting symbol.
	std::vector<op_bfd_symbol> syms;

	/// return in bits the bfd_vma size for this binary. This is needed
	/// because gprof output depend on the bfd_vma for *this* binary
	/// and do not depend on sizeof(bfd_vma)
	size_t bfd_arch_bits_per_address() const;

	/// return true if binary contain some debug information
	bool has_debug_info() const;

	/**
	 * @param sym_idx symbol index
	 *
	 * Return true or false, indicating whether or not the
	 * symbol referenced by the passed sym_idx has code available.
	 * Some symbols have no code associated with them; for example,
	 * artificial symbols created for anonymous memory samples or for
	 * stripped binaries with no symbol debug info.  Additionally,
	 * if the bfd object associated with the symbol is not valid,
	 * this function will also return false.
	 *
	 * NOTE:  This call should be made prior to invoking
	 *        get_symbol_contents to avoid unnecessarily allocating
	 *        memory for the symbol contents.
	 */
	bool symbol_has_contents(symbol_index_t sym_idx);

	bool get_symbol_contents(symbol_index_t sym_index,
		unsigned char * contents) const;

	bool valid() const { return ibfd.valid(); }

	bfd_vma get_vma_adj(void) const { return vma_adj; }

private:
	/// temporary container type for getting symbols
	typedef std::list<op_bfd_symbol> symbols_found_t;

	/**
	 * Parse and sort in ascending order all symbols
	 * in the file pointed to by abfd that reside in
	 * a %SEC_CODE section.
	 *
	 * The symbols are filtered through
	 * the interesting_symbol() predicate and sorted
	 * with op_bfd_symbol::operator<() comparator.
	 */
	void get_symbols(symbols_found_t & symbols);

	/* functions for reading kallsyms */
	void get_kallsym_symbols(symbols_found_t & symbols, std::ifstream& infile);

	/**
	 * Helper function for get_symbols.
	 * Populates bfd_syms and extracts the "interesting_symbol"s.
	 */
	void get_symbols_from_file(bfd_info & bfd, size_t start,
				   op_bfd::symbols_found_t & symbols,
				   bool debug_file);

	/**
	 * Add the symbols in the binary, applying filtering,
	 * and handling artificial symbols.
	 */
	void add_symbols(symbols_found_t & symbols,
	                 string_filter const & symbol_filter);

	/**
	 * symbol_size - return the size of a symbol
	 * @param sym  symbol to get size
	 * @param next  next symbol in vma order if any
	 */
	size_t symbol_size(op_bfd_symbol const & sym,
			   op_bfd_symbol const * next) const;

	/// create an artificial symbol for a symbolless binary
	op_bfd_symbol const create_artificial_symbol();

        /* Generate symbols using bfd functions for
	 * the image file associated with the ibfd arg.
	 */
	uint process_symtab(bfd_info * bfd, uint start);

	/// filename we open (not including archive path)
	std::string filename;

	/// path to archive
	std::string archive_path;

	/// reference to extra_images
	extra_images const & extra_found_images;

	/// file size in bytes
	off_t file_size;

	/// corresponding debug file name
	mutable std::string debug_filename;

	/// true if at least one section has (flags & SEC_DEBUGGING) != 0
	mutable cached_value<bool> debug_info;

	/// our main bfd object: .bfd may be NULL
	bfd_info ibfd;

	// corresponding debug bfd object, if one is found
	mutable bfd_info dbfd;

	/// sections we will avoid to use symbol from, this is needed
	/// because elf file allows sections with identical vma and we can't
	/// allow overlapping symbols. Such elf layout is used actually by
	/// kernel modules where all code section vma are set to 0.
	std::vector<asection const *> filtered_section;

	typedef std::map<std::string, u32> filepos_map_t;
	// mapping of section names to filepos in the original binary
	filepos_map_t filepos_map;

	bool anon_obj;

	/**
	 * If a runtime binary is prelinked, then its p_vaddr field in the
	 * first PT_LOAD segment will give the address where the binary will
	 * be loaded into memory.  However, the corresponding debuginfo file
	 * may have a different p_vaddr value.  In profile_container::add_samples,
	 * this difference is handled by adding the "base_vma" to the sample
	 * vma.  However, if the runtime binary has no symbol information at all,
	 * then the "base_vma" is obtained from the debuginfo symbol information.
	 * For opreport, this works OK, since under such conditions, ALL symbol
	 * and debug data is then obtained from the debuginfo files, and the sample
	 * vma's should match up fine with the symbol vma's in the debuginfo file.
	 * But when doing 'opannoate --assembly', the real (runtime) image is used for
	 * annotation, and, thus, we may have a mis-match between real image p_vaddr
	 * and the impliled p_vaddr stored with the samples.  To handle this case,
	 * we do the following:  When a bfd_info is created for a debuginfo
	 * file, we set vma_adj to the difference between runtime load address
	 * and the p_vaddr of the first PT_LOAD segment of the debuginfo file, if and
	 * only if the real image has no symbol info; otherwise vma_adj is set to 0.
	 */
        bfd_vma vma_adj;

        /**
         * The file descriptor for an image file that we pass to fdopen_bfd must be kept
         * open through the life of the op_bfd to enable proper behavior of certain
         * BFD functions -- in particular, bfd_find_nearest_line().
         */
        int fd;

};


#endif /* !OP_BFD_H */
