elfedit - 检查或编辑 ELF 文件
elfedit [-adrV] [-e cmd] [-L path] [-o default | simple | canon | num] [infile] [outfile]
elfedit 是一个用于检查或修改现有 ELF 目标文件内容的工具。可以访问目标文件中包含的大多数 ELF 数据,这些数据包括 ELF 头、节头表、程序头表、动态节、硬件和软件功能、字符串表和符号表。
elfedit 可以处理来自命令行(–e 选项)或标准输入的命令。如果标准输入是一个终端,elfedit 可提供终端编辑功能以及涵盖大量命令的命令补齐功能。ELF 对特殊整数值和位掩码使用许多标准符号名称。elfedit 可识别此类名称的最有可能的完整形式。在输入 elfedit 命令时,您可以随时按 TAB 键,令 elfedit 显示用法消息以及当前光标处文本的任意已知完整形式。
elfedit 功能以模块形式组织。每个模块提供一组命令,这些命令针对相关功能。通过使用冒号 (:) 分隔符将模块和命令名称组合到一起(中间无空格)来指定命令。例如,dyn:runpath 指的是由 dyn 模块提供的 runpath 命令。模块名称必须是唯一的。给定模块中的命令名称在该模块中必须唯一,但可在多个模块中使用相同的命令名称。
某些模块将模块内的某个命令指定为该模块的缺省命令。用户只需指定模块名称,就可运行此命令。大多数 elfedit 模块均提供一个名为 dump 的命令,它针对模块涵盖的 ELF 文件部分生成的信息与 elfdump 实用程序所显示的信息相同。通常,模块会将 dump 指定为其缺省命令。
用于执行 elfedit 命令的语法在设计上采用类似 UNIX 命令行实用程序的语法,这样任何会使用 UNIX 命令行实用程序的人都可以方便地使用 elfedit 命令。该语法由空格分隔的标记组成。第一个标记是命令名称。选项(即以连字符 (-) 开头的参数)跟在命令后面。纯参数(操作数)跟在选项后面。一个给定的命令可以有 0 个或多个选项和操作数,但是如果它们同时存在,选项始终位于纯参数前面。可使用特殊选项 --(两个连字符)来限定选项的结尾。如果遇到此选项,其余所有参数均被视为纯参数,即使它们以 - 开头。
elfedit 标记中的字符的解释取决于所用的引用格式:
单引号 (') 或双引号 (“) 外面的反斜杠 (\) 充当转义符。elfedit 发现反斜杠字符时会将其忽略,并按字面意思处理反斜杠后面的字符(即使它后面的字符为反斜杠)。此功能可用于在命令的字符串参数中插入一个空白字符,从而无需将一个字符串分为两个单独的标记。同样,它可用于插入一个引号或反斜杠作为文本字符。
在单引号 (') 中,空白字符不用于分隔标记,且会被解释为标记内部的文本字符。双引号 (“) 和反斜杠 (\) 字符会被解释为文本字符,无特殊意义。
在双引号 (“) 中,空白字符不用于分隔标记。单引号字符会被解释为文本,不具有引用功能。反斜杠 (\) 是一个转义字符,在字符串文本中,其作用与 C 编程语言中反斜杠的作用类似:
警报(响铃)
退格
换页符
换行符
回车
水平制表符
垂直制表符
反斜杠
单引号
双引号
一个八进制常数,其中 ooo 是一到三个八进制数字 (0...7)
在反斜杠后面跟有任何其他字符,均会出错。
核心命令均属于一个名为 sys 的内部模块。所有其他模块均打包为可动态装入的可共享目标文件。当执行需要某个模块的命令时或者当执行 sys:load 命令时,elfedit 会按需装入模块。由于 sys 模块特殊的内置状态,而且其命令使用频繁,所以 elfedit 命令允许在不加 sys: 前缀的情况下指定 sys 模块中的命令,例如,使用 load 而非 sys:load。要访问任何其他模块中的命令,必须采用 module:cmd 完整格式指定。
elfedit 随以下标准模块一起提供:
功能节
动态节
ELF 头
程序头数组
重定位节
节头数组
字符串表节
符号表节
符号信息节
内置的核心 elfedit 命令
状态和命令文档
状态 (sys:status) 命令显示有关当前 elfedit 会话的信息:
输入和输出文件
选项设置
模块搜索路径
已装入的模块
每个 elfedit 模块均包含介绍每个命令的详细联机文档,文档格式类似于 UNIX 手册页。可使用 help (sys:help) 命令显示此信息。要了解有关 elfedit 的详细信息,请启动 elfedit 并使用不带有参数的 help 命令:
% elfedit > help
elfedit 将显示一条欢迎消息,其中包含有关 elfedit 的详细信息和有关如何使用帮助系统的信息。
获取某个模块的摘要信息:
> help module
获取某个模块提供的某个特定命令的完整文档:
> help module:command
以 dyn 模块和 dyn:runpath 命令为例:
> help dyn > help dyn:runpath
help (sys:help) 可用于获取 help 命令本身的帮助:
> help help
elfedit 模块是作为按需装入的可共享目标文件实现的。当需要某个模块时,elfedit 会搜索模块路径以便找到实现了该模块的可共享目标文件。路径是以冒号 (:) 字符分界的目录名称序列。除了标准字符,路径还可以包含以下任意标记:
扩展到 elfedit 命令的 ELFCLASS (32, 64)。
扩展到 64 位 ELFCLASS。对于 64 位版本的 elfedit,此标记与 %e 相同,但对于 32 位版本,会扩展到空字符串。
扩展到 elfedit 命令的当前指令集体系结构 (instruction set architecture, ISA) 名称 (sparc, sparcv9, i386, amd64)。
扩展到 64 位 ISA。对于 64 位版本的 elfedit,此标记与 %i 相同,但对于 32 位版本,会扩展到空字符串。
扩展到内置缺省模块路径值。要将目录附加到缺省路径之前或之后时,这非常有用。
elfedit 程序所在文件系统树的根。在标准系统中,此标记就是标准系统根目录 (/)。在开发系统中(这种情况下,elfedit 副本可安装在任意位置),使用 %r 可确保使用匹配的模块集。
扩展到单个 % 字符
可以显示 elfedit 的缺省模块搜索路径(利用这些标记)。
% elfedit -e status
通过设置 ELFEDIT_PATH 环境变量或使用 –L 命令行选项可更改缺省搜素路径。如果指定两者,则环境变量取代 –L 选项。
支持以下选项:
启用 autoprint 模式。启用 autoprint 后,elfedit 将输出修改 ELF 文件后所生成的修改值。此输出以当前输出样式显示,可使用 –o 选项更改此样式。缺省输出样式是 elfdump(1) 实用程序使用的样式。以交互方式使用 elfedit 时,autoprint 模式为缺省模式(当 stdin 和 stdout 为终端时)。因此,仅当在非交互的上下文中使用 elfedit 时,–a 选项才有意义。要在交互会话中禁用 autoprint,请使用 elfedit 命令:
> set a off
如果已设置,此选项可使 elfedit 发布信息性消息,说明其内部操作和要处理的 ELF 目标文件的详细信息。这在需要深入了解所执行的操作时非常有用。
指定一个编辑命令。可以指定多个 –e 选项。如果在命令行上指定了多个编辑命令,elfedit 将在批处理模式下运行。打开文件后,elfedit 按给定的顺序执行每个命令,然后保存修改的文件,最后 elfedit 退出。从 shell 脚本和 makefile 执行简单操作时,批处理模式非常有用。
设置用于定位 elfedit 模块的缺省路径。本手册页的“模块搜索路径”部分介绍了各个模块。
用于显示 ELF 数据的样式。此选项用于确立会话的当前样式。可在 elfedit 会话中更改此样式,方法是使用 set (sys:set) 命令或向会话中执行的各个命令提供 –o 选项。
缺省样式是以适合用户查看的格式显示输出。此样式与 elfdump 实用程序使用的样式类似。
整数值始终以整数格式显示。字符串显示为在内含的字符串表中定位所用的整数偏移量。
显示 ELF 文件中的字符串时,仅显示字符串。如果可能,整数值显示为符号常量,否则以整数格式显示。不显示任何标题、头或其他补充输出。
simple 和 canon 样式在符号常量的显示方面有所不同。指定 simple 后,符号常量会以最常用的格式显示。指定 canon 后,符号常量会以 /usr/include/sys/elf.h 和 /usr/include/sys/link.h 中的完全规范格式显示。建议将规范样式用于 shell 脚本和其他要求报告输出具有稳定性的上下文。
只读模式。输入文件以只读访问模式打开,编辑会话的结果不会保存。指定了 –r 时,elfedit 不允许使用 outfile 参数。如果不打算修改文件,强烈建议使用只读模式。除了提供额外保护以防止意外修改外,该选项还允许检查用户没有写入权限的文件。
输出版本信息并立即退出。
输出用法消息并立即退出。
支持下列操作数:
包含要处理的 ELF 目标文件的输入文件。
此文件可以为可执行文件 (ET_EXEC)、共享目标文件 (ET_DYN) 或可重定位目标文件 (ET_REL)。不直接支持归档文件。要编辑归档中的目标文件,必须提取目标文件,编辑副本,然后将其重新插入到该归档文件。
如果未提供 infile,elfedit 将在限定模式下运行,此模式仅允许执行 sys: 模块中的命令。此模式主要用于访问 help (sys:help) 命令提供的命令文档。
如果提供了 infile,但未给定任何 outfile,elfedit 将就地编辑文件并将结果写入同一文件,这会导致原始文件内容被覆盖。通常,不建议以此模式使用 elfedit,建议指定输出文件。生成的文件经过测试和验证后,可将其移动到原始文件所在的位置。
–r 选项可用于以只读访问模式打开 infile。在检查不希望修改的现有文件时,此选项非常有用。
输出文件。如果同时提供了 infile 和 outfile,infile 将以只读访问模式打开,修改的目标文件内容会写入到 outfile 中。
如果系统支持,elfedit 可作为 64 位应用程序运行,这种情况下可处理大于或等于 2 GB(2^31 个字节)的文件。
启动时,elfedit 使用 libelf 打开输入文件并在内存中缓存其内容副本以供编辑。然后,elfedit 可执行一个或多个命令。随后,修改的目标文件写入输出文件(不一定如此),elfedit 退出,会话即结束。
如果未提供 infile,elfedit 将在限定模式下运行,此模式仅允许执行 sys 模块中的命令。此模式主要用于访问 help (sys:help) 命令提供的命令文档。
如果指定了一个或多个 –e 选项,将按给定顺序执行提供的命令。elfedit 会紧跟在给定命令之后添加对 write (sys:write) 和 quit (sys:quit) 的隐式调用,从而写入输出文件并退出 elfedit 进程。在 shell 脚本和 makefile 中使用此方式非常方便。
如果未指定 –e 选项,elfedit 将从 stdin 中读取命令并按给定顺序执行它们。在此模式下运行时,调用者必须显式发出 write (sys:write) 和 quit (sys:quit) 命令才能保存其工作并退出。
将返回以下退出值:
成功完成。
发生了致命错误。
指定的命令行选项无效。
在以下示例中,显示了将 elfedit 与 shell 提示符 (%) 和 elfedit 提示符 (>) 交互使用的情况。用户不应输入上述字符中的任何一个。
示例 1 更改可执行文件的 Runpath以下示例假定名为 prog 的可执行文件安装在一个 bin 目录下,该目录的相邻目录为保存可共享目标文件的 lib 目录。以下命令将该可执行文件的 runpath 设置为 lib 目录:
% elfedit -e 'dyn:runpath $ORIGIN/../lib'
需要对 –e 选项的参数使用单引号,以确保 shell 将整个命令作为一个参数传递给 elfedit。
另外,可在非批处理模式下使用 elfedit 执行相同的操作。
% elfedit prog > dyn:runpath $ORIGIN/../lib index tag value [30] RUNPATH 0x3e6 $ORIGIN/../lib > write > quit
仅当目标文件中存在 padding 时,才能对元素(例如 runpath)或所需的条目进行添加或修改。请参见“附注”部分。
示例 2 删除硬件功能位需要满足可选硬件支持才能运行的目标文件均构建有功能节,此节包含的位掩码指定了这些目标文件所需的功能。运行时链接程序 (ld.so.1) 根据运行系统的属性检查此掩码,以确定当前系统是否可运行给定目标文件。如果系统未提供程序需要的功能,则会阻止这些程序运行。
该检查可防止未显式检查其所需硬件支持的不完善程序发生莫名奇妙的崩溃。但是,对于编写了在运行时显式检查系统功能的程序,该检查可能会有不利影响。此类程序可能有在满足硬件支持时使用的优化代码,同时也提供了在硬件支持满足不了时可运行的通用回退版本(尽管运行速度比较慢)。在这种情况下,硬件兼容性掩码会阻止此类程序在早期的硬件上运行。此时,从掩码中删除相关位可允许程序运行。
以下示例从使用 SSE3 CPU 扩展的 x86 二进制文件中删除 AV_386_SSE3 硬件功能。这样就将验证能否使用 SSE3 的职责从运行时链接程序转移到程序自身:
% elfedit -e 'cap:hw1 -and -cmp sse3' prog示例 3 从目标文件中读取信息
elfedit 可用于从目标文件中提取特定目标信息。以下 shell 命令读取文件 /usr/bin/ls 中包含的节头数:
% SHNUM=`elfedit -r -onum -e 'ehdr:e_shnum' /usr/bin/ls` % echo $SHNUM 29
您可能会得到不同的值,具体取决于您使用的 Oracle Solaris 版本和计算机类型。–r 选项会使文件以只读方式打开,允许具有普通访问权限的用户打开文件并防止重要的系统可执行文件意外损坏。使用 num 输出样式是为了仅获取所需的值,而无任何多余的文本。
同样,以下示例从 C 运行时库提取 unlink 符号的符号类型:
% TYPE=`elfedit -r -osimple -e 'sym:st_type unlink' \ /lib/libc.so` % echo $TYPE STT_FUNC示例 4 指定可执行文件的 ASLR 设置
可执行文件的缺省地址空间布局随机化 (Address Space Layout Randomization, ASLR) 行为是使用 DT_SUNW_SX_ASLR 动态节条目指定的。以下演示了如何对指定程序启用或禁用 ASLR。
% elfedit prog > dyn:sunw_sx aslr enable index tag value [40] SUNW_SX_ASLR 0x2 ENABLE > dyn:sunw_sx aslr disable index tag value [40] SUNW_SX_ASLR 0x1 DISABLE
更改缺省模块搜索路径。本手册页的“模块搜索路径”部分讨论了模块搜索路径。
禁止自动执行 64 位 elfedit。缺省情况下,如果系统具有 64 位功能,则会运行 64 位版本的 elfedit。
以交互方式将输出从 elfedit 传送到屏幕。如果未设置,将使用 more。请参见 more(1)。
elfedit 模块的缺省目录,这些模块会按需装入供编辑命令使用。
用于命令行编辑的 tecla 个人定制文件。请参见 tecla(7)。
有关下列属性的说明,请参见 attributes(7):
|
dump(1)、elfcompress(1)、elfdump(1)、ld.so.1(1)、more(1)、nm(1)、pvs(1)、elf(3ELF)、libelf(3LIB)、attributes(7)、tecla(7)、sxadm(8)
《Oracle Solaris 11.4 Linkers and Libraries Guide》
elfedit 是一个专用于测试和开发 ELF 系统的工具。通过该工具,用户几乎可检查和更改目标文件中的每一部分 ELF 元数据。对于会产生无效或不可用 ELF 文件的编辑操作,该工具也会执行,不会做出提醒。用户应当了解 ELF 格式并知晓应遵守的规则和约定。使用 elfedit 时,可从《Oracle Solaris 11.4 Linkers and Libraries Guide》中获取帮助。
elfedit 允许用户更改目标文件中的 ELF 元数据,但用户无法了解或更改实际程序的代码。因此,以不符合文件实际内容的方式设置 ELF 属性(例如类型、大小、对齐方式等)可能会生成一个损坏的且不可用的输出目标文件。此类更改在测试链接程序组件时可能很有用,但在其他情况下应避免此类更改。
较高级别的操作(例如,使用 dyn:runpath 命令更改目标文件的 runpath)是安全的,在执行时不存在本节中叙述的此类风险。
并非 elfedit 支持的每个 ELF 操作都可在每个 ELF 目标文件上成功执行。elfedit 受文件中现有的节制约。
特别需要指出的是,elfedit 可能无法修改给定目标文件的 runpath。要修改 runpath,必须满足以下条件:
动态字符串表中必须存在所需的字符串,或者,此节中必须有足够的预留空间可供添加新字符串使用。如果您的目标文件有一个字符串表预留区域,则 .dynamic DT_SUNW_STRPAD 元素的值表示该区域的大小。以下 elfedit 命令可用于检查此大小:
% elfedit -r -e 'dyn:tag DT_SUNW_STRPAD' file
动态节必须已有一个 runpath 元素,或者,必须存在可向其插入一个 runpath 元素的未使用的动态插槽。测试是否存在一个 runpath:
% elfedit -r -e 'dyn:runpath' file
动态节使用一个类型为 DT_NULL 的元素终止在该节中找到的数组。终止 DT_NULL 无法更改,但是如果存在多个此元素,elfedit 可将其中一个转换为 runpath 元素。测试是否有额外的动态插槽:
% elfedit -r -e 'dyn:tag DT_NULL' file
早期的目标文件不具备完成此类操作所需的额外空间。Oracle Solaris Express Community Edition 发行版中引入了执行此类操作所需的空间。
如果操作失败,使用 –d (debug) 选项输出的详细信息可帮助揭示失败原因。
elfedit 模块遵循一个约定,根据该约定,直接操作 ELF 结构中的某个字段的命令与该字段的名称相同,但实现较高级别概念的命令不使用此命名方式。例如,用于操作 ELF 头中的 e_flags 字段的命令名为 ehdr:e_flags。因此,您通常可以通过确定模块并查找具有相应字段名称的命令来查找修改 ELF 字段的命令。
elfedit 对压缩节提供只读支持。压缩节由 SHF_COMPRESSED 节标头标志来标识。要使用 elfedit 修改此类节,需要首先使用外部实用程序(例如 elfcompress(1))解压缩该节。
elfedit 命令无法修改核心文件。可指定 –r 选项以 readonly 模式对核心文件进行操作。