blob: 5724f5cece9cacf53b8e48ecdf5a07e934586a35 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @author Evgueni Brevnov
*/
#include <bfd.h>
#include <dis-asm.h>
#include <stdarg.h>
#include <string.h>
#include <assert.h>
#include <stdlib.h>
#include "port_disasm.h"
#include <apr_dso.h>
#include <apr_strings.h>
#include <apr_portable.h>
// this is mostly imperical data
#if defined(_IA32_)
#define ADDR_SIZE 16
#define MNEMONIC_SIZE 15
#define BYTES_PER_LINE 2
#define BYTES_TOTAL 100
//BYTES_SIZE = BYTES_PER_LINE * 3
#define BYTES_SIZE 6
#elif defined(_EM64T_)
#define ADDR_SIZE 24
#define MNEMONIC_SIZE 20
#define BYTES_PER_LINE 2
#define BYTES_TOTAL 100
#define BYTES_SIZE 6
#elif defined(_IPF_)
#define ADDR_SIZE 24
#define MNEMONIC_SIZE 30
#define BYTES_PER_LINE 6
#define BYTES_TOTAL 150
#define BYTES_SIZE 18
#else
#define ADDR_SIZE 0
#define MNEMONIC_SIZE 0
#define BYTES_PER_LINE 0
#define BYTES_TOTAL 0
#define BYTES_SIZE 0
#endif
typedef void (* init_disassemble_info_t)(struct disassemble_info *info,
void *stream,
fprintf_ftype fprintf_func);
typedef void (* disassemble_init_for_target_t)(struct disassemble_info * info);
/* Private Interface */
struct port_disassembler_t {
struct disassemble_info bfd_info;
port_disasm_info_t port_info;
int line_size;
char * real_stream;
apr_pool_t * user_pool;
apr_file_t * user_file;
apr_size_t num_bytes_total;
apr_size_t num_bytes_used;
};
static apr_pool_t * disasm_pool = NULL;
static disassembler_ftype bfd_decoder = NULL;
static init_disassemble_info_t bfd_init_info = NULL;
static disassemble_init_for_target_t bfd_init_target = NULL;
#if defined(_IA32_) || defined(_EM64T_) || defined(_IPF_)
/* Memory reading routines */
static int disasm_read_memory(bfd_vma src,
bfd_byte * buffer,
unsigned int n,
struct disassemble_info * info) {
#if defined(_IA32_)
memcpy(buffer, (void *)(apr_uint32_t)src, n);
#elif defined(_EM64T_) || defined(_IPF_)
memcpy(buffer, (void *)src, n);
#endif
return 0;
}
/* Address printing routines */
static void disasm_print_adress_default(bfd_vma memaddr, struct disassemble_info * info) {
#if defined(_IA32_)
info->fprintf_func(info->stream, "0x%08X", (apr_int32_t)memaddr);
#elif defined(_EM64T_) || defined(_IPF_)
apr_uint64_t addr = (apr_uint64_t)memaddr;
apr_uint32_t hi = (apr_uint32_t)((addr >> 32) & UINT_MAX);
apr_uint32_t lo = (apr_uint32_t)(addr & UINT_MAX);
info->fprintf_func(info->stream, "0x%08x%08x", hi, lo);
#endif
}
/* General printing routines */
static int disasm_sprint_default(void * stream, const char * fmt, ...) {
va_list args;
va_start(args, fmt);
port_disassembler_t * disassembler = (port_disassembler_t *)stream;
int required_length = apr_vsnprintf(NULL, 0, fmt, args);
// insure space
while (required_length >= disassembler->num_bytes_total -
disassembler->num_bytes_used) {
void * buf = malloc(disassembler->num_bytes_used);
memcpy(buf, disassembler->real_stream, disassembler->num_bytes_used);
apr_pool_clear(disassembler->user_pool);
disassembler->num_bytes_total *= 2;
disassembler->real_stream = apr_palloc(disassembler->user_pool,
disassembler->num_bytes_total);
memcpy(disassembler->real_stream, buf, disassembler->num_bytes_used);
free(buf);
}
apr_vsnprintf(disassembler->real_stream + disassembler->num_bytes_used,
required_length + 1, fmt, args);
disassembler->num_bytes_used += required_length;
return required_length;
}
static int disasm_fprint_default(void * stream, const char * fmt, ...) {
port_disassembler_t * disassembler = (port_disassembler_t *)stream;
va_list args;
va_start(args, fmt);
int required_length = apr_vsnprintf(NULL, 0, fmt, args);
va_end(args);
// insure space
if (required_length >= disassembler->num_bytes_total -
disassembler->num_bytes_used) {
apr_file_write(disassembler->user_file, disassembler->real_stream,
&disassembler->num_bytes_used);
disassembler->num_bytes_used = 0;
}
while (required_length >= disassembler->num_bytes_total -
disassembler->num_bytes_used) {
apr_pool_clear(disassembler->user_pool);
disassembler->num_bytes_total *= 2;
disassembler->real_stream = apr_palloc(disassembler->user_pool,
disassembler->num_bytes_total);
}
va_start(args, fmt);
apr_vsnprintf(disassembler->real_stream + disassembler->num_bytes_used,
required_length + 1, fmt, args);
va_end(args);
disassembler->num_bytes_used += required_length;
return 0;
}
static void disasm_print(port_disassembler_t * disassembler,
const char * code,
apr_int64_t len) {
// iterate over the code buffer
while (len > 0) {
int bytes_read = 1;
#if defined(_IA32_)
// print instruction address
if (disassembler->port_info.print_addr) {
disassembler->bfd_info.print_address_func((bfd_vma)(apr_uint32_t)code,
&disassembler->bfd_info);
disassembler->bfd_info.fprintf_func(disassembler, "\t");
}
// print mnemonic
if (disassembler->port_info.print_mnemonic) {
bytes_read = bfd_decoder((bfd_vma)(apr_uint32_t)code, &disassembler->bfd_info);
}
#elif defined(_EM64T_) || defined(_IPF_)
// print instruction address
if (disassembler->port_info.print_addr) {
disassembler->bfd_info.print_address_func((bfd_vma)code,
&disassembler->bfd_info);
disassembler->bfd_info.fprintf_func(disassembler, "\t");
}
// print mnemonic
if (disassembler->port_info.print_mnemonic) {
bytes_read = bfd_decoder((bfd_vma)code, &disassembler->bfd_info);
}
#endif
// print native bytes
if (disassembler->port_info.print_bytes) {
disassembler->bfd_info.fprintf_func(disassembler, "\t");
int i;
for (i = 0; i < bytes_read; i++) {
disassembler->bfd_info.fprintf_func(disassembler,
"%02X ", ((int)*(code + i)) & 0xff);
}
}
disassembler->bfd_info.fprintf_func(disassembler, "\n");
code += bytes_read;
len -= bytes_read;
#ifndef NDEBUG
if (len < 0) {
fprintf(stderr, "WARNING: Disassembler read %i byte(s) more "
"than specified buffer length\n", (apr_int32_t)-len);
}
#endif
}
}
#endif // defined(_IA32_) || defined(_EM64T_) || defined(_IPF_)
/* Public Interface */
APR_DECLARE(apr_status_t) port_disasm_initialize() {
#if defined(_IA32_) || defined(_EM64T_) || defined(_IPF_)
static apr_status_t stat = APR_EINIT;
const char * BFD_LIB_NAME = "libbfd.so";
const char * OPCODES_LIB_NAME = "libopcodes.so";
const char * BFD_INIT_INFO_NAME = "init_disassemble_info";
const char * BFD_INIT_TARGET_NAME = "disassemble_init_for_target";
#if defined (_IA32_) || defined(_EM64T_)
const char * BFD_PRINT_INSN_NAME = "print_insn_i386_intel";
#elif defined(_IPF_)
const char * BFD_PRINT_INSN_NAME = "print_insn_ia64";
apr_dso_handle_t * nnn_lib_handle = NULL;
#endif
apr_dso_handle_t * bfd_lib_handle = NULL;
apr_dso_handle_t * opcodes_lib_handle = NULL;
apr_dso_handle_sym_t bfd_init_info_sym;
apr_dso_handle_sym_t bfd_init_target_sym;
apr_dso_handle_sym_t bfd_print_insn_sym;
if (stat == APR_SUCCESS) {
return APR_SUCCESS;
}
if ((stat = apr_pool_create(&disasm_pool, NULL)) != APR_SUCCESS) {
return stat;
}
if ((stat = apr_dso_load(&bfd_lib_handle, BFD_LIB_NAME, disasm_pool))
!= APR_SUCCESS) {
apr_pool_destroy(disasm_pool);
disasm_pool = NULL;
return stat;
}
#if defined(_IPF_)
if ((stat = apr_dso_load(&nnn_lib_handle, "libgettextlib-0.14.1.so", disasm_pool))
!= APR_SUCCESS) {
apr_pool_destroy(disasm_pool);
disasm_pool = NULL;
return stat;
}
#endif
if ((stat = apr_dso_load(&opcodes_lib_handle, OPCODES_LIB_NAME, disasm_pool))
!= APR_SUCCESS) {
apr_pool_destroy(disasm_pool);
disasm_pool = NULL;
return stat;
}
if ((stat = apr_dso_sym(&bfd_init_info_sym,
opcodes_lib_handle, BFD_INIT_INFO_NAME)) != APR_SUCCESS) {
// library handle will be closed automatically
apr_pool_destroy(disasm_pool);
disasm_pool = NULL;
return stat;
}
if ((stat = apr_dso_sym(&bfd_init_target_sym,
opcodes_lib_handle, BFD_INIT_TARGET_NAME)) != APR_SUCCESS) {
// library handle will be closed automatically
apr_pool_destroy(disasm_pool);
disasm_pool = NULL;
return stat;
}
if ((stat = apr_dso_sym(&bfd_print_insn_sym,
opcodes_lib_handle, BFD_PRINT_INSN_NAME)) != APR_SUCCESS) {
// library handle will be closed automatically
apr_pool_destroy(disasm_pool);
disasm_pool = NULL;
return stat;
}
// initialize globals
bfd_decoder = (disassembler_ftype)bfd_print_insn_sym;
bfd_init_info = (init_disassemble_info_t)bfd_init_info_sym;
bfd_init_target = (disassemble_init_for_target_t)bfd_init_target_sym;
return stat = APR_SUCCESS;
#else
return APR_ENOTIMPL;
#endif
}
APR_DECLARE(apr_status_t) port_disassembler_create(port_disassembler_t ** disassembler,
apr_pool_t * pool) {
#if defined(_IA32_) || defined(_EM64T_) || defined(_IPF_)
apr_status_t status;
if ((status = port_disasm_initialize()) != APR_SUCCESS) {
return status;
}
*disassembler = (port_disassembler_t *)
apr_palloc(pool, sizeof(port_disassembler_t));
port_disasm_info_t info = {1, 1, 1};
// initialize port_info
port_disasm_set_info(*disassembler, info, NULL);
// initialize bfd_info
bfd_init_info(&(*disassembler)->bfd_info, *disassembler, NULL);
(*disassembler)->bfd_info.read_memory_func = disasm_read_memory;
(*disassembler)->bfd_info.print_address_func = disasm_print_adress_default;
#if defined(_IA32_)
(*disassembler)->bfd_info.arch = bfd_arch_i386;
(*disassembler)->bfd_info.mach = bfd_mach_i386_i386_intel_syntax;
#elif defined(_EM64T_)
(*disassembler)->bfd_info.arch = bfd_arch_i386;
(*disassembler)->bfd_info.mach = bfd_mach_x86_64_intel_syntax;
#elif defined(_IPF_)
(*disassembler)->bfd_info.arch = bfd_arch_ia64;
(*disassembler)->bfd_info.mach = bfd_mach_ia64_elf64;
#endif
(*disassembler)->bfd_info.endian = BFD_ENDIAN_LITTLE;
(*disassembler)->bfd_info.display_endian = BFD_ENDIAN_LITTLE;
bfd_init_target(&(*disassembler)->bfd_info);
// initialize the rest fields
(*disassembler)->real_stream = NULL;
(*disassembler)->user_pool = pool;
(*disassembler)->user_file = NULL;
(*disassembler)->num_bytes_total = 0;
(*disassembler)->num_bytes_used = 0;
return APR_SUCCESS;
#else
return APR_ENOTIMPL;
#endif
}
APR_DECLARE(apr_status_t) port_disasm_set_info(port_disassembler_t * disassembler,
const port_disasm_info_t new_info,
port_disasm_info_t * old_info) {
#if defined(_IA32_) || defined(_EM64T_) || defined(_IPF_)
if (old_info != NULL) {
*old_info = disassembler->port_info;
}
disassembler->port_info = new_info;
disassembler->line_size = 0;
if (disassembler->port_info.print_addr) {
disassembler->line_size += ADDR_SIZE;
}
if (disassembler->port_info.print_mnemonic) {
disassembler->line_size += MNEMONIC_SIZE;
}
if (disassembler->port_info.print_bytes) {
disassembler->line_size += BYTES_SIZE;
}
return APR_SUCCESS;
#else
return APR_ENOTIMPL;
#endif
}
APR_DECLARE(apr_status_t) port_disasm(port_disassembler_t * disassembler,
const char * code,
unsigned int len,
char ** disasm_code) {
#if defined(_IA32_) || defined(_EM64T_) || defined(_IPF_)
// check if nothing should be printed
if (disassembler->line_size == 0) {
*disasm_code = NULL;
return APR_SUCCESS;
}
if (disassembler->num_bytes_total == 0) {
// Calculate required number of bytes
disassembler->num_bytes_total = disassembler->line_size * len / BYTES_PER_LINE;
// initialize stream
disassembler->real_stream = apr_palloc(disassembler->user_pool,
disassembler->num_bytes_total);
}
strcpy(disassembler->real_stream, "");
disassembler->bfd_info.fprintf_func = disasm_sprint_default;
disasm_print(disassembler, code, len);
*disasm_code = disassembler->real_stream;
disassembler->real_stream = NULL;
disassembler->num_bytes_total = 0;
disassembler->num_bytes_used = 0;
return APR_SUCCESS;
#else
return APR_ENOTIMPL;
#endif
}
APR_DECLARE(apr_status_t) port_disasm_to_file(port_disassembler_t * disassembler,
const char * code,
unsigned int len,
apr_file_t * thefile) {
#if defined(_IA32_) || defined(_EM64T_) || defined(_IPF_)
// check if nothing should be printed
if (disassembler->line_size == 0) {
return APR_SUCCESS;
}
if (disassembler->num_bytes_total == 0) {
// Calculate required number of bytes
disassembler->num_bytes_total = disassembler->line_size * BYTES_TOTAL / BYTES_PER_LINE;
// initialize stream
disassembler->real_stream = apr_palloc(disassembler->user_pool,
disassembler->num_bytes_total);
}
// initialize file
disassembler->user_file = thefile;
strcpy(disassembler->real_stream, "");
disassembler->bfd_info.fprintf_func = disasm_fprint_default;
disasm_print(disassembler, code, len);
// flush
apr_file_write(disassembler->user_file, disassembler->real_stream,
&disassembler->num_bytes_used);
apr_file_flush(disassembler->user_file);
disassembler->user_file = NULL;
disassembler->num_bytes_used = 0;
return APR_SUCCESS;
#else
return APR_ENOTIMPL;
#endif
}