我正在try 从头开始创建一个ELF64文件;我现在用来测试它的程序只是使用汇编调用exit()syscall;没有库的链接等.下面是"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 ! */ };

如果我try 在x86_64 Linux中执行该程序,程序会出现分段错误.试图在GDB中调试它实际上是不可能的,GDB告诉我程序"在启动期间"出现了段错误,所以在那里没有帮助.

在与这个问题纠缠了几天之后,我始终无法弄清楚为什么这会导致段错误;我已经查看了许多其他可执行文件,包括系统二进制文件和用各种不同程序编译的文件,它们都加载了vaddr=0x0处的ELF头文件,没有问题,为什么在这种情况下它会失败?

如果我将Program Header #1 0x78PT_LOAD更改为PT_NULL,以不将ELF64标头加载到内存中,程序将按预期运行,并且没有错误地退出,因此我知道可执行部分是正常的.这会将问题缩小到该标头,但这不是一个可行的解决方案.

编辑:这是与使用readelf -a查看的相同文件:

  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
  Class:                             ELF64
  Data:                              2'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.

编辑#2:添加了以普通用户身份和以root身份输出的strace,这令人惊讶地工作:

$ strace ./filename
execve("./filename", ["./filename"], 0x7ffd660b1910 /* 28 vars */) = -1 EPERM (Operation not permitted)
+++ killed by SIGSEGV +++
Segmentation fault

(as root)
# strace ./filename
execve("./filename", ["./filename"], 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

推荐答案

在对这个问题苦苦思索了几天之后,我无论如何也想不出为什么这会导致段错误

这并不是真正导致了SIGSEGV分.相反,内核查看您的二进制文件并说"不,不能做",并向它发送一个"用火杀死"的信号.

原因与this answer相同:您有一个ET_EXEC文件(该文件必须加载到链接地址,您的地址为0).但是内核不会加载低于0x10000的任何东西(有一些内核常量定义了允许的最低地址,我还没有追踪到它).

您可以将二进制更改为ET_DYN(使其成为PIE二进制;可以在任意位置加载),或者您可以将ET_EXEC链接到地址0x10000或更高,它就会开始工作.

Linux相关问答推荐

使用sed替换字符,但如果它是在bash csv文件中的字符串中,则不使用

使用ms宏将PIC图像放置在页面的任意位置

为什么waitpid(2)可以指定非子进程?

我需要从 Ubuntu 中删除 .txt 文件中以白色间距分隔的行的白色间距

匹配模式和提取

使用来自 yocto build 而不是主机系统的 protoc

使用 PowerShell 删除重复行所需的时间比 WSL 长得多

如何从 AWK 中的 CSV 列中获取中值? (Linux)

如何计算文本的时差,如 YYYYMMDDHHMMSSXXX 格式,包括毫秒

查找更高版本的文件

Linux合并文件

使用 bash 在包含模式的 java 文件中查找行,然后替换该行的另一部分

如何在 Linux 中设置目录大小限制?

为什么 JVM 报告的已提交内存比 linux 进程驻留集大小更多?

bashrc 在运行 bash 命令之前不会加载

如何在 Linux 中使用单行命令获取 Java 版本

Shell 脚本, echo 消息后在同一行读取

如何安装python开发者包?

什么是 linux 脚本中的 start-stop-daemon?

readelf vs. objdump:为什么都需要