/**
 * @file create_bfd.c
 * Routine to handle elf file creation
 *
 * @remark Copyright 2007 OProfile authors
 * @remark Read the file COPYING
 *
 * @author Jens Wilke
 * @Modifications Maynard Johnson
 * @Modifications Philippe Elie
 * @Modifications Daniel Hansel
 *
 * Copyright IBM Corporation 2007
 *
 */

#include "opjitconv.h"
#include "opd_printf.h"
#include "op_libiberty.h"

#include <bfd.h>
#include <stdint.h>
#include <stdio.h>

/* Create the symbols and fill the syms array for all functions
 * from start_idx to end_idx pointing into entries_address_ascending array */
static int fill_symtab(void)
{
	int rc = OP_JIT_CONV_OK;
	u32 i;
	int r;
	struct jitentry const * e;
	asymbol * s;
	asection * section = NULL;

	/* Check for valid value of entry_count to avoid integer overflow. */
	if (entry_count > UINT32_MAX - 1) {
		bfd_perror("invalid entry_count value");
		rc = OP_JIT_CONV_FAIL;
		goto out;
	}
	
	syms = xmalloc(sizeof(asymbol *) * (entry_count+1));
	syms[entry_count] = NULL;
	for (i = 0; i < entry_count; i++) {
		e = entries_address_ascending[i];
		if (e->section)
			section = e->section;
		s = bfd_make_empty_symbol(cur_bfd);
		if (!s) {
			bfd_perror("bfd_make_empty_symbol");
			rc = OP_JIT_CONV_FAIL;
			goto out;
		}
		s->name = e->symbol_name;
		s->section = section;
		s->flags = BSF_GLOBAL | BSF_FUNCTION;
		s->value = e->vma - section->vma;
		verbprintf(debug,"add sym: name=%s, value=%llx\n", s->name,
			   (unsigned long long)s->value);
		syms[i] = s;
	}
	r = bfd_set_symtab(cur_bfd, syms, entry_count);
	if (r == FALSE) {
		bfd_perror("bfd_set_symtab");
		rc = OP_JIT_CONV_FAIL;
	}
out:
	return rc;
}

/*
 * create a new section.
 */
asection * create_section(bfd * abfd, char const * section_name,
			  size_t size, bfd_vma vma, flagword flags)
{
	asection * section;

	verbprintf(debug, "create_section() %s\n", section_name);
	section = bfd_make_section(abfd, section_name);
	if (section == NULL)  {
		bfd_perror("bfd_make_section");
		goto error;
	}
	if (bfd_set_section_vma(abfd, section, vma) == FALSE) {
		bfd_perror("bfd_set_section_vma");
		goto error;
	}
	if (bfd_set_section_size(abfd, section, size) == FALSE) {
		bfd_perror("bfd_set_section_size");
		goto error;
	}
	if (bfd_set_section_flags(abfd, section, flags) == FALSE) {
		bfd_perror("bfd_set_section_flags");
		goto error;
	}
	return section;
error:
	return NULL;
}


/* create a .text section. end_idx: index last jitentry (inclusive!)  */
static int create_text_section(int start_idx, int end_idx)
{
	int rc = OP_JIT_CONV_OK;

	asection * section;
	char const * section_name;
	int idx = start_idx;
	unsigned long long vma_start =
		entries_address_ascending[start_idx]->vma;
	struct jitentry * ee = entries_address_ascending[end_idx];
	unsigned long long vma_end = ee->vma + ee->code_size;
	int size = vma_end - vma_start;

	section_name = bfd_get_unique_section_name(cur_bfd, ".text", &idx);
	verbprintf(debug, "section idx=%i, name=%s, vma_start=%llx, size=%i\n",
		   idx, section_name, vma_start, size);

	section = create_section(cur_bfd, section_name, size, vma_start,
               SEC_ALLOC|SEC_LOAD|SEC_READONLY|SEC_CODE|SEC_HAS_CONTENTS);
	if (section)
		entries_address_ascending[start_idx]->section = section;
	else
		rc = OP_JIT_CONV_FAIL;

	return rc;
}

/* fill a section contents at a given offset from the start of the section */
int fill_section_content(bfd * abfd, asection * section,
			 void const * b, file_ptr offset, size_t sz)
{
	if (bfd_set_section_contents(abfd, section, b, offset, sz) == FALSE) {
		bfd_perror("bfd_set_section_contents");
		return OP_JIT_CONV_FAIL;
	}
	return OP_JIT_CONV_OK;
}

/*
 * Copy all code of the functions that are within start_idx and end_idx to 
 * the section.
 */
static int fill_text_section_content(asection * section, int start_idx,
				     int end_idx)
{
	int rc = OP_JIT_CONV_OK;
	unsigned long long vma_start =
		entries_address_ascending[start_idx]->vma;
	struct jitentry const * e;
	int i;

	for (i = start_idx; i <= end_idx; i++) {
		e = entries_address_ascending[i];
		verbprintf(debug, "section = %s, i = %i, code = %llx,"
			   " vma = %llx, offset = %llx,"
			   "size = %i, name = %s\n",
			   section->name, i,
			   (unsigned long long) (uintptr_t) e->code,
			   e->vma, e->vma - vma_start,
			   e->code_size, e->symbol_name);
		/* the right part that is created by split_entry may 
		 * have no code; also, the agent may have passed NULL
		 * for the code location.
		 */
		if (e->code) {
			rc = fill_section_content(cur_bfd, section,
				e->code, (file_ptr) (e->vma - vma_start),
				(bfd_size_type)e->code_size);
			if (rc != OP_JIT_CONV_OK)
				break;
		}
	}
	return rc;
}


/* Walk over the symbols sorted by address and create ELF sections. Whenever we
 * have a gap greater or equal to 4096 make a new section.
 */
int partition_sections(void)
{
	int rc = OP_JIT_CONV_OK;
	u32 i, j;
	struct jitentry const * pred;
	struct jitentry const * entry;
	unsigned long long end_addr;

	// i: start index of the section
	i = 0;
	for (j = 1; j < entry_count; j++) {
		entry = entries_address_ascending[j];
		pred = entries_address_ascending[j - 1];
		end_addr = pred->vma + pred->code_size;
		// calculate gap between code, if it is more than one page
		// create an additional section
		if ((entry->vma - end_addr) >= 4096) {
			rc = create_text_section(i, j - 1);
			if (rc == OP_JIT_CONV_FAIL)
				goto out;
			i = j;
		}
	}
	// this holds always if we have at least one jitentry
	if (i < entry_count)
		rc = create_text_section(i, entry_count - 1);
out:
	return rc;
}


/* Fill the code content into the sections created by partition_sections() */
int fill_sections(void)
{
	int rc = OP_JIT_CONV_OK;
	u32 i, j;
	asection * section;

	rc = fill_symtab();
	if (rc == OP_JIT_CONV_FAIL)
		goto out;

	verbprintf(debug, "opjitconv: fill_sections\n");
	i = 0;
	for (j = 1; j < entry_count; j++) {
		if (entries_address_ascending[j]->section) {
			section = entries_address_ascending[i]->section;
			rc = fill_text_section_content(section, i,
						       j - 1);
			if (rc == OP_JIT_CONV_FAIL)
				goto out;
			i = j;
		}
	}
	// this holds always if we have at least one jitentry
	if (i < entry_count) {
		section = entries_address_ascending[i]->section;
		rc = fill_text_section_content(section,
					       i, entry_count - 1);
	}
out:
	return rc;
}


/* create the elf file */
bfd * open_elf(char const * filename)
{
	bfd * abfd;

	abfd = bfd_openw(filename, dump_bfd_target_name);
	if (!abfd) {
		bfd_perror("bfd_openw");
		goto error1;
	}
	if (bfd_set_format(abfd, bfd_object) == FALSE) {
		bfd_perror("bfd_set_format");
		goto error;
	}
	if (bfd_set_arch_mach(abfd, dump_bfd_arch, dump_bfd_mach) == FALSE) {
		bfd_perror("bfd_set_format");
		goto error;
	}
	return abfd;
error:
	bfd_close(abfd);
error1:
	return NULL;
}