为什么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)