CANN Runtime 错误码 EE1014 深度解析:算子二进制文件解析失败(File_Operation_Error_Parse)
【免费下载链接】runtime本项目提供CANN运行时组件和维测功能组件。项目地址: https://gitcode.com/cann/runtime
EE1014 是 CANN Runtime(本仓库cann/runtime)在加载与解析算子二进制(ELF)文件时上报的典型错误,归属于 RTS-Errors 错误码体系中的文件操作类错误。本文以 EE1014 官方错误码文档为主体,结合仓库中错误码定义、ELF 解析源码与单元测试,逐层拆解该错误的报错格式、底层触发链路、常见成因与排查方案,帮助开发者快速定位并解决算子二进制解析失败的问题。
错误码速览
| 属性 | 内容 |
|---|---|
| 错误码 | EE1014 |
| 错误类别 | RTS Errors(Runtime 系统错误) |
| 错误标题 | File_Operation_Error_Parse(文件操作类错误——解析失败) |
| 典型触发阶段 | 算子二进制(ELF)文件加载与解析 |
| 日志级别 | DLOG_ERROR |
该错误码在错误码定义文件中登记为:
{ "errClass": "RTS Errors", "errTitle": "File_Operation_Error_Parse", "ErrCode": "EE1014", "ErrMessage": "Failed to parse the binary file of the operator. Reason: %s.", "Arglist": "reason", "suggestion": { "Possible Cause": "1. The binary file of the operator is damaged. 2. The build parameter is incorrect.", "Solution": "Rebuild and load the binary file of the operator." } }定义位置:src/dfx/error_manager/error_code.json。同一条目也以宏形式注册在 src/runtime/core/inc/common/error_code_meta.h 中,用于 Runtime 侧实际输出日志。
报错格式与实例
标准报错格式
错误信息由固定前缀与占位符组成,%s为具体的解析失败原因:
Failed to parse the binary file of the operator. Reason: %s.实际输出时,日志会附加ErrorCode=EE1014后缀。这一点可以从 Runtime 的错误码注册宏中得到印证,其打印格式为:
Failed to parse the binary file of the operator. Reason: %s. ErrorCode=EE1014.即真实日志形如:
Failed to parse the binary file of the operator. Reason: The ELF section header address in the operator binary ELF file header cannot be empty. ErrorCode=EE1014.官方文档中的报错示例
以The ELF section header address in the operator binary ELF file header cannot be empty为具体原因的示例:
Failed to parse the binary file of the operator. Reason: The ELF section header address in the operator binary ELF file header cannot be empty.该示例并非虚构,它对应的正是 Runtime ELF 解析实现中Get64bitSectionHeaders()对 ELF 节区头(section header)地址为空的校验分支(见下文“底层触发链路”)。参考:EE1014-File_Operation_Error_Parse.md(英文) 与 中文对照文档。
错误码在 Runtime 中的登记与输出方式
在 Runtime 侧,EE1014 通过错误码注册宏X(...)声明,携带一个参数reason,并指定日志级别为DLOG_ERROR:
/* EE1014 - File_Operation_Error_Parse */ X(EE1014, "EE1014", ("reason"), "Failed to parse the binary file of the operator. " "Reason: %s. ErrorCode=EE1014.\n", DLOG_ERROR)代码位置:src/runtime/core/inc/common/error_code_meta.h。RT_LOG_OUTER_MSG_IMPL是该仓库中面向用户的错误输出宏,ELF 解析模块正是通过它来携带具体reason上报 EE1014。
从错误码区间看,EE1013(主机内存不足)属于资源类错误,EE1015(驱动版本能力不足)属于包版本类错误,而 EE1014 独立成类为文件操作类解析错误,说明 Runtime 对“算子二进制解析失败”这一场景单独做了错误归类,便于用户根据错误码快速定位问题所属模块。
底层触发链路:算子二进制是如何被解析的
理解 EE1014 的触发点,需要先了解算子二进制在 Runtime 中的加载流程。该错误在 ELF 解析阶段抛出,核心代码位于 src/runtime/core/src/kernel/elf.cc,调用链如下:
ElfProgram::ParserBinary() // program.cc:程序二进制解析入口 └─ ProcessObject() // elf.cc:整体解析流程编排 ├─ GetFileHeader() // 校验并读取 ELF 文件头(仅支持 64 位 ELF) └─ ProcessSymbolTable() // 解析节区头、字符串表、符号表并产出 RtKernel ├─ Get64bitSectionHeaders()// 读取并校验 ELF 节区头 ├─ GetStringTable() // 获取字符串表 ├─ ParseKernelMetaData() // 解析算子元数据(TLV 格式) ├─ ParseElfStackInfoHeader()/ParseElfStackInfoFromSection() ├─ ProcessDynamicSection() // 处理动态段 └─ GetKernels() // 汇总生成内核句柄列表1. 解析入口:ElfProgram::ParserBinary
算子二进制以ElfProgram的形式管理。ParserBinary()首先将二进制内容与大小灌入rtElfData,随后调用ProcessObject()完成解析,解析失败即返回错误:
rtError_t ElfProgram::ParserBinary() { NULL_PTR_RETURN_MSG(elfData_, RT_ERROR_PROGRAM_DATA); ... elfData_->obj_size = binarySize_; kernels_ = ProcessObject(RtPtrToPtr<char_t*>(binary_), elfData_); NULL_PTR_RETURN_MSG_OUTER_WITH_FUNC_DESC( kernels_, RT_ERROR_INVALID_VALUE, "Parsing the binary file data of the operator"); ... }代码位置:src/runtime/core/src/kernel/program.cc。可以看到“Parsing the binary file data of the operator”正是该入口对解析动作的描述,与 EE1014 错误信息中的 “binary file of the operator” 一一对应。
2. 文件头校验:GetFileHeader
GetFileHeader()负责读取 ELF 头部的标识区(e_ident)并决定字节序解析函数,同时校验 ELF 位数——Runtime 只支持 64 位 ELF,若检测到 32 位对象会直接上报 EE1014:
const bool is32bitElf = (static_cast<int32_t>(elfData->elf_header.e_ident[EI_CLASS]) != ELFCLASS64); if (is32bitElf) { RT_LOG_OUTER_MSG_IMPL(ErrorCode::EE1014, "The ELF file must be a 64-bit file"); return ELF_FAIL; }代码位置:src/runtime/core/src/kernel/elf.cc。这意味着“编译参数不正确”导致的 32 位产物、或文件头损坏导致EI_CLASS字段异常,都会在此处命中 EE1014。
3. 节区头解析:Get64bitSectionHeaders 的多重校验
Get64bitSectionHeaders()是 EE1014 报错最密集的函数,围绕 ELF 节区头做了多维度合法性校验,任何一项不满足都会上报 EE1014:
e_shentsize / e_shnum 合理性校验:节区头大小与节区数量均不能为 0,且二者的乘积不能超过
uint64_t最大值,否则上报:The value %u of e_shentsize or the value %u of e_shnum in the operator binary ELF file header is incorrect...代码位置:src/runtime/core/src/kernel/elf.cc。
e_shentsize 一致性校验:必须与
Elf64_External_Shdr的实际大小一致,否则上报:The value %u of e_shentsize in the operator binary ELF file header must be equal to the size %u of the ELF section header代码位置:src/runtime/core/src/kernel/elf.cc。
节区头地址非空校验:根据
e_shoff计算出的节区头指针为空时,正是官方文档示例中的报错:The ELF section header address in the operator binary ELF file header cannot be empty代码位置:src/runtime/core/src/kernel/elf.cc。
节区偏移越界校验:第 i 个节区的偏移超过 ELF 对象总大小时上报:
The offset %llu of the section ranked %u exceeds the size %llu of the ELF object代码位置:src/runtime/core/src/kernel/elf.cc。
sh_link 越界校验:节区关联索引超出节区总数范围
[0, num]时上报,代码位置:src/runtime/core/src/kernel/elf.cc。
4. 符号表与元数据解析
在符号表解析Get64bitElfSymbols()中,还会校验节区大小sh_size大于 0、sh_entsize位于(0, sh_size]区间,以及符号表偏移是否越界,失败均上报 EE1014(src/runtime/core/src/kernel/elf.cc)。此外,ProcessSymbolTable()在汇总代码段(text section)大小时若发生整数溢出风险,同样上报 EE1014(src/runtime/core/src/kernel/elf.cc)。
从以上校验点可以总结出 EE1014 的本质:它是一组“ELF 结构合法性”校验失败的统一出口,任何导致 ELF 头、节区头、符号表结构异常的输入都会收敛到这个错误码。
可能原因分析
根据官方文档(英文版 / 中文版)的说明,EE1014 的可能原因有两点:
- 算子的二进制文件损坏:文件在传输、落盘或裁剪过程中被破坏,导致 ELF 头字段缺失或数值非法(例如
e_shoff指向无效地址、e_shentsize异常、节区偏移越界),从而命中上述各种校验分支。 - 编译参数不正确:编译产物不符合 Runtime 的解析约束。最典型的例子是产出了 32 位 ELF(不满足“仅支持 64 位 ELF”的硬性要求);此外,SoC 版本、工具链选项、编译宏等与目标环境不匹配,也可能生成结构上不合法或无法被当前 Runtime 识别的二进制。
结合源码还可以补充推断:凡是在GetFileHeader()、Get64bitSectionHeaders()、Get64bitElfSymbols()等校验点失败的情况,都会以“Reason: xxx”的形式填充到 EE1014 的%s占位符中,因此报错原因部分本身就有极强的定位价值——它直接指出了是文件头、节区头还是符号表哪个环节不合法。
排查与解决方法
官方文档给出的解决方法是:
Rebuild and load the binary file of the operator.(重新编译并加载算子的二进制文件。)
在此基础上,结合本文的源码分析,可以给出更完整的排查路径:
第一步:读取完整报错信息
EE1014 的Reason字段是首要线索,请从 plog 日志中抓取完整错误行(含ErrorCode=EE1014后缀),并根据 Reason 文本判断命中哪个校验分支:
- 含
e_shentsize/e_shnum→ 文件头被破坏或头字段异常; - 含
cannot be empty→ 节区头地址(e_shoff)无效,文件头被截断或篡改; - 含
exceeds the size→ 节区/符号偏移越界,文件内容与头部信息不一致; - 含
must be a 64-bit file→ 编译参数错误,产物为 32 位。
第二步:核对编译参数并重新编译
- 确认使用的编译工具链与目标 SoC 型号匹配,生成 64 位 ELF 产物;
- 检查编译选项、链接脚本和裁剪步骤,避免 strip 掉必要的节区(如
.strtab、.symtab及算子元数据所在节区); - 清理构建缓存后重新生成算子二进制,并比对新旧文件的哈希值,排除缓存污染。
第三步:重新加载与验证
将重新编译生成的二进制通过正常的加载流程(参考仓库 example/2_advanced_features/kernel 中的内核加载示例)重新下发到 Runtime,并再次观察日志。若错误消失,则确认问题出在旧二进制文件本身。
第四步:回归测试验证
仓库的单元测试覆盖了 ELF 解析的正反向用例(详见下一节),可在本地复现与验证解析逻辑,用于确认新产物能通过全部合法性校验。
单元测试佐证
仓库为 ELF 解析链路提供了系统的单元测试,主要位于 tests/ut/runtime/runtime/test/rt_utest_elf.cc,不同平台目录(如tests/ut/runtime/runtime/test/platform/910B/rt_utest_elf.cc、tests/ut/runtime/runtime/test/platform/950/rt_utest_david_elf.cc)下还有平台相关的补充用例。
测试以ELFTest为 fixture,通过构造二进制缓冲区直接调用ProcessObject()验证解析结果,覆盖了:
- 正常二进制的符号表、内核信息解析;
- 非法输入(空指针、损坏的 ELF 结构)下解析失败并正确释放资源;
- 算子元数据 TLV 解析(参数汇总、参数信息等),例如
ElfParseParamSummary_Success、ElfParseParamInfo_Success等用例。
此外,tests/ut/runtime/runtime/test/rt_error_code_test.cc 覆盖了错误码本身的注册与输出格式校验。这些测试既验证了 EE1014 所关联的解析逻辑的正确性,也为开发者修改 ELF 解析代码提供了回归保障。
相关参考
- EE1014 官方错误码文档(英文)
- EE1014 官方错误码文档(中文)
- RTS 错误码索引(英文)
- RTS 错误码索引(中文)
- 错误码定义文件
- 错误码注册宏
- ELF 解析实现
- ELF 程序解析入口(ElfProgram::ParserBinary)
- ELF 解析单元测试
- 错误信息编写指南
【免费下载链接】runtime本项目提供CANN运行时组件和维测功能组件。项目地址: https://gitcode.com/cann/runtime
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考