LLVM调试记录 - 20180409

本文记录了在使用LLVM编译器时遇到的printk函数异常问题,详细描述了问题现象及调试过程,最终通过调整va_start实现解决了问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题现象

调试sp02

LLVM编译的可执行程序在Linux模拟器上运行,无法执行结束。在Windows模拟器可以正常执行(有乱码)。

Linux模拟器运行结果

*** TEST 2 ***
INIT - xxos_task_wake_after - yielding processor
PREEMPT - xxos_task_delete - deleting selfINIT - suspending TA2 while middle task on a ready chainTA1 - xxos_task_wake_after - sleep 1 secondTA2 - xxos_task_wake_after - sleep 1 minuteTA3 - xxos_task_wake_after - sleep 5 secondsTA1 - xxos_task_ident - tid of TA2 (0x.8x)
TA1 - xxos_get_classic_name - id -> name of TA2   OK
TA1 - xxos_task_ident - tid of TA3 (0x.8x)

Windows模拟器运行结果

*** TEST 2 ***
INIT - xxos_task_wake_after - yielding processor
PREEMPT - xxos_task_delete - deleting selfINIT - suspending TA2 while middle task on a ready chainTA1 - xxos_task_wake_after - sleep 1 secondTA2 - xxos_task_wake_after - sleep 1 minuteTA3 - xxos_task_wake_after - sleep 5 secondsTA1 - xxos_task_ident - tid of TA2 (0x.8x)
TA1 - xxos_get_classic_name - id -> name of TA2  籓K
TA1 - xxos_task_ident - tid of TA3 (0x.8x)
TA1 - xxos_task_set_priority - set TA3's priority to 2TA1 - xxos_task_suspend - suspend TA2TA1 - xxos_task_delete - delete TA2TA1 - xxos_task_wake_after - sleep for 5 secondsTA3 - xxos_task_delete - delete self*** END OF TEST 2 ***   
调试sp03
调试前

Linux模拟器

*** TEST 3 ***

Windows模拟器

*** TEST 3 ***

狻籃0

程序卡在vprintk()中, sp04也是

尝试解决
  • printk打印加上换行
  • 修改print_time()put_name()函数:分别在他们调用的printk打印结尾加上换行。

修改之后Linux模拟器多打印了几行乱码。看来也不是因为缓冲没及时flush的原因。

问题锁定:printk加参数就崩

既然知道是printk()vprintk()的问题(程序卡死在vprintk()中),那么就把问题出现的条件简单化,在程序中以从简单到复杂的形式来调用printk(),以逐步排查问题。首先尝试直接打印一个常量字符串,没问题。接下来尝试打印一个局部整形变量,出问题了。多试几次之后发现,只要printk()以加参数打印的形式就会崩。

int a = 10;
printk("a=%d\n", a);

打印结果竟然是

a=8411292

打印的看起来是一个地址。改vprintk还是编译器?????

问题剖析

看vprintk汇编代码

  1. U0是第一个参数format字符串的地址,因为是字符串常量,该地址在数据段。U1存的是第二个参数va_list的地址,在栈中

printk的汇编有问题

_printk:
0b000480:   XR39=SER || YR39=U9
0b000482:   [U8+=-2,-1]=XYR39 
0b000484:   U9=U8 
0b000485:   U8=U8+-2 
0b000486:   XR0=[U9+3] 
0b000487:   [U9+0]=XR0 
0b000488:   U0=U9+0 
0b000489:   XR1=U0 
0b00048a:   XR1=R1+1 //这里把第二个参数的位置指错了
0b00048b:   U0=XR0 
0b00048c:   U1=XR1 
0b00048d:   CALL 0x0b001400
0b00048f:   U8=U9 
0b000490:   XYR39=[U8+1,1] || U8=U8+2
0b000493:   U9=XR39 || YSER=R39
0b000495:   RET 

printk源码:

/*
 * printk
 *
 * Kernel printf function requiring minimal infrastrure.
 */
void printk(const char *fmt, ...)
{
  va_list  ap;       /* points to each unnamed argument in turn */

  va_start(ap, fmt); /* make ap point to 1st unnamed arg */
  vprintk(fmt, ap);
  va_end(ap);        /* clean up when done */
}

printk()va_start(ap, fmt);会把ap指向fmt的下一个地址,以前的编译器编译的结果不复制参数,所以没问题。这个编译器会复制参数,但是对于这种可变参数的,又只复制了第一个参数也就是fmt,之后把ap指向复制后的fmt的下一个地址,就错了。如图1所示。

图 1 vprintk()函数调用栈

图1 printk()函数调用栈

问题实质

因此错误就是printk()处理参数错误,对可变参数列表没有进行复制,而在使用时又是按照已复制的形式来使用,因此使用了错误的参数,打印的结果自然也是错的,之前打印的是SER寄存器入栈之前的值。再深一层的原因就需要分析编译器的代码了,我的猜想可能是这个编译器处理可变参数列表的情况就会出错,当然只是瞎猜,有机会可以测试一下。

解决问题

为了解决这个问题,这里我们调整va_start(ap, fmt)的实现。使得ap的位置指向复制后参数的往后 4 个地址,也即参数被复制之前的地址。
目前这种方法看起来是可行的,但是绝对不符合逻辑,编译器对我们来说不透明了,我们不应该知道编译器是怎么传参的。

<think>好的,用户之前遇到了编译错误,是因为在非ARM架构下编译了AArch64的代码。现在用户说他们已经指定了交叉编译的参数,比如--target-os=android,--arch=aarch64,启用交叉编译,并设置了交叉编译工具链的前缀、cc和cxx。但问题依旧存在,所以需要深入分析可能的原因。 首先,用户可能正确设置了这些参数,但实际编译过程中,某些配置或环境变量可能覆盖了这些设置,导致仍然在错误的环境下编译。比如,检查是否在正确的目录下执行configure,或者是否有残留的缓存文件影响配置。另外,工具链的路径是否正确,$TOOLCHAIN变量是否被正确设置,clang是否为Android NDK的版本,这些都是可能的问题点。 接下来,需要确认NDK版本是否兼容。旧版本的NDK可能不支持某些架构或参数,导致配置失败。用户应该检查NDK的版本,并确保其支持目标架构和API级别。同时,API级别的设置是否正确,是否通过--extra-cflags和--extra-ldflags指定了正确的sysroot路径。 然后,检查FFmpeg的配置日志config.log,寻找是否有错误信息,特别是在检测架构或工具链时的错误。用户可能需要在配置命令中加入--logfile=config.log来生成日志文件,然后查看是否有关于工具链无法识别或参数无效的记录。 另外,用户可能需要显式禁用本地汇编优化,比如使用--disable-asm,以避免在交叉编译时使用主机架构的汇编指令。此外,检查是否启用了不必要的组件或模块,这些可能依赖主机架构的特定功能,导致编译失败。 还有,用户的环境变量可能干扰了交叉编译,比如CFLAGS或LDFLAGS可能包含不适合目标架构的参数。建议在配置时清除这些环境变量,或在配置命令中覆盖它们。 最后,如果所有配置都正确,问题可能出在FFmpeg本身的配置脚本对Android交叉编译的支持上。某些旧版本可能存在bug,需要升级到最新版本或应用补丁。用户也可以尝试寻找针对Android交叉编译的特定指南或社区解决方案,确保所有必要的参数和补丁都已应用。</think>--- ### **问题核心分析** 即使你指定了交叉编译参数,编译仍失败,通常由以下原因导致: 1. **工具链路径或权限问题**:`$TOOLCHAIN` 变量未正确定义,或 `clang` 工具链未针对 Android 配置 2. **NDK 版本兼容性**:NDK 版本与 FFmpeg 存在兼容性问题 3. **API 级别未指定**:Android 目标 API 级别未通过 `--sysroot` 显式传递 4. **FFmpeg 配置残留**:旧编译缓存未清理干净 --- ### **分步解决方案** #### **第一步:验证工具链完整性** 确保 `$TOOLCHAIN` 指向正确的 NDK 路径: ```bash # 示例:NDK 路径应为类似结构 export TOOLCHAIN=/path/to/android-ndk-r25b/toolchains/llvm/prebuilt/linux-x86_64 ``` 检查关键工具是否存在: ```bash ls $TOOLCHAIN/bin/aarch64-linux-android-*clang* # 应看到 aarch64-linux-android21-clang 等文件 ``` #### **第二步:强制指定 API 级别和 sysroot** 在 `configure` 命令中显式添加 API 级别(例如 21): ```bash ./configure \ --target-os=android \ --arch=aarch64 \ --enable-cross-compile \ --cross-prefix=$TOOLCHAIN/bin/llvm- \ --cc=$TOOLCHAIN/bin/aarch64-linux-android21-clang \ --cxx=$TOOLCHAIN/bin/aarch64-linux-android21-clang++ \ --sysroot=$TOOLCHAIN/sysroot \ --extra-cflags="-D__ANDROID_API__=21" \ --extra-ldflags="-pie" ``` #### **第三步:清理编译环境** 彻底删除旧配置和编译产物: ```bash make distclean # 手动删除 config.mak 和 ffbuild 目录 rm -f config.mak ffbuild/config.log ``` #### **第四步:禁用冲突优化** 强制禁用与架构冲突的优化模块: ```bash # 添加以下参数到 configure --disable-armv5te \ --disable-armv6 \ --disable-armv6t2 \ --disable-neon \ --disable-asm ``` #### **第五步:检查 NDK 版本兼容性** - **NDK r21+** 推荐使用 LLVM 工具链 - 若使用旧版 NDK(如 r15c),需替换 `clang` 为 `gcc`: ```bash --cc=$TOOLCHAIN/bin/aarch64-linux-android-gcc \ --cxx=$TOOLCHAIN/bin/aarch64-linux-android-g++ ``` --- ### **关键配置验证** #### **1. 检查 `config.mak`** 编译前生成的 `config.mak` 应包含: ```make # 确认关键变量 ARCH=aarch64 TARGET_OS=android CC=aarch64-linux-android21-clang CROSS_PREFIX=llvm- ``` #### **2. 查看预处理宏** ```bash # 临时编译测试 echo '#include <stdio.h>' | $CC -E -dM - 2>&1 | grep -i 'aarch64\|android' # 应输出 __aarch64__ 和 __ANDROID__ 相关宏 ``` --- ### **典型错误场景** #### **场景 1:NDK 工具链路径错误** ```log clang: error: no such file or directory: 'aarch64-linux-android21-clang' ``` **修复**:检查 `$TOOLCHAIN/bin` 下文件名是否包含 API 级别后缀(如 `21`) #### **场景 2:未定义 `__ANDROID_API__`** ```log error: use of undeclared identifier 'MBEDTLS_SSL_MAX_FRAGMENT_LENGTH' ``` **修复**:添加 `--extra-cflags="-D__ANDROID_API__=21"` --- ### **完整配置示例** ```bash export NDK=/path/to/android-ndk-r25b export TOOLCHAIN=$NDK/toolchains/llvm/prebuilt/linux-x86_64 export API=21 ./configure \ --prefix=./android-aarch64 \ --target-os=android \ --arch=aarch64 \ --enable-cross-compile \ --cross-prefix=$TOOLCHAIN/bin/llvm- \ --cc=$TOOLCHAIN/bin/aarch64-linux-android$API-clang \ --cxx=$TOOLCHAIN/bin/aarch64-linux-android$API-clang++ \ --sysroot=$TOOLCHAIN/sysroot \ --extra-cflags="-D__ANDROID_API__=$API -fPIC" \ --extra-ldflags="-pie -llog" \ --disable-static \ --enable-shared \ --disable-doc \ --disable-asm \ --disable-x86asm \ --disable-neon ``` --- ### **调试建议** 1. **查看 `config.log`**:搜索 `error` 或 `warning`,定位具体失败点 2. **手动执行预编译步骤**: ```bash # 测试编译器能否生成有效目标文件 $TOOLCHAIN/bin/aarch64-linux-android21-clang -c empty.c -o empty.o file empty.o # 应显示 ELF 64-bit LSB relocatable, ARM aarch64 ``` 3. **升级 FFmpeg**:使用最新 git 仓库代码,修复历史兼容性问题 通过上述步骤可系统性排除交叉编译配置错误。若仍有问题,请提供 `config.log` 关键段落。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值