Przeglądaj źródła

[elf] Work around entry point bug in NetBSD kernels

NetBSD kernels are multiboot ELF kernels with an entry point
incorrectly specified as a virtual address rather than a physical
address.

Work around this by looking for the segment that could plausibly
contain the entry point address (interpreted as either a physical or
virtual address), and using that to determine the eventual physical
entry point.

In the event of any ambiguity, precedence is given to interpretation
of the entry point as a physical address.
tags/v0.9.8
Michael Brown 15 lat temu
rodzic
commit
4b8e021161
1 zmienionych plików z 41 dodań i 14 usunięć
  1. 41
    14
      src/image/elf.c

+ 41
- 14
src/image/elf.c Wyświetl plik

@@ -42,11 +42,14 @@ typedef Elf32_Off	Elf_Off;
42 42
  *
43 43
  * @v image		ELF file
44 44
  * @v phdr		ELF program header
45
+ * @v ehdr		ELF executable header
45 46
  * @ret rc		Return status code
46 47
  */
47
-static int elf_load_segment ( struct image *image, Elf_Phdr *phdr ) {
48
+static int elf_load_segment ( struct image *image, Elf_Phdr *phdr,
49
+			      Elf_Ehdr *ehdr ) {
48 50
 	physaddr_t dest;
49 51
 	userptr_t buffer;
52
+	unsigned long e_offset;
50 53
 	int rc;
51 54
 
52 55
 	/* Do nothing for non-PT_LOAD segments */
@@ -55,7 +58,7 @@ static int elf_load_segment ( struct image *image, Elf_Phdr *phdr ) {
55 58
 
56 59
 	/* Check segment lies within image */
57 60
 	if ( ( phdr->p_offset + phdr->p_filesz ) > image->len ) {
58
-		DBG ( "ELF segment outside ELF file\n" );
61
+		DBGC ( image, "ELF %p segment outside image\n", image );
59 62
 		return -ENOEXEC;
60 63
 	}
61 64
 
@@ -67,26 +70,43 @@ static int elf_load_segment ( struct image *image, Elf_Phdr *phdr ) {
67 70
 	if ( ! dest )
68 71
 		dest = phdr->p_vaddr;
69 72
 	if ( ! dest ) {
70
-		DBG ( "ELF segment loads to physical address 0\n" );
73
+		DBGC ( image, "ELF %p segment loads to physical address 0\n",
74
+		       image );
71 75
 		return -ENOEXEC;
72 76
 	}
73 77
 	buffer = phys_to_user ( dest );
74 78
 
75
-	DBG ( "ELF loading segment [%x,%x) to [%x,%x,%x)\n",
76
-	      phdr->p_offset, ( phdr->p_offset + phdr->p_filesz ),
77
-	      phdr->p_paddr, ( phdr->p_paddr + phdr->p_filesz ),
78
-	      ( phdr->p_paddr + phdr->p_memsz ) );
79
+	DBGC ( image, "ELF %p loading segment [%x,%x) to [%x,%x,%x)\n", image,
80
+	       phdr->p_offset, ( phdr->p_offset + phdr->p_filesz ),
81
+	       phdr->p_paddr, ( phdr->p_paddr + phdr->p_filesz ),
82
+	       ( phdr->p_paddr + phdr->p_memsz ) );
79 83
 
80 84
 	/* Verify and prepare segment */
81 85
 	if ( ( rc = prep_segment ( buffer, phdr->p_filesz,
82 86
 				   phdr->p_memsz ) ) != 0 ) {
83
-		DBG ( "ELF could not prepare segment: %s\n", strerror ( rc ) );
87
+		DBGC ( image, "ELF %p could not prepare segment: %s\n",
88
+		       image, strerror ( rc ) );
84 89
 		return rc;
85 90
 	}
86 91
 
87 92
 	/* Copy image to segment */
88 93
 	memcpy_user ( buffer, 0, image->data, phdr->p_offset, phdr->p_filesz );
89 94
 
95
+	/* Set execution address, if it lies within this segment */
96
+	if ( ( e_offset = ( ehdr->e_entry - dest ) ) < phdr->p_filesz ) {
97
+		image->priv.phys = ehdr->e_entry;
98
+		DBGC ( image, "ELF %p found physical entry point at %lx\n",
99
+		       image, image->priv.phys );
100
+	} else if ( ( e_offset = ( ehdr->e_entry - phdr->p_vaddr ) )
101
+		    < phdr->p_filesz ) {
102
+		if ( ! image->priv.phys ) {
103
+			image->priv.phys = ( dest + e_offset );
104
+			DBGC ( image, "ELF %p found virtual entry point at %lx"
105
+			       " (virt %lx)\n", image, image->priv.phys,
106
+			       ( ( unsigned long ) ehdr->e_entry ) );
107
+		}
108
+	}
109
+
90 110
 	return 0;
91 111
 }
92 112
 
@@ -109,25 +129,32 @@ int elf_load ( struct image *image ) {
109 129
 	/* Read ELF header */
110 130
 	copy_from_user ( &ehdr, image->data, 0, sizeof ( ehdr ) );
111 131
 	if ( memcmp ( &ehdr.e_ident[EI_MAG0], ELFMAG, SELFMAG ) != 0 ) {
112
-		DBG ( "Invalid ELF signature\n" );
132
+		DBGC ( image, "ELF %p has invalid signature\n", image );
113 133
 		return -ENOEXEC;
114 134
 	}
115 135
 
136
+	/* Invalidate execution address */
137
+	image->priv.phys = 0;
138
+
116 139
 	/* Read ELF program headers */
117 140
 	for ( phoff = ehdr.e_phoff , phnum = ehdr.e_phnum ; phnum ;
118 141
 	      phoff += ehdr.e_phentsize, phnum-- ) {
119 142
 		if ( phoff > image->len ) {
120
-			DBG ( "ELF program header %d outside ELF image\n",
121
-			      phnum );
143
+			DBGC ( image, "ELF %p program header %d outside "
144
+			       "image\n", image, phnum );
122 145
 			return -ENOEXEC;
123 146
 		}
124 147
 		copy_from_user ( &phdr, image->data, phoff, sizeof ( phdr ) );
125
-		if ( ( rc = elf_load_segment ( image, &phdr ) ) != 0 )
148
+		if ( ( rc = elf_load_segment ( image, &phdr, &ehdr ) ) != 0 )
126 149
 			return rc;
127 150
 	}
128 151
 
129
-	/* Record execution entry point in image private data field */
130
-	image->priv.phys = ehdr.e_entry;
152
+	/* Check for a valid execution address */
153
+	if ( ! image->priv.phys ) {
154
+		DBGC ( image, "ELF %p entry point %lx outside image\n",
155
+		       image, ( ( unsigned long ) ehdr.e_entry ) );
156
+		return -ENOEXEC;
157
+	}
131 158
 
132 159
 	return 0;
133 160
 }

Ładowanie…
Anuluj
Zapisz