|
| 1 | +# 引言 |
| 2 | + |
| 3 | +在 LoongArch64 系统上运行 GDB 测试套件时,我在执行 gdb.threads/step-over-thread-exit-while-stop-all-threads.exp 等测试用例时遇到了编译错误。错误源于 my-syscalls.S 汇编文件,最初报错为“未支持架构”(Unsupported architecture),在添加 LoongArch 支持后,又出现了无法识别 syscall 指令的错误。本文详细记录了问题的原因、解决步骤,以及对 LoongArch 指令集和系统调用处理的深入分析。 |
| 4 | + |
| 5 | +# 问题描述 |
| 6 | + |
| 7 | +在运行 GDB 测试套件时,出现了以下错误: |
| 8 | +``` |
| 9 | +Running /home/yzw/binutils-gdb/build/gdb/testsuite/../../../gdb/testsuite/gdb.threads/step-over-thread-exit-while-stop-all-threads.exp ... |
| 10 | +gdb compile failed, /home/yzw/binutils-gdb/build/gdb/testsuite/../../../gdb/testsuite/lib/my-syscalls.S:67:3: error: #error "Unsupported architecture" |
| 11 | + 67 | # error "Unsupported architecture" |
| 12 | + | ^~~~~ |
| 13 | +``` |
| 14 | +在修改 my-syscalls.S 添加 LoongArch 支持后,出现了新的错误: |
| 15 | +``` |
| 16 | +gdb compile failed, /home/yzw/binutils-gdb/build/gdb/testsuite/../../../gdb/testsuite/lib/my-syscalls.S:81: Error: no match insn: syscall |
| 17 | +/home/yzw/binutils-gdb/build/gdb/testsuite/../../../gdb/testsuite/lib/my-syscalls.S:85: Error: no match insn: syscall |
| 18 | +``` |
| 19 | +这些错误表明存在两个问题: |
| 20 | + |
| 21 | + |
| 22 | +汇编器(as)最初无法识别 LoongArch 架构,触发了 #error "Unsupported architecture" 指令。 |
| 23 | + |
| 24 | +添加 LoongArch 支持后,汇编器无法识别 syscall 指令,表明工具链支持不完整。 |
| 25 | + |
| 26 | +# 背景:my-syscalls.S 文件 |
| 27 | + |
| 28 | +GDB 测试套件中的 my-syscalls.S 文件为需要精确控制系统调用指令的测试提供包装函数。它定义了一个 SYSCALL 宏,用于为不同架构生成类似 my_execve(系统调用号 221)和 my_exit(系统调用号 93)的函数,支持 x86_64、i386、aarch64 等架构。在我的案例中,需要为 LoongArch64 添加支持。 |
| 29 | + |
| 30 | +原始文件中缺少 LoongArch 分支,导致“未支持架构”错误。在添加 LoongArch 支持后,syscall 指令问题暴露了工具链的局限性。 |
| 31 | + |
| 32 | +## 问题原因分析 |
| 33 | + |
| 34 | +1. “未支持架构”错误 |
| 35 | + |
| 36 | +初始错误是因为 my-syscalls.S 文件的预处理器条件中未处理 __loongarch64: |
| 37 | + |
| 38 | +#if defined(__x86_64__) |
| 39 | + /* x86_64 系统调用代码 */ |
| 40 | +#elif defined(__i386__) |
| 41 | + /* i386 系统调用代码 */ |
| 42 | +#elif defined(__aarch64__) |
| 43 | + /* aarch64 系统调用代码 */ |
| 44 | +#else |
| 45 | +# error "Unsupported architecture" |
| 46 | +#endif |
| 47 | + |
| 48 | +由于未定义或处理 __loongarch64,汇编器触发了 #error 指令。这需要为 LoongArch 添加特定分支。 |
| 49 | + |
| 50 | +2. syscall 指令错误 |
| 51 | + |
| 52 | +在添加以下 LoongArch 宏后: |
| 53 | + |
| 54 | +#elif defined(__loongarch64) |
| 55 | +/* LoongArch 64-bit syscall wrapper */ |
| 56 | +#define SYSCALL(NAME, NR) \ |
| 57 | +.global NAME ;\ |
| 58 | +NAME: ;\ |
| 59 | + ori $a7, $zero, NR ;\ |
| 60 | + /* $a0-$a2 assumed to contain arguments */ \ |
| 61 | +NAME ## _syscall: ;\ |
| 62 | + syscall 0 ;\ |
| 63 | + jr $ra |
| 64 | + |
| 65 | +汇编器在第 81 行和第 85 行(对应 my_execve 和 my_exit 的 syscall 0)报错 no match insn: syscall。这表明: |
| 66 | + |
| 67 | + |
| 68 | + |
| 69 | + |
| 70 | + |
| 71 | +工具链问题:GNU 汇编器(as)版本可能过旧(例如 < 2.39),不支持 LoongArch 的 syscall 指令(机器码 0x002b0000)。 |
| 72 | + |
| 73 | + |
| 74 | + |
| 75 | +架构配置错误:汇编器可能未以 LoongArch64 模式运行,导致无法识别特定指令。 |
| 76 | + |
| 77 | + |
| 78 | + |
| 79 | +语法敏感性:虽然 syscall 0 是正确语法,但旧版汇编器可能对语法解析有问题。 |
| 80 | + |
| 81 | +3. 为什么避免使用 li 和 addi.w |
| 82 | + |
| 83 | +最初,我尝试使用 li 伪指令加载系统调用号到 $a7: |
| 84 | + |
| 85 | +li $a7, NR |
| 86 | + |
| 87 | +这导致错误(no match insn: li $a7, 221),因为旧版 binutils 不支持 LoongArch 的 li 伪指令。我也考虑过 addi.w,但它不适合: |
| 88 | + |
| 89 | +立即数限制:addi.w 只支持 12 位有符号立即数(-2048 到 +2047),无法处理较大的系统调用号。 |
| 90 | + |
| 91 | +初始值依赖:addi.w $a7, $a7, NR 需要 $a7 预先初始化(例如为 0),增加了复杂性。 |
| 92 | + |
| 93 | + |
| 94 | +符号扩展:addi.w 会将结果符号扩展到 64 位,可能导致高位错误设置(系统调用号应为无符号整数)。 |
| 95 | + |
| 96 | +因此,我选择了 ori $a7, $zero, NR,它直接从零寄存器($zero)加载 12 位无符号立即数(0 到 4095),适用于小的系统调用号(如 221 和 93)。 |
| 97 | + |
| 98 | +# 解决方案 |
| 99 | + |
| 100 | +步骤 1:更新 binutils 到最新版本 |
| 101 | + |
| 102 | +syscall 指令错误很可能是由于 binutils 版本过旧。LoongArch 的支持在 binutils 2.39 及以上版本得到显著改进,推荐使用 2.41。 |
| 103 | + |
| 104 | + |
| 105 | +检查当前汇编器版本: |
| 106 | + |
| 107 | +as --version |
| 108 | + |
| 109 | +如果版本低于 2.41,更新 binutils: |
| 110 | + |
| 111 | +wget https://sourceware.org/pub/binutils/releases/binutils-2.41.tar.gz |
| 112 | +tar -xzf binutils-2.41.tar.gz |
| 113 | +cd binutils-2.41 |
| 114 | +./configure --target=loongarch64-unknown-linux-gnu |
| 115 | +make |
| 116 | +sudo make install |
| 117 | + |
| 118 | + |
| 119 | + |
| 120 | +# 验证新版本: |
| 121 | + |
| 122 | +/usr/local/bin/as --version |
| 123 | + |
| 124 | + |
| 125 | + |
| 126 | +# 重新构建 GDB: |
| 127 | + |
| 128 | +cd /home/yzw/binutils-gdb |
| 129 | +make clean |
| 130 | +./configure --target=loongarch64-unknown-linux-gnu |
| 131 | +make |
| 132 | + |
| 133 | +# 步骤 2:修改 my-syscalls.S |
| 134 | + |
| 135 | +为解决“未支持架构”和 syscall 问题,更新 my-syscalls.S 文件,添加 LoongArch 特定宏。对于小的系统调用号(221 和 93),单条 ori 指令足够: |
| 136 | + |
| 137 | +#elif defined(__loongarch64) |
| 138 | +/* LoongArch 64-bit syscall wrapper */ |
| 139 | +#define SYSCALL(NAME, NR) \ |
| 140 | +.global NAME ;\ |
| 141 | +NAME: ;\ |
| 142 | + ori $a7, $zero, NR ;\ |
| 143 | + /* $a0-$a2 assumed to contain arguments */ \ |
| 144 | +NAME ## _syscall: ;\ |
| 145 | + syscall 0 ;\ |
| 146 | + jr $ra |
| 147 | + |
| 148 | +为支持更大的系统调用号,可使用 lu12i.w 和 ori 的组合: |
| 149 | + |
| 150 | +#elif defined(__loongarch64) |
| 151 | +/* LoongArch 64-bit syscall wrapper */ |
| 152 | +#define SYSCALL(NAME, NR) \ |
| 153 | +.global NAME ;\ |
| 154 | +NAME: ;\ |
| 155 | + lu12i.w $a7, NR >> 12 ;\ |
| 156 | + ori $a7, $a7, NR & 0xfff ;\ |
| 157 | + /* $a0-$a2 assumed to contain arguments */ \ |
| 158 | +NAME ## _syscall: ;\ |
| 159 | + syscall 0 ;\ |
| 160 | + jr $ra |
| 161 | + |
| 162 | + |
| 163 | + |
| 164 | + |
| 165 | + |
| 166 | +lu12i.w $a7, NR >> 12:将 NR 的高 20 位(右移 12 位)加载到 $a7 的位 [31:12],低 12 位清零。 |
| 167 | + |
| 168 | + |
| 169 | + |
| 170 | +ori $a7, $a7, NR & 0xfff:将 NR 的低 12 位填充到 $a7 的位 [11:0]。 |
| 171 | + |
| 172 | +# 步骤 3:确保正确架构 |
| 173 | + |
| 174 | +确保汇编器以 LoongArch64 模式运行: |
| 175 | + |
| 176 | +as -march=loongarch64 -o my-syscalls.o my-syscalls.S |
| 177 | + |
| 178 | +或者,在 my-syscalls.S 文件顶部添加: |
| 179 | + |
| 180 | +.arch loongarch64 |
| 181 | + |
| 182 | +# 步骤 4:重新运行测试 |
| 183 | + |
| 184 | +重新运行测试套件: |
| 185 | + |
| 186 | +cd /home/yzw/binutils-gdb/build/gdb/testsuite |
| 187 | +runtest /home/yzw/binutils-gdb/build/gdb/testsuite/../../../gdb/testsuite/gdb.threads/step-over-thread-exit-while-stop-all-threads.exp |
| 188 | + |
| 189 | +如果错误仍未解决,启用调试日志: |
| 190 | + |
| 191 | +runtest --debug /home/yzw/binutils-gdb/build/gdb/testsuite/../../../gdb/testsuite/gdb.threads/step-over-thread-exit-while-stop-all-threads.exp |
| 192 | + |
| 193 | +检查 gdb.sum 和 gdb.log 文件以获取详细错误信息。 |
| 194 | + |
| 195 | +# 步骤 5:临时绕过方案 |
| 196 | + |
| 197 | +如果无法立即更新 binutils,可将 syscall 0 替换为机器码: |
| 198 | + |
| 199 | +.word 0x002b0000 |
| 200 | + |
| 201 | +这绕过了汇编器的问题,但因可读性差,不建议长期使用。 |
| 202 | + |
| 203 | +# 技术洞察:加载系统调用号 |
| 204 | + |
| 205 | +为什么使用 ori $a7, $zero, NR? |
| 206 | + |
| 207 | +对于小的系统调用号(例如 221 或 93): |
| 208 | + |
| 209 | + |
| 210 | +立即数范围:ori 支持 12 位无符号立即数(0 到 4095),覆盖大多数 Linux 系统调用号。 |
| 211 | + |
| 212 | + |
| 213 | +无依赖性:使用 $zero 确保结果精确为 NR,无需依赖 $a7 的初始值。 |
| 214 | + |
| 215 | + |
| 216 | + |
| 217 | +高效性:仅需一条指令,相比 lu12i.w + ori 的两指令方案更简洁。 |
| 218 | + |
| 219 | +理解 lu12i.w 和 ori |
| 220 | + |
| 221 | +对于较大的系统调用号(例如 4096 = 0x1000): |
| 222 | + |
| 223 | + |
| 224 | +二进制表示:00000000000000000001000000000000 |
| 225 | + |
| 226 | +高 20 位(位 [31:12]):00000000000000000001(NR >> 12 = 1) |
| 227 | + |
| 228 | +低 12 位(位 [11:0]):000000000000(NR & 0xFFF = 0) |
| 229 | + |
| 230 | +执行过程: |
| 231 | + |
| 232 | +lu12i.w $a7, 1:将位 [31:12] 设置为 0x1000(1 << 12 = 4096),低 12 位清零。 |
| 233 | + |
| 234 | +ori $a7, $a7, 0:保持值不变(4096)。 |
| 235 | + |
| 236 | +结果:$a7 = 4096。 |
| 237 | + |
| 238 | +此方法支持任意 32 位系统调用号。 |
| 239 | + |
| 240 | +# 经验教训 |
| 241 | + |
| 242 | +工具链兼容性:LoongArch 是较新的架构,需使用最新 binutils(2.41 或更高)以确保完整指令支持。 |
| 243 | + |
| 244 | +指令选择:对于小立即数,使用 ori 高效;对于通用场景,lu12i.w + ori 更稳健。 |
| 245 | + |
| 246 | +架构指定:使用 -march=loongarch64 或 .arch loongarch64 确保正确解析指令。 |
| 247 | + |
| 248 | +调试技巧:启用 runtest --debug 并分析 gdb.sum 和 gdb.log 以获取详细错误信息。 |
| 249 | + |
| 250 | +# 结论 |
| 251 | + |
| 252 | +LoongArch64 上的 GDB 测试套件错误源于过旧的 binutils 版本和 my-syscalls.S 中缺少 LoongArch 支持。通过更新 binutils 到 2.41、添加适当的 LoongArch SYSCALL 宏并确保正确架构模式,问题得以解决。这次经历强调了为新架构保持更新工具链的重要性。 |
| 253 | + |
| 254 | +如果你在 LoongArch 或其他架构上遇到类似问题,请检查工具链版本,单独测试汇编文件,或考虑临时使用机器码绕过。欢迎在评论区分享你的经验或问题! |
0 commit comments