Asm
/* $Id$ */
/* Copyright (c) 2011-2018 Pierre Pronchery <khorben@defora.org> */
/* This file is part of DeforaOS Devel Asm */
/* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include <System.h>
#include <dirent.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include "Asm/format.h"
#include "format.h"
#include "../config.h"
/* AsmFormat */
/* private */
/* types */
struct _AsmFormat
{
AsmFormatPluginHelper helper;
Plugin * handle;
AsmFormatPluginDefinition * definition;
AsmFormatPlugin * plugin;
/* internal */
/* file */
char const * filename;
FILE * fp;
/* deassembly */
AsmCode * code;
};
/* prototypes */
/* helpers */
static char const * _format_helper_get_filename(AsmFormat * format);
static AsmFunction * _format_helper_get_function_by_id(AsmFormat * format,
AsmFunctionId id);
static void _format_helper_get_functions(AsmFormat * format,
AsmFunction ** functions, size_t * functions_cnt);
static AsmSection * _format_helper_get_section_by_id(AsmFormat * format,
AsmSectionId id);
static AsmString * _format_helper_get_string_by_id(AsmFormat * format,
AsmStringId id);
static AsmFunction * _format_helper_set_function(AsmFormat * format,
AsmFunctionId id, char const * name, off_t offset,
ssize_t size);
static AsmSection * _format_helper_set_section(AsmFormat * format,
AsmSectionId id, unsigned int flags, char const * name,
off_t offset, ssize_t size, off_t base);
static AsmString * _format_helper_set_string(AsmFormat * format, AsmStringId id,
char const * name, off_t offset, ssize_t size);
static int _format_helper_decode(AsmFormat * format, off_t offset, size_t size,
off_t base, AsmArchInstructionCall ** calls, size_t * calls_cnt);
static ssize_t _format_helper_read(AsmFormat * format, void * buf, size_t size);
static off_t _format_helper_seek(AsmFormat * format, off_t offset, int whence);
static ssize_t _format_helper_write(AsmFormat * format, void const * buf,
size_t size);
/* public */
/* functions */
#ifndef STANDALONE
/* format_new */
AsmFormat * format_new(char const * format)
{
AsmFormat * f;
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s(\"%s\")\n", __func__, format);
#endif
if(format == NULL)
{
error_set_code(1, "%s", strerror(EINVAL));
return NULL;
}
if((f = object_new(sizeof(*f))) == NULL)
return NULL;
if((f->handle = plugin_new(LIBDIR, PACKAGE, "format", format)) == NULL
|| (f->definition = plugin_lookup(f->handle,
"format_plugin")) == NULL)
{
if(f->handle != NULL)
plugin_delete(f->handle);
object_delete(f);
return NULL;
}
f->plugin = NULL;
memset(&f->helper, 0, sizeof(f->helper));
f->helper.format = f;
f->helper.decode = _format_helper_decode;
f->helper.get_filename = _format_helper_get_filename;
f->helper.get_function_by_id = _format_helper_get_function_by_id;
f->helper.get_functions = _format_helper_get_functions;
f->helper.get_section_by_id = _format_helper_get_section_by_id;
f->helper.get_string_by_id = _format_helper_get_string_by_id;
f->helper.set_function = _format_helper_set_function;
f->helper.set_section = _format_helper_set_section;
f->helper.set_string = _format_helper_set_string;
f->helper.read = _format_helper_read;
f->helper.seek = _format_helper_seek;
f->helper.write = _format_helper_write;
return f;
}
#endif
#ifndef STANDALONE
/* format_new_match */
AsmFormat * format_new_match(char const * filename, FILE * fp)
{
char const path[] = LIBDIR "/" PACKAGE "/format";
DIR * dir;
struct dirent * de;
size_t len;
#if defined(__APPLE__)
char const ext[] = ".dylib";
#elif defined(__WIN32__)
char const ext[] = ".dll";
#else
char const ext[] = ".so";
#endif
AsmFormat * flat = NULL;
AsmFormat * format = NULL;
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s(\"%s\")\n", __func__, filename);
#endif
if((dir = opendir(path)) == NULL)
{
error_set_code(-errno, "%s: %s", path, strerror(errno));
return NULL;
}
while((de = readdir(dir)) != NULL)
{
if((len = strlen(de->d_name)) < sizeof(ext))
continue;
if(strcmp(&de->d_name[len - sizeof(ext) + 1], ext) != 0)
continue;
de->d_name[len - sizeof(ext) + 1] = '\0';
if((format = format_new(de->d_name)) == NULL)
continue;
if(format_init(format, NULL, filename, fp) == 0
&& format_match(format) == 1)
break;
if(strcmp(de->d_name, "flat") == 0)
flat = format;
else
format_delete(format);
format = NULL;
}
closedir(dir);
/* fallback on the "flat" format plug-in if necessary and available */
if(format == NULL && flat != NULL)
return flat;
if(flat != NULL)
format_delete(flat);
return format;
}
#endif
/* format_delete */
void format_delete(AsmFormat * format)
{
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s()\n", __func__);
#endif
/* XXX may fail */
format_exit(format);
if(format->handle != NULL)
plugin_delete(format->handle);
object_delete(format);
}
/* accessors */
/* format_can_decode */
int format_can_decode(AsmFormat * format)
{
return format->definition->decode != NULL
/* && format->plugin->decode_section != NULL */;
}
/* format_get_arch */
char const * format_get_arch(AsmFormat * format)
{
if(format->definition->detect == NULL)
return NULL;
return format->definition->detect(format->plugin);
}
/* format_get_description */
char const * format_get_description(AsmFormat * format)
{
return format->definition->description;
}
/* format_get_name */
char const * format_get_name(AsmFormat * format)
{
return format->definition->name;
}
/* useful */
/* format_decode */
int format_decode(AsmFormat * format, AsmCode * code, int raw)
{
int ret;
if(format->definition->decode == NULL)
return -error_set_code(1, "%s: %s", format_get_name(format),
"Disassembly is not supported");
format->code = code;
ret = format->definition->decode(format->plugin, raw);
format->code = NULL;
return ret;
}
/* format_decode_section */
int format_decode_section(AsmFormat * format, AsmCode * code, AsmSection * section,
AsmArchInstructionCall ** calls, size_t * calls_cnt)
{
int ret;
if(format->definition->decode_section == NULL)
return -error_set_code(1, "%s: %s", format_get_name(format),
"Disassembly is not supported");
if(section == NULL || section->id < 0)
return -error_set_code(1, "%s: %s", format_get_name(format),
"Invalid argument");
format->code = code;
ret = format->definition->decode_section(format->plugin, section, calls,
calls_cnt);
format->code = NULL;
return ret;
}
/* format_detect_arch */
char const * format_detect_arch(AsmFormat * format)
{
if(format->definition->detect == NULL)
{
error_set_code(1, "%s: %s", format->definition->name,
"Unable to detect the architecture");
return NULL;
}
return format->definition->detect(format->plugin);
}
/* format_directive */
int format_directive(AsmFormat * format, char const * name,
char const ** args, size_t args_cnt)
{
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s(\"%s\")\n", __func__, name);
#endif
if(format->plugin == NULL || format->definition == NULL)
return -error_set_code(1, "%s", "Plug-in not loaded");
if(format->definition->directive == NULL)
return -error_set_code(1, "%s: %s", format->definition->name,
"No support for directives");
return format->definition->directive(format->plugin, name, args,
args_cnt);
}
/* format_exit */
int format_exit(AsmFormat * format)
{
int ret = 0;
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s()\n", __func__);
#endif
if(format->plugin != NULL && format->definition->destroy != NULL)
ret = format->definition->destroy(format->plugin);
format->plugin = NULL;
format->fp = NULL;
format->filename = NULL;
return ret;
}
/* format_function */
int format_function(AsmFormat * format, char const * name)
{
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s(\"%s\")\n", __func__, name);
#endif
if(format->plugin == NULL || format->definition == NULL)
return -error_set_code(1, "%s", "Plug-in not loaded");
if(format->definition->function == NULL)
return -error_set_code(1, "%s: %s", format->definition->name,
"No support for functions");
return format->definition->function(format->plugin, name);
}
/* format_guess_arch */
char const * format_guess_arch(AsmFormat * format, char const * hint)
{
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s(\"%s\")\n", __func__, hint);
#endif
if(format->definition->guess == NULL)
return NULL;
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s() => \"%s\"\n", __func__,
format->definition->guess(format->plugin, hint));
#endif
return format->definition->guess(format->plugin, hint);
}
/* format_init */
int format_init(AsmFormat * format, char const * arch, char const * filename,
FILE * fp)
{
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s(\"%s\", %p)\n", __func__, filename,
(void *)fp);
#endif
if(format->plugin != NULL)
format_exit(format);
format->filename = filename;
format->fp = fp;
if(format->definition->init != NULL
&& (format->plugin = format->definition->init(
&format->helper, arch)) == NULL)
return -1;
return 0;
}
/* format_match */
int format_match(AsmFormat * format)
{
int ret = 0;
char const * s = format->definition->signature;
ssize_t s_len = format->definition->signature_len;
char * buf = NULL;
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s()\n", __func__);
#endif
if(s_len > 0)
if((buf = malloc(s_len)) == NULL)
ret = error_set_code(-errno, "%s", strerror(errno));
if(buf != NULL)
{
if(_format_helper_seek(format, 0, SEEK_SET) != 0)
ret = -1;
else if(_format_helper_read(format, buf, s_len) != s_len)
ret = -1;
else if(memcmp(buf, s, s_len) == 0)
ret = 1;
free(buf);
}
return ret;
}
/* format_section */
int format_section(AsmFormat * format, char const * name)
{
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s(\"%s\")\n", __func__, name);
#endif
if(format->plugin == NULL || format->definition == NULL)
return -error_set_code(1, "%s", "Plug-in not loaded");
if(format->definition->section == NULL)
return -error_set_code(1, "%s: %s", format->definition->name,
"No support for sections");
return format->definition->section(format->plugin, name);
}
/* private */
/* functions */
/* helpers */
/* format_helper_get_filename */
static char const * _format_helper_get_filename(AsmFormat * format)
{
return format->filename;
}
/* format_helper_get_function_by_id */
static AsmFunction * _format_helper_get_function_by_id(AsmFormat * format,
AsmFunctionId id)
{
return asmcode_get_function_by_id(format->code, id);
}
/* format_helper_get_functions */
static void _format_helper_get_functions(AsmFormat * format,
AsmFunction ** functions, size_t * functions_cnt)
{
asmcode_get_functions(format->code, functions, functions_cnt);
}
/* format_helper_get_section_by_id */
static AsmSection * _format_helper_get_section_by_id(AsmFormat * format,
AsmSectionId id)
{
return asmcode_get_section_by_id(format->code, id);
}
/* format_helper_get_string_by_id */
static AsmString * _format_helper_get_string_by_id(AsmFormat * format,
AsmStringId id)
{
return asmcode_get_string_by_id(format->code, id);
}
/* format_helper_set_function */
static AsmFunction * _format_helper_set_function(AsmFormat * format,
AsmFunctionId id, char const * name, off_t offset, ssize_t size)
{
return asmcode_set_function(format->code, id, name, offset, size);
}
/* format_helper_set_section */
static AsmSection * _format_helper_set_section(AsmFormat * format,
AsmSectionId id, unsigned int flags, char const * name,
off_t offset, ssize_t size, off_t base)
{
return asmcode_set_section(format->code, id, flags,
name, offset, size, base);
}
/* format_helper_set_string */
static AsmString * _format_helper_set_string(AsmFormat * format, AsmStringId id,
char const * name, off_t offset, ssize_t size)
{
return asmcode_set_string(format->code, id, name, offset, size);
}
/* format_helper_decode */
static int _format_helper_decode(AsmFormat * format, off_t offset, size_t size,
off_t base, AsmArchInstructionCall ** calls, size_t * calls_cnt)
{
int ret;
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s(0x%lx, 0x%lx, 0x%lx)\n", __func__, offset,
size, base);
#endif
if((ret = asmcode_decode_at(format->code, offset, size, base,
calls, calls_cnt)) != 0)
error_print("deasm");
return ret;
}
/* format_helper_read */
static ssize_t _format_helper_read(AsmFormat * format, void * buf, size_t size)
{
if(fread(buf, size, 1, format->fp) == 1)
return size;
if(ferror(format->fp))
return error_set_code(-errno, "%s: %s", format->filename,
strerror(errno));
if(feof(format->fp))
return -error_set_code(1, "%s: %s", format->filename,
"End of file reached");
return -error_set_code(1, "%s: %s", format->filename, "Read error");
}
/* format_helper_seek */
static off_t _format_helper_seek(AsmFormat * format, off_t offset, int whence)
{
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s(%zd, %d)\n", __func__, offset, whence);
#endif
if(whence == SEEK_SET)
{
if(fseeko(format->fp, offset, whence) == 0)
return offset;
}
else if(whence == SEEK_CUR || whence == SEEK_END)
{
if(fseeko(format->fp, offset, whence) == 0)
return ftello(format->fp);
}
else
return -error_set_code(1, "%s: %s", format->filename,
"Invalid argument for seeking");
return error_set_code(-errno, "%s: %s", format->filename,
strerror(errno));
}
/* format_helper_write */
static ssize_t _format_helper_write(AsmFormat * format, void const * buf,
size_t size)
{
if(fwrite(buf, size, 1, format->fp) == 1)
return size;
if(ferror(format->fp))
return error_set_code(-errno, "%s: %s", format->filename,
strerror(errno));
if(feof(format->fp))
return -error_set_code(1, "%s: %s", format->filename,
"End of file reached");
return -error_set_code(1, "%s: %s", format->filename, "Write error");
}