目标代码定位工具
TODO
本章节将用于介绍正在研究的 RISC-V 架构适配目标代码定位工具 RVPortingTool
- 关于规则文档的撰写
同一系列规则集以两个大写字母(如 XX
)的形式编号,每条规则以 XX-0001
的形式递增编号。 每条规则包含 描述
、代码示例
、修复建议
和 自动化检测技术细节
四部分, 其中 代码示例
中,不同项目(或同一项目的不同用法)需要加小标题,格式为 示例:用法简短说明
。
每条规则包含三项元数据:
- 编程语言:规则涉及的编程语言,任意语言表示为
Any
,与源代码文件内容无关表示为None
。 - 默认级别:产生的检查报告中该规则对应问题的默认级别,可选值:
Hint
,Information
,Warning
,Error
。 - TAG:问题对应的标签,应简明扼要。
CC:C/C++ 相关规则
本节中定义了 C/C++ 语言的架构相关代码检查规则。
CC-0001 ARM 架构特有宏
- 编程语言:C/C++
- 默认级别:Error
- TAG:ARM,宏
描述
C/C++ 程序源代码中使用了 ARM 架构特有宏,这些宏在 RISC-V 架构下需要进行适配调整。
完整的 ARM 架构特有宏列表详见 此处
另外,由于宏判断结构的存在,宏的架构相关性具有 传递性, 需要同时关注在上述架构特有宏 作为条件 的宏判断结构下定义的 其它自定义宏 的用法。
本条规则是按照字典严格匹配的结果,即匹配到的宏是已知的 ARM 架构特有宏。 CC-0002 疑似 ARM 架构特有宏 是对本条规则的补充,用于模糊匹配其它以 __ARM_
开头的疑似 ARM 架构特有宏。
代码示例
以下形式的 C/C++ 程序代码可被称为“使用了”ARM 架构特有宏。
// 使用 ifdef / ifndef 判断 ARM 架构特有宏是否已定义
#ifdef __arm__
#ifndef __aarch64__
// 使用 if / elif 判断 ARM 架构特有宏是否已定义
#if __arm__
#elif (__arm__ || __aarch64__)
#if defined(__arm__)
#elif defined(__aarch64__) && defined(__ARM_FEATURE_CRYPTO)
// 使用 elifdef / elifndef 判断 ARM 架构特有宏是否已定义 (C++ 23)
#elifdef __arm__
#elifndef __aarch64__
示例:zlib 库中使用 ARM 指令集 CRC32 拓展功能
// =============================================================================
// https://github.com/madler/zlib/blob/04f42ceca40f73e2978b50e93806c2a18c1281fc/crc32.c#L102-L104
// 此处使用两个 ARM 架构特有宏 __aarch64__ 和 __ARM_FEATURE_CRC32 作为条件,定义宏 ARMCRC32
#if defined(__aarch64__) && defined(__ARM_FEATURE_CRC32) && W == 8
# define ARMCRC32
#endif
// -----------------------------------------------------------------------------
// https://github.com/madler/zlib/blob/04f42ceca40f73e2978b50e93806c2a18c1281fc/crc32.c#L110-L112
// 后续使用定义的宏 ARMCRC32 调整变量数据类型
#if defined(W) && (!defined(ARMCRC32) || defined(DYNAMIC_CRC_TABLE))
local z_word_t byte_swap OF((z_word_t word));
#endif
// =============================================================================
示例:ZeroTier 中使用 ARM 指令集 CRYPTO 拓展功能
// =============================================================================
// https://github.com/zerotier/ZeroTierOne/blob/9c9d1650d1f97f57addaf4027a8579d308eed6d5/node/Constants.hpp#L141-L144
// 此处使用 ARM 架构特有宏 __ARM_FEATURE_CRYPTO 作为条件,定义宏 ZT_ARCH_ARM_HAS_CRYPTO
#if defined(__ARM_FEATURE_CRYPTO)
// ARM Cryptography Extension
#define ZT_ARCH_ARM_HAS_CRYPTO
#endif
// -----------------------------------------------------------------------------
// https://github.com/zerotier/ZeroTierOne/blob/9c9d1650d1f97f57addaf4027a8579d308eed6d5/node/AES.hpp#L27-L29
// 后续使用定义的宏 ZT_ARCH_ARM_HAS_CRYPTO 作为条件,定义宏 ZT_AES_NEON
#if !defined(ZT_AES_NO_ACCEL) && defined(ZT_ARCH_ARM_HAS_NEON) && defined(ZT_ARCH_ARM_HAS_CRYPTO)
#define ZT_AES_NEON 1
#endif
// -----------------------------------------------------------------------------
// https://github.com/zerotier/ZeroTierOne/blob/9c9d1650d1f97f57addaf4027a8579d308eed6d5/node/AES.hpp#L107-L122
// 后续使用定义的宏 ZT_AES_NEON 作为条件,调用不同架构下的函数实现
ZT_INLINE void encrypt(const void *const in, void *const out) const noexcept
{
// ......
#ifdef ZT_AES_NEON
if (Utils::ARMCAP.aes) {
// 可能的话,调用 ARM NEON 版本 encrypt 函数实现
p_encrypt_armneon_crypto(in, out);
return;
}
#endif
// 回退到软件模拟版本 encrypt 函数实现
p_encryptSW(reinterpret_cast<const uint8_t *>(in), reinterpret_cast<uint8_t *>(out));
}
// =============================================================================
修复建议
关于原有代码:请确保原有针对 ARM 架构的相关代码已包含在 ARM 架构相关宏判断结构 中, 或已通过其它方法确保仅在 ARM 架构下启用,以防止在其它架构下产生编译错误。
关于适配代码:请对应查找目标架构下是否具有相同的拓展特性与功能, 在保证上层 API 封装兼容的前提下,结合 目标架构相关宏判断结构 引入适配代码, 并确保适配代码仅在目标架构下启用,以防止在原有架构下产生编译错误。
自动化检测技术细节
- 除
__arm__
、__aarch64__
等形式的 编译器预定义宏 外,ARM 架构特有宏通常会以__ARM_
开头,这些宏通常在特定的编译参数下由编译器预定义。 - 该条规则的自动检查需要从编译器源码入手,提取已知的架构特有宏,组织为字典进行精确识别。
- gcc 编译器源码中,应重点关注
gcc/config/arm
和gcc/config/aarch64
文件夹下的相关头文件和源文件。 - clang 编译器源码中,应重点关注
lib/Basic/Targets/AArch64.cpp
、lib/Basic/Targets/ARM.cpp
、lib/Headers/arm_*.h
这些文件。
CC-0002 疑似 ARM 架构特有宏
- 编程语言:C/C++
- 默认级别:Warning
- TAG:ARM,宏
描述
C/C++ 程序源代码中使用了 __ARM_
开头的宏,这些宏疑似是 ARM 架构特有宏, 可能需要在 RISC-V 架构下需要进行适配调整。
具体细节详见 CC-0001 ARM 架构特有宏
自动化检测技术细节
- 该条规则作为 CC-0001 的补充,检查扫描到的所有宏中是否有以
__ARM_
开头的, 如果有则将其判定为疑似 ARM 架构特有宏。
CC-0003 X86 架构特有宏
- 编程语言:C/C++
- 默认级别:Error
- TAG:ARM,宏
描述
C/C++ 程序源代码中使用了 X86 架构特有宏,这些宏在 RISC-V 架构下需要进行适配调整。
完整的 X86 架构特有宏列表详见 此处
另外,由于宏判断结构的存在,宏的架构相关性具有 传递性, 需要同时关注在上述架构特有宏 作为条件 的宏判断结构下定义的 其它自定义宏 的用法。
本条规则是按照字典严格匹配的结果,即匹配到的宏是已知的 X86 架构特有宏。
代码示例
以下形式的 C/C++ 程序代码可被称为“使用了”X86 架构特有宏。
// 使用 ifdef / ifndef 判断 X86 架构特有宏是否已定义
#ifdef __i386__
#ifndef __amd64__
// 使用 if / elif 判断 X86 架构特有宏是否已定义
#if __i386__
#elif (__i386__ || __amd64__)
#if defined(__i386__)
#elif defined(__i386__) && defined(__amd64__)
// 使用 elifdef / elifndef 判断 X86 架构特有宏是否已定义 (C++ 23)
#elifdef __i386__
#elifndef __amd64__
示例:Radare2 库中使用 X86 架构特有宏
// =============================================================================
// https://github.com/radareorg/radare2/blob/5f8c619d457aadf75b058207409fade667e51e3b/libr/debug/p/native/reg.c#L46-L49
// 此处使用 X86 架构特有宏控制 include 引入的头文件
#if __i386__ || __i386
#include "reg/kfbsd-x86.h"
#elif __x86_64__ || __amd64__
#include "reg/kfbsd-x64.h"
// =============================================================================
UPX 库中使用 X86 架构特有宏
// =============================================================================
// https://github.com/upx/upx/blob/7993e619cda0792ae82a252799ede32957149c93/src/miniacc.h#L939-L941
// 此处使用 X86 架构特有宏控制自定义宏
#elif defined(__amd64__) || defined(__x86_64__) || defined(_M_AMD64)
# define ACC_ARCH_AMD64 1
# define ACC_INFO_ARCH "amd64"
// =============================================================================
修复建议
关于原有代码:请确保原有针对 X86 架构的相关代码已包含在 X86 架构相关宏判断结构 中, 或已通过其它方法确保仅在 X86 架构下启用,以防止在其它架构下产生编译错误。
关于适配代码:请对应查找目标架构下是否具有相同的拓展特性与功能, 在保证上层 API 封装兼容的前提下,结合 目标架构相关宏判断结构 引入适配代码, 并确保适配代码仅在目标架构下启用,以防止在原有架构下产生编译错误。
自动化检测技术细节
- 除
__i386__
、__amd64__
等形式的 编译器预定义宏 外,X86 架构还有许多扩展集对应的宏(如__SSE*
、__AVX*
等),这些宏通常在特定的编译参数下由编译器预定义。 - 该条规则的自动检查需要从编译器源码入手,提取已知的架构特有宏,组织为字典进行精确识别。
- gcc 编译器源码中,应重点关注
gcc/config/i386
和gcc/config/ia64
文件夹下的相关头文件和源文件。 - clang 编译器源码中,应重点关注
lib/Basic/Targets/X86.cpp
和lib/Headers/x86intrin.h
(以及该文件所 include 的所有文件)这些文件。
CC-0004 架构相关 builtin 函数
- 编程语言:C/C++
- 默认级别:Error
- TAG:内置函数
描述
警告
通常而言,__builtin_
开头的编译器内置函数具有其上层包装的方法, 理论上 非编译器的普通程序 应当使用上层包装方法,而不应直接使用这些内置函数。 特殊情况需要使用时,应添加 __has_builtin
预处理宏用于检查欲使用的内置函数是否存在。
C/C++ 程序源代码中直接使用了 __builtin_
开头的编译器内置函数。 这些内置函数中,目前已知以下述列表中的任意前缀开头的是与特定目标架构相关的函数, 这些函数可能需要针对目标架构进行适配调整。
前缀 | 架构名称 | 描述 |
---|---|---|
__builtin_arm_ | ARM | |
__builtin_neon_ | ARM | |
__builtin_mve_ | ARM | |
__builtin_ia32_ | X86 | |
__builtin_ia64_ | X86 | |
__builtin_riscv_ | RISC-V |
代码示例
示例:Torque6 库中使用 ARM 架构特有 builtin 函数
// =============================================================================
// https://github.com/andr3wmac/Torque6/blob/db6cd08f18f4917e0c6557b2766fb40d8e2bee39/lib/bgfx/include/bx/float4_neon.h#L270-L278
// 此处使用 ARM 架构特有 builtin 函数加速浮点运算
BX_FLOAT4_FORCE_INLINE float4_t float4_and(float4_t _a, float4_t _b)
{
const _i32x4_t tmp0 = __builtin_neon_vreinterpretv4siv4sf(_a);
const _i32x4_t tmp1 = __builtin_neon_vreinterpretv4siv4sf(_b);
const _i32x4_t tmp2 = __builtin_neon_vandv4si(tmp0, tmp1, 0);
const float4_t result = __builtin_neon_vreinterpretv4sfv4si(tmp2);
return result;
}
// =============================================================================
示例:fmt 库中使用 X86 架构特有 builtin 函数
// =============================================================================
// https://github.com/fmtlib/fmt/blob/f89cd276f7dead38f11cebc73d1e91a1b1b38124/include/fmt/format.h#L433-L437
// 此处使用 X86 架构特有 builtin 函数加速运算
#elif FMT_HAS_BUILTIN(__builtin_ia32_addcarryx_u64) && !defined(__ibmxl__)
unsigned long long result;
auto carry = __builtin_ia32_addcarryx_u64(0, lo_, n, &result);
lo_ = result;
hi_ += carry;
// =============================================================================
修复建议
尽量避免直接使用
__builtin_
开头的函数和数据结构,而是使用它们的上层封装方法。如确需使用这些函数和数据结构,请确保这些函数和数据结构已包含在
__has_builtin
或其它形式的 架构相关宏判断结构 中,即仅在特定架构下启用, 从而避免在其它架构下产生编译错误。
自动化检测技术细节
- 该条规则通过关键字匹配 C/C++ 源码内容(或静态分析提取的函数名称), 检查是否含有已知的架构相关内置函数前缀。
CC-0005 内联汇编
- 编程语言:C/C++
- 默认级别:Error
- TAG:汇编
描述
C/C++ 程序源代码中使用了内联汇编代码,这些汇编代码可能需要针对目标架构进行适配调整。
对 GCC 编译器 来说,它同时支持 Intel 和 AT&T 两种语法的内联汇编。
对 MSVC 编译器 来说,它支持 Microsoft C/C++ Inline Assembly 语法的内联汇编。
修复建议
合理评估使用内联汇编的必要性,非必要不使用内联汇编。
参考 汇编程序 一节中提供的移植方案和辅助工具, 尝试对已有的内联汇编代码进行修改,以适配目标架构。若移植难度较高或缺少复杂指令的替代方案, 请尝试通过 C/C++ 代码模拟原有汇编代码的功能,或到开源社区寻求帮助。
自动化检测技术细节
- 静态分析过程中,内联汇编语句在抽象语法树 (AST) 上会被构建为内联汇编类型的结点, 对 clang 来说通常为
AsmStmt
类型的结点,可以此作为标准检出内联汇编语句。 - 也可以通过检查
asm
、__asm
和__asm__
开头的语句(或语句块)来识别内联汇编, 但可能需要自行将汇编语句块进行拼接处理。
CC-0006 有符号数的右移运算
- 编程语言:C/C++
- 默认级别:Error
- TAG:移位,位运算,有符号数,实现定义行为
描述
C/C++ 程序源代码中,出现了有符号数的右移运算。
C17 标准 6.5.7 节给出了有关右移运算的相关说明:
The result of E1 >> E2 is E1 right-shifted E2 bit positions.
...
If E1 has a signed type and a negative value, the resulting value is implementation-defined.
这段表述指出,若右移运算中的左操作数是有符号数且为负值,右移运算的结果取决于编译器的实现方式 (算术右移或逻辑右移),即该用法属于一种 实现定义行为 (Implementation-defined Behavior)。 右移运算的具体实现方式主要是考虑到一些精简指令集的处理器不包括算术右移指令,此时为编译器提供一种额外的选择。
尽管主流的编译器(如 GCC 和 Clang 等)都默认将 有符号数的右移运算 处理为 算术右移, 主流的硬件架构(如 X86、ARM 和 RISC-V 等)也都支持 算数右移 指令, 但还是应尽可能避免在 C/C++ 程序中出现有符号数的右移操作,以免在使用不同平台或不同编译器时出现问题。
注:右移运算相关的操作符包括 右移运算符
(>>) 和 右移赋值运算符
(>>=) 两种。
修复建议
避免对有符号数进行右移运算操作。
代码示例
示例:有符号数的右移运算导致缓冲区溢出
本示例节选自 INT13-C. Use bitwise operators only on unsigned operands - SEI CERT C Coding Standard
// =============================================================================
// https://wiki.sei.cmu.edu/confluence/display/c/INT13-C.+Use+bitwise+operators+only+on+unsigned+operands
// 节选自 INT13-C. Use bitwise operators only on unsigned operands - SEI CERT C Coding Standard
int rc = 0;
// 有符号数 stringify
int stringify = 0x80000000;
// 字符串缓冲区 buf
char buf[sizeof("256")];
// 有符号数 stringify 右移 24 位,结果转为字符串存入缓冲区 buf
rc = snprintf(buf, sizeof(buf), "%u", stringify >> 24);
if (rc == -1 || rc >= sizeof(buf)) {
/* Handle error */
}
// =============================================================================
上述示例中,第 4 行将有符号数 stringify
右移了 24 位。
对于按照 逻辑右移 处理的情形,右移后值为 0x00000080
,按照 %u
控制符格式化为字符串后值为 256
, 可被缓冲区 buf
正常存储。
对于按照 算数右移 处理的情形,右移后值为 0xFFFFFF80
,按照 %u
控制符格式化为字符串后值为 4294967168
, 超出了缓冲区 buf
的长度上限,导致 snprintf()
函数出错。若使用的是不安全的 sprintf()
函数, 则会造成缓冲区溢出。
自动化检测技术细节
- 由于需要检查右移运算符的 左操作数类型,建议通过 clang AST 进行检查,判断是否为有符号数类型。
- 在 clang AST 上,二元运算符所对应节点类型为
BinaryOperator
, 右移运算符>>
所对应的运算符 ID 为BO_Shr
, 右移赋值运算符>>=
所对应的运算符 ID 为BO_ShrAssign
。
CC-0007 编译器架构相关预定义宏
- 编程语言:C/C++
- 默认级别:Warning
- TAG:宏
描述
C/C++ 程序源代码中使用的宏和架构直接相关(或间接相关)。
- 直接相关:指所使用的宏是已知的 编译器预定义宏 ,这些宏直接表明当前欲编译到的目标平台。
- 间接相关:以上述 直接相关 宏作为条件的判断结构中,由开发者自定义的宏。 这些宏作为条件的判断结构中定义的宏也视为 间接相关宏。
需要注意的是,间接相关宏可能会存在用宏函数拼接的情形。例如:
// Macro __x86_64__ is directly architecture-related
#if defined(__x86_64__)
// Macro ARCH_X86_64 is indirectly architecture-related
#define ARCH_X86_64
#endif
// A macro function to check whether macro `ARCH_##ARCH_NAME` is defined
#define ISARCH(ARCH_NAME) (defined ARCH_##ARCH_NAME && ARCH_##ARCH_NAME)
// ISARCH(X86_64) expands to: defined ARCH_X86_64 && ARCH_X86_64
#if ISARCH(X86_64)
// do something...
#endif
修复建议
确保特定架构下的代码已具有对应目标架构的版本。
代码示例
示例:qtscript 中使用 WTF_CPU_ 为前缀的间接架构相关宏
// =============================================================================
// https://github.com/qt/qtscript/blob/b65a9693f730f625cda95684acfaefd21538a5da/src/3rdparty/javascriptcore/JavaScriptCore/wtf/Platform.h#L43-L44
// qtscript 中定义宏函数 CPU() 用于判断当前目标架构
// =============================================================================
/* CPU() - the target CPU architecture */
#define CPU(WTF_FEATURE) (defined WTF_CPU_##WTF_FEATURE && WTF_CPU_##WTF_FEATURE)
// =============================================================================
// https://github.com/qt/qtscript/blob/b65a9693f730f625cda95684acfaefd21538a5da/src/3rdparty/javascriptcore/JavaScriptCore/wtf/Platform.h#L229-L236
// qtscript 中根据架构相关编译器预定义宏,自定义以 WTF_CPU_ 为前缀的架构间接相关宏
// =============================================================================
/* CPU(X86) - i386 / x86 32-bit */
#if defined(__i386__) \
|| defined(i386) \
|| defined(_M_IX86) \
|| defined(_X86_) \
|| defined(__THW_INTEL)
#define WTF_CPU_X86 1
#endif
// =============================================================================
// https://github.com/qt/qtscript/blob/b65a9693f730f625cda95684acfaefd21538a5da/src/3rdparty/javascriptcore/JavaScriptCore/wtf/TCSpinLock.h#L65-L70
// qtscript 中通过自定义宏函数 CPU() 判断目标架构,应用内联汇编代码
// =============================================================================
#if CPU(X86) || CPU(X86_64)
__asm__ __volatile__
("xchgl %0, %1"
: "=r"(r), "=m"(lockword_)
: "0"(1), "m"(lockword_)
: "memory");
// =============================================================================
自动化检测技术细节
- 编译器的架构相关预定义宏可参考知识库内已整理的相关内容
- 宏的传递性建议通过 Clang Preprocessor Callback 实现
- 由于分析顺序原因,只有分析到在架构直接相关宏的判断结构中定义的间接相关宏后,才能传递 宏的架构相关性,因此可能需要先记录扫描到的所有宏名称(包括展开前和展开后的),最终 再根据传递关系进行统一判断(可能还需要考虑跨文件分析的问题)。
CC-0008 疑似架构相关宏
- 编程语言:C/C++
- 默认级别:Warning
- TAG:宏,关键字检查
描述
C/C++ 程序源代码中使用的宏出现了常见的 架构相关关键字 ,疑似为架构相关宏。
本条规则所检查的宏也将针对架构分为 直接相关宏 和 间接相关宏。
- 直接相关宏:宏名称中直接包含架构名称关键字。
- 间接相关宏:宏定义在以其它直接相关宏(或其它已确定的间接相关宏)为条件的判断结构中。
注:该条规则误报风险较高,实际检查时需谨慎启用。
修复建议
若确认该宏及其相关代码结构与特定架构相关,则应确保这些代码具有对应目标架构的版本。
代码示例
示例:kexec-tools 中 EM_ 开头含架构关键字的宏
// =============================================================================
// https://github.com/horms/kexec-tools/blob/ae1bd3dde601516f9e90fdc127aa48a81c0d33bc/include/elf.h#L179-L269
// kexec-tools 中定义的 EM_ 开头含架构关键字的宏
// =============================================================================
#define EM_386 3 /* Intel 80386 */
#define EM_MIPS 8 /* MIPS R3000 big-endian */
#define EM_S390 22 /* IBM S390 */
#define EM_LOONGARCH 258 /* Loongson Loongarch*/
// =============================================================================
示例:kexec-tools 中 KEXEC_ARCH_ 开头含架构关键字的宏
// =============================================================================
// https://github.com/horms/kexec-tools/blob/ae1bd3dde601516f9e90fdc127aa48a81c0d33bc/kexec/kexec-syscall.h#L122-L140
// kexec-tools 中定义的 KEXEC_ARCH_ 开头含架构关键字的宏
// =============================================================================
/* These values match the ELF architecture values.
* Unless there is a good reason that should continue to be the case.
*/
#define KEXEC_ARCH_DEFAULT ( 0 << 16)
#define KEXEC_ARCH_386 ( 3 << 16)
#define KEXEC_ARCH_68K ( 4 << 16)
#define KEXEC_ARCH_HPPA (15 << 16)
#define KEXEC_ARCH_X86_64 (62 << 16)
#define KEXEC_ARCH_PPC (20 << 16)
#define KEXEC_ARCH_PPC64 (21 << 16)
#define KEXEC_ARCH_IA_64 (50 << 16)
#define KEXEC_ARCH_ARM (40 << 16)
#define KEXEC_ARCH_ARM64 (183 << 16)
#define KEXEC_ARCH_S390 (22 << 16)
#define KEXEC_ARCH_SH (42 << 16)
#define KEXEC_ARCH_MIPS_LE (10 << 16)
#define KEXEC_ARCH_MIPS ( 8 << 16)
#define KEXEC_ARCH_CRIS (76 << 16)
#define KEXEC_ARCH_LOONGARCH (258 << 16)
// =============================================================================
自动化检测技术细节
- 根据预定义的架构相关关键字检测宏名称,若宏名称包含架构相关关键字则标记为直接相关宏
- 间接相关宏与 CC-0007 的检测方法相同
CC-0009 疑似架构相关枚举类型
- 编程语言:C/C++
- 默认级别:Warning
- TAG:枚举类型,关键字检查
描述
C/C++ 程序源代码中使用的枚举类型及其成员名称中出现了常见的 架构相关关键字 ,疑似为架构相关枚举类型。
修复建议
注意增补目标架构对应的枚举类型(或成员)。
代码示例
示例:lldb 中定义各硬件架构的子架构枚举类型
// =============================================================================
// https://github.com/llvm/llvm-project/blob/e9c34618c904e0b22b6c9fec1f4a410e7484b8d3/lldb/include/lldb/Utility/ArchSpec.h#L34-L48
// lldb 中定义的 MIPS 子架构枚举类型
// =============================================================================
enum MIPSSubType {
eMIPSSubType_unknown,
eMIPSSubType_mips32,
eMIPSSubType_mips32r2,
eMIPSSubType_mips32r6,
eMIPSSubType_mips32el,
eMIPSSubType_mips32r2el,
eMIPSSubType_mips32r6el,
eMIPSSubType_mips64,
eMIPSSubType_mips64r2,
eMIPSSubType_mips64r6,
eMIPSSubType_mips64el,
eMIPSSubType_mips64r2el,
eMIPSSubType_mips64r6el,
};
// =============================================================================
// https://github.com/llvm/llvm-project/blob/e9c34618c904e0b22b6c9fec1f4a410e7484b8d3/lldb/include/lldb/Utility/ArchSpec.h#L105-L109
// lldb 中定义的 RISC-V 子架构枚举类型 (2021年1月8日加入)
// =============================================================================
enum RISCVSubType {
eRISCVSubType_unknown,
eRISCVSubType_riscv32,
eRISCVSubType_riscv64,
};
// =============================================================================
// https://github.com/llvm/llvm-project/blob/e9c34618c904e0b22b6c9fec1f4a410e7484b8d3/lldb/include/lldb/Utility/ArchSpec.h#L111-L115
// lldb 中定义的 loongarch 子架构枚举类型 (2022年11月1日加入)
// =============================================================================
enum LoongArchSubType {
eLoongArchSubType_unknown,
eLoongArchSubType_loongarch32,
eLoongArchSubType_loongarch64,
};
// =============================================================================
自动化检测技术细节
- 对枚举类型本身及其成员的名称进行关键字检测。
CC-0010 多版本函数
- 编程语言:C/C++
- 默认级别:Warning
- TAG:多版本函数
描述
C/C++ 程序源代码中使用了 多版本函数 (Function Multiversioning) 机制,这些多版本函数代码可能需要针对目标架构进行适配调整。
多版本函数机制可以允许同一个函数在不同的目标扩展指令集下拥有多套不同的实现, 以充分利用特定扩展指令集的优势,加速程序运行。
修复建议
若软件项目所使用的编译器支持针对目标架构的多版本函数特性,则应考虑为目标架构对应的扩展指令集编写对应版本的函数实现。
若目标架构尚不支持多版本函数特性,则应确保对应功能的函数有默认版本的实现。
代码示例
示例:针对 X86/ARM 架构特定扩展指令集的多版本函数实现
__attribute__ ((target ("default")))
int foo(void) {
// 默认版本的 foo 函数
return 0;
}
__attribute__ ((target ("sse4.2")))
int foo(void) {
// 针对 SSE 4.2 架构的 foo 函数
return 1;
}
__attribute__ ((target ("arch=atom")))
int foo(void) {
// 针对 Atom 系列处理器的 foo 函数
return 2;
}
__attribute__ ((target ("arch=amdfam10")))
int foo(void) {
// 针对 0x10 系列 AMD 处理器的 foo 函数
return 3;
}
int main () {
// 自动根据目标硬件架构平台调用对应版本的 foo 函数
printf("%d", foo());
return 0;
}
自动化检测技术细节
- 在 Clang 中,
__attribute__
语句在 AST 上体现为Attr
类型语句, 可通过FunctionNameVisitor::VisitAttr()
回调方法进行分析。 - 通过
AttributeCommonInfo::getParsedKind()
方法可获得__attribute__
语句 对应的类型,当类型为Attr::AT_Target
时,表示该语句为多版本函数配置语句。
CC-0011 X86 架构特有系统调用
- 编程语言:C/C++
- 默认级别:Warning
- TAG:系统调用
描述
C/C++ 程序源代码中使用了 X86 架构特有的系统调用,这些系统调用可能需要针对目标架构进行适配调整。
目前已知的 X86 平台特有的系统调用主要包括:epoll_ctl_old
、epoll_wait_old
、old_getpagesize
和 timerfd
。 具体细节详见:X86 架构特有的系统调用
修复建议
由于名称中含 old
标记的系统调用已基本弃用,开发者应考虑使用其它系统调用替代。
代码示例
最新版本的 Linux 内核已移除了上述 X86 平台下特有的系统调用, 若项目版本过早仍包含这些系统调用,请考虑使用新版的系统调用。 具体的替换方案详见:X86 架构特有的系统调用
自动化检测技术细节
- 检查是否出现了这些函数名称(精确匹配),需要排除自定义的函数。
- 这些函数在新版本的 Linux 系统中可能会出现找不到定义的问题,如不便在编译阶段检测,可采用正则匹配。
RB:rpmbuild 配置文件相关规则
RB-0001 架构判断宏语句块
- 编程语言:rpmbuild
- 默认级别:Warning
- TAG:rpmbuild,rpmspec,架构判断宏,ifarch,ifnarch
描述
rpmbuild 配置文件中出现了 %ifarch
/ %ifnarch
形式的架构判断宏语句, 其中判断结构内的代码可能仅适用于特定架构,需考虑是否需要针对目标架构进行修改适配。
修复建议
如有需要,应在该判断结构的同级增加针对目标架构的判断语句,并为目标架构编写兼容配置。
代码示例
详见 ifarch/ifnarch 语句 中给出的示例。
自动化检测技术细节
- 检查 rpmbuild 配置文件(spec 文件)中是否出现
%ifarch
/%ifnarch
与%endif
组成的结构 - 可根据 ifarch/ifnarch 语句 中说明的几种形式进一步检查已知结构的目的,包括但不限于: 选择性应用 Patch、合成编译参数、自定义宏或全局变量、切换必需依赖、推荐弹性依赖等。
RB-0002 架构相关内置宏语句块
- 编程语言:rpmbuild
- 默认级别:Warning
- TAG:rpmbuild,rpmspec,架构相关宏
描述
rpmbuild 配置文件中出现了除标准形式架构判断宏语句(%ifarch
/ %ifnarch
)外的架构相关内置宏, 其中判断结构内的代码可能仅适用于特定架构,需考虑是否需要针对目标架构进行修改适配。
修复建议
如有需要,应在该判断结构的同级增加针对目标架构的判断语句,并为目标架构编写兼容配置。
代码示例
详见 rpm 工具内置宏 中给出的示例。
自动化检测技术细节
- 检查 rpmbuild 配置文件(spec 文件)中是否出现 rpm 工具内置宏 中给出的四种 rpm 工具内置宏用法,识别对应的判断语句块。
- 可仿照 架构判断宏语句块 中的方法进一步检查已知判断结构的目的, 包括但不限于:选择性应用 Patch、合成编译参数、自定义宏或全局变量、切换必需依赖、推荐弹性依赖等。
RB-0003 内嵌架构相关 Shell 脚本
- 编程语言:rpmbuild
- 默认级别:Warning
- TAG:rpmbuild,rpmspec,uname
描述
rpmbuild 配置文件中出现了架构相关的内嵌 Shell 脚本(目前主要指包含 uname
命令), 其中判断结构内的代码可能仅适用于特定架构,需考虑是否需要针对目标架构进行修改适配。
修复建议
如有需要,应在该判断结构的同级增加针对目标架构的判断语句,并为目标架构编写兼容配置。
建议优先使用 rpm 工具官方提供的 %ifarch
和 %ifnarch
宏进行架构判断。
代码示例
详见 内嵌 Shell 脚本 中给出的示例。
自动化检测技术细节
- 检查 rpmbuild 配置文件(spec 文件)中是否出现包含
uname
命令的内嵌 Shell 脚本, 如果出现在判断结构中,则将判断结构一并报出。
RB-0004 依赖项名称出现架构相关内置宏
- 编程语言:C/C++
- 默认级别:Notice
- TAG:rpmbuild,rpmspec,架构相关宏
描述
rpmbuild 配置文件的依赖配置项 (Requires
、BuildRequires
和 Recommends
等字段) 指定的软件包名称中出现了架构相关内置宏 (_arch
、_target_cpu
、_isa
和 __isa_name
等), 需要确认目标架构下对应的依赖项是否存在。
修复建议
- 若依赖项 存在 目标架构下对应的版本,则无需进行修改。
- 若依赖项 不存在 目标架构下对应的版本,则应考虑是否需要针对目标架构寻找替代依赖。
- 若目标架构下 无需该依赖也可正常编译运行,则应使用架构判断宏将其屏蔽。
代码示例
参见 根据架构相关内置宏取值引入不同版本依赖 中给出的示例。
自动化检测技术细节
- 检查 rpmspec 配置文件的依赖配置项中是否出现了上述 架构相关内置宏,如出现了则报出。
- 有条件的话应进一步检查这些依赖项是否已被
%ifarch
等架构判断结构包裹,若已被包裹则可以降低问题级别。 但也仍有可能会造成目标架构下依赖缺失,还是应当作为问题保留。
MK:Makefile 相关规则
MK-0001 通过 uname 命令获取架构相关信息
- 编程语言:Makefile
- 默认级别:Warning
- TAG:Makefile,uname
描述
Makefile 中使用 uname
命令获取了架构相关信息,需要关注相关变量的使用情况。
通常而言,通过 uname
命令获取
修复建议
此处是修复建议。
代码示例
此处是代码示例。
自动化检测技术细节
此处是自动化检测技术细节。
模板
- 编程语言:C/C++
- 默认级别:Error
- TAG:汇编
描述
C/C++ 程序源代码中使用了内联汇编代码,这些汇编代码可能需要针对目标架构进行适配调整。
修复建议
此处是修复建议。
代码示例
此处是代码示例。
自动化检测技术细节
此处是自动化检测技术细节。
附录1 常见架构相关关键字列表
注:本关键字列表不区分大小写。
"i386", "i486", "i586", "i686", "i786", "x86",
"amd64", "x86_64", "x64",
"ppc", "ppc64", "ppc64le",
"arm", "armv7l",
"aarch64",
"s390x", "s390",
"riscv64"