/* -*- c-file-style: "GNU" -*- */
/*
 * Copyright (C) CNRS, INRIA, Université Bordeaux 1, Télécom SudParis
 * See COPYING in top-level directory.
 *
 *
 * opcode.c
 *
 * Determining the size to override (by computing the size of instructions) using the opcode library
 */

#ifndef HAVE_ARM
#error "This file is for x86_64 only"
#endif

#ifndef ENABLE_BINARY_INSTRUMENTATION
#error "This file is for binary instrumentation only"
#endif

#include "opcodes.h"
#include "tracing.h"

#include <bfd.h>
#include <dis-asm.h>
#include <eztrace-core/eztrace_attributes.h>
#include <stdlib.h>

int dummy_print(void* stream MAYBE_UNUSED, const char* fmt MAYBE_UNUSED, ...) {
  return 0;
}

/*
 * This function determines the size of instructions that will be replaced.
 * Output is the size of the replacement
 */
ssize_t opcode_get_overridden_size(bfd* abfd, pid_t child, word_uint symbol,
                                   size_t trampoline_size) {
  size_t max_size = trampoline_size * 2;
  if (max_size < 10)
    max_size = 10;

  unsigned char* code = (unsigned char*)malloc(max_size);
  trace_read(child, symbol, code, max_size);
  struct disassemble_info i;
#if HAVE_BINUTILS_2_38_50_OR_HIGHER
  init_disassemble_info(&i, NULL, dummy_print, NULL);
#else
 init_disassemble_info(&i, NULL, dummy_print);
#endif
  i.arch = bfd_get_arch(abfd);
  i.mach = bfd_get_mach(abfd);
#if HAVE_BINUTILS_2_28_OR_OLDER
  disassembler_ftype disassemble = disassembler(abfd);
#else
  disassembler_ftype disassemble = disassembler(i.arch, bfd_big_endian(abfd), i.mach, abfd);
#endif

  i.buffer = code;
  i.buffer_vma = (unsigned long)i.buffer;
  i.buffer_length = max_size;
  i.disassembler_options = "force-thumb";

  size_t j;
  for (j = 0; j < trampoline_size;) {
    j += disassemble(i.buffer_vma + j, &i);
  }
  free(code);

  return j;
}

ssize_t get_overridden_size(void* bin, pid_t child, word_uint symbol,
                            size_t trampoline_size) {
  return opcode_get_overridden_size((bfd*)bin, child, symbol, trampoline_size);
}
