xref: /illumos-gate/usr/src/tools/mbh_patch/mbh_patch.c (revision 24da5b34f49324ed742a340010ed5bd3d4e06625)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <stdlib.h>
30 #include <fcntl.h>
31 #include <strings.h>
32 #include <stdio.h>
33 #include <sys/types.h>
34 #include <sys/inttypes.h>
35 #include <sys/elf.h>
36 #include <sys/elf_notes.h>
37 #include <sys/mman.h>
38 #include <sys/stat.h>
39 #include "sys/multiboot.h"
40 
41 static char *pname;
42 static char *fname;
43 static char *image;	/* pointer to the ELF file in memory */
44 
45 #define	ELFSEEK(offset) ((void *)(image + offset))
46 
47 /*
48  * patch the load address / entry address
49  * Find the physical load address of the 1st PT_LOAD segment.
50  * Find the amount that e_entry exceeds that amount.
51  * Now go back and subtract the excess from the p_paddr of the LOAD segment.
52  */
53 static void
54 patch64(Elf64_Ehdr *eh)
55 {
56 	Elf64_Phdr *phdr;
57 	caddr_t allphdrs;
58 	int i;
59 	int m;
60 	int extra;
61 	multiboot_header_t *mbh;
62 
63 	allphdrs = NULL;
64 
65 	if (eh->e_type != ET_EXEC) {
66 		(void) fprintf(stderr, "%s: not ET_EXEC, e_type = 0x%x\n",
67 		    pname, eh->e_type);
68 		exit(1);
69 	}
70 	if (eh->e_phnum == 0 || eh->e_phoff == 0) {
71 		(void) fprintf(stderr, "%s: no program headers\n", pname);
72 		exit(1);
73 	}
74 
75 	/*
76 	 * Get the program headers.
77 	 */
78 	allphdrs = ELFSEEK(eh->e_phoff);
79 	if (allphdrs == NULL) {
80 		(void) fprintf(stderr, "%s: Failed to get %d program hdrs\n",
81 		    pname, eh->e_phnum);
82 		exit(1);
83 	}
84 
85 	/*
86 	 * Look for multiboot header.  It must be 32-bit aligned and
87 	 * completely contained in the 1st 8K of the file.
88 	 */
89 	for (m = 0; m < 8192 - sizeof (multiboot_header_t); m += 4) {
90 		mbh = (void *)(image + m);
91 		if (mbh->magic == MB_HEADER_MAGIC)
92 			break;
93 	}
94 
95 	if (m >= 8192 - sizeof (multiboot_header_t)) {
96 		(void) fprintf(stderr, "%s: Didn't find multiboot header\n",
97 		    pname);
98 		exit(1);
99 	}
100 
101 	/*
102 	 * Find the 1:1 mapped PT_LOAD section
103 	 */
104 	for (i = 0; i < eh->e_phnum; i++) {
105 		/*LINTED [ELF program header alignment]*/
106 		phdr = (Elf64_Phdr *)(allphdrs + eh->e_phentsize * i);
107 
108 		/*
109 		 * Find the low memory 1:1 PT_LOAD section!
110 		 */
111 		if (phdr->p_type != PT_LOAD)
112 			continue;
113 
114 		if (phdr->p_memsz == 0)
115 			continue;
116 
117 		if (phdr->p_paddr != phdr->p_vaddr)
118 			continue;
119 
120 		if (i != 0) {
121 			(void) fprintf(stderr, "%s: identity mapped PT_LOAD"
122 			    " wasn't 1st\n", pname);
123 			exit(1);
124 		}
125 
126 		/*
127 		 * Patch the multiboot header fields to get entire
128 		 * file loaded, with entry aligned at extra.
129 		 */
130 		if (eh->e_entry != phdr->p_paddr) {
131 			(void) fprintf(stderr, "%s: entry != paddr\n", pname);
132 			exit(1);
133 		}
134 
135 		/*
136 		 * Grub uses the MB header for 64 bit loading.
137 		 */
138 		mbh->load_addr = phdr->p_paddr - phdr->p_offset;
139 		mbh->entry_addr = phdr->p_paddr;
140 		mbh->header_addr = mbh->load_addr + m;
141 #ifdef VERBOSE
142 		(void) printf("%s ELF64 MB header patched by 0x%0x "
143 		    "bytes:\n", fname, extra);
144 		(void) printf("\tload_addr now 0x%x\n", mbh->load_addr);
145 		(void) printf("\tentry_addr now 0x%x\n", mbh->entry_addr);
146 		(void) printf("\theader_addr now 0x%x\n", mbh->header_addr);
147 #endif
148 		exit(0);
149 	}
150 
151 	(void) fprintf(stderr, "%s: Didn't find 1:1 mapped PT_LOAD section\n",
152 	    pname);
153 	exit(1);
154 }
155 
156 
157 int
158 main(int argc, char **argv)
159 {
160 	int fd;
161 	uchar_t *ident;
162 	void *hdr = NULL;
163 
164 	/*
165 	 * we expect one argument -- the elf file
166 	 */
167 	if (argc != 2) {
168 		(void) fprintf(stderr, "usage: %s <unix-elf-file>\n", argv[0]);
169 		exit(1);
170 	}
171 
172 	pname = strrchr(argv[0], '/');
173 	if (pname == NULL)
174 		pname = argv[0];
175 	else
176 		++pname;
177 
178 	fname = argv[1];
179 	fd = open(fname, O_RDWR);
180 	if (fd < 0) {
181 		(void) fprintf(stderr, "%s: open(%s, O_RDWR) failed\n",
182 		    pname, fname);
183 		exit(1);
184 	}
185 
186 	/*
187 	 * mmap just the 1st 8K -- since that's where the GRUB
188 	 * multiboot header must be located.
189 	 */
190 	image = mmap(NULL, 8192, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
191 	if (image == MAP_FAILED) {
192 		(void) fprintf(stderr, "%s: mmap() of %s failed\n",
193 		    pname, fname);
194 		exit(1);
195 	}
196 
197 	ident = ELFSEEK(0);
198 	if (ident[EI_MAG0] != ELFMAG0 || ident[EI_MAG1] != ELFMAG1 ||
199 	    ident[EI_MAG2] != ELFMAG2 || ident[EI_MAG3] != ELFMAG3) {
200 		(void) fprintf(stderr, "%s: not an ELF file!\n", pname);
201 		exit(1);
202 	}
203 
204 	if (ident[EI_CLASS] == ELFCLASS64) {
205 		hdr = ELFSEEK(0);
206 		patch64(hdr);
207 	} else if (ident[EI_CLASS] != ELFCLASS32) {
208 		(void) fprintf(stderr, "%s: Unknown ELF class 0x%x\n", pname,
209 		    ident[EI_CLASS]);
210 		exit(1);
211 	}
212 	return (0);
213 }
214