]> git.proxmox.com Git - mirror_edk2.git/blame - Tools/CCode/Source/FwImage/fwimage.c
Perfected MSA files.
[mirror_edk2.git] / Tools / CCode / Source / FwImage / fwimage.c
CommitLineData
878ddf1f 1/*++\r
2\r
18998564 3Copyright (c) 2004 - 2007, Intel Corporation \r
878ddf1f 4All rights reserved. This program and the accompanying materials \r
5are licensed and made available under the terms and conditions of the BSD License \r
6which accompanies this distribution. The full text of the license may be found at \r
7http://opensource.org/licenses/bsd-license.php \r
8 \r
9THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, \r
10WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. \r
11\r
12Module Name:\r
13\r
14 fwimage.c\r
15\r
16Abstract:\r
17\r
18 Converts a pe32+ image to an FW image type\r
19\r
20--*/\r
21\r
ce53a8c3 22#include "WinNtInclude.h"\r
23\r
8ba7afaf 24//\r
25// List of OS and CPU which support ELF to PE conversion\r
26//\r
27#if defined(linux)
28#if defined(i386)
29#define HAVE_ELF
30#endif
31#endif
32
878ddf1f 33#ifndef __GNUC__\r
34#include <windows.h>\r
35#endif\r
36#include <stdio.h>\r
37#include <stdlib.h>\r
38#include <string.h>\r
39#include <time.h>\r
8ba7afaf 40
41#ifdef HAVE_ELF
42#include <elf.h>
43#endif
878ddf1f 44\r
ce53a8c3 45#include <Common/UefiBaseTypes.h>\r
46#include <Common/EfiImage.h>\r
47\r
48#include "CommonLib.h"\r
49#include "EfiUtilityMsgs.c"\r
878ddf1f 50\r
f091efb3 51//\r
52// Version of this utility\r
53//\r
54#define UTILITY_NAME "FwImage"\r
55#define UTILITY_MAJOR_VERSION 1\r
56#define UTILITY_MINOR_VERSION 0\r
878ddf1f 57\r
58#ifdef __GNUC__\r
59typedef unsigned long ULONG;\r
60typedef unsigned char UCHAR;\r
61typedef unsigned char *PUCHAR;\r
62typedef unsigned short USHORT;\r
63#endif\r
64\r
8ba7afaf 65PUCHAR InImageName;\r
66\r
f091efb3 67static\r
68void\r
69Version (\r
70 VOID\r
71 )\r
72{\r
73 printf ("%s v%d.%d -EDK Utility for Converting a pe32+ image to an FW image type.\n", UTILITY_NAME, UTILITY_MAJOR_VERSION, UTILITY_MINOR_VERSION);\r
74 printf ("Copyright (c) 1999-2006 Intel Corporation. All rights reserved.\n");\r
75}\r
76\r
77\r
878ddf1f 78VOID\r
79Usage (\r
80 VOID\r
81 )\r
82{\r
f091efb3 83 Version();\r
84 printf ("\nUsage: " UTILITY_NAME " {-t time-date} {-h|--help|-?|/?|-V|--version} \n\\r
85 [BASE|SEC|PEI_CORE|PEIM|DXE_CORE|DXE_DRIVER|DXE_RUNTIME_DRIVER|\n\\r
86 DXE_SAL_DRIVER|DXE_SMM_DRIVER|TOOL|UEFI_DRIVER|UEFI_APPLICATION|\n\\r
8ba7afaf 87 USER_DEFINED] peimage [outimage]\n");\r
878ddf1f 88}\r
89\r
90static\r
91STATUS\r
92FCopyFile (\r
93 FILE *in,\r
94 FILE *out\r
95 )\r
96{\r
97 ULONG filesize;\r
98 ULONG offset;\r
99 ULONG length;\r
100 UCHAR Buffer[8 * 1024];\r
101\r
102 fseek (in, 0, SEEK_END);\r
103 filesize = ftell (in);\r
104\r
105 fseek (in, 0, SEEK_SET);\r
106 fseek (out, 0, SEEK_SET);\r
107\r
108 offset = 0;\r
109 while (offset < filesize) {\r
110 length = sizeof (Buffer);\r
111 if (filesize - offset < length) {\r
112 length = filesize - offset;\r
113 }\r
114\r
115 fread (Buffer, length, 1, in);\r
116 fwrite (Buffer, length, 1, out);\r
117 offset += length;\r
118 }\r
119\r
120 if ((ULONG) ftell (out) != filesize) {\r
121 Error (NULL, 0, 0, "write error", NULL);\r
122 return STATUS_ERROR;\r
123 }\r
124\r
125 return STATUS_SUCCESS;\r
126}\r
127\r
3edf127e 128static\r
129STATUS\r
130FReadFile (\r
131 FILE *in,\r
132 VOID **Buffer,\r
133 UINTN *Length\r
134 )\r
135{\r
136 fseek (in, 0, SEEK_END);\r
137 *Length = ftell (in);\r
138 *Buffer = malloc (*Length);\r
139 fseek (in, 0, SEEK_SET);\r
140 fread (*Buffer, *Length, 1, in);\r
141 return STATUS_SUCCESS;\r
142}\r
143\r
144static\r
145STATUS\r
146FWriteFile (\r
147 FILE *out,\r
148 VOID *Buffer,\r
149 UINTN Length\r
150 )\r
151{\r
152 fseek (out, 0, SEEK_SET);\r
153 fwrite (Buffer, Length, 1, out);\r
154 if ((ULONG) ftell (out) != Length) {\r
155 Error (NULL, 0, 0, "write error", NULL);\r
156 return STATUS_ERROR;\r
157 }\r
158 free (Buffer);\r
159 return STATUS_SUCCESS;\r
160}\r
161\r
8ba7afaf 162#ifdef HAVE_ELF
163INTN
164IsElfHeader(
165 UINT8 *FileBuffer
166)
167{
168 return (FileBuffer[EI_MAG0] == ELFMAG0
169 && FileBuffer[EI_MAG1] == ELFMAG1
170 && FileBuffer[EI_MAG2] == ELFMAG2
171 && FileBuffer[EI_MAG3] == ELFMAG3);
172}
173
174typedef Elf32_Shdr Elf_Shdr;
175typedef Elf32_Ehdr Elf_Ehdr;
176typedef Elf32_Rel Elf_Rel;
177typedef Elf32_Sym Elf_Sym;
178#define ELFCLASS ELFCLASS32
179#define ELF_R_TYPE(r) ELF32_R_TYPE(r)
180#define ELF_R_SYM(r) ELF32_R_SYM(r)
181
182//
183// Well known ELF structures.
184//
185Elf_Ehdr *Ehdr;
186Elf_Shdr *ShdrBase;
187
188//
189// PE section alignment.
190//
191const UINT32 CoffAlignment = 0x20;
192const UINT32 CoffNbrSections = 4;
193
194//
195// Current offset in coff file.
196//
197UINT32 CoffOffset;
198
199//
200// Result Coff file in memory.
201//
202UINT8 *CoffFile;
203
204//
205// Offset in Coff file of headers and sections.
206//
207UINT32 NtHdrOffset;
208UINT32 TableOffset;
209UINT32 TextOffset;
210UINT32 DataOffset;
211UINT32 RelocOffset;
212
213//
214// ELF sections to offset in Coff file.
215//
216UINT32 *CoffSectionsOffset;
217
218EFI_IMAGE_BASE_RELOCATION *CoffBaseRel;
219UINT16 *CoffEntryRel;
220
221UINT32
222CoffAlign(
223 UINT32 Offset
224 )
225{
226 return (Offset + CoffAlignment - 1) & ~(CoffAlignment - 1);
227}
228
229Elf_Shdr *
230GetShdrByIndex(
231 UINT32 Num
232 )
233{
234 if (Num >= Ehdr->e_shnum)
235 return NULL;
236 return (Elf_Shdr*)((UINT8*)ShdrBase + Num * Ehdr->e_shentsize);
237}
238
239INTN
240CheckElfHeader(
241 VOID
242 )
18998564 243{\r
8ba7afaf 244 //
245 // Note: Magic has already been tested.
246 //
247 if (Ehdr->e_ident[EI_CLASS] != ELFCLASS)
248 return 0;
249 if (Ehdr->e_ident[EI_DATA] != ELFDATA2LSB)
250 return 0;
251 if (Ehdr->e_type != ET_EXEC)
252 return 0;
253 if (Ehdr->e_machine != EM_386)
254 return 0;
255 if (Ehdr->e_version != EV_CURRENT)
256 return 0;
18998564 257\r
258 //\r
259 // Find the section header table\r
260 //
8ba7afaf 261 ShdrBase = (Elf_Shdr *)((UINT8 *)Ehdr + Ehdr->e_shoff);
262
18998564 263 CoffSectionsOffset = (UINT32 *)malloc(Ehdr->e_shnum * sizeof (UINT32));\r
264
265 memset(CoffSectionsOffset, 0, Ehdr->e_shnum * sizeof(UINT32));\r
8ba7afaf 266 return 1;
267}
268
269int
270IsTextShdr(
271 Elf_Shdr *Shdr
272 )
273{
274 return (Shdr->sh_flags & (SHF_WRITE | SHF_ALLOC)) == SHF_ALLOC;
275}
18998564 276\r
8ba7afaf 277int
278IsDataShdr(
279 Elf_Shdr *Shdr
280 )
281{
282 return (Shdr->sh_flags & (SHF_WRITE | SHF_ALLOC)) == (SHF_ALLOC | SHF_WRITE);
283}
284
285void
286CreateSectionHeader(
287 const char *Name,
288 UINT32 Offset,
289 UINT32 Size,
290 UINT32 Flags
291 )
292{
293 EFI_IMAGE_SECTION_HEADER *Hdr;
294 Hdr = (EFI_IMAGE_SECTION_HEADER*)(CoffFile + TableOffset);
295
296 strcpy(Hdr->Name, Name);
297 Hdr->Misc.VirtualSize = Size;
298 Hdr->VirtualAddress = Offset;
299 Hdr->SizeOfRawData = Size;
300 Hdr->PointerToRawData = Offset;
301 Hdr->PointerToRelocations = 0;
302 Hdr->PointerToLinenumbers = 0;
303 Hdr->NumberOfRelocations = 0;
304 Hdr->NumberOfLinenumbers = 0;
305 Hdr->Characteristics = Flags;
306
307 TableOffset += sizeof (EFI_IMAGE_SECTION_HEADER);
308}
309
310void
311ScanSections(
312 VOID
313 )
314{
315 UINT32 i;
316 EFI_IMAGE_DOS_HEADER *DosHdr;
317 EFI_IMAGE_NT_HEADERS *NtHdr;
318 UINT32 CoffEntry = 0;
319
320 CoffOffset = 0;
321
322 //
323 // Coff file start with a DOS header.
324 //
325 CoffOffset = sizeof(EFI_IMAGE_DOS_HEADER) + 0x40;
326 NtHdrOffset = CoffOffset;
327 CoffOffset += sizeof(EFI_IMAGE_NT_HEADERS);
328 TableOffset = CoffOffset;
329 CoffOffset += CoffNbrSections * sizeof(EFI_IMAGE_SECTION_HEADER);
330
331 //
332 // First text sections.
333 //
334 CoffOffset = CoffAlign(CoffOffset);
335 TextOffset = CoffOffset;
336 for (i = 0; i < Ehdr->e_shnum; i++) {
337 Elf_Shdr *shdr = GetShdrByIndex(i);
18998564 338 if (IsTextShdr(shdr)) {\r
339 //\r
1d6992b9 340 // Align the coff offset to meet with the alignment requirement of section\r
341 // itself.\r
18998564 342 // \r
d91b2da7 343 if ((shdr->sh_addralign != 0) && (shdr->sh_addralign != 1)) {\r
344 CoffOffset = (CoffOffset + shdr->sh_addralign - 1) & ~(shdr->sh_addralign - 1);\r
345 }\r
1d6992b9 346\r
8ba7afaf 347 /* Relocate entry. */
18998564 348 if ((Ehdr->e_entry >= shdr->sh_addr) && \r
349 (Ehdr->e_entry < shdr->sh_addr + shdr->sh_size)) {
350 CoffEntry = CoffOffset + Ehdr->e_entry - shdr->sh_addr;
8ba7afaf 351 }
18998564 352 CoffSectionsOffset[i] = CoffOffset;\r
8ba7afaf 353 CoffOffset += shdr->sh_size;
18998564 354 }\r
355 }\r
8ba7afaf 356 CoffOffset = CoffAlign(CoffOffset);
18998564 357\r
8ba7afaf 358 //
359 // Then data sections.
360 //
361 DataOffset = CoffOffset;
362 for (i = 0; i < Ehdr->e_shnum; i++) {
363 Elf_Shdr *shdr = GetShdrByIndex(i);
1d6992b9 364 if (IsDataShdr(shdr)) {\r
365 //\r
366 // Align the coff offset to meet with the alignment requirement of section\r
367 // itself.\r
368 // \r
d91b2da7 369 if ((shdr->sh_addralign != 0) && (shdr->sh_addralign != 1)) {\r
370 CoffOffset = (CoffOffset + shdr->sh_addralign - 1) & ~(shdr->sh_addralign - 1);\r
371 }
372 \r
8ba7afaf 373 CoffSectionsOffset[i] = CoffOffset;
374 CoffOffset += shdr->sh_size;
375 }
376 }
377 CoffOffset = CoffAlign(CoffOffset);
378
379 RelocOffset = CoffOffset;
380
381 //
382 // Allocate base Coff file. Will be expanded later for relocations.
383 //
384 CoffFile = (UINT8 *)malloc(CoffOffset);
385 memset(CoffFile, 0, CoffOffset);
386
387 //
388 // Fill headers.
389 //
390 DosHdr = (EFI_IMAGE_DOS_HEADER *)CoffFile;
391 DosHdr->e_magic = EFI_IMAGE_DOS_SIGNATURE;
392 DosHdr->e_lfanew = NtHdrOffset;
393
394 NtHdr = (EFI_IMAGE_NT_HEADERS*)(CoffFile + NtHdrOffset);
395
396 NtHdr->Signature = EFI_IMAGE_NT_SIGNATURE;
397
398 NtHdr->FileHeader.Machine = EFI_IMAGE_MACHINE_IA32;
399 NtHdr->FileHeader.NumberOfSections = CoffNbrSections;
400 NtHdr->FileHeader.TimeDateStamp = time(NULL);
401 NtHdr->FileHeader.PointerToSymbolTable = 0;
402 NtHdr->FileHeader.NumberOfSymbols = 0;
403 NtHdr->FileHeader.SizeOfOptionalHeader = sizeof(NtHdr->OptionalHeader);
404 NtHdr->FileHeader.Characteristics = EFI_IMAGE_FILE_EXECUTABLE_IMAGE
405 | EFI_IMAGE_FILE_LINE_NUMS_STRIPPED
406 | EFI_IMAGE_FILE_LOCAL_SYMS_STRIPPED
407 | EFI_IMAGE_FILE_32BIT_MACHINE;
408
409 NtHdr->OptionalHeader.Magic = EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC;
410 NtHdr->OptionalHeader.SizeOfCode = DataOffset - TextOffset;
411 NtHdr->OptionalHeader.SizeOfInitializedData = RelocOffset - DataOffset;
412 NtHdr->OptionalHeader.SizeOfUninitializedData = 0;
413 NtHdr->OptionalHeader.AddressOfEntryPoint = CoffEntry;
414 NtHdr->OptionalHeader.BaseOfCode = TextOffset;
415
416 NtHdr->OptionalHeader.BaseOfData = DataOffset;
417 NtHdr->OptionalHeader.ImageBase = 0;
418 NtHdr->OptionalHeader.SectionAlignment = CoffAlignment;
419 NtHdr->OptionalHeader.FileAlignment = CoffAlignment;
420 NtHdr->OptionalHeader.SizeOfImage = 0;
421
422 NtHdr->OptionalHeader.SizeOfHeaders = TextOffset;
423 NtHdr->OptionalHeader.NumberOfRvaAndSizes = EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES;
424
425 //
426 // Section headers.
427 //
428 CreateSectionHeader (".text", TextOffset, DataOffset - TextOffset,
429 EFI_IMAGE_SCN_CNT_CODE
430 | EFI_IMAGE_SCN_MEM_EXECUTE
431 | EFI_IMAGE_SCN_MEM_READ);
432 CreateSectionHeader (".data", DataOffset, RelocOffset - DataOffset,
433 EFI_IMAGE_SCN_CNT_INITIALIZED_DATA
434 | EFI_IMAGE_SCN_MEM_WRITE
435 | EFI_IMAGE_SCN_MEM_READ);
436}
437
438void
439WriteSections(
440 int (*Filter)(Elf_Shdr *)
441 )
442{
443 UINT32 Idx;
444
445 //
446 // First: copy sections.
447 //
448 for (Idx = 0; Idx < Ehdr->e_shnum; Idx++) {
449 Elf_Shdr *Shdr = GetShdrByIndex(Idx);
450 if ((*Filter)(Shdr)) {
451 switch (Shdr->sh_type) {
452 case SHT_PROGBITS:
453 /* Copy. */
454 memcpy(CoffFile + CoffSectionsOffset[Idx],
455 (UINT8*)Ehdr + Shdr->sh_offset,
456 Shdr->sh_size);
457 break;
458 case SHT_NOBITS:
459 memset(CoffFile + CoffSectionsOffset[Idx], 0, Shdr->sh_size);
460 break;
461 default:
462 Error (NULL, 0, 0, InImageName, "unhandle section type %x",
463 (UINTN)Shdr->sh_type);
464 }
465 }
466 }
467
468 //
469 // Second: apply relocations.
470 //
471 for (Idx = 0; Idx < Ehdr->e_shnum; Idx++) {
472 Elf_Shdr *RelShdr = GetShdrByIndex(Idx);
473 if (RelShdr->sh_type != SHT_REL)
474 continue;
475 Elf_Shdr *SecShdr = GetShdrByIndex(RelShdr->sh_info);
476 UINT32 SecOffset = CoffSectionsOffset[RelShdr->sh_info];
477 if (RelShdr->sh_type == SHT_REL && (*Filter)(SecShdr)) {
478 UINT32 RelIdx;
479 Elf_Shdr *SymtabShdr = GetShdrByIndex(RelShdr->sh_link);
480 UINT8 *Symtab = (UINT8*)Ehdr + SymtabShdr->sh_offset;
481
482 for (RelIdx = 0; RelIdx < RelShdr->sh_size; RelIdx += RelShdr->sh_entsize) {
483 Elf_Rel *Rel = (Elf_Rel *)((UINT8*)Ehdr + RelShdr->sh_offset + RelIdx);
484 Elf_Sym *Sym = (Elf_Sym *)
485 (Symtab + ELF_R_SYM(Rel->r_info) * SymtabShdr->sh_entsize);
486 Elf_Shdr *SymShdr;
487 UINT8 *Targ;
488
489 if (Sym->st_shndx == SHN_UNDEF
490 || Sym->st_shndx == SHN_ABS
491 || Sym->st_shndx > Ehdr->e_shnum) {
492 Error (NULL, 0, 0, InImageName, "bad symbol definition");
493 }
494 SymShdr = GetShdrByIndex(Sym->st_shndx);
495
496 //
497 // Note: r_offset in a memory address.
498 // Convert it to a pointer in the coff file.
499 //
500 Targ = CoffFile + SecOffset + (Rel->r_offset - SecShdr->sh_addr);
501
502 switch (ELF_R_TYPE(Rel->r_info)) {
503 case R_386_NONE:
504 break;
505 case R_386_32:
506 //
507 // Absolute relocation.
508 //
509 *(UINT32 *)Targ = *(UINT32 *)Targ - SymShdr->sh_addr
510 + CoffSectionsOffset[Sym->st_shndx];
511 break;
512 case R_386_PC32:
513 //
514 // Relative relocation: Symbol - Ip + Addend
515 //
516 *(UINT32 *)Targ = *(UINT32 *)Targ
517 + (CoffSectionsOffset[Sym->st_shndx] - SymShdr->sh_addr)
518 - (SecOffset - SecShdr->sh_addr);
519 break;
520 default:
521 Error (NULL, 0, 0, InImageName, "unhandled relocation type %x",
522 ELF_R_TYPE(Rel->r_info));
523 }
524 }
525 }
526 }
527}
528
529void
530CoffAddFixupEntry(
531 UINT16 Val
532 )
533{
534 *CoffEntryRel = Val;
535 CoffEntryRel++;
536 CoffBaseRel->SizeOfBlock += 2;
537 CoffOffset += 2;
538}
539
540void
541CoffAddFixup(
542 UINT32 Offset,
543 UINT8 Type
544 )
545{
546 if (CoffBaseRel == NULL
547 || CoffBaseRel->VirtualAddress != (Offset & ~0xfff)) {
548 if (CoffBaseRel != NULL) {
549 //
550 // Add a null entry (is it required ?)
551 //
552 CoffAddFixupEntry (0);
553 //
554 // Pad for alignment.
555 //
556 if (CoffOffset % 4 != 0)
557 CoffAddFixupEntry (0);
558 }
559
560 CoffFile = realloc
561 (CoffFile,
562 CoffOffset + sizeof(EFI_IMAGE_BASE_RELOCATION) + 2*0x1000);
563 memset(CoffFile + CoffOffset, 0,
564 sizeof(EFI_IMAGE_BASE_RELOCATION) + 2*0x1000);
565
566 CoffBaseRel = (EFI_IMAGE_BASE_RELOCATION*)(CoffFile + CoffOffset);
567 CoffBaseRel->VirtualAddress = Offset & ~0xfff;
568 CoffBaseRel->SizeOfBlock = sizeof(EFI_IMAGE_BASE_RELOCATION);
569
570 CoffEntryRel = (UINT16 *)(CoffBaseRel + 1);
571 CoffOffset += sizeof(EFI_IMAGE_BASE_RELOCATION);
572 }
573
574 //
575 // Fill the entry.
576 //
577 CoffAddFixupEntry((Type << 12) | (Offset & 0xfff));
578}
579
580void
581WriteRelocations(
582 VOID
583 )
584{
585 UINT32 Idx;
586 EFI_IMAGE_NT_HEADERS *NtHdr;
587 EFI_IMAGE_DATA_DIRECTORY *Dir;
588
589 for (Idx = 0; Idx < Ehdr->e_shnum; Idx++) {
590 Elf_Shdr *RelShdr = GetShdrByIndex(Idx);
591 if (RelShdr->sh_type == SHT_REL) {
592 Elf_Shdr *SecShdr = GetShdrByIndex(RelShdr->sh_info);
593 if (IsTextShdr(SecShdr) || IsDataShdr(SecShdr)) {
594 UINT32 RelIdx;
595 for (RelIdx = 0; RelIdx < RelShdr->sh_size; RelIdx += RelShdr->sh_entsize) {
596 Elf_Rel *Rel = (Elf_Rel *)
597 ((UINT8*)Ehdr + RelShdr->sh_offset + RelIdx);
598 switch (ELF_R_TYPE(Rel->r_info)) {
599 case R_386_NONE:
600 case R_386_PC32:
601 break;
602 case R_386_32:
603 CoffAddFixup(CoffSectionsOffset[RelShdr->sh_info]
604 + (Rel->r_offset - SecShdr->sh_addr),
605 EFI_IMAGE_REL_BASED_HIGHLOW);
606 break;
607 default:
608 Error (NULL, 0, 0, InImageName, "unhandled relocation type %x",
609 ELF_R_TYPE(Rel->r_info));
610 }
611 }
612 }
613 }
614 }
615
616 //
617 // Pad by adding empty entries.
618 //
619 while (CoffOffset & (CoffAlignment - 1)) {
620 CoffAddFixupEntry(0);
621 }
622
623 CreateSectionHeader (".reloc", RelocOffset, CoffOffset - RelocOffset,
624 EFI_IMAGE_SCN_CNT_INITIALIZED_DATA
625 | EFI_IMAGE_SCN_MEM_DISCARDABLE
626 | EFI_IMAGE_SCN_MEM_READ);
627
628 NtHdr = (EFI_IMAGE_NT_HEADERS *)(CoffFile + NtHdrOffset);
629 Dir = &NtHdr->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC];
630 Dir->VirtualAddress = RelocOffset;
631 Dir->Size = CoffOffset - RelocOffset;
632}
633
634void
635WriteDebug(
636 VOID
637 )
638{
639 UINT32 Len = strlen(InImageName) + 1;
640 UINT32 DebugOffset = CoffOffset;
641 EFI_IMAGE_NT_HEADERS *NtHdr;
642 EFI_IMAGE_DATA_DIRECTORY *DataDir;
643 EFI_IMAGE_DEBUG_DIRECTORY_ENTRY *Dir;
644 EFI_IMAGE_DEBUG_CODEVIEW_NB10_ENTRY *Nb10;
645
646 CoffOffset += sizeof(EFI_IMAGE_DEBUG_DIRECTORY_ENTRY)
647 + sizeof(EFI_IMAGE_DEBUG_CODEVIEW_NB10_ENTRY)
648 + Len;
649 CoffOffset = CoffAlign(CoffOffset);
650
651 CoffFile = realloc
652 (CoffFile, CoffOffset);
653 memset(CoffFile + DebugOffset, 0, CoffOffset - DebugOffset);
654
655 Dir = (EFI_IMAGE_DEBUG_DIRECTORY_ENTRY*)(CoffFile + DebugOffset);
656 Dir->Type = EFI_IMAGE_DEBUG_TYPE_CODEVIEW;
657 Dir->SizeOfData = sizeof(EFI_IMAGE_DEBUG_DIRECTORY_ENTRY) + Len;
658 Dir->RVA = DebugOffset + sizeof(EFI_IMAGE_DEBUG_DIRECTORY_ENTRY);
659 Dir->FileOffset = DebugOffset + sizeof(EFI_IMAGE_DEBUG_DIRECTORY_ENTRY);
660
661 Nb10 = (EFI_IMAGE_DEBUG_CODEVIEW_NB10_ENTRY*)(Dir + 1);
662 Nb10->Signature = CODEVIEW_SIGNATURE_NB10;
663 strcpy ((PUCHAR)(Nb10 + 1), InImageName);
664
665 CreateSectionHeader (".debug", DebugOffset, CoffOffset - DebugOffset,
666 EFI_IMAGE_SCN_CNT_INITIALIZED_DATA
667 | EFI_IMAGE_SCN_MEM_DISCARDABLE
668 | EFI_IMAGE_SCN_MEM_READ);
669
670 NtHdr = (EFI_IMAGE_NT_HEADERS *)(CoffFile + NtHdrOffset);
671 DataDir = &NtHdr->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG];
672 DataDir->VirtualAddress = DebugOffset;
673 DataDir->Size = CoffOffset - DebugOffset;
674}
675
676void
677ConvertElf (
678 UINT8 **FileBuffer,
679 UINTN *FileLength
680 )
681{
682 EFI_IMAGE_NT_HEADERS *NtHdr;
683
684 //
685 // Check header, read section table.
686 //
687 Ehdr = (Elf32_Ehdr*)*FileBuffer;
688 if (!CheckElfHeader())
689 return;
690
691 //
692 // Compute sections new address.
693 //
694 ScanSections();
695
696 //
697 // Write and relocate sections.
698 //
699 WriteSections(IsTextShdr);
700 WriteSections(IsDataShdr);
701
702 //
703 // Translate and write relocations.
704 //
705 WriteRelocations();
706
707 //
708 // Write debug info.
709 //
710 WriteDebug();
711
712 NtHdr = (EFI_IMAGE_NT_HEADERS *)(CoffFile + NtHdrOffset);
713 NtHdr->OptionalHeader.SizeOfImage = CoffOffset;
714
715 //
716 // Replace.
717 //
718 free(*FileBuffer);
719 *FileBuffer = CoffFile;
720 *FileLength = CoffOffset;
721}
722#endif // HAVE_ELF
723
878ddf1f 724int\r
725main (\r
726 int argc,\r
727 char *argv[]\r
728 )\r
729/*++\r
730\r
731Routine Description:\r
732\r
733 Main function.\r
734\r
735Arguments:\r
736\r
737 argc - Number of command line parameters.\r
738 argv - Array of pointers to command line parameter strings.\r
739\r
740Returns:\r
741 STATUS_SUCCESS - Utility exits successfully.\r
742 STATUS_ERROR - Some error occurred during execution.\r
743\r
744--*/\r
745{\r
746 ULONG Type;\r
747 PUCHAR Ext;\r
748 PUCHAR p;\r
749 PUCHAR pe;\r
750 PUCHAR OutImageName;\r
751 UCHAR outname[500];\r
752 FILE *fpIn;\r
753 FILE *fpOut;\r
3edf127e 754 VOID *ZeroBuffer;\r
755 EFI_IMAGE_DOS_HEADER *DosHdr;\r
756 EFI_IMAGE_NT_HEADERS *PeHdr;\r
757 EFI_IMAGE_OPTIONAL_HEADER32 *Optional32;\r
758 EFI_IMAGE_OPTIONAL_HEADER64 *Optional64;\r
878ddf1f 759 time_t TimeStamp;\r
760 struct tm TimeStruct;\r
761 EFI_IMAGE_DOS_HEADER BackupDosHdr;\r
762 ULONG Index;\r
3edf127e 763 ULONG Index1;\r
307fd197 764 ULONG Index2;\r
765 ULONG Index3;\r
878ddf1f 766 BOOLEAN TimeStampPresent;\r
0411bcaf 767 UINTN AllignedRelocSize;\r
3edf127e 768 UINTN Delta;\r
769 EFI_IMAGE_SECTION_HEADER *SectionHeader;\r
770 UINT8 *FileBuffer;\r
771 UINTN FileLength;\r
307fd197 772 RUNTIME_FUNCTION *RuntimeFunction;\r
773 UNWIND_INFO *UnwindInfo;\r
878ddf1f 774\r
775 SetUtilityName (UTILITY_NAME);\r
776 //\r
777 // Assign to fix compile warning\r
778 //\r
779 OutImageName = NULL;\r
780 Type = 0;\r
781 Ext = 0;\r
782 TimeStamp = 0;\r
783 TimeStampPresent = FALSE;\r
784\r
db608e6b 785 if (argc == 1) {\r
f091efb3 786 Usage();\r
787 return STATUS_ERROR;\r
788 }\r
789 \r
790 if ((strcmp(argv[1], "-h") == 0) || (strcmp(argv[1], "--help") == 0) ||\r
791 (strcmp(argv[1], "-?") == 0) || (strcmp(argv[1], "/?") == 0)) {\r
792 Usage();\r
793 return STATUS_ERROR;\r
794 }\r
795 \r
796 if ((strcmp(argv[1], "-V") == 0) || (strcmp(argv[1], "--version") == 0)) {\r
797 Version();\r
798 return STATUS_ERROR;\r
799 }\r
800 \r
878ddf1f 801 //\r
802 // Look for -t time-date option first. If the time is "0", then\r
803 // skip it.\r
804 //\r
805 if ((argc > 2) && !strcmp (argv[1], "-t")) {\r
806 TimeStampPresent = TRUE;\r
807 if (strcmp (argv[2], "0") != 0) {\r
808 //\r
809 // Convert the string to a value\r
810 //\r
811 memset ((char *) &TimeStruct, 0, sizeof (TimeStruct));\r
812 if (sscanf(\r
813 argv[2], "%d/%d/%d,%d:%d:%d",\r
814 &TimeStruct.tm_mon, /* months since January - [0,11] */\r
815 &TimeStruct.tm_mday, /* day of the month - [1,31] */\r
816 &TimeStruct.tm_year, /* years since 1900 */\r
817 &TimeStruct.tm_hour, /* hours since midnight - [0,23] */\r
818 &TimeStruct.tm_min, /* minutes after the hour - [0,59] */\r
819 &TimeStruct.tm_sec /* seconds after the minute - [0,59] */\r
820 ) != 6) {\r
821 Error (NULL, 0, 0, argv[2], "failed to convert to mm/dd/yyyy,hh:mm:ss format");\r
822 return STATUS_ERROR;\r
823 }\r
824 //\r
825 // Now fixup some of the fields\r
826 //\r
827 TimeStruct.tm_mon--;\r
828 TimeStruct.tm_year -= 1900;\r
829 //\r
830 // Sanity-check values?\r
831 // Convert\r
832 //\r
833 TimeStamp = mktime (&TimeStruct);\r
834 if (TimeStamp == (time_t) - 1) {\r
835 Error (NULL, 0, 0, argv[2], "failed to convert time");\r
836 return STATUS_ERROR;\r
837 }\r
838 }\r
839 //\r
840 // Skip over the args\r
841 //\r
842 argc -= 2;\r
843 argv += 2;\r
844 }\r
845 //\r
846 // Check for enough args\r
847 //\r
848 if (argc < 3) {\r
849 Usage ();\r
850 return STATUS_ERROR;\r
851 }\r
852\r
8ba7afaf 853 InImageName = argv[2];\r
854\r
878ddf1f 855 if (argc == 4) {\r
856 OutImageName = argv[3];\r
857 }\r
858 //\r
859 // Get new image type\r
860 //\r
861 p = argv[1];\r
862 if (*p == '/' || *p == '\\') {\r
863 p += 1;\r
864 }\r
865\r
8a286638 866 if (stricmp (p, "app") == 0 || stricmp (p, "UEFI_APPLICATION") == 0) {\r
878ddf1f 867 Type = EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION;\r
868 Ext = ".efi";\r
869\r
8a286638 870 } else if (stricmp (p, "bsdrv") == 0 || stricmp (p, "DXE_DRIVER") == 0) {\r
878ddf1f 871 Type = EFI_IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER;\r
872 Ext = ".efi";\r
873\r
8a286638 874 } else if (stricmp (p, "rtdrv") == 0 || stricmp (p, "DXE_RUNTIME_DRIVER") == 0) {\r
878ddf1f 875 Type = EFI_IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER;\r
876 Ext = ".efi";\r
877\r
8a286638 878 } else if (stricmp (p, "rtdrv") == 0 || stricmp (p, "DXE_SAL_DRIVER") == 0) {\r
878ddf1f 879 Type = EFI_IMAGE_SUBSYSTEM_SAL_RUNTIME_DRIVER;\r
880 Ext = ".efi";\r
8a286638 881 } else if (stricmp (p, "SEC") == 0) {\r
878ddf1f 882 Type = EFI_IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER;\r
883 Ext = ".sec";\r
884 } else if (stricmp (p, "peim") == 0 ||\r
8a286638 885 stricmp (p, "BASE") == 0 ||\r
878ddf1f 886 stricmp (p, "PEI_CORE") == 0 ||\r
8a286638 887 stricmp (p, "PEIM") == 0 ||\r
888 stricmp (p, "DXE_SMM_DRIVER") == 0 ||\r
889 stricmp (p, "TOOL") == 0 ||\r
890 stricmp (p, "UEFI_APPLICATION") == 0 ||\r
891 stricmp (p, "USER_DEFINED") == 0 ||\r
892 stricmp (p, "UEFI_DRIVER") == 0 ||\r
893 stricmp (p, "DXE_CORE") == 0\r
878ddf1f 894 ) {\r
895 Type = EFI_IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER;\r
896 Ext = ".pei";\r
897 } else {\r
8a286638 898 printf ("%s", p);\r
878ddf1f 899 Usage ();\r
900 return STATUS_ERROR;\r
901 }\r
902 //\r
903 // open source file\r
904 //\r
8ba7afaf 905 fpIn = fopen (InImageName, "rb");\r
878ddf1f 906 if (!fpIn) {\r
8ba7afaf 907 Error (NULL, 0, 0, InImageName, "failed to open input file for reading");\r
878ddf1f 908 return STATUS_ERROR;\r
909 }\r
3edf127e 910\r
911 FReadFile (fpIn, (VOID **)&FileBuffer, &FileLength);\r
912\r
8ba7afaf 913#ifdef HAVE_ELF
914 if (IsElfHeader(FileBuffer)) {
915 ConvertElf(&FileBuffer, &FileLength);
916 }
917#endif
878ddf1f 918 //\r
919 // Read the dos & pe hdrs of the image\r
920 //\r
3edf127e 921 DosHdr = (EFI_IMAGE_DOS_HEADER *)FileBuffer;\r
922 if (DosHdr->e_magic != EFI_IMAGE_DOS_SIGNATURE) {\r
8ba7afaf 923 Error (NULL, 0, 0, InImageName, "DOS header signature not found in source image");\r
878ddf1f 924 fclose (fpIn);\r
925 return STATUS_ERROR;\r
926 }\r
927\r
3edf127e 928 PeHdr = (EFI_IMAGE_NT_HEADERS *)(FileBuffer + DosHdr->e_lfanew);\r
929 if (PeHdr->Signature != EFI_IMAGE_NT_SIGNATURE) {\r
8ba7afaf 930 Error (NULL, 0, 0, InImageName, "PE header signature not found in source image");\r
878ddf1f 931 fclose (fpIn);\r
932 return STATUS_ERROR;\r
933 }\r
3edf127e 934\r
878ddf1f 935 //\r
936 // open output file\r
937 //\r
8ba7afaf 938 strcpy (outname, InImageName);\r
878ddf1f 939 pe = NULL;\r
940 for (p = outname; *p; p++) {\r
941 if (*p == '.') {\r
942 pe = p;\r
943 }\r
944 }\r
945\r
946 if (!pe) {\r
947 pe = p;\r
948 }\r
949\r
950 strcpy (pe, Ext);\r
951\r
952 if (!OutImageName) {\r
953 OutImageName = outname;\r
954 }\r
955\r
956 fpOut = fopen (OutImageName, "w+b");\r
957 if (!fpOut) {\r
958 Error (NULL, 0, 0, OutImageName, "could not open output file for writing");\r
959 fclose (fpIn);\r
960 return STATUS_ERROR;\r
961 }\r
3edf127e 962\r
878ddf1f 963 //\r
964 // Zero all unused fields of the DOS header\r
965 //\r
3edf127e 966 memcpy (&BackupDosHdr, DosHdr, sizeof (EFI_IMAGE_DOS_HEADER));\r
967 memset (DosHdr, 0, sizeof (EFI_IMAGE_DOS_HEADER));\r
968 DosHdr->e_magic = BackupDosHdr.e_magic;\r
969 DosHdr->e_lfanew = BackupDosHdr.e_lfanew;\r
970\r
971 for (Index = sizeof (EFI_IMAGE_DOS_HEADER); Index < (ULONG) DosHdr->e_lfanew; Index++) {\r
972 FileBuffer[Index] = DosHdr->e_cp;\r
878ddf1f 973 }\r
3edf127e 974\r
878ddf1f 975 //\r
8ba7afaf 976 // Patch the PE header\r
878ddf1f 977 //\r
3edf127e 978 PeHdr->OptionalHeader.Subsystem = (USHORT) Type;\r
878ddf1f 979 if (TimeStampPresent) {\r
3edf127e 980 PeHdr->FileHeader.TimeDateStamp = (UINT32) TimeStamp;\r
981 }\r
982\r
3edf127e 983 if (PeHdr->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {\r
984 Optional32 = (EFI_IMAGE_OPTIONAL_HEADER32 *)&PeHdr->OptionalHeader;\r
985 Optional32->MajorLinkerVersion = 0;\r
986 Optional32->MinorLinkerVersion = 0;\r
987 Optional32->MajorOperatingSystemVersion = 0;\r
988 Optional32->MinorOperatingSystemVersion = 0;\r
989 Optional32->MajorImageVersion = 0;\r
990 Optional32->MinorImageVersion = 0;\r
991 Optional32->MajorSubsystemVersion = 0;\r
992 Optional32->MinorSubsystemVersion = 0;\r
993 Optional32->Win32VersionValue = 0;\r
994 Optional32->CheckSum = 0;\r
995 Optional32->SizeOfStackReserve = 0;\r
996 Optional32->SizeOfStackCommit = 0;\r
997 Optional32->SizeOfHeapReserve = 0;\r
998 Optional32->SizeOfHeapCommit = 0;\r
999\r
3edf127e 1000 //\r
1001 // Strip zero padding at the end of the .reloc section \r
1002 //\r
1003 if (Optional32->NumberOfRvaAndSizes >= 6) {\r
1004 if (Optional32->DataDirectory[5].Size != 0) {\r
1005 SectionHeader = (EFI_IMAGE_SECTION_HEADER *)(FileBuffer + DosHdr->e_lfanew + sizeof(UINT32) + sizeof (EFI_IMAGE_FILE_HEADER) + PeHdr->FileHeader.SizeOfOptionalHeader);\r
1006 for (Index = 0; Index < PeHdr->FileHeader.NumberOfSections; Index++, SectionHeader++) {\r
0411bcaf 1007 //\r
1008 // Look for the Section Header that starts as the same virtual address as the Base Relocation Data Directory\r
1009 //\r
3edf127e 1010 if (SectionHeader->VirtualAddress == Optional32->DataDirectory[5].VirtualAddress) {\r
0411bcaf 1011 SectionHeader->Misc.VirtualSize = Optional32->DataDirectory[5].Size;\r
1012 AllignedRelocSize = (Optional32->DataDirectory[5].Size + Optional32->FileAlignment - 1) & (~(Optional32->FileAlignment - 1));\r
1013 //\r
1014 // Check to see if there is zero padding at the end of the base relocations\r
1015 //\r
1016 if (AllignedRelocSize < SectionHeader->SizeOfRawData) {\r
1017 //\r
1018 // Check to see if the base relocations are at the end of the file\r
1019 //\r
1020 if (SectionHeader->PointerToRawData + SectionHeader->SizeOfRawData == Optional32->SizeOfImage) {\r
1021 //\r
1022 // All the required conditions are met to strip the zero padding of the end of the base relocations section\r
1023 //\r
1024 Optional32->SizeOfImage -= (SectionHeader->SizeOfRawData - AllignedRelocSize);\r
1025 Optional32->SizeOfInitializedData -= (SectionHeader->SizeOfRawData - AllignedRelocSize);\r
1026 SectionHeader->SizeOfRawData = AllignedRelocSize;\r
1027 FileLength = Optional32->SizeOfImage;\r
1028 }\r
1029 }\r
3edf127e 1030 }\r
1031 }\r
1032 }\r
1033 }\r
1034 } \r
1035 if (PeHdr->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC) {\r
1036 Optional64 = (EFI_IMAGE_OPTIONAL_HEADER64 *)&PeHdr->OptionalHeader;\r
1037 Optional64->MajorLinkerVersion = 0;\r
1038 Optional64->MinorLinkerVersion = 0;\r
1039 Optional64->MajorOperatingSystemVersion = 0;\r
1040 Optional64->MinorOperatingSystemVersion = 0;\r
1041 Optional64->MajorImageVersion = 0;\r
1042 Optional64->MinorImageVersion = 0;\r
1043 Optional64->MajorSubsystemVersion = 0;\r
1044 Optional64->MinorSubsystemVersion = 0;\r
1045 Optional64->Win32VersionValue = 0;\r
1046 Optional64->CheckSum = 0;\r
1047 Optional64->SizeOfStackReserve = 0;\r
1048 Optional64->SizeOfStackCommit = 0;\r
1049 Optional64->SizeOfHeapReserve = 0;\r
1050 Optional64->SizeOfHeapCommit = 0;\r
1051\r
1052 //\r
1053 // Zero the .pdata section if the machine type is X64 and the Debug Directory is empty\r
1054 //\r
1055 if (PeHdr->FileHeader.Machine == 0x8664) { // X64\r
1056 if (Optional64->NumberOfRvaAndSizes >= 4) {\r
1057 if (Optional64->NumberOfRvaAndSizes < 7 || (Optional64->NumberOfRvaAndSizes >= 7 && Optional64->DataDirectory[6].Size == 0)) {\r
1058 SectionHeader = (EFI_IMAGE_SECTION_HEADER *)(FileBuffer + DosHdr->e_lfanew + sizeof(UINT32) + sizeof (EFI_IMAGE_FILE_HEADER) + PeHdr->FileHeader.SizeOfOptionalHeader);\r
1059 for (Index = 0; Index < PeHdr->FileHeader.NumberOfSections; Index++, SectionHeader++) {\r
1060 if (SectionHeader->VirtualAddress == Optional64->DataDirectory[3].VirtualAddress) {\r
307fd197 1061 RuntimeFunction = (RUNTIME_FUNCTION *)(FileBuffer + SectionHeader->PointerToRawData);\r
1062 for (Index1 = 0; Index1 < Optional64->DataDirectory[3].Size / sizeof (RUNTIME_FUNCTION); Index1++, RuntimeFunction++) {\r
1063 SectionHeader = (EFI_IMAGE_SECTION_HEADER *)(FileBuffer + DosHdr->e_lfanew + sizeof(UINT32) + sizeof (EFI_IMAGE_FILE_HEADER) + PeHdr->FileHeader.SizeOfOptionalHeader);\r
1064 for (Index2 = 0; Index2 < PeHdr->FileHeader.NumberOfSections; Index2++, SectionHeader++) {\r
1065 if (RuntimeFunction->UnwindInfoAddress > SectionHeader->VirtualAddress && RuntimeFunction->UnwindInfoAddress < (SectionHeader->VirtualAddress + SectionHeader->SizeOfRawData)) {\r
1066 UnwindInfo = (UNWIND_INFO *)(FileBuffer + SectionHeader->PointerToRawData + (RuntimeFunction->UnwindInfoAddress - SectionHeader->VirtualAddress));\r
1067 if (UnwindInfo->Version == 1) {\r
1068 memset (UnwindInfo + 1, 0, UnwindInfo->CountOfUnwindCodes * sizeof (UINT16));\r
1069 memset (UnwindInfo, 0, sizeof (UNWIND_INFO));\r
1070 }\r
1071 }\r
1072 }\r
1073 memset (RuntimeFunction, 0, sizeof (RUNTIME_FUNCTION));\r
3edf127e 1074 }\r
d9e0f88e 1075\r
1076 break;\r
3edf127e 1077 }\r
1078 }\r
1079 Optional64->DataDirectory[3].Size = 0;\r
1080 Optional64->DataDirectory[3].VirtualAddress = 0;\r
1081 }\r
1082 }\r
1083 }\r
1084\r
1085 //\r
1086 // Strip zero padding at the end of the .reloc section \r
1087 //\r
1088 if (Optional64->NumberOfRvaAndSizes >= 6) {\r
1089 if (Optional64->DataDirectory[5].Size != 0) {\r
1090 SectionHeader = (EFI_IMAGE_SECTION_HEADER *)(FileBuffer + DosHdr->e_lfanew + sizeof(UINT32) + sizeof (EFI_IMAGE_FILE_HEADER) + PeHdr->FileHeader.SizeOfOptionalHeader);\r
1091 for (Index = 0; Index < PeHdr->FileHeader.NumberOfSections; Index++, SectionHeader++) {\r
0411bcaf 1092 //\r
1093 // Look for the Section Header that starts as the same virtual address as the Base Relocation Data Directory\r
1094 //\r
3edf127e 1095 if (SectionHeader->VirtualAddress == Optional64->DataDirectory[5].VirtualAddress) {\r
0411bcaf 1096 SectionHeader->Misc.VirtualSize = Optional64->DataDirectory[5].Size;\r
1097 AllignedRelocSize = (Optional64->DataDirectory[5].Size + Optional64->FileAlignment - 1) & (~(Optional64->FileAlignment - 1));\r
1098 //\r
1099 // Check to see if there is zero padding at the end of the base relocations\r
1100 //\r
1101 if (AllignedRelocSize < SectionHeader->SizeOfRawData) {\r
1102 //\r
1103 // Check to see if the base relocations are at the end of the file\r
1104 //\r
1105 if (SectionHeader->PointerToRawData + SectionHeader->SizeOfRawData == Optional64->SizeOfImage) {\r
1106 //\r
1107 // All the required conditions are met to strip the zero padding of the end of the base relocations section\r
1108 //\r
1109 Optional64->SizeOfImage -= (SectionHeader->SizeOfRawData - AllignedRelocSize);\r
1110 Optional64->SizeOfInitializedData -= (SectionHeader->SizeOfRawData - AllignedRelocSize);\r
1111 SectionHeader->SizeOfRawData = AllignedRelocSize;\r
1112 FileLength = Optional64->SizeOfImage;\r
1113 }\r
1114 }\r
3edf127e 1115 }\r
1116 }\r
1117 }\r
1118 }\r
1119 }\r
1120\r
3edf127e 1121 FWriteFile (fpOut, FileBuffer, FileLength);\r
878ddf1f 1122\r
1123 //\r
1124 // Done\r
1125 //\r
1126 fclose (fpIn);\r
1127 fclose (fpOut);\r
1128 //\r
1129 // printf ("Created %s\n", OutImageName);\r
1130 //\r
1131 return STATUS_SUCCESS;\r
1132}\r