From 149e3827350441149286cf6ba9bfa2c6fd706855 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20=22gsus=22=20Lapastora?= Date: Sat, 2 Dec 2023 10:11:39 +0100 Subject: [PATCH] LibJIT: Integrate GDB JIT Interface with ELF builders Provide a function to create an ELF image in a format GDB expects. Outside of ELF platforms this image doesn't make much sense, and in MacOS a Mach-O memory image is required: see https://chromium.googlesource.com/v8/v8.git/+/refs/heads/main/src/diagnostics/gdb-jit.cc#1802 Since GDB requires active runtime addresses for the code, copying the generated code into the image will not help. Instead, `build_gdb_image` writes the runtime addresses of the code into a NOBITS `.text` section. --- Userland/Libraries/LibJIT/CMakeLists.txt | 8 ++ Userland/Libraries/LibJIT/GDB.h | 13 +++ Userland/Libraries/LibJIT/GDBElf.cpp | 87 ++++++++++++++++++++ Userland/Libraries/LibJIT/GDBUnsupported.cpp | 14 ++++ 4 files changed, 122 insertions(+) create mode 100644 Userland/Libraries/LibJIT/GDBElf.cpp create mode 100644 Userland/Libraries/LibJIT/GDBUnsupported.cpp diff --git a/Userland/Libraries/LibJIT/CMakeLists.txt b/Userland/Libraries/LibJIT/CMakeLists.txt index 917ee72ca3e..9f496193910 100644 --- a/Userland/Libraries/LibJIT/CMakeLists.txt +++ b/Userland/Libraries/LibJIT/CMakeLists.txt @@ -2,6 +2,14 @@ set(SOURCES Assembler.cpp GDB.cpp ) +if(NOT APPLE AND NOT WIN32 AND NOT EMSCRIPTEN) + list(APPEND SOURCES GDBElf.cpp) +else() + list(APPEND SOURCES GDBUnsupported.cpp) +endif() serenity_lib(LibJIT jit) +if(NOT APPLE AND NOT WIN32 AND NOT EMSCRIPTEN) + target_link_libraries(LibJIT PRIVATE LibELF) +endif() target_link_libraries(LibJIT PRIVATE LibCore) diff --git a/Userland/Libraries/LibJIT/GDB.h b/Userland/Libraries/LibJIT/GDB.h index 5d9c97a59e2..d3717f5b719 100644 --- a/Userland/Libraries/LibJIT/GDB.h +++ b/Userland/Libraries/LibJIT/GDB.h @@ -6,10 +6,23 @@ #pragma once +#include #include +#include namespace JIT::GDB { void register_into_gdb(ReadonlyBytes data); void unregister_from_gdb(ReadonlyBytes data); + +// Build a GDB compatible image to register with the GDB JIT Interface. +// Returns Optional since the platform may not be supported. +// The `code` must be the region of memory that will be executed, since the +// image will hold direct references to addresses within the code. This way GDB +// will be able to identify the code region and insert breakpoints into it. +// Both `file_symbol_name` and `code_symbol_name` will end up in the symbol +// table of the image. They represent a file name for the image and a name for +// the region of code that is being executed. +Optional> build_gdb_image(ReadonlyBytes code, StringView file_symbol_name, StringView code_symbol_name); + } diff --git a/Userland/Libraries/LibJIT/GDBElf.cpp b/Userland/Libraries/LibJIT/GDBElf.cpp new file mode 100644 index 00000000000..49d02fc86e2 --- /dev/null +++ b/Userland/Libraries/LibJIT/GDBElf.cpp @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2023, Jesús Lapastora + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include + +namespace JIT::GDB { + +Optional> build_gdb_image(ReadonlyBytes code, StringView file_symbol_name, StringView code_symbol_name) +{ + Vector symbols; + ELF::StringTable section_names; + ELF::StringTable symbol_names; + ELF::SectionTable sections; + + auto const null_section = sections.build_null(); + + // empty name so that its name in the dump isn't confused with '.text'. + sections.header_at(null_section).sh_name = section_names.insert(""sv); + + // Build .text as a NOBITS section since the code isn't loaded inside the + // image. The image just holds the addresses for the executable region. + auto const text = sections.build_nobits([&](Elf64_Shdr& text) { + text.sh_name = section_names.insert(".text"sv); + text.sh_flags = SHF_EXECINSTR | SHF_ALLOC; + text.sh_addr = bit_cast(code.offset(0)); + text.sh_size = code.size(); + text.sh_link = 0; + text.sh_info = 0; + text.sh_addralign = 1; + text.sh_entsize = 0; + }); + + // Without this, GDB won't show the symbol names for our code. + Elf64_Sym file; + { + file.st_name = symbol_names.insert(file_symbol_name); + file.st_info = ELF64_ST_INFO(STB_GLOBAL, STT_FILE); + file.st_other = STV_DEFAULT; + file.st_shndx = SHN_ABS; + file.st_value = 0; + file.st_size = code.size(); + } + symbols.append(file); + + // The index of the first symbol that does not have a `STB_LOCAL` binding. + // Note that all non-local bindings must come before all local bindings. + auto const first_non_local_symbol_index = symbols.size(); + Elf64_Sym code_sym; + { + code_sym.st_name = symbol_names.insert(code_symbol_name); + code_sym.st_info = ELF64_ST_INFO(STB_GLOBAL, STT_FUNC); + code_sym.st_other = STV_DEFAULT; + code_sym.st_shndx = text.index, + code_sym.st_value = 0; // 0 bytes relative to .text + code_sym.st_size = code.size(); + } + symbols.append(code_sym); + + auto const strtab = symbol_names.emit_into_builder( + section_names.insert(".strtab"sv), sections); + + sections.build(symbols.span(), + [§ion_names, first_non_local_symbol_index, strtab](Elf64_Shdr& symtab) { + symtab.sh_name = section_names.insert(".symtab"sv); + symtab.sh_type = SHT_SYMTAB; + symtab.sh_flags = 0; + symtab.sh_addr = 0; + symtab.sh_info = first_non_local_symbol_index; + symtab.sh_link = strtab.raw_index(); + symtab.sh_addralign = 0; + }); + + // Make sure we find where the name for .shstrtab resides before we insert + // it into the image. + auto const shstrtab_name_index = section_names.insert(".shstrtab"sv); + auto const shstrtab = section_names.emit_into_builder( + shstrtab_name_index, sections); + + // Set the type to an "object" file, as GDB seems to request it: + // https://sourceware.org/gdb/current/onlinedocs/gdb.html/Registering-Code.html#Registering-Code + return ELF::build_elf_image(shstrtab.raw_index(), ET_REL, sections.span()); +} +} diff --git a/Userland/Libraries/LibJIT/GDBUnsupported.cpp b/Userland/Libraries/LibJIT/GDBUnsupported.cpp new file mode 100644 index 00000000000..07fd990cd13 --- /dev/null +++ b/Userland/Libraries/LibJIT/GDBUnsupported.cpp @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2023, Jesús Lapastora + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include + +namespace JIT::GDB { +Optional> build_gdb_image(ReadonlyBytes, StringView, StringView) +{ + return {}; +} +}