Browse Source

Begin refactoring assembler into multiple files; fix use-after-free.

master
Ben Kurtovic 9 years ago
parent
commit
2186044476
5 changed files with 260 additions and 205 deletions
  1. +1
    -1
      makefile
  2. +50
    -204
      src/assembler.c
  3. +125
    -0
      src/assembler/errors.c
  4. +15
    -0
      src/assembler/errors.h
  5. +69
    -0
      src/assembler/state.h

+ 1
- 1
makefile View File

@@ -2,7 +2,7 @@
# Released under the terms of the MIT License. See LICENSE for details. # Released under the terms of the MIT License. See LICENSE for details.


PROGRAM = crater PROGRAM = crater
SOURCES = src
SOURCES = src src/assembler
BUILD = build BUILD = build
DEVEXT = -dev DEVEXT = -dev




+ 50
- 204
src/assembler.c View File

@@ -9,7 +9,8 @@
#include <sys/stat.h> #include <sys/stat.h>


#include "assembler.h" #include "assembler.h"
#include "asm_errors.h"
#include "assembler/errors.h"
#include "assembler/state.h"
#include "logging.h" #include "logging.h"
#include "util.h" #include "util.h"


@@ -38,83 +39,6 @@
#define DIRECTIVE_OFFSET(line, d) \ #define DIRECTIVE_OFFSET(line, d) \
(DIRECTIVE_HAS_ARG(line, d) ? strlen(d) : 0) (DIRECTIVE_HAS_ARG(line, d) ? strlen(d) : 0)


#define ERROR_TYPE(err_info) (asm_error_types[err_info->type])
#define ERROR_DESC(err_info) (asm_error_descs[err_info->desc])

#define SYMBOL_TABLE_BUCKETS 128

/* Internal structs */

struct ASMLine {
char *data;
size_t length;
const Line *original;
const char *filename;
struct ASMLine *next;
};
typedef struct ASMLine ASMLine;

struct ASMInclude {
LineBuffer *lines;
struct ASMInclude *next;
};
typedef struct ASMInclude ASMInclude;

struct ASMInstruction {
size_t offset;
uint8_t length;
uint8_t b1, b2, b3, b4;
uint8_t virtual_byte;
char *symbol;
struct ASMInstruction *next;
};
typedef struct ASMInstruction ASMInstruction;

struct ASMSymbol {
size_t offset;
char *symbol;
struct ASMSymbol *next;
};
typedef struct ASMSymbol ASMSymbol;

typedef struct {
ASMSymbol *buckets[SYMBOL_TABLE_BUCKETS];
} ASMSymbolTable;

typedef struct {
size_t offset;
bool checksum;
uint32_t product_code;
uint8_t version;
uint8_t region;
uint8_t rom_size;
} ASMHeaderInfo;

typedef struct {
ASMHeaderInfo header;
bool optimizer;
size_t rom_size;
ASMLine *lines;
ASMInclude *includes;
ASMInstruction *instructions;
ASMSymbolTable *symtable;
} AssemblerState;

struct ASMErrorLine {
char *data;
size_t length;
size_t lineno;
char *filename;
struct ASMErrorLine *next;
};
typedef struct ASMErrorLine ASMErrorLine;

struct ErrorInfo {
ASMErrorType type;
ASMErrorDesc desc;
ASMErrorLine *line;
};

/* /*
Deallocate a LineBuffer previously created with read_source_file(). Deallocate a LineBuffer previously created with read_source_file().
*/ */
@@ -238,102 +162,6 @@ static bool write_binary_file(const char *path, const uint8_t *data, size_t size
} }


/* /*
Create an ASMErrorLine object from an ASMLine.
*/
static ASMErrorLine* create_error_line(const ASMLine *line)
{
ASMErrorLine *el = malloc(sizeof(ASMErrorLine));
if (!el)
OUT_OF_MEMORY()

const char *source = line->original->data;
size_t length = line->original->length;
if (!(el->data = malloc(sizeof(char) * length)))
OUT_OF_MEMORY()

// Ignore spaces at beginning:
while (length > 0 && (*source == ' ' || *source == '\t'))
source++, length--;
memcpy(el->data, source, length);

el->length = length;
el->lineno = line->original->lineno;

el->filename = strdup(line->filename);
if (!el->filename)
OUT_OF_MEMORY()

el->next = NULL;
return el;
}

/*
Create an ErrorInfo object describing a particular error.

The ErrorInfo object can be printed with error_info_print(), and must be
freed when done with error_info_destroy().

This function never fails (OOM triggers an exit()); the caller can be
confident the returned object is valid.
*/
static ErrorInfo* create_error(
const ASMLine *line, ASMErrorType err_type, ASMErrorDesc err_desc)
{
ErrorInfo *einfo = malloc(sizeof(ErrorInfo));
if (!einfo)
OUT_OF_MEMORY()

einfo->type = err_type;
einfo->desc = err_desc;
einfo->line = create_error_line(line);
return einfo;
}

/*
Add an ASMLine to an ErrorInfo object, as part of a file trace.
*/
static void append_to_error(ErrorInfo *einfo, const ASMLine *line)
{
ASMErrorLine* el = create_error_line(line);
el->next = einfo->line;
einfo->line = el;
}

/*
Print an ErrorInfo object returned by assemble() to the given stream.
*/
void error_info_print(const ErrorInfo *einfo, FILE *file)
{
ASMErrorLine *line = einfo->line;

fprintf(file, "error: %s: %s\n", ERROR_TYPE(einfo), ERROR_DESC(einfo));
while (line) {
fprintf(file, "%s:%zu:\n", line->filename, line->lineno);
fprintf(file, " %.*s\n", (int) line->length, line->data);
line = line->next;
}
}

/*
Destroy an ErrorInfo object created by assemble().
*/
void error_info_destroy(ErrorInfo *error_info)
{
if (!error_info)
return;

ASMErrorLine *line = error_info->line, *temp;
while (line) {
temp = line->next;
free(line->data);
free(line->filename);
free(line);
line = temp;
}
free(error_info);
}

/*
Initialize default values in an AssemblerState object. Initialize default values in an AssemblerState object.
*/ */
static void init_state(AssemblerState *state) static void init_state(AssemblerState *state)
@@ -588,14 +416,14 @@ static ErrorInfo* build_asm_lines(
ErrorInfo *ei; ErrorInfo *ei;
char *path = read_include_path(line); char *path = read_include_path(line);
if (!path) { if (!path) {
ei = create_error(line, ET_INCLUDE, ED_INC_BAD_ARG);
ei = error_info_create(line, ET_INCLUDE, ED_INC_BAD_ARG);
free_asm_lines(line); free_asm_lines(line);
free_asm_lines(dummy.next); free_asm_lines(dummy.next);
return ei; return ei;
} }


if (path_has_been_loaded(path, root, *includes)) { if (path_has_been_loaded(path, root, *includes)) {
ei = create_error(line, ET_INCLUDE, ED_INC_RECURSION);
ei = error_info_create(line, ET_INCLUDE, ED_INC_RECURSION);
free_asm_lines(line); free_asm_lines(line);
free_asm_lines(dummy.next); free_asm_lines(dummy.next);
free(path); free(path);
@@ -606,7 +434,7 @@ static ErrorInfo* build_asm_lines(
LineBuffer *incbuffer = read_source_file(path, false); LineBuffer *incbuffer = read_source_file(path, false);
free(path); free(path);
if (!incbuffer) { if (!incbuffer) {
ei = create_error(line, ET_INCLUDE, ED_INC_FILE_READ);
ei = error_info_create(line, ET_INCLUDE, ED_INC_FILE_READ);
free_asm_lines(line); free_asm_lines(line);
free_asm_lines(dummy.next); free_asm_lines(dummy.next);
return ei; return ei;
@@ -623,7 +451,7 @@ static ErrorInfo* build_asm_lines(
ASMLine *inchead, *inctail; ASMLine *inchead, *inctail;
if ((ei = build_asm_lines(root, incbuffer, &inchead, &inctail, if ((ei = build_asm_lines(root, incbuffer, &inchead, &inctail,
includes))) { includes))) {
append_to_error(ei, line);
error_info_append(ei, line);
free_asm_lines(line); free_asm_lines(line);
free_asm_lines(dummy.next); free_asm_lines(dummy.next);
return ei; return ei;
@@ -700,12 +528,10 @@ static inline bool read_bool_argument(
static ErrorInfo* preprocess(AssemblerState *state, const LineBuffer *source) static ErrorInfo* preprocess(AssemblerState *state, const LineBuffer *source)
{ {
// state->header.offset <-- check in list of acceptable values // state->header.offset <-- check in list of acceptable values
// state->header.checksum <-- boolean check
// state->header.product_code <-- range check // state->header.product_code <-- range check
// state->header.version <-- range check // state->header.version <-- range check
// state->header.region <-- string conversion, check // state->header.region <-- string conversion, check
// state->header.rom_size <-- value/range check // state->header.rom_size <-- value/range check
// state->optimizer <-- boolean check
// state->rom_size <-- value check // state->rom_size <-- value check


// if giving rom size, check header offset is in rom size range // if giving rom size, check header offset is in rom size range
@@ -713,18 +539,25 @@ static ErrorInfo* preprocess(AssemblerState *state, const LineBuffer *source)


#define CATCH_DUPES(line, first, oldval, newval) \ #define CATCH_DUPES(line, first, oldval, newval) \
if (first && oldval != newval) { \ if (first && oldval != newval) { \
ei = create_error(line, ET_PREPROC, ED_PP_DUPLICATE); \
append_to_error(ei, first); \
ei = error_info_create(line, ET_PREPROC, ED_PP_DUPLICATE); \
error_info_append(ei, first); \
free_asm_lines(condemned); \
return ei; \ return ei; \
}
} \
oldval = newval; \
first = line;


#define REQUIRE_ARG(line, d) \ #define REQUIRE_ARG(line, d) \
if (!DIRECTIVE_HAS_ARG(line, d)) \
return create_error(line, ET_PREPROC, ED_PP_NO_ARG);
if (!DIRECTIVE_HAS_ARG(line, d)) { \
free_asm_lines(condemned); \
return error_info_create(line, ET_PREPROC, ED_PP_NO_ARG); \
}


#define VALIDATE(retval) \ #define VALIDATE(retval) \
if (!(retval)) \
return create_error(line, ET_PREPROC, ED_PP_BAD_ARG);
if (!(retval)) { \
free_asm_lines(condemned); \
return error_info_create(line, ET_PREPROC, ED_PP_BAD_ARG); \
}


DEBUG("Running preprocessor:") DEBUG("Running preprocessor:")


@@ -734,8 +567,8 @@ static ErrorInfo* preprocess(AssemblerState *state, const LineBuffer *source)
return ei; return ei;


ASMLine dummy = {.next = state->lines}; ASMLine dummy = {.next = state->lines};
ASMLine *prev, *line = &dummy, *next = state->lines;
const ASMLine *first_optimizer = NULL;
ASMLine *prev, *line = &dummy, *next = state->lines, *condemned = NULL;
const ASMLine *first_optimizer = NULL, *first_checksum = NULL;


while ((prev = line, line = next)) { while ((prev = line, line = next)) {
next = line->next; next = line->next;
@@ -750,36 +583,49 @@ static ErrorInfo* preprocess(AssemblerState *state, const LineBuffer *source)
bool arg; bool arg;
VALIDATE(read_bool_argument(&arg, line, DIR_OPTIMIZER, false)) VALIDATE(read_bool_argument(&arg, line, DIR_OPTIMIZER, false))
CATCH_DUPES(line, first_optimizer, state->optimizer, arg) CATCH_DUPES(line, first_optimizer, state->optimizer, arg)
state->optimizer = arg;
first_optimizer = line;
} else if (IS_DIRECTIVE(line, DIR_ROM_SIZE)) {
// TODO
} else if (IS_DIRECTIVE(line, DIR_ROM_HEADER)) {
}
else if (IS_DIRECTIVE(line, DIR_ROM_SIZE)) {
// TODO // TODO
} else if (IS_DIRECTIVE(line, DIR_ROM_CHECKSUM)) {
}
else if (IS_DIRECTIVE(line, DIR_ROM_HEADER)) {
// TODO // TODO
} else if (IS_DIRECTIVE(line, DIR_ROM_PRODUCT)) {
}
else if (IS_DIRECTIVE(line, DIR_ROM_CHECKSUM)) {
REQUIRE_ARG(line, DIR_ROM_CHECKSUM)
bool arg;
VALIDATE(read_bool_argument(&arg, line, DIR_ROM_CHECKSUM, true))
CATCH_DUPES(line, first_checksum, state->header.checksum, arg)
}
else if (IS_DIRECTIVE(line, DIR_ROM_PRODUCT)) {
// TODO // TODO
} else if (IS_DIRECTIVE(line, DIR_ROM_VERSION)) {
}
else if (IS_DIRECTIVE(line, DIR_ROM_VERSION)) {
// TODO // TODO
} else if (IS_DIRECTIVE(line, DIR_ROM_REGION)) {
}
else if (IS_DIRECTIVE(line, DIR_ROM_REGION)) {
// TODO // TODO
} else if (IS_DIRECTIVE(line, DIR_ROM_DECLSIZE)) {
}
else if (IS_DIRECTIVE(line, DIR_ROM_DECLSIZE)) {
// TODO // TODO
} else {
return create_error(line, ET_PREPROC, ED_PP_UNKNOWN);
}
else {
free_asm_lines(condemned);
return error_info_create(line, ET_PREPROC, ED_PP_UNKNOWN);
} }


// Remove the directive from the line list:
// Remove directive from lines, and schedule it for deletion:
line->next = condemned;
condemned = line;
prev->next = next; prev->next = next;
line->next = NULL;
free_asm_lines(line);
line = prev; line = prev;
} }
} }


state->rom_size = 8; // TODO state->rom_size = 8; // TODO


free_asm_lines(condemned);
state->lines = dummy.next; // Fix list head if first line was a directive

#ifdef DEBUG_MODE #ifdef DEBUG_MODE
DEBUG("Dumping ASMLines:") DEBUG("Dumping ASMLines:")
const ASMLine *temp = state->lines; const ASMLine *temp = state->lines;


+ 125
- 0
src/assembler/errors.c View File

@@ -0,0 +1,125 @@
/* Copyright (C) 2014-2015 Ben Kurtovic <ben.kurtovic@gmail.com>
Released under the terms of the MIT License. See LICENSE for details. */

#include <stdio.h>

#include "errors.h"
#include "state.h"
#include "../assembler.h"
#include "../logging.h"

#define ERROR_TYPE(err_info) (asm_error_types[err_info->type])
#define ERROR_DESC(err_info) (asm_error_descs[err_info->desc])

/* Internal structs */

struct ASMErrorLine {
char *data;
size_t length;
size_t lineno;
char *filename;
struct ASMErrorLine *next;
};
typedef struct ASMErrorLine ASMErrorLine;

struct ErrorInfo {
ASMErrorType type;
ASMErrorDesc desc;
ASMErrorLine *line;
};

/*
Create an ASMErrorLine object from an ASMLine.
*/
static ASMErrorLine* create_error_line(const ASMLine *line)
{
ASMErrorLine *el = malloc(sizeof(ASMErrorLine));
if (!el)
OUT_OF_MEMORY()

const char *source = line->original->data;
size_t length = line->original->length;
if (!(el->data = malloc(sizeof(char) * length)))
OUT_OF_MEMORY()

// Ignore spaces at beginning:
while (length > 0 && (*source == ' ' || *source == '\t'))
source++, length--;
memcpy(el->data, source, length);

el->length = length;
el->lineno = line->original->lineno;

el->filename = strdup(line->filename);
if (!el->filename)
OUT_OF_MEMORY()

el->next = NULL;
return el;
}

/*
Create an ErrorInfo object describing a particular error.

The ErrorInfo object can be printed with error_info_print(), and must be
freed when done with error_info_destroy().

This function never fails (OOM triggers an exit()); the caller can be
confident the returned object is valid.
*/
ErrorInfo* error_info_create(
const ASMLine *line, ASMErrorType err_type, ASMErrorDesc err_desc)
{
ErrorInfo *einfo = malloc(sizeof(ErrorInfo));
if (!einfo)
OUT_OF_MEMORY()

einfo->type = err_type;
einfo->desc = err_desc;
einfo->line = create_error_line(line);
return einfo;
}

/*
Add an ASMLine to an ErrorInfo object, as part of a file trace.
*/
void error_info_append(ErrorInfo *einfo, const ASMLine *line)
{
ASMErrorLine* el = create_error_line(line);
el->next = einfo->line;
einfo->line = el;
}

/*
Print an ErrorInfo object returned by assemble() to the given stream.
*/
void error_info_print(const ErrorInfo *einfo, FILE *file)
{
ASMErrorLine *line = einfo->line;

fprintf(file, "error: %s: %s\n", ERROR_TYPE(einfo), ERROR_DESC(einfo));
while (line) {
fprintf(file, "%s:%zu:\n", line->filename, line->lineno);
fprintf(file, " %.*s\n", (int) line->length, line->data);
line = line->next;
}
}

/*
Destroy an ErrorInfo object created by assemble().
*/
void error_info_destroy(ErrorInfo *error_info)
{
if (!error_info)
return;

ASMErrorLine *line = error_info->line, *temp;
while (line) {
temp = line->next;
free(line->data);
free(line->filename);
free(line);
line = temp;
}
free(error_info);
}

src/asm_errors.h → src/assembler/errors.h View File

@@ -3,6 +3,10 @@


#pragma once #pragma once


#include <stdlib.h>

#include "state.h"

/* Enums */ /* Enums */


typedef enum { typedef enum {
@@ -38,3 +42,14 @@ static const char *asm_error_descs[] = {
"missing argument for directive", "missing argument for directive",
"invalid argument for directive" "invalid argument for directive"
}; };

/* Structs */

typedef struct ErrorInfo ErrorInfo;

/* Functions */

ErrorInfo* error_info_create(const ASMLine*, ASMErrorType, ASMErrorDesc);
void error_info_append(ErrorInfo*, const ASMLine*);
void error_info_print(const ErrorInfo*, FILE*);
void error_info_destroy(ErrorInfo*);

+ 69
- 0
src/assembler/state.h View File

@@ -0,0 +1,69 @@
/* Copyright (C) 2014-2015 Ben Kurtovic <ben.kurtovic@gmail.com>
Released under the terms of the MIT License. See LICENSE for details. */

#pragma once

#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>

#include "../assembler.h"

#define SYMBOL_TABLE_BUCKETS 128

/* Structs */

struct ASMLine {
char *data;
size_t length;
const Line *original;
const char *filename;
struct ASMLine *next;
};
typedef struct ASMLine ASMLine;

struct ASMInclude {
LineBuffer *lines;
struct ASMInclude *next;
};
typedef struct ASMInclude ASMInclude;

struct ASMInstruction {
size_t offset;
uint8_t length;
uint8_t b1, b2, b3, b4;
uint8_t virtual_byte;
char *symbol;
struct ASMInstruction *next;
};
typedef struct ASMInstruction ASMInstruction;

struct ASMSymbol {
size_t offset;
char *symbol;
struct ASMSymbol *next;
};
typedef struct ASMSymbol ASMSymbol;

typedef struct {
ASMSymbol *buckets[SYMBOL_TABLE_BUCKETS];
} ASMSymbolTable;

typedef struct {
size_t offset;
bool checksum;
uint32_t product_code;
uint8_t version;
uint8_t region;
uint8_t rom_size;
} ASMHeaderInfo;

typedef struct {
ASMHeaderInfo header;
bool optimizer;
size_t rom_size;
ASMLine *lines;
ASMInclude *includes;
ASMInstruction *instructions;
ASMSymbolTable *symtable;
} AssemblerState;

Loading…
Cancel
Save