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 |
|
---|
24 | static 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 |
|
---|
34 | static 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 |
|
---|
62 | static void putu16(char *pb, unsigned uVal)
|
---|
63 | {
|
---|
64 | pb[0] = uVal & 0xff;
|
---|
65 | pb[1] = uVal >> 8;
|
---|
66 | }
|
---|
67 |
|
---|
68 |
|
---|
69 | static 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 |
|
---|
78 | static 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 |
|
---|
157 | static 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 |
|
---|
227 | int 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 |
|
---|