VirtualBox

source: kStuff/hacks/os2ldr/os2ldr_patcher.cpp

Last change on this file was 45, checked in by bird, 13 years ago

os2ldr: Hack around broken PCI BIOS in recent AMI UEFI firmwares.

File size: 9.0 KB
Line 
1/* $Id: $ */
2/** @file
3 * Bird's OS2LDR patch utility.
4 */
5
6/*
7 * Copyright (c) 2011 knut st. osmundsen <bird-kBuild-spamx@anduin.net>
8 * All Rights Reserved.
9 *
10 */
11
12
13/*******************************************************************************
14* Header Files *
15*******************************************************************************/
16#include <errno.h>
17#include <stdio.h>
18#include <stdarg.h>
19#include <stdlib.h>
20#include <string.h>
21#include <sys/stat.h>
22
23
24static int failed(const char *pszFormat, ...)
25{
26 va_list va;
27 va_start(va, pszFormat);
28 vfprintf(stderr, pszFormat, va);
29 va_end(va);
30 return 1;
31}
32
33
34static char *readfile(const char *pszFile, struct stat const *pSt)
35{
36 char *pb = (char *)calloc(pSt->st_size + 16, 1);
37 if (pb)
38 {
39 errno = 0;
40 FILE *pFile = fopen(pszFile, "rb");
41 if (pFile)
42 {
43 errno = 0;
44 int rc = fread(pb, (unsigned)pSt->st_size, 1, pFile);
45 fclose(pFile);
46 if (rc == 1)
47 return pb;
48 failed("error: fread failed on '%s': %s\n", pszFile, strerror(errno));
49 }
50 else
51 failed("error: failed to open '%s': %s\n", pszFile, strerror(errno));
52
53 free(pb);
54 }
55 else
56 failed("error: out of memory!\n");
57 return NULL;
58
59}
60
61
62static void putu16(char *pb, unsigned uVal)
63{
64 pb[0] = uVal & 0xff;
65 pb[1] = uVal >> 8;
66}
67
68
69static unsigned getu16(char const *pb)
70{
71 unsigned u = *(unsigned char const *)(pb + 1);
72 u <<= 8;
73 u |= *(unsigned char const *)pb;
74 return u;
75}
76
77
78static int validatepatch(char const *pbPatch, unsigned cbPatch, char const *pbInput, unsigned cbInput)
79{
80 if (strncmp(pbPatch, "bird", 4))
81 return failed("error: Bad patch signature!\n");
82
83 unsigned uOrg = getu16(&pbPatch[4]);
84 if (uOrg <= cbInput)
85 return failed("error: The patch origin is lower or equal to the OS2LDR size: %#u <= %#u\n", uOrg, cbInput);
86 if (uOrg > 0xe0000)
87 return failed("error: The patch origin is too high: %#x\n", uOrg);
88
89 unsigned cbTotal = uOrg + cbPatch;
90 cbTotal += 0x00001fff; /* Arena + alignment */
91 cbTotal &= 0xfffff000;
92 if (cbTotal > 0xf000)
93 return failed("error: The patched OS2LDR is too large: %#x (%u), max 0xf000\n", cbTotal, cbTotal);
94
95 /*
96 * Verify the size patches.
97 */
98 const char *pb = &pbPatch[6];
99 if (strncmp(pb, "size", 4))
100 return failed("error: Bad patch header (size)!\n");
101 pb += 4;
102 unsigned cSizes = getu16(pb);
103 pb += 2;
104
105 while (cSizes-- > 0)
106 {
107 unsigned offSize = getu16(pb);
108 pb += 2;
109 unsigned uOrgSize = getu16(pb);
110 pb += 2 + 2;
111 if (offSize + 2 > cbInput)
112 return failed("error: Size patch at %#x is beyond the end of the input file (%#x)!\n", offSize, cbInput);
113
114 unsigned u = getu16(pbInput + offSize);
115 if (u != uOrgSize)
116 return failed("error: Size patch at %#x states a different value (%#x) than the input (%#x)!\n", offSize, uOrgSize, u);
117 }
118
119 /*
120 * Verify the jmp patches.
121 */
122 if (strncmp(pb, "jmps", 4))
123 return failed("error: Bad patch header (jmps)!\n");
124 unsigned cJmps = getu16(pb + 4);
125 pb += 6;
126
127 for (unsigned iJmp = 0; iJmp < cJmps; iJmp++)
128 {
129 unsigned offJmp = getu16(pb + 0);
130 unsigned offDst = getu16(pb + 2);
131 unsigned cbLeadIn = getu16(pb + 4);
132 pb += 6;
133 if (cbLeadIn >= 16)
134 return failed("error: Jmp patch #%u at %#x: too many lead in bytes: %#x\n", iJmp, offJmp, cbLeadIn);
135 if (offJmp + cbLeadIn > cbInput || offJmp >= cbInput)
136 return failed("error: Jmp patch #%u at %#x is beyond the end of the input file (%#x)!\n", iJmp, offJmp, cbInput);
137 if (offDst < uOrg || offDst >= uOrg + cbPatch)
138 return failed("error: Jmp patch #%u at %#x destination is out of range: %#x\n", iJmp, offJmp, offDst);
139 if (memcmp(pbInput + offJmp, pb, cbLeadIn))
140 {
141 failed("error: Jmp patch #%u at %#x states other lead in bytes than the input\n", iJmp, offJmp);
142 for (unsigned off = 0; off < cbLeadIn; off++)
143 fprintf(stderr, " %#x+%u: %02x %02x\n", offJmp, off, (unsigned char)pb[off], (unsigned char)pbInput[offJmp + off]);
144 return 1;
145 }
146 pb += cbLeadIn + 3;
147 }
148
149 /* The end */
150 if (strncmp(pb, "end", 4))
151 return failed("error: Bad patch header (end)!\n");
152
153 return 0;
154}
155
156
157static int linkpatch(char *pbOutput, unsigned *pcbOutput, char const *pbInput, unsigned cbInput,
158 char const *pbPatch, unsigned cbPatch)
159{
160 if (strncmp(pbPatch, "bird", 4))
161 return failed("error: Doofus - bird!\n");
162
163 /* Figure the size. */
164 unsigned const offPatch = getu16(&pbPatch[4]);
165 printf("offPatch=%#x\n", offPatch);
166 *pcbOutput = cbPatch + offPatch;
167
168 /* Link the two input binaries. */
169 memset(pbOutput, 0, *pcbOutput);
170 memcpy(pbOutput, pbInput, cbInput);
171 memcpy(pbOutput + offPatch, pbPatch, cbPatch);
172
173 /*
174 * Apply size patches
175 */
176 const char *pb = pbPatch + 6;
177 if (strncmp(pb, "size", 4))
178 return failed("error: Doofus - size!\n");
179 unsigned cSizes = getu16(pb + 4);
180 pb += 6;
181
182 while (cSizes-- > 0)
183 {
184 unsigned offSize = getu16(pb);
185 signed iDelta = getu16(pb + 4);
186 pb += 6;
187 putu16(pbOutput + offSize, *pcbOutput + iDelta);
188 }
189
190 /*
191 * Apply the jmp patches.
192 */
193 if (strncmp(pb, "jmps", 4))
194 return failed("error: Doofus - jmps!\n");
195 unsigned cJmps = getu16(pb + 4);
196 pb += 6;
197
198 while (cJmps-- > 0)
199 {
200 unsigned offJmp = getu16(pb + 0);
201 unsigned offDst = getu16(pb + 2);
202 unsigned cbLeadIn = getu16(pb + 4);
203 pb += 6 + cbLeadIn + 3;
204#if 0 /* debug */
205 pbOutput[offJmp++] = (char)0xf4; /* hlt */
206 pbOutput[offJmp++] = (char)0xf4; /* hlt */
207 pbOutput[offJmp++] = (char)0xf4; /* hlt */
208 pbOutput[offJmp++] = (char)0xf4; /* hlt */
209 pbOutput[offJmp++] = (char)0xf4; /* hlt */
210 pbOutput[offJmp++] = (char)0xf4; /* hlt */
211 pbOutput[offJmp++] = (char)0xeb; /* jmp $-6 */
212 pbOutput[offJmp++] = (char)-6;
213#endif
214 pbOutput[offJmp++] = (char)0x90; /* NOP */
215 pbOutput[offJmp++] = (char)0x90; /* NOP */
216 pbOutput[offJmp++] = (char)0x90; /* NOP */
217 pbOutput[offJmp++] = (char)0xe9; /* jmp rel16 */
218 putu16(&pbOutput[offJmp], offDst - offJmp - 2);
219 }
220
221 /* The end */
222 if (strncmp(pb, "end", 4))
223 return failed("error: Doofus - end!\n");
224 return 0;
225}
226
227int main(int argc, char **argv)
228{
229 if (argc != 4)
230 return failed("syntax error: %s <os2ldr> <patch-binary> <output>\nargc=%d\n", argv[0], argc);
231
232 const char *pszInput = argv[1];
233 const char *pszPatch = argv[2];
234 const char *pszOutput = argv[3];
235
236 /*
237 * Check file existences and get the sizes of the the inputs.
238 */
239 struct stat StInput, StPatch;
240 if (stat(pszOutput, &StInput) == 0)
241 return failed("error: The output file '%s' exists already.\n", pszOutput);
242 if (errno != ENOENT)
243 return failed("error: Expected errno=%d (ENOENT), got %d: %s\n", ENOENT, errno, strerror(errno));
244
245 if (stat(pszInput, &StInput) != 0)
246 return failed("error: stat(%s) -> %d: %s\n", pszInput, errno, strerror(errno));
247 if (stat(pszPatch, &StPatch) != 0)
248 return failed("error: stat(%s) -> %d: %s\n", pszPatch, errno, strerror(errno));
249
250 if (StInput.st_size >= 0xe000)
251 return failed("error: %s is too big! %u bytes\n", pszInput, (unsigned)StInput.st_size);
252 if (StPatch.st_size >= 0x2000)
253 return failed("error: %s is too big! %u bytes\n", pszOutput, (unsigned)StInput.st_size);
254 if (StInput.st_size + StPatch.st_size >= 0xf000)
255 return failed("error: the input files are too big! %u bytes\n", (unsigned)(StInput.st_size + StPatch.st_size));
256
257 /*
258 * Read the input files.
259 */
260 char *pbInput = readfile(pszInput, &StInput);
261 if (!pbInput)
262 return 1;
263
264 char *pbPatch = readfile(pszPatch, &StPatch);
265 if (!pbInput)
266 return 1;
267
268 /*
269 * Validate the patch and construct the output file.
270 */
271 int rc = validatepatch(pbPatch, (unsigned)StPatch.st_size, pbInput, (unsigned)StInput.st_size);
272 if (rc)
273 return rc;
274
275 char *pbOutput = (char *)malloc(0x10000);
276 if (!pbOutput)
277 return failed("error: out of memory\n");
278
279 unsigned cbOutput = 0;
280 rc = linkpatch(pbOutput, &cbOutput, pbInput, (unsigned)StInput.st_size, pbPatch, (unsigned)StPatch.st_size);
281 if (rc)
282 return rc;
283
284 /*
285 * Write it to the output file.
286 */
287 errno = 0;
288 FILE *pFile = fopen(pszOutput, "wb");
289 if (!pFile)
290 return failed("error: Failed to create output file '%s': %s\n", pszOutput, strerror(errno));
291 rc = fwrite(pbOutput, cbOutput, 1, pFile);
292 if (rc != 1 || fclose(pFile) != 0)
293 return failed("error: Error writing output file: %s\n", strerror(errno));
294
295 printf("Successfully created '%s'\n", pszOutput);
296 return 0;
297}
298
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette