diff --git a/src/gopheros/kernel/hal/multiboot/multiboot.go b/src/gopheros/kernel/hal/multiboot/multiboot.go index 8886907..3f34653 100644 --- a/src/gopheros/kernel/hal/multiboot/multiboot.go +++ b/src/gopheros/kernel/hal/multiboot/multiboot.go @@ -7,9 +7,8 @@ import ( ) var ( - infoData uintptr - cmdLineKV map[string]string - elfSectionList []*ElfSection + infoData uintptr + cmdLineKV map[string]string ) type tagType uint32 @@ -149,7 +148,7 @@ const ( // 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. -type MemRegionVisitor func(entry *MemoryMapEntry) bool +type MemRegionVisitor func(*MemoryMapEntry) bool // MemoryMapEntry describes a memory region entry, namely its physical address, // its length and its type. @@ -230,63 +229,44 @@ const ( 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 - } +// ElfSectionVisitor defies a visitor function that gets invoked by VisitElfSections +// for rach ELF section that belongs to the loaded kernel image. +type ElfSectionVisitor func(name string, flags ElfSectionFlag, address uintptr, size uint64) +// VisitElfSections invokes visitor for each ELF entry that belongs to the +// loaded kernel image. +func VisitElfSections(visitor ElfSectionVisitor) { curPtr, size := findTagByType(tagElfSymbols) 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 ( - strTable = *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{ - Len: int(sectionData[ptrElfSections.strtabSectionIndex].size), - Cap: int(sectionData[ptrElfSections.strtabSectionIndex].size), - Data: uintptr(sectionData[ptrElfSections.strtabSectionIndex].address), - })) + sectionPayload elfSection64 + ptrElfSections = (*elfSections)(unsafe.Pointer(curPtr)) + secPtr = uintptr(unsafe.Pointer(&ptrElfSections.sectionData)) + 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 { continue } // String table entries are C-style NULL-terminated strings - end := secData.nameIndex - for ; strTable[end] != 0; end++ { + end := uintptr(secData.nameIndex) + for ; *(*byte)(unsafe.Pointer(uintptr(strTableSection.address) + end)) != 0; end++ { } - elfSectionList = append(elfSectionList, &ElfSection{ - Name: string(strTable[secData.nameIndex:end]), - Flags: ElfSectionFlag(secData.flags), - Address: uintptr(secData.address), - }) - } + secNameHeader.Len = int(end - uintptr(secData.nameIndex)) + secNameHeader.Data = uintptr(unsafe.Pointer(uintptr(strTableSection.address) + uintptr(secData.nameIndex))) - return elfSectionList + visitor(secName, ElfSectionFlag(secData.flags), uintptr(secData.address), secData.size) + } } // SetInfoPtr updates the internal multiboot information pointer to the given diff --git a/src/gopheros/kernel/hal/multiboot/multiboot_test.go b/src/gopheros/kernel/hal/multiboot/multiboot_test.go index 99217b2..a806573 100644 --- a/src/gopheros/kernel/hal/multiboot/multiboot_test.go +++ b/src/gopheros/kernel/hal/multiboot/multiboot_test.go @@ -196,9 +196,9 @@ func TestGetBootCmdLine(t *testing.T) { func TestGetElfSections(t *testing.T) { 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") - } + }) SetInfoPtr(uintptr(unsafe.Pointer(&multibootInfoTestData[0]))) @@ -209,43 +209,45 @@ func TestGetElfSections(t *testing.T) { 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 { - 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 + VisitElfSections(func(secName string, secFlags ElfSectionFlag, _ uintptr, secSize uint64) { + for secIndex, sec := range expSections { + if secName != sec.secName { + continue } - } - if found == nil { - t.Errorf("[spec %d] missing section %q", specIndex, spec.secName) - continue - } + if secFlags != sec.expFlags { + t.Errorf("[section %d] expected section flags to be: 0x%x; got 0x%x", secIndex, sec.expFlags, secFlags) + return + } - if found.Flags != spec.expFlags { - t.Errorf("[spec %d] expected section flags to be: 0x%x; got 0x%x", specIndex, spec.expFlags, found.Flags) - } - } + if secSize == 0 { + t.Errorf("[section %d] expected section size to be > 0", secIndex) + return + } - // 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") + matchedSections++ + return + } + }) + + if exp := len(expSections); matchedSections != exp { + t.Fatalf("expected to match %d sections; matched %d", exp, matchedSections) } }