Add support for including files in linkerscript

Files can now be included with the following syntax:

    INCLUDE "path.link"

The maximum include depth is 5.

Fixed linkerscript parser and lexer error messages so that they are more
informative (show file and line of the error).

Man page updated.

Signed-off-by: Antonio Niño Díaz <antonio_nd@outlook.com>
This commit is contained in:
Antonio Niño Díaz
2017-04-08 18:10:24 +01:00
parent 3d8396b86f
commit 206275df57
5 changed files with 156 additions and 46 deletions

View File

@@ -17,8 +17,16 @@
#ifndef RGBDS_LINK_SCRIPT_H #ifndef RGBDS_LINK_SCRIPT_H
#define RGBDS_LINK_SCRIPT_H #define RGBDS_LINK_SCRIPT_H
#include "extern/stdnoreturn.h"
noreturn void script_fatalerror(const char *fmt, ...);
void script_Parse(const char *path); void script_Parse(const char *path);
void script_IncludeFile(const char *path);
int script_IncludeDepthGet(void);
void script_IncludePop(void);
void script_InitSections(void); void script_InitSections(void);
void script_SetCurrentSectionType(const char *type, unsigned int bank); void script_SetCurrentSectionType(const char *type, unsigned int bank);
void script_SetAddress(unsigned int addr); void script_SetAddress(unsigned int addr);

View File

@@ -445,6 +445,7 @@ AssignSections(void)
*/ */
if (tzLinkerscriptName) { if (tzLinkerscriptName) {
script_InitSections();
script_Parse(tzLinkerscriptName); script_Parse(tzLinkerscriptName);
} }

View File

@@ -16,22 +16,38 @@
%option noinput %option noinput
%option nounput %option nounput
%option yylineno
%{ %{
#include <stdarg.h>
#include <unistd.h> #include <unistd.h>
#include "extern/err.h" #include "extern/err.h"
#include "link/mylink.h"
#include "link/script.h"
#include "parser.h" #include "parser.h"
/* File include stack. */
#define MAX_INCLUDE_DEPTH 8
static int include_stack_ptr = 0;
static YY_BUFFER_STATE include_stack[MAX_INCLUDE_DEPTH];
static char include_path[MAX_INCLUDE_DEPTH][_MAX_PATH + 1];
static int include_line[MAX_INCLUDE_DEPTH];
static char linkerscript_path[_MAX_PATH + 1]; /* Base file */
%} %}
%% %%
\"([^\\\"]|\\.)*\" { \"([^\\\"]|\\.)*\" {
if (strlen(yytext) > sizeof(yylval.s) - 1) if (strlen(yytext) > sizeof(yylval.s) - 1)
errx(1, "String is too long: \"%s\"\n.", yytext); script_fatalerror("String is too long: %s\n.", yytext);
if (strlen(yytext) < 3) /* 2 quotes + 1 character */ if (strlen(yytext) < 3) /* 2 quotes + 1 character */
errx(1, "String \"%s\" is invalid\n.", yytext); script_fatalerror("String %s is invalid\n.", yytext);
yytext++; /* ignore first quote */ yytext++; /* ignore first quote */
strcpy(yylval.s, yytext); strcpy(yylval.s, yytext);
@@ -62,13 +78,99 @@
(?i:ALIGN) { return COMMAND_ALIGN; } (?i:ALIGN) { return COMMAND_ALIGN; }
(?i:ORG) { return COMMAND_ORG; } (?i:ORG) { return COMMAND_ORG; }
(?i:INCLUDE) { return COMMAND_INCLUDE; }
"\n" { return NEWLINE; } "\n" { return NEWLINE; }
;.* { /* Ignore comments. A dot doesn't match newline. */ } ;.* { /* Ignore comments. A dot doesn't match newline. */ }
[[:space:]] { /* Ignore whitespace. */ } [[:space:]] { /* Ignore whitespace. */ }
. { errx(1, "Invalid character [%s]\n.", yytext); } . { script_fatalerror("Invalid character [%s]\n.", yytext); }
%% %%
extern FILE *yyin;
void script_Parse(const char * path)
{
yyin = fopen(path, "r");
if (!yyin)
errx(1, "Error opening file! \"%s\"\n", path);
strncpy(linkerscript_path, path, sizeof(linkerscript_path));
linkerscript_path[sizeof(linkerscript_path) - 1] = '\0';
do {
yyparse();
} while (!feof(yyin));
fclose(yyin);
}
void script_IncludeFile(const char * path)
{
if (include_stack_ptr == (MAX_INCLUDE_DEPTH-1))
script_fatalerror("Includes nested too deeply.");
include_line[include_stack_ptr] = yylineno;
include_stack[include_stack_ptr] = YY_CURRENT_BUFFER;
include_stack_ptr++;
yyin = fopen(path, "r" );
if (!yyin)
script_fatalerror("Couldn't open file \"%s\"", path);
strncpy(include_path[include_stack_ptr], path, sizeof(include_path[0]));
include_path[include_stack_ptr][sizeof(include_path[0])-1] = '\0';
yy_switch_to_buffer(yy_create_buffer(yyin, YY_BUF_SIZE));
yylineno = 0;
}
int script_IncludeDepthGet(void)
{
return include_stack_ptr;
}
void script_IncludePop(void)
{
fclose(yyin);
include_stack_ptr--;
yy_delete_buffer(YY_CURRENT_BUFFER);
yy_switch_to_buffer(include_stack[include_stack_ptr]);
yylineno = include_line[include_stack_ptr];
}
void script_PrintFileStack(void)
{
int i = include_stack_ptr;
include_line[i] = yylineno;
while (i > 0) {
fprintf(stderr, "%s(%d) -> ", include_path[i], include_line[i]);
i--;
}
fprintf(stderr, "%s(%d)", linkerscript_path, include_line[i]);
}
noreturn void script_fatalerror(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
fprintf(stderr, "error: ");
script_PrintFileStack();
fprintf(stderr, ":\n\t");
vfprintf(stderr, fmt, args);
fprintf(stderr, "\n");
va_end(args);
exit(1);
}

View File

@@ -23,7 +23,7 @@
int yylex(); int yylex();
void yyerror(char *); void yyerror(char *);
static int nline = 1; extern int yylineno;
%} %}
%union { int i; char s[512]; } %union { int i; char s[512]; }
@@ -37,6 +37,8 @@ static int nline = 1;
%token COMMAND_ALIGN %token COMMAND_ALIGN
%token COMMAND_ORG %token COMMAND_ORG
%token COMMAND_INCLUDE
%token NEWLINE %token NEWLINE
%start lines %start lines
@@ -49,8 +51,8 @@ lines:
; ;
line: line:
/* empty */ { nline++; } /* empty */
| statement { nline++; } | statement
; ;
statement: statement:
@@ -59,11 +61,11 @@ statement:
script_SetCurrentSectionType($1, 0); script_SetCurrentSectionType($1, 0);
} }
| SECTION_NONBANKED INTEGER { | SECTION_NONBANKED INTEGER {
errx(1, "%d:Trying to assign a bank to a non-banked section.\n", nline); script_fatalerror("Trying to assign a bank to a non-banked section.\n");
} }
| SECTION_BANKED { | SECTION_BANKED {
errx(1, "%d:Banked section without assigned bank.\n", nline); script_fatalerror("Banked section without assigned bank.\n");
} }
| SECTION_BANKED INTEGER { | SECTION_BANKED INTEGER {
script_SetCurrentSectionType($1, $2); script_SetCurrentSectionType($1, $2);
@@ -74,13 +76,13 @@ statement:
script_SetAlignment($2); script_SetAlignment($2);
} }
| COMMAND_ALIGN { | COMMAND_ALIGN {
errx(1, "%d:ALIGN keyword needs an argument.\n", nline); script_fatalerror("ALIGN keyword needs an argument.\n");
} }
| COMMAND_ORG INTEGER { | COMMAND_ORG INTEGER {
script_SetAddress($2); script_SetAddress($2);
} }
| COMMAND_ORG { | COMMAND_ORG {
errx(1, "%d:ORG keyword needs an argument.\n", nline); script_fatalerror("ORG keyword needs an argument.\n");
} }
/* Section name */ /* Section name */
@@ -88,6 +90,11 @@ statement:
script_OutputSection($1); script_OutputSection($1);
} }
/* Include file */
| COMMAND_INCLUDE STRING {
script_IncludeFile($2);
}
/* End */ /* End */
; ;
@@ -96,33 +103,18 @@ statement:
extern int yylex(); extern int yylex();
extern int yyparse(); extern int yyparse();
extern FILE *yyin; int yywrap(void)
int yywrap (void)
{ {
return 1; if (script_IncludeDepthGet() == 0)
return 1;
script_IncludePop();
return 0;
} }
void yyerror(char *s) void yyerror(char *s)
{ {
errx(1, "%d:Linkerscript parse error: \"%s\"\n", nline, s); script_fatalerror("Linkerscript parse error: \"%s\"\n", s);
}
void script_Parse(const char *path)
{
script_InitSections();
FILE *f = fopen(path, "r");
if (!f)
errx(1, "Error opening file! \"%s\"\n", path);
yyin = f;
do {
yyparse();
} while (!feof(yyin));
fclose(f);
} }

View File

@@ -12,7 +12,7 @@
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\" .\"
.Dd March 27, 2017 .Dd April 8, 2017
.Dt RGBLINK 5 .Dt RGBLINK 5
.Os RGBDS Manual .Os RGBDS Manual
.Sh NAME .Sh NAME
@@ -34,35 +34,42 @@ Any line can contain a comment starting with
that ends at the end of the line: that ends at the end of the line:
.Pp .Pp
ROMX $F ; This is a comment ROMX $F ; This is a comment
"Functions to read array" "Functions to read array"
ALIGN 8 ALIGN 8
"Array aligned to 256 bytes" "Array aligned to 256 bytes"
WRAMX 2 WRAMX 2
"Some variables" "Some variables"
.Pp .Pp
Numbers can be in decimal or hexadecimal format (the prefix is Numbers can be in decimal or hexadecimal format (the prefix is
.Ql $ ) . .Ql $ ) .
It is an error if any bank or command is found before setting a bank. It is an error if any bank or command is found before setting a bank.
.Pp .Pp
The possible bank types are: ROM0, ROMX, VRAM, WRAM0, WRAMX, OAM and HRAM. Files can be included by using the
Types ROMX, VRAM, WRAMX and SRAM are banked, which means that it is needed to .Ar INCLUDE No keyword followed by a string with the path of the file that has
specify a bank after the type. to be included.
.Pp
The possible bank types are:
.Sy ROM0 , ROMX , VRAM , WRAM0 , WRAMX , OAM No and Sy HRAM .
Types
.Sy ROMX , VRAM , WRAMX No and Sy SRAM No are banked, which means that it is
needed to specify a bank after the type.
.Pp .Pp
When a new bank statement is found, sections found after it will be placed When a new bank statement is found, sections found after it will be placed
right from the beginning of that bank. right from the beginning of that bank.
If the linkerscript switches to a different bank and then it comes back to the If the linkerscript switches to a different bank and then it comes back to the
previous one it will continue from the last address that was used. previous one it will continue from the last address that was used.
.Pp .Pp
The only two commands are ORG and ALIGN: The only two commands are
.Ar ORG No and Ar ALIGN :
.Bl -bullet .Bl -bullet
.It .It
ORG sets the address in which new sections will be placed. .Ar ORG No sets the address in which new sections will be placed.
It can not be lower than the current address. It can not be lower than the current address.
.It .It
ALIGN will increase the address until it is aligned to the specified boundary .Ar ALIGN No will increase the address until it is aligned to the specified
(it tries to set to 0 the number of bits specified after the command: ALIGN 8 boundary (it tries to set to 0 the number of bits specified after the command:
will align to $100). .Ar ALIGN No 8 No will align to No $100 ) .
.El .El
.Pp .Pp
Note: The bank, alignment, address and type of sections can be specified both Note: The bank, alignment, address and type of sections can be specified both