Asm

/* $Id$ */
/* Copyright (c) 2011-2022 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 <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include "Asm/arch.h"
#include "Asm/asm.h"
#include "arch.h"
#include "../config.h"
/* macros */
#ifndef abs
# define abs(a) ((a) >= 0 ? (a) : -(a))
#endif
#ifndef min
# define min(a, b) ((a) < (b) ? (a) : (b))
#endif
/* AsmArch */
/* private */
/* types */
struct _AsmArch
{
AsmArchPluginHelper helper;
Plugin * handle;
AsmArchPluginDefinition * definition;
AsmArchPlugin * plugin;
size_t instructions_cnt;
size_t prefixes_cnt;
size_t registers_cnt;
/* internal */
AsmCode * code;
off_t base;
char const * filename;
FILE * fp;
char const * buffer;
size_t buffer_cnt;
size_t buffer_pos;
};
/* prototypes */
/* callbacks */
static char const * _arch_get_filename(AsmArch * arch);
static AsmFunction * _arch_get_function_by_id(AsmArch * arch, AsmFunctionId id);
static AsmString * _arch_get_string_by_id(AsmArch * arch, AsmStringId id);
static ssize_t _arch_peek(AsmArch * arch, void * buf, size_t size);
static ssize_t _arch_read(AsmArch * arch, void * buf, size_t size);
static ssize_t _arch_peek_buffer(AsmArch * arch, void * buf, size_t size);
static ssize_t _arch_read_buffer(AsmArch * arch, void * buf, size_t size);
static off_t _arch_seek(AsmArch * arch, off_t offset, int whence);
static off_t _arch_seek_buffer(AsmArch * arch, off_t offset, int whence);
static ssize_t _arch_write(AsmArch * arch, void const * buf, size_t size);
/* public */
/* functions */
#ifndef STANDALONE
/* arch_new */
AsmArch * arch_new(char const * name)
{
AsmArch * a;
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s(\"%s\")\n", __func__, name);
#endif
if((a = object_new(sizeof(*a))) == NULL)
return NULL;
memset(&a->helper, 0, sizeof(a->helper));
if((a->handle = plugin_new(LIBDIR, PACKAGE, "arch", name)) == NULL
|| (a->definition = plugin_lookup(a->handle,
"arch_plugin")) == NULL)
{
if(a->handle != NULL)
plugin_delete(a->handle);
object_delete(a);
return NULL;
}
a->plugin = NULL;
#if 1 /* XXX should be done when initializing */
a->instructions_cnt = 0;
if(a->definition->instructions != NULL)
for(; a->definition->instructions[a->instructions_cnt].name
!= NULL; a->instructions_cnt++);
a->prefixes_cnt = 0;
if(a->definition->prefixes != NULL)
for(; a->definition->prefixes[a->prefixes_cnt].name != NULL;
a->prefixes_cnt++);
a->registers_cnt = 0;
if(a->definition->registers != NULL)
for(; a->definition->registers[a->registers_cnt].name != NULL;
a->registers_cnt++);
#endif
a->filename = NULL;
a->fp = NULL;
a->buffer = NULL;
a->buffer_cnt = 0;
a->buffer_pos = 0;
return a;
}
#endif
/* arch_delete */
void arch_delete(AsmArch * arch)
{
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s()\n", __func__);
#endif
if(arch->handle != NULL)
plugin_delete(arch->handle);
object_delete(arch);
}
/* accessors */
/* arch_can_decode */
int arch_can_decode(AsmArch * arch)
{
return (arch->definition->decode != NULL) ? 1 : 0;
}
/* arch_get_definition */
AsmArchDefinition const * arch_get_definition(AsmArch * arch)
{
return arch->definition->definition;
}
/* arch_get_description */
char const * arch_get_description(AsmArch * arch)
{
return arch->definition->description;
}
/* arch_get_format */
char const * arch_get_format(AsmArch * arch)
{
if(arch->definition->definition != NULL
&& arch->definition->definition->format != NULL)
return arch->definition->definition->format;
return NULL;
}
/* arch_get_instruction */
AsmArchInstruction const * arch_get_instruction(AsmArch * arch, size_t index)
{
if(index >= arch->instructions_cnt)
return NULL;
return &arch->definition->instructions[index];
}
/* arch_get_instruction_by_name */
AsmArchInstruction const * arch_get_instruction_by_name(AsmArch * arch,
char const * name)
{
size_t i;
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s(arch, \"%s\")\n", __func__, name);
#endif
for(i = 0; i < arch->instructions_cnt; i++)
if(strcmp(arch->definition->instructions[i].name, name) == 0)
return &arch->definition->instructions[i];
return NULL;
}
/* arch_get_instruction_by_opcode */
AsmArchInstruction const * arch_get_instruction_by_opcode(AsmArch * arch,
uint8_t size, uint32_t opcode)
{
size_t i;
AsmArchInstruction const * ai;
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s(arch, %u, 0x%x)\n", __func__, size, opcode);
#endif
for(i = 0; i < arch->instructions_cnt; i++)
{
ai = &arch->definition->instructions[i];
if(AO_GET_SIZE(ai->flags) != size)
continue;
if(ai->opcode == opcode)
return ai;
}
return NULL;
}
/* arch_get_instruction_by_call */
static int _call_operands(AsmArch * arch,
AsmArchInstruction const * instruction,
AsmArchInstructionCall * call);
static int _call_operands_constant(AsmArchOperandDefinition definition,
AsmArchOperand * operand);
static int _call_operands_dregister(AsmArch * arch,
AsmArchOperandDefinition definition, AsmArchOperand * operand);
static int _call_operands_immediate(AsmArchOperandDefinition definition,
AsmArchOperand * operand);
static int _call_operands_register(AsmArch * arch,
AsmArchOperandDefinition definition, AsmArchOperand * operand);
static AsmArchInstruction const * _call_update(
AsmArchInstruction const * instruction,
AsmArchInstructionCall * call);
AsmArchInstruction const * arch_get_instruction_by_call(AsmArch * arch,
AsmArchInstructionCall * call)
{
size_t i;
AsmArchInstruction const * ai;
int found = 0;
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s(\"%s\")\n", __func__, call->name);
#endif
for(i = 0; i < arch->instructions_cnt; i++)
{
ai = &arch->definition->instructions[i];
/* FIXME use a (sorted) hash table */
if(strcmp(ai->name, call->name) != 0)
continue;
found = 1;
if(_call_operands(arch, ai, call) == 0)
return _call_update(ai, call);
}
error_set_code(1, "%s \"%s\"", found ? "Invalid arguments to"
: "Unknown instruction", call->name);
return NULL;
}
static int _call_operands(AsmArch * arch,
AsmArchInstruction const * instruction,
AsmArchInstructionCall * call)
{
size_t i;
AsmArchOperandDefinition definition;
AsmArchOperand * operand;
if(call->operands_cnt == 0 && AO_GET_TYPE(instruction->op1) != AOT_NONE)
return -1;
for(i = 0; i < call->operands_cnt; i++)
{
switch(i)
{
case 0: definition = instruction->op1; break;
case 1: definition = instruction->op2; break;
case 2: definition = instruction->op3; break;
case 3: definition = instruction->op4; break;
case 4: definition = instruction->op5; break;
default:
return -1;
}
operand = &call->operands[i];
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s() operand %lu, type %u, type %u\n",
__func__, i, AO_GET_TYPE(definition),
AO_GET_TYPE(operand->definition));
#endif
if(AO_GET_TYPE(definition) == AOT_CONSTANT)
{
if(AO_GET_TYPE(operand->definition) != AOT_IMMEDIATE)
return -1;
}
else if(AO_GET_TYPE(definition)
!= AO_GET_TYPE(operand->definition))
return -1;
switch(AO_GET_TYPE(definition))
{
case AOT_CONSTANT:
if(_call_operands_constant(definition, operand)
!= 0)
return -1;
break;
case AOT_IMMEDIATE:
if(_call_operands_immediate(definition, operand)
!= 0)
return -1;
break;
case AOT_DREGISTER:
if(_call_operands_dregister(arch, definition,
operand) != 0)
return -1;
break;
case AOT_REGISTER:
if(_call_operands_register(arch, definition,
operand) != 0)
return -1;
break;
}
}
return 0;
}
static int _call_operands_constant(AsmArchOperandDefinition definition,
AsmArchOperand * operand)
{
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s() %u %lu\n", __func__,
AO_GET_VALUE(definition),
operand->value.immediate.value);
#endif
if(AO_GET_VALUE(definition) != operand->value.immediate.value)
return -1;
/* set this operand as a constant */
operand->definition &= AOM_TYPE;
operand->definition |= (AOT_CONSTANT << AOD_TYPE);
return 0;
}
static int _call_operands_dregister(AsmArch * arch,
AsmArchOperandDefinition definition, AsmArchOperand * operand)
{
uint64_t offset;
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s() %ld\n", __func__,
operand->value.dregister.offset);
#endif
if(_call_operands_register(arch, definition, operand) != 0)
return -1;
/* check if there is an offset applied */
if(operand->value.dregister.offset == 0)
return 0;
/* check if the offset fits */
offset = abs(operand->value.dregister.offset);
offset >>= AO_GET_DSIZE(definition);
if(offset > 0)
return -1;
return 0;
}
static int _call_operands_immediate(AsmArchOperandDefinition opdefinition,
AsmArchOperand * operand)
{
uint64_t value;
uint32_t size;
/* check if the size fits */
value = operand->value.immediate.value;
#if 0 /* XXX ignore for now */
if((size = AO_GET_SIZE(opdefinition)) > 0
&& AO_GET_FLAGS(opdefinition) & AOF_SIGNED)
size--;
#else
size = AO_GET_SIZE(opdefinition);
#endif
value >>= size;
if(value > 0)
return -1;
/* check if it is signed */
if(operand->value.immediate.negative
&& !(AO_GET_FLAGS(opdefinition) & AOF_SIGNED))
return -1;
return 0;
}
static int _call_operands_register(AsmArch * arch,
AsmArchOperandDefinition opdefinition, AsmArchOperand * operand)
{
char const * name = operand->value._register.name;
AsmArchDefinition const * definition;
uint32_t size;
AsmArchRegister const * ar;
/* obtain the size */
if((definition = arch->definition->definition) != NULL
&& definition->instruction_size != 0)
size = definition->instruction_size;
else
size = AO_GET_SIZE(opdefinition);
/* check if it exists */
if((ar = arch_get_register_by_name_size(arch, name, size)) == NULL)
return -1;
/* for implicit instructions it must match */
if(AO_GET_FLAGS(opdefinition) & AOF_IMPLICIT
&& AO_GET_VALUE(opdefinition) != ar->id)
return -1;
return 0;
}
static AsmArchInstruction const * _call_update(
AsmArchInstruction const * instruction,
AsmArchInstructionCall * call)
{
size_t i;
for(i = 0; i < call->operands_cnt; i++)
switch(i)
{
case 0:
call->operands[i].definition = instruction->op1;
break;
case 1:
call->operands[i].definition = instruction->op2;
break;
case 2:
call->operands[i].definition = instruction->op3;
break;
case 3:
call->operands[i].definition = instruction->op4;
break;
case 4:
call->operands[i].definition = instruction->op5;
break;
default:
return NULL;
}
return instruction;
}
/* arch_get_instructions */
AsmArchInstruction const * arch_get_instructions(AsmArch * arch)
{
return arch->definition->instructions;
}
/* arch_get_name */
char const * arch_get_name(AsmArch * arch)
{
return arch->definition->name;
}
/* arch_get_prefix */
AsmArchPrefix const * arch_get_prefix(AsmArch * arch, size_t index)
{
if(index >= arch->prefixes_cnt)
return NULL;
return &arch->definition->prefixes[index];
}
/* arch_get_prefix_by_call */
AsmArchPrefix const * arch_get_prefix_by_call(AsmArch * arch,
AsmArchInstructionCall * call)
{
if(call->prefix == NULL)
return NULL;
return arch_get_prefix_by_name(arch, call->prefix);
}
/* arch_get_prefix_by_name */
AsmArchPrefix const * arch_get_prefix_by_name(AsmArch * arch,
char const * name)
{
size_t i;
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s(\"%s\") %zu\n", __func__, name,
arch->prefixes_cnt);
#endif
for(i = 0; i < arch->prefixes_cnt; i++)
if(strcmp(arch->definition->prefixes[i].name, name) == 0)
return &arch->definition->prefixes[i];
return NULL;
}
/* arch_get_prefix_by_opcode */
AsmArchPrefix const * arch_get_prefix_by_opcode(AsmArch * arch, uint8_t size,
uint32_t opcode)
{
size_t i;
AsmArchOperandDefinition flags;
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s(\"%s\") %zu\n", __func__, name,
arch->prefixes_cnt);
#endif
for(i = 0; i < arch->prefixes_cnt; i++)
if(arch->definition->prefixes[i].opcode == opcode)
{
if(size != 0)
{
flags = arch->definition->prefixes[i].flags;
if(AO_GET_SIZE(flags) != size)
continue;
}
return &arch->definition->prefixes[i];
}
return NULL;
}
/* arch_get_prefixes */
AsmArchPrefix const * arch_get_prefixes(AsmArch * arch)
{
return arch->definition->prefixes;
}
/* arch_get_register */
AsmArchRegister const * arch_get_register(AsmArch * arch, size_t index)
{
if(index >= arch->registers_cnt)
return NULL;
return &arch->definition->registers[index];
}
/* arch_get_register_by_id_size */
AsmArchRegister const * arch_get_register_by_id_size(AsmArch * arch,
uint32_t id, uint32_t size)
{
size_t i;
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s(%u, %u)\n", __func__, id, size);
#endif
for(i = 0; i < arch->registers_cnt; i++)
if(arch->definition->registers[i].id == id
&& arch->definition->registers[i].size == size)
return &arch->definition->registers[i];
return NULL;
}
/* arch_get_register_by_name */
AsmArchRegister const * arch_get_register_by_name(AsmArch * arch,
char const * name)
{
size_t i;
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s(\"%s\")\n", __func__, name);
#endif
for(i = 0; i < arch->registers_cnt; i++)
if(strcmp(arch->definition->registers[i].name, name) == 0)
return &arch->definition->registers[i];
return NULL;
}
/* arch_get_register_by_name_size */
AsmArchRegister const * arch_get_register_by_name_size(AsmArch * arch,
char const * name, uint32_t size)
{
size_t i;
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s(\"%s\", %u)\n", __func__, name, size);
#endif
for(i = 0; i < arch->registers_cnt; i++)
if(arch->definition->registers[i].size != size)
continue;
else if(strcmp(arch->definition->registers[i].name, name) == 0)
return &arch->definition->registers[i];
return NULL;
}
/* arch_get_registers */
AsmArchRegister const * arch_get_registers(AsmArch * arch)
{
return arch->definition->registers;
}
/* useful */
/* arch_decode */
int arch_decode(AsmArch * arch, AsmCode * code, off_t base,
AsmArchInstructionCall ** calls, size_t * calls_cnt)
{
int ret = 0;
AsmArchInstructionCall * c;
size_t c_cnt;
AsmArchInstructionCall * p;
size_t offset = 0;
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s(%ld)\n", __func__, base);
#endif
if(arch->definition->decode == NULL)
return -error_set_code(1, "%s: %s", arch->definition->name,
"Disassembly not supported");
/* check the arguments */
if(calls == NULL || calls_cnt == NULL)
return -error_set_code(1, "%s: %s", arch->definition->name,
strerror(EINVAL));
c = *calls;
c_cnt = *calls_cnt;
arch->code = code;
for(;;)
{
if((p = realloc(c, sizeof(*c) * (c_cnt + 1))) == NULL)
{
free(c);
ret = -error_set_code(1, "%s", strerror(errno));
break;
}
c = p;
p = &c[c_cnt];
memset(p, 0, sizeof(*p));
p->base = base + offset;
p->offset = arch->buffer_pos;
if(arch->definition->decode(arch->plugin, p) != 0)
break;
p->size = arch->buffer_pos - p->offset;
offset += p->size;
c_cnt++;
}
*calls = c;
*calls_cnt = c_cnt;
arch->code = NULL;
return ret;
}
/* arch_decode_at */
int arch_decode_at(AsmArch * arch, AsmCode * code, off_t offset, size_t size,
off_t base, AsmArchInstructionCall ** calls, size_t * calls_cnt)
{
int ret;
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s(%ld, %lu, %ld)\n", __func__, offset, size,
base);
#endif
/* FIXME this only works for files */
if(arch->fp == NULL)
return -error_set_code(1, "%s", strerror(ENOSYS));
if(fseek(arch->fp, offset, SEEK_SET) != 0)
return -error_set_code(1, "%s", strerror(errno));
if(size == 0)
return 0;
arch->code = code;
arch->buffer_pos = offset;
arch->buffer_cnt = offset + size;
if((ret = arch_decode(arch, code, base, calls, calls_cnt)) == 0
&& fseek(arch->fp, offset + size, SEEK_SET) != 0)
{
free(*calls); /* XXX the pointer was updated anyway... */
ret = -error_set_code(1, "%s", strerror(errno));
}
return ret;
}
/* arch_encode */
int arch_encode(AsmArch * arch, AsmArchPrefix const * prefix,
AsmArchInstruction const * instruction,
AsmArchInstructionCall * call)
{
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s(\"%s\")\n", __func__, instruction->name);
#endif
return arch->definition->encode(arch->plugin, prefix, instruction, call);
}
/* arch_exit */
int arch_exit(AsmArch * arch)
{
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s()\n", __func__);
#endif
if(arch->plugin != NULL)
arch->definition->destroy(arch->plugin);
arch->plugin = NULL;
arch->filename = NULL;
arch->fp = NULL;
arch->buffer = NULL;
arch->buffer_cnt = 0;
arch->buffer_pos = 0;
memset(&arch->helper, 0, sizeof(arch->helper));
return 0;
}
/* arch_init */
int arch_init(AsmArch * arch, char const * filename, FILE * fp)
{
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s(\"%s\", %p)\n", __func__, filename,
(void *)fp);
#endif
if(arch->plugin != NULL)
arch_exit(arch);
arch->base = 0;
arch->filename = filename;
arch->fp = fp;
arch->buffer = NULL;
arch->buffer_cnt = 0;
arch->buffer_pos = 0; /* XXX used as offset */
arch->helper.arch = arch;
arch->helper.get_filename = _arch_get_filename;
arch->helper.get_function_by_id = _arch_get_function_by_id;
arch->helper.get_prefix_by_opcode = arch_get_prefix_by_opcode;
arch->helper.get_instruction_by_opcode = arch_get_instruction_by_opcode;
arch->helper.get_register_by_id_size = arch_get_register_by_id_size;
arch->helper.get_register_by_name_size = arch_get_register_by_name_size;
arch->helper.get_string_by_id = _arch_get_string_by_id;
arch->helper.peek = _arch_peek;
arch->helper.read = _arch_read;
arch->helper.seek = _arch_seek;
arch->helper.write = _arch_write;
if(arch->definition->init != NULL
&& (arch->plugin = arch->definition->init(
&arch->helper)) == NULL)
return -1;
return 0;
}
/* arch_init */
int arch_init_buffer(AsmArch * arch, char const * buffer, size_t size)
{
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s()\n", __func__);
#endif
if(arch->plugin != NULL)
arch_exit(arch);
arch->base = 0;
arch->filename = "buffer";
arch->fp = NULL;
arch->buffer = buffer;
arch->buffer_cnt = size;
arch->buffer_pos = 0;
arch->helper.arch = arch;
arch->helper.get_filename = _arch_get_filename;
arch->helper.get_function_by_id = _arch_get_function_by_id;
arch->helper.get_prefix_by_opcode = arch_get_prefix_by_opcode;
arch->helper.get_instruction_by_opcode = arch_get_instruction_by_opcode;
arch->helper.get_register_by_id_size = arch_get_register_by_id_size;
arch->helper.get_register_by_name_size = arch_get_register_by_name_size;
arch->helper.get_string_by_id = _arch_get_string_by_id;
arch->helper.write = NULL;
arch->helper.peek = _arch_peek_buffer;
arch->helper.read = _arch_read_buffer;
arch->helper.seek = _arch_seek_buffer;
if(arch->definition->init != NULL
&& (arch->plugin = arch->definition->init(
&arch->helper)) == NULL)
return -1;
return 0;
}
/* arch_read */
ssize_t arch_read(AsmArch * arch, void * buf, size_t size)
{
if(arch->helper.read == NULL)
return -error_set_code(1, "%s", "read: No helper defined");
return arch->helper.read(arch, buf, size);
}
/* arch_seek */
off_t arch_seek(AsmArch * arch, off_t offset, int whence)
{
if(arch->helper.seek == NULL)
return -error_set_code(1, "%s", "seek: No helper defined");
return arch->helper.seek(arch, offset, whence);
}
/* private */
/* callbacks */
/* arch_get_filename */
static char const * _arch_get_filename(AsmArch * arch)
{
return arch->filename;
}
/* arch_get_function_by_id */
static AsmFunction * _arch_get_function_by_id(AsmArch * arch, AsmFunctionId id)
{
return asmcode_get_function_by_id(arch->code, id);
}
/* arch_get_string_by_id */
static AsmString * _arch_get_string_by_id(AsmArch * arch, AsmStringId id)
{
return asmcode_get_string_by_id(arch->code, id);
}
/* arch_peek */
static ssize_t _arch_peek(AsmArch * arch, void * buf, size_t size)
{
ssize_t s;
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s(arch, %p, %lu)\n", __func__, buf, size);
#endif
if((s = _arch_read(arch, buf, size)) == -1)
return -1;
if(_arch_seek(arch, -s, SEEK_CUR) == -1)
return -1;
return s;
}
/* arch_peek_buffer */
static ssize_t _arch_peek_buffer(AsmArch * arch, void * buf, size_t size)
{
ssize_t s;
if((s = _arch_read_buffer(arch, buf, size)) == -1)
return -1;
if(_arch_seek_buffer(arch, -s, SEEK_CUR) == -1)
return -1;
return s;
}
/* arch_read */
static ssize_t _arch_read(AsmArch * arch, void * buf, size_t size)
{
size_t s = min(arch->buffer_cnt - arch->buffer_pos, size);
if(fread(buf, s, 1, arch->fp) == 1)
{
arch->buffer_pos += s;
return s;
}
if(ferror(arch->fp))
return -error_set_code(1, "%s: %s", arch->filename,
strerror(errno));
if(feof(arch->fp))
return -error_set_code(1, "%s: %s", arch->filename,
"End of file reached");
return -error_set_code(1, "%s: %s", arch->filename, "Read error");
}
/* arch_read_buffer */
static ssize_t _arch_read_buffer(AsmArch * arch, void * buf, size_t size)
{
ssize_t s = min(arch->buffer_cnt - arch->buffer_pos, size);
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s()\n", __func__);
#endif
if(s == 0)
return -error_set_code(1, "%s", "End of buffer reached");
memcpy(buf, &arch->buffer[arch->buffer_pos], s);
arch->buffer_pos += s;
return s;
}
/* arch_seek */
static off_t _arch_seek(AsmArch * arch, off_t offset, int whence)
{
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s(arch, %ld, %d)\n", __func__, offset, whence);
#endif
if(fseek(arch->fp, offset, whence) != 0)
return -error_set_code(1, "%s: %s", arch->filename, strerror(
errno));
arch->buffer_pos = ftello(arch->fp);
return arch->buffer_pos;
}
/* arch_seek_buffer */
static off_t _arch_seek_buffer(AsmArch * arch, off_t offset, int whence)
{
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s(arch, %ld, %d)\n", __func__, offset, whence);
#endif
if(whence == SEEK_SET)
{
if(offset < 0 || (size_t)offset >= arch->buffer_cnt)
return -error_set_code(1, "%s", "Invalid seek");
arch->buffer_pos = offset;
}
else if(whence == SEEK_CUR)
{
if(offset < 0)
{
if((size_t)(-offset) > arch->buffer_pos)
return -error_set_code(1, "%s", "Invalid seek");
}
else if(offset > 0)
{
if((size_t)offset + arch->buffer_pos
>= arch->buffer_cnt)
return -error_set_code(1, "%s", "Invalid seek");
}
arch->buffer_pos += offset;
}
else
/* FIXME implement */
return -error_set_code(1, "%s", "Not implemented");
return arch->buffer_pos;
}
/* arch_write */
static ssize_t _arch_write(AsmArch * arch, void const * buf, size_t size)
{
if(fwrite(buf, size, 1, arch->fp) == 1)
return size;
if(ferror(arch->fp))
return -error_set_code(1, "%s: %s", arch->filename,
strerror(errno));
if(feof(arch->fp))
return -error_set_code(1, "%s: %s", arch->filename,
"End of file reached");
return -error_set_code(1, "%s: %s", arch->filename, "Write error");
}