本文记录使用命令 OHOS_ARCH=aarch64 OHOS_ABI=arm64-v8a sh ./create-hnp.sh 构建 c-ares 1.34.5 的完整过程,包括环境、构建链路、关键日志、常见问题与解决方案、产物验证与重建方法,便于复现与运维。
📖 c-ares 简介
c-ares(C Asynchronous DNS Requests)是一个 C 语言实现的异步 DNS 解析库。它提供了高性能、非阻塞的 DNS 查询功能,被广泛用于需要异步 DNS 解析的应用程序中,如 curl、Node.js 等。
🎯 c-ares 的作用与重要性
c-ares 是异步 DNS 解析的核心库,提供了:
- 异步 DNS 解析:非阻塞的 DNS 查询,不会阻塞应用程序主线程
- 高性能:高效的 DNS 查询实现,支持并发查询
- 跨平台:支持多种操作系统和平台
- 轻量级:库体积小,依赖少,易于集成
- 网络应用支持:为 curl、Node.js 等网络工具提供 DNS 解析支持
- DNS 协议支持:支持 A、AAAA、MX、TXT、SRV 等多种 DNS 记录类型
🔧 c-ares 核心特性
1. 异步 DNS 解析
- 非阻塞查询:DNS 查询不会阻塞应用程序
- 事件驱动:基于事件循环的异步处理
- 并发支持:支持多个并发 DNS 查询
- 回调机制:查询完成后通过回调函数通知
2. DNS 协议支持
- 标准记录类型:A、AAAA、MX、TXT、SRV、NS、CNAME、PTR 等
- DNS over HTTPS (DoH):支持 DNS over HTTPS 查询
- DNS over TLS (DoT):支持 DNS over TLS 查询
- EDNS0 支持:支持扩展 DNS 功能
3. 命令行工具
- adig:异步 DNS 查询工具,类似 dig
- ahost:异步主机名解析工具
- 批量查询:支持批量查询多个域名
- 多种输出格式:支持多种输出格式
4. 编程接口
- C API:提供完整的 C 语言接口
- 简单易用:简洁的 API 设计
- 线程安全:线程安全的实现
- 错误处理:完善的错误码和错误处理
5. 应用场景
- Web 客户端:curl、wget 等工具的 DNS 解析
- 网络应用:需要异步 DNS 解析的网络应用
- 服务器应用:需要高性能 DNS 解析的服务器应用
- 移动应用:移动设备上的 DNS 解析
🚀 构建入口与环境
- 📝 执行命令:
OHOS_ARCH=aarch64 OHOS_ABI=arm64-v8a sh ./create-hnp.sh - 🔧 入口脚本:
create-hnp.sh- 检查必需的环境变量
OHOS_ARCH和OHOS_ABI - 导出
LC_CTYPE、TOOL_HOME、OHOS_SDK_HOME - 执行
make -C build-hnp
- 检查必需的环境变量
- 📦 顶层构建:
build-hnp/MakefilePKGS变量定义需要构建的包列表(包含c-ares)- 通过
check-pkgs机制自动检测PKGS变化并触发重新构建 - 自动合并
external-hnp目录下的外部 HNP 包 base.hnp依赖所有包的.stamp和外部 HNP 包- 总目标
all: copy,打包base.hnp并拷贝到entry/hnp/$(OHOS_ABI)
⚙️ c-ares 包的构建配置
- 📁 包目录:
build-hnp/c-ares/Makefile- 继承通用规则:
include ../utils/Makefrag - 源地址:
https://github.com/c-ares/c-ares/releases/download/v1.34.5/c-ares-1.34.5.tar.gz - 版本:
1.34.5
- 继承通用规则:
- ⚙️ CMake 配置参数:
-DCMAKE_INSTALL_PREFIX=$(PREFIX)- 安装前缀(/data/app/base.org/base_1.0)-DCMAKE_SYSTEM_NAME=Linux- 目标系统-DCMAKE_SYSTEM_PROCESSOR=$(OHOS_ARCH)- 目标架构-DCARES_STATIC=OFF- 禁用静态库-DCARES_SHARED=ON- 构建共享库-DCMAKE_BUILD_TYPE=RelWithDebInfo- 构建类型
- 🔨 构建流程:
- 下载源码包(从 GitHub releases)
- 解包并进入
temp/c-ares-1.34.5目录 - 创建
build目录并运行cmake配置 - 使用
make -j $(nproc)并行编译 - 使用
make install安装 - 执行 ELF strip 减小体积
- 复制到
../sysroot
- 🔧 通用工具链与路径:
build-hnp/utils/MakefragCC/CXX/LD/AR/RANLIB/...均指向 OHOS SDK 的 LLVM 工具链- CMake 通过
CMAKE_C_COMPILER等变量指定交叉编译器
- 📦 依赖关系:
- 前置依赖:无(c-ares 是独立的 DNS 解析库)
- 后续依赖:
curl的异步 DNS 支持通常依赖c-ares - 建议构建顺序:
c-ares → curl(如果 curl 需要异步 DNS 支持)
📋 关键日志与过程节点
- 📥 下载与解包:
- 从 GitHub releases 下载
c-ares-1.34.5.tar.gz - 完成解包并进入
temp/c-ares-1.34.5目录 - 创建
build目录用于 CMake 构建
- 从 GitHub releases 下载
- ⚙️ CMake 配置阶段:
- 运行
cmake -DCMAKE_INSTALL_PREFIX=... -DCARES_STATIC=OFF -DCARES_SHARED=ON ... - 检测 Clang 编译器与一系列编译警告标志
- 部分
HAVE__W*标志测试失败属正常(CMake 会仅启用可用标志) CMAKE_SYSTEM_NAME=Linux与三元组工具链生效- ABI 与编译特性探测通过
- 配置成功,启用共享库构建
- 生成
Makefile和构建配置
- 运行
- 🔨 编译与安装:
- 使用
make -j $(nproc)并行编译 - 成功编译生成
libcares.so.2.19.4、adig和ahost命令行工具 - 使用
make install安装到临时前缀 - 执行
llvm-strip剥离共享库和二进制符号 - 复制到
../sysroot
- 使用
- 📦 打包:
- 完成
base.hnp重打包,拷贝产物到entry/hnp/arm64-v8a/ - c-ares 库和工具已成功打包到
base.hnp中
- 完成
✅ 产物验证
📦 检查打包文件
ls build-hnp/base.hnp # 应存在
ls entry/hnp/arm64-v8a/*.hnp # 应包含 base.hnp 与 base-public.hnp
🔍 检查二进制和库文件
# 检查 adig 和 ahost 命令行工具
ls -lh build-hnp/sysroot/bin/adig build-hnp/sysroot/bin/ahost
file build-hnp/sysroot/bin/adig build-hnp/sysroot/bin/ahost
# 检查库文件
ls -lh build-hnp/sysroot/lib/libcares.so*
file build-hnp/sysroot/lib/libcares.so.2.19.4
# 检查头文件
ls -lh build-hnp/sysroot/include/ares.h build-hnp/sysroot/include/ares_dns.h
# 检查 pkg-config 文件
ls -lh build-hnp/sysroot/lib/pkgconfig/libcares.pc
cat build-hnp/sysroot/lib/pkgconfig/libcares.pc
✅ 构建验证结果:
- ✅ c-ares 命令行工具已成功安装:
adig(31K) - 异步 DNS 查询工具ahost(11K) - 异步主机名解析工具
- ✅ c-ares 库已安装:
libcares.so.2.19.4(195K) - 主库文件libcares.so.2- 版本符号链接libcares.so- 通用符号链接
- ✅ 文件类型:ELF 64-bit LSB pie executable/shared object, ARM aarch64
- ✅ 动态链接:interpreter
/lib/ld-musl-aarch64.so.1 - ✅ 已剥离符号:
stripped - ✅ 头文件已安装:
ares.h(47K)、ares_dns.h(5.7K) - ✅ pkg-config 文件已安装:
libcares.pc(704 bytes) - ✅ 手册页面已安装:
share/man/man1/adig.1、share/man/man1/ahost.1、share/man/man3/ares_*.3 - ✅ 已打包到
base.hnp中
💻 终端中执行的示例命令
🌐 adig 基本使用

1. DNS 查询(A 记录)
# 查询 A 记录(IPv4 地址)
adig www.example.com
# 查询指定 DNS 服务器
adig @8.8.8.8 www.example.com
# 查询并显示详细信息
adig -x www.example.com
# 查询多个域名
adig www.example.com www.google.com
2. AAAA 记录查询(IPv6)
# 查询 AAAA 记录(IPv6 地址)
adig -t AAAA www.example.com
# 查询 IPv6 地址并显示详细信息
adig -t AAAA -x www.example.com
# 查询多个域名的 IPv6 地址
adig -t AAAA www.example.com www.google.com
3. MX 记录查询
# 查询 MX 记录(邮件交换记录)
adig -t MX example.com
# 查询 MX 记录并显示详细信息
adig -t MX -v example.com
# 查询多个域名的 MX 记录
adig -t MX example.com google.com
4. TXT 记录查询
# 查询 TXT 记录
adig -t TXT example.com
# 查询 TXT 记录并显示详细信息
adig -t TXT -v example.com
# 查询 SPF 记录
adig -t TXT example.com | grep spf
# 查询 DKIM 记录
adig -t TXT default._domainkey.example.com
5. SRV 记录查询
# 查询 SRV 记录
adig -t SRV _http._tcp.example.com
# 查询 SRV 记录并显示详细信息
adig -t SRV -v _http._tcp.example.com
# 查询 SIP SRV 记录
adig -t SRV _sip._tcp.example.com
# 查询 XMPP SRV 记录
adig -t SRV _xmpp-client._tcp.example.com
🌐 adig 高级用法
6. NS 和 CNAME 记录查询
# 查询 NS 记录(名称服务器)
adig -t NS example.com
# 查询 CNAME 记录(别名)
adig -t CNAME www.example.com
# 查询 PTR 记录(反向 DNS)
adig -t PTR 8.8.8.8.in-addr.arpa
# 查询 SOA 记录(起始授权机构)
adig -t SOA example.com
7. 批量查询
# 从文件读取域名并查询
echo -e "www.example.com\nwww.google.com" | xargs -n1 adig
# 批量查询 A 记录
cat domains.txt | while read domain; do
echo "=== $domain ==="
adig "$domain"
done
# 批量查询多个记录类型
for type in A AAAA MX TXT; do
echo "=== $type ==="
adig -t "$type" example.com
done
# 批量查询并保存结果
adig www.example.com > dns_result.txt
8. 输出格式控制
# 简洁输出(只显示 IP 地址)
adig -s www.example.com
# 详细输出
adig -v www.example.com
# 显示查询时间
adig -t www.example.com
# 显示统计信息
adig -S www.example.com
# 显示版本信息
adig -V
# 显示帮助信息
adig -h
9. DNS 服务器选择
# 使用 Google DNS
adig @8.8.8.8 www.example.com
# 使用 Cloudflare DNS
adig @1.1.1.1 www.example.com
# 使用本地 DNS 服务器
adig @127.0.0.1 www.example.com
# 使用 IPv6 DNS 服务器
adig @2001:4860:4860::8888 www.example.com
🌐 ahost 基本使用

10. 主机名解析
# 解析主机名(A 记录)
ahost www.example.com
# 解析主机名(AAAA 记录)
ahost -t AAAA www.example.com
# 解析多个主机名
ahost www.example.com www.google.com
# 解析并显示详细信息
ahost -v www.example.com
11. 反向 DNS 查询
# 反向 DNS 查询(PTR 记录)
ahost -r 8.8.8.8
# 反向 DNS 查询 IPv6 地址
ahost -r 2001:4860:4860::8888
# 反向 DNS 查询并显示详细信息
ahost -r -v 8.8.8.8
12. 实际应用示例
# 检查域名解析是否正常
adig www.example.com && echo "DNS resolution OK" || echo "DNS resolution failed"
# 查询域名的所有记录类型
for type in A AAAA MX TXT NS SOA; do
echo "=== $type ==="
adig -t "$type" example.com
done
# 比较不同 DNS 服务器的响应
for dns in 8.8.8.8 1.1.1.1 208.67.222.222; do
echo "=== DNS: $dns ==="
adig @"$dns" www.example.com
done
# 查询邮件服务器配置
echo "=== MX Records ==="
adig -t MX example.com
echo "=== SPF Record ==="
adig -t TXT example.com | grep spf
# 检查 DNS 解析延迟
time adig www.example.com
# 批量检查域名解析
cat domains.txt | while read domain; do
if adig "$domain" > /dev/null 2>&1; then
echo "$domain: OK"
else
echo "$domain: FAILED"
fi
done
# 查询 DNS 记录并保存到文件
adig -t A www.example.com > a_record.txt
adig -t AAAA www.example.com > aaaa_record.txt
adig -t MX example.com > mx_record.txt
# 监控 DNS 解析变化
while true; do
echo "$(date): $(adig www.example.com)"
sleep 60
done
# 查询并格式化输出
adig www.example.com | awk '{print "IP:", $1}'
# 检查 DNS 服务器响应时间
for dns in 8.8.8.8 1.1.1.1; do
echo -n "DNS $dns: "
time adig @"$dns" www.example.com > /dev/null 2>&1
done
🔧 编程接口示例(C 语言)
13. 基本 DNS 查询示例
// 示例:使用 c-ares 库进行 DNS 查询
#include <ares.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
void callback(void *arg, int status, int timeouts, struct hostent *host) {
if (status == ARES_SUCCESS) {
printf("Hostname resolved successfully\n");
struct in_addr **addr_list = (struct in_addr **)host->h_addr_list;
for (int i = 0; addr_list[i] != NULL; i++) {
printf("IP: %s\n", inet_ntoa(*addr_list[i]));
}
} else {
printf("DNS query failed: %s\n", ares_strerror(status));
}
}
int main() {
ares_channel channel;
int status;
// 初始化 ares 库
status = ares_library_init(ARES_LIB_INIT_ALL);
if (status != ARES_SUCCESS) {
fprintf(stderr, "ares_library_init failed: %s\n", ares_strerror(status));
return 1;
}
// 创建 ares 通道
status = ares_init(&channel);
if (status != ARES_SUCCESS) {
fprintf(stderr, "ares_init failed: %s\n", ares_strerror(status));
ares_library_cleanup();
return 1;
}
// 执行 DNS 查询
ares_gethostbyname(channel, "www.example.com", AF_INET, callback, NULL);
// 处理事件循环
for (;;) {
fd_set read_fds, write_fds;
FD_ZERO(&read_fds);
FD_ZERO(&write_fds);
int nfds = ares_fds(channel, &read_fds, &write_fds);
if (nfds == 0) {
break;
}
struct timeval tv;
tv.tv_sec = 1;
tv.tv_usec = 0;
select(nfds, &read_fds, &write_fds, NULL, &tv);
ares_process(channel, &read_fds, &write_fds);
}
// 清理资源
ares_destroy(channel);
ares_library_cleanup();
return 0;
}
// 编译命令
// gcc -o test_ares test_ares.c -lcares
14. 使用 pkg-config
# 使用 pkg-config 获取编译和链接标志
pkg-config --cflags libcares
pkg-config --libs libcares
# 编译示例
gcc -o program program.c $(pkg-config --cflags --libs libcares)
# 检查库版本
pkg-config --modversion libcares
# 检查库是否存在
pkg-config --exists libcares && echo "Library found"
# 显示库信息
pkg-config --print-provides libcares
🧪 功能验证脚本
#!/bin/bash
# c-ares 工具验证脚本
CARES_BIN="build-hnp/sysroot/bin"
CARES_LIB="build-hnp/sysroot/lib"
CARES_INCLUDE="build-hnp/sysroot/include"
echo "=== c-ares 工具验证 ==="
# 检查 adig 二进制
if [ -f "$CARES_BIN/adig" ]; then
echo "✓ adig: 存在"
file "$CARES_BIN/adig"
echo "文件大小: $(ls -lh "$CARES_BIN/adig" | awk '{print $5}')"
echo "架构信息: $(file "$CARES_BIN/adig" | grep -o "ARM aarch64")"
else
echo "✗ adig: 缺失"
fi
# 检查 ahost 二进制
if [ -f "$CARES_BIN/ahost" ]; then
echo "✓ ahost: 存在"
file "$CARES_BIN/ahost"
echo "文件大小: $(ls -lh "$CARES_BIN/ahost" | awk '{print $5}')"
echo "架构信息: $(file "$CARES_BIN/ahost" | grep -o "ARM aarch64")"
else
echo "✗ ahost: 缺失"
fi
# 检查库文件
echo ""
echo "=== 库文件验证 ==="
if [ -f "$CARES_LIB/libcares.so.2.19.4" ]; then
echo "✓ libcares.so.2.19.4: 存在"
file "$CARES_LIB/libcares.so.2.19.4"
echo "文件大小: $(ls -lh "$CARES_LIB/libcares.so.2.19.4" | awk '{print $5}')"
else
echo "✗ libcares.so.2.19.4: 缺失"
fi
# 检查符号链接
for link in libcares.so libcares.so.2; do
if [ -L "$CARES_LIB/$link" ]; then
echo "✓ $link: 符号链接 -> $(readlink "$CARES_LIB/$link")"
else
echo "✗ $link: 缺失"
fi
done
# 检查头文件
echo ""
echo "=== 头文件验证 ==="
for header in ares.h ares_dns.h ares_version.h; do
if [ -f "$CARES_INCLUDE/$header" ]; then
echo "✓ $header: 存在"
echo " 文件大小: $(ls -lh "$CARES_INCLUDE/$header" | awk '{print $5}')"
else
echo "✗ $header: 缺失"
fi
done
# 检查 pkg-config 文件
echo ""
echo "=== pkg-config 验证 ==="
if [ -f "build-hnp/sysroot/lib/pkgconfig/libcares.pc" ]; then
echo "✓ libcares.pc: 存在"
cat build-hnp/sysroot/lib/pkgconfig/libcares.pc
else
echo "✗ libcares.pc: 缺失"
fi
# 测试基本功能(在目标设备上)
echo ""
echo "=== 功能测试(需要在目标设备上运行)==="
echo "测试 adig DNS 查询:"
echo " $CARES_BIN/adig www.example.com"
echo ""
echo "测试 ahost 主机名解析:"
echo " $CARES_BIN/ahost www.example.com"
echo ""
echo "测试 AAAA 记录查询:"
echo " $CARES_BIN/adig -t AAAA www.example.com"
echo ""
echo "测试 MX 记录查询:"
echo " $CARES_BIN/adig -t MX example.com"
🐛 常见问题与处理
❌ 问题 1:CMake 警告标志失败
- 🔍 症状:配置阶段部分
HAVE__W*标志测试失败 - 🔎 原因:部分编译警告标志在目标平台上不可用
- ✅ 解决方法:
- 这是正常现象,CMake 会仅启用可用标志
- 不影响构建结果,可以忽略这些警告
- 位置:CMake 配置阶段
❌ 问题 2:GitHub 下载失败
- 🔍 症状:无法从 GitHub releases 下载源码包
- 🔎 原因:网络问题或 GitHub 访问受限
- ✅ 解决方法:
- 手动下载源码包放置到
build-hnp/c-ares/download/c-ares-1.34.5.tar.gz - 使用代理或镜像站点下载
- 位置:
build-hnp/c-ares/Makefile:3
- 手动下载源码包放置到
❌ 问题 3:curl 异步 DNS 支持缺失
- 🔍 症状:curl 构建时未启用 c-ares 支持
- 🔎 原因:
c-ares未在 curl 之前构建,或 curl 配置时未检测到 - ✅ 解决方法:
- 确保
c-ares在curl之前构建 - 检查 curl 配置是否启用了 c-ares 支持
- 位置:构建顺序和 curl 配置
- 确保
❌ 问题 4:静态/共享库选择
- 🔍 症状:需要静态库但构建时使用了
-DCARES_STATIC=OFF - 🔎 原因:当前配置禁用了静态库以减小体积
- ✅ 解决方法:
- 如果需要静态库,修改
CMAKE_ARGS为-DCARES_STATIC=ON -DCARES_SHARED=OFF - 或同时启用:
-DCARES_STATIC=ON -DCARES_SHARED=ON - 位置:
build-hnp/c-ares/Makefile:6
- 如果需要静态库,修改
❌ 问题 5:链接失败
- 🔍 症状:链接错误,无法找到
libcares符号 - 🔎 原因:链接时未指定库或库路径不正确
- ✅ 解决方法:
- 确保链接时添加
-lcares - 使用
pkg-config --libs libcares获取正确的链接标志 - 检查
LD_LIBRARY_PATH是否包含库路径 - 位置:编译和链接阶段
- 确保链接时添加
❌ 问题 6:头文件未找到
- 🔍 症状:编译错误
ares.h: No such file or directory - 🔎 原因:头文件路径未正确设置
- ✅ 解决方法:
- 确保编译时添加
-I$(prefix)/include - 使用
pkg-config --cflags libcares获取正确的编译标志 - 检查头文件是否已安装到
sysroot/include - 位置:编译阶段
- 确保编译时添加
❌ 问题 7:架构不支持
- 🔍 症状:
Unsupported OHOS_ARCH= - 🔎 原因:传入的架构不在支持列表中
- ✅ 解决方法:
- 确保传入支持架构(
aarch64或x86_64) - 位置:
build-hnp/Makefile:1-49
- 确保传入支持架构(
🔄 重建与扩展
-
🔧 重建单包:
make -C build-hnp rebuild-c-ares # 触发子包重新编译并刷新 .stamp -
🧹 清理:
make -C build-hnp clean # 清理 sysroot、所有 .stamp 和 PKGS_MARKER -
📦 扩展:c-ares 是异步 DNS 解析的基础库,被 curl 等网络工具依赖
-
🔄 自动重建机制:
- 修改
PKGS后,check-pkgs会自动检测变化并触发重新构建 - 新增外部 HNP 包到
external-hnp目录后,会自动合并到base.hnp
- 修改
💡 实践建议
- 🔧 构建配置:使用共享库构建以减少体积,适合大多数场景
- 🚀 依赖管理:c-ares 是独立的 DNS 解析库,无需前置依赖
- 📦 网络应用:c-ares 为 curl 等网络工具提供异步 DNS 支持
- 🔗 编程接口:使用
pkg-config获取正确的编译和链接标志 - 🌐 DNS 查询:使用
adig和ahost命令行工具进行 DNS 查询和主机名解析
📝 结论与建议
- ✅ 本次已在 aarch64 环境下完成 c-ares 1.34.5 的交叉编译与打包,adig、ahost 工具和库已安装到
sysroot并纳入 HNP 包。 - 💡 为保证构建稳定:
- c-ares 是独立的 DNS 解析库,无需前置依赖
- 使用共享库构建以减少体积
- 确保通过
create-hnp.sh触发构建以获得完整环境变量 - 利用
check-pkgs机制自动检测包列表变化,无需手动清理 - c-ares 为异步 DNS 解析提供了强大的工具和库支持
- 常见陷阱包括 CMake 警告标志失败、静态/共享库选择、链接失败;当前已通过构建配置和参数处理
- 建议在
curl构建阶段启用 c-ares 支持,并结合libidn2/libunistring/openssl/zlib完成网络栈的功能验证与性能对比 - 已完成 aarch64 目标下 c-ares 的交叉编译与打包,库与头文件进入
sysroot并纳入 HNP 包
📚 以上为 c-ares 构建的深度解读与实践记录。
1万+

被折叠的 条评论
为什么被折叠?



