动态库加载出错,cannot restore segment prot after reloc: Permission denied以及为何编译动态库需要-fPIC

在将编译好的SO文件部署到其他机器上时遇到'cannot restore segment prot after reloc: Permission denied'错误,该问题通常由SELinux严格配置引起。解决方案包括修改/etc/selinux/config文件或使用/usr/sbin/setenforce 0临时关闭SELinux。此外,可使用ldd -z ignore缺失权限的SO文件。本文还提及-fPIC编译选项在生成动态库时的重要性。

项目中碰到的问题,编译好的so文件,放到其他机器上去加载,报了错误,cannot restore segment prot after reloc: Permission denied

网上查了一下,原因是selinux的问题,selinux的设置太过严格导致的。解决的办法是在root用户下,修改/etc/selinux/config 文件,

把SELINUX=enforcing 改成 SELINUX=disabled。

然后,保存关闭,重启机器就可以了。

另外还有一个暂时关闭的方法,就是 /usr/sbin/setenforce 0 。

此外呢,我自己在犄角旮旯里发现了另外一个方法,举个例子,你碰到问题的so文件是test.so,

那么

chcon -t texrel_shlib_t test.so 

就可以了。不过呢,这个命令只能修改一个so,如果想让你的系统以后都不会碰到这个错误,那最好还是采用修改文件

的方式。

如果你只是来看怎么解决这个问题的,那下面的就不用看了,下面是说gcc的编译命令-fPIC的作用。源自点击打开链接。记住,-fPIC是编译命令,-shared是链接命令。

编译GNU/Linux共享库, 为什么要用PIC编译?

       一直以为不管是编译共享库还是静态库,中间生成的目标文件(.o文件)是没有区别的,区别只在:最后是用-shared编译还是用ar打包; 可是事情的真相并不是这样的:

from 《 Binary Hacks:黑客秘笈100选

本hack中, 我们来研究编译共享库时,为什么要用PIC(选项)编译?

通常编译GNU/Linux共享库时,把各个.c文件编译编译成PIC(Position Independent Code, 位置无关代码)。但是,实际上不用PIC编译的话也可以编译共享库。那么使用PIC还有意义吗?

让我们来进行一个实验:
    #include <stdio.h>
    void func() {
        printf(" ");
        printf(" ");
        printf(" ");
    }

用 PIC编译必须把参数-fpic或-fPIC传给gcc,-fpic可以生成小而高效的代码,但是不同的处理器中-fpic生成的GOT(Global Offset Table, 全局偏移表)的大小有限制,另一方面,使用-fPIC的话,任何处理器都可以放心使用。在这里,使用-fPIC。(在X86中使用-fpic和-fPIC 没有任何区别)。
    $ gcc -o fpic-no-pic.s -S fpic.c
    $ gcc -fPIC -o fpic-pic.s -S fpic.c

阅读上述生成的汇编代码,则可以知道PIC版本通过PLT(Procedure Linkage Table)调用printf。
    $ grep printf fpic-no-pic.s
             call printf
             call printf
             call printf
    $ grep printf fpic-pic.s
             call printf@PLT
             call printf@PLT
             call printf@PLT
下面,编译共享库
    $ gcc -shared -o fpic-no-pic.so fpic.c
    $ gcc -shared -fPIC -o fpic-pic.so fpic.c
这 些共享库的动态节(dynamic section)用readelf阅读的话,非PIC版本中有TEXTREL输入方法(需要在text内进行再配置),并且RELCOUNT(再配置的数 量)为5 -- 比PIC版本的多3个。多出三个是因为printf()的调用进行了3次。
    $ readelf -d fpic-no-pic.so | egrep 'TEXTREL|RELCOUNT'
     0x00000016 (TEXTREL)                  0x0
     0x6ffffffa (RELCOUNT)                 5
    $ readelf -d fpic-pic.so | egrep 'TEXTREL|RELCOUNT'
     0x6ffffffa (RELCOUNT)  2

PIC版本的RELCOUNT非0是由于gcc在缺省时使用的是包含在启动文件里的代码。若加-nostartfiles选项,则RELCOUNT值为0。

PIC和非PIC共享库的性能对比
上面例子阐述了非PIC版本运行时(动态运行时)需要5个地址的再分配。那么,若在配置的数量大增时会出现什么样的情况呢?

运行下面的shell脚本,用非PIC版本和PIC版本编译含有1000万次printf()调用的共享库,和相应的可执行文件fpic-no-pic和fpic-pic。
  #! /bin/sh
    rm -f *.o *.so
    num=1000
    for i in `seq $num`; do
        echo -e "#include <stdio.h>\nvoid func$i() {" >fpic$i.c
        #ruby -e "10000.times { puts 'printf(\" \");' }" >>fpic$i.c
        perl -e 'print("printf(\" \");\n"x10000);' >>fpic$i.c
        echo "}" >> fpic$i.c
        gcc -o fpic-no-pic$i.o -c fpic$i.c
        gcc -o fpic-pic$i.o -fPIC -c fpic$i.c
    done
    gcc -o fpic-no-pic.so -shared fpic-no-pic*.o
    gcc -o fpic-pic.so -shared fpic-pic*.o

    echo "int main() { return 0; }" >fpic-main.c
    gcc -o no-pic-load fpic-main.c ./fpic-no-pic.so
    gcc -o pic-load fpic-main.c ./fpic-pic.so

    echo "int main() {" >main.c
    for i in `seq $num`; do echo "func$i();"; done >>main.c
    echo "}" >>main.c
    gcc -o fpic-no-pic main.c ./fpic-no-pic.so
    gcc -o fpic-pic main.c ./fpic-pic.so

两个版本程序,运行结果如下: 非PIC版本首次运行时间2.15秒,第二次以后大约0.55秒,而PIC版本的首次用了0.02秒,第二次以后用了0.00秒。

    $ repeat 3 time ./no-pic-load
    2.15s total : 0.29s user 0.48s system 35% cpu
    0.56s total : 0.25s user 0.31s system 99% cpu
    0.55s total : 0.30s user 0.25s system 99% cpu
    $ repeat 3 time ./pic-load
    0.02s total : 0.00s user 0.00s system 0% cpu
    0.00s total : 0.00s user 0.01s system 317% cpu
    0.00s total : 0.00s user 0.00s system 0% cpu

main() 本身是空的,可以知道非PIC版本动态链接时的再分配需要2.15~0.55秒。运行环境Xeon-2.8GHz+Debian GNU/Linux sarge+GCC 3.3.5。

非PIC版本的缺点不仅是在运行时再配置上花时间。为了更新再配置部分的代码,将会发生这样的情况(下载text segment里需要再配置的页->更新->进行copy on write -> 不能与其他路径和text共享)。

另外比较非PIC版本的fpic-no-pic.so和PIC版本的fpic-pic.so的大小,前者268M,后者134M,差别很明显。用readelf -S查看节头,会有以下区别:
                 .rel.dyn      .text
非PIC            152MB         114MB
PIC              0MB           133MB

非PIC版本的代码(.text)比PIC版本的小,但再配置所需要的信息占很大的空间。

tall -d "/opt/brcm/hndtools-mipsel-uclibc-0.9.19"/include install -m 644 lib/*.[ao] "/opt/brcm/hndtools-mipsel-uclibc-0.9.19"/lib/ install: cannot remove '/opt/brcm/hndtools-mipsel-uclibc-0.9.19/lib/crt0.o': Permission denied install: cannot remove '/opt/brcm/hndtools-mipsel-uclibc-0.9.19/lib/crt1.o': Permission denied install: cannot remove '/opt/brcm/hndtools-mipsel-uclibc-0.9.19/lib/crti.o': Permission denied install: cannot remove '/opt/brcm/hndtools-mipsel-uclibc-0.9.19/lib/crtn.o': Permission denied install: cannot remove '/opt/brcm/hndtools-mipsel-uclibc-0.9.19/lib/libc.a': Permission denied install: cannot remove '/opt/brcm/hndtools-mipsel-uclibc-0.9.19/lib/libcrypt.a': Permission denied install: cannot remove '/opt/brcm/hndtools-mipsel-uclibc-0.9.19/lib/libdl.a': Permission denied install: cannot remove '/opt/brcm/hndtools-mipsel-uclibc-0.9.19/lib/libm.a': Permission denied install: cannot remove '/opt/brcm/hndtools-mipsel-uclibc-0.9.19/lib/libnsl.a': Permission denied install: cannot remove '/opt/brcm/hndtools-mipsel-uclibc-0.9.19/lib/libpthread.a': Permission denied install: cannot remove '/opt/brcm/hndtools-mipsel-uclibc-0.9.19/lib/libresolv.a': Permission denied install: cannot remove '/opt/brcm/hndtools-mipsel-uclibc-0.9.19/lib/libutil.a': Permission denied Makefile:199: recipe for target 'install_dev' failed make[2]: [install_dev] Error 1 (ignored) tar -chf - include | tar -xf - -C "/opt/brcm/hndtools-mipsel-uclibc-0.9.19"; tar: include/sysexits.h: Cannot open: File exists tar: include/string.h: Cannot open: File exists tar: include/math.h: Cannot open: File exists tar: include/link.h: Cannot open: File exists tar: include/malloc.h: Cannot open: File exists tar: include/utmp.h: Cannot open: File exists tar: include/time.h: Cannot open: File exists tar: include/semaphore.h: Cannot open: File exists tar: include/unistd.h: Cannot open: File exists tar: include/lastlog.h: Cannot open: File exists tar: include/iconv.h: Cannot open: File exists tar: include/complex.h: Cannot open: File exists tar: include/assert.h: Cannot open: File exists tar: include/netpacket/packet.h: Cannot open: File exists tar: include/netpacket: Cannot utime: Operation not permitted tar: include/netpacket: Cannot change mode to rwxrwxr-x: Operation not permitted tar: include/limits.h: Cannot open: File exists tar: include/features.h: Cannot open: File exists tar: include/netipx/ipx.h: Cannot open: File exists tar: include/netipx: Cannot utime: Operation not permitted tar: include/netipx: Cannot change mode to rwxrwxr-x: Operation not permitted tar: include/locale.h: Cannot open: File exists tar: include/thread_db.h: Cannot open: File exists tar: include/protocols/routed.h: Cannot open: File exists 这个报错怎么解决
最新发布
12-13
<think>我们面对两个主要问题:1) `install`命令因权限不足无法删除旧文件;2) `tar`命令因权限问题无法修改文件属性和时间戳。以下分步骤解决: ### 一、解决`install`命令的`cannot remove`错误 **错误示例**: ``` install: cannot remove 'old_file': Permission denied ``` #### 解决方案: 1. **提升权限执行**(适用于系统目录): ```bash sudo install -c new_file /path/to/target ``` - 使用`-o`指定所有者: ```bash sudo install -o $USER -c new_file /path/to/target ``` 2. **强制覆盖已存在文件**: ```bash install -c --backup=numbered new_file existing_file # 备份原文件 ``` 3. **修复目录所有权**(关键): ```bash # 查看目标目录权限 ls -ld /path/to/target # 递归修改为当前用户所有 sudo chown -R $USER:$USER /path/to/target ``` ### 二、解决`tar`命令的权限错误 **错误示例**: ``` tar: include/bits: Cannot utime: Operation not permitted tar: include/bits: Cannot change mode to rwxrwxr-x: Operation not permitted ``` #### 解决方案: 1. **避免在FAT/NTFS分区操作**(常见于Windows共享目录): ```bash df -T . # 查看当前文件系统 ``` - 若为`vboxsf`/`fuseblk`,迁移至Linux原生分区: ```bash cp -a project /tmp/build && cd /tmp/build ``` 2. **使用兼容性参数解压**: ```bash tar xvf archive.tar.gz \ --no-same-owner \ # 忽略原始属主 --no-same-permissions \ # 忽略原始权限 --delay-directory-restore # 延迟目录权限设置 ``` 3. **手动修复权限**: ```bash find . -type d -exec chmod 755 {} \; # 目录设为755 find . -type f -exec chmod 644 {} \; # 文件设为644 ``` ### 三、综合处理流程 ```mermaid graph TD A[遇到权限错误] --> B{错误类型} B -->|install cannot remove| C[提升权限+修复目录所有权] B -->|tar cannot utime| D[迁移至ext4分区+兼容解压] C --> E[验证:ls -l 目标文件] D --> F[验证:tar --test 检查压缩包] ``` ### 四、高级场景:修复被锁定的文件系统 当遇到只读文件系统时: 1. 检查挂载状态: ```bash mount | grep ' /path ' ``` 2. 重新挂载为读写: ```bash sudo mount -o remount,rw /path ``` ### 五、预防措施 1. **构建环境隔离**: ```bash # 创建专用构建目录 mkdir ~/build && chmod 700 ~/build export BUILD_ROOT=~/build ``` 2. **使用容器编译**: ```dockerfile FROM alpine RUN apk add build-base COPY src /src RUN make -C /src ``` > **关键原则**:始终在Linux原生文件系统(ext4/xfs)中进行开发操作,避免权限元数据丢失[^1]。 §§相关问题§§ 1. 如何在Docker容器中正确挂载宿主目录以避免权限冲突? 2. 当`chown`命令提示"Operation not permitted"时,如何排查SELinux或AppArmor策略? 3. 对于FAT32格式的U盘,如何安全地解压包含Linux权限信息的tar包? 4. 在持续集成(CI)环境中如何配置无特权的安全构建流程? </think> ### 解决 install 和 tar 命令的权限及文件冲突问题 #### 一、install 命令 "cannot remove" 错误处理 **错误原因**: 1. 目标文件/目录被锁定(其他进程占用)[^2] 2. 用户权限不足(需 root 权限) 3. 文件系统只读状态 **解决方案**: 1. **强制覆盖安装**: ```bash sudo install -f -o $USER -m 755 source_file /target/path ``` - `-f`:强制覆盖 - `-o $USER`:设置文件所有者 - `-m 755`:设置权限 2. **解除文件锁定**: ```bash # 查找占用进程 sudo lsof /target/path/file | grep DEL # 结束占用进程 sudo kill -9 $(lsof -t /target/path/file) ``` 3. **修复文件系统权限**: ```bash sudo chown -R $USER:$USER /target/path # 递归修改所有权 sudo chmod u+w /target/path # 添加写权限 ``` #### 二、tar 命令 "Cannot utime/change mode" 错误处理 **错误原因**: 1. 在FAT32/NTFS文件系统解压(不支持Linux权限) 2. 用户权限不足 3. 压缩包包含特殊权限文件 **解决方案**: 1. **使用兼容参数解压**: ```bash tar xvf archive.tar.gz \ --no-overwrite-dir \ # 不覆盖目录权限 --no-same-owner \ # 不保留原始所有者 --no-same-permissions # 不保留原始权限 ``` 2. **在Linux原生文件系统操作**: ```bash # 检查文件系统类型 df -T /path/to/extract # 若为vboxsf/ntfs,迁移到ext4分区 sudo mv archive /home && cd /home ``` 3. **手动修复权限**: ```bash # 解压后统一修复权限 find extracted_dir -type d -exec chmod 755 {} \; find extracted_dir -type f -exec chmod 644 {} \; ``` #### 三、综合解决方案(推荐) ```mermaid graph LR A[遇到权限错误] --> B{错误类型} B -->|install cannot remove| C[结束占用进程+强制安装] B -->|tar cannot utime| D[迁移到ext4+兼容解压] C --> E[验证:ls -l 目标文件] D --> F[验证:tar tvf 检查压缩包] ``` **完整修复流程**: 1. 迁移工作目录到Linux原生分区: ```bash sudo mkdir /build && sudo chown $USER:$USER /build mv project /build && cd /build/project ``` 2. 安全解压文件: ```bash tar xvf archive.tar.gz \ --no-overwrite-dir \ --no-same-owner \ --no-same-permissions ``` 3. 安装前清理环境: ```bash make distclean # 清理构建 sudo rm -f /target/path/conflicting_file # 移除冲突文件 ``` 4. 带权限安装: ```bash sudo install -o $USER -m 755 -v src_file /target/path ``` #### 四、高级场景处理 **只读文件系统问题**: ```bash # 重新挂载为读写模式 sudo mount -o remount,rw /target ``` **SELinux/AppArmor拦截**: ```bash # 临时禁用 sudo setenforce 0 # 或添加规则 sudo ausearch -c 'install' --raw | audit2allow -M mypolicy sudo semodule -i mypolicy.pp ``` > **关键原则**:始终在Linux原生文件系统(ext4/xfs)中处理含权限操作,避免跨文件系统权限丢失[^1]。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值