1
0
mirror of https://github.com/taigrr/gopher-os synced 2025-01-18 04:43:13 -08:00

Support parsing of 64-bit ELF section tags off from the multiboot data

This commit is contained in:
Achilleas Anagnostopoulos 2017-07-10 08:18:14 +01:00
parent af9613e336
commit a79be7c268
2 changed files with 192 additions and 5 deletions

View File

@ -6,6 +6,12 @@ import (
"unsafe"
)
var (
infoData uintptr
cmdLineKV map[string]string
elfSectionList []*ElfSection
)
type tagType uint32
// nolint
@ -140,11 +146,6 @@ const (
memUnknown
)
var (
infoData uintptr
cmdLineKV map[string]string
)
// MemRegionVisitor defies a visitor function that gets invoked by VisitMemRegions
// for each memory region provided by the boot loader. The visitor must return true
// to continue or false to abort the scan.
@ -179,6 +180,115 @@ func (t MemoryEntryType) String() string {
}
}
type elfSections struct {
numSections uint16
sectionSize uint32
strtabSectionIndex uint32
sectionData [0]byte
}
/*
type elfSection32 struct {
nameIndex uint32
sectionType uint32
flags uint32
address uint32
offset uint32
size uint32
link uint32
info uint32
addrAlign uint32
entSize uint32
}
*/
type elfSection64 struct {
nameIndex uint32
sectionType uint32
flags uint64
address uint64
offset uint64
size uint64
link uint32
info uint32
addrAlign uint64
entSize uint64
}
// ElfSectionFlag defines an OR-able flag associated with an ElfSection.
type ElfSectionFlag uint32
const (
// ElfSectionWritable marks the section as writable.
ElfSectionWritable ElfSectionFlag = 1 << iota
// ElfSectionAllocated means that the section is allocated in memory
// when the image is loaded (e.g .bss sections)
ElfSectionAllocated
// ElfSectionExecutable marks the section as executable.
ElfSectionExecutable
)
// ElfSection deefines the name, flags and virtual address of an ELF section
// which is part of the kernel image.
type ElfSection struct {
// The section name.
Name string
// The list of flags associated with this section
Flags ElfSectionFlag
// The virtual address of this section.
Address uintptr
}
// GetElfSections returns a slice of ElfSections for the loaded kernel image.
func GetElfSections() []*ElfSection {
if elfSectionList != nil {
return elfSectionList
}
curPtr, size := findTagByType(tagElfSymbols)
if size == 0 {
return nil
}
ptrElfSections := (*elfSections)(unsafe.Pointer(curPtr))
sectionData := *(*[]elfSection64)(unsafe.Pointer(&reflect.SliceHeader{
Len: int(ptrElfSections.numSections),
Cap: int(ptrElfSections.numSections),
Data: uintptr(unsafe.Pointer(&ptrElfSections.sectionData)),
}))
var (
strTable = *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{
Len: int(sectionData[ptrElfSections.strtabSectionIndex].size),
Cap: int(sectionData[ptrElfSections.strtabSectionIndex].size),
Data: uintptr(sectionData[ptrElfSections.strtabSectionIndex].address),
}))
)
for _, secData := range sectionData {
if secData.size == 0 {
continue
}
// String table entries are C-style NULL-terminated strings
end := secData.nameIndex
for ; strTable[end] != 0; end++ {
}
elfSectionList = append(elfSectionList, &ElfSection{
Name: string(strTable[secData.nameIndex:end]),
Flags: ElfSectionFlag(secData.flags),
Address: uintptr(secData.address),
})
}
return elfSectionList
}
// SetInfoPtr updates the internal multiboot information pointer to the given
// value. This function must be invoked before invoking any other function
// exported by this package.

View File

@ -1,6 +1,8 @@
package multiboot
import (
"bytes"
"encoding/binary"
"reflect"
"testing"
"unsafe"
@ -191,6 +193,62 @@ func TestGetBootCmdLine(t *testing.T) {
}
}
func TestGetElfSections(t *testing.T) {
SetInfoPtr(uintptr(unsafe.Pointer(&emptyInfoData[0])))
if GetElfSections() != nil {
t.Fatalf("expected GetElfSections() to return nil when no elf sections tag is present")
}
SetInfoPtr(uintptr(unsafe.Pointer(&multibootInfoTestData[0])))
// Patch the strtab address to point to out mock data
var buf bytes.Buffer
binary.Write(&buf, binary.LittleEndian, uint64(uintptr(unsafe.Pointer(&mockStrTable[0]))))
for i, b := range buf.Bytes() {
multibootInfoTestData[1660+i] = b
}
sections := GetElfSections()
specs := []struct {
secName string
expFlags ElfSectionFlag
}{
{".text", ElfSectionAllocated | ElfSectionExecutable},
{".bss", ElfSectionAllocated | ElfSectionWritable},
{".noptrbss", ElfSectionAllocated | ElfSectionWritable},
{".data", ElfSectionAllocated | ElfSectionWritable},
{".rodata", ElfSectionAllocated},
{".strtab", 0},
}
for specIndex, spec := range specs {
var found *ElfSection
for _, sec := range sections {
if sec.Name == spec.secName {
found = sec
break
}
}
if found == nil {
t.Errorf("[spec %d] missing section %q", specIndex, spec.secName)
continue
}
if found.Flags != spec.expFlags {
t.Errorf("[spec %d] expected section flags to be: 0x%x; got 0x%x", specIndex, spec.expFlags, found.Flags)
}
}
// Second call should return the memoized data
sections[0].Name = "foo"
if sections2 := GetElfSections(); !reflect.DeepEqual(sections2, sections) {
t.Error("expected second call to GetElfSections() to return the memoized section list")
}
}
var (
emptyInfoData = []byte{
0, 0, 0, 0, // size
@ -374,4 +432,23 @@ var (
0x59, 0x42, 0x4f, 0x43, 0x48, 0x53, 0x20, 0x00, 0xdc, 0x18, 0xfe, 0x07, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
}
mockStrTable = []byte{
0x00, 0x2e, 0x73, 0x79, 0x6d, 0x74, 0x61, 0x62, 0x00, 0x2e, 0x73, 0x74, 0x72, 0x74, 0x61, 0x62, // ..symtab..strtab
0x00, 0x2e, 0x73, 0x68, 0x73, 0x74, 0x72, 0x74, 0x61, 0x62, 0x00, 0x2e, 0x74, 0x65, 0x78, 0x74, // ..shstrtab..text
0x00, 0x2e, 0x72, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x00, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x6c, 0x69, // ..rodata..typeli
0x6e, 0x6b, 0x00, 0x2e, 0x69, 0x74, 0x61, 0x62, 0x6c, 0x69, 0x6e, 0x6b, 0x00, 0x2e, 0x67, 0x6f, // nk..itablink..go
0x70, 0x63, 0x6c, 0x6e, 0x74, 0x61, 0x62, 0x00, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x00, 0x2e, 0x6e, // pclntab..data..n
0x6f, 0x70, 0x74, 0x72, 0x64, 0x61, 0x74, 0x61, 0x00, 0x2e, 0x62, 0x73, 0x73, 0x00, 0x2e, 0x6e, // optrdata..bss..n
0x6f, 0x70, 0x74, 0x72, 0x62, 0x73, 0x73, 0x00, 0x2e, 0x67, 0x6f, 0x72, 0x65, 0x64, 0x69, 0x72, // optrbss..goredir
0x65, 0x63, 0x74, 0x73, 0x74, 0x62, 0x6c, 0x00, 0x2e, 0x6e, 0x6f, 0x74, 0x65, 0x2e, 0x67, 0x6f, // ectstbl..note.go
0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x69, 0x64, 0x00, 0x2e, 0x74, 0x62, 0x73, 0x73, 0x00, 0x2e, // .buildid..tbss..
0x64, 0x65, 0x62, 0x75, 0x67, 0x5f, 0x61, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x00, 0x2e, 0x64, // debug_aranges..d
0x65, 0x62, 0x75, 0x67, 0x5f, 0x70, 0x75, 0x62, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x00, 0x2e, 0x64, // ebug_pubnames..d
0x65, 0x62, 0x75, 0x67, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x00, 0x2e, 0x64, 0x65, 0x62, 0x75, 0x67, // ebug_info..debug
0x5f, 0x61, 0x62, 0x62, 0x72, 0x65, 0x76, 0x00, 0x2e, 0x64, 0x65, 0x62, 0x75, 0x67, 0x5f, 0x6c, // _abbrev..debug_l
0x69, 0x6e, 0x65, 0x00, 0x2e, 0x64, 0x65, 0x62, 0x75, 0x67, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, // ine..debug_frame
0x00, 0x2e, 0x64, 0x65, 0x62, 0x75, 0x67, 0x5f, 0x6c, 0x6f, 0x63, 0x00, 0x2e, 0x64, 0x65, 0x62, // ..debug_loc..deb
0x75, 0x67, 0x5f, 0x70, 0x75, 0x62, 0x74, 0x79, 0x70, 0x65, 0x73, 0x00, // ug_pubtypes.
}
)