diff --git a/compile.go b/compile.go index bb58baf..a4fc0c5 100644 --- a/compile.go +++ b/compile.go @@ -125,29 +125,29 @@ func (c *compiler) CreateSymbol(name string, class string, offset int64, length esym := Elf64_Sym{} 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) + 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) + 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) + 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) if err != nil { @@ -155,7 +155,7 @@ func (c *compiler) CreateSymbol(name string, class string, offset int64, length } // Stash in symtabEntry - ste.name_shstrtabOffset = int(esym.st_name) + ste.name_shstrtabOffset = int(esym.St_name) c.symtab[name] = ste return nil @@ -240,9 +240,9 @@ func (c *compiler) Reloc(symbolName string, mode ElfRelocationType) error { // Add the relocation to the .rela section rr := Elf64_Rela{} - rr.r_offset = uint64(c.currentSection.buff.Len()) - 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) + rr.R_offset = uint64(c.currentSection.buff.Len()) + 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 { @@ -455,8 +455,49 @@ func (c *compiler) Compile(t Token) error { func (c *compiler) Finalize(dest io.Writer) error { // Find some well-known section indexes - symtabSectionIndex, _ := c.FindSectionIndex(`.symtab`) - shstrtabSectionIndex, _ := c.FindSectionIndex(`.shstrtab`) + symtabSectionIndex, ok := c.FindSectionIndex(`.symtab`) + if !ok { + return fmt.Errorf("No symbol table present") + } + + shstrtabSectionIndex, ok := c.FindSectionIndex(`.shstrtab`) + if !ok { + return fmt.Errorf("No string table present") + } + + // (Safely) move all global symtab to the end + // Because there may be existing references to global symtab entries (e.g. relocs) + // just duplicate them in place + tmp := c.sections[symtabSectionIndex].buff.Bytes() + extraSymtabContent := bytes.Buffer{} + for i := 0; i < len(tmp); i += 24 { + sym := Elf64_Sym{} + err := binary.Read(bytes.NewReader(tmp[i:i+24]), binary.LittleEndian, &sym) + if err != nil { + return err + } + + if sym.St_info&(STB_GLOBAL<<4) == 0 { + continue // not a global symbol + } + + // Was a global symbol + // Re-add the global symbol at the end + extraSymtabContent.Write(tmp[i : i+24]) + + // Patch the existing symbol + sym.St_name = 0 + sym.St_info &= ^uint8(STB_GLOBAL << 4) + replacement := bytes.Buffer{} + err = binary.Write(&replacement, binary.LittleEndian, &sym) + if err != nil { + return err + } + + copy(tmp[i:i+24], replacement.Bytes()) + } + numLocalSymbols := len(tmp) / 24 + c.sections[symtabSectionIndex].buff.Write(extraSymtabContent.Bytes()) // Write ELF header ehdr := Elf64_Ehdr{} @@ -496,31 +537,35 @@ func (c *compiler) Finalize(dest io.Writer) error { shdr := Elf64_Shdr{} shdr.sh_name = uint32(sec.name_shstrtabOffset) - shdr.sh_addralign = 1 // Not doing any proper alignment switch sec.name { case ".text": shdr.sh_type = SHT_PROGBITS shdr.sh_flags = SHF_ALLOC | SHF_EXECINSTR + shdr.sh_addralign = 16 // Request for final linking case ".data": shdr.sh_type = SHT_PROGBITS shdr.sh_flags = SHF_WRITE | SHF_ALLOC + shdr.sh_addralign = 4 // Request for final linking case ".symtab": shdr.sh_type = SHT_SYMTAB 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(!?!) + shdr.sh_info = uint32(numLocalSymbols) // sh_info points to the first global symbol. Global symbols must go after local symbols + 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_addralign = 8 // Request for final linking case ".shstrtab": shdr.sh_type = SHT_STRTAB shdr.sh_flags = 0 + shdr.sh_addralign = 1 // Not doing any proper alignment case ".rodata": shdr.sh_type = SHT_PROGBITS shdr.sh_flags = SHF_ALLOC + shdr.sh_addralign = 4 // Request for final linking default: if strings.HasPrefix(sec.name, ".rela.") { @@ -535,6 +580,7 @@ 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) + shdr.sh_addralign = 8 // Request for final linking } else { return fmt.Errorf("don't know the right flags to use for section %q", sec.name) } diff --git a/elf.go b/elf.go index dbefd9f..60aace3 100644 --- a/elf.go +++ b/elf.go @@ -105,19 +105,19 @@ const ( // Elf64_Sym is a symbol type Elf64_Sym struct { - st_name uint32 - st_info uint8 - st_other uint8 - st_shndx uint16 - st_value uint64 - st_size uint64 + St_name uint32 + St_info uint8 + St_other uint8 + St_shndx uint16 + St_value uint64 + St_size uint64 } // Elf64_Rela is a relocation with addend type Elf64_Rela struct { - r_offset uint64 - r_info uint64 - r_addend int64 + R_offset uint64 + R_info uint64 + R_addend int64 } // Relocation types