前面【参考1】提到了 StartImage 加载 CLib 编写Application 出错的原因,这篇文章介绍如何解决这个问题。
根据原因来看是因为找不到提供 Parameters 的Protocol,那么我们在调用之前给被加载的Application 装上需要的Protocol即可。安装 Protocol 需要用到 InstallProtocollInterface,具体定义如下【参考2】:
欲安装的 Protocol 实例则是从加载程序(Exec6)上面取下来的。
没有多少人愿意看大篇幅的代码,我这里列下最关键的部分:
首先,取出当前的 Shell Interface, 不同的环境下还可以使用 Shell Parameter Protocol , NT32 环境下只支持前者
//如果你在实体机上发现有问题,那么可以考虑这段代码的问题 Status = gBS->OpenProtocol(gImageHandle, &gEfiShellInterfaceGuid, (VOID **)&EfiShellInterface, gImageHandle, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL ); if (EFI_ERROR(Status)) { Print(L"Shell Parameters Protocol not Found!\r\n",Status); return (Status); } //之后,将取下来的 Protocol 安装给被加载的 Application Status = gBS->InstallProtocolInterface ( &NewHandle, &gEfiShellInterfaceGuid, EFI_NATIVE_INTERFACE, EfiShellInterface ); if (EFI_ERROR(Status)) { Print(L"Protocol Interface Installed fail!\r\n",Status); return (Status); }
最后,安装之后不能忘记 Uninstall,还要调用一下,特别注意第一个参数传递的不是指针。
运行结果,可以看出 Hello1和 Hello2都可以被正常加载运行:
看到这里,这篇文章就可以结束了,下面列出 Exec6 的代码:
#include <Uefi.h> #include <Library/UefiLib.h> #include <Library/ShellCEntryLib.h> #include <stdio.h> #include <stdlib.h> #include <wchar.h> #include <Protocol/EfiShell.h> #include <Library/ShellLib.h> extern EFI_BOOT_SERVICES *gBS; extern EFI_SYSTEM_TABLE *gST; extern EFI_RUNTIME_SERVICES *gRT; extern EFI_SHELL_PROTOCOL *gEfiShellProtocol; extern EFI_SHELL_ENVIRONMENT2 *mEfiShellEnvironment2; extern EFI_HANDLE gImageHandle; typedef struct { UINTN Signature; /// Image handle EFI_HANDLE Handle; /// Image type UINTN Type; /// If entrypoint has been called BOOLEAN Started; /// The image's entry point EFI_IMAGE_ENTRY_POINT EntryPoint; /// loaded image protocol EFI_LOADED_IMAGE_PROTOCOL Info; /// Location in memory EFI_PHYSICAL_ADDRESS ImageBasePage; } LOADED_IMAGE_PRIVATE_DATA_TEMP; #define _CR(Record, TYPE, Field) ((TYPE *) ((CHAR8 *) (Record) - (CHAR8 *) &(((TYPE *) 0)->Field))) #define LOADED_IMAGE_PRIVATE_DATA_FROM_THIS(a) \ _CR(a, LOADED_IMAGE_PRIVATE_DATA_TEMP, Info) /** GET DEVICEPATH **/ EFI_DEVICE_PATH_PROTOCOL * EFIAPI ShellGetDevicePath ( IN CHAR16 * CONST DeviceName OPTIONAL ) { // // Check for UEFI Shell 2.0 protocols // if (gEfiShellProtocol != NULL) { return (gEfiShellProtocol->GetDevicePathFromFilePath(DeviceName)); } // // Check for EFI shell // if (mEfiShellEnvironment2 != NULL) { return (mEfiShellEnvironment2->NameToPath(DeviceName)); } return (NULL); } int EFIAPI main ( IN int Argc, IN CHAR16 **Argv ) { EFI_DEVICE_PATH_PROTOCOL *DevicePath; EFI_HANDLE NewHandle; EFI_STATUS Status; LOADED_IMAGE_PRIVATE_DATA_TEMP *private = NULL; UINTN ExitDataSizePtr; EFI_LOADED_IMAGE_PROTOCOL *ImageInfo = NULL; EFI_SHELL_INTERFACE *EfiShellInterface=NULL; if (Argc!=2) { Print(L"Usage: Exec4 FileName\n"); return EFI_SUCCESS; } Print(L"File [%s]\n",Argv[1]); DevicePath=ShellGetDevicePath(Argv[1]); // // Load the image with: // FALSE - not from boot manager and NULL, 0 being not already in memory // Status = gBS->LoadImage( FALSE, gImageHandle, DevicePath, NULL, 0, &NewHandle); if (EFI_ERROR(Status)) { if (NewHandle != NULL) { gBS->UnloadImage(NewHandle); } Print(L"Error during LoadImage [%X]\n",Status); return (Status); } Status = gBS -> HandleProtocol ( NewHandle, &gEfiLoadedImageProtocolGuid, &ImageInfo ); private = LOADED_IMAGE_PRIVATE_DATA_FROM_THIS(ImageInfo); Print(L"ImageBase in EFI_LOADED_IMAGE_PROTOCOL [%lX]\n",ImageInfo->ImageBase); Print(L"ImageBase in LOADED_IMAGE_PRIVATE_DATA_TEMP [%lX]\n",private->ImageBasePage); Print(L"Entry Point [%lX]\n",private->EntryPoint); Status = gBS->OpenProtocol(gImageHandle, &gEfiShellInterfaceGuid, (VOID **)&EfiShellInterface, gImageHandle, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL ); if (EFI_ERROR(Status)) { Print(L"Shell Parameters Protocol not Found!\r\n",Status); return (Status); } Status = gBS->InstallProtocolInterface ( &NewHandle, &gEfiShellInterfaceGuid, EFI_NATIVE_INTERFACE, EfiShellInterface ); if (EFI_ERROR(Status)) { Print(L"Protocol Interface Installed fail!\r\n",Status); return (Status); } Print(L"================================RUN================================\r\n",Status); // // now start the image, passing up exit data if the caller requested it // Status = gBS->StartImage( NewHandle, &ExitDataSizePtr, NULL ); if (EFI_ERROR(Status)) { if (NewHandle != NULL) { gBS->UnloadImage(NewHandle); } Print(L"Error during StartImage [%X]\r\n",Status); return (Status); } Print(L"===============================EXIT================================\r\n",Status); Status = gBS->UninstallProtocolInterface ( NewHandle, &gEfiShellInterfaceGuid, EfiShellInterface ); if (EFI_ERROR(Status)) { Print(L"Protocol Interface Uninstalled fail!\r\n",Status); return (Status); } gBS->UnloadImage (NewHandle); Print(L"NewHandle [%lX]\n",NewHandle); return EFI_SUCCESS; }
完整代码下载:
exec6
至此,终于回答了 StartImage 执行Application 的问题,如果你发现本文有任何问题欢迎给我留言,或者你有什么其他问题,同样可以给我发 e-Mail。
就是这样。
参考:
1. http://www.lab-z.com/stu85/ StartImage CLib
2. Uefi Spec 2.4 P153
我采用EDK2,别人拷给我的,大概是2017、2018的。
下载exec6源码,Hello1和Hello2都无法加载,报错Error during LoadImage [E]
Print(L”File [%s]\n”,Argv[1]);无法打印,我改成Print(L”File [%a]\n”,Argv[1]);了。
Print(L”Error during LoadImage [%X]\n”,Status);我改成
Print(L”Error during LoadImage [%d]\n”,Status);报错Error during LoadImage [14]。
只是因为 64位和32位UEFI Shell 下的Argv 是不同的,一个是 CHAR8,一个是 CHAR16 导致的。
编译的是64位应用程序
32位efi,SecMain.exe中运行,SecMain.exe崩溃,报错ASSERT!:[Shell] c:\edk2\MdePkg\Library\BaseLib\String.c (173):((UINTN) String &0x00000001) == 0
谢谢回复,昨天Debug跟进去看到原因,现在可以加载了,运行到OpenProtocol出错,跟进去CoreGetProtocolInterface返回的是NULL。
64位的还是报错Error during LoadImage [14]。
32位的现在可以加载了,运行到OpenProtocol出错,跟进去CoreGetProtocolInterface返回的是NULL。
CoreGetProtocolInterface返回的是NULL,查看SecMain,InstallProtocolInterface,确实没有SHELL_INTERFACE_PROTOCOL_GUID 0x47c7b223……
后来的 Shell 里面 SHELL_INTERFACE_PROTOCOL 有变化,好像是废弃了,改成另外一个 Protocol了
你可以研究一下
EmulatorIA32、EmulatorX64 可以正常加载,实体机加载报错Error during LoadImage如何解决?感谢回答。
RU.efi 这种能运行吗?是不是打开 secure boot了?
secure boot关闭了,我自己编写的efi和您别的例子在实体机中可以运行,需求是给自己编写的efi加个壳,只有您的文章有相关的。现在成功的解决方案是用您另一篇文章介绍的使用ShellExecute,但是会清屏,不能完美解决,还是想结合您的自定义shell命令,RunInMem,加载CLib程序这三篇。
如果这样的话,估计需要你研究一下 ShellExecute 的实现了。
通过研究您的文章和网上资料,前几天我成功实现了,自定义Shell命令并且命令调用以内存方式加载CLib程序。
多谢博主,另网址提供的给我写信功能好像有问题。在我坚持实现这个功能的时候,我很庆幸您很及时回复留言。
恭喜一下~
我网站用的 wordpress ,经常会有大量垃圾留言(每天800左右),
没办法只能用这个验证插件,每次我自己验证的时候也觉得不好用。