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:
		
							parent
							
								
									fdd5611220
								
							
						
					
					
						commit
						a2d58f8949
					
				| @ -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 | ||||||
|  | |||||||
| @ -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) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user