嵌入式开发实战:为Android设备交叉编译mmc-utils工具集
1. 为什么需要交叉编译mmc-utils在嵌入式开发中我们经常需要与eMMC存储设备打交道。mmc-utils就是这样一套专门用于管理eMMC存储设备的实用工具集它提供了读取extcsd、修改分区配置、设置写保护等强大功能。但问题来了——Android设备通常没有预装这些工具而直接在设备上编译又几乎不可能这就是交叉编译技术大显身手的时候了。交叉编译简单说就是在PC上编译出能在ARM架构Android设备上运行的程序。我最近在调试一块基于Rockchip SoC的开发板时就遇到了这个问题需要修改eMMC的boot分区配置但设备上缺少必要的工具。通过交叉编译mmc-utils最终成功解决了这个问题。2. 搭建编译环境2.1 获取源码第一步当然是获取mmc-utils的源代码。官方仓库托管在GitHub上我们可以直接克隆git clone https://github.com/mhei/mmc-utils.git cd mmc-utils建议使用最新release版本而非master分支因为主分支可能包含未经验证的改动。我上次就踩过坑用了master分支导致编译出的工具在设备上运行崩溃。2.2 准备交叉编译工具链Android环境下有两种主要编译方式AOSP完整编译环境适合已经在做系统级开发的团队Android NDK独立工具链更适合单独编译某个工具对于大多数开发者我推荐使用NDK方式因为它更轻量。下载NDK后需要生成独立的工具链# 假设NDK安装在/opt/android-ndk /opt/android-ndk/build/tools/make-standalone-toolchain.sh \ --archarm \ --platformandroid-21 \ --install-dir/opt/ndk-standalone这里有几个关键参数需要注意--arch必须匹配目标设备的CPU架构arm/arm64/x86等--platformAndroid API级别建议不低于设备系统版本--install-dir工具链安装路径3. 解决编译问题3.1 头文件缺失问题直接编译通常会遇到第一个错误找不到endian.h头文件。这是因为Android的C库与标准Linux有所不同。解决方法是在mmc_cmds.c文件开头添加#include endian.h如果仍然报错可以尝试使用Android提供的替代方案#include sys/endian.h3.2 结构体初始化问题第二个常见问题是结构体未完全初始化导致的编译错误。在mmc.c文件中需要补全commands数组的所有字段。原始代码可能像这样{ do_read_extcsd, -1, extcsd read, device\n Print extcsd data from device., NULL }需要修改为{ do_read_extcsd, -1, extcsd read, device\n Print extcsd data from device., NULL, NULL, 0 }这个修改确保了结构体的所有字段都被正确初始化。我在第一次编译时忽略了这个问题导致工具运行时出现段错误排查了很久才发现是这个原因。4. 编译与部署4.1 使用Android.mk编译如果你使用AOSP环境可以创建一个Android.mk文件LOCAL_PATH : $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE : mmc_utils LOCAL_SRC_FILES : mmc.c mmc_cmds.c LOCAL_CFLAGS : -Wall -Werror LOCAL_MODULE_TAGS : optional include $(BUILD_EXECUTABLE)然后在AOSP根目录下执行source build/envsetup.sh lunch 你的设备配置 mmma external/mmc-utils编译完成后可执行文件会生成在out/target/product/设备/system/bin目录下。4.2 使用NDK独立编译如果使用NDK工具链需要修改MakefileCC /opt/ndk-standalone/bin/arm-linux-androideabi-gcc CFLAGS -Wall -Werror -pie -fPIE LDFLAGS -pie -fPIE all: mmc_utils mmc_utils: mmc.o mmc_cmds.o $(CC) $(LDFLAGS) -o $ $^ clean: rm -f *.o mmc_utils编译命令很简单make4.3 部署到设备无论哪种方式编译出的二进制文件都需要推送到设备上adb push mmc_utils /data/local/tmp/ adb shell chmod x /data/local/tmp/mmc_utils如果需要永久安装可以推送到/system/bin需要root权限adb remount adb push mmc_utils /system/bin/ adb shell chmod 755 /system/bin/mmc_utils5. 实际使用案例5.1 读取eMMC信息最基本的用法是读取eMMC的extcsd信息mmc_utils extcsd read /dev/block/mmcblk0这个命令会输出eMMC的所有配置参数包括设备容量分区大小支持的命令集当前工作模式我在调试一个启动问题时就是通过这个命令发现boot分区大小配置不正确导致的。5.2 修改boot分区配置如果需要修改boot分区设置可以使用mmc_utils bootpart enable 1 1 /dev/block/mmcblk0这个命令启用第一个boot分区参数1设置需要ACK确认第二个参数1操作设备/dev/block/mmcblk0特别注意很多eMMC操作是一次性编程OTP的修改后无法恢复操作前务必确认参数正确。5.3 RPMB安全区域操作对于安全要求高的场景可能需要操作RPMB区域# 写入RPMB密钥 echo -n 32字节密钥内容 rpmb_key.bin mmc_utils rpmb write-key /dev/block/mmcblk0rpmb rpmb_key.bin # 读取RPMB计数器 mmc_utils rpmb read-counter /dev/block/mmcblk0rpmbRPMB操作需要特别注意权限问题普通应用通常无法直接访问这些设备节点。6. 常见问题排查6.1 权限问题在非root设备上可能会遇到权限不足的错误error: could not open device /dev/block/mmcblk0解决方法使用root权限运行修改设备节点权限需要root使用已经有权访问的组如system组6.2 版本兼容性问题不同eMMC版本支持的指令可能不同。如果遇到invalid command错误可以先检查extcsd中的eMMC版本mmc_utils extcsd read /dev/block/mmcblk0 | grep eMMC Version6.3 工具运行崩溃如果工具运行直接崩溃可能是编译时架构不匹配如用ARMv7工具链编译但设备是ARMv8缺少必要的库文件可以用NDK静态编译解决内核版本不兼容7. 进阶技巧7.1 静态编译为避免依赖问题可以静态链接所有库。在Makefile中添加LDFLAGS -static这样编译出的二进制文件会稍大但可以在任何同架构设备上运行。7.2 调试符号遇到问题时可以保留调试符号CFLAGS -g编译后用ndk-stack工具分析崩溃日志adb logcat | ndk-stack -sym ./obj/local/armeabi-v7a/7.3 自动化脚本对于需要频繁执行的操作可以编写shell脚本#!/system/bin/sh # 备份当前extcsd配置 mmc_utils extcsd read /dev/block/mmcblk0 /sdcard/extcsd_backup.txt # 修改boot配置 mmc_utils bootpart enable 1 1 /dev/block/mmcblk0 # 验证修改 mmc_utils extcsd read /dev/block/mmcblk0 | grep BOOT_CONFIG记得给脚本执行权限chmod x /sdcard/emmc_config.sh