int
main (
  IN int             Argc, 
  IN char            **Argv
  )
{
  COMPILER_RUN_STATUS  Status;

  SetPrintLevel(WARNING_LOG_LEVEL);
  CVfrCompiler         Compiler(Argc, Argv);
  
  Compiler.PreProcess();
  Compiler.Compile();
  Compiler.AdjustBin();
  Compiler.GenBinary();
  Compiler.GenCFile();
  Compiler.GenRecordListFile ();

  Status = Compiler.RunStatus ();
  if ((Status == STATUS_DEAD) || (Status == STATUS_FAILED)) {
    return 2;
  }

  if (gCBuffer.Buffer != NULL) {
    delete gCBuffer.Buffer;
  }
  
  if (gRBuffer.Buffer != NULL) {
    delete gRBuffer.Buffer;
  }

  return GetUtilityStatus ();
}
Beispiel #2
0
int
main (
  int   Argc,
  char  *Argv[]
  )
/*++

Routine Description:

  Call the routine to parse the command-line options, then process the file.

Arguments:

  Standard C main() argc and argv.

Returns:

  0       if successful
  nonzero otherwise

--*/
{
  STATUS  Status;

  //
  // Set the utility name for error reporting purposes
  //
  SetUtilityName (UTILITY_NAME);

  //
  // Process the command-line arguments
  //
  Status = ProcessArgs (Argc, Argv);
  if (Status != STATUS_SUCCESS) {
    return Status;
  }

  //
  // Switch based on args
  //
  if (mGlobals.Mode & MODE_CREATE_HII_RESOURCE_FILE) {
    CreateResourceScript (mGlobals.ResourceFileName, &mGlobals.Guid, mGlobals.PackageFile);
  }

  if (mGlobals.Mode & MODE_CREATE_HII_PACKAGE_LIST) {
    CreatePackageList (mGlobals.PackageListFileName, &mGlobals.Guid, mGlobals.PackageFile);
  }

  FreeGlobals ();

  return GetUtilityStatus ();
}
Beispiel #3
0
static
STATUS
ProcessFile (
  INT8      *InFileName,
  INT8      *OutFileName
  )
/*++

Routine Description:
  
  Process a PE32 EFI file.

Arguments:
  
  InFileName      - the file name pointer to the input file
  OutFileName     - the file name pointer to the output file

Returns:

  STATUS_SUCCESS  - the process has been finished successfully
  STATUS_ERROR    - error occured during the processing

--*/
{
  STATUS                      Status;
  FILE                        *InFptr;
  FILE                        *OutFptr;
  UINT16                      MachineType;
  UINT16                      SubSystem;
  EFI_TE_IMAGE_HEADER         TEImageHeader;
  UINT32                      PESigOffset;
  EFI_IMAGE_FILE_HEADER       FileHeader;
  EFI_IMAGE_OPTIONAL_HEADER32 OptionalHeader32;
  EFI_IMAGE_OPTIONAL_HEADER64 OptionalHeader64;
  UINT32                      BytesStripped;
  UINT32                      FileSize;
  UINT8                       *Buffer;
  long                        SaveFilePosition;

  InFptr  = NULL;
  OutFptr = NULL;
  Buffer  = NULL;
  Status  = STATUS_ERROR;

  //
  // Try to open the input file
  //
  if ((InFptr = fopen (InFileName, "rb")) == NULL) {
    Error (NULL, 0, 0, InFileName, "failed to open input file for reading");
    return STATUS_ERROR;
  }
  //
  // Double-check the file to make sure it's what we expect it to be
  //
  if (CheckPE32File (InFileName, InFptr, &MachineType, &SubSystem) != STATUS_SUCCESS) {
    goto Finish;
  }
  //
  // Initialize our new header
  //
  memset (&TEImageHeader, 0, sizeof (EFI_TE_IMAGE_HEADER));

  //
  // Seek to the end to get the file size
  //
  fseek (InFptr, 0, SEEK_END);
  FileSize = ftell (InFptr);
  fseek (InFptr, 0, SEEK_SET);

  //
  // Per the PE/COFF specification, at offset 0x3C in the file is a 32-bit
  // offset (from the start of the file) to the PE signature, which always
  // follows the MSDOS stub. The PE signature is immediately followed by the
  // COFF file header.
  //
  //
  if (fseek (InFptr, 0x3C, SEEK_SET) != 0) {
    Error (NULL, 0, 0, InFileName, "failed to seek to PE signature in file", NULL);
    goto Finish;
  }

  if (fread (&PESigOffset, sizeof (PESigOffset), 1, InFptr) != 1) {
    Error (NULL, 0, 0, InFileName, "failed to read PE signature offset from file");
    goto Finish;
  }

  if (fseek (InFptr, PESigOffset + 4, SEEK_SET) != 0) {
    Error (NULL, 0, 0, InFileName, "failed to seek to PE signature");
    goto Finish;
  }
  //
  // We should now be at the COFF file header. Read it in and verify it's
  // of an image type we support.
  //
  if (fread (&FileHeader, sizeof (EFI_IMAGE_FILE_HEADER), 1, InFptr) != 1) {
    Error (NULL, 0, 0, InFileName, "failed to read file header from image");
    goto Finish;
  }

  if ((FileHeader.Machine != EFI_IMAGE_MACHINE_IA32) &&
      (FileHeader.Machine != EFI_IMAGE_MACHINE_X64) &&
      (FileHeader.Machine != EFI_IMAGE_MACHINE_IA64)) {
    Error (NULL, 0, 0, InFileName, "image is of an unsupported machine type 0x%X", (UINT32) FileHeader.Machine);
    goto Finish;
  }
  //
  // Calculate the total number of bytes we're going to strip off. The '4' is for the
  // PE signature PE\0\0. Then sanity check the size.
  //
  BytesStripped = PESigOffset + 4 + sizeof (EFI_IMAGE_FILE_HEADER) + FileHeader.SizeOfOptionalHeader;
  if (BytesStripped >= FileSize) {
    Error (NULL, 0, 0, InFileName, "attempt to strip more bytes than the total file size");
    goto Finish;
  }

  if (BytesStripped &~0xFFFF) {
    Error (NULL, 0, 0, InFileName, "attempt to strip more than 64K bytes", NULL);
    goto Finish;
  }

  TEImageHeader.StrippedSize = (UINT16) BytesStripped;

  //
  // Read in the optional header. Assume PE32, and if not, then re-read as PE32+
  //
  SaveFilePosition = ftell (InFptr);
  if (fread (&OptionalHeader32, sizeof (EFI_IMAGE_OPTIONAL_HEADER32), 1, InFptr) != 1) {
    Error (NULL, 0, 0, InFileName, "failed to read optional header from input file");
    goto Finish;
  }

  if (OptionalHeader32.SectionAlignment != OptionalHeader32.FileAlignment) {
    Error (NULL, 0, 0, InFileName, "Section alignment is not same to file alignment.");
    goto Finish;
  }

  if (OptionalHeader32.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
    //
    // Fill in our new header with required data directory entries
    //
    TEImageHeader.AddressOfEntryPoint = OptionalHeader32.AddressOfEntryPoint;
    //
    // - BytesStripped + sizeof (EFI_TE_IMAGE_HEADER);
    //
    // We're going to pack the subsystem into 1 byte. Make sure it fits
    //
    if (OptionalHeader32.Subsystem &~0xFF) {
      Error (
        NULL,
        0,
        0,
        InFileName,
        NULL,
        "image subsystem 0x%X cannot be packed into 1 byte",
        (UINT32) OptionalHeader32.Subsystem
        );
      goto Finish;
    }

    TEImageHeader.Subsystem   = (UINT8) OptionalHeader32.Subsystem;
    TEImageHeader.BaseOfCode  = OptionalHeader32.BaseOfCode;
    TEImageHeader.ImageBase   = (UINT64) (OptionalHeader32.ImageBase);
    if (OptionalHeader32.NumberOfRvaAndSizes > EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC) {
      TEImageHeader.DataDirectory[EFI_TE_IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress = OptionalHeader32.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress;
      TEImageHeader.DataDirectory[EFI_TE_IMAGE_DIRECTORY_ENTRY_BASERELOC].Size = OptionalHeader32.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC].Size;
    }

    if (OptionalHeader32.NumberOfRvaAndSizes > EFI_IMAGE_DIRECTORY_ENTRY_DEBUG) {
      TEImageHeader.DataDirectory[EFI_TE_IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress = OptionalHeader32.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress;
      TEImageHeader.DataDirectory[EFI_TE_IMAGE_DIRECTORY_ENTRY_DEBUG].Size = OptionalHeader32.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG].Size;
    }
  } else if (OptionalHeader32.Magic == EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
    //
    // Rewind and re-read the optional header
    //
    fseek (InFptr, SaveFilePosition, SEEK_SET);
    if (fread (&OptionalHeader64, sizeof (EFI_IMAGE_OPTIONAL_HEADER64), 1, InFptr) != 1) {
      Error (NULL, 0, 0, InFileName, "failed to re-read optional header from input file");
      goto Finish;
    }

    TEImageHeader.AddressOfEntryPoint = OptionalHeader64.AddressOfEntryPoint;
    //
    // - BytesStripped + sizeof (EFI_TE_IMAGE_HEADER);
    //
    // We're going to pack the subsystem into 1 byte. Make sure it fits
    //
    if (OptionalHeader64.Subsystem &~0xFF) {
      Error (
        NULL,
        0,
        0,
        InFileName,
        NULL,
        "image subsystem 0x%X cannot be packed into 1 byte",
        (UINT32) OptionalHeader64.Subsystem
        );
      goto Finish;
    }

    TEImageHeader.Subsystem   = (UINT8) OptionalHeader64.Subsystem;
    TEImageHeader.BaseOfCode  = OptionalHeader64.BaseOfCode;
    TEImageHeader.ImageBase = (UINT64) (OptionalHeader64.ImageBase);
    if (OptionalHeader64.NumberOfRvaAndSizes > EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC) {
      TEImageHeader.DataDirectory[EFI_TE_IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress = OptionalHeader64.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress;
      TEImageHeader.DataDirectory[EFI_TE_IMAGE_DIRECTORY_ENTRY_BASERELOC].Size = OptionalHeader64.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC].Size;
    }

    if (OptionalHeader64.NumberOfRvaAndSizes > EFI_IMAGE_DIRECTORY_ENTRY_DEBUG) {
      TEImageHeader.DataDirectory[EFI_TE_IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress = OptionalHeader64.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress;
      TEImageHeader.DataDirectory[EFI_TE_IMAGE_DIRECTORY_ENTRY_DEBUG].Size = OptionalHeader64.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG].Size;
    }
  } else {
    Error (
      NULL,
      0,
      0,
      InFileName,
      "unsupported magic number 0x%X found in optional header",
      (UINT32) OptionalHeader32.Magic
      );
    goto Finish;
  }
  //
  // Fill in the remainder of our new image header
  //
  TEImageHeader.Signature = EFI_TE_IMAGE_HEADER_SIGNATURE;
  TEImageHeader.Machine   = FileHeader.Machine;
  //
  // We're going to pack the number of sections into a single byte. Make sure it fits.
  //
  if (FileHeader.NumberOfSections &~0xFF) {
    Error (
      NULL,
      0,
      0,
      InFileName,
      NULL,
      "image's number of sections 0x%X cannot be packed into 1 byte",
      (UINT32) FileHeader.NumberOfSections
      );
    goto Finish;
  }

  TEImageHeader.NumberOfSections = (UINT8) FileHeader.NumberOfSections;

  //
  // Now open our output file
  //
  if ((OutFptr = fopen (OutFileName, "wb")) == NULL) {
    Error (NULL, 0, 0, OutFileName, "failed to open output file for writing");
    goto Finish;
  }
  //
  // Write the TE header
  //
  if (fwrite (&TEImageHeader, sizeof (EFI_TE_IMAGE_HEADER), 1, OutFptr) != 1) {
    Error (NULL, 0, 0, "failed to write image header to output file", NULL);
    goto Finish;
  }
  //
  // Position into the input file, read the part we're not stripping, and
  // write it out.
  //
  fseek (InFptr, BytesStripped, SEEK_SET);
  Buffer = (UINT8 *) malloc (FileSize - BytesStripped);
  if (Buffer == NULL) {
    Error (NULL, 0, 0, "application error", "failed to allocate memory");
    goto Finish;
  }

  if (fread (Buffer, FileSize - BytesStripped, 1, InFptr) != 1) {
    Error (NULL, 0, 0, InFileName, "failed to read remaining contents of input file");
    goto Finish;
  }

  if (fwrite (Buffer, FileSize - BytesStripped, 1, OutFptr) != 1) {
    Error (NULL, 0, 0, OutFileName, "failed to write all bytes to output file");
    goto Finish;
  }

  Status = STATUS_SUCCESS;

Finish:
  if (InFptr != NULL) {
    fclose (InFptr);
  }
  //
  // Close the output file. If there was an error, delete the output file so
  // that a subsequent build will rebuild it.
  //
  if (OutFptr != NULL) {
    fclose (OutFptr);
    if (GetUtilityStatus () == STATUS_ERROR) {
      remove (OutFileName);
    }
  }

  //
  // Free up our buffer
  //
  if (Buffer != NULL) {
    free (Buffer);
  }

  return Status;
}
Beispiel #4
0
int
main (
  int   Argc,
  char  *Argv[]
  )
/*++

Routine Description:
  

Arguments:

  Argc            - standard C main() argument count

  Argv            - standard C main() argument list

Returns:

  0             success
  non-zero      otherwise

--*/
// GC_TODO:    ] - add argument and description to function comment
{
  INT8    *Ext;
  UINT32  Status;

  SetUtilityName (UTILITY_NAME);
  //
  // Parse the command line arguments
  //
  if (ParseCommandLine (Argc, Argv)) {
    return STATUS_ERROR;
  }
  //
  // If dumping an image, then do that and quit
  //
  if (mOptions.Dump) {
    DumpImage (mOptions.InFileName);
    goto Finish;
  }
  //
  // Determine the output filename. Either what they specified on
  // the command line, or the first input filename with a different extension.
  //
  if (!mOptions.OutFileName[0]) {
    strcpy (mOptions.OutFileName, mOptions.InFileName);
    //
    // Find the last . on the line and replace the filename extension with
    // the default
    //
    for (Ext = mOptions.OutFileName + strlen (mOptions.OutFileName) - 1;
         (Ext >= mOptions.OutFileName) && (*Ext != '.') && (*Ext != '\\');
         Ext--
        )
      ;
    //
    // If dot here, then insert extension here, otherwise append
    //
    if (*Ext != '.') {
      Ext = mOptions.OutFileName + strlen (mOptions.OutFileName);
    }

    strcpy (Ext, DEFAULT_OUTPUT_EXTENSION);
  }
  //
  // Make sure we don't have the same filename for input and output files
  //
  if (_stricmp (mOptions.OutFileName, mOptions.InFileName) == 0) {
    Error (NULL, 0, 0, mOptions.OutFileName, "input and output file names must be different");
    goto Finish;
  }
  //
  // Process the file
  //
  ProcessFile (mOptions.InFileName, mOptions.OutFileName);
Finish:
  Status = GetUtilityStatus ();
  return Status;
}
Beispiel #5
0
int
main (
  int   Argc,
  char  *Argv[]
  )
/*++

Routine Description:

  Call the routine to parse the command-line options, then process each file
  to build dependencies.
  
Arguments:

  Argc - Standard C main() argc.
  Argv - Standard C main() argv.

Returns:

  0       if successful
  nonzero otherwise
  
--*/
{
  STRING_LIST *File;
  STRING_LIST ProcessedFiles;
  STRING_LIST *TempList;
  STATUS      Status;
  INT8        *Cptr;
  INT8        TargetFileName[MAX_PATH];

  SetUtilityName (UTILITY_NAME);
  //
  // Process the command-line arguments
  //
  Status = ProcessArgs (Argc, Argv);
  if (Status != STATUS_SUCCESS) {
    return STATUS_ERROR;
  }
  //
  // Go through the list of source files and process each.
  //
  memset (&ProcessedFiles, 0, sizeof (STRING_LIST));
  File = mGlobals.SourceFiles;
  while (File != NULL) {
    //
    // Clear out our list of processed files
    //
    TempList = ProcessedFiles.Next;
    while (ProcessedFiles.Next != NULL) {
      TempList = ProcessedFiles.Next->Next;
      free (ProcessedFiles.Next->Str);
      free (ProcessedFiles.Next);
      ProcessedFiles.Next = TempList;
    }
    //
    // Replace filename extension with ".obj" if they did not
    // specifically specify the target file
    //
    if (mGlobals.TargetFileName[0] == 0) {
      strcpy (TargetFileName, File->Str);
      //
      // Find the .extension
      //
      for (Cptr = TargetFileName + strlen (TargetFileName) - 1;
           (*Cptr != '\\') && (Cptr > TargetFileName) && (*Cptr != '.');
           Cptr--
          )
        ;
      if (Cptr == TargetFileName) {
        Error (NULL, 0, 0, File->Str, "could not locate extension in filename");
        goto Finish;
      }
      //
      // Tack on the ".obj"
      //
      strcpy (Cptr, ".obj");
    } else {
      //
      // Copy the target filename they specified
      //
      strcpy (TargetFileName, mGlobals.TargetFileName);
    }

    if (mGlobals.IsCl) {
      Status = ProcessClOutput (TargetFileName, File->Str, &ProcessedFiles);
    } else {
      Status = ProcessFile (TargetFileName, File->Str, START_NEST_DEPTH, 
                            &ProcessedFiles, SearchCurrentDir);
    }
    if (Status != STATUS_SUCCESS) {
      goto Finish;
    }

    File = File->Next;
  }

Finish:
  //
  // Free up memory
  //
  FreeLists ();
  //
  // Free up our processed files list
  //
  TempList = ProcessedFiles.Next;
  while (ProcessedFiles.Next != NULL) {
    TempList = ProcessedFiles.Next->Next;
    free (ProcessedFiles.Next->Str);
    free (ProcessedFiles.Next);
    ProcessedFiles.Next = TempList;
  }
  //
  // Close our temp output file
  //
  if ((mGlobals.OutFptr != stdout) && (mGlobals.OutFptr != NULL)) {
    fclose (mGlobals.OutFptr);
  }

  if (mGlobals.NeverFail) {
    return STATUS_SUCCESS;
  }

  if (mGlobals.OutFileName != NULL) {
    if (GetUtilityStatus () == STATUS_ERROR) {
      //
      // If any errors, then delete our temp output
      // Also try to delete target file to improve the incremental build
      //      
      remove (mGlobals.TmpFileName);
      remove (TargetFileName);
    } else {
      //
      // Otherwise, rename temp file to output file
      //
      remove (mGlobals.OutFileName);
      rename (mGlobals.TmpFileName, mGlobals.OutFileName);
    }
  }

  return GetUtilityStatus ();
}
Beispiel #6
0
EFI_STATUS
main (
    IN INTN   argc,
    IN CHAR8  **argv
)
/*++

Routine Description:

  This utility uses GenFvImage.Lib to build a firmware volume image.

Arguments:

  FvInfFileName      The name of an FV image description file.

  Arguments come in pair in any order.
    -I FvInfFileName

Returns:

  EFI_SUCCESS            No error conditions detected.
  EFI_INVALID_PARAMETER  One or more of the input parameters is invalid.
  EFI_OUT_OF_RESOURCES   A resource required by the utility was unavailable.
                         Most commonly this will be memory allocation
                         or file creation.
  EFI_LOAD_ERROR         GenFvImage.lib could not be loaded.
  EFI_ABORTED            Error executing the GenFvImage lib.

--*/
{
    EFI_STATUS  Status;
    CHAR8       InfFileName[_MAX_PATH];
    CHAR8       *InfFileImage;
    UINTN       InfFileSize;
    UINT8       *FvImage;
    UINTN       FvImageSize;
    UINT8       Index;
    CHAR8       FvFileNameBuffer[_MAX_PATH];
    CHAR8       *FvFileName;
    FILE        *FvFile;
    FILE        *SymFile;
    CHAR8       SymFileNameBuffer[_MAX_PATH];
    CHAR8       *SymFileName;
    UINT8       *SymImage;
    UINTN       SymImageSize;
    CHAR8       *CurrentSymString;

    FvFileName  = FvFileNameBuffer;
    SymFileName = SymFileNameBuffer;

    SetUtilityName (UTILITY_NAME);
    //
    // Display utility information
    //
    PrintUtilityInfo ();

    //
    // Verify the correct number of arguments
    //
    if (argc != MAX_ARGS) {
        Error (NULL, 0, 0, "invalid number of input parameters specified", NULL);
        PrintUsage ();
        return GetUtilityStatus ();
    }
    //
    // Initialize variables
    //
    strcpy (InfFileName, "");

    //
    // Parse the command line arguments
    //
    for (Index = 1; Index < MAX_ARGS; Index += 2) {
        //
        // Make sure argument pair begin with - or /
        //
        if (argv[Index][0] != '-' && argv[Index][0] != '/') {
            Error (NULL, 0, 0, argv[Index], "argument pair must begin with \"-\" or \"/\"");
            PrintUsage ();
            return GetUtilityStatus ();
        }
        //
        // Make sure argument specifier is only one letter
        //
        if (argv[Index][2] != 0) {
            Error (NULL, 0, 0, argv[Index], "unrecognized argument");
            PrintUsage ();
            return GetUtilityStatus ();
        }
        //
        // Determine argument to read
        //
        switch (argv[Index][1]) {

        case 'I':
        case 'i':
            if (strlen (InfFileName) == 0) {
                strcpy (InfFileName, argv[Index + 1]);
            } else {
                Error (NULL, 0, 0, argv[Index + 1], "FvInfFileName may only be specified once");
                PrintUsage ();
                return GetUtilityStatus ();
            }
            break;

        default:
            Error (NULL, 0, 0, argv[Index], "unrecognized argument");
            PrintUsage ();
            return GetUtilityStatus ();
            break;
        }
    }
    //
    // Read the INF file image
    //
    Status = GetFileImage (InfFileName, &InfFileImage, &InfFileSize);
    if (EFI_ERROR (Status)) {
        return STATUS_ERROR;
    }
    //
    // Call the GenFvImage lib
    //
    Status = GenerateFvImage (
                 InfFileImage,
                 InfFileSize,
                 &FvImage,
                 &FvImageSize,
                 &FvFileName,
                 &SymImage,
                 &SymImageSize,
                 &SymFileName
             );

    if (EFI_ERROR (Status)) {
        switch (Status) {

        case EFI_INVALID_PARAMETER:
            Error (NULL, 0, 0, "invalid parameter passed to GenFvImage Lib", NULL);
            return GetUtilityStatus ();
            break;

        case EFI_ABORTED:
            Error (NULL, 0, 0, "error detected while creating the file image", NULL);
            return GetUtilityStatus ();
            break;

        case EFI_OUT_OF_RESOURCES:
            Error (NULL, 0, 0, "GenFvImage Lib could not allocate required resources", NULL);
            return GetUtilityStatus ();
            break;

        case EFI_VOLUME_CORRUPTED:
            Error (NULL, 0, 0, "no base address was specified, but the FV.INF included a PEI or BSF file", NULL);
            return GetUtilityStatus ();
            break;

        case EFI_LOAD_ERROR:
            Error (NULL, 0, 0, "could not load FV image generation library", NULL);
            return GetUtilityStatus ();
            break;

        default:
            Error (NULL, 0, 0, "GenFvImage Lib returned unknown status", "status returned = 0x%X", Status);
            return GetUtilityStatus ();
            break;
        }
    }
    //
    // Write file
    //
    FvFile = fopen (FvFileName, "wb");
    if (FvFile == NULL) {
        Error (NULL, 0, 0, FvFileName, "could not open output file");
        free (FvImage);
        free (SymImage);
        return GetUtilityStatus ();
    }

    if (fwrite (FvImage, 1, FvImageSize, FvFile) != FvImageSize) {
        Error (NULL, 0, 0, FvFileName, "failed to write to output file");
        free (FvImage);
        free (SymImage);
        fclose (FvFile);
        return GetUtilityStatus ();
    }

    fclose (FvFile);
    free (FvImage);

    //
    // Write symbol file
    //
    if (strcmp (SymFileName, "")) {
        SymFile = fopen (SymFileName, "wt");
        if (SymFile == NULL) {
            Error (NULL, 0, 0, SymFileName, "could not open output symbol file");
            free (SymImage);
            return GetUtilityStatus ();
        }

        fprintf (SymFile, "TEXTSYM format | V1.0\n");

        CurrentSymString = SymImage;
        while (((UINTN) CurrentSymString - (UINTN) SymImage) < SymImageSize) {
            fprintf (SymFile, "%s", CurrentSymString);
            CurrentSymString = (CHAR8 *) (((UINTN) CurrentSymString) + strlen (CurrentSymString) + 1);
        }

        fclose (SymFile);
    }

    free (SymImage);

    return GetUtilityStatus ();
}