我有一个正在建设的图书馆.当我运行以下任何一个时,我的所有对象都会连续编译和链接:
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
像我期望的那样.我究竟做错了什么?
我究竟做错了什么?
没有.
听起来您的GCC -pie
默认配置为构建二进制文件.这些二进制文件实际上是共享库(类型ET_DYN
),除了它们像普通可执行文件一样运行.
所以你应该运行你的二进制文件,并且(如果它有效)不用担心它.
或者您可以链接您的二进制文件,gcc -no-pie ...
并且应该生成PIE
类型的不可执行文件ET_EXEC
,对此file
将说明ELF 64-bit LSB executable
.
file
5.36清楚地说
file
如果可执行文件是否为PIE,则5.36实际上会清晰地打印出来,如下所示:https : //unix.stackexchange.com/questions/89211/how-to-test-whether-a-linux-binary-was-compiled-as-位置无关代码/ 435038#435038
例如,PIE可执行文件显示为:
main.out:ELF 64位LSB Pie可执行文件,x86-64,版本1(SYSV),动态链接,未剥离
非PIE则为:
main.out:ELF 64位LSB可执行文件,x86-64,版本1(SYSV),静态链接,未剥离
该功能是在5.33中引入的,但仅进行了简单的chmod +x
检查。在此之前,它只是shared object
为PIE 打印。
在5.34中,本来打算开始检查更专业的DF_1_PIE
ELF元数据,但是由于在提交9109a696f3289ba00eaa222fd432755ec4287e28的实现中存在错误,它实际上使事情变得混乱,并将GCC PIE可执行文件显示为shared objects
。
该错误已在5.36 修订为03084b161cf888b5286dbbcd964c31ccad4f64d9。
该错误特别存在于具有file
5.34的Ubuntu 18.10中。
ld -pie
由于巧合,在链接汇编代码时它不会显示出来。
源代码细分显示在file
此答案的“ 5.36源代码分析”部分。
Linux内核5.0根据以下信息确定是否可以使用ASLR: ET_DYN
file
“混乱” 的根本原因是PIE可执行文件和共享库都是位置无关的,可以放置在随机存储位置中。
在fs / binfmt_elf.c,内核仅接受这两种类型的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;
然后,仅将for ET_DYN
设置load_bias
为不为零的值。该load_bias
是那么什么决定了ELF偏移:如何在Linux中确定的PIE可执行文件的文本部分的地址?
/* * 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;
我通过以下实验进行了确认:对于gcc和ld中与位置无关的可执行文件,-fPIE选项是什么?
file
5.36行为细分
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
其他
打印 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
已在Ubuntu 18.10,GCC 8.2.0,Binutils 2.31.1中进行了测试。
每种实验的完整测试示例在以下位置描述:
gcc -pie
和gcc -no-pie
:gcc和ld中与位置无关的可执行文件的-fPIE选项是什么?
请记住,-pie
自Ubuntu 17.10起,默认情况下已启用该功能,相关信息:x86-64 Linux中不再允许使用32位绝对地址?
gcc -shared
(.so
共享库):https : //github.com/cirosantilli/cpp-cheat/tree/b80ccb4a842db52d719a16d3716b02b684ebbf11/shared_library/basic
ld
实验:如何在Linux中创建静态链接的位置无关可执行ELF?
ELF type
和DF_1_PIE
分别通过以下方式确定:
readelf --file-header main.out | grep Type readelf --dynamic main.out | grep FLAGS_1
file
5.36源代码分析
要分析的密钥文件是magic / Magdir / elf。
这种不可思议的格式仅取决于固定位置的字节值来确定文件类型。
格式本身记录在:
man 5 magic
因此,在这一点上,您将需要准备以下文件:
ELF标头部分的http://www.sco.com/developers/devspecs/gabi41.pdf ELF标准
http://www.cirosantilli.com/elf-hello-world/#elf-header我的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
标准中的结构进行比较,我们看到字节4(第5个字节,魔术标识符之后的第一个字节)确定ELF类:
e_ident[EI_CLASSELFCLASS]
其一些可能的值为:
ELFCLASS32 1 ELFCLASS64 2
在file
源代码中,我们有:
1 32-bit 2 64-bit
和32-bit
和64-bit
是字符串file
输出到标准输出!
因此,现在我们shared object
在该文件中搜索,然后导致:
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
其某些值是:
ET_EXEC 2 ET_DYN 3
因此,ET_EXEC
总是打印为executable
。
ET_DYN
但是有两种可能性,具体取决于${x
:
pie executable
shared object
${x
问:文件是否可以由用户,组或其他人执行?如果是,请显示pie executable
,否则shared object
。
此扩展是在的varexpand
函数中完成的src/softmagic.c
:
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;
但是,还有另外一种技巧!在src/readelf.c
function中dodynamic
,如果存在DT_FLAGS_1
动态节(PT_DYNAMIC
)的标志条目,则st->mode
存在或不存在DF_1_PIE
标志将覆盖其中的权限:
case DT_FLAGS_1: if (xdh_val & DF_1_PIE) ms->mode |= 0111; else ms->mode &= ~0111; break;
5.34中的错误是初始代码编写为:
if (xdh_val == DF_1_PIE)
这意味着,如果设置了另一个标志(默认情况下GCC会这样做),则可DF_1_NOW
执行文件显示为shared object
。
该DT_FLAGS_1
标志条目不会在ELF标准描述所以它必须是一个Binutils的扩展。
该标志在Linux内核5.0或glibc 2.27中没有任何用处,因此我似乎只是在提供信息,以指示文件是否为PIE。