Skip to content

Commit 760e23c

Browse files
committed
Implement .importsym directive
1 parent a8c277c commit 760e23c

File tree

4 files changed

+156
-0
lines changed

4 files changed

+156
-0
lines changed

Commands/CDirectiveFile.cpp

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -562,3 +562,125 @@ void DirectiveObjImport::writeSymData(SymbolData& symData) const
562562

563563
rel.writeSymbols(symData);
564564
}
565+
566+
//
567+
// CDirectiveSymImport
568+
//
569+
CDirectiveSymImport::CDirectiveSymImport(const fs::path& fileName)
570+
{
571+
// We may be adding global labels, so start a new section
572+
updateSection(++Global.Section);
573+
574+
this->fileName = getFullPathName(fileName);
575+
if (!fs::exists(this->fileName))
576+
{
577+
Logger::printError(Logger::FatalError,"File %s not found",this->fileName.u8string());
578+
return;
579+
}
580+
581+
fs::ifstream file(this->fileName,fs::ifstream::in|fs::ifstream::binary);
582+
if (!file.is_open())
583+
{
584+
Logger::printError(Logger::FatalError,"Could not open file %s",this->fileName.u8string());
585+
return;
586+
}
587+
588+
std::string line;
589+
size_t l = 0;
590+
while (l++, std::getline(file,line,'\n'))
591+
{
592+
// End line at comment (starting with ;)
593+
// \x1A included here as well since No$gba .sym file ends with it
594+
size_t lineEnd = line.find_first_of(";\r\x1A");
595+
if (lineEnd != std::string::npos && lineEnd > 0)
596+
{
597+
lineEnd = line.find_last_not_of(" \t",lineEnd-1);
598+
if (lineEnd != std::string::npos)
599+
lineEnd += 1;
600+
}
601+
602+
// Skip empty line
603+
if (lineEnd <= 0)
604+
continue;
605+
line = line.substr(0,lineEnd);
606+
607+
// Parse address of exactly 8 chars
608+
const char* addressStart = line.c_str();
609+
char* addressEnd;
610+
uint32_t address = strtoul(addressStart,&addressEnd,16);
611+
if (addressEnd != addressStart+8)
612+
{
613+
Logger::printError(Logger::Warning,"Invalid symbol address on line %i of symbols file %s",l,this->fileName);
614+
continue;
615+
}
616+
617+
// Skip one or more space or tabs
618+
size_t startOfName = line.find_first_not_of(" \t",8);
619+
if (startOfName == std::string::npos || startOfName < (8+1))
620+
{
621+
Logger::printError(Logger::Warning,"Invalid symbol address on line %i of symbols file %s",l,this->fileName);
622+
continue;
623+
}
624+
625+
// Rest of the line is the symbol name
626+
std::string name = line.substr(startOfName);
627+
628+
// Create label for this symbol, if it's not a directive and it would be a global label
629+
const Identifier identifier = Identifier(name);
630+
if (name.find('.') != 0 && Global.symbolTable.isGlobalSymbol(identifier))
631+
{
632+
std::shared_ptr<Label> label = Global.symbolTable.getLabel(identifier,-1,-1);
633+
if (label == nullptr)
634+
{
635+
// No$gba (supposedly...) allows pretty much any character, but armips doesn't
636+
// We can't import this label, but if the user never references it, it's fine
637+
// (If it IS referenced, that will eventually yield an error anyway)
638+
Logger::printError(Logger::Warning, "Invalid label name \"%s\" on line %i of symbols file %s",name,l,this->fileName);
639+
continue;
640+
}
641+
if (label->isDefined())
642+
{
643+
Logger::printError(Logger::Error, "Label \"%s\" already defined on line %i of symbols file %s",name,l,this->fileName);
644+
continue;
645+
}
646+
// If already defined and not a global symbol, that's fine, we are in a dedicated section anyway
647+
label->setOriginalName(identifier);
648+
label->setValue(address);
649+
label->setUpdateInfo(false);
650+
label->setDefined(true);
651+
652+
// Store for later
653+
labels.push_back(label);
654+
}
655+
656+
// Store the symbol either way since we want to merge everything into the output symfile
657+
symbols.push_back(std::pair(address, name));
658+
}
659+
}
660+
661+
bool CDirectiveSymImport::Validate(const ValidateState& state)
662+
{
663+
return false;
664+
}
665+
666+
void CDirectiveSymImport::Encode() const
667+
{
668+
669+
}
670+
671+
void CDirectiveSymImport::writeTempData(TempData& tempData) const
672+
{
673+
tempData.writeLine(-1,tfm::format(".importsym \"%s\"",fileName.u8string()));
674+
for (const auto& label: labels)
675+
{
676+
tempData.writeLine(label->getValue(),tfm::format("%s",label->getName()));
677+
}
678+
}
679+
680+
void CDirectiveSymImport::writeSymData(SymbolData& symData) const
681+
{
682+
for (const auto& symbol: symbols)
683+
{
684+
symData.addLabel(symbol.first,symbol.second);
685+
}
686+
}

Commands/CDirectiveFile.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,3 +134,18 @@ class DirectiveObjImport: public CAssemblerCommand
134134
ElfRelocator rel;
135135
std::unique_ptr<CAssemblerCommand> ctor;
136136
};
137+
138+
class CDirectiveSymImport : public CAssemblerCommand
139+
{
140+
public:
141+
CDirectiveSymImport(const fs::path& fileName);
142+
~CDirectiveSymImport() { };
143+
bool Validate(const ValidateState& state) override;
144+
void Encode() const override;
145+
void writeTempData(TempData& tempData) const override;
146+
void writeSymData(SymbolData& symData) const override;
147+
private:
148+
fs::path fileName;
149+
std::vector<std::pair<uint64_t, std::string>> symbols;
150+
std::vector<std::shared_ptr<Label>> labels;
151+
};

Parser/DirectivesParser.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,16 @@ std::unique_ptr<CAssemblerCommand> parseDirectiveObjImport(Parser& parser, int f
190190
return std::make_unique<DirectiveObjImport>(fileName.path());
191191
}
192192

193+
std::unique_ptr<CAssemblerCommand> parseDirectiveSymImport(Parser& parser, int flags)
194+
{
195+
Expression exp = parser.parseExpression();
196+
StringLiteral fileName;
197+
if (!exp.evaluateString(fileName,true))
198+
return nullptr;
199+
200+
return std::make_unique<CDirectiveSymImport>(fileName.path());
201+
}
202+
193203
std::unique_ptr<CAssemblerCommand> parseDirectiveConditional(Parser& parser, int flags)
194204
{
195205
ConditionType type;
@@ -792,6 +802,7 @@ const DirectiveMap directives = {
792802

793803
{ ".importobj", { &parseDirectiveObjImport, 0 } },
794804
{ ".importlib", { &parseDirectiveObjImport, 0 } },
805+
{ ".importsym", { &parseDirectiveSymImport, 0 } },
795806

796807
{ ".erroronwarning", { &parseDirectiveErrorWarning, 0 } },
797808
{ ".relativeinclude", { &parseDirectiveRelativeInclude, 0 } },

Readme.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -816,6 +816,14 @@ By specifying `.nocash on`, No$gba semantics will be enabled for data directives
816816

817817
By specifying `.sym off`, any symbols (e.g. labels) defined after it will not be written to the symfile (if specified with the `-sym`/`-sym2` command line flag). This can be useful when using labels to define enum values that should not be interpreted as memory addresses. Writing to the symfile can be enabled again with `.sym on`. By default, this feature is on.
818818

819+
### Import symfile
820+
821+
```
822+
.importsym SymFile
823+
```
824+
825+
Imports all symbols from the existing symfile specified by `SymFile`. For any symbols in `SymFile` not starting with `.`, `@` or `@@`, a global label will be created, which can be used like any other label. Regardless of prefix, all symbols and directives in `SymFile` are also written to the main output symfile for this run of armips, if symfile writing is enabled. This directive terminates the scope for local labels and `equ`s.
826+
819827
## 5.2 MIPS directives
820828

821829
### Load delay

0 commit comments

Comments
 (0)