Skip to content

Instantly share code, notes, and snippets.

@ISSOtm
Created September 15, 2019 14:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ISSOtm/6e93a21c957509ed5161e9626cf9cca9 to your computer and use it in GitHub Desktop.
Save ISSOtm/6e93a21c957509ed5161e9626cf9cca9 to your computer and use it in GitHub Desktop.
Tentative "ad-hoc" linkerscript parser for RGBDS
#include <stdlib.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include "link/main.h"
#include "link/script.h"
#include "link/section.h"
#include "extern/err.h"
struct SectionPlacement section;
static char const * const types[] = {
[SECTTYPE_ROM0] = "ROM0",
[SECTTYPE_ROMX] = "ROMX",
[SECTTYPE_VRAM] = "VRAM",
[SECTTYPE_SRAM] = "SRAM",
[SECTTYPE_WRAM0] = "WRAM0",
[SECTTYPE_WRAMX] = "WRAMX",
[SECTTYPE_OAM] = "OAM",
[SECTTYPE_HRAM] = "HRAM"
};
/* Part of internal state, but has data that needs to be freed */
static uint16_t *curaddr[SECTTYPE_INVALID];
static bool firstTime = true;
struct SectionPlacement *script_NextSection(void)
{
static uint64_t lineNo;
static enum SectionType type;
static uint32_t bank;
if (firstTime) {
firstTime = false;
/* Init search */
for (enum SectionType type = 0; type < SECTTYPE_INVALID;
type++) {
curaddr[type] =
malloc(sizeof(*curaddr[type]) * nbbanks(type));
if (!curaddr[type])
err(1, "Failed to init linker script parsing");
for (uint32_t i = 0; i < nbbanks(type); i++)
curaddr[type][type] = startaddr[type];
}
lineNo = 1;
type = SECTTYPE_INVALID;
}
while (1) {
struct SectionPlacement *retptr = NULL;
char curchar;
/* Skip initial whitespace */
do {
int _char = getc(linkerScript);
if (_char == EOF) {
if (!feof(linkerScript))
goto error;
return NULL;
}
curchar = _char;
} while (curchar == ' ' || curchar == '\t');
if (curchar == '"') {
/* If the first char is a quote, being a section decl */
if (type == SECTTYPE_INVALID)
errx(1, "Section name encountered without a bank on line %u",
lineNo);
char *str;
if (fscanf(linkerScript, "%m[^\"\r\n]%c",
&str, &curchar) != 2 || curchar != '"') {
if (feof(linkerScript))
errx(1, "Unterminated section name on line %u",
lineNo);
else
goto error;
}
struct Section *targetSect = sect_GetSection(str);
if (!targetSect)
errx(1, "Referenced unknown section \"%s\" on line %u",
str, lineNo);
section.section = targetSect;
section.address = curaddr[type][bank];
section.bank = bank;
curaddr[type][bank] += targetSect->size;
free(str);
} else if (!strchr(";\n\r", curchar)) {
char *directive;
if (fscanf(linkerScript, "%m[^ \t;\r\n]%c",
&directive, &curchar) != 1)
goto error;
/* Try reading an argument number */
bool hasArgument = false;
uint32_t argument;
if (curchar == ' ' || curchar == '\t') {
if (fscanf(linkerScript, "%*[ \t]%c",
&curchar) == 1
&& !strchr(";\r\n", curchar)) {
hasArgument = true;
char const *format = "%x";
if (curchar != '$') {
ungetc(curchar, linkerScript);
format = "%u";
}
if (fscanf(linkerScript, format,
&argument) != 1)
errx(1, "Failed to parse argument on line %u",
lineNo);
} else if (!feof(linkerScript)) {
goto error;
}
}
if (!strcmp("ALIGN", directive)) {
/* Align current PC to the nearest boundary */
} else if (!strcmp("ORG", directive)) {
/* Go to a certain address, but not backwards */
} else {
/* New memory type! */
enum SectionType newType = 0;
for ( ; newType < SECTTYPE_INVALID; newType++) {
if (!strcmp(types[newType], directive))
break;
}
if (newType == SECTTYPE_INVALID)
errx(1, "Unknown directive \"%s\" on line %u",
directive, lineNo);
type = newType;
if (!hasArgument) {
bank = bankranges[type][0];
if (bank != bankranges[type][1])
errx(1, "Expected bank number for %s on line %u",
directive, lineNo);
} else if (argument < bankranges[type][0]
|| argument > bankranges[type][1]) {
errx(1, "Bank %u is not in range for type %s on line %u",
bank, directive, lineNo);
}
}
free(directive);
} else {
ungetc(curchar, linkerScript);
}
/* Read trailing whitespace and/or comment */
if (fscanf(linkerScript, "%*[ \t]%c", &curchar) != 1) {
if (!feof(linkerScript))
goto error;
} else if (curchar == ';') {
/* Comment: read until the end of the line or EOF */
if (fscanf(linkerScript, "%*[^\r\n]%c",
&curchar) != 1) {
if (!feof(linkerScript))
goto error;
else
curchar = '\n';
}
} else {
errx(1, "Unexpected character '%c' on line %u", curchar,
lineNo);
}
/* Handle all possible line endings */
if (curchar == '\r') {
int _char = getc(linkerScript);
if (_char != '\n') {
if (_char != EOF)
ungetc(_char, linkerScript);
else if (!feof(linkerScript))
goto error;
}
}
if (retptr)
return retptr;
}
error:
err(1, "Unexpected failure while reading linker script on line %u",
lineNo);
}
void script_Cleanup(void)
{
for (enum SectionType type = 0; type < SECTTYPE_INVALID; type++)
free(curaddr[type]);
}
/*
* This file is part of RGBDS.
*
* Copyright (c) 1997-2019, Carsten Sorensen and RGBDS contributors.
*
* SPDX-License-Identifier: MIT
*/
/* Parsing a linker script */
#ifndef RGBDS_LINK_SCRIPT_H
#define RGBDS_LINK_SCRIPT_H
#include <stdint.h>
struct SectionPlacement {
struct Section *section;
uint16_t address;
uint32_t bank;
};
extern uint64_t script_lineNo;
/**
* Parses the linker script to return the next section constraint
* @return A pointer to a struct, or NULL on EOF. The pointer shouldn't be freed
*/
struct SectionPlacement *script_NextSection(void);
/**
* `free`s all assignment memory that was allocated.
*/
void script_Cleanup(void);
#endif /* RGBDS_LINK_SCRIPT_H */
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment