Userland: Add readelf utility

This commit is contained in:
Brendan Coles 2020-12-29 08:04:29 +00:00 committed by Andreas Kling
parent 5c99296b92
commit 2b3837a908
Notes: sideshowbarker 2024-07-19 00:27:47 +09:00

451
Userland/readelf.cpp Normal file
View File

@ -0,0 +1,451 @@
/*
* Copyright (c) 2020, the SerenityOS developers.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <AK/MappedFile.h>
#include <AK/String.h>
#include <AK/StringBuilder.h>
#include <AK/StringView.h>
#include <LibCore/ArgsParser.h>
#include <LibELF/Image.h>
#include <LibELF/Validation.h>
#include <ctype.h>
#include <stdio.h>
static const char* object_file_type_to_string(Elf32_Half type)
{
switch (type) {
case ET_NONE:
return "None";
case ET_REL:
return "Relocatable";
case ET_EXEC:
return "Executable";
case ET_DYN:
return "Shared object";
case ET_CORE:
return "Core";
default:
return "(?)";
}
}
static const char* object_machine_type_to_string(Elf32_Half type)
{
switch (type) {
case ET_NONE:
return "None";
case EM_M32:
return "AT&T WE 32100";
case EM_SPARC:
return "SPARC";
case EM_386:
return "Intel 80386";
case EM_68K:
return "Motorola 68000";
case EM_88K:
return "Motorola 88000";
case EM_486:
return "Intel 80486";
case EM_860:
return "Intel 80860";
case EM_MIPS:
return "MIPS R3000 Big-Endian only";
default:
return "(?)";
}
}
static const char* object_program_header_type_to_string(Elf32_Word type)
{
switch (type) {
case PT_NULL:
return "NULL";
case PT_LOAD:
return "LOAD";
case PT_DYNAMIC:
return "DYNAMIC";
case PT_INTERP:
return "INTERP";
case PT_NOTE:
return "NOTE";
case PT_SHLIB:
return "SHLIB";
case PT_PHDR:
return "PHDR";
case PT_TLS:
return "TLS";
case PT_LOOS:
return "LOOS";
case PT_HIOS:
return "HIOS";
case PT_LOPROC:
return "LOPROC";
case PT_HIPROC:
return "HIPROC";
case PT_GNU_EH_FRAME:
return "GNU_EH_FRAME";
case PT_GNU_RELRO:
return "GNU_RELRO";
case PT_GNU_STACK:
return "GNU_STACK";
case PT_OPENBSD_RANDOMIZE:
return "OPENBSD_RANDOMIZE";
case PT_OPENBSD_WXNEEDED:
return "OPENBSD_WXNEEDED";
case PT_OPENBSD_BOOTDATA:
return "OPENBSD_BOOTDATA";
default:
return "(?)";
}
}
static const char* object_section_header_type_to_string(Elf32_Word type)
{
switch (type) {
case SHT_NULL:
return "NULL";
case SHT_PROGBITS:
return "PROGBITS";
case SHT_SYMTAB:
return "SYMTAB";
case SHT_STRTAB:
return "STRTAB";
case SHT_RELA:
return "RELA";
case SHT_HASH:
return "HASH";
case SHT_DYNAMIC:
return "DYNAMIC";
case SHT_NOTE:
return "NOTE";
case SHT_NOBITS:
return "NOBITS";
case SHT_REL:
return "REL";
case SHT_SHLIB:
return "SHLIB";
case SHT_DYNSYM:
return "DYNSYM";
case SHT_NUM:
return "NUM";
case SHT_INIT_ARRAY:
return "INIT_ARRAY";
case SHT_FINI_ARRAY:
return "FINI_ARRAY";
case SHT_PREINIT_ARRAY:
return "PREINIT_ARRAY";
case SHT_GROUP:
return "GROUP";
case SHT_SYMTAB_SHNDX:
return "SYMTAB_SHNDX";
case SHT_LOOS:
return "SOOS";
case SHT_SUNW_dof:
return "SUNW_dof";
case SHT_GNU_LIBLIST:
return "GNU_LIBLIST";
case SHT_SUNW_move:
return "SUNW_move";
case SHT_SUNW_syminfo:
return "SUNW_syminfo";
case SHT_SUNW_verdef:
return "SUNW_verdef";
case SHT_SUNW_verneed:
return "SUNW_verneed";
case SHT_SUNW_versym: // or SHT_HIOS
return "SUNW_versym";
case SHT_LOPROC:
return "LOPROC";
case SHT_HIPROC:
return "HIPROC";
case SHT_LOUSER:
return "LOUSER";
case SHT_HIUSER:
return "HIUSER";
case SHT_GNU_HASH:
return "GNU_HASH";
default:
return "(?)";
}
}
static const char* object_symbol_type_to_string(Elf32_Word type)
{
switch (type) {
case STT_NOTYPE:
return "NOTYPE";
case STT_OBJECT:
return "OBJECT";
case STT_FUNC:
return "FUNC";
case STT_SECTION:
return "SECTION";
case STT_FILE:
return "FILE";
case STT_TLS:
return "TLS";
case STT_LOPROC:
return "LOPROC";
case STT_HIPROC:
return "HIPROC";
default:
return "(?)";
}
}
static const char* object_symbol_binding_to_string(Elf32_Word type)
{
switch (type) {
case STB_LOCAL:
return "LOCAL";
case STB_GLOBAL:
return "GLOBAL";
case STB_WEAK:
return "WEAK";
case STB_NUM:
return "NUM";
case STB_LOPROC:
return "LOPROC";
case STB_HIPROC:
return "HIPROC";
default:
return "(?)";
}
}
int main(int argc, char** argv)
{
if (pledge("stdio rpath", nullptr) < 0) {
perror("pledge");
return 1;
}
const char* path;
static bool display_all = false;
static bool display_elf_header = false;
static bool display_program_headers = false;
static bool display_section_headers = false;
static bool display_headers = false;
static bool display_symbol_table = false;
static bool display_dynamic_symbol_table = false;
static bool display_core_notes = false;
static bool display_relocations = false;
static bool display_unwind_info = false;
static bool display_dynamic_section = false;
Core::ArgsParser args_parser;
args_parser.add_option(display_all, "Display all", "all", 'a');
args_parser.add_option(display_elf_header, "Display ELF header", "file-header", 'h');
args_parser.add_option(display_program_headers, "Display program headers", "program-headers", 'l');
args_parser.add_option(display_section_headers, "Display section headers", "section-headers", 'S');
args_parser.add_option(display_headers, "Equivalent to: -h -l -S", "headers", 'e');
args_parser.add_option(display_symbol_table, "Display the symbol table", "syms", 's');
args_parser.add_positional_argument(path, "ELF path", "path");
args_parser.parse(argc, argv);
if (argc < 3) {
args_parser.print_usage(stderr, argv[0]);
return -1;
}
if (display_headers) {
display_elf_header = true;
display_program_headers = true;
display_section_headers = true;
}
if (display_all) {
display_elf_header = true;
display_program_headers = true;
display_section_headers = true;
display_symbol_table = true;
}
MappedFile mapped_executable(path);
if (!mapped_executable.is_valid()) {
fprintf(stderr, "Unable to map file %s\n", path);
return -1;
}
ELF::Image executable_elf((const u8*)mapped_executable.data(), mapped_executable.size());
if (!executable_elf.is_valid()) {
fprintf(stderr, "File is not a valid ELF object\n");
return -1;
}
String interpreter_path;
if (!ELF::validate_program_headers(*(Elf32_Ehdr*)mapped_executable.data(), mapped_executable.size(), (u8*)mapped_executable.data(), mapped_executable.size(), &interpreter_path)) {
fprintf(stderr, "Invalid ELF headers\n");
return -1;
}
if (executable_elf.is_dynamic() && interpreter_path.is_null()) {
fprintf(stderr, "Warning: Dynamic ELF object has no interpreter path\n");
}
ELF::Image interpreter_image((const u8*)mapped_executable.data(), mapped_executable.size());
if (!interpreter_image.is_valid()) {
fprintf(stderr, "ELF image is invalid\n");
return -1;
}
auto header = *reinterpret_cast<const Elf32_Ehdr*>(mapped_executable.data());
if (display_elf_header) {
printf("ELF header:\n");
String magic = String::format("%s", header.e_ident);
printf(" Magic: ");
for (size_t i = 0; i < magic.length(); i++) {
if (isprint(magic[i])) {
printf("%c ", magic[i]);
} else {
printf("%02x ", magic[i]);
}
}
printf("\n");
printf(" Type: %d (%s)\n", header.e_type, object_file_type_to_string(header.e_type));
printf(" Machine: %u (%s)\n", header.e_machine, object_machine_type_to_string(header.e_machine));
printf(" Version: 0x%x\n", header.e_version);
printf(" Entry point address: 0x%x\n", header.e_entry);
printf(" Start of program headers: %u (bytes into file)\n", header.e_phoff);
printf(" Start of section headers: %u (bytes into file)\n", header.e_shoff);
printf(" Flags: 0x%x\n", header.e_flags);
printf(" Size of this header: %u (bytes)\n", header.e_ehsize);
printf(" Size of program headers: %u (bytes)\n", header.e_phentsize);
printf(" Number of program headers: %u\n", header.e_phnum);
printf(" Size of section headers: %u (bytes)\n", header.e_shentsize);
printf(" Number of section headers: %u\n", header.e_shnum);
printf(" Section header string table index: %u\n", header.e_shstrndx);
printf("\n");
}
if (display_section_headers) {
if (!display_all) {
printf("There are %u section headers, starting at offset 0x%x:\n", header.e_shnum, header.e_shoff);
printf("\n");
}
if (!interpreter_image.section_count()) {
printf("There are no sections in this file.\n");
} else {
printf("Section Headers:\n");
printf(" Name Type Address Offset Size Flags\n");
interpreter_image.for_each_section([](const ELF::Image::Section& section) {
printf(" %-15s ", section.name().characters_without_null_termination());
printf("%-15s ", object_section_header_type_to_string(section.type()));
printf("%08x ", section.address());
printf("%08x ", section.offset());
printf("%08x ", section.size());
printf("%u", section.flags());
printf("\n");
return IterationDecision::Continue;
});
}
printf("\n");
}
if (display_program_headers) {
if (!display_all) {
printf("Elf file type is %d (%s)\n", header.e_type, object_file_type_to_string(header.e_type));
printf("Entry point 0x%x\n", header.e_entry);
printf("There are %u program headers, starting at offset %u\n", header.e_phnum, header.e_phoff);
printf("\n");
}
printf("Program Headers:\n");
printf(" Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align\n");
interpreter_image.for_each_program_header([](const ELF::Image::ProgramHeader& program_header) {
printf(" %-14s ", object_program_header_type_to_string(program_header.type()));
printf("0x%08x ", program_header.offset());
printf("%p ", program_header.vaddr().as_ptr());
printf("%p ", program_header.vaddr().as_ptr()); // FIXME: assumes PhysAddr = VirtAddr
printf("0x%08x ", program_header.size_in_image());
printf("0x%08x ", program_header.size_in_memory());
printf("%04x ", program_header.flags());
printf("0x%08x", program_header.alignment());
printf("\n");
if (program_header.type() == PT_INTERP)
printf(" [Interpreter: %s]\n", program_header.raw_data());
return IterationDecision::Continue;
});
printf("\n");
}
if (display_dynamic_section) {
// TODO: Dynamic section
}
if (display_relocations) {
// TODO: Relocations section
}
if (display_unwind_info) {
// TODO: Unwind info
}
if (display_core_notes) {
// TODO: Core notes
}
if (display_dynamic_symbol_table || display_symbol_table) {
// TODO: dynamic symbol table
if (!interpreter_image.symbol_count()) {
printf("Dynamic symbol information is not available for displaying symbols.\n");
printf("\n");
}
}
if (display_symbol_table) {
if (interpreter_image.symbol_count()) {
printf("Symbol table '.symtab' contains %u entries:\n", interpreter_image.symbol_count());
printf(" Num: Value Size Type Bind Name\n");
interpreter_image.for_each_symbol([](const ELF::Image::Symbol& sym) {
printf(" %4u: ", sym.index());
printf("%08x ", sym.value());
printf("%08x ", sym.size());
printf("%-8s ", object_symbol_type_to_string(sym.type()));
printf("%-8s ", object_symbol_binding_to_string(sym.bind()));
printf("%s", sym.name().characters_without_null_termination());
printf("\n");
return IterationDecision::Continue;
});
}
printf("\n");
}
return 0;
}