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

Switch to a visitor-based no-alloc implementation for fetching ELF sections

This change is required as the code to create a new PDT should execute
before the Go allocator is bootstrapped.
This commit is contained in:
Achilleas Anagnostopoulos 2017-07-12 23:38:01 +01:00
parent fdd5611220
commit a2d58f8949
2 changed files with 61 additions and 79 deletions

View File

@ -7,9 +7,8 @@ import (
) )
var ( var (
infoData uintptr infoData uintptr
cmdLineKV map[string]string cmdLineKV map[string]string
elfSectionList []*ElfSection
) )
type tagType uint32 type tagType uint32
@ -149,7 +148,7 @@ const (
// MemRegionVisitor defies a visitor function that gets invoked by VisitMemRegions // MemRegionVisitor defies a visitor function that gets invoked by VisitMemRegions
// for each memory region provided by the boot loader. The visitor must return true // for each memory region provided by the boot loader. The visitor must return true
// to continue or false to abort the scan. // to continue or false to abort the scan.
type MemRegionVisitor func(entry *MemoryMapEntry) bool type MemRegionVisitor func(*MemoryMapEntry) bool
// MemoryMapEntry describes a memory region entry, namely its physical address, // MemoryMapEntry describes a memory region entry, namely its physical address,
// its length and its type. // its length and its type.
@ -230,63 +229,44 @@ const (
ElfSectionExecutable ElfSectionExecutable
) )
// ElfSection deefines the name, flags and virtual address of an ELF section // ElfSectionVisitor defies a visitor function that gets invoked by VisitElfSections
// which is part of the kernel image. // for rach ELF section that belongs to the loaded kernel image.
type ElfSection struct { type ElfSectionVisitor func(name string, flags ElfSectionFlag, address uintptr, size uint64)
// 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
}
// VisitElfSections invokes visitor for each ELF entry that belongs to the
// loaded kernel image.
func VisitElfSections(visitor ElfSectionVisitor) {
curPtr, size := findTagByType(tagElfSymbols) curPtr, size := findTagByType(tagElfSymbols)
if size == 0 { if size == 0 {
return nil return
} }
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 ( var (
strTable = *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{ sectionPayload elfSection64
Len: int(sectionData[ptrElfSections.strtabSectionIndex].size), ptrElfSections = (*elfSections)(unsafe.Pointer(curPtr))
Cap: int(sectionData[ptrElfSections.strtabSectionIndex].size), secPtr = uintptr(unsafe.Pointer(&ptrElfSections.sectionData))
Data: uintptr(sectionData[ptrElfSections.strtabSectionIndex].address), sizeofSection = unsafe.Sizeof(sectionPayload)
})) strTableSection = (*elfSection64)(unsafe.Pointer(secPtr + uintptr(ptrElfSections.strtabSectionIndex)*sizeofSection))
secName string
secNameHeader = (*reflect.StringHeader)(unsafe.Pointer(&secName))
) )
for _, secData := range sectionData { for secIndex := uint16(0); secIndex < ptrElfSections.numSections; secIndex, secPtr = secIndex+1, secPtr+sizeofSection {
secData := (*elfSection64)(unsafe.Pointer(secPtr))
if secData.size == 0 { if secData.size == 0 {
continue continue
} }
// String table entries are C-style NULL-terminated strings // String table entries are C-style NULL-terminated strings
end := secData.nameIndex end := uintptr(secData.nameIndex)
for ; strTable[end] != 0; end++ { for ; *(*byte)(unsafe.Pointer(uintptr(strTableSection.address) + end)) != 0; end++ {
} }
elfSectionList = append(elfSectionList, &ElfSection{ secNameHeader.Len = int(end - uintptr(secData.nameIndex))
Name: string(strTable[secData.nameIndex:end]), secNameHeader.Data = uintptr(unsafe.Pointer(uintptr(strTableSection.address) + uintptr(secData.nameIndex)))
Flags: ElfSectionFlag(secData.flags),
Address: uintptr(secData.address),
})
}
return elfSectionList visitor(secName, ElfSectionFlag(secData.flags), uintptr(secData.address), secData.size)
}
} }
// SetInfoPtr updates the internal multiboot information pointer to the given // SetInfoPtr updates the internal multiboot information pointer to the given

View File

@ -196,9 +196,9 @@ func TestGetBootCmdLine(t *testing.T) {
func TestGetElfSections(t *testing.T) { func TestGetElfSections(t *testing.T) {
SetInfoPtr(uintptr(unsafe.Pointer(&emptyInfoData[0]))) SetInfoPtr(uintptr(unsafe.Pointer(&emptyInfoData[0])))
if GetElfSections() != nil { VisitElfSections(func(_ string, _ ElfSectionFlag, _ uintptr, _ uint64) {
t.Fatalf("expected GetElfSections() to return nil when no elf sections tag is present") t.Fatalf("expected GetElfSections() to return nil when no elf sections tag is present")
} })
SetInfoPtr(uintptr(unsafe.Pointer(&multibootInfoTestData[0]))) SetInfoPtr(uintptr(unsafe.Pointer(&multibootInfoTestData[0])))
@ -209,43 +209,45 @@ func TestGetElfSections(t *testing.T) {
multibootInfoTestData[1660+i] = b multibootInfoTestData[1660+i] = b
} }
sections := GetElfSections() // There are more sections in the test data but we will only focus on these ones for the test
var (
expSections = []struct {
secName string
expFlags ElfSectionFlag
}{
{".text", ElfSectionAllocated | ElfSectionExecutable},
{".bss", ElfSectionAllocated | ElfSectionWritable},
{".noptrbss", ElfSectionAllocated | ElfSectionWritable},
{".data", ElfSectionAllocated | ElfSectionWritable},
{".rodata", ElfSectionAllocated},
{".strtab", 0},
}
matchedSections int
)
specs := []struct { VisitElfSections(func(secName string, secFlags ElfSectionFlag, _ uintptr, secSize uint64) {
secName string for secIndex, sec := range expSections {
expFlags ElfSectionFlag if secName != sec.secName {
}{ continue
{".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 { if secFlags != sec.expFlags {
t.Errorf("[spec %d] missing section %q", specIndex, spec.secName) t.Errorf("[section %d] expected section flags to be: 0x%x; got 0x%x", secIndex, sec.expFlags, secFlags)
continue return
} }
if found.Flags != spec.expFlags { if secSize == 0 {
t.Errorf("[spec %d] expected section flags to be: 0x%x; got 0x%x", specIndex, spec.expFlags, found.Flags) t.Errorf("[section %d] expected section size to be > 0", secIndex)
} return
} }
// Second call should return the memoized data matchedSections++
sections[0].Name = "foo" return
if sections2 := GetElfSections(); !reflect.DeepEqual(sections2, sections) { }
t.Error("expected second call to GetElfSections() to return the memoized section list") })
if exp := len(expSections); matchedSections != exp {
t.Fatalf("expected to match %d sections; matched %d", exp, matchedSections)
} }
} }