work on elf output (2)

This commit is contained in:
mappu 2023-12-11 17:26:59 +13:00
parent e03434bd50
commit d95fa7e564
2 changed files with 140 additions and 54 deletions

View File

@ -16,7 +16,9 @@ type section struct {
}
type symtabEntry struct {
symtabSectionIndex int
// The index of this symbol within the whole symtab
symtabSectionIndex int
name_shstrtabOffset int
sectionName string
kind string
@ -41,55 +43,79 @@ func NewCompiler() *compiler {
// First, there's an all-zero entry that is reserved for extended ELF headers
c.sections = append(c.sections, section{})
// Real entry: shstrtab
c.sections = append(c.sections, section{
name: `.shstrtab`, // Mandatory: the table that names sections themselves
name_shstrtabOffset: 1,
buff: &bytes.Buffer{},
name: `.shstrtab`, // Mandatory: the table that names sections themselves
buff: &bytes.Buffer{},
})
c.shstrtab = &c.sections[1]
// The first byte in a string table is conventionally expected be \x00, so that you can reference
// null strings with it
c.shstrtab.buff.WriteByte(0)
c.StringTable("")
c.shstrtab.buff.WriteString(c.shstrtab.name)
c.shstrtab.buff.WriteByte(0)
c.shstrtab.name_shstrtabOffset = c.StringTable(c.shstrtab.name)
return c
}
func (c *compiler) StringTable(text string) int {
pos := c.shstrtab.buff.Len()
c.shstrtab.buff.WriteString(text)
c.shstrtab.buff.WriteByte(0)
return pos
}
func (c *compiler) CreateSymbol(name string, class string, offset int64, length int64, global bool) error {
if _, ok := c.symtab[name]; ok {
return fmt.Errorf("Symbol %q already exists", name)
}
// fmt.Printf("--> CreateSymbol(%s)\n", name)
// Find the .symtab section, or create if it does not exist
symtabSec := c.FindOrCreateSection(`.symtab`)
if symtabSec.buff.Len() == 0 {
// First time initialized
// Add a zeroth symtab entry - zero is a sentinel, not a usable entry
symtabSec.buff.Write(make([]byte, 8*3))
}
// New entry index = length / len(entry) = length / 24
nextIndex := symtabSec.buff.Len() / 24
// Add to our fast lookup table
c.symtab[name] = symtabEntry{
ste := symtabEntry{
symtabSectionIndex: nextIndex,
sectionName: c.currentSection.name,
kind: class,
offset: offset,
global: global,
length: length,
kind: class,
offset: offset,
global: global,
length: length,
}
// Find the section index for the section containing this symbol
sectionIndex := -1
for i, _ := range c.sections {
if c.sections[i].name == c.currentSection.name {
sectionIndex = i
break
var srcSectionIdx int = 0
var sttType uint8 = STT_NOTYPE
if class == `.section` {
ste.sectionName = name
srcSectionIdx = len(c.sections) - 1 // The most recent added section
sttType = STT_SECTION
} else if c.currentSection != nil {
ste.sectionName = c.currentSection.name
var ok bool
srcSectionIdx, ok = c.FindSectionIndex(c.currentSection.name)
if !ok {
panic("current section does not exist?")
}
}
if sectionIndex == -1 {
return fmt.Errorf("Current section missing index")
} else {
panic("Symbol is neither a section, nor within a section (?)")
}
// Add to the .symtab section
@ -97,14 +123,42 @@ func (c *compiler) CreateSymbol(name string, class string, offset int64, length
// created, linking it with any other .o files will create a combined .text
// section where all the offsets have shifted
esym := Elf64_Sym{}
esym.st_name = 0 // Default: unnamed
esym.st_info = STT_SECTION | (STB_LOCAL << 4)
esym.st_other = STV_HIDDEN // For this translation unit only
esym.st_shndx = uint16(sectionIndex)
if class == `.section` {
esym.st_name = uint32(c.StringTable(name)) // Write name into public string table
esym.st_info = sttType | (STB_LOCAL << 4)
esym.st_other = STV_DEFAULT
esym.st_shndx = uint16(srcSectionIdx)
} else if global {
esym.st_name = uint32(c.StringTable(name)) // Write name into public string table
esym.st_info = sttType | (STB_GLOBAL << 4)
esym.st_other = STV_DEFAULT
esym.st_shndx = uint16(srcSectionIdx)
} else {
// Private variable for this translation unit
// Needs an entry, but no need to expose the name
esym.st_name = 0 // uint32(c.StringTable(name)) // Write name into public string table // 0 // Default: unnamed (0th entry in our string table is \x00)
esym.st_info = sttType | (STB_LOCAL << 4)
esym.st_other = STV_HIDDEN // For this translation unit only
esym.st_shndx = uint16(srcSectionIdx)
}
fmt.Printf("-->New symbol %q in section %q (sectionidx %v)\n", name, ste.sectionName, srcSectionIdx)
esym.st_size = uint64(length)
err := binary.Write(symtabSec.buff, binary.LittleEndian, &esym)
return err
if err != nil {
return err
}
// Stash in symtabEntry
ste.name_shstrtabOffset = int(esym.st_name)
c.symtab[name] = ste
return nil
}
func (c *compiler) Must(b []byte) {
@ -149,18 +203,24 @@ func (c *compiler) FindOrCreateSection(sectionName string) *section {
// No section with this name. Create it
c.sections = append(c.sections, section{
name: sectionName,
name_shstrtabOffset: c.shstrtab.buff.Len(),
buff: &bytes.Buffer{},
name: sectionName,
buff: &bytes.Buffer{},
})
sec := &c.sections[len(c.sections)-1]
c.shstrtab.buff.WriteString(sectionName)
c.shstrtab.buff.WriteByte(0)
// Create a symbol for it
// This creates a string table entry for us
err := c.CreateSymbol(sectionName, ".section", 0, 0, true)
if err != nil {
panic("CreateSymbol: " + err.Error())
}
return &c.sections[len(c.sections)-1]
sec.name_shstrtabOffset = c.StringTable(sectionName)
return sec
}
func (c *compiler) Reloc(symbolName string, mode ElfRelocationType, addOffset int64) error {
func (c *compiler) Reloc(symbolName string, mode ElfRelocationType) error {
// Find '.rela.{currentsection}', creating it if it does not exist
var relaSec *section = c.FindOrCreateSection(`.rela` + c.currentSection.name)
@ -170,11 +230,19 @@ func (c *compiler) Reloc(symbolName string, mode ElfRelocationType, addOffset in
return fmt.Errorf("Reference to unknown symbol %q", symbolName)
}
// Find the symbol pointing to its parent section
parentSectionSyminfo, ok := c.symtab[syminfo.sectionName]
if !ok {
return fmt.Errorf("Bad parent section")
}
fmt.Printf("-->Relocation %q found in %q (sectionidx %d)\n", symbolName, syminfo.sectionName, parentSectionSyminfo.symtabSectionIndex)
// Add the relocation to the .rela section
rr := Elf64_Rela{}
rr.r_offset = uint64(c.currentSection.buff.Len())
rr.r_info = uint64(syminfo.symtabSectionIndex)<<32 | uint64(mode) // high bits: Index of search symbol in the symtab. low bits: mode type
rr.r_addend = addOffset
rr.r_info = uint64(parentSectionSyminfo.symtabSectionIndex)<<32 | uint64(mode) // high bits: Index of search symbol in the symtab (the source section). low bits: mode type
rr.r_addend = syminfo.offset // Add to the result when relocating (offset within source section)
err := binary.Write(relaSec.buff, binary.LittleEndian, &rr)
if err != nil {
@ -235,7 +303,7 @@ func (c *compiler) Compile(t Token) error {
return fmt.Errorf("variable %q has unknown size class %q", tok.VarName, tok.Sizeclass)
}
err := c.CreateSymbol(tok.VarName, ".var."+tok.Sizeclass, int64(c.currentSection.buff.Len()), position-int64(c.currentSection.buff.Len()), false)
err := c.CreateSymbol(tok.VarName, ".var."+tok.Sizeclass, int64(position), int64(c.currentSection.buff.Len())-position, false)
if err != nil {
return err
}
@ -293,7 +361,7 @@ func (c *compiler) Compile(t Token) error {
panic("mov $var,rax pattern: missing case")
}
err = c.Reloc(tok.Args[0][1:], R_X86_64_32S, 0) // Declare that this is a 32-bit reloc, not a 64-bit one
err = c.Reloc(tok.Args[0][1:], R_X86_64_32S) // Declare that this is a 32-bit reloc, not a 64-bit one
if err != nil {
return fmt.Errorf("mov with relocation: %w", err)
}
@ -313,7 +381,7 @@ func (c *compiler) Compile(t Token) error {
panic("mov rxx,$var pattern: missing case")
}
err = c.Reloc(tok.Args[1][1:], R_X86_64_32S, 0) // Declare that this is a 32-bit reloc, not a 64-bit one
err = c.Reloc(tok.Args[1][1:], R_X86_64_32S) // Declare that this is a 32-bit reloc, not a 64-bit one
if err != nil {
return fmt.Errorf("mov with relocation: %w", err)
}
@ -339,7 +407,7 @@ func (c *compiler) Compile(t Token) error {
panic("mov $var,rxx pattern: missing case")
}
err = c.Reloc(tok.Args[1][2:], R_X86_64_64, 0)
err = c.Reloc(tok.Args[1][2:], R_X86_64_64)
if err != nil {
return fmt.Errorf("mov with relocation: %w", err)
}
@ -428,33 +496,38 @@ func (c *compiler) Finalize(dest io.Writer) error {
shdr := Elf64_Shdr{}
shdr.sh_name = uint32(sec.name_shstrtabOffset)
// Default (unknown section):
// Treat as extra read-only data (.rodata)
shdr.sh_type = 1 // SHT_PROGBITS, program data
shdr.sh_flags = 0x10 // MERGE
shdr.sh_addralign = 1 // Not doing any proper alignment
switch sec.name {
case ".text":
shdr.sh_type = SHT_PROGBITS
shdr.sh_flags = 0x2 | 0x4 | 0x10 // WRITE|ALLOC|MERGE
shdr.sh_flags = SHF_ALLOC | SHF_EXECINSTR
case ".data":
shdr.sh_type = SHT_PROGBITS
shdr.sh_flags = 0x2 | 0x10 // WRITE|MERGE
shdr.sh_flags = SHF_WRITE | SHF_ALLOC
case ".symtab":
shdr.sh_type = SHT_SYMTAB
shdr.sh_flags = 0x10 | 0x20 // MERGE|STRINGS
shdr.sh_info = uint32(len(c.symtab))
shdr.sh_entsize = uint64(sec.buff.Len()) // Number of fixed-sized symtab entries
shdr.sh_link = 1 // The index of the section containing the actual strings. We reuse shstrtab(!?!)
shdr.sh_flags = 0
shdr.sh_info = uint32(len(c.symtab) + 1)
shdr.sh_entsize = 24 // Size in bytes of each entry
shdr.sh_link = 1 // The index of the section containing the actual strings. We reuse shstrtab(!?!)
case ".shstrtab":
shdr.sh_type = SHT_STRTAB
shdr.sh_flags = 0x10 | 0x20 // MERGE|STRINGS
shdr.sh_flags = 0
case ".rodata":
shdr.sh_type = SHT_PROGBITS
shdr.sh_flags = SHF_ALLOC
default:
if strings.HasPrefix(sec.name, ".rela.") {
shdr.sh_type = SHT_RELA
shdr.sh_flags = 0 // ?
shdr.sh_link = uint32(symtabSectionIndex) // The index of the symtab section
shdr.sh_flags = 0 // ?
shdr.sh_link = uint32(symtabSectionIndex)
shdr.sh_entsize = 24 // Size in bytes of each entry
// Find the index of the section for which this relocates. Match by name
srcSectionIdx, ok := c.FindSectionIndex(sec.name[5:])
@ -462,6 +535,8 @@ func (c *compiler) Finalize(dest io.Writer) error {
return fmt.Errorf("Missing parent section for relocation section %q", sec.name)
}
shdr.sh_info = uint32(srcSectionIdx)
} else {
return fmt.Errorf("don't know the right flags to use for section %q", sec.name)
}
}

11
elf.go
View File

@ -57,6 +57,17 @@ const (
SHT_PREINIT_ARRAY = 16
SHT_GROUP = 17
SHT_SYMTAB_SHNDX = 18
SHF_WRITE = 0x1
SHF_ALLOC = 0x2
SHF_EXECINSTR = 0x4
SHF_MERGE = 0x10
SHF_STRINGS = 0x20
SHF_INFO_LINK = 0x40
SHF_LINK_ORDER = 0x80
SHF_OS_NONCONFORMING = 0x100
SHF_GROUP = 0x200
SHF_TLS = 0x400
)
// Elf64_Shdr is the Section header