Qt 載入 libjpeg 庫出現“長跳轉已經執行”錯誤

sun發表於2023-04-21

繼上篇 Qt5.15.0 升級至 Qt5.15.9 遇到的一些錯誤

篇幅有點長,先說解決方法,在編譯靜態庫時加上 -qt-libjpeg,編譯出 libjpeg 庫後,在專案中使用 #pragma comment 連結即可


OK,開始回顧痛苦經歷

在我以為升級到 Qt5.15.9 後,執行沒有什麼問題時,問題就來了

在載入 jpeg 格式的圖片時,出現了“長跳轉已經執行”的錯誤

這個錯誤一般是由 setjmp/longjmp 函式觸發的,出現的可能的原因有以下幾種(聽聽 chatgpt 的回答)


longjmp 是 C 標準庫中的一個函式,用於跳轉到之前透過 setjmp 函式設定的跳轉點,並在跳轉時返回指定的錯誤碼。如果在呼叫 longjmp 函式時出現錯誤,通常會表現為程式異常終止、崩潰或死鎖等問題。

longjmp 函式可能出現錯誤的原因和解決方法如下:

  1. 呼叫 setjmp 的函式已經返回

使用 longjmp 函式必須要在之前呼叫過 setjmp 函式並且還沒有從 setjmp 函式返回時才能使用。如果在 setjmp 函式返回後再呼叫 longjmp 函式,就會導致未定義行為,可能會導致程式崩潰或死鎖。解決方法是檢查 longjmp 函式的呼叫時機,並確保它是在 setjmp 函式之後而且還沒有從 setjmp 函式返回時呼叫的。

  1. 跳轉目標不存在

當 longjmp 函式嘗試跳轉到之前設定的跳轉點時,如果該跳轉點已經不存在,就會導致錯誤。這種情況通常發生在跳轉點所在的函式已經返回或者被釋放時。解決方法是檢查跳轉點是否存在,並確保跳轉點所在的函式仍然有效。

  1. 跨執行緒呼叫

longjmp 函式通常不適用於多執行緒程式。如果在一個執行緒中設定了跳轉點,然後在另一個執行緒中呼叫 longjmp 函式來跳轉到該點,就會導致未定義行為。解決方法是使用執行緒安全的異常處理機制來代替 longjmp 和 setjmp 函式。

  1. 其他原因

除了上述幾種情況外,還有可能是其他一些原因導致 longjmp 錯誤。例如,記憶體洩漏、非法指標訪問、堆疊溢位等問題都可能導致 longjmp 函式失敗。解決方法是根據具體錯誤資訊進行排查和修復。


我這邊是觸發 ERREXIT2 後而跳出的錯誤

ERREXIT2 的原型如下

觸發 ERREXIT2,進入 my_error_exit 函式,然後呼叫 longjmp,最後程式異常崩潰

為何有這種醜陋的報錯呢?官方應該可以把這種報錯 catch 下來,而不是讓程式閃退,看看 llbjpeg-turbo 作者給出的解釋

原始連結:Possibility of non-unwinding error handling

簡單說來,ERREXIT 是官方的 libjpeg 遺留下來的,程式碼很古老,至今沒人修,而 libjpeg-turbo 只是包裝了 libjpeg 庫,這樣載入更快,對 libjpeg 中的 api 沒有任何改變,他也可以幫忙包裝下這個報錯,只是要加錢

話說回來,我為何會遇到 ERREXIT 呢?

那就不得不說 Qt 對 5.15 後續版本做出的一些改動了

見:https://doc.qt.io/qt-5/qtgui-attribution-libjpeg.html

就是說 libjpeg 要你自己去連結,我們不再幫你整合到 qjpeg.lib 中了,可能是協議問題

Independent JPEG Group License and BSD 3-Clause "New" or "Revised" License and zlib License.

 

既然問題找到了,那解決方法“應該”也能浮出水面了,對,打上雙引號的應該

事實上是我低估了這個問題,原本我以為加個 libjpeg-turbo 的庫之後就能萬事大吉時,結果往往給你一個嘴巴子

我用 vcpkg 包管理器新增了 libjpeg-turbo:x86-windows-static,程式編譯透過,也沒有出現 ERROR2019 的錯誤,但是使用 loadFromData 載入 jpeg 圖片資料還是會報錯

QImage photo;
photo.loadFromData(buffer.GetBuffer(), buffer.GetBufferLen()); // buffer 裡放置 jpeg 圖片資料

我第一反應是 libjpeg-turbo 的庫版本太高了,就查閱低版本的庫,想透過 vcpkg 新出的版本控制來實現的,奈何水平有限,沒弄出來,就去官網下載 2.1.3 的壓縮包自己編譯

之所以編譯 2.1.3 的包,是因為 Qt5.15.9 版本將 libjpeg-turbo 更新至 2.1.3

見:https://code.qt.io/cgit/qt/qtreleasenotes.git/about/qt/5.15.9/release-note.md

編譯出一個 lib 庫後,連結到程式中,還是會報錯,嗯,那先排除 libjpeg 版本問題

從堆疊下手吧,一層一層的剝開問題表皮,看本質

報錯是停在紅框中的 ERREXIT2 中,單步除錯後發現,qt 裡要求 libjpeg-turbo 的 version 為 80,而 vcpkg 提供的所有 libjpeg-turbo 版本都是 62,可以在 jconfig.h 中檢視 version

 

嗯,80 的為 qt 專屬,這就解釋了為啥觸發了 ERREXIT2 了,順便說一句,vcpkg 提供的庫其實就是官方的庫,libjpeg-turbo 不管是 2.1.5 還是 2.1.3,JPEG_LIB_VERSION 都是 62

因此我們只要編譯一個 libjpeg 的 qt 三方庫就行了

藉助這篇教程

使用 Qt Creator(我使用的 Qt Creator 10.0.0) 開啟 libjpeg.pro,再在 .pro 檔案裡改 lib 輸出路徑就行

 

順便貼上構建設定

可能需要將 jom.exe 改成 nmake.exe(開啟 pro 專案後,在構建和執行中選擇)

這些都準備後,點選編譯即可,在 lib 資料夾中就可以找到了

把庫放到專案檔案的庫目錄下,並靜態繫結即可

 


是不是很複雜,直到上一步我也是這麼以為的,在我全域性搜尋 qtlibjpeg.lib 時,我發現 qt 下已經給你編譯好了

驚不驚喜意不意外(我都要罵娘了)

重新閱讀了官方文件,上面說你可以選擇在編譯靜態庫時新增一些引數來一起編譯你需要的三方庫,比如 libjpeg

見:https://doc.qt.io/archives/qt-6.0/configure-options.html#third-party-libraries

是我大意了,附上編譯命令,

configure -static -static-runtime -debug-and-release -mp -prefix "..\msvc2019_x86_static" -opensource -confirm-license -optimize-size -qt-libjpeg -make libs -nomake examples -nomake tests -skip qtwebengine

編譯出的庫檔案就在 lib 下

小結:還是對 Qt 的庫配置不熟悉,導致花了大量工夫來解決這種問題;好在有了這次經驗後,以後再遇到類似問題,就能手到擒來了

 

相關文章