简介一份面向嵌入式与 Android 调试场景的 adb 工具完整源码包适配开发者手动交叉编译生成不同架构的可执行文件。资源整体为 tgz 压缩归档大小约 41.9MB作者在 ARM 设备上实测编译通过生成的 adb 可正常用于设备连接、shell 交互与文件下发等调试操作平台暂未提供包内文件明细因此不罗列具体目录。对需要在 arm/arm64/x86 等平台定制 adb 的工程师来说可直接参照 README 完成构建免去从源码收集依赖、排查环境的重复劳动适合具备基础 Linux 操作能力的开发者和希望深入理解 adb 源码结构的读者。资源已有 180 人学习浏览属于轻量但实用的工具源码既能快速产出可用程序也能作为 ADB 协议学习、交叉编译练习的参考。1. 拿到 arm_adb-master_default.tgz 先别急着解压它是 ARM 设备调试里最容易被低估的一个包看到 arm_adb-master_default.tgz 这个压缩包时十个工程师里有九个会先tar zxvf解出来再说然后对着散落一地的二进制和脚本发呆半小时。这种事我干过不止一次。这个包名拆开看很直白arm_adb 是面向 ARM 架构编译出来的 adb 调试工具集合master_default 多数来自 SDK 的构建系统——master 是代码分支或发布通道标记default 是默认配置工程通常对应一块开发板或一个产品的基础配置。它解决的是标准 adb 在 ARM 目标机上“跑不起来、装不进去、连不上”这三类问题。下面我会从拆包前的判断开始讲到装进设备、配通连接最后把常见的翻车点按现象拆开讲。2. 拆包前的三个动作看清单、认架构、分清 adb 与 adbd2.1 master 和 default 到底代表什么先别急着把 tgz 当“版本号”看。我拿到这类包的第一件事是把它当作“工程产物”来对待而不是一个普通压缩包。master 在大多数 SDK 的发布流程里代表构建时的代码分支或发布通道default 则更接近“默认配置工程”的含义。很多厂商的构建系统会为某块开发板维护一套 defconfig或者干脆叫 default 工程里面锁定了内核配置、库版本、编译选项最后打包时就把分支名加配置名拼到了文件名上。换句话说arm_adb-master_default.tgz 大概率不是某个人手动 tar 出来的而是 CI 或发布脚本生成的。为什么这个区分重要因为它决定了你后续排查问题的方向。如果它是 CI 产物包里的二进制会比较干净、可复现碰到问题时应该优先怀疑运行环境如果它是某工程师手工打包的里面很可能混有本地路径、调试残留甚至库版本和二进制不配套。我见过一个包解出来 adbd 是新的但配套的 libc 还是老的装到设备上跑起来就段错误查了两天才发现是打包的人忘了重新拷贝库。另外“default”不一定代表“官方推荐”。在很多 SDK 里default 工程只是拿来验证编译链路的最小配置连 adb 都不是默认开启的。所以你能在这个包里看到 adb/adbd只能说明构建脚本包含了它不能说明这套配置已经被产品验证过。拿到包后第一件事不是看说明文件而是看文件清单和二进制属性这是下面两步要做的事。2.2 tar -tzf 先看文件清单不解包也能判断布局很多人习惯直接tar xzvf这是第一个翻车点。包里有几十个文件没搞清楚布局就解包往往把一堆 .so 和二进制散到当前目录污染工程目录后面还得一个个清理。更稳的做法是先用tar -tzf列清单tar -tzvf arm_adb-master_default.tgz | head -60参数拆开讲z表示 gzip 压缩t是列出内容而不解包v会输出权限、属主、大小、日期这些详细信息f指定文件。head -60只是限制输出行数防止文件特别多刷屏。我一般还会把结果存下来慢慢看tar -tzf arm_adb-master_default.tgz manifest.txt wc -l manifest.txt grep -E adb|adbd|\.so$ manifest.txt这样做的意义是让你在解包之前就对包的布局有预判。常见的 arm_adb 包内会有几类东西adb 客户端、adbd 服务端、若干动态库、一个 .conf 或启动脚本偶尔还有一份说明文档。如果包内只有一个 adb 可执行文件没有 adbd那这个包大概率只是给 ARM 宿主机用的客户端不能直接拿来当设备端服务用如果 adbd 和 adb 都在多半是整套调试工具链。还有一个容易被忽略的细节查看 manifest.txt 时留意文件权限位。tar 会保留权限如果包内 adbd 的权限不是-rwxr-xr-x而是-rw-r--r--解包后直接跑一定会报权限错误。这种问题在手工打包的包里很常见提前看到可以省一轮排障。另外留意清单里的第一层目录有的包顶层是一个arm_adb-master/文件夹有的直接散在根上。有顶层目录的解压时可以用--strip-components1剥掉一层散在根上的就得自己先建目录再解。我个人偏好先建空目录再解包目录名带日期后缀多个版本并存也不冲突。2.3 file 与 readelf确认二进制架构和动态依赖清单只能告诉你“有什么”不能告诉你“能不能在这块板子上跑”。下一步是确认架构用file看二进制属性file adbd adb典型输出大概是ELF 32-bit LSB executable, ARM, EABI5或者ELF 64-bit LSB shared object, ARM aarch64。前者是 32 位 ARM 程序后者是 64 位。这里要提醒一句很多 default 工程在 ARMv8 平台上仍编译 32 位用户态程序所以“板子是 64 位”不代表“必须用 aarch64 的 adb”关键看你的用户态环境是 32 位还是 64 位。在设备上跑uname -m只能看到内核架构要看用户态环境用file /system/bin/sh或ls /lib这类实际文件更可靠。file输出只能确认架构大类还要看动态链接依赖。ARM 设备上最容易翻车的不是架构不匹配而是缺库readelf -d adbd | grep NEEDED这条命令列出 adbd 运行时需要加载的共享库。如果依赖里有libgcc_s.so.1、libc.so.6这类你得确认设备系统里存在对应库且版本不低于编译时要求的版本。嵌入式 rootfs 为了精简经常砍掉一部分库特别是libstdc、librt、libpthread这些看似“不必要”的库。缺库的症状通常是 adbd 一启动就退出dmesg 里看不到明显报错非常容易误导排查方向。另外readelf -d输出里如果有RPATH或RUNPATH字段也值得看。如果标了$ORIGIN说明程序会优先从自己所在目录找库你单独把 adbd 拷到别的目录库找不到就会崩没标则走系统默认路径。这两种情况拷贝策略完全不同。最后再用readelf -A adbd看 ARM 属性段比如Tag_CPU_arch: ARMv7还是ARMv8。如果你的目标芯片是 ARMv7 平台装 ARMv8 的包跑不起来反过来把 ARMv7 的包塞进 ARMv8 平台通常能跑但可能被新内核的 selinux 策略拦截。这个判断要提前做不要等到 adbd 起不来才回头看。3. 装入 ARM 目标机的两条路线内置 adbd 与宿主机侧复用3.1 路线一把 adbd 放进设备系统最常见的需求是把 adbd 装到 ARM 设备上让 PC 端 adb 能连进去调试。这里分两类设备Android 类车机、电视盒子的系统镜像和纯 Linux 类工控屏、边缘网关。Android 类设备一般已经有 adbd包里的版本通常用于替换年久失修的旧版纯 Linux 类设备才是这个包的主要用处因为标准 Linux 发行版默认不装 adbd。先建一个干净的临时目录再解包别在工程根目录里直接解mkdir -p /tmp/arm_adb cd /tmp/arm_adb tar xzf /path/to/arm_adb-master_default.tgz find . -maxdepth 3 -type f -name adbd*解包后先确认 adbd 存在再拷贝到目标设备的可执行目录。Android 系统常见做法是放/system/bin或/vendor/bin但多数可写系统需要 adb remount 后才能操作纯 Linux 设备放到/usr/bin或/usr/local/bin都行。我一般先推到/data/local/tmp试跑而不是直接覆盖系统里的 adbdadb push adbd /data/local/tmp/ adb shell chmod 755 /data/local/tmp/adbd adb shell /data/local/tmp/adbd 这里把 adbd 先推到临时目录的原因是调试阶段直接覆盖系统文件一旦新 adbd 有问题重新刷机才能救回来代价太大。先在临时目录跑起来确认稳定后再放到最终路径。注意chmod 755这一步不能省adb push不会保留你本地的执行权限推过去的文件默认是 644少跑一次 chmod后面就是权限报错。最后的让 adbd 在后台运行shell 退出后进程继续存在如果设备有 init 管理4.2 会讲就不需要手动后台运行。如果设备上原本有 adbd 而且在跑替换前先备份adb shell cp /system/bin/adbd /data/local/tmp/adbd.bak adb shell /data/local/tmp/adbd.bak 备份的回报在于新 adbd 连不上或反复崩溃时一条命令切回旧版现场调试不至于卡死。3.2 路线二ARM 版 adb 客户端跑在 ARM 宿主机上第二个需求容易被忽略包里的 adb 客户端也能直接在另一台 ARM 设备上跑。很多嵌入式现场没有 x86 PC只有一台 ARM 工控机当产测主机。标准 adb 发行版基本只有 x86_64 二进制你在 ARM 主机上跑必然报 “Exec format error”这时包里的 ARM 版 adb 就是应急通道。./adb devices ./adb connect 192.168.1.100:5555把包解压到 ARM 宿主机的某个目录下用./adb显式调用避免和系统里的 x86_64 adb 混淆。这里有个血泪教训很多人把 ARM 版 adb 重命名成adb丢进/usr/bin然后某天升级系统时被同名包覆盖又变回 x86_64 版本现场调试半天才发现调错了二进制。我现在的习惯是保留包的原始目录结构用完整路径调用或者建一个adbar别名让它和系统 adb 明确区分开。ARM 版客户端和 PC 版在功能上没有本质差别adb shell、adb push、adb reverse都能用。但有一个缓存问题adb客户端会在~/.android/adbkey生成一对 RSA 密钥用于和设备端 adbd 做认证。如果你把 ARM 版 adb 拷到多台宿主机且共享同一个 home 目录多台主机会共用同一对密钥插上设备时会遇到“已经信任但连不上”的诡异问题清掉~/.android/adbkey*重新配对即可。3.3 选型什么时候用包内 adb什么时候用系统自带不是所有场景都需要这个 tgz 里的东西选错了反而多一层麻烦。下面是我常用的判断逻辑场景推荐做法原因设备系统里有可用的 adbd只是版本旧先用系统自带确认问题再替换避免引入架构或库依赖问题纯 Linux ARM 设备没有任何调试通道用包内 adbd 手动拉起最快的临时通道不必重刷系统ARM 宿主机需要调试另一台 ARM 设备用包内 adb 客户端完整路径x86_64 adb 在 ARM 机器上必然跑不了想把调试能力固化到产品镜像不要直接拷二进制建议重新编译或对照打包包里的库和配置未必适配你的 rootfs表格最后一行多说一句产品镜像里的 adbd我一般不直接从这种分发包里拷贝而是建议用同套 SDK 重新编译一次至少要把依赖库一起放进镜像。之前在某项目的联调阶段直接拷包内 adbd 替换对方编译时用的 glibc 版本比我们镜像高了半个版本adbd 起来就崩最后重新编了一版才稳定。包能解开不代表你的系统能跑这是最贵的认知。4. 让 adb 在 ARM 设备上真正连得通三处必调配置与最小验证序列4.1 USB 权限与设备节点udev 规则和 usb 模式adb 连不上的第一道坎往往不在包内而在 USB 权限。PC 端 Linux 默认不会给普通用户访问 USB 调试设备的权限你可能lsusb看到设备但adb devices永远空白或提示 no permissions。标准解法是写 udev 规则SUBSYSTEMusb, ATTR{idVendor}VENDOR_ID, MODE0666, GROUPplugdev把这行保存到/etc/udev/rules.d/51-android.rules然后执行sudo udevadm control --reload sudo udevadm trigger。这里的VENDOR_ID要换成实际设备的厂商 ID。设备连接后跑lsusb能看到比如Bus 001 Device 002: ID 1234:5678里的1234就是。如果手头一时没有具体 ID可以先adb kill-server adb start-server重启 adb 服务重新枚举但这只是缓解不写规则每次插拔都要折腾一遍。设备端同样有开关。Android 设备要在开发者选项里打开 USB 调试纯 Linux 设备要看内核是否启用了 USB gadget 和 adb 相关功能。如果设备内核没有对应的 gadget 配置USB 线插上去根本没反应这种问题不是 adb 配置能解决的得回到内核配置。所以我一直建议在选型阶段确认官方系统镜像自带 adb 支持可以省掉一整层麻烦。4.2 adbd 的监听配置端口、tcpip 与 init 脚本USB 通了之后很多人还想要网络 adb。设备不在手边、只有 IP 可达时这个功能很实用。配置网络监听的命令序列如下adb tcpip 5555 adb connect 192.168.1.100:5555第一行的adb tcpip 5555通知设备端 adbd 切换到 TCP 监听模式端口 5555第二行让 PC 端主动连接。跑完adb devices应该能看到192.168.1.100:5555 device之后的操作和 USB 模式完全一致。想回到 USB 模式插上线下adb usb即可。如果是手动拉起的 adbd没有 init 管理默认监听方式可能不响应网络连接。Android 类设备可以这样设adb shell setprop service.adb.tcp.port 5555 adb shell stop adbd adb shell start adbdsetprop设置了 adb 服务的 TCP 端口属性stop/start adbd是让 init 重启 adb 守护进程使配置生效。这三个命令的顺序不能混先设属性再停再启。直接start adbd不会重新读端口配置。纯 Linux 设备没有setprop概念可以用adbd -p 5555或环境变量指定端口具体参数因编译选项而异不确定时跑adbd --help看一眼。对产品固化场景与其每次手动 setprop不如把 service 写进 init 脚本。Android 在 init.rc 里加一段纯 Linux 在 systemd 或 sysvinit 加服务service adbd /system/bin/adbd user root group root shell disabled seclabel u:r:adbd:s0disabled表示默认不启动需要时再start adbd。seclabel这行在 Android 上尤其重要如果你替换了系统自带 adbd而 selinux 策略里没有为它定义域init 会直接拒绝拉起。这个问题在 5.2 里还会碰到。4.3 最小验证命令序列从 devices 到 shell配置完不要急着跑业务先用最小命令序列验证通道真的通了。我的顺序是固定的adb kill-server adb start-server adb devices -l adb shell echo okkill-server和start-server用于清理旧状态。刚改过 udev 规则或换过 adb 二进制时不做这一步服务可能还拿着旧设备列表。adb devices -l列出设备-l额外显示设备型号信息能帮助区分多个设备。最后adb shell echo ok才是真正的验证只要 shell 能跑说明从客户端到服务端的链路已经打通接下来可以放心做 push、forward、gdb 这些操作。多台设备同时连时记得用adb -s serial指定设备否则 adb 会报 “more than one device”。这里有个常见误区看到adb devices列出了设备就算成功。实际上 offline 状态的设备也会被列出只是后面带 offline 标记只有看到device才算可用。另外连接网络设备时输出里可能会出现daemon started successfully这类信息别慌它们只是状态提示最终以 devices 列表为准。5. arm_adb 配套排查与避坑offline、反复重启与权限玄学5.1 现象adb devices 能看到但一直 offline这个问题的出现频率最高。现象是adb devices里设备在列但状态是 offline等多久都不变。原因绝大多数时候是 adbd 版本和 adb 客户端的协议不匹配或者设备端 adbd 已经进入半死不活的状态服务异常但进程没退。还有一种少见但真实的情况USB 线质量差或用了延长线传输层不稳定设备反复断开重连最终停在 offline。解决方式是按层级来先重启两端服务——PC 端adb kill-server设备端把 adbd 进程杀掉或直接重启无效就换 USB 线几十块的充电线能充电不代表能传数据还不行就考虑协议版本把 PC 端 adb 换旧一版试试因为新版客户端对认证和握手流程更严格。我踩过最深的坑是设备端 adbd 协议版本偏老PC 端 adb 太新握手信息对不上最后降级 PC 端 adb 才解决。5.2 现象adbd 启动后反复重启手动拉起的 adbd 出现“起来一下、过几秒消失”的情况是另一类高频翻车。先看日志Android 上跑logcat -d | grep adbd纯 Linux 设备看dmesg | tail或journalctl。原因通常有两个方向一是缺少动态库依赖adbd 启动时加载失败脚本或 init 又把它拉起来形成死循环二是 selinux 权限拦截Android 系统里如果 adbd 的上下文不对内核会在启动时直接杀掉进程。还有第三种adbd 端口被占用起第二个实例时 bind 失败退出脚本又反复拉起。解决缺库就用 2.3 里的readelf -d逐个核对缺哪个补哪个selinux 问题在测试设备上可以临时setenforce 0确认确认后把 seclabel 和策略修正端口占用用netstat -tlnp | grep 5555或lsof -i:5555找占用进程。手动拉起的 adbd 如果反复被杀还要确认运行用户权限——有些 adbd 编译时要求 root 身份用普通 shell 用户拉起会直接退出。5.3 现象adb push 到一半断连报 permission deniedpush 大文件或连续 push 多个文件时传输中断或报failed to copy ... Permission denied。第一个原因是目标目录的写权限。adb push默认用 shell 用户身份很多系统目录 shell 用户没有写权限比如/system这类分区还得先 remount。第二个原因是设备端存储空间不足或 inode 用尽adb pull有时反而能成功所以容易被误导成“只有 push 有问题”。第三个原因是 adbd 的连接数或内存限制网络 adb 模式下更容易触发。解决先 push 到有权限的目录比如/data/local/tmp再在设备端用mv移动到最终位置检查df -h和df -i网络模式传大文件时改回 USB 模式。我个人习惯是大文件一律先传到/data/local/tmp校验大小后再移动这样即使断连重传代价也小。5.4 现象ARMv7 的 adbd 在 64 位系统上直接段错误设备能跑起来但 adbd 段错误或提示cannot execute binary file。这个问题在 2.3 提过这里展开。原因是架构不匹配目标系统用户态是 aarch64而你塞进了 armv732 位的 adbd。如果报的是段错误而不是“无法执行”说明内核开启了 32 位兼容支持能加载程序但运行时兼容层有问题比如缺少 32 位动态库。解决确认用户态架构后找对应的 aarch64 或 armv7 版本。不要以为“都是 ARM 就通用”ARMv7 和 AArch64 的指令集、调用约定、动态库路径都不兼容。如果这个 tgz 里两种架构都有一定按file输出选不要按文件名猜。踩过这个坑的人现在都会在解包后第一时间做一次find加file的混合遍历把全部二进制体检一遍。5.5 现象包内 adb 版本太老认不了新设备这个坑在旧 SDK 的包里特别多。现象是包内 adb 在 PC 上运行正常但连较新的手机或开发板时设备列表能看到状态一直是unauthorized或device unknown。原因是新版 Android 的 adb 认证协议升级了旧版本客户端没有对应的密钥处理逻辑或者设备端 adbd 要求的协议版本高于客户端支持范围。这不是配置问题是版本断层。解决优先用包内的 adbd 服务端PC 客户端换用较新的版本。客户端和服务端版本不要求一致只要握手协议兼容即可。反过来如果 PC 端 adb 无法更新就别硬连新设备改用串口或网络方式进入系统。我曾经在某项目的产测工具里用混搭方案设备端用包内 adbd定制过必须用PC 端用新版 adb 客户端握手完全没问题。6. 进阶把 arm_adb-master_default.tgz 变成一套可复用的调试工具链6.1 用 adb forward 打通设备内网端口调试嵌入式设备时设备端往往有 web 服务、modbus 服务或 gdb server但网络隔离让 PC 直连不了。用adb forward把设备端口映射到 PC 上立刻就能访问adb forward tcp:8080 tcp:80 adb forward tcp:2345 tcp:2345第一条把设备的 80 端口映射到本机 8080浏览器打开http://127.0.0.1:8080就能看到设备上的服务页面第二条是 gdb 调试常用把设备的 gdbserver 端口透传到本地。这比改防火墙配路由快得多而且走 adb 通道不受局域网环境影响。6.2 把常用验证动作固化成一个脚本每次调试都敲一遍重复命令容易漏尤其是换设备、换环境后。我习惯把第 4 章的最小验证序列固化成脚本放在包外面而不是包内避免污染原始文件#!/bin/bash adb kill-server adb start-server adb wait-for-device adb shell echo adb link ok adb shell getprop ro.product.modelwait-for-device会阻塞直到设备出现适合放进自动化脚本里避免顺序问题。这样每次拿到新的 ARM 设备解包、校验、跑脚本几分钟内就能确认调试通道可用。这套 tgz 用久了你会发现真正的价值不在那个 adb 可执行文件本身而在它让你在设备启动早期就有了一个可控的调试入口。我现在拿到任意的分发包第一件事永远是tar -tzf看清单、file看架构、readelf -d看依赖这个习惯救过我至少三次也让我在新板卡面前少出了很多洋相。希望帮到你。本文还有配套的精品资源点击获取