Fixed bug in nedasm related to uninitialized size of text_segment array.
[ned1] / nedasm / nedasm_codegen.c
/*
* © 2018 Aaron Taylor <ataylor at subgeniuskitty dot com>
* See LICENSE.txt file for copyright and license details.
*/
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "nedasm_structures.h"
#include "nedasm_misc.h"
#include "../common/a.out.h"
void
write_aout_exec_header(FILE * output, struct exec * aout_exec)
{
uint32_t write_count = 0;
for (uint32_t i=0; i<8; i++) {
switch (i) {
case 0: write_count = fwrite(&(aout_exec->a_midmag), 4, 1, output); break;
case 1: write_count = fwrite(&(aout_exec->a_text), 4, 1, output); break;
case 2: write_count = fwrite(&(aout_exec->a_data), 4, 1, output); break;
case 3: write_count = fwrite(&(aout_exec->a_bss), 4, 1, output); break;
case 4: write_count = fwrite(&(aout_exec->a_syms), 4, 1, output); break;
case 5: write_count = fwrite(&(aout_exec->a_entry), 4, 1, output); break;
case 6: write_count = fwrite(&(aout_exec->a_trsize), 4, 1, output); break;
case 7: write_count = fwrite(&(aout_exec->a_drsize), 4, 1, output); break;
}
if (write_count != 1) {
fprintf(stderr, "ERROR: Failed to write aout header.\n");
exit(EXIT_FAILURE);
}
}
}
void
write_aout_text_segment(FILE * output, uint32_t * text_segment)
{
for (uint32_t i=1; i <= text_segment[0]; i++) {
uint32_t write_count = fwrite(&(text_segment[i]), 4, 1, output);
if (write_count != 1) {
fprintf(stderr, "ERROR: Failed to write text segment.\n");
exit(EXIT_FAILURE);
}
}
}
void
write_aout_symbol_table(FILE * output, struct nlist * symbol_table, uint32_t symbol_count)
{
uint32_t write_count = 0;
for (uint32_t i=0; i < symbol_count; i++) {
for (uint32_t j=0; j<5; j++) {
switch (j) {
case 0: write_count = fwrite(&(symbol_table[i].n_un.n_strx), 4, 1, output); break;
case 1: write_count = fwrite(&(symbol_table[i].n_type), 4, 1, output); break;
case 2: write_count = fwrite(&(symbol_table[i].n_other), 4, 1, output); break;
case 3: write_count = fwrite(&(symbol_table[i].n_desc), 4, 1, output); break;
case 4: write_count = fwrite(&(symbol_table[i].n_value), 4, 1, output); break;
}
if (write_count != 1) {
fprintf(stderr, "ERROR: Failed to write symbol table.\n");
exit(EXIT_FAILURE);
}
}
}
}
void
write_aout_string_table(FILE * output, char * string_table, uint32_t string_table_size)
{
uint32_t write_count = fwrite(&string_table_size, 4, 1, output);
if (write_count != 1) {
fprintf(stderr, "ERROR: Failed to write string table size.\n");
exit(EXIT_FAILURE);
}
for (uint32_t i=0; i < string_table_size; i++) {
write_count = fwrite(&(string_table[i]), 1, 1, output);
if (write_count != 1) {
fprintf(stderr, "ERROR: Failed to write string table.\n");
exit(EXIT_FAILURE);
}
}
}
void
generate_aout(FILE * output, uint32_t * text_segment,
struct nlist * symbol_table, uint32_t symbol_count)
{
struct exec aout_exec;
N_SETMAGIC(aout_exec, NED_MAGIC1, MID_NED, 0);
aout_exec.a_text = (4 * text_segment[0]); /* 4 bytes per word. */
aout_exec.a_data = 0;
aout_exec.a_bss = 0;
aout_exec.a_syms = (20 * symbol_count); /* 20 = 5x 32-bit values from nlist struct. */
aout_exec.a_entry = MEM_BEGIN;
aout_exec.a_trsize = 0;
aout_exec.a_drsize = 0;
write_aout_exec_header(output, &aout_exec);
write_aout_text_segment(output,text_segment);
uint32_t string_table_size = 0;
for (uint32_t i = 0; i < symbol_count; i++) {
string_table_size += (strnlen(symbol_table[i].n_un.n_name, MAX_LABEL_LEN) + 1);
}
char * string_table = malloc(string_table_size);
uint32_t string_table_offset = 0;
for (uint32_t i = 0; i < symbol_count; i++) {
uint32_t len = (strnlen(symbol_table[i].n_un.n_name, MAX_LABEL_LEN) + 1);
strncpy(string_table+string_table_offset, symbol_table[i].n_un.n_name, len);
symbol_table[i].n_un.n_strx = string_table_offset;
string_table_offset += len;
}
write_aout_symbol_table(output, symbol_table, symbol_count);
write_aout_string_table(output, string_table, string_table_size);
}
void
write_word_to_text_segment(uint32_t * word, uint32_t * text_seg)
{
text_seg[(++text_seg[0])] = *word;
}
void
pad_word_boundary(uint8_t * syllable_count, uint32_t * word, uint32_t * text_seg)
{
if (*syllable_count > 0) {
while (*syllable_count <= 4) {
*word |= 0b000001 << 6 * (4 - (*syllable_count)++);
}
write_word_to_text_segment(word, text_seg);
*syllable_count = 0;
*word = 0;
}
}
void
generate_code_WORD(struct instruction * instructions, uint32_t * text_seg)
{
/* Set the instruction format to Type A. */
uint32_t temp_word = 0b10000000000000000000000000000000;
/* Since x in WORD_x will be shifted left by one position, check for validity. */
if (instructions->data % 2 != 0) {
fprintf(stderr, "ERROR: Unable to use odd value with WORD on line %d.\n",
instructions->linenum);
exit(EXIT_FAILURE);
}
if (instructions->data > 0b01111111111111111111111111111111) {
fprintf(stderr, "ERROR: Value too large for WORD on line %d.\n",
instructions->linenum);
exit(EXIT_FAILURE);
}
/* Set the data portion of the instruction. */
temp_word |= instructions->data >> 1;
write_word_to_text_segment(&temp_word, text_seg);
}
void
generate_code_IM(struct instruction * instructions,
uint8_t * syllable_count, uint32_t * temp_word)
{
uint8_t temp_syllable = 0b00100000;
if (instructions->data > 0b11111) {
fprintf(stderr, "ERROR: Value too large for IM on line %d.\n",
instructions->linenum);
exit(EXIT_FAILURE);
}
temp_syllable |= instructions->data;
*temp_word |= temp_syllable << 6 * (4 - *syllable_count);
}
void
generate_code_LDSP(struct instruction * instructions,
uint8_t * syllable_count, uint32_t * temp_word)
{
uint8_t temp_syllable = 0b00011000;
if (instructions->data > 0b111) {
fprintf(stderr, "ERROR: Value too large for LDSP on line %d.\n",
instructions->linenum);
exit(EXIT_FAILURE);
}
temp_syllable |= instructions->data;
*temp_word |= temp_syllable << 6 * (4 - *syllable_count);
}
void
generate_code_STSP(struct instruction * instructions,
uint8_t * syllable_count, uint32_t * temp_word)
{
uint8_t temp_syllable = 0b00010000;
if (instructions->data > 0b111) {
fprintf(stderr, "ERROR: Value too large for STSP on line %d.\n",
instructions->linenum);
exit(EXIT_FAILURE);
}
temp_syllable |= instructions->data;
*temp_word |= temp_syllable << 6 * (4 - *syllable_count);
}
void
generate_label(struct instruction * instructions, struct nlist * symbol_table, uint32_t index)
{
symbol_table[index].n_un.n_name = instructions->label;
symbol_table[index].n_type = N_ABS;
symbol_table[index].n_other = AUX_FUNC;
symbol_table[index].n_desc = 0;
while (instructions->syllable == LABEL) instructions = instructions->next;
symbol_table[index].n_value = instructions->address;
}
void
generate_code(struct instruction * instructions, FILE * output)
{
uint32_t label_count = 0;
uint32_t max_word_count = 0;
struct instruction * temp = seek_instruction_list_start(instructions);
while (temp != NULL) {
(temp->syllable == LABEL) ? (label_count++) : (max_word_count++);
temp = temp->next;
}
/* +1 to store the number of entries as the first element of the array. */
uint32_t * text_segment = malloc((max_word_count * sizeof(uint32_t)) + 1);
struct nlist * symbol_table = malloc(label_count * sizeof(struct nlist));
text_segment[0] = 0;
uint8_t syllable_count = 0;
uint32_t temp_word = 0;
label_count = 0;
instructions = seek_instruction_list_start(instructions);
while (instructions != NULL) {
/* If starting a new word, zero the word, setting it to Type C by default. */
if (syllable_count == 0) temp_word = 0b00000000000000000000000000000000;
switch (instructions->syllable) {
case WORD:
/* Must pad partial word w/NOPs & write to disk before starting new WORD. */
pad_word_boundary(&syllable_count, &temp_word, text_segment);
generate_code_WORD(instructions, text_segment);
break;
case IM:
generate_code_IM(instructions, &syllable_count, &temp_word);
break;
case LDSP:
generate_code_LDSP(instructions, &syllable_count, &temp_word);
break;
case STSP:
generate_code_STSP(instructions, &syllable_count, &temp_word);
break;
case MVSTCK: temp_word |= 0b001111 << 6 * (4 - syllable_count); break;
case ADD: temp_word |= 0b001100 << 6 * (4 - syllable_count); break;
case XOR: temp_word |= 0b001011 << 6 * (4 - syllable_count); break;
case NOT: temp_word |= 0b001010 << 6 * (4 - syllable_count); break;
case OR: temp_word |= 0b001001 << 6 * (4 - syllable_count); break;
case AND: temp_word |= 0b001000 << 6 * (4 - syllable_count); break;
case BRZ: temp_word |= 0b000111 << 6 * (4 - syllable_count); break;
case TEST: temp_word |= 0b000110 << 6 * (4 - syllable_count); break;
case CMPSWP: temp_word |= 0b000101 << 6 * (4 - syllable_count); break;
case SHIFT: temp_word |= 0b000100 << 6 * (4 - syllable_count); break;
case STORE: temp_word |= 0b000011 << 6 * (4 - syllable_count); break;
case LOAD: temp_word |= 0b000010 << 6 * (4 - syllable_count); break;
case NOP: temp_word |= 0b000001 << 6 * (4 - syllable_count); break;
case HALT: temp_word |= 0b000000 << 6 * (4 - syllable_count); break;
case SWAP: temp_word |= 0b001101 << 6 * (4 - syllable_count); break;
case JMP: temp_word |= 0b001110 << 6 * (4 - syllable_count); break;
case LABEL:
generate_label(instructions, symbol_table, label_count);
label_count++;
break;
default:
fprintf(stderr, "ERROR: Unassigned syllable on line %u.\n", instructions->linenum);
break;
}
if (syllable_count == 4) write_word_to_text_segment(&temp_word, text_segment);
if (instructions->syllable != WORD && instructions->syllable != LABEL) {
syllable_count = (syllable_count + 1) % 5;
}
instructions = instructions->next;
}
/* If necessary, pad incomplete word with NOPs. */
pad_word_boundary(&syllable_count, &temp_word, text_segment);
/* Write to disk */
generate_aout(output, text_segment, symbol_table, label_count);
}