加载 ELF64 头部导致分段错误的原因是什么?

huangapple go评论65阅读模式
英文:

Why is loading ELF64 headers causing a segmentation fault?

问题

I am trying to create an ELF64 file from scratch; the program I'm using to test it right now just calls the exit() syscall using assembly; there's no linking of libraries etc. Here's the output of "dumpelf":

#include <elf.h>;

/*
 * ELF dump of 'elftest'
 *     4120 (0x1018) bytes
 */

Elf64_Dyn dumpedelf_dyn_0[];
struct {
        Elf64_Ehdr ehdr;
        Elf64_Phdr phdrs[3];
        Elf64_Shdr shdrs[3];
        Elf64_Dyn *dyns;
} dumpedelf_0 = {

.ehdr = {
        .e_ident = { /* (EI_NIDENT bytes) */
                /* [0] EI_MAG:        */ 0x7F,'E','L','F',
                /* [4] EI_CLASS:      */ 2 , /* (ELFCLASS64) */
                /* [5] EI_DATA:       */ 1 , /* (ELFDATA2LSB) */
                /* [6] EI_VERSION:    */ 1 , /* (EV_CURRENT) */
                /* [7] EI_OSABI:      */ 0 , /* (ELFOSABI_NONE) */
                /* [8] EI_ABIVERSION: */ 0 ,
                /* [9-15] EI_PAD:     */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
        },
        .e_type      = 2          , /* (ET_EXEC) */
        .e_machine   = 62         , /* (EM_X86_64) */
        .e_version   = 1          , /* (EV_CURRENT) */
        .e_entry     = 0x400000   , /* (start address at runtime) */
        .e_phoff     = 64         , /* (bytes into file) */
        .e_shoff     = 232        , /* (bytes into file) */
        .e_flags     = 0x0        ,
        .e_ehsize    = 64         , /* (bytes) */
        .e_phentsize = 56         , /* (bytes) */
        .e_phnum     = 3          , /* (program headers) */
        .e_shentsize = 64         , /* (bytes) */
        .e_shnum     = 3          , /* (section headers) */
        .e_shstrndx  = 1
},

.phdrs = {
/* Program Header #0 0x40 */
{
        .p_type   = 6          , /* [PT_PHDR] */
        .p_offset = 64         , /* (bytes into file) */
        .p_vaddr  = 0x40       , /* (virtual addr at runtime) */
        .p_paddr  = 0x40       , /* (physical addr at runtime) */
        .p_filesz = 168        , /* (bytes in file) */
        .p_memsz  = 168        , /* (bytes in mem at runtime) */
        .p_flags  = 0x4        , /* PF_R */
        .p_align  = 8          , /* (min mem alignment in bytes) */
},
/* Program Header #1 0x78 */ <-- PT_NULL=OK, PT_LOAD=SEGFAULT
{
        .p_type   = 1          , /* [PT_LOAD] */ 
        .p_offset = 0          , /* (bytes into file) */
        .p_vaddr  = 0x0        , /* (virtual addr at runtime) */
        .p_paddr  = 0x0        , /* (physical addr at runtime) */
        .p_filesz = 441        , /* (bytes in file) */
        .p_memsz  = 441        , /* (bytes in mem at runtime) */
        .p_flags  = 0x4        , /* PF_R */
        .p_align  = 4096       , /* (min mem alignment in bytes) */
},
/* Program Header #2 0xB0 */ <-- WORKS AS EXPECTED
{
        .p_type   = 1          , /* [PT_LOAD] */
        .p_offset = 4096       , /* (bytes into file) */
        .p_vaddr  = 0x400000   , /* (virtual addr at runtime) */
        .p_paddr  = 0x400000   , /* (physical addr at runtime) */
        .p_filesz = 24         , /* (bytes in file) */
        .p_memsz  = 24         , /* (bytes in mem at runtime) */
        .p_flags  = 0x5        , /* PF_R | PF_X */
        .p_align  = 4096       , /* (min mem alignment in bytes) */
},
},

.shdrs = {
/* Section Header #0 '' 0xE8 */
{
        .sh_name      = 0          ,
        .sh_type      = 0          , /* [SHT_NULL] */
        .sh_flags     = 0          ,
        .sh_addr      = 0x0        ,
        .sh_offset    = 0          , /* (bytes) */
        .sh_size      = 0          , /* (bytes) */
        .sh_link      = 0          ,
        .sh_info      = 0          ,
        .sh_addralign = 0          ,
        .sh_entsize   = 0
},
/* Section Header #1 '.shstrtab' 0x128 */
{
        .sh_name      = 1          ,
        .sh_type      = 3          , /* [SHT_STRTAB] */
        .sh_flags     = 32         ,
        .sh_addr      = 0x1A8      ,
        .sh_offset    = 424        , /* (bytes) */
        .sh_size      = 17         , /* (bytes) */
        .sh_link      = 0          ,
        .sh_info      = 0          ,
        .sh_addralign = 1          ,
        .sh_entsize   = 1
},
/* Section Header #2 '.text' 0x168 */
{
        .sh_name      = 11         ,
        .sh_type      = 1          , /* [SHT_PROGBITS] */
        .sh_flags     = 6          ,
        .sh_addr      = 0x400000   ,
        .sh_offset    = 4096       , /* (bytes) */
        .sh_size      = 24         , /* (bytes) */
        .sh_link      = 0          ,
        .sh_info      = 0          ,
        .sh_addralign = 16         ,
        .sh_entsize   = 0
},
},

.dyns = dumpedelf_dyn_0,
};
Elf64_Dyn dumpedelf_dyn_0[] = {
 /* no dynamic tags ! */ };

If I try to execute the program in x86_64 Linux, the program segfaults. Attempting to debug it in gdb is literally a non-starter

英文:

I am trying to create an ELF64 file from scratch; the program I'm using to test it right now just calls the exit() syscall using assembly; there's no linking of libraries etc. Here's the output of "dumpelf":

#include &lt;elf.h&gt;
/*
* ELF dump of &#39;elftest&#39;
*     4120 (0x1018) bytes
*/
Elf64_Dyn dumpedelf_dyn_0[];
struct {
Elf64_Ehdr ehdr;
Elf64_Phdr phdrs[3];
Elf64_Shdr shdrs[3];
Elf64_Dyn *dyns;
} dumpedelf_0 = {
.ehdr = {
.e_ident = { /* (EI_NIDENT bytes) */
/* [0] EI_MAG:        */ 0x7F,&#39;E&#39;,&#39;L&#39;,&#39;F&#39;,
/* [4] EI_CLASS:      */ 2 , /* (ELFCLASS64) */
/* [5] EI_DATA:       */ 1 , /* (ELFDATA2LSB) */
/* [6] EI_VERSION:    */ 1 , /* (EV_CURRENT) */
/* [7] EI_OSABI:      */ 0 , /* (ELFOSABI_NONE) */
/* [8] EI_ABIVERSION: */ 0 ,
/* [9-15] EI_PAD:     */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
},
.e_type      = 2          , /* (ET_EXEC) */
.e_machine   = 62         , /* (EM_X86_64) */
.e_version   = 1          , /* (EV_CURRENT) */
.e_entry     = 0x400000   , /* (start address at runtime) */
.e_phoff     = 64         , /* (bytes into file) */
.e_shoff     = 232        , /* (bytes into file) */
.e_flags     = 0x0        ,
.e_ehsize    = 64         , /* (bytes) */
.e_phentsize = 56         , /* (bytes) */
.e_phnum     = 3          , /* (program headers) */
.e_shentsize = 64         , /* (bytes) */
.e_shnum     = 3          , /* (section headers) */
.e_shstrndx  = 1
},
.phdrs = {
/* Program Header #0 0x40 */
{
.p_type   = 6          , /* [PT_PHDR] */
.p_offset = 64         , /* (bytes into file) */
.p_vaddr  = 0x40       , /* (virtual addr at runtime) */
.p_paddr  = 0x40       , /* (physical addr at runtime) */
.p_filesz = 168        , /* (bytes in file) */
.p_memsz  = 168        , /* (bytes in mem at runtime) */
.p_flags  = 0x4        , /* PF_R */
.p_align  = 8          , /* (min mem alignment in bytes) */
},
/* Program Header #1 0x78 */ &lt;-- PT_NULL=OK, PT_LOAD=SEGFAULT
{
.p_type   = 1          , /* [PT_LOAD] */ 
.p_offset = 0          , /* (bytes into file) */
.p_vaddr  = 0x0        , /* (virtual addr at runtime) */
.p_paddr  = 0x0        , /* (physical addr at runtime) */
.p_filesz = 441        , /* (bytes in file) */
.p_memsz  = 441        , /* (bytes in mem at runtime) */
.p_flags  = 0x4        , /* PF_R */
.p_align  = 4096       , /* (min mem alignment in bytes) */
},
/* Program Header #2 0xB0 */ &lt;-- WORKS AS EXPECTED
{
.p_type   = 1          , /* [PT_LOAD] */
.p_offset = 4096       , /* (bytes into file) */
.p_vaddr  = 0x400000   , /* (virtual addr at runtime) */
.p_paddr  = 0x400000   , /* (physical addr at runtime) */
.p_filesz = 24         , /* (bytes in file) */
.p_memsz  = 24         , /* (bytes in mem at runtime) */
.p_flags  = 0x5        , /* PF_R | PF_X */
.p_align  = 4096       , /* (min mem alignment in bytes) */
},
},
.shdrs = {
/* Section Header #0 &#39;&#39; 0xE8 */
{
.sh_name      = 0          ,
.sh_type      = 0          , /* [SHT_NULL] */
.sh_flags     = 0          ,
.sh_addr      = 0x0        ,
.sh_offset    = 0          , /* (bytes) */
.sh_size      = 0          , /* (bytes) */
.sh_link      = 0          ,
.sh_info      = 0          ,
.sh_addralign = 0          ,
.sh_entsize   = 0
},
/* Section Header #1 &#39;.shstrtab&#39; 0x128 */
{
.sh_name      = 1          ,
.sh_type      = 3          , /* [SHT_STRTAB] */
.sh_flags     = 32         ,
.sh_addr      = 0x1A8      ,
.sh_offset    = 424        , /* (bytes) */
.sh_size      = 17         , /* (bytes) */
.sh_link      = 0          ,
.sh_info      = 0          ,
.sh_addralign = 1          ,
.sh_entsize   = 1
},
/* Section Header #2 &#39;.text&#39; 0x168 */
{
.sh_name      = 11         ,
.sh_type      = 1          , /* [SHT_PROGBITS] */
.sh_flags     = 6          ,
.sh_addr      = 0x400000   ,
.sh_offset    = 4096       , /* (bytes) */
.sh_size      = 24         , /* (bytes) */
.sh_link      = 0          ,
.sh_info      = 0          ,
.sh_addralign = 16         ,
.sh_entsize   = 0
},
},
.dyns = dumpedelf_dyn_0,
};
Elf64_Dyn dumpedelf_dyn_0[] = {
/* no dynamic tags ! */ };

If I try to execute the program in x86_64 Linux, the program segfaults. Attempting to debug it in gdb is literally a non-starter, GDB tells me the program segfaults "during startup" so no help there.

After several days of beating my head against this problem, I can not for the life of me figure out why this is causing a segfault; I have looked at numerous other executable files including system binaries and files compiled with various different programs, they all load the ELF headers at vaddr=0x0 without problems so why does it fail in this case?

If I change Program Header #1 0x78 from PT_LOAD to PT_NULL to not load the ELF64 headers into memory, the program runs as expected and exits without error so I know the executable part is OK. This narrows the problem down to that header but it isn't a workable solution.

Edit: Here's the same file as viewed with readelf -a:

  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class:                             ELF64
Data:                              2&#39;s complement, little endian
Version:                           1 (current)
OS/ABI:                            UNIX - System V
ABI Version:                       0
Type:                              EXEC (Executable file)
Machine:                           Advanced Micro Devices X86-64
Version:                           0x1
Entry point address:               0x400000
Start of program headers:          64 (bytes into file)
Start of section headers:          232 (bytes into file)
Flags:                             0x0
Size of this header:               64 (bytes)
Size of program headers:           56 (bytes)
Number of program headers:         3
Size of section headers:           64 (bytes)
Number of section headers:         3
Section header string table index: 1
Section Headers:
[Nr] Name              Type             Address           Offset
Size              EntSize          Flags  Link  Info  Align
[ 0]                   NULL             0000000000000000  00000000
0000000000000000  0000000000000000           0     0     0
[ 1] .shstrtab         STRTAB           00000000000001a8  000001a8
0000000000000011  0000000000000001   S       0     0     1
[ 2] .text             PROGBITS         0000000000400000  00001000
0000000000000018  0000000000000000  AX       0     0     16
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
L (link order), O (extra OS processing required), G (group), T (TLS),
C (compressed), x (unknown), o (OS specific), E (exclude),
D (mbind), l (large), p (processor specific)
There are no section groups in this file.
Program Headers:
Type           Offset             VirtAddr           PhysAddr
FileSiz            MemSiz              Flags  Align
PHDR           0x0000000000000040 0x0000000000000040 0x0000000000000040
0x00000000000000a8 0x00000000000000a8  R      0x8
LOAD           0x0000000000000000 0x0000000000000000 0x0000000000000000
0x00000000000001b9 0x00000000000001b9  R      0x1000
LOAD           0x0000000000001000 0x0000000000400000 0x0000000000400000
0x0000000000000018 0x0000000000000018  R E    0x1000
Section to Segment mapping:
Segment Sections...
00
01
02     .text
There is no dynamic section in this file.
There are no relocations in this file.
No processor specific unwind information to decode
No version information found in this file.

Edit #2: Added output of strace as both ordinary user, and as root which surprisingly works:

$ strace ./filename
execve(&quot;./filename&quot;, [&quot;./filename&quot;], 0x7ffd660b1910 /* 28 vars */) = -1 EPERM (Operation not permitted)
+++ killed by SIGSEGV +++
Segmentation fault
(as root)
# strace ./filename
execve(&quot;./filename&quot;, [&quot;./filename&quot;], 0x7ffd06138b90 /* 27 vars */) = 0
exit(0)                                 = ?
+++ exited with 0 +++
$ stat filename
File: filename
Size: 4120            Blocks: 16         IO Block: 4096   regular file
Device: 820h/2080d      Inode: 1920        Links: 1
Access: (0755/-rwxr-xr-x)  Uid: ( 1000/ username)   Gid: ( 1000/ username)
Access: 2023-06-15 13:24:24.007538545 +0200
Modify: 2023-06-15 13:24:16.787538566 +0200
Change: 2023-06-15 13:24:16.787538566 +0200
Birth: 2023-06-15 13:24:16.787538566 +0200

答案1

得分: 4

在经过数天的苦苦探索后,我简直想不明白为什么这会导致段错误。

实际上,这并不会导致SIGSEGV。相反,内核会检查你的二进制文件并表示“无法执行”,然后发送一个“用火烧毁”的信号。

原因与这个答案相同:你有一个ET_EXEC文件(必须加载到链接的地址,你设置为0)。但是内核不会加载任何地址低于0x10000的东西(有一个内核常数定义了最低允许的地址,我没有追踪到它)。

你可以将二进制文件更改为ET_DYN(使其成为PIE二进制文件;可以加载到任意位置),或者将ET_EXEC链接到地址0x10000或更高的位置,然后它就能正常工作了。

英文:

> After several days of beating my head against this problem, I can not for the life of me figure out why this is causing a segfault

It's not actually causing a SIGSEGV. Rather, the kernel looks at your binary and says "no can't do", and sends it a "kill with fire" signal.

The reason is the same as this answer: you have an ET_EXEC file (which must be loaded at the linked-at address, which you have as 0). But the kernel will not load anything below 0x10000 (there is some kernel constant which defines the lowest allowed address, I haven't tracked it down).

You can change the binary to ET_DYN (making it a PIE binary; which can be loaded at arbitrary location), or you can link that ET_EXEC at address 0x10000 or above, and it would start working.

huangapple
  • 本文由 发表于 2023年6月15日 15:38:06
  • 转载请务必保留本文链接:https://go.coder-hub.com/76480155.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定