前面【参考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左右),
没办法只能用这个验证插件,每次我自己验证的时候也觉得不好用。