Linux错误(6)X64向量指令访问地址未对齐引起SIGSEGV

Linux错误(6)X64向量指令访问地址未对齐引起SIGSEGV


Author: Once Day Date: 2025年4月4日

一位热衷于Linux学习和开发的菜鸟,试图谱写一场冒险之旅,也许终点只是一场白日梦…

漫漫长路,有人对你微笑过嘛…

全系列文章可参考专栏: Linux实践记录_Once_day的博客-CSDN博客


文章目录

  • Linux错误(6)X64向量指令访问地址未对齐引起SIGSEGV
        • 1. 问题分析
          • 1.1 现象介绍
          • 1.2 分析原因
          • 1.3 解决思路
          • 1.4 内存申请与对齐
        • 2. 实例验证
          • 2.1 使用posix_memalign对齐内存
          • 2.2 使用aligned_alloc 对齐内存
        • 3. 总结

1. 问题分析
1.1 现象介绍

X64设备上,如果定义一个结构体,其包含连续的整数字段,并且存在类似的算术操作,编译会自动优化代码,生成向量指令SSE/AVX(xmm0),但是由于这些结构体的地址没有对齐到16字节,读取数据时会触发SIGSEGV错误,造成coredump。

如下面这段代码在O3优化等级下,会生成向量指令,从而触发SIGSEGV问题:

struct result128 {uint64_t low;uint64_t high;
} __attribute__((aligned(16)));/* 禁止内联 */
__attribute__((noinline)) void data128_add(struct result128 *result128, uint64_t low, uint64_t high)
{result128->low += low;result128->high += high;
}// data128_add函数二进制反汇编如下
// 1374:       66 48 0f 6e c6          movq   %rsi,%xmm0
// 1379:       66 48 0f 6e ca          movq   %rdx,%xmm1
// 137e:       66 0f 6c c1             punpcklqdq %xmm1,%xmm0
// 1382:       66 0f d4 07             paddq  (%rdi),%xmm0
// 1386:       0f 29 07                movaps %xmm0,(%rdi)

GDB调试运行结果如下,可以清晰看到执行的指令、代码行和数据地址信息:

在这里插入图片描述

整个源码文件如下:

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>struct result128 {uint64_t low;uint64_t high;
} __attribute__((aligned(16)));/* 禁止内联 */
__attribute__((noinline)) void data128_add(struct result128 *result128, uint64_t low, uint64_t high)
{result128->low += low;result128->high += high;// -O3 编译会生成如下代码
// 1374:       66 48 0f 6e c6          movq   %rsi,%xmm0
// 1379:       66 48 0f 6e ca          movq   %rdx,%xmm1
// 137e:       66 0f 6c c1             punpcklqdq %xmm1,%xmm0
// 1382:       66 0f d4 07             paddq  (%rdi),%xmm0
// 1386:       0f 29 07                movaps %xmm0,(%rdi)
}int main(void)
{// 申请一段内存, 64字节uint8_t *data = malloc(64);memset(data, 0xb, 64);// 手动对齐到 1/2/4/8/16 字节uint8_t  *data1   = (uint8_t *)(((uintptr_t)data + 0) & ~0) + 1;uint16_t *data16  = (uint16_t *)(((uintptr_t)data + 1) & ~1);uint32_t *data32  = (uint32_t *)(((uintptr_t)data + 3) & ~3);uint64_t *data64  = (uint64_t *)(((uintptr_t)data + 7) & ~7);uint64_t *data128 = (uint64_t *)(((uintptr_t)data + 15) & ~15);printf("data addr       : %p \n", data);printf("  data1 addr    : %p \n", data1);printf("  data16 addr   : %p \n", data16);printf("  data32 addr   : %p \n", data32);printf("  data64 addr   : %p \n", data64);printf("  data128 addr  : %p \n", data128);printf("Data load test:\n");printf("  2 bytes, unalign: 0x%x(%p).\n", *(uint16_t *)data1, data1);printf("  4 bytes, unalign: 0x%x(%p).\n", *(uint32_t *)data1, data1);printf("  8 bytes, unalign: 0x%lx(%p).\n", *(uint64_t *)data1, data1);// 使用ASM内联汇编读取到xmm0寄存器struct result128 result128;// 读取 128位数据到xmm0寄存器__asm__ volatile("movdqa %0, %%xmm0" : : "m"(*data128) : "%xmm0");// 将xmm0寄存器的值存储到result128中__asm__ volatile("movdqa %%xmm0, %0" : "=m"(result128) : : "%xmm0");printf("  16 bytes align, result: 0x%lx, 0x%lx.\n", result128.low, result128.high);struct result128 *result128_addr = (struct result128 *)data1;data128_add(result128_addr, 0x1234567890abcdef, 0xfedcba0987654321);printf("  16 bytes unalign, result: 0x%lx, 0x%lx.\n", result128.low, result128.high);return 0;
}
1.2 分析原因

在X64架构下,未对齐的内存访问(如2/4/8字节非对齐访问)可能会导致性能下降,但通常不会引发SIGSEGV错误。当使用SSE/AVX向量指令(如paddq)访问未对齐的内存时,会触发SIGSEGV错误,因为这些指令要求内存地址必须对齐到16字节边界。

编译器在O3优化等级下,识别出result128结构体的连续整数字段,并自动生成了向量指令来优化代码。虽然result128结构体本身声明了16字节对齐,但实际传入的结构体指针可能没有对齐到16字节边界,导致向量指令访问未对齐内存而触发SIGSEGV。

1.3 解决思路

确保传入的result128结构体指针已对齐到16字节边界。可以使用posix_memalignaligned_alloc等函数分配对齐的内存。如果无法保证传入指针的对齐性,可以在函数内部使用memcpy将未对齐的数据复制到局部的、已对齐的result128结构体中,再进行计算和写回。

可以使用#pragma pack(16)声明结构体,强制编译器始终按16字节对齐结构体。但这可能会浪费内存空间。也可以使用__attribute__((aligned(16)))修饰函数参数,确保传入的指针已对齐。但这需要调用方遵循对齐要求。

在编译选项中使用-mno-sse-mno-avx禁用向量指令,避免自动向量化。但这会影响性能。

如果以上方法都无法实现,可以尝试修改算法,避免在结构体上使用连续的算术操作,从而避免触发向量化。

1.4 内存申请与对齐

常见的动态内存分配函数如malloc、calloc、realloc等,默认情况下返回的内存地址已经满足了基本的对齐要求,一般是按照系统的最大基本数据类型对齐(如long double、指针等)。但这些函数无法直接指定更大的对齐字节数。

以下是一些支持指定内存对齐字节数的函数:

(1)posix_memalign (POSIX标准)

int posix_memalign(void **memptr, size_t alignment, size_t size);

posix_memalign可以指定alignment参数,要求必须是2的幂次且至少为sizeof(void*)。函数将分配size字节的内存,并确保内存地址按alignment字节对齐。

(2)aligned_alloc (C11标准)

void *aligned_alloc(size_t alignment, size_t size);

aligned_alloc类似于posix_memalign,但将对齐的内存地址直接返回,而不是通过指针参数传递。

(3)memalign (GNU扩展)

void *memalign(size_t alignment, size_t size);

memalign是GNU的扩展函数,功能与aligned_alloc相似,但可移植性较差。

(4)_aligned_malloc (Windows)

void *_aligned_malloc(size_t size, size_t alignment);

_aligned_malloc是Windows平台下的对齐内存分配函数,类似于aligned_alloc。

(5)valloc (已废弃)

void *valloc(size_t size);

valloc分配的内存按虚拟内存页大小(通常为4KB)对齐,但已被废弃,不建议使用。

使用这些对齐内存分配函数获取的内存,必须使用对应的内存释放函数(如aligned_free、free等)来释放,以避免内存泄漏

2. 实例验证
2.1 使用posix_memalign对齐内存

使用posix_memalign申请16字节对齐内存,执行函数,然后释放:

struct result128 *result128_addr;
int32_t ret = posix_memalign((void **)&result128_addr, 16, sizeof(struct result128));
if (ret != 0) {printf("posix_memalign failed, ret: %d.\n", ret);return -1;
}
data128_add(result128_addr, 0x1234567890abcdef, 0xfedcba0987654321);
printf("  16 bytes unalign, result: 0x%lx, 0x%lx.\n", result128_addr->low,result128_addr->high);// 释放内存
free(data);
free(result128_addr);

在类Unix系统(如Linux、macOS等)中,只需包含<stdlib.h>即可使用posix_memalign函数。

posix_memalign是POSIX标准定义的函数,在某些嵌入式系统或者非POSIX兼容的操作系统上可能无法使用。在这种情况下,可以考虑使用其他平台特定的对齐内存分配函数,或者自行实现对齐内存分配的逻辑。

2.2 使用aligned_alloc 对齐内存

使用与posix_memalign类似,如下:

struct result128 *result128_addr = aligned_alloc(16, sizeof(struct result128));
data128_add(result128_addr, 0x1234567890abcdef, 0xfedcba0987654321);
printf("  16 bytes unalign, result: 0x%lx, 0x%lx.\n", result128_addr->low,result128_addr->high);// 释放内存
free(data);
free(result128_addr);

aligned_alloc函数是C11标准引入的,用于分配指定对齐字节数的内存。

aligned_alloc函数返回一个指向对齐内存的指针,该内存块的大小为size字节,并按alignment字节对齐。如果分配成功,返回的指针可以直接传递给free函数释放。

aligned_alloc函数与普通的malloc函数都遵循了相同的内存管理约定,即使用free函数释放内存。

3. 总结

在X64架构下,使用未对齐的内存地址进行SSE/AVX向量指令访问时,可能会触发SIGSEGV错误。这通常发生在编译器对包含连续整数字段的结构体进行自动向量化优化时。

为了避免这类问题,可以采取以下措施:

  • 确保传入的结构体指针已对齐到16字节边界,可使用posix_memalign、aligned_alloc等函数分配对齐内存。
  • 在函数内部使用memcpy处理未对齐数据,复制到局部的对齐结构体中进行计算和写回。
  • 在编译选项中禁用向量指令,或修改算法避免触发自动向量化。

常见的动态内存分配函数如malloc、calloc等默认返回的内存已经满足基本的对齐要求(8字节),但无法直接指定更大的对齐字节数。为此,可以使用posix_memalign、aligned_alloc、memalign、_aligned_malloc等支持指定对齐字节数的函数。

在实际应用中,应优先使用标准的对齐内存分配函数,遵循最小适配原则,并使用对应的内存释放函数,以提高代码的可移植性、兼容性和内存使用效率。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/75781.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

解码 __iter__ 和 itertools.islice - 迭代的艺术

文章目录 前言一、`_iter__`:自定义迭代的钥匙1.1 什么是 __iter__?1.2 基本用法1.3 高级用法:独立迭代器二、itertools.islice:迭代切片的利器2.1 什么是 itertools.islice?2.2 基本用法2.3 处理无限序列2.4 实际应用三、`__iter__` 与 `islice` 的结合六、为什么需要它们…

使用VSCode编写C#程序

目录 一、环境搭建&#xff1a;构建高效开发基础1. 安装VSCode2. 配置.NET SDK3. 安装核心扩展 二、项目开发全流程1. 创建项目2. 代码编辑技巧3. 调试配置4. 高级调试技巧5. 编译与运行 三、常见问题解决指南1. 项目加载失败2. IntelliSense失效3. 代码格式化4. 典型编译错误&…

日本汽车规模性经济计划失败,日产三大品牌的合并合作共赢,还是绝地求生?本田与日产合并确认失败,将成为世界第三大汽车集团愿景失败

本田与日产(含三菱汽车)的合并计划最终因核心矛盾无法调和而宣告失败,这一事件揭示了传统车企在行业变革期的深层困境。以下从合并动机、失败原因、本质判断及未来影响等方面综合分析: 一、合并的初衷:生存压力主导的被动策略 市场危机与财务困境 中国市场溃败:日系品牌在…

AutoCAD2026中文版下载安装教程

AutoCAD是一款由Autodesk公司开发的计算机辅助设计软件&#xff0c;被广泛应用于建筑设计、机械设计、电气设计、土木工程、装饰装潢等多个领域。AutoCAD2026中文版在原有的基础上进行了多项改进和优化&#xff0c;为用户提供了更为高效、便捷的绘图和设计体验。这里我给大家分…

Latex语法入门之数学公式

Latex是一种高质量的排版系统&#xff0c;尤其擅长于数学公式的排版。本文我将带大家深入了解Latex在数学公式排版中的应用。从基础的数学符号到复杂的公式布局&#xff0c;我们都会一一讲解&#xff0c;通过本文的学习&#xff0c;你将能够轻松编写出清晰、美观的数学公式&…

洛谷 P3214 [HNOI2011] 卡农

题目传送门 前言 再次败在 d p dp dp 手下&#xff0c;但是数据范围这么小应该是可以看出是 d p dp dp 的&#xff08;毕竟对于其他组合数的问题数据范围都是 1 0 9 10^9 109 起步&#xff09;。 思路 题意简化 现有 1 , 2 , 3 , . . . , n − 1 , n 1, 2, 3, ... , n -…

【年份数据类型及使用】

在数据分析中,年份的处理需要根据具体场景选择合适的数据类型,以确保后续分析的准确性和效率。以下是常见的年份数据类型及使用场景: 1. 数值类型(整数或浮点数) 适用场景: 仅需存储年份数值(如 2020, 2023),无需进行日期计算。需要将年份作为连续变量参与数学运算(如…

详解七大排序

目录 一.直接插入排序 &#xff08;1&#xff09;基本思想 &#xff08;2&#xff09;算法步骤 &#xff08;3&#xff09;代码实现 &#xff08;4&#xff09;算法特性 &#xff08;5&#xff09;算法优化 &#xff08;6&#xff09;示例演示 二.希尔排序 &#xff08…

YOLOv12 训练从这里开始:LabelImg 标注数据集

视频讲解&#xff1a; YOLOv12 训练从这里开始&#xff1a;LabelImg 标注数据集 labelimg https://github.com/tzutalin/labelImg sudo apt-get install pyqt5-dev-tools pip3 install lxml git clone https://github.com/tzutalin/labelImg.git cd labelImg 开始编译 make…

Day2:前端项目uniapp壁纸实战

先来做一个轮番图。 效果如下&#xff1a; common-style.css view,swiper,swiper-item{box-sizing: border-box; } index.vue <template><view class"homeLayout"><view class"banner"><swiper circular indicator-dots autoplay…

SAP-ABAP:ABAP `LEAVE LIST-PROCESSING` 深度解析

ABAP LEAVE LIST-PROCESSING 深度解析 核心机制 模式切换(Dialog → List) 中断屏幕流 强制终止当前Dialog程序的PBO/PAI处理,脱离屏幕序列控制(如事务码SE38执行的程序)。触发报表事件 激活类报表程序的事件链:INITIALIZATION → AT SELECTION-SCREEN → START-OF-SEL…

在PyTorch中使用GPU加速:从基础操作到模型部署

本文将通过具体代码示例&#xff0c;详细介绍如何在PyTorch中利用GPU进行张量计算和模型训练&#xff0c;包含设备查询、数据迁移以及模型部署等完整流程。 1. 查看GPU硬件信息 使用 nvidia-smi 命令检查GPU状态和进程信息&#xff1a; # 查看GPU信息 !nvidia-smi 输出示例&…

lib-zo,C语言另一个协程库,dns协程化, gethostbyname

lib-zo,C语言另一个协程库,dns协程化, gethostbyname 另一个 C 协程库 https://blog.csdn.net/eli960/article/details/146802313 本协程库 支持 DNS查询 协程化. 禁用所有 UDP 协程化 zvar_coroutine_disable_udp 1;禁用 53 端口的UDP 协程化 zvar_coroutine_disable_ud…

Java第三节:新手如何用idea创建java项目

作者往期文章&#xff1a; Java第一节&#xff1a;debug如何调试程序&#xff08;附带源代码&#xff09;-CSDN博客 Java第二节&#xff1a;debug如何调试栈帧链&#xff08;附带源代码&#xff09;-CSDN博客 步骤一 ​ 步骤二 ​ 步骤三 创建src文件夹包含main文件&#…

(一)从零开始:用 LangChain 和 ZhipuAI 搭建简单对话

最近一直在研究如何用 LangChain 和 ZhipuAI 搭建一个智能对话系统&#xff0c;发现这个组合真的非常强大&#xff0c;而且实现起来并不复杂。今天就来分享一下我的学习过程和一些心得体会&#xff0c;希望能帮到同样在探索这个领域的小伙伴们。 一、 环境搭建&#xff1a;从零…

uniapp地图导航及后台百度地图回显(v2/v3版本)

百度地图申请地址&#xff1a;控制台 | 百度地图开放平台 效果&#xff1a; 1.后台 1.1申请百度地图APi 1.2 引入百度地图 <script type"text/javascript" src"//api.map.baidu.com/api?v3.0&ak自己百度生气apikey"></script> 1.3 v2组…

Java模板方法模式详解

模板方法模式详解 一、模式定义 模板方法模式(Template Method Pattern)定义一个操作中的算法骨架&#xff0c;将某些步骤延迟到子类实现。 二、核心结构 1. 抽象模板类 public abstract class AbstractTemplate {// 模板方法&#xff08;final防止子类覆盖&#xff09;pu…

(5)模拟后——Leonardo的可视化操作

1 引言 经过n天的模拟&#xff0c;模拟结果相信已经到手&#xff0c;但如何进行分析呢。 首先是可视化&#xff0c;可视化方法基本分为两类 基于ENVI-met自带软件Leonardo的可视化操作基于NetCDF的可视化操作 2 模拟结果变量说明 首先&#xff0c;模拟结果会有以下几个文件…

基于YOLO11实例分割与奥比中光相机的快递包裹抓取点检测

本博客来源于CSDN机器鱼&#xff0c;未同意任何人转载。 更多内容&#xff0c;欢迎点击本专栏&#xff0c;查看更多内容。 0 引言 项目采用六轴机械臂搭配末端真空吸盘&#xff0c;从无序包裹中抓取想要的包裹。AI算法需要提供各包裹的抓取点的3D坐标与3D姿态。由于快递包裹含…

【学Rust写CAD】31 muldiv255函数(muldiv255.rs)

源码 // Calculates floor(a*b/255 0.5) #[inline] pub fn muldiv255(a: u32, b: u32) -> u32 {// The deriviation for this formula can be// found in "Three Wrongs Make a Right" by Jim Blinn.let tmp a * b 128;(tmp (tmp >> 8)) >> 8 }代…