我有一个正在建设的图书馆。当我运行以下任何一个时,我的所有对象都会连续编译和链接:
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-bit LSB shared object
而不是test: ELF 64-bit LSB executable
。我做错了什么?
答案 0 :(得分:27)
我做错了什么?
没有
听起来您的GCC默认配置为构建-pie
二进制文件。这些二进制文件确实是共享库(类型为ET_DYN
),除非它们像普通的可执行文件一样运行。
所以你应该运行你的二进制文件,并且(如果它有效)不要担心它。
或者您可以将二进制文件与gcc -no-pie ...
相关联,这样就可以生成类型为PIE
的非ET_EXEC
可执行文件,file
将为其ELF 64-bit LSB executable
答案 1 :(得分:3)
file
5.36清楚地说
file
5.36实际上可以清晰地打印出可执行文件是否为PIE,如下所示:https://unix.stackexchange.com/questions/89211/how-to-test-whether-a-linux-binary-was-compiled-as-position-independent-code/435038#435038
例如,PIE可执行文件显示为:
main.out: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, not stripped
和一个非PIE:
main.out: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, not stripped
此功能是在5.33中引入的,但它仅进行了简单的chmod +x
检查。在此之前,它只是为PIE打印shared object
。
在5.34版中,它打算开始检查更专业的DF_1_PIE
ELF元数据,但是由于在提交9109a696f3289ba00eaa222fd432755ec4287e28时实现中存在错误,它实际上使事情破败,并且将GCC PIE可执行文件显示为{{ 1}}。
该错误已在5.36版中的提交03084b161cf888b5286dbbcd964c31ccad4f64d9中修复。
该错误特别存在于具有shared objects
5.34的Ubuntu 18.10中。
由于巧合,在将汇编代码与file
链接时,它不会显现出来。
此答案的“ ld -pie
5.36源代码分析”部分显示了源代码细分。
Linux内核5.0根据file
ET_DYN
“混乱”的根本原因在于,PIE executables和共享库都是位置无关的,可以放置在随机存储位置。
在fs/binfmt_elf.c,内核仅接受这两种类型的ELF文件:
file
然后,仅将/* First of all, some simple consistency checks */
if (interp_elf_ex->e_type != ET_EXEC &&
interp_elf_ex->e_type != ET_DYN)
goto out;
的{{1}}设置为非零值。然后ET_DYN
决定ELF偏移量:How is the address of the text section of a PIE executable determined in Linux?
load_bias
我在What is the -fPIE option for position-independent executables in gcc and ld?
上通过实验进行了确认 load_bias
5.36行为细分
在研究/*
* If we are loading ET_EXEC or we have already performed
* the ET_DYN load_addr calculations, proceed normally.
*/
if (loc->elf_ex.e_type == ET_EXEC || load_addr_set) {
elf_flags |= elf_fixed;
} else if (loc->elf_ex.e_type == ET_DYN) {
/*
* This logic is run once for the first LOAD Program
* Header for ET_DYN binaries to calculate the
* randomization (load_bias) for all the LOAD
* Program Headers, and to calculate the entire
* size of the ELF mapping (total_size). (Note that
* load_addr_set is set to true later once the
* initial mapping is performed.)
*
* There are effectively two types of ET_DYN
* binaries: programs (i.e. PIE: ET_DYN with INTERP)
* and loaders (ET_DYN without INTERP, since they
* _are_ the ELF interpreter). The loaders must
* be loaded away from programs since the program
* may otherwise collide with the loader (especially
* for ET_EXEC which does not have a randomized
* position). For example to handle invocations of
* "./ld.so someprog" to test out a new version of
* the loader, the subsequent program that the
* loader loads must avoid the loader itself, so
* they cannot share the same load range. Sufficient
* room for the brk must be allocated with the
* loader as well, since brk must be available with
* the loader.
*
* Therefore, programs are loaded offset from
* ELF_ET_DYN_BASE and loaders are loaded into the
* independently randomized mmap region (0 load_bias
* without MAP_FIXED).
*/
if (elf_interpreter) {
load_bias = ELF_ET_DYN_BASE;
if (current->flags & PF_RANDOMIZE)
load_bias += arch_mmap_rnd();
elf_flags |= elf_fixed;
} else
load_bias = 0;
的来源之后。我们将得出以下结论:
file
file
Elf32_Ehdr.e_type == ET_EXEC
executable
个动态部分,
Elf32_Ehdr.e_type == ET_DYN
中设置了DT_FLAGS_1
:
DF_1_PIE
DT_FLAGS_1
pie executable
shared object
这是一些证实这一点的实验:
pie executable
在Ubuntu 18.10,GCC 8.2.0,Binutils 2.31.1中进行了测试。
每种实验的完整测试示例在以下网址中描述:
shared object
和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
:What is the -fPIE option for position-independent executables in gcc and ld?
请记住,自Ubuntu 17.10起,gcc -pie
默认情况下处于启用状态,相关内容为:32-bit absolute addresses no longer allowed in x86-64 Linux?
gcc -no-pie
(-pie
共享库):https://github.com/cirosantilli/cpp-cheat/tree/b80ccb4a842db52d719a16d3716b02b684ebbf11/shared_library/basic
gcc -shared
实验:How to create a statically linked position independent executable ELF in Linux? .so
和ld
分别通过以下方式确定:
ELF type
DF_1_PIE
5.36源代码分析
要分析的密钥文件是magic/Magdir/elf。
这种魔术格式仅取决于固定位置的字节值来确定文件类型。
该格式本身记录在:
readelf --file-header main.out | grep Type
readelf --dynamic main.out | grep FLAGS_1
因此,在这一点上,您将需要准备以下文件:
在文件末尾,我们看到:
file
man 5 magic
是每个ELF文件开头的4个魔术字节。 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
的八进制。
然后通过与标准中的\177
结构进行比较,我们看到字节4(第5个字节,魔术标识符之后的第一个字节)确定了ELF类:
0x7F
及其一些可能的值是:
Elf32_Ehdr
那么在e_ident[EI_CLASSELFCLASS]
源中,我们有:
ELFCLASS32 1
ELFCLASS64 2
和file
和1 32-bit
2 64-bit
是32-bit
输出到标准输出的字符串!
因此,现在我们在该文件中搜索64-bit
,并被引至:
file
因此,shared object
是某种标识符,已包含在代码的上半部分。
字节16正是ELF类型:
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
因此,Elf32_Ehdr.e_type
始终被打印为ET_EXEC 2
ET_DYN 3
。
ET_EXEC
但是有两种可能性取决于executable
:
ET_DYN
${x
pie executable
问:文件是否可以由用户,组或其他人执行?如果是,则显示shared object
,否则显示${x
。
此扩展是在src/softmagic.c
中的pie executable
函数中完成的:
shared object
但是,还有另外一种技巧!在src/readelf.c
函数varexpand
中,如果存在动态节(static int
varexpand(struct magic_set *ms, char *buf, size_t len, const char *str)
{
[...]
case 'x':
if (ms->mode & 0111) {
ptr = t;
l = et - t;
} else {
ptr = e;
l = ee - e;
}
break;
)的dodynamic
标志条目,则DT_FLAGS_1
中的权限将被存在覆盖或没有PT_DYNAMIC
标志:
st->mode
5.34中的错误是初始代码编写为:
DF_1_PIE
这意味着如果设置了另一个标志(默认情况下,由于case DT_FLAGS_1:
if (xdh_val & DF_1_PIE)
ms->mode |= 0111;
else
ms->mode &= ~0111;
break;
而GCC进行了设置),则可执行文件显示为 if (xdh_val == DF_1_PIE)
。
DF_1_NOW
标志条目在ELF标准中没有描述,因此它必须是Binutils扩展名。
该标志在Linux内核5.0或glibc 2.27中没有用,所以我似乎纯粹是在提供信息,以指示文件是否为PIE。