解決pyinstaller打包的程序無法執行,提示Operation not permitted

前言

PyInstaller是一個強大的工具,它可以分析我們的python腳本,發現腳本執行所依賴的模塊,並將他們打包到一個文件夾,或者封裝成一個可執行文件(exe或者binary)。然後,我們就可以將這個文件(文件夾)放到其他機器上去執行,目標機器甚至可以不用安裝python環境!這跟docker有異曲同工之妙。

問題描述

Centos7上,用PyInstallerpy文件打成一個binary,把該可執行文件放到其他的Centos7上去執行,結果報如下的錯誤:

error while loading shared libraries: libz.so.1: failed to map segment from shared object: Operation not permitted

問題定位

一般Operation not permitted都是權限不足導致的,但是看了一下文件,是有執行(x)的權限的。
把這個文件放到其他的機器上去執行,發現有的機器可以跑,有的機器不能跑!所以肯定是機器配置上的差異。

回到PyInstaller官網,看到如下介紹:

The bootloader is the heart of the one-file bundle also. When started it creates a temporary folder in the appropriate temp-folder location for this OS. The folder is named _MEIxxxxxx, where xxxxxx is a random number.
In GNU/Linux and related systems, it is possible to mount the /tmp folder with a “no-execution” option. That option is not compatible with a PyInstaller one-file bundle. It needs to execute code out of /tmp.

可見,可執行文件在執行的時候,會去/tmp目錄下創建一個臨時文件夾_MEIxxxxxx,裏面包含了運行時需要的類庫。當程序執行完畢,臨時文件夾會自動被刪除。
所以,我們必須保證對/tmp目錄需要有可執行的權限。於是用mount命令查看了一下有問題的機器:

[root@localhost ~]# mount | grep noexec
/dev/sda1 on /boot type ext4 (rw,noexec,nosuid,nodev)
/dev/sda5 on /tmp type ext4 (rw,noexec,nosuid,nodev)

果不其然,/tmp目錄被打上了noexec的flag。這個flag的意思是,在此目錄下的所有文件都不能被執行。
一般/tmp目錄的權限是很大的,都是777,所以通常是很多黑客或者是惡意程序的攻擊對象。如果將/tmp目錄以noexec的形式來mount,可以很好的保護計算機。

問題解決

究其根源,是/tmp目錄缺少可執行權限,導致程序運行所需的lib庫沒法生成。這裏有兩個解決方案

  1. 用如下命令,將/tmp目錄的noexec改成exec:

mount -o remount,exec /tmp

  1. 如果/tmp目錄確實需要用noexec加以保護,那可以在PyInstaller打包的時候,加上–runtime-tmpdir參數,用以顯示指定臨時lib庫存放的目錄。這樣的話,程序就不會用/tmp目錄而改用顯示指定的目錄。

總結

當運行程序報Operation not permitted錯誤時,不僅需要考慮程序本身是否有可執行權限,還要查看看文件系統是否以noexec形式mount了。該問題不僅出現在PyInstaller中,在docker中也很常見。

參考

PyInstaller
fstab

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章