mirror of
https://github.com/gbdev/rgbds.git
synced 2025-11-21 18:52:07 +00:00
Rewrite RGBLINK entirely
The goal was to improve readability, but along the way a few things were gained. - Sorted sym and map files - Infrastructure for supporting multiple .o versions - Valgrind-proof, as far as my testing goes anyways - Improved verbosity messages - Added error checking - Performance improvements, see end of commit message The readability improvement was spurred while trying to make sense of the old code while trying to implement features such as sorted sym and map files. I also did my best to remove hardcoded logic, such that modifications should be doable; for example, "RAM loading" sections, which are linked against a different location than the one they're stored at. Some work remains to be done, see the "TODO:" and "FIXME:" comments. Further, while regression tests pass, this new linker should be tested on different codebases (ideally while instrumented with `make develop` and under valgrind). The few errors spotted in the man pages (alignment) need to be corrected. Finally, documentation comments need to be written, I have written a lot of them but not all. This also provides a significant performance boost (benchmarked with a 51994-symbol project): Current master RGBLINK: 2.02user 0.03system 0:02.06elapsed 99%CPU (0avgtext+0avgdata 84336maxresident)k 0inputs+11584outputs (0major+20729minor)pagefaults 0swaps Rewritten RGBLINK: 0.19user 0.06system 0:00.63elapsed 40%CPU (0avgtext+0avgdata 32460maxresident)k 23784inputs+11576outputs (0major+7672minor)pagefaults 0swaps
This commit is contained in:
@@ -1,237 +1,410 @@
|
||||
/*
|
||||
* This file is part of RGBDS.
|
||||
*
|
||||
* Copyright (c) 2017-2018, Antonio Nino Diaz and RGBDS contributors.
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "link/main.h"
|
||||
#include "link/script.h"
|
||||
#include "link/section.h"
|
||||
|
||||
#include "extern/err.h"
|
||||
|
||||
#include "link/assign.h"
|
||||
#include "link/mylink.h"
|
||||
|
||||
static struct {
|
||||
uint32_t address; /* current address to write sections to */
|
||||
uint32_t top_address; /* not inclusive */
|
||||
enum eSectionType type;
|
||||
} bank[BANK_INDEX_MAX];
|
||||
|
||||
static int32_t current_bank = -1; /* Bank as seen by the bank array */
|
||||
static int32_t current_real_bank = -1; /* bank as seen by the GB */
|
||||
|
||||
/* Current section attributes */
|
||||
static int32_t fix_org = -1;
|
||||
static int32_t fix_align = 1;
|
||||
|
||||
void script_InitSections(void)
|
||||
static inline bool isWhiteSpace(int c)
|
||||
{
|
||||
int32_t i;
|
||||
return c == ' ' || c == '\t';
|
||||
}
|
||||
|
||||
for (i = 0; i < BANK_INDEX_MAX; i++) {
|
||||
if (BankIndexIsROM0(i)) {
|
||||
/* ROM0 bank */
|
||||
bank[i].address = 0x0000;
|
||||
if (options & OPT_TINY)
|
||||
bank[i].top_address = 0x8000;
|
||||
else
|
||||
bank[i].top_address = 0x4000;
|
||||
bank[i].type = SECT_ROM0;
|
||||
} else if (BankIndexIsROMX(i)) {
|
||||
/* Swappable ROM bank */
|
||||
bank[i].address = 0x4000;
|
||||
bank[i].top_address = 0x8000;
|
||||
bank[i].type = SECT_ROMX;
|
||||
} else if (BankIndexIsWRAM0(i)) {
|
||||
/* WRAM */
|
||||
bank[i].address = 0xC000;
|
||||
if (options & OPT_CONTWRAM)
|
||||
bank[i].top_address = 0xE000;
|
||||
else
|
||||
bank[i].top_address = 0xD000;
|
||||
bank[i].type = SECT_WRAM0;
|
||||
} else if (BankIndexIsSRAM(i)) {
|
||||
/* Swappable SRAM bank */
|
||||
bank[i].address = 0xA000;
|
||||
bank[i].top_address = 0xC000;
|
||||
bank[i].type = SECT_SRAM;
|
||||
} else if (BankIndexIsWRAMX(i)) {
|
||||
/* Swappable WRAM bank */
|
||||
bank[i].address = 0xD000;
|
||||
bank[i].top_address = 0xE000;
|
||||
bank[i].type = SECT_WRAMX;
|
||||
} else if (BankIndexIsVRAM(i)) {
|
||||
/* Swappable VRAM bank */
|
||||
bank[i].address = 0x8000;
|
||||
bank[i].type = SECT_VRAM;
|
||||
if (options & OPT_DMG_MODE && i != BANK_INDEX_VRAM) {
|
||||
/* In DMG the only available bank is bank 0. */
|
||||
bank[i].top_address = 0x8000;
|
||||
} else {
|
||||
bank[i].top_address = 0xA000;
|
||||
static inline bool isNewline(int c)
|
||||
{
|
||||
return c == '\r' || c == '\n';
|
||||
}
|
||||
|
||||
static bool tryParseNumber(char const *str, uint32_t *number)
|
||||
{
|
||||
static char const digits[] = {
|
||||
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
||||
'A', 'B', 'C', 'D', 'E', 'F'
|
||||
};
|
||||
uint8_t base = 10;
|
||||
|
||||
if (*str == '$') {
|
||||
str++;
|
||||
base = 16;
|
||||
}
|
||||
|
||||
/* An empty string is not a number */
|
||||
if (!*str)
|
||||
return false;
|
||||
|
||||
*number = 0;
|
||||
do {
|
||||
char chr = toupper(*str++);
|
||||
uint8_t digit = 0;
|
||||
|
||||
while (digit < base) {
|
||||
if (chr == digits[digit])
|
||||
break;
|
||||
digit++;
|
||||
}
|
||||
if (digit == base)
|
||||
return false;
|
||||
*number = *number * base + digit;
|
||||
} while (*str);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
enum LinkerScriptTokenType {
|
||||
TOKEN_NEWLINE,
|
||||
TOKEN_COMMAND,
|
||||
TOKEN_BANK,
|
||||
TOKEN_NUMBER,
|
||||
TOKEN_SECTION,
|
||||
TOKEN_EOF,
|
||||
|
||||
TOKEN_INVALID
|
||||
};
|
||||
|
||||
enum LinkerScriptCommand {
|
||||
COMMAND_ORG,
|
||||
COMMAND_ALIGN,
|
||||
|
||||
COMMAND_INVALID
|
||||
};
|
||||
|
||||
struct LinkerScriptToken {
|
||||
enum LinkerScriptTokenType type;
|
||||
union LinkerScriptTokenAttr {
|
||||
enum LinkerScriptCommand command;
|
||||
enum SectionType secttype;
|
||||
uint32_t number;
|
||||
char *string;
|
||||
} attr;
|
||||
};
|
||||
|
||||
static char const * const commands[] = {
|
||||
[COMMAND_ORG] = "ORG",
|
||||
[COMMAND_ALIGN] = "ALIGN"
|
||||
};
|
||||
|
||||
static uint32_t lineNo;
|
||||
|
||||
static int readChar(FILE *file)
|
||||
{
|
||||
int curchar = getc_unlocked(file);
|
||||
|
||||
if (curchar == EOF && ferror(file))
|
||||
err(1, "%s: Unexpected error reading linker script", __func__);
|
||||
return curchar;
|
||||
}
|
||||
|
||||
static struct LinkerScriptToken const *nextToken(void)
|
||||
{
|
||||
static struct LinkerScriptToken token;
|
||||
int curchar;
|
||||
|
||||
/* If the token has a string, make sure to avoid leaking it */
|
||||
if (token.type == TOKEN_SECTION)
|
||||
free(token.attr.string);
|
||||
|
||||
/* Skip initial whitespace... */
|
||||
do
|
||||
curchar = readChar(linkerScript);
|
||||
while (isWhiteSpace(curchar));
|
||||
|
||||
/* If this is a comment, skip to the end of the line */
|
||||
if (curchar == ';') {
|
||||
do
|
||||
curchar = readChar(linkerScript);
|
||||
while (!isNewline(curchar) && curchar != EOF);
|
||||
}
|
||||
|
||||
if (curchar == EOF) {
|
||||
token.type = TOKEN_EOF;
|
||||
} else if (isNewline(curchar)) {
|
||||
/* If we have a newline char, this is a newline token */
|
||||
token.type = TOKEN_NEWLINE;
|
||||
|
||||
/* FIXME: This works with CRLF newlines, but not CR-only */
|
||||
if (curchar == '\r')
|
||||
readChar(linkerScript); /* Read and discard LF */
|
||||
} else if (curchar == '"') {
|
||||
/* If we have a string start, this is a section name */
|
||||
token.type = TOKEN_SECTION;
|
||||
token.attr.string = NULL; /* Force initial alloc */
|
||||
|
||||
size_t size = 0;
|
||||
size_t capacity = 16; /* Half of the default capacity */
|
||||
|
||||
do {
|
||||
curchar = readChar(linkerScript);
|
||||
if (curchar == EOF || isNewline(curchar))
|
||||
errx(1, "Line %u: Unterminated string", lineNo);
|
||||
else if (curchar == '"')
|
||||
/* Quotes force a string termination */
|
||||
curchar = '\0';
|
||||
|
||||
if (size >= capacity || token.attr.string == NULL) {
|
||||
capacity *= 2;
|
||||
token.attr.string = realloc(token.attr.string,
|
||||
capacity);
|
||||
if (!token.attr.string)
|
||||
err(1, "%s: Failed to allocate memory for section name",
|
||||
__func__);
|
||||
}
|
||||
token.attr.string[size++] = curchar;
|
||||
} while (curchar);
|
||||
} else {
|
||||
/* This is either a number, command or bank, that is: a word */
|
||||
char *str = NULL;
|
||||
size_t size = 0;
|
||||
size_t capacity = 8; /* Half of the default capacity */
|
||||
|
||||
for (;;) {
|
||||
if (size >= capacity || str == NULL) {
|
||||
capacity *= 2;
|
||||
str = realloc(str, capacity);
|
||||
if (!str)
|
||||
err(1, "%s: Failed to allocate memory for token",
|
||||
__func__);
|
||||
}
|
||||
str[size] = toupper(curchar);
|
||||
size++;
|
||||
|
||||
if (!curchar)
|
||||
break;
|
||||
|
||||
curchar = readChar(linkerScript);
|
||||
/* Whitespace, a newline or a comment end the token */
|
||||
if (isWhiteSpace(curchar) || isNewline(curchar)
|
||||
|| curchar == ';') {
|
||||
ungetc(curchar, linkerScript);
|
||||
curchar = '\0';
|
||||
}
|
||||
} else if (BankIndexIsOAM(i)) {
|
||||
/* OAM */
|
||||
bank[i].address = 0xFE00;
|
||||
bank[i].top_address = 0xFEA0;
|
||||
bank[i].type = SECT_OAM;
|
||||
} else if (BankIndexIsHRAM(i)) {
|
||||
/* HRAM */
|
||||
bank[i].address = 0xFF80;
|
||||
bank[i].top_address = 0xFFFF;
|
||||
bank[i].type = SECT_HRAM;
|
||||
} else {
|
||||
errx(1, "%s: Unknown bank type %d", __func__, i);
|
||||
}
|
||||
|
||||
token.type = TOKEN_INVALID;
|
||||
for (enum LinkerScriptCommand i = 0; i < COMMAND_INVALID; i++) {
|
||||
if (!strcmp(commands[i], str)) {
|
||||
token.type = TOKEN_COMMAND;
|
||||
token.attr.command = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (token.type == TOKEN_INVALID) {
|
||||
for (enum SectionType type = 0; type < SECTTYPE_INVALID;
|
||||
type++) {
|
||||
if (!strcmp(typeNames[type], str)) {
|
||||
token.type = TOKEN_BANK;
|
||||
token.attr.secttype = type;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (token.type == TOKEN_INVALID) {
|
||||
/* None of the strings matched, do we have a number? */
|
||||
if (tryParseNumber(str, &token.attr.number))
|
||||
token.type = TOKEN_NUMBER;
|
||||
else
|
||||
errx(1, "Unknown token \"%s\" on linker script line %u",
|
||||
str, lineNo);
|
||||
}
|
||||
|
||||
free(str);
|
||||
}
|
||||
|
||||
return &token;
|
||||
}
|
||||
|
||||
void script_SetCurrentSectionType(const char *type, uint32_t bank_num)
|
||||
static void processCommand(enum LinkerScriptCommand command, uint16_t arg,
|
||||
uint16_t *pc)
|
||||
{
|
||||
if (strcmp(type, "ROM0") == 0) {
|
||||
if (bank_num != 0)
|
||||
errx(1, "Trying to assign a bank number to ROM0.\n");
|
||||
current_bank = BANK_INDEX_ROM0;
|
||||
current_real_bank = 0;
|
||||
return;
|
||||
} else if (strcmp(type, "ROMX") == 0) {
|
||||
if (bank_num == 0)
|
||||
errx(1, "ROMX index can't be 0.\n");
|
||||
if (bank_num > BANK_COUNT_ROMX) {
|
||||
errx(1, "ROMX index too big (%d > %d).\n", bank_num,
|
||||
BANK_COUNT_ROMX);
|
||||
}
|
||||
current_bank = BANK_INDEX_ROMX + bank_num - 1;
|
||||
current_real_bank = bank_num;
|
||||
return;
|
||||
} else if (strcmp(type, "VRAM") == 0) {
|
||||
if (bank_num >= BANK_COUNT_VRAM) {
|
||||
errx(1, "VRAM index too big (%d >= %d).\n", bank_num,
|
||||
BANK_COUNT_VRAM);
|
||||
}
|
||||
current_bank = BANK_INDEX_VRAM + bank_num;
|
||||
current_real_bank = bank_num;
|
||||
return;
|
||||
} else if (strcmp(type, "WRAM0") == 0) {
|
||||
if (bank_num != 0)
|
||||
errx(1, "Trying to assign a bank number to WRAM0.\n");
|
||||
switch (command) {
|
||||
case COMMAND_INVALID:
|
||||
trap_;
|
||||
|
||||
current_bank = BANK_INDEX_WRAM0;
|
||||
current_real_bank = 0;
|
||||
return;
|
||||
} else if (strcmp(type, "WRAMX") == 0) {
|
||||
if (bank_num == 0)
|
||||
errx(1, "WRAMX index can't be 0.\n");
|
||||
if (bank_num > BANK_COUNT_WRAMX) {
|
||||
errx(1, "WRAMX index too big (%d > %d).\n", bank_num,
|
||||
BANK_COUNT_WRAMX);
|
||||
}
|
||||
current_bank = BANK_INDEX_WRAMX + bank_num - 1;
|
||||
current_real_bank = bank_num;
|
||||
return;
|
||||
} else if (strcmp(type, "SRAM") == 0) {
|
||||
if (bank_num >= BANK_COUNT_SRAM) {
|
||||
errx(1, "SRAM index too big (%d >= %d).\n", bank_num,
|
||||
BANK_COUNT_SRAM);
|
||||
}
|
||||
current_bank = BANK_INDEX_SRAM + bank_num;
|
||||
current_real_bank = bank_num;
|
||||
return;
|
||||
} else if (strcmp(type, "OAM") == 0) {
|
||||
if (bank_num != 0) {
|
||||
errx(1, "%s: Trying to assign a bank number to OAM.\n",
|
||||
__func__);
|
||||
}
|
||||
current_bank = BANK_INDEX_OAM;
|
||||
current_real_bank = 0;
|
||||
return;
|
||||
} else if (strcmp(type, "HRAM") == 0) {
|
||||
if (bank_num != 0) {
|
||||
errx(1, "%s: Trying to assign a bank number to HRAM.\n",
|
||||
__func__);
|
||||
}
|
||||
current_bank = BANK_INDEX_HRAM;
|
||||
current_real_bank = 0;
|
||||
return;
|
||||
case COMMAND_ORG:
|
||||
*pc = arg;
|
||||
break;
|
||||
|
||||
case COMMAND_ALIGN:
|
||||
if (arg >= 16)
|
||||
arg = 0;
|
||||
else
|
||||
arg = (*pc + (1 << arg) - 1) & ~((1 << arg) - 1);
|
||||
}
|
||||
|
||||
errx(1, "%s: Unknown section type \"%s\".\n", __func__, type);
|
||||
if (arg < *pc)
|
||||
errx(1, "Linkerscript line %u: `%s` cannot be used to go backwards",
|
||||
lineNo, commands[command]);
|
||||
*pc = arg;
|
||||
}
|
||||
|
||||
void script_SetAddress(uint32_t addr)
|
||||
enum LinkerScriptParserState {
|
||||
PARSER_FIRSTTIME,
|
||||
PARSER_LINESTART,
|
||||
PARSER_LINEEND
|
||||
};
|
||||
|
||||
/* Part of internal state, but has data that needs to be freed */
|
||||
static uint16_t *curaddr[SECTTYPE_INVALID];
|
||||
|
||||
/* Put as global to ensure it's initialized only once */
|
||||
static enum LinkerScriptParserState parserState = PARSER_FIRSTTIME;
|
||||
|
||||
struct SectionPlacement *script_NextSection(void)
|
||||
{
|
||||
if (current_bank == -1)
|
||||
errx(1, "Trying to set an address without assigned bank\n");
|
||||
static struct SectionPlacement section;
|
||||
static enum SectionType type;
|
||||
static uint32_t bank;
|
||||
static uint32_t bankID;
|
||||
|
||||
/* Make sure that we don't go back. */
|
||||
if (bank[current_bank].address > addr) {
|
||||
errx(1, "Trying to go to a previous address (0x%04X to 0x%04X)\n",
|
||||
bank[current_bank].address, addr);
|
||||
if (parserState == PARSER_FIRSTTIME) {
|
||||
lineNo = 1;
|
||||
|
||||
/* Init PC for all banks */
|
||||
for (enum SectionType i = 0; i < SECTTYPE_INVALID; i++) {
|
||||
curaddr[i] = malloc(sizeof(*curaddr[i]) * nbbanks(i));
|
||||
for (uint32_t b = 0; b < nbbanks(i); b++)
|
||||
curaddr[i][b] = startaddr[i];
|
||||
}
|
||||
|
||||
type = SECTTYPE_INVALID;
|
||||
|
||||
parserState = PARSER_LINESTART;
|
||||
}
|
||||
|
||||
bank[current_bank].address = addr;
|
||||
for (;;) {
|
||||
struct LinkerScriptToken const *token = nextToken();
|
||||
enum LinkerScriptTokenType tokType;
|
||||
union LinkerScriptTokenAttr attr;
|
||||
bool hasArg;
|
||||
uint32_t arg;
|
||||
|
||||
/* Make sure we don't overflow */
|
||||
if (bank[current_bank].address >= bank[current_bank].top_address) {
|
||||
errx(1, "Bank overflowed (0x%04X >= 0x%04X)\n",
|
||||
bank[current_bank].address,
|
||||
bank[current_bank].top_address);
|
||||
if (type != SECTTYPE_INVALID) {
|
||||
if (curaddr[type][bankID] > endaddr(type) + 1)
|
||||
errx(1, "Linkerscript line %u: PC overflowed (%u > %u)",
|
||||
lineNo, curaddr[type][bankID],
|
||||
endaddr(type));
|
||||
if (curaddr[type][bankID] < startaddr[type])
|
||||
errx(1, "Linkerscript line %u: PC underflowed (%u < %u)",
|
||||
lineNo, curaddr[type][bankID],
|
||||
startaddr[type]);
|
||||
}
|
||||
|
||||
switch (parserState) {
|
||||
case PARSER_FIRSTTIME:
|
||||
trap_;
|
||||
|
||||
case PARSER_LINESTART:
|
||||
switch (token->type) {
|
||||
case TOKEN_INVALID:
|
||||
trap_;
|
||||
|
||||
case TOKEN_EOF:
|
||||
return NULL;
|
||||
|
||||
case TOKEN_NUMBER:
|
||||
errx(1, "Linkerscript line %u: stray number",
|
||||
lineNo);
|
||||
|
||||
case TOKEN_NEWLINE:
|
||||
lineNo++;
|
||||
break;
|
||||
|
||||
case TOKEN_SECTION:
|
||||
parserState = PARSER_LINEEND;
|
||||
|
||||
if (type == SECTTYPE_INVALID)
|
||||
errx(1, "Linkerscript line %u: Didn't specify a location before the section",
|
||||
lineNo);
|
||||
|
||||
section.section =
|
||||
sect_GetSection(token->attr.string);
|
||||
section.org = curaddr[type][bankID];
|
||||
section.bank = bank;
|
||||
|
||||
curaddr[type][bankID] += section.section->size;
|
||||
return §ion;
|
||||
|
||||
case TOKEN_COMMAND:
|
||||
case TOKEN_BANK:
|
||||
tokType = token->type;
|
||||
attr = token->attr;
|
||||
|
||||
token = nextToken();
|
||||
hasArg = token->type == TOKEN_NUMBER;
|
||||
/*
|
||||
* Leaving `arg` uninitialized when `!hasArg`
|
||||
* causes GCC to warn about its use as an
|
||||
* argument to `processCommand`. This cannot
|
||||
* happen because `hasArg` has to be true, but
|
||||
* silence the warning anyways.
|
||||
* I dislike doing this because it could swallow
|
||||
* actual errors, but I don't have a choice.
|
||||
*/
|
||||
arg = hasArg ? token->attr.number : 0;
|
||||
|
||||
if (tokType == TOKEN_COMMAND) {
|
||||
if (type == SECTTYPE_INVALID)
|
||||
errx(1, "Linkerscript line %u: Didn't specify a location before the command",
|
||||
lineNo);
|
||||
if (!hasArg)
|
||||
errx(1, "Linkerscript line %u: Command specified without an argument",
|
||||
lineNo);
|
||||
|
||||
processCommand(attr.command, arg,
|
||||
&curaddr[type][bankID]);
|
||||
} else { /* TOKEN_BANK */
|
||||
type = attr.secttype;
|
||||
/*
|
||||
* If there's only one bank,
|
||||
* specifying the number is optional.
|
||||
*/
|
||||
if (!hasArg && nbbanks(type) != 1)
|
||||
errx(1, "Linkerscript line %u: Didn't specify a bank number",
|
||||
lineNo);
|
||||
else if (!hasArg)
|
||||
arg = bankranges[type][0];
|
||||
else if (arg < bankranges[type][0])
|
||||
errx(1, "Linkerscript line %u: specified bank number is too low (%u < %u)",
|
||||
lineNo, arg,
|
||||
bankranges[type][0]);
|
||||
else if (arg > bankranges[type][1])
|
||||
errx(1, "Linkerscript line %u: specified bank number is too high (%u > %u)",
|
||||
lineNo, arg,
|
||||
bankranges[type][1]);
|
||||
bank = arg;
|
||||
bankID = arg - bankranges[type][0];
|
||||
}
|
||||
|
||||
/* If we read a token we shouldn't have... */
|
||||
if (token->type != TOKEN_NUMBER)
|
||||
goto lineend;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case PARSER_LINEEND:
|
||||
lineend:
|
||||
if (token->type == TOKEN_EOF)
|
||||
return NULL;
|
||||
else if (token->type != TOKEN_NEWLINE)
|
||||
errx(1, "Linkerscript line %u: Unexpected token at the end",
|
||||
lineNo);
|
||||
lineNo++;
|
||||
parserState = PARSER_LINESTART;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
fix_org = addr;
|
||||
}
|
||||
|
||||
void script_SetAlignment(uint32_t alignment)
|
||||
void script_Cleanup(void)
|
||||
{
|
||||
if (current_bank == -1)
|
||||
errx(1, "Trying to set an alignment without assigned bank\n");
|
||||
|
||||
if (alignment > 15)
|
||||
errx(1, "Trying to set an alignment too big: %d\n", alignment);
|
||||
|
||||
uint32_t size = 1 << alignment;
|
||||
uint32_t mask = size - 1;
|
||||
|
||||
if (bank[current_bank].address & mask) {
|
||||
bank[current_bank].address &= ~mask;
|
||||
bank[current_bank].address += size;
|
||||
}
|
||||
|
||||
/* Make sure we don't overflow */
|
||||
if (bank[current_bank].address >= bank[current_bank].top_address) {
|
||||
errx(1, "Bank overflowed (0x%04X >= 0x%04X)\n",
|
||||
bank[current_bank].address,
|
||||
bank[current_bank].top_address);
|
||||
}
|
||||
|
||||
fix_align = size;
|
||||
}
|
||||
|
||||
void script_OutputSection(const char *section_name)
|
||||
{
|
||||
if (current_bank == -1) {
|
||||
errx(1, "Trying to place section \"%s\" without assigned bank\n",
|
||||
section_name);
|
||||
}
|
||||
|
||||
if (!IsSectionSameTypeBankAndAttrs(section_name,
|
||||
bank[current_bank].type,
|
||||
current_real_bank,
|
||||
fix_org,
|
||||
fix_align)) {
|
||||
errx(1, "Different attributes for \"%s\" in source and linkerscript\n",
|
||||
section_name);
|
||||
}
|
||||
|
||||
/* Move section to its place. */
|
||||
bank[current_bank].address +=
|
||||
AssignSectionAddressAndBankByName(section_name,
|
||||
bank[current_bank].address,
|
||||
current_real_bank);
|
||||
|
||||
fix_org = -1;
|
||||
fix_align = 1;
|
||||
for (enum SectionType type = 0; type < SECTTYPE_INVALID; type++)
|
||||
free(curaddr[type]);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user