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.pswp.cn/bicheng/75781.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/75781.shtml
英文地址,請注明出處:http://en.pswp.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 }代…