<file_sep>/*
 * Нужно переделать списки.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>

/*
 * Компоновщик
 */

#define DEF_OUT_FILE_NAME "out"
char *fname_output = DEF_OUT_FILE_NAME;

#define FILE_SEG_CODE_NAME "seccode"
#define FILE_SEG_DATA_NAME "secdata"
FILE *f_sec_code;
FILE *f_sec_data;

uint16_t code_off = 0;
uint16_t data_off = 0;

#define MODE_RAW 0
#define MODE_EXE 1
uint8_t link_mode = MODE_RAW;

/*
 * Таблица символов
 */

#define LABEL_NAME 32
#define SEC_CODE 0
#define SEC_DATA 1

struct Fixup {
	struct Fixup *next;
	struct Label *label;
	uint8_t  sec;
	uint16_t off;
	uint16_t pos;
	uint8_t size;
};

struct Label {
	char name[LABEL_NAME];
	uint8_t sec;
	uint16_t off;
	uint16_t pos;
	uint8_t def;
	struct Label *next;
};

struct Label *lab_hd = NULL;
struct Label *lab_tl = NULL;
struct Fixup *fix_hd = NULL;
struct Fixup *fix_tl = NULL;

struct Label *label_find(char *name) {
	struct Label *p = lab_hd;
	while (p) {
		if (strcmp(p->name, name) == 0)
			return p;
		p = p->next;
	}
	return NULL;
}

// Нужно проверять успешность malloc! В ВМ мало памяти!
struct Label *label_new(char *name, uint8_t lb_sec, uint16_t lb_off, uint16_t lb_pos, uint8_t lb_def) {
	struct Label *p = malloc(sizeof(struct Label));
	p->pos  = lb_pos;
	p->def  = lb_def;
	p->sec  = lb_sec;
	p->off  = lb_off;
	p->next = NULL;
	strcpy(p->name, name);

	if (lab_hd == NULL)
		lab_hd = p;

	if (lab_tl != NULL)
		lab_tl->next = p;
	lab_tl = p;

	return p;
}

struct Label *label_add(char *name, uint8_t lb_sec, uint16_t lb_off, uint16_t lb_pos, uint8_t lb_def) {
	struct Label *p = label_find(name);
	// коммент
	if (p != NULL) {
		if (p->def && lb_def) {
			printf("Label %s is def!\n", p->name);
			return NULL;
		}
		if (!lb_def) {
			printf("USING LABEL %s\n", p->name);
			return p;
		}
		p->sec = lb_sec;
		p->off = lb_off;
		p->pos = lb_pos;
		p->def = lb_def;
#ifdef DEBUG
		printf("LABEL DEF:\n\tNAME:%s\n\tPOS:%d\n\tDEF:%d\n\tSEC:%d\n\tOFF:%d\n", p->name, p->pos, p->def, p->sec, p->off);
#endif
		return p;
	}
	struct Label *lab;
	lab = label_new(name, lb_sec, lb_off, lb_pos, lb_def);
#ifdef DEBUG
	printf("LABEL ADD:\n\tNAME:%s\n\tPOS:%d\n\tDEF:%d\n\tSEC:%d\n\tOFF:%d\n", lab->name, lab->pos, lab->def, lab->sec, lab->off);
#endif
	return lab;
}

// Нужно проверять успешность malloc! В ВМ мало памяти!
struct Fixup *label_fixup_new(struct Label *label, uint8_t sec, uint16_t off, uint16_t pos, uint8_t size) {
	struct Fixup *p = malloc(sizeof(struct Fixup));
	p->next  = NULL;
	p->label = label;
	p->sec  = sec;
	p->off  = off;
	p->pos  = pos;
	p->size = size;
#ifdef DEBUG
	printf("TO LABEL %s ADD USE AT POS %d SIZE %d OFF %d SEC %d\n", p->label->name, p->pos, p->size, p->off, p->sec);
#endif
	if (fix_hd == NULL)
		fix_hd = p;

	if (fix_tl)
		fix_tl->next = p;
	fix_tl = p;

	return p;
}

/*
 * Считывание файла
 */

#define BUFF_SIZE 512

uint8_t link_read_file(char *file) {
	FILE *f = fopen(file, "rb");

	if (!f)
		return 0;

	/*
	 * Чтение таблицы символов
	 */

	uint16_t labs_count;
	fread(&labs_count, sizeof(uint16_t), 1, f);
#ifdef DEBUG
	printf("LABELS COUNT: %d\n", labs_count);
#endif
	while (labs_count--) {
		uint16_t pos;
		uint8_t len, nam[32] = {0}, sec, def;
		fread(&len, sizeof(uint8_t), 1, f);
		fread(nam, sizeof(uint8_t), len, f);
		fread(&pos, sizeof(uint16_t), 1, f);
		fread(&sec, sizeof(uint8_t), 1, f);
		fread(&def, sizeof(uint8_t), 1, f);
#ifdef DEBUG
		printf("LABEL READ: \n\tNAME: %s\n\tPOS: %d\n\tSEC: %d\n\tDEF: %d\n", nam, pos, sec, def);
#endif
		struct Label *lab;
		if (!(lab = label_add((char *)nam, sec, sec ? data_off : code_off, pos, def))) {
			return 0;
		}

		uint16_t uses;
		fread(&uses, sizeof(uint16_t), 1, f);
#ifdef DEBUG
		printf("USED: %d\n", uses);
#endif
		while (uses--) {
			uint16_t pos, off;
			uint8_t size, sec;
			fread(&pos, sizeof(uint16_t), 1, f);
			fread(&size, sizeof(uint8_t), 1, f);
			fread(&sec,  sizeof(uint8_t), 1, f);
			off = sec ? data_off : code_off;
			label_fixup_new(lab, sec, off, pos, size);
		}
	}

	/*
	 * Чтение секции кода
	 */

	uint16_t code_size;
	fread(&code_size, sizeof(uint16_t), 1, f);
#ifdef DEBUG
	printf("CODE SECTION SIZE: %d\n", code_size);
#endif
	uint8_t buf[BUFF_SIZE];
	uint16_t tmp = fread(buf, sizeof(uint8_t), code_size, f); //секции больше BUFF_SIZE будут читаться не полностью
	fwrite(buf, tmp, 1, f_sec_code);
	code_off += code_size;

	/*
	 * Чтение секции данных
	 */

	uint16_t data_size;
	fread(&data_size, sizeof(uint16_t), 1, f);
#ifdef DEBUG
	printf("DATA SECTION SIZE: %d\n", data_size);
#endif
	//uint8_t buf[BUFF_SIZE];
	tmp = fread(buf, 1, data_size, f); //секции больше BUFF_SIZE будут читаться не полностью
	fwrite(buf, tmp, 1, f_sec_data);
	data_off += data_size;

	fclose(f);
	return 1;
}

/*
 * Исправление адресов меток в файле
 */

void link_fix() {
	uint16_t code_size = ftell(f_sec_code);
	//uint16_t data_size = ftell(f_sec_data);

	struct Fixup *p = fix_hd;
	while (p) {
		uint16_t f_real_adr, l_real_adr;
		f_real_adr = p->off + p->pos;
		l_real_adr = p->label->off + p->label->pos;

		if (link_mode == MODE_RAW) {
			if (p->sec == SEC_DATA)
				f_real_adr += code_size;
			if (p->label->sec == SEC_DATA)
				l_real_adr += code_size;
		}

		FILE *trg_sec = p->sec ? f_sec_data : f_sec_code;

		fseek(trg_sec, f_real_adr, SEEK_SET);

		if (p->size == sizeof(uint8_t)) {
			fwrite(&l_real_adr, sizeof(uint8_t), 1, trg_sec);
		} else {
			fwrite(&l_real_adr, sizeof(uint16_t), 1, trg_sec);
		}

#ifdef DEBUG
		printf("LABEL %s ADR %d INSERT OT %d\n", p->label->name, l_real_adr, f_real_adr);
#endif
		p = p->next;
	}
}

/*
 * Генерация RAW файла
 */

uint8_t gen_raw() {
	FILE *f;
	f = fopen(fname_output, "wb");

	if (!f)
		return 0;

	fseek(f_sec_code, 0, SEEK_END);
	uint16_t code_size = ftell(f_sec_code);
	fseek(f_sec_code, 0, SEEK_SET);

	fseek(f_sec_data, 0, SEEK_END);
	uint16_t data_size = ftell(f_sec_data);
	fseek(f_sec_data, 0, SEEK_SET);

	uint8_t *code_buff, *data_buff;
	code_buff = malloc(code_size);
	data_buff = malloc(data_size);

		fread(code_buff, code_size, 1, f_sec_code);
		fread(data_buff, data_size, 1, f_sec_data);

		fwrite(code_buff, code_size, 1, f);
		fwrite(data_buff, data_size, 1, f);

	free(code_buff);
	free(data_buff);

	fclose(f);
	return 1;
}

int main(int argc, char *argv[])
{
	f_sec_code = fopen(FILE_SEG_CODE_NAME, "wb");
	f_sec_data = fopen(FILE_SEG_DATA_NAME, "wb");

	if (argc < 2) {
		printf("Help:\n\tlink input_file_1 [input_file_2 ...] output_file\n");
		return 0;
	}

	int i;
	for(i = 1; i < argc; i++) {
		if (!strcmp("-r", argv[i])) {
			link_mode = MODE_RAW;
#ifdef DEBUG
			printf("RAW MODE\n");
#endif
		} else if (!strcmp("-e", argv[i])) {
			link_mode = MODE_EXE;
#ifdef DEBUG
			printf("EXE MODE\n");
#endif
		} else if (!strcmp("-o", argv[i])) {
			++i;
			if (i < argc && *argv[i] != '-') {
				fname_output = argv[i];
			} else {
				printf("Out file name expected!\n");
				return 0;
			}
#ifdef DEBUG
			printf("OUT FILE %s\n", fname_output);
#endif
		} else if (!link_read_file(argv[i])) {
			printf("Error in file %s\n", argv[i]);
			return 0;
		}
	}

	link_fix();

	switch (link_mode) {
		case MODE_RAW:
			gen_raw();
			break;
		case MODE_EXE:
			//gen_raw();
			break;
	}

	fclose(f_sec_code);
	remove(FILE_SEG_CODE_NAME);
	fclose(f_sec_data);
	remove(FILE_SEG_DATA_NAME);
    return 0;
}
<file_sep>CC=cc
FILES=linker.c
default:
	$(CC) $(FILES) -o linker
debug:
	$(CC) -g -DDEBUG -Werror $(FILES) -o linker
