为什么GCC根据文件创建共享对象而不是可执行二进制文件?

为什么GCC根据文件创建共享对象而不是可执行二进制文件?,c,gcc,shared-libraries,static-libraries,position-independent-code,C,Gcc,Shared Libraries,Static Libraries,Position Independent Code,我正在建一个图书馆。运行以下任一操作时,我的所有对象都会依次编译和链接: ar rcs lib/libryftts.a$^ gcc-shared$^-o lib/libryftts.so 在我的Makefile中。我还能够成功地将它们安装到/usr/local/lib 当我用nm测试文件时,所有函数都在那里。 我的问题是,当我运行gcc testing/test.c-lryftts-o test&file./test或gcc testing/test.c lib/libryftts.a-o te

我正在建一个图书馆。运行以下任一操作时,我的所有对象都会依次编译和链接:
ar rcs lib/libryftts.a$^

gcc-shared$^-o lib/libryftts.so

在我的Makefile中。我还能够成功地将它们安装到
/usr/local/lib
当我用nm测试文件时,所有函数都在那里。 我的问题是,当我运行
gcc testing/test.c-lryftts-o test&file./test
gcc testing/test.c lib/libryftts.a-o test&file./test
它说:

test:ELF 64位LSB共享对象
而不是我所期望的
test:ELF 64位LSB可执行文件
。我做错了什么

我做错了什么

没什么

听起来您的GCC默认配置为生成
-pie
二进制文件。这些二进制文件实际上是共享库(类型为
ET\u DYN
),但它们的运行方式与普通可执行文件类似

因此,您应该只运行二进制文件,并且(如果它工作的话)不必担心它


或者您可以将二进制文件链接到
gcc-no pie…
,这应该会生成一个类型为
ET_EXEC
的非
pie
可执行文件,其中
文件
会显示
ELF 64位LSB可执行文件
文件
5.36清楚地显示了这一点

文件
5.36如果可执行文件是饼图或不是饼图,实际上会清晰地打印出来,如所示:

例如,饼图可执行文件显示为:

main.out:ELF 64位LSB pie可执行文件,x86-64,版本1(SYSV),动态链接,未剥离

而非饼状图则是:

main.out:ELF 64位LSB可执行文件,x86-64,版本1(SYSV),静态链接,未剥离

该功能是在5.33中引入的,但它只做了一个简单的
chmod+x
检查。在此之前,它只是为饼图打印
共享对象

在5.34中,它打算开始检查更专业的
DF_1_PIE
ELF元数据,但由于提交时实现中的错误,它实际上破坏了一切,并将GCC PIE可执行文件显示为
共享对象

该错误在提交时在5.36中修复

这个bug尤其出现在Ubuntu 18.10中,它有
文件
5.34

当将程序集代码与
ld-pie
链接时,由于巧合,它不会自行显示

源代码分解如本答案的“
文件
5.36源代码分析”部分所示

Linux内核5.0根据
ET\u DYN

文件
“混乱”的根本原因是,和共享库都是位置独立的,可以放置在随机内存位置

在内核中,仅接受这两种类型的ELF文件:

/* First of all, some simple consistency checks */
if (interp_elf_ex->e_type != ET_EXEC &&
        interp_elf_ex->e_type != ET_DYN)
        goto out;
然后,只有对于
ET_DYN
它才会将
load_bias
设置为非零的值。然后,
加载偏差
决定ELF偏移:

我在以下地点证实了这一点:


文件
5.36行为分类

在研究了
文件
如何从源代码开始工作之后。我们将得出结论:

  • 如果
    Elf32\u Ehdr.e\u type==ET\u EXEC
    • 打印
      可执行文件
  • else如果
    Elf32\u Ehdr.e\u type==ET\u DYN
    • 如果存在
      DT\u标志\u 1
      动态区段条目
      • 如果在
        DT\u FLAGS\u 1
        中设置了
        DF\u 1\u PIE
        • 打印
          pie可执行文件
      • 否则
        • 打印
          共享对象
    • 否则
      • 如果文件可由用户、组或其他人执行
        • 打印
          pie可执行文件
      • 否则
        • 打印
          共享对象
以下是一些实验,证实了:

Executable generation        ELF type  DT_FLAGS_1  DF_1_PIE  chdmod +x      file 5.36
---------------------------  --------  ----------  --------  -------------- --------------
gcc -fpie -pie               ET_DYN    y           y         y              pie executable
gcc -fno-pie -no-pie         ET_EXEC   n           n         y              executable
gcc -shared                  ET_DYN    n           n         y              pie executable
gcc -shared                  ET_DYN    n           n         n              shared object
ld                           ET_EXEC   n           n         y              executable
ld -pie --dynamic-linker     ET_DYN    y           y         y              pie executable
ld -pie --no-dynamic-linker  ET_DYN    y           y         y              pie executable
在Ubuntu 18.10、GCC 8.2.0、Binutils 2.31.1中测试

每种试验类型的完整试验示例如下所述:

  • gcc-饼状图
    gcc-无饼状图

    请记住,自Ubuntu 17.10以来,
    -pie
    默认设置为打开,相关:

  • gcc-shared
    .so
    共享库):

  • ld
    实验:
ELF类型
DF_1_PIE
分别由以下参数确定:

readelf --file-header main.out | grep Type
readelf --dynamic     main.out | grep FLAGS_1

文件
5.36源代码分析

要分析的关键文件是

这种神奇的格式仅根据固定位置的字节值确定文件类型

格式本身记录在:

man 5 magic
因此,此时您需要准备以下文档:

  • ELF标题部分的ELF标准
  • 我的ELF文件格式介绍和分解
在文件末尾,我们看到:

0       string          \177ELF         ELF
!:strength *2
>4      byte            0               invalid class
>4      byte            1               32-bit
>4      byte            2               64-bit
>5      byte            0               invalid byte order
>5      byte            1               LSB
>>0     use             elf-le
>5      byte            2               MSB
>>0     use             \^elf-le
\177ELF
是每个ELF文件开头的4个魔法字节
\177
0x7F
的八进制

然后,通过与标准中的
Elf32_Ehdr
struct进行比较,我们发现字节4(第5个字节,魔法标识符后的第一个字节)决定了ELF类:

e_ident[EI_CLASSELFCLASS]
它的一些可能值是:

ELFCLASS32 1
ELFCLASS64 2
ET_EXEC 2
ET_DYN  3
文件
源代码中,我们有:

1 32-bit
2 64-bit
32位
64位
file
输出到标准输出的字符串

因此,现在我们在该文件中搜索
共享对象
,结果是:

0       name            elf-le
>16     leshort         0               no file type,
!:mime  application/octet-stream
>16     leshort         1               relocatable,
!:mime  application/x-object
>16     leshort         2               executable,
!:mime  application/x-executable
>16     leshort         3               ${x?pie executable:shared object},
因此,这个
elf-le
是某种标识符,包含在代码的前一部分中

字节16正是ELF类型:

Elf32_Ehdr.e_type
它的一些价值观是:

ELFCLASS32 1
ELFCLASS64 2
ET_EXEC 2
ET_DYN  3
因此,
ET_EXEC
总是作为
可执行文件打印出来

ET_DYN
但是,根据
${x的不同,有两种可能性
case DT_FLAGS_1:
        if (xdh_val & DF_1_PIE)
                ms->mode |= 0111;
        else
                ms->mode &= ~0111;
        break;
    if (xdh_val == DF_1_PIE)