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 { type symtabEntry struct {
symtabSectionIndex int // The index of this symbol within the whole symtab
symtabSectionIndex int
name_shstrtabOffset int
sectionName string sectionName string
kind string kind string
@ -41,55 +43,79 @@ func NewCompiler() *compiler {
// First, there's an all-zero entry that is reserved for extended ELF headers // First, there's an all-zero entry that is reserved for extended ELF headers
c.sections = append(c.sections, section{}) c.sections = append(c.sections, section{})
// Real entry: shstrtab
c.sections = append(c.sections, section{ c.sections = append(c.sections, section{
name: `.shstrtab`, // Mandatory: the table that names sections themselves name: `.shstrtab`, // Mandatory: the table that names sections themselves
name_shstrtabOffset: 1, buff: &bytes.Buffer{},
buff: &bytes.Buffer{},
}) })
c.shstrtab = &c.sections[1] c.shstrtab = &c.sections[1]
// The first byte in a string table is conventionally expected be \x00, so that you can reference // The first byte in a string table is conventionally expected be \x00, so that you can reference
// null strings with it // null strings with it
c.shstrtab.buff.WriteByte(0) c.StringTable("")
c.shstrtab.buff.WriteString(c.shstrtab.name) c.shstrtab.name_shstrtabOffset = c.StringTable(c.shstrtab.name)
c.shstrtab.buff.WriteByte(0)
return c 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 { func (c *compiler) CreateSymbol(name string, class string, offset int64, length int64, global bool) error {
if _, ok := c.symtab[name]; ok { if _, ok := c.symtab[name]; ok {
return fmt.Errorf("Symbol %q already exists", name) 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 // Find the .symtab section, or create if it does not exist
symtabSec := c.FindOrCreateSection(`.symtab`) 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 // New entry index = length / len(entry) = length / 24
nextIndex := symtabSec.buff.Len() / 24 nextIndex := symtabSec.buff.Len() / 24
// Add to our fast lookup table // Add to our fast lookup table
c.symtab[name] = symtabEntry{ ste := symtabEntry{
symtabSectionIndex: nextIndex, symtabSectionIndex: nextIndex,
kind: class,
sectionName: c.currentSection.name, offset: offset,
kind: class, global: global,
offset: offset, length: length,
global: global,
length: length,
} }
// Find the section index for the section containing this symbol // Find the section index for the section containing this symbol
sectionIndex := -1 var srcSectionIdx int = 0
for i, _ := range c.sections { var sttType uint8 = STT_NOTYPE
if c.sections[i].name == c.currentSection.name {
sectionIndex = i if class == `.section` {
break 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 { } else {
return fmt.Errorf("Current section missing index") panic("Symbol is neither a section, nor within a section (?)")
} }
// Add to the .symtab 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 // created, linking it with any other .o files will create a combined .text
// section where all the offsets have shifted // section where all the offsets have shifted
esym := Elf64_Sym{} esym := Elf64_Sym{}
esym.st_name = 0 // Default: unnamed
esym.st_info = STT_SECTION | (STB_LOCAL << 4) if class == `.section` {
esym.st_other = STV_HIDDEN // For this translation unit only esym.st_name = uint32(c.StringTable(name)) // Write name into public string table
esym.st_shndx = uint16(sectionIndex) 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) esym.st_size = uint64(length)
err := binary.Write(symtabSec.buff, binary.LittleEndian, &esym) 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) { func (c *compiler) Must(b []byte) {
@ -149,18 +203,24 @@ func (c *compiler) FindOrCreateSection(sectionName string) *section {
// No section with this name. Create it // No section with this name. Create it
c.sections = append(c.sections, section{ c.sections = append(c.sections, section{
name: sectionName, name: sectionName,
name_shstrtabOffset: c.shstrtab.buff.Len(), buff: &bytes.Buffer{},
buff: &bytes.Buffer{},
}) })
sec := &c.sections[len(c.sections)-1]
c.shstrtab.buff.WriteString(sectionName) // Create a symbol for it
c.shstrtab.buff.WriteByte(0) // 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 // Find '.rela.{currentsection}', creating it if it does not exist
var relaSec *section = c.FindOrCreateSection(`.rela` + c.currentSection.name) 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) 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 // Add the relocation to the .rela section
rr := Elf64_Rela{} rr := Elf64_Rela{}
rr.r_offset = uint64(c.currentSection.buff.Len()) 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_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 = addOffset rr.r_addend = syminfo.offset // Add to the result when relocating (offset within source section)
err := binary.Write(relaSec.buff, binary.LittleEndian, &rr) err := binary.Write(relaSec.buff, binary.LittleEndian, &rr)
if err != nil { 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) 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 { if err != nil {
return err return err
} }
@ -293,7 +361,7 @@ func (c *compiler) Compile(t Token) error {
panic("mov $var,rax pattern: missing case") 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 { if err != nil {
return fmt.Errorf("mov with relocation: %w", err) 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") 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 { if err != nil {
return fmt.Errorf("mov with relocation: %w", err) 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") 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 { if err != nil {
return fmt.Errorf("mov with relocation: %w", err) return fmt.Errorf("mov with relocation: %w", err)
} }
@ -428,33 +496,38 @@ func (c *compiler) Finalize(dest io.Writer) error {
shdr := Elf64_Shdr{} shdr := Elf64_Shdr{}
shdr.sh_name = uint32(sec.name_shstrtabOffset) shdr.sh_name = uint32(sec.name_shstrtabOffset)
shdr.sh_addralign = 1 // Not doing any proper alignment
// Default (unknown section):
// Treat as extra read-only data (.rodata)
shdr.sh_type = 1 // SHT_PROGBITS, program data
shdr.sh_flags = 0x10 // MERGE
switch sec.name { switch sec.name {
case ".text": case ".text":
shdr.sh_type = SHT_PROGBITS shdr.sh_type = SHT_PROGBITS
shdr.sh_flags = 0x2 | 0x4 | 0x10 // WRITE|ALLOC|MERGE shdr.sh_flags = SHF_ALLOC | SHF_EXECINSTR
case ".data": case ".data":
shdr.sh_type = SHT_PROGBITS shdr.sh_type = SHT_PROGBITS
shdr.sh_flags = 0x2 | 0x10 // WRITE|MERGE shdr.sh_flags = SHF_WRITE | SHF_ALLOC
case ".symtab": case ".symtab":
shdr.sh_type = SHT_SYMTAB shdr.sh_type = SHT_SYMTAB
shdr.sh_flags = 0x10 | 0x20 // MERGE|STRINGS shdr.sh_flags = 0
shdr.sh_info = uint32(len(c.symtab)) shdr.sh_info = uint32(len(c.symtab) + 1)
shdr.sh_entsize = uint64(sec.buff.Len()) // Number of fixed-sized symtab entries 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(!?!) shdr.sh_link = 1 // The index of the section containing the actual strings. We reuse shstrtab(!?!)
case ".shstrtab": case ".shstrtab":
shdr.sh_type = SHT_STRTAB 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: default:
if strings.HasPrefix(sec.name, ".rela.") { if strings.HasPrefix(sec.name, ".rela.") {
shdr.sh_type = SHT_RELA shdr.sh_type = SHT_RELA
shdr.sh_flags = 0 // ? shdr.sh_flags = 0 // ?
shdr.sh_link = uint32(symtabSectionIndex) // The index of the symtab section 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 // Find the index of the section for which this relocates. Match by name
srcSectionIdx, ok := c.FindSectionIndex(sec.name[5:]) 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) return fmt.Errorf("Missing parent section for relocation section %q", sec.name)
} }
shdr.sh_info = uint32(srcSectionIdx) 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_PREINIT_ARRAY = 16
SHT_GROUP = 17 SHT_GROUP = 17
SHT_SYMTAB_SHNDX = 18 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 // Elf64_Shdr is the Section header