Save ELF symbols in a hash table

This commit is contained in:
Eugen Wissner 2022-06-13 17:46:02 +02:00
parent 72a60be386
commit fbbabe115f
Signed by: belka
GPG Key ID: A27FDC1E8EE902C0
3 changed files with 118 additions and 115 deletions

View File

@ -59,36 +59,37 @@ int generate(string inFile, ref String outputFilename) @nogc
auto program = writeNext(ir); auto program = writeNext(ir);
auto elf = Elf!ELFCLASS32(move(handle)); auto elf = Elf!ELFCLASS32(move(handle));
auto readOnlyData = Array!ubyte(cast(const(ubyte)[]) "%d\n".ptr[0 .. 4]); // With \0. auto readOnlyData = Array!ubyte(cast(const(ubyte)[]) "%d\n".ptr[0 .. 4]); // With \0.
Array!Relocation relocationData;
elf.addReadOnlyData(String(".CL0"), readOnlyData);
elf.addCode(program.name, program.text);
elf.addExternSymbol(String("printf"));
foreach (ref reference; program.symbols) foreach (ref reference; program.symbols)
{ {
Relocation relocationEntry; elf.Rela relocationEntry = {
r_offset: cast(elf.Addr) reference.offset
};
elf.Rela relocationSub = {
r_offset: cast(elf.Addr) reference.offset,
r_info: R_RISCV_RELAX
};
relocationEntry.symbol = reference;
final switch (reference.target) final switch (reference.target)
{ {
case Reference.Target.text: case Reference.Target.text:
relocationEntry.typeInformation = R_RISCV_CALL; relocationEntry.r_info = R_RISCV_CALL;
break; break;
case Reference.Target.high20: case Reference.Target.high20:
relocationEntry.typeInformation = R_RISCV_HI20; relocationEntry.r_info = R_RISCV_HI20;
break; break;
case Reference.Target.lower12i: case Reference.Target.lower12i:
relocationEntry.typeInformation = R_RISCV_LO12_I; relocationEntry.r_info = R_RISCV_LO12_I;
break; break;
} }
relocationEntry.hasEntry = true;
relocationData.insertBack(relocationEntry);
relocationEntry.typeInformation = R_RISCV_RELAX; elf.relocate(reference.name, relocationEntry, relocationSub);
relocationEntry.hasEntry = false;
relocationData.insertBack(relocationEntry);
} }
elf.addReadOnlyData(readOnlyData);
elf.addCode(program.name, program.text, relocationData);
elf.finish(); elf.finish();
return 0; return 0;

View File

@ -4,6 +4,7 @@ import elna.extended;
import elna.result; import elna.result;
import std.algorithm; import std.algorithm;
import tanya.container.array; import tanya.container.array;
import tanya.container.hashtable;
import tanya.container.string; import tanya.container.string;
/// Unsigned program address. /// Unsigned program address.
@ -327,17 +328,17 @@ enum : Elf64_Xword
SHF_MASKPROC = 0xF0000000, SHF_MASKPROC = 0xF0000000,
} }
auto ELF64_R_SYM(I)(I i) ubyte ELF64_R_SYM(Elf64_Xword i) @nogc nothrow pure @safe
{ {
return i >> 32; return cast(ubyte) (i >> 32);
} }
auto ELF64_R_TYPE(I)(I i) Elf64_Xword ELF64_R_TYPE(Elf64_Xword i) @nogc nothrow pure @safe
{ {
return i & 0xffffffffL; return i & 0xffffffffL;
} }
Elf64_Word ELF64_R_INFO(S)(S s, ubyte t) Elf64_Xword ELF64_R_INFO(Elf64_Xword s, Elf64_Xword t) @nogc nothrow pure @safe
{ {
return (s << 32) + (t & 0xffffffffL); return (s << 32) + (t & 0xffffffffL);
} }
@ -352,24 +353,24 @@ ubyte ELF32_ST_TYPE(ubyte i) @nogc nothrow pure @safe
return i & 0xf; return i & 0xf;
} }
ubyte ELF32_ST_INFO(ubyte b, ubyte t) @nogc nothrow pure @safe ubyte ELF32_ST_INFO(Elf32_Word b, ubyte t) @nogc nothrow pure @safe
{ {
return cast(ubyte) ((b << 4) + (t & 0xf)); return cast(ubyte) ((b << 4) + (t & 0xf));
} }
auto ELF32_R_SYMT(I)(I i) Elf32_Word ELF32_R_SYM(Elf32_Word i) @nogc nothrow pure @safe
{ {
return i >> 8; return i >> 8;
} }
ubyte ELF32_R_TYPE(I)(I i) ubyte ELF32_R_TYPE(Elf32_Word i) @nogc nothrow pure @safe
{ {
return cast(ubyte) i; return cast(ubyte) i;
} }
Elf32_Word ELF32_R_INFO(S)(S s, ubyte t) Elf32_Word ELF32_R_INFO(Elf32_Word s, Elf32_Word t) @nogc nothrow pure @safe
{ {
return cast(Elf32_Word) ((s << 8) + t); return (s << 8) + t;
} }
enum : uint enum : uint
@ -602,6 +603,12 @@ auto pad(ubyte elfClass)(size_t value) @nogc
} }
} }
private struct Relocation(Sym, Rel)
{
Sym symbol;
Array!Rel relocations;
}
struct Elf(ubyte elfClass) struct Elf(ubyte elfClass)
{ {
static if (elfClass == ELFCLASS32) static if (elfClass == ELFCLASS32)
@ -620,7 +627,7 @@ struct Elf(ubyte elfClass)
alias Rela = Elf32_Rela; alias Rela = Elf32_Rela;
alias Sym = Elf32_Sym; alias Sym = Elf32_Sym;
alias R_SYMT = ELF32_R_SYMT; alias R_SYM = ELF32_R_SYM;
alias R_TYPE = ELF32_R_TYPE; alias R_TYPE = ELF32_R_TYPE;
alias R_INFO = ELF32_R_INFO; alias R_INFO = ELF32_R_INFO;
alias ST_BIND = ELF32_ST_BIND; alias ST_BIND = ELF32_ST_BIND;
@ -643,7 +650,7 @@ struct Elf(ubyte elfClass)
alias Rela = Elf64_Rela; alias Rela = Elf64_Rela;
alias Sym = Elf64_Sym; alias Sym = Elf64_Sym;
alias R_SYMT = ELF64_R_SYMT; alias R_SYM = ELF64_R_SYM;
alias R_TYPE = ELF64_R_TYPE; alias R_TYPE = ELF64_R_TYPE;
alias R_INFO = ELF64_R_INFO; alias R_INFO = ELF64_R_INFO;
alias ST_BIND = ELF64_ST_BIND; alias ST_BIND = ELF64_ST_BIND;
@ -655,16 +662,17 @@ struct Elf(ubyte elfClass)
static assert(false, "Invalid ELF class"); static assert(false, "Invalid ELF class");
} }
private alias Relocation = .Relocation!(Sym, Rela);
private Array!Shdr sectionHeaders; private Array!Shdr sectionHeaders;
private Off currentOffset = Elf32_Ehdr.sizeof; private Off currentOffset = Elf32_Ehdr.sizeof;
private Array!Sym symbols;
static immutable char[52] sections = static immutable char[52] sections =
"\0.symtab\0.strtab\0.shstrtab\0.text\0.rodata\0.rela.text\0"; "\0.symtab\0.strtab\0.shstrtab\0.text\0.rodata\0.rela.text\0";
private String strings; private String strings;
private Word lastLocalSymbol;
private File output; private File output;
private Array!ubyte readOnly; private Array!ubyte readOnly;
private Array!Rela relocations;
private HashTable!(String, Relocation) symbolTable;
private enum HeaderName private enum HeaderName
{ {
@ -681,7 +689,6 @@ struct Elf(ubyte elfClass)
Elf elf = Elf.init; Elf elf = Elf.init;
elf.initializeSectionHeaders(); elf.initializeSectionHeaders();
elf.insertSymbols();
elf.output = move(output); elf.output = move(output);
elf.output.seek(Ehdr.sizeof, File.Whence.set); elf.output.seek(Ehdr.sizeof, File.Whence.set);
@ -700,7 +707,6 @@ struct Elf(ubyte elfClass)
void finish() @nogc void finish() @nogc
{ {
writeRelaTable();
writeRoDataTable(); writeRoDataTable();
writeSymbolTable(); writeSymbolTable();
writeStringTables(); writeStringTables();
@ -712,19 +718,18 @@ struct Elf(ubyte elfClass)
writeFileHeader(); writeFileHeader();
} }
private void insertSymbols() @nogc private Sym initializeSymbols() @nogc
{ {
// Zero symbol // Zero symbol
Elf32_Sym symbol; Sym symbol;
symbol.st_name = 0; // Word symbol.st_name = 0; // Word
symbol.st_value = 0; // Addr symbol.st_value = 0; // Addr
symbol.st_size = 0; // Word symbol.st_size = 0; // Word
symbol.st_info = 0; // char symbol.st_info = 0; // char
symbol.st_other = 0; // char symbol.st_other = 0; // char
symbol.st_shndx = 0; // Half word symbol.st_shndx = 0; // Half word
this.symbols.insertBack(symbol);
// All symbols are global. return symbol;
this.lastLocalSymbol = cast(Elf32_Word) this.symbols.length;
} }
private void makeStringHeader(HeaderName position)() @nogc private void makeStringHeader(HeaderName position)() @nogc
@ -791,25 +796,64 @@ struct Elf(ubyte elfClass)
private void writeSymbolTable() @nogc private void writeSymbolTable() @nogc
{ {
auto index = findHeader!(HeaderName.symbol)(); const index = findHeader!(HeaderName.symbol)();
auto stringIndex = findHeader!(HeaderName.string_)(); const stringIndex = findHeader!(HeaderName.string_)();
const relaIndex = findHeader!(HeaderName.rela);
const textIndex = findHeader!(HeaderName.text)();
assert(index != -1); assert(index != -1);
assert(stringIndex != -1); assert(stringIndex != -1);
assert(relaIndex != -1);
assert(textIndex != -1);
this.sectionHeaders[index].sh_offset = this.currentOffset; this.sectionHeaders[index].sh_offset = this.currentOffset;
this.sectionHeaders[index].sh_info = this.lastLocalSymbol;
this.sectionHeaders[index].sh_link = cast(Word) stringIndex; this.sectionHeaders[index].sh_link = cast(Word) stringIndex;
this.sectionHeaders[index].sh_size = cast(Word) (this.symbols.length * Sym.sizeof); this.sectionHeaders[index].sh_size = cast(Word) ((1 + symbolTable.length) * Sym.sizeof);
foreach (symbol; this.symbols) this.sectionHeaders[relaIndex].sh_link = cast(Word) index;
this.sectionHeaders[relaIndex].sh_info = cast(Word) textIndex;
this.sectionHeaders[relaIndex].sh_offset = this.sectionHeaders[index].sh_offset
+ this.sectionHeaders[index].sh_size;
auto initialSymbol = initializeSymbols();
output.write((cast(ubyte*) &initialSymbol)[0 .. Sym.sizeof]);
this.currentOffset += Sym.sizeof;
int i = 1;
Array!Relocation symbols = Array!Relocation(this.symbolTable.byValue());
auto rightRange = symbols[].partition!(symbol => ST_BIND(symbol.symbol.st_info) != STB_GLOBAL);
// Greater than last local symbol.
this.sectionHeaders[index].sh_info = cast(Word) (symbols.length - rightRange.length + 1);
foreach (ref symbol; symbols[])
{ {
this.output.seek(this.sectionHeaders[relaIndex].sh_offset + this.sectionHeaders[relaIndex].sh_size,
File.Whence.set);
if (!symbol.relocations.empty)
{
foreach (ref relocation; symbol.relocations[])
{
relocation.r_info = R_INFO(i, R_TYPE(relocation.r_info));
}
this.sectionHeaders[relaIndex].sh_flags = SHF_ALLOC;
const size = cast(Word) (Rela.sizeof * symbol.relocations.length);
this.output.write((cast(ubyte*) symbol.relocations.get)[0 .. size]);
this.sectionHeaders[relaIndex].sh_size += size;
this.currentOffset += size;
}
this.output.seek(this.sectionHeaders[index].sh_offset + i * Sym.sizeof, File.Whence.set);
output.write((cast(ubyte*) &symbol)[0 .. Sym.sizeof]); output.write((cast(ubyte*) &symbol)[0 .. Sym.sizeof]);
this.currentOffset += Sym.sizeof; this.currentOffset += Sym.sizeof;
++i;
} }
this.output.seek(0, File.Whence.end);
} }
void addCode(ref String name, ref Array!ubyte text, Array!Relocation usedSymbols) void addCode(String name, ref Array!ubyte text)
@nogc @nogc
{ {
this.output.write(text.get); this.output.write(text.get);
@ -828,57 +872,15 @@ struct Elf(ubyte elfClass)
symbol.st_other = 0; // char symbol.st_other = 0; // char
// .text header index, half word // .text header index, half word
symbol.st_shndx = cast(Half) textHeaderIndex; symbol.st_shndx = cast(Half) textHeaderIndex;
this.symbols.insertBack(symbol); this.symbolTable[name] = Relocation(symbol);
this.strings.insertBack(name[]); this.strings.insertBack(name[]);
this.sectionHeaders[textHeaderIndex].sh_size += text.length; this.sectionHeaders[textHeaderIndex].sh_size += text.length;
this.currentOffset += text.length; this.currentOffset += text.length;
foreach (usedSymbol; usedSymbols)
{
Rela relocationEntry;
relocationEntry.r_offset = cast(Addr) usedSymbol.symbol.offset;
if (usedSymbol.hasEntry)
{
if (usedSymbol.symbol.target == Reference.Target.text)
{
relocationEntry.r_info = R_INFO(this.symbols.length, usedSymbol.typeInformation);
this.relocations.insertBack(relocationEntry);
}
else
{
relocationEntry.r_info = R_INFO(1, usedSymbol.typeInformation);
this.relocations.insertBack(relocationEntry);
}
}
else
{
relocationEntry.r_info = R_INFO(0, usedSymbol.typeInformation);
this.relocations.insertBack(relocationEntry);
continue;
}
if (usedSymbol.symbol.target == Reference.Target.text)
{
Sym usedSymbolEntry;
this.strings.insertBack("\0");
usedSymbolEntry.st_name = cast(Word) this.strings.length;
usedSymbolEntry.st_value = 0;
usedSymbolEntry.st_size = 0;
usedSymbolEntry.st_info = ST_INFO(STB_GLOBAL, STT_NOTYPE);
usedSymbolEntry.st_other = 0;
usedSymbolEntry.st_shndx = SHN_UNDEF;
this.strings.insertBack(usedSymbol.symbol.name[]);
this.strings.insertBack("\0");
this.symbols.insertBack(usedSymbolEntry);
}
}
} }
void addReadOnlyData(ref Array!ubyte data) @nogc void addReadOnlyData(String name, ref Array!ubyte data) @nogc
{ {
auto roDataIndex = findHeader!(HeaderName.roData)(); auto roDataIndex = findHeader!(HeaderName.roData)();
assert(roDataIndex != -1); assert(roDataIndex != -1);
@ -894,13 +896,40 @@ struct Elf(ubyte elfClass)
symbol.st_other = 0; // char symbol.st_other = 0; // char
// .text header index, half word // .text header index, half word
symbol.st_shndx = cast(Half) roDataIndex; symbol.st_shndx = cast(Half) roDataIndex;
this.symbols.insertBack(symbol); this.symbolTable[name] = Relocation(symbol);
++this.lastLocalSymbol;
this.strings.insertBack(".CL0"); this.strings.insertBack(name[]);
this.readOnly.insertBack(data[]); this.readOnly.insertBack(data[]);
} }
void addExternSymbol(String name) @nogc
{
Sym usedSymbolEntry;
this.strings.insertBack("\0");
usedSymbolEntry.st_name = cast(Word) this.strings.length;
usedSymbolEntry.st_value = 0;
usedSymbolEntry.st_size = 0;
usedSymbolEntry.st_info = ST_INFO(STB_GLOBAL, STT_NOTYPE);
usedSymbolEntry.st_other = 0;
usedSymbolEntry.st_shndx = SHN_UNDEF;
this.strings.insertBack(name[]);
this.strings.insertBack("\0");
this.symbolTable[name] = Relocation(usedSymbolEntry);
}
void relocate(String name, Rela[] usedSymbols...) @nogc
{
foreach (usedSymbol; usedSymbols)
{
Rela relocationEntry = usedSymbol;
relocationEntry.r_info = usedSymbol.r_info;
this.symbolTable[name].relocations.insertBack(relocationEntry);
}
}
private ptrdiff_t findHeader(HeaderName position)() private ptrdiff_t findHeader(HeaderName position)()
{ {
return countUntil!(header => header.sh_name == position)(this.sectionHeaders[]); return countUntil!(header => header.sh_name == position)(this.sectionHeaders[]);
@ -1028,24 +1057,4 @@ struct Elf(ubyte elfClass)
this.sectionHeaders.insertBack(table); this.sectionHeaders.insertBack(table);
} }
private void writeRelaTable() @nogc
{
auto index = findHeader!(HeaderName.rela);
auto textIndex = findHeader!(HeaderName.text)();
auto symbolIndex = findHeader!(HeaderName.symbol)();
assert(index != -1);
assert(textIndex != -1);
assert(symbolIndex != -1);
this.sectionHeaders[index].sh_link = cast(Word) symbolIndex;
this.sectionHeaders[index].sh_info = cast(Word) textIndex;
this.sectionHeaders[index].sh_flags = this.relocations.length == 0 ? 0 : SHF_ALLOC;
this.sectionHeaders[index].sh_offset = this.currentOffset;
this.sectionHeaders[index].sh_size = cast(Word) (Rela.sizeof * this.relocations.length);
this.output.write((cast(ubyte*) this.relocations.get)[0 .. Rela.sizeof * this.relocations.length]);
this.currentOffset += Rela.sizeof * this.relocations.length;
}
} }

View File

@ -105,10 +105,3 @@ struct Symbol
Array!ubyte text; Array!ubyte text;
Array!Reference symbols; Array!Reference symbols;
} }
struct Relocation
{
Reference symbol;
ubyte typeInformation;
bool hasEntry = true;
}