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 elf = Elf!ELFCLASS32(move(handle));
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)
{
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)
{
case Reference.Target.text:
relocationEntry.typeInformation = R_RISCV_CALL;
relocationEntry.r_info = R_RISCV_CALL;
break;
case Reference.Target.high20:
relocationEntry.typeInformation = R_RISCV_HI20;
relocationEntry.r_info = R_RISCV_HI20;
break;
case Reference.Target.lower12i:
relocationEntry.typeInformation = R_RISCV_LO12_I;
relocationEntry.r_info = R_RISCV_LO12_I;
break;
}
relocationEntry.hasEntry = true;
relocationData.insertBack(relocationEntry);
relocationEntry.typeInformation = R_RISCV_RELAX;
relocationEntry.hasEntry = false;
relocationData.insertBack(relocationEntry);
elf.relocate(reference.name, relocationEntry, relocationSub);
}
elf.addReadOnlyData(readOnlyData);
elf.addCode(program.name, program.text, relocationData);
elf.finish();
return 0;

View File

@ -4,6 +4,7 @@ import elna.extended;
import elna.result;
import std.algorithm;
import tanya.container.array;
import tanya.container.hashtable;
import tanya.container.string;
/// Unsigned program address.
@ -327,17 +328,17 @@ enum : Elf64_Xword
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;
}
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);
}
@ -352,24 +353,24 @@ ubyte ELF32_ST_TYPE(ubyte i) @nogc nothrow pure @safe
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));
}
auto ELF32_R_SYMT(I)(I i)
Elf32_Word ELF32_R_SYM(Elf32_Word i) @nogc nothrow pure @safe
{
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;
}
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
@ -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)
{
static if (elfClass == ELFCLASS32)
@ -620,7 +627,7 @@ struct Elf(ubyte elfClass)
alias Rela = Elf32_Rela;
alias Sym = Elf32_Sym;
alias R_SYMT = ELF32_R_SYMT;
alias R_SYM = ELF32_R_SYM;
alias R_TYPE = ELF32_R_TYPE;
alias R_INFO = ELF32_R_INFO;
alias ST_BIND = ELF32_ST_BIND;
@ -643,7 +650,7 @@ struct Elf(ubyte elfClass)
alias Rela = Elf64_Rela;
alias Sym = Elf64_Sym;
alias R_SYMT = ELF64_R_SYMT;
alias R_SYM = ELF64_R_SYM;
alias R_TYPE = ELF64_R_TYPE;
alias R_INFO = ELF64_R_INFO;
alias ST_BIND = ELF64_ST_BIND;
@ -655,16 +662,17 @@ struct Elf(ubyte elfClass)
static assert(false, "Invalid ELF class");
}
private alias Relocation = .Relocation!(Sym, Rela);
private Array!Shdr sectionHeaders;
private Off currentOffset = Elf32_Ehdr.sizeof;
private Array!Sym symbols;
static immutable char[52] sections =
"\0.symtab\0.strtab\0.shstrtab\0.text\0.rodata\0.rela.text\0";
private String strings;
private Word lastLocalSymbol;
private File output;
private Array!ubyte readOnly;
private Array!Rela relocations;
private HashTable!(String, Relocation) symbolTable;
private enum HeaderName
{
@ -681,7 +689,6 @@ struct Elf(ubyte elfClass)
Elf elf = Elf.init;
elf.initializeSectionHeaders();
elf.insertSymbols();
elf.output = move(output);
elf.output.seek(Ehdr.sizeof, File.Whence.set);
@ -700,7 +707,6 @@ struct Elf(ubyte elfClass)
void finish() @nogc
{
writeRelaTable();
writeRoDataTable();
writeSymbolTable();
writeStringTables();
@ -712,19 +718,18 @@ struct Elf(ubyte elfClass)
writeFileHeader();
}
private void insertSymbols() @nogc
private Sym initializeSymbols() @nogc
{
// Zero symbol
Elf32_Sym symbol;
Sym symbol;
symbol.st_name = 0; // Word
symbol.st_value = 0; // Addr
symbol.st_size = 0; // Word
symbol.st_info = 0; // char
symbol.st_other = 0; // char
symbol.st_shndx = 0; // Half word
this.symbols.insertBack(symbol);
// All symbols are global.
this.lastLocalSymbol = cast(Elf32_Word) this.symbols.length;
return symbol;
}
private void makeStringHeader(HeaderName position)() @nogc
@ -791,25 +796,64 @@ struct Elf(ubyte elfClass)
private void writeSymbolTable() @nogc
{
auto index = findHeader!(HeaderName.symbol)();
auto stringIndex = findHeader!(HeaderName.string_)();
const index = findHeader!(HeaderName.symbol)();
const stringIndex = findHeader!(HeaderName.string_)();
const relaIndex = findHeader!(HeaderName.rela);
const textIndex = findHeader!(HeaderName.text)();
assert(index != -1);
assert(stringIndex != -1);
assert(relaIndex != -1);
assert(textIndex != -1);
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_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]);
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
{
this.output.write(text.get);
@ -828,57 +872,15 @@ struct Elf(ubyte elfClass)
symbol.st_other = 0; // char
// .text header index, half word
symbol.st_shndx = cast(Half) textHeaderIndex;
this.symbols.insertBack(symbol);
this.symbolTable[name] = Relocation(symbol);
this.strings.insertBack(name[]);
this.sectionHeaders[textHeaderIndex].sh_size += 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)();
assert(roDataIndex != -1);
@ -894,13 +896,40 @@ struct Elf(ubyte elfClass)
symbol.st_other = 0; // char
// .text header index, half word
symbol.st_shndx = cast(Half) roDataIndex;
this.symbols.insertBack(symbol);
++this.lastLocalSymbol;
this.symbolTable[name] = Relocation(symbol);
this.strings.insertBack(".CL0");
this.strings.insertBack(name[]);
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)()
{
return countUntil!(header => header.sh_name == position)(this.sectionHeaders[]);
@ -1028,24 +1057,4 @@ struct Elf(ubyte elfClass)
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!Reference symbols;
}
struct Relocation
{
Reference symbol;
ubyte typeInformation;
bool hasEntry = true;
}