
简介面向ARMv9/ARM SoC、嵌入式及芯片软件开发者这份mbedtls移植实践笔记以PDF文档形式完整记录了将mbedtls安全库从源码获取、工程模板搭建、目录整合到编译运行的全流程。资源共1个PDF文件大小仅199KB内容聚焦在可复现的操作步骤上适合当作移植参考手册。文档按实际执行顺序展开先通过git clone拉取mbedtls源码及配套代码模板复制出mbedtls_test工程再拷贝library与include目录之后修改Makefile指定交叉编译工具链将aes.c、sha256.c加入SRCS通过CFLAGS开启MBEDTLS_SELF_TEST宏并在c文件中调用mbedtls_sha256_self_test完成自测最后在QEMU环境中打包buildroot并运行程序验证移植结果。已有333人学习尤其适合刚入门安全库移植或想了解ARM交叉编译流程的开发者能帮助避开Makefile配置、宏定义、工具链选择等常见坑缩短从零到可运行的调试时间为后续在安全启动、TEE等场景中集成mbedtls打下基础。1. 一次 mbedTLS 移植体验自测程序跑通才敢继续往下走做 ARM SoC 底层软件的人几乎都会遇到一个很平凡的诉求安全启动要校验镜像TEE 侧要跑 AES/SHA-256但工程里连一个能用的密码学库都没有。自己写不现实桌面端那套大库又太重硬塞进约束严格的启动镜像里往往得不偿失。mbedTLS 的定位就是这个空隙——为资源受限环境设计RSA、AES、SHA-256 都能按宏裁剪最后编出来的是一个干净的可执行文件或静态库。这次移植的目标不是把整个库编译进某个大系统而是把它接进一个干净的 C 工程模板在 QEMU 模拟环境里跑通 mbedTLS 自测程序。整个过程涉及源码目录切割、Makefile 改造、SELF_TEST 宏启用和交叉编译。适合刚接触 ARMv9 平台、想在芯片开发环境里验证 mbedTLS 的开发者。如果你之前只听说过库里有 library 和 include不知道怎么塞进自己的工程这篇可以帮你省掉几小时的盲试时间。2. 移植前置源码、模板与自测函数怎么落到本地工程2.1 拉代码与固定版本LTS 分支最省心mbedTLS 的源码仓库结构很标准根目录下分 include、library、programs、tests 等目录。真正移植时只需要两个目录include 里放的是对外头文件和默认配置模板library 里放的是全部算法与平台层的 C 源文件。其它目录在裁剪时可以先不看programs 下的示例代码只在需要抄调用方式时才会打开。先把源码和一份干净的 C 工程模板都拉到本地。我个人的习惯是先固定一个 LTS 的 tag而不是直接用默认分支。原因很简单2.28 和 3.x 的配置宏、API 都有差异网上大多数旧的移植笔记基于 2.x如果拉了 master步骤对不上时会很浪费时间。下面的命令用占位符代替仓库地址实际操作时把尖括号部分替换成源码仓库和模板仓库地址即可git clone mbedtls 源码仓库地址 git clone C 工程模板仓库地址 cd mbedtls git checkout v2.28.7 ls include library这里的git checkout v2.28.7是固定版本的关键动作。2.28 是长期支持分支安全固件和 TEE 相关场景里用得很多。ls include library只是确认目录存在不是必须。如果 clone 下来的默认分支已经是 3.x这一步尤其重要否则后面配置宏的名字会对不上。模板工程不需要固定版本它的任务只是提供一套干净的 Makefile 和 src 目录后面我们自己脱离模板单独改编译选项。2.2 模板拷贝与头文件落位cp 命令的三个注意点拿到模板后先复制一份给 mbedTLS 专用命名简单直接比如 mbedtls_test。命令行最容易犯的错是忘掉-r目录拷贝必须递归否则报错或者拷出来是空目录cp native_c_template mbedtls_test -r cp mbedtls/library ./mbedtls_test/ -r cp -r mbedtls/include ./mbedtls_test/ ls mbedtls_test第一条命令把模板目录整体复制一份第二条把 library 下所有源文件拷进新工程第三条把 include 原样拷进去。注意第二条没有加通配符直接拷目录可以避免漏掉隐藏文件或者子目录。拷贝之前要确认 library 目录里没有历史编译产物。如果源码目录之前被编译过.o和.a会被一起拷过来在后续 make 时可能触发文件存在但不需要重新编译的隐蔽问题。我一般在拷之前先执行一次make clean或者干脆只拷*.c文件。library 目录里宁可多一些没用的xxx.c也不要漏掉关键的平台层文件因为交叉编译时的缺失符号排查比多编几个文件更耗时间。2.3 裁剪原则为什么要选源码编译而不是预编译库把 library 整体拷进来之后需要一个明确的裁剪思路。mbedTLS 支持两种接入方式一是编成静态库.a再链接业务代码二是直接让业务工程把选中的.c文件编进最终可执行文件。我强烈建议嵌入式移植用第二种原因是宏裁剪。MBEDTLS_SELF_TEST这类宏会影响头文件里的函数声明和 C 文件里的条件编译。如果用预编译库Makefile 里的宏和库内编译宏不一致时链接可能正常通过但运行起来某些功能没生效这种问题排查起来非常玄学。把源码直接编入工程所有编译单元共用同一组 CFLAGS宏状态是确定的后面自测程序才好定位问题。这里还有一个边界要说清楚直接编源码不代表把整个 library 目录编一遍而是先把需要的算法文件加到 SRCS 里缺什么符号再补什么文件。这样好处是最终可执行文件体积可控并且自测起来路径短。坏处是库内部文件之间的依赖要自己摸清楚这个在第 3 章会具体演示。2.4 自测程序入口SHA-256 与 AES 的 self_test 在哪mbedTLS 每个算法模块都带一个_self_test函数用来在目标板上把算法跑一遍跟标准测试向量比对。移植完成后可以用自测先验证库本身没问题再去找业务问题。自测函数位于对应算法源文件中比如sha256.c里有mbedtls_sha256_self_testaes.c里有mbedtls_aes_self_test。它们是否被编译进去由两个条件共同决定算法宏和MBEDTLS_SELF_TEST宏。源文件自测函数需要打开的头文件宏library/sha256.cmbedtls_sha256_self_testMBEDTLS_SHA256_C, MBEDTLS_SELF_TESTlibrary/aes.cmbedtls_aes_self_testMBEDTLS_AES_C, MBEDTLS_SELF_TESTlibrary/platform_util.c无MBEDTLS_PLATFORM_C编译时除了加-DMBEDTLS_SELF_TEST还要确认MBEDTLS_SHA256_C和MBEDTLS_AES_C没有被手动关闭。如果使用默认配置这两个宏默认是打开的但如果是从其它工程复用 config就得打开检查一遍。自测函数是移植是否成功的直接证据所以第一步先让它跑起来比封装上层接口更优先。2.5 移植后的目录应该长什么样子移植完成后的 mbedtls_test 目录结构大致如下mbedtls_test/ ├── Makefile ├── include/ │ └── mbedtls/ ├── library/ │ ├── aes.c │ ├── sha256.c │ ├── mbedtls_internal_sha256.c │ ├── platform_util.c │ └── ... └── src/ └── main.csrc 放业务入口library 放第三方源码include 直接让别人引用头文件。Makefile 在编译时只需要把-Iinclude和-Isrc加上就能把事情做完。第三方 C 代码不额外套目录层省得以后把 mbedTLS 升级换版本时还要改一大串 include 路径。如果团队规范要求放到 third_party也可以整体移动但移动之后要同步改-I路径这一步容易在大型工程里被忽略。3. 改 Makefile 并编译工具链、SRCS 与 SELF_TEST 宏的配置顺序3.1 CROSS_COMPILE 换成交叉编译工具链由于 mbedTLS 最终要在 QEMU 模拟的 ARM SoC 环境里跑x86 上编译出的二进制不能直接用必须指定交叉编译工具链。不同项目工具链前缀差异很大但变量抽象是一致的用一个CROSS_COMPILE前缀后面拼接 gcc、ar、strip。这样换平台时只改一行CROSS_COMPILE ? arm-linux-gnueabihf- CC : $(CROSS_COMPILE)gcc AR : $(CROSS_COMPILE)ar STRIP : $(CROSS_COMPILE)strip如果你要在 64 位的 ARMv8/ARMv9 模拟环境里跑前缀可能是aarch64-none-elf-或者aarch64-linux-gnu-取决于工具链和 sysroot。QEMU 用户态模拟下arm-linux-gnueabihf-的产物通常配qemu-armaarch64-linux-gnu-的产物配qemu-aarch64。后面验证时会用到。使用?不是为了花哨而是允许命令行执行make CROSS_COMPILEaarch64-linux-gnu-覆盖不需要修改 Makefile。交叉编译的环境变量在命令行临时指定通常比在 Makefile 里写死可维护性更好。3.2 SRCS 里放了哪些文件用到哪几个算法就放哪几个这里要明确一点直接编译源码不等于把整个 library 编一遍。裁剪从 SRCS 就要开始。下面这个示例是一个能跑通 SHA-256 与 AES 自测的最小集合SRCS : src/main.c \ library/aes.c \ library/sha256.c \ library/mbedtls_internal_sha256.c \ library/platform_util.c \ library/platform.c逐个解释aes.c提供 AES 自测和加解密 APIsha256.c提供 SHA-256 算法mbedtls_internal_sha256.c是内部实现辅助文件某些配置下必须一起编platform_util.c提供内存清零等基础函数很多算法文件都引用它platform.c提供时间、打印等平台抽象。如果在链接时报找不到mbedtls_zeroize之类的符号基本是platform_util.c没放进来。这种缺符号是移植里最典型的连锁反应库内部文件之间有依赖不是加了一个算法源文件就完事。把这个现象反过来想也能用来判断 SRCS 是否已经够用只要链接阶段没有报 undefined reference说明当前算法闭合了。后续添加 RSA 或 ECC 时还要再增加rsa.c、ecp.c、bignum.c等文件仍然按报错逐个补。3.3 CFLAGS 与 configSELF_TEST 宏和自定义配置头mbedTLS 的裁剪主要靠 CFLAGS 里的宏。下面是这次工程里实际用到的三行CFLAGS -Iinclude -Isrc CFLAGS -DMBEDTLS_SELF_TEST CFLAGS -DMBEDTLS_CONFIG_FILE\mbedtls_config.h\第一行设置头文件搜索路径第二行打开自测宏让算法源文件里的自测函数参与编译第三行指定自定义配置头。第三行不写时mbedTLS 会使用默认配置默认配置里几乎所有算法都开着虽然能编过但最终体积和交叉编译时间都不理想。这里有一个新手容易忽略的细节-DMBEDTLS_CONFIG_FILE\mbedtls_config.h\的引号转义。Makefile 解析后传给编译器的内容是-DMBEDTLS_CONFIG_FILEmbedtls_config.hC 语言里的 include 指令才能正确把文件名当字符串用。如果少一个反斜杠预处理时就报fatal error: mbedtls_config.h: No such file or directory。遇到这个报错先别改路径绝大多数是刚才说的转义问题而不是文件不存在。3.4 在 main.c 里调用 SHA-256 与 AES 自测自测程序不需要做复杂业务直接在 main 里依次调用即可#include stdio.h #include mbedtls/sha256.h #include mbedtls/aes.h int main(void) { int ret 0; ret mbedtls_sha256_self_test(1); if (ret ! 0) { printf(sha256 self test failed: %d\n, ret); return -1; } ret mbedtls_aes_self_test(1); if (ret ! 0) { printf(aes self test failed: %d\n, ret); return -1; } printf(all self tests passed\n); return 0; }mbedtls_sha256_self_test和mbedtls_aes_self_test都接收一个 verbose 参数传 1 时会把每个测试向量是否通过打印出来传 0 只返回最终结果。返回值 0 代表通过其它值从负的MBEDTLS_ERR错误码到非零整数都有可能。第一次跑推荐传 1既能确认算法工作又能从打印信息里看到测试向量描述后续封装业务接口时再改成传 0。3.5 编译与检查产物file 命令和体积参考Makefile 改好后编译动作本身很简单make clean make 21 | tee build.log file mbedtls_selftest_demo ls -lh mbedtls_selftest_demomake clean是为了清掉模板工程里可能残留的旧对象文件。make 21 | tee build.log让错误和标准输出都进日志排查问题不用反复重开终端。file命令用来确认产物架构如果输出ELF 32-bit LSB executable, ARM说明交叉编译生效如果输出x86-64说明 Makefile 里CC变量没有生效回到 3.1 检查前缀。体积方面只编 SHA-256 和 AES 的最小自测程序去掉调试信息后通常在几十 KB 量级。如果编译出来一两百 KB 甚至更大优先怀疑默认配置的宏开太多。此时可以用$(STRIP)对产物瘦身但更根本的问题是裁剪没到位后面集成进安全启动镜像时镜像体积会直接卡你。3.6 编译后核对自测符号别等到运行才发现编译通过后先用交叉工具链的 nm 看一眼产物里是否有自测函数符号避免把问题留到 QEMU 运行阶段arm-linux-gnueabihf-nm mbedtls_selftest_demo | grep -E self_test如果输出里有mbedtls_sha256_self_test和mbedtls_aes_self_test说明宏生效。如果什么都没有即使编译通过了运行时也不会走到自测流程。这个检查比直接跑模拟器更快。自测函数会不会被链接优化掉取决于编译选项比如-ffunction-sections配合链接时未引用不保留因此 nm 检查能确认可执行文件里是否存在该符号但要确认是否真的被调用还需要回到 main.c 看调用链。这个技巧在交叉编译里很实用因为目标板上跑起来成本高先用静态检查把明显的问题拦下来。实际上我每次移植完都会跑一遍这个命令特别是在更换工具链版本之后。4. 移植避坑五个典型翻车现场与排查路径移植 mbedTLS 的最大特点是你以为编译过了就没事真正的问题其实都藏在配置宏和源文件的咬合关系里。下面按出现频率整理这次移植中踩过的五个典型问题。4.1 config 头文件找不到现象、原因、解决现象编译时第一行就报fatal error: mbedtls_config.h: No such file or directory或者config.h找不到。原因默认配置头在新旧版本里名字不一样。旧工程里叫config.h新版本叫mbedtls_config.h。Makefile 里MBEDTLS_CONFIG_FILE指到了一个不存在的文件头文件搜索路径没指到include也会是同样表现。解决先用ls include/mbedtls/确认配置文件具体名再把CFLAGS -I指到include目录。如果配置宏里指定了文件把反斜杠和双引号的转义写对。这个错误最常见的原因其实是路径写成了-Iinclude/mbedtls而 mbedTLS 源文件里的写法是#include mbedtls/xxx.h头文件实际搜索路径应该从工程根目录下开始多了或少了层级都会挂。4.2 SELF_TEST 宏没生效函数有声明但链接不到现象main.c 里调用了mbedtls_sha256_self_test编译不报错链接时报undefined reference to mbedtls_sha256_self_test。或者更隐蔽链接过了但运行输出显示程序直接跳过了自测。原因宏只在主程序里定义了算法文件在另一个编译命令里编译CFLAGS 不一致。Makefile 中有的目标使用旧变量没有追加宏导致算法 .c 文件编译时MBEDTLS_SELF_TEST没开自测函数被条件编译掉了。解决把宏统一加到全局CFLAGS不要只加在某一个 rule 上。用make V1打印每条编译命令肉眼检查算法文件是否带上了宏。这是排查宏不统一最快的方式不要只看链接错误去补函数。4.3 链接缺失内部符号platform_util 引发的连锁反应现象加上 aes.c 和 sha256.c 后链接报undefined reference to mbedtls_platform_zeroize或mbedtls_platform_gmtime_r。原因mbedTLS 的算法层不是孤立的。比如 AES 内部清除密钥调的是平台层提供的安全清零函数时间相关功能依赖平台抽象。裁剪时如果 Makefile 只放了两个算法 .c 文件这些基础符号就会缺。解决直接在报错信息里找符号名再到 library 目录里搜定义。一般最先缺的是platform_util.c后续可能还会缺constant_time.c取决于版本和配置。补齐之后重新 make直到没有未定义符号。这个补文件的过程不需要一次猜对报一个补一个反而是最稳的。4.4 QEMU 里自测跑飞栈对齐和字节序的坑现象在模拟环境里执行./mbedtls_selftest_demoAES 自测输出到一半程序崩溃或者 SHA-256 的结果和标准向量不一致但编译平台上完全没问题。原因常见情况有三种。第一是交叉编译工具链与 QEMU 模拟的 CPU 不匹配比如用带 VFP 的gnueabihf工具链QEMU 默认 CPU 较旧时遇到非法指令崩溃第二是栈指针在入口处没有 8 字节对齐C 库或向量处理有时依赖对齐第三是字节序配置错ARM 大多数场景是小端但模拟环境启动参数偶尔会把 CPU 指定成大端。解决先换一种模拟方式缩小问题范围。用qemu-arm -cpu cortex-a9或qemu-aarch64 -cpu cortex-a53指定与工具链匹配的 CPU加上-L指向工具链 sysroot。栈对齐问题从启动文件和链接脚本里查 sp 初始化与对齐要求必要时调整链接参数。字节序则用file输出和lscpu类指令确认。这类问题很难一步到位通常做法是先跑一个只有 SHA-256 的最小 demo再逐步加 AES二分定位。4.5 不要迷信默认配置裁剪宏与自测宏的关系现象程序能编译能运行但自测返回-1或者函数返回MBEDTLS_ERR_SHA256_BAD_INPUT_DATA。原因默认配置文件把大部分功能都打开了算法文件之间存在大量交叉耦合。有些算法宏开着但对应源文件没有编进 SRCS运行时调用的接口找不到类型定义或没有实现表现为错误码而不一定是崩溃。解决自测环境不要用默认配置跑全功能。先把需要的算法宏在配置头里列清楚其它关掉。裁剪宏和MBEDTLS_SELF_TEST要同时出现。这个环节没有捷径编译一次查一次依赖比网上找现成配置可靠得多。后面要加 RSA 或 ECC 时也需要按同样的方式把新宏和源文件配对。5. QEMU 上验证移植结果两个技巧和一个固定检查习惯5.1 先用 qemu-user 快速验证自测打包完整根文件系统之前可以先在用户态模拟里跑一次。静态编译的可执行文件不需要额外装载器只要有对应的系统调用模拟就能跑qemu-arm -L /usr/arm-linux-gnueabihf ./mbedtls_selftest_demo-L指定工具链的 sysroot 路径用于加载动态库和解析路径。如果前面用静态编译-L甚至可以省掉但加上更保险。对应 64 位环境命令换成qemu-aarch64 ./mbedtls_selftest_demo。这一步跑通后基本可以确认算法和编译参数没问题再进完整镜像能省掉大量反复打包的时间。5.2 再进根文件系统镜像跑完整流程用户态模拟验证的是库本身完整镜像验证的是集成结果。把编译产物放到根文件系统目录重新打包镜像启动 QEMU 之后执行即可cp mbedtls_selftest_demo output/target/root/ make qemu-system-arm -M vexpress-a9 -nographic -kernel kernel_image -sd rootfs_image启动后直接进入 shell运行./mbedtls_selftest_demo。这里有一个容易忽略的细节可执行文件尽量静态编译否则 rootfs 里要带上对应的动态库和链接器缺失时启动命令没有任何提示程序直接找不到入口。runtime 阶段的报错大多不是代码问题而是文件系统里少了东西。验证时优先看串口输出自测函数传了 verbose1会在串口打出一长串测试向量状态看到[ OK ]说明算法确实在目标环境里跑通了。从那以后我每次移植完第三方 C 库都强制走一遍自带 self_test再封装业务接口省得后面应用层跑挂了说不清楚是库的问题还是我的问题。这套流程如果用现成的移植模板二十来分钟可以走完我把模板和 Makefile 整理进了资源包拿到手按第 2 章的顺序核对目录结构再照着第 3 章配置宏就行。希望帮到你。本文还有配套的精品资源点击获取