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

发布于 2025-02-02 11:52:14 字数 459 浏览 2 评论 0 原文

我有一个我正在建造的图书馆。当我运行其中一个: ar rcs lib/libryftts.a $^

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

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

测试:ELF 64位LSB共享对象而不是测试:ELF 64位LSB可执行如我所期望的。我在做什么错?

I have a library I am building. All of my objects compile and link successively when I run either one of:
ar rcs lib/libryftts.a $^

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

in my Makefile. I also am able to successfully install them into /usr/local/lib
When I test the file with nm, all the functions are there.
My problem is that when I run gcc testing/test.c -lryftts -o test && file ./test or gcc testing/test.c lib/libryftts.a -o test && file ./test
it says:

test: ELF 64-bit LSB shared object instead of test: ELF 64-bit LSB executable as I would expect. What am I doing wrong?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

°如果伤别离去 2025-02-09 11:52:14

我在做什么错?

没有什么。

听起来您的GCC默认情况下将GCC配置为构建 -pie 二进制文件。这些二进制文件确实是 共享库(类型 et_dyn ),除了它们像普通可执行文件一样运行。

因此,您应该只运行您的二进制文件,并且(如果有效)不用担心。

或者,您可以将二进制文件与 gcc -no-pie ... 链接,这应该产生非 - pie type eT_exec 的可执行文件,用于哪个文件会说精灵64位LSB可执行文件

What am I doing wrong?

Nothing.

It sounds like your GCC is configured to build -pie binaries by default. These binaries really are shared libraries (of type ET_DYN), except they run just like a normal executable would.

So your should just run your binary, and (if it works) not worry about it.

Or you could link your binary with gcc -no-pie ... and that should produce a non-PIE executable of type ET_EXEC, for which file will say ELF 64-bit LSB executable.

落花随流水 2025-02-09 11:52:14

文件 5.36说它清楚

file 5.36实际上,如果可执行文件为pie,实际上会清楚地打印出来,如: htttps://unix.stackexchange .com/Question/89211/How-to-To-thewere-a-a-linux-binary-was-was-as as as-as-aS-bositiond-repentent-code/435038#435038

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

main。

而非Pie One则为:

main。

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

在5.34中,它是为了开始检查更专业的 df_1_pie elf metadata,但由于实现中的错误共享对象。

该错误是在5.36中的5.36 commit

该错误特别存在于Ubuntu 18.10中,其中具有 file 5.34。

由于巧合而将汇编代码与 ld -pie 链接时,它不会自我表现出来。

源代码分解显示在“ 文件 5.36源代码分析”部分中。

linux内核5.0确定是否可以基于 et_dyn

file> file> file> file “混乱”的根本原因,是两个 pie operutables 共享库是独立的位置,可以放置在随机的内存位置中。

at

/* 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 设置为不为零的东西。 load_bias 是确定小精灵偏移的方法: of-a-pie-pie-cecuter-ninux/51343797”>如何在linux中确定的派的文本部分的地址如何?

/*
 * 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;

我在实验中确认了这一点: -fpie选项是什么GCC和LD中的位置独立的可执行文件?

文件 5.36行为分解

在研究 File> File 从其源起作用。我们将得出结论:

  • 如果 elf32_ehdr.e_type == et_exec
    • 打印可执行文件
  • else如果 elf32_ehdr.e_type == et_dyn
    • 如果 dt_flags_1 动态部分条目存在
      • 如果 df_1_pie dt_flags_1 中设置
        • 打印 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中进行了测试。

每种类型实验的完整测试示例描述:

精灵类型 df_1_pie 分别用:

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

文件 5.36源代码分析

是分析的关键文件IS Magic/Magdir/Elf

此魔术格式仅根据固定位置的字节值确定文件类型。

格式本身记录在以下位置:

man 5 magic

因此,此时您将需要方便地拥有以下文档:

对文件结束的细分,我们看到:

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 是每个精灵文件开始时的4个魔术字节。 \ 177 0x7f 的八分之一。

然后,通过与标准的 elf32_ehdr 结构进行比较,我们看到字节4(第五字节,魔术标识符的第一个字节)确定精灵类:

e_ident[EI_CLASSELFCLASS]

其某些可能的值是:

ELFCLASS32 1
ELFCLASS64 2

然后,在文件来源中,我们有:

1 32-bit
2 64-bit

32-bit and 64位 file> file 输出的字符串要stdout!

因此,现在我们在该文件中搜索共享对象,我们被引导到:

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正是精灵类型:

Elf32_Ehdr.e_type

它的某些值是:

ET_EXEC 2
ET_DYN  3

因此, et_exec 始终以可执行性打印。

et_dyn 有两种可能性,具体取决于 $ {x

  • pie可执行文件
  • 共享对象

$ {x 问:用户,组或其他文件是否可执行文件?如果是,请显示 pie可执行,else 共享对象

此扩展是在 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 函数 dodynamic ,如果存在 dt_flags_1 flags动态部分的条目( pt_dynamic ),则在 st-中的权限> mode df_1_pie flag的存在或不存在,

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 gcc默认情况下,由于 df_1_now ,可执行文件显示为共享对象

dt_flags_1 标志条目未在精灵标准中描述,因此必须是binutils扩展名。

该标志在Linux内核5.0或GLIBC 2.27中没有用途,因此我似乎纯粹是有用的,以表明文件是否为PIE。

file 5.36 says it clearly

file 5.36 actually prints it clearly if the executable is PIE or not as shown at: https://unix.stackexchange.com/questions/89211/how-to-test-whether-a-linux-binary-was-compiled-as-position-independent-code/435038#435038

For example, a PIE executable shows as:

main.out: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, not stripped

and a non-PIE one as:

main.out: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, not stripped

The feature was introduced in 5.33 but it did just a simple chmod +x check. Before that it just printed shared object for PIE.

In 5.34, it was meant to start checking the more specialized DF_1_PIE ELF metadata, but due to a bug in the implementation at commit 9109a696f3289ba00eaa222fd432755ec4287e28 it actually broke things and showed GCC PIE executables as shared objects.

The bug was fixed in 5.36 at commit 03084b161cf888b5286dbbcd964c31ccad4f64d9.

The bug is present in particular in Ubuntu 18.10 which has file 5.34.

It does not manifest itself when linking assembly code with ld -pie because of a coincidence.

Source code breakdown is shown in the "file 5.36 source code analysis" section of this answer.

The Linux kernel 5.0 determines if ASLR can be used based on ET_DYN

The root cause of the file "confusion", is that both PIE executables and shared libraries are position independent and can be placed in randomized memory locations.

At fs/binfmt_elf.c the kernel only accepts those two types of ELF files:

/* First of all, some simple consistency checks */
if (interp_elf_ex->e_type != ET_EXEC &&
        interp_elf_ex->e_type != ET_DYN)
        goto out;

Then, only for ET_DYN does it set the load_bias to something that is not zero. The load_bias is then what determines the ELF offset: How is the address of the text section of a PIE executable determined in Linux?

/*
 * 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;

I confirm this experimentally at: What is the -fPIE option for position-independent executables in gcc and ld?

file 5.36 behaviour breakdown

After studying how file works from its source. We will conclude that:

  • if Elf32_Ehdr.e_type == ET_EXEC
    • print executable
  • else if Elf32_Ehdr.e_type == ET_DYN
    • if DT_FLAGS_1 dynamic section entry is present
      • if DF_1_PIE is set in DT_FLAGS_1:
        • print pie executable
      • else
        • print shared object
    • else
      • if file is executable by user, group or others
        • print pie executable
      • else
        • print shared object

And here are some experiments that confirm that:

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

Tested in Ubuntu 18.10, GCC 8.2.0, Binutils 2.31.1.

The full test example for for each type of experiment is described at:

ELF type and DF_1_PIE are determined respectively with:

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

file 5.36 source code analysis

The key file to analyse is magic/Magdir/elf.

This magic format determines file types depending only on the values of bytes at fixed positions.

The format itself is documented at:

man 5 magic

So at this point you will want to have the following documents handy:

Towards the end of the file, we see:

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 are the 4 magic bytes at the start of every ELF file. \177 is the octal for 0x7F.

Then by comparing with the Elf32_Ehdr struct from the standard, we see that byte 4 (the 5th byte, the first one after the magic identifier), determines the ELF class:

e_ident[EI_CLASSELFCLASS]

and some of its possible values are:

ELFCLASS32 1
ELFCLASS64 2

In file source then, we have:

1 32-bit
2 64-bit

and 32-bit and 64-bit are the strings that file outputs to stdout!

So now we search for shared object in that file, and we are led to:

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},

So this elf-le is some kind of identifier that gets included on the previous part of the code.

Byte 16 is exactly the ELF type:

Elf32_Ehdr.e_type

and some of its values are:

ET_EXEC 2
ET_DYN  3

Therefore, ET_EXEC always gets printed as executable.

ET_DYN however has two possibilities depending on ${x:

  • pie executable
  • shared object

${x asks: are the file executable or not by either user, group or other? If yes, show pie executable, else shared object.

This expansion is done in the varexpand function in 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;

There is, however, one more hack! In src/readelf.c function dodynamic, if the DT_FLAGS_1 flags entry of the dynamic section (PT_DYNAMIC) is present, then the permissions in st->mode are overridden by the presence or absence of the DF_1_PIE flag:

case DT_FLAGS_1:
        if (xdh_val & DF_1_PIE)
                ms->mode |= 0111;
        else
                ms->mode &= ~0111;
        break;

The bug in 5.34 is that the initial code was written as:

    if (xdh_val == DF_1_PIE)

which means that if another flag was set, which GCC does by default due to DF_1_NOW, the executable showed as shared object.

The DT_FLAGS_1 flags entry is not described in the ELF standard so it must be a Binutils extension.

That flag has no uses in the Linux kernel 5.0 or glibc 2.27, so I seems to be purely informative to indicate that a file is PIE or not.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文