diff --git a/src/gopheros/kernel/hal/multiboot/multiboot.go b/src/gopheros/kernel/hal/multiboot/multiboot.go index 77ee31f..8886907 100644 --- a/src/gopheros/kernel/hal/multiboot/multiboot.go +++ b/src/gopheros/kernel/hal/multiboot/multiboot.go @@ -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. diff --git a/src/gopheros/kernel/hal/multiboot/multiboot_test.go b/src/gopheros/kernel/hal/multiboot/multiboot_test.go index 25a8861..99217b2 100644 --- a/src/gopheros/kernel/hal/multiboot/multiboot_test.go +++ b/src/gopheros/kernel/hal/multiboot/multiboot_test.go @@ -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. + } )