尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
如何在EDKII中编译UNIX风格C语言
一、前言通过阅读前面的 EDKII 相关代码实现我们可以很容易发现其虽使用的是 C 语言语法但编写规则与我们在 IDE 或者操作系统上运行的 C 语言代码不太一样。以简单的 HelloWorld 程序为例。EDKII 中的代码为#include Uefi.h#include Library/UefiLib.hEFI_STATUSEFIAPIUefiMain (IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE *SystemTable) {Print(LHello, World!\n);return EFI_SUCCESS;}UNIX 风格代码如下#include stdio.h#include stdlib.hint main(int argc, char **argv) {printf(Hello UEFI World from LibC!\n);void* ptr malloc(1024);if (ptr) {printf(Memory allocated successfully.\n);free(ptr);}return 0;}对比以上两个程序下面的代码可以使用我们常见的 C 语言库函数。EDKII 中不能使用类似printf之类的库函数就是因为 EDKII 工程中没有此类函数的实现因此我们的任务就是引入 C 语言库函数的实现。二、EDK2-LIBCEDK2-LIBC也称为EADK即 EDK II Application Development Kit是一个开源项目该项目的目标就是降我们熟悉的 C 语言标准库移植到 UEFI 环境中从能能够让我们在编写标准库程序的时候能够使用printf、malloc和open等函数。为什么需要这个环境呢虽然 UEFI 规范中提供了大量的 API 函数但这些函数主要提供必要的硬件访问功能无法使用很多已有的高级应用功能。举个例子来说前面我们介绍了使用 UEFI 中的图形输出协议 GOP 在屏幕上显示一张图片这种显示方式非常笨拙本质上是通过控制屏幕上每个像素点的数据内容来实现对不同协议的图像数据解析很不友好。但是 C 语言世界存在很多图片格式的解析代码只需要我们直接调用即可。想象一下如果我们在 UEFI 中实现了这个 UNIX 风格的 C 语言环境这些非常有空的功能就可以直接移植和调用了。三、环境搭建下载 edk2-libc 源文件git clone https://github.com/tianocore/edk2-libc.git将 edk2-libc 与 edk2 放到同一层级路径中。yuanayuan-virtual-machine:~/src$ tree -L 1.├── edk2/ # 主仓库│ ├── MdePkg/│ ├── OvmfPkg/│ └── ...└── edk2-libc/ # libc 仓库├── StdLib/├── AppPkg/└── ...配置环境变量export PACKAGES_PATH$PWD/edk2:$PWD/edk2-libc配置这个环境变量的作用是告诉 EDK2 的构建系统去哪些额外的目录下寻找包Packages的源代码。需要注意的是每次打开一个新的终端都需要执行一下这个命令。如果你只进入edk2目录运行build命令构建系统只会解析edk2目录下的*.dsc平台描述文件和*.inf模块描述文件。它不知道edk2-libc的存在因此当你编译一个需要标准 C 库的应用程序时会报错“找不到 StdLib 包”。通过PACKAGES_PATH构建系统会把edk2-libc也加入搜索路径。当你的应用*.inf文件中声明了StdLib或SocketLib时构建系统就能在edk2-libc/StdLib目录下找到对应的头文件和实现代码。如果将来需要引用其他第三方 EDK2 包如edk2-platforms只需继续用冒号:分隔路径追加到这个变量中即可。这种方法很方便并且不会污染到 EDK2 的源码文件。EDK2 构建系统build命令实际是BaseTools在解析依赖时会按以下顺序查找包工作区根目录即WORKSPACE环境变量指向的目录通常是edk2的父目录。PACKAGES_PATH环境变量中列出的所有目录按顺序查找。EDK_TOOLS_PATH指定的目录通常指向BaseTools。当在edk2/OvmfPkg/OvmfPkg.dsc中看到类似这样的定义时[Packages]StdLib/StdLib.dec构建系统就会依次去PACKAGES_PATH里的每个目录下寻找StdLib文件夹及其中的StdLib.dec包声明文件。编写三大文件C 程序源码#include stdio.h#include stdlib.hint main(int argc, char **argv) {printf(Hello UEFI World from LibC!\n);void* ptr malloc(1024);if (ptr) {printf(Memory allocated successfully.\n);free(ptr);}return 0;}inf 文件[Defines]INF_VERSION 0x00010006BASE_NAME MyStdLibAppFILE_GUID 4e397097-665f-4745-88c3-6305ac8623aaMODULE_TYPE UEFI_APPLICATIONVERSION_STRING 1.0ENTRY_POINT ShellCEntryLib[Sources]MyStdLibApp.c[Packages]MdePkg/MdePkg.decShellPkg/ShellPkg.decStdLib/StdLib.dec[LibraryClasses]UefiApplicationEntryPointUefiLibUefiBootServicesTableLibLibCLibStdioShellCEntryLibdsc 文件[Defines]PLATFORM_NAME MyPkgPLATFORM_GUID 87654321-4321-4321-4321-CBA987654321PLATFORM_VERSION 1.0DSC_SPECIFICATION 0x00010005OUTPUT_DIRECTORY Build/MyPkgSUPPORTED_ARCHITECTURES X64BUILD_TARGETS DEBUG|RELEASE[LibraryClasses]UefiLib|MdePkg/Library/UefiLib/UefiLib.infUefiApplicationEntryPoint|MdePkg/Library/UefiApplicationEntryPoint/UefiApplicationEntryPoint.infPrintLib|MdePkg/Library/BasePrintLib/BasePrintLib.infPcdLib|MdePkg/Library/BasePcdLibNull/BasePcdLibNull.infMemoryAllocationLib|MdePkg/Library/UefiMemoryAllocationLib/UefiMemoryAllocationLib.infDebugLib|MdePkg/Library/UefiDebugLibConOut/UefiDebugLibConOut.infBaseMemoryLib|MdePkg/Library/BaseMemoryLib/BaseMemoryLib.infBaseLib|MdePkg/Library/BaseLib/BaseLib.infUefiBootServicesTableLib|MdePkg/Library/UefiBootServicesTableLib/UefiBootServicesTableLib.infDevicePathLib|MdePkg/Library/UefiDevicePathLib/UefiDevicePathLib.infUefiRuntimeServicesTableLib|MdePkg/Library/UefiRuntimeServicesTableLib/UefiRuntimeServicesTableLib.infRegisterFilterLib|MdePkg/Library/RegisterFilterLibNull/RegisterFilterLibNull.infDebugPrintErrorLevelLib|MdePkg/Library/BaseDebugPrintErrorLevelLib/BaseDebugPrintErrorLevelLib.inf# 解决 HiiLib 缺失问题HiiLib|MdeModulePkg/Library/UefiHiiLib/UefiHiiLib.infUefiHiiServicesLib|MdeModulePkg/Library/UefiHiiServicesLib/UefiHiiServicesLib.inf# 解决 UefiShellLib 相关的其他潜在缺失ShellLib|ShellPkg/Library/UefiShellLib/UefiShellLib.infFileHandleLib|MdePkg/Library/UefiFileHandleLib/UefiFileHandleLib.infSortLib|MdeModulePkg/Library/UefiSortLib/UefiSortLib.inf!include StdLib/StdLib.inc[Components]MyPkg/Application/MyStdLibApp/MyStdLibApp.inf编译build -p edk2/MyPkg/MyPkg.dsc附录为什么程序入口地址是ShellCEntryLib回忆一下我们在写Linux应用程序的时候是怎么接受命令行命令和参数的int main(int argc, char **argv) {}是不是跟我们上面的示例程序完全一致。也就是说使用ShellCEntryLib入口函数的目的就是为了使应用程序能够接收命令行参数以对不同参数做不同处理。也就是说表面上我们输入的命令行参数似乎是直接进入了我们编写的 UNIX 应用程序的main函数中实际不是这样。真实情况是首先进入入口函数ShellCEntryLib然后在入口函数中借用 shell 相关 Protocol 接收参数并调用用户定义的ShellAppMain把参数传入这个函数。edk2-libc对ShellAppMain又做了一层包装在ShellAppMain又调用了真正是我们自己定义的main函数。ShellCEntryLib的实现非常简洁位于ShellPkg/Library/UefiShellCEntryLib/UefiShellCEntryLib.cEFI_STATUSEFIAPIShellCEntryLib (IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE *SystemTable){INTN ReturnFromMain;EFI_SHELL_PARAMETERS_PROTOCOL *EfiShellParametersProtocol NULL;EFI_SHELL_INTERFACE *EfiShellInterface NULL;EFI_STATUS Status;// 优先尝试 Shell 2.0 接口推荐Status SystemTable-BootServices-OpenProtocol(ImageHandle,gEfiShellParametersProtocolGuid,(VOID **)EfiShellParametersProtocol,ImageHandle, NULL,EFI_OPEN_PROTOCOL_GET_PROTOCOL);if (!EFI_ERROR(Status)) {ReturnFromMain ShellAppMain(EfiShellParametersProtocol-Argc,EfiShellParametersProtocol-Argv);} else {// 兼容旧版 Shell 1.0 接口Status SystemTable-BootServices-OpenProtocol(ImageHandle,gEfiShellInterfaceGuid,(VOID **)EfiShellInterface,ImageHandle, NULL,EFI_OPEN_PROTOCOL_GET_PROTOCOL);if (!EFI_ERROR(Status)) {// 重点在这里调用用户定义的 ShellAppMain 入口函数并传入命令行参数ReturnFromMain ShellAppMain(EfiShellInterface-Argc,EfiShellInterface-Argv);} else {ASSERT(FALSE); // 没有 Shell 环境}}return ReturnFromMain; // INTN 会被隐式转换为 EFI_STATUS}入口方式函数签名参数处理适合场景标准 UEFIEFI_STATUS EFIAPI UefiMain(ImageHandle, SystemTable)需手动 OpenProtocol 获取参数简单应用、不依赖 ShellShellCEntryLibINTN EFIAPI ShellAppMain(Argc, Argv)自动获取Shell 命令行工具、libc 应用edk2-libc mainint main(int argc, char **argv)内部再包装最接近 Unix C 风格
RELATED

相关推荐

并发编程核心概念辨析

并发编程核心概念辨析

一、背景:CPU 多级缓存架构为了读者在阅读后序章节时,有更清晰、更形象的认知,这里放上现代 CPU 缓存的典型结构。一图胜千言,不多赘述,仅陈述以下要点:一颗 CPU,往往有多个核心(cor…

📅 2026/10/21 16:46:44
弯弦:散文

弯弦:散文

弯弦 他走过一条很长的路。 路是弧形的,从他出发的地方就开始弯曲。起初弯曲很轻微,他几乎察觉不到——每走一步,地面只偏了一点点。他以为自己在走直线,直到很久以后回头,才发现自己已经绕了一个巨大的弯。他不是走错…

📅 2026/10/21 18:20:18
美团 LongCat-2.0 正式 MIT 全开源|1.6T MoE 大模型技术拆解与完整本地部署实战教程

美团 LongCat-2.0 正式 MIT 全开源|1.6T MoE 大模型技术拆解与完整本地部署实战教程

摘要 2026年7月5日美团正式开源新一代混合专家大模型LongCat-2.0,采用MIT商用友好开源协议,完整开放模型权重、推理代码。该模型总参数量1.6万亿MoE架构,单Token激活参数约48B,原生支持100万上下文窗口,依托自研LSA稀疏…

📅 2026/10/21 18:25:13
MORE NEWS

更多资讯

📰

开源一个静态类型语言——NLang

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

slack-gif-creator - SKILL

name: slack-gif-creator description: “A toolkit providing utilities and knowledge for creating animated GIFs optimized for Slack.” risk: critical source: community date_added: “2026-02-27” Slack GIF 创建器 一个提供用于创建针对 Slack 优化的动画 GIF 的工…

📰

PMP 新版教材!《PMBOK® 第 8 版指南》来了!

很多备考 PMP 的小伙伴都知道,教材是备考的根基。想要吃透考试考点,官方指定参考书必不可少。 《PMBOK 指南(第 8 版)》由美国项目管理学会 PMI 编写,是 PMP 官方唯一指定考试用书,整本书搭建起 PMP 考试完…

📰

黑苹果EFI配置指南:一份长期维护的机型清单,5分钟定位引导与网卡

黑苹果EFI配置指南:一份长期维护的机型清单,5分钟定位引导与网卡 【免费下载链接】Hackintosh Hackintosh long-term maintenance model EFI and installation tutorial 项目地址: https://gitcode.com/gh_mirrors/ha/Hackintosh 装完 macOS&…

📰

全球首个开源吠陀AI占星师:VedAstro ChatAPI结合Cohere Embeddings的NLP实现全解

【免费下载链接】VedAstro A non-profit, open source project to make Vedic Astrology easily available to all. 项目地址: https://gitcode.com/gh_mirrors/ve/VedAstro 点击查看 免费下载 VedAstro 是一个非营利、开源的吠陀占星(Vedic Astrology&…

📰

【Spring-响应式编程】什么是反应式编程(Reactive Programming)?它的核心思想是什么?

什么是反应式编程?从“拉”到“推”的范式革命与高并发系统的终极解法 用户问题原文:1. 什么是反应式编程(Reactive Programming)?它的核心思想是什么? 本文将彻底解析反应式编程的本质,不仅回答“是什么”,更揭示其在 金融行情推送、IoT 设备监控、大促订单流处理 等高…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬