[lcrash/gdb] ADD: Automatically load debug symbols when lcrashkern is ready

This commit is contained in:
xwashere 2024-04-11 11:14:41 -04:00
parent 76de467bdc
commit cd237386e3
Signed by: XWasHere
GPG Key ID: 042F8BFA1B0EF93B
13 changed files with 273 additions and 3 deletions

13
.gdbinit Normal file
View File

@ -0,0 +1,13 @@
# INNNNNNNNNNNTRODUCING: the gdb unfucker 9000
printf "Setting up Lindows debug tools\n"
# We operate in MODES, modes are guessed by what we're connecting to
set $XU_LWDBG_MODE = "INACTIVE"
# Load guile code
source common/gdb/guile/core.scm
# Create guile hooks
define target hookpost-remote
lwdbg internal handle-target-remote-post
end

View File

@ -435,6 +435,8 @@ add_custom_command(OUTPUT lindows.img
BYPRODUCTS disk.sfdisk BYPRODUCTS disk.sfdisk
) )
configure_file(config.h.in config.h)
add_custom_target(lindows ALL DEPENDS lindows.img) add_custom_target(lindows ALL DEPENDS lindows.img)
add_subdirectory(executive) add_subdirectory(executive)

5
README
View File

@ -1,3 +1,8 @@
== STYLE ==
=== GUILE ===
- Avoid direct use of (execute)
== NAMING == == NAMING ==
Mph: Metaprogramming helpers Mph: Metaprogramming helpers

150
common/gdb/guile/core.scm Normal file
View File

@ -0,0 +1,150 @@
(use-modules (gdb)) ; import gdb libraries
; == Misc libraries ==
(use-modules (srfi srfi-9)) ; provides define-record-type
(use-modules (ice-9 string-fun)) ; provides string-replace-substring
; Types
(define btp-u8 (lookup-type "unsigned char"))
(define btp-u16 (lookup-type "unsigned short"))
(define btp-u32 (lookup-type "unsigned int"))
(define btp-u64 (lookup-type "unsigned long long"))
(define btp-i8 (lookup-type "char"))
(define btp-i16 (lookup-type "short"))
(define btp-i32 (lookup-type "int"))
(define btp-i64 (lookup-type "long long"))
(define btp-c8 (lookup-type "char"))
(define btp-bool btp-u8)
(define btp-uptr btp-u64)
; == Execute wrappers ==
; Be careful modifying this, other commands depend on it for multiline commands
(define gdbw-python (lambda (code) (execute (format #f "python ~a" code) #:to-string #t)))
(define gdbw-remove-symbol-file (lambda (file) (execute (format #f "remove-symbol-file ~s" file))))
(define gdbw-add-symbol-file (lambda* (file #:key (offset '()))
(define cmd (format #f "add-symbol-file ~s" file))
(when (number? offset) (set! cmd (string-append cmd (format #f " -o ~a" offset))))
(execute cmd)))
; HACK: gdb doesn't support multiline execute for guile, so we use execute to call python's execute
(define gdbw-commands (lambda (breakpoints cmds)
(for-each (lambda (breakpoint)
(define gdb-breakpoint breakpoint)
(when (breakpoint? gdb-breakpoint) (set! gdb-breakpoint (breakpoint-number gdb-breakpoint)))
; HACK: This is a silly hack that abuses an unchecked cast from unsigned numbers to signed ones in the gdb shell, allowing us to bypass the "no negative numbers!" thing
(when (< gdb-breakpoint 0) (set! gdb-breakpoint (+ 4294967295 (+ 1 gdb-breakpoint))))
(gdbw-python (format #f "gdb.execute(~s)" (format #f "commands ~a\n~a\nend" gdb-breakpoint (string-join cmds "\n")))))
breakpoints)))
; HACK: gdb doesn't support connection data for guile, so we have to implement it ourself with this incredible hack
(define-record-type <gdbw-info-connections-result>
(make-gdbw-info-connections-result current number what description)
gdbw-info-connections-result?
(current gdbw-info-connections-result-current)
(number gdbw-info-connections-result-number)
(what gdbw-info-connections-result-what)
(description gdbw-info-connections-result-description))
(define gdbw-info-connections (lambda () ; TODO: this looks awful, i'm positive there's a better way to do a lot of this, come back when i understand scheme
(define compact-gdb-table-row (lambda (raw)
(define crunch (lambda (pos chunks remain)
(if (< pos (string-length remain))
(let ((nidx (string-index remain #\space pos)))
(if (< (+ nidx 1) (string-length remain))
(if (char=? (string-ref remain (+ nidx 1)) #\space)
(let ((fidx (string-skip remain #\space nidx)))
(crunch 0 (append chunks (list (substring remain 0 nidx))) (substring remain fidx)))
(crunch (+ nidx 1) chunks remain))
(append chunks (list (substring remain 0 (- (string-length remain) 1))))))
(append chunks (list (substring remain 0 (- (string-length remain) 1)))))))
(crunch 0 '() raw)))
(map (lambda (l) (let ((crow (compact-gdb-table-row l)))
(make-gdbw-info-connections-result
(char=? (string-ref (car crow) 0) #\*)
(string->number (substring (car crow) 2))
(car (cdr crow))
(car (cdr (cdr crow))))))
(list-head (list-tail (string-split (execute "info connections" #:to-string #t) #\newline) 1) 1))))
; == Debugging mode stuff ==
(define DEBUG_MODE_KERNEL 'KERNEL)
(define DEBUG_MODE_INVALID 'INVALID)
(define DEBUG_MODE_NONE 'NONE)
(define debug-mode DEBUG_MODE_NONE)
(define debug-mode-transition (lambda (mode) ; Reconfigure internal state to debug a new target
; Check for major fuckups
(when (equal? mode DEBUG_MODE_INVALID) (error "Transitioned into an invalid debug mode, this should never happen!"))
; Deinitialize the previous target's debug code
(cond
((equal? debug-mode DEBUG_MODE_KERNEL)
(delete-breakpoint! kernel-debug-table-update-watcher)))
; Set the new debug mode
(set! debug-mode mode)
; Initialize the current target's debug code
(cond
((equal? debug-mode DEBUG_MODE_KERNEL)
(register-breakpoint! kernel-debug-table-update-watcher)
(gdbw-commands (list kernel-debug-table-update-watcher) '("silent" "lwdbg kernel refresh-debug-tables" "continue"))
(process-kernel-debug-tables)))))
; == Code for processing data in kernel debug tables ==
(define cckernel-binary-location '())
(define cckernel-binary-offset '())
(define kernel-binary-location '())
(define kernel-binary-offset '())
(define kernel-debug-table-update-watcher (make-breakpoint "*(0x100000)"
#:type BP_WATCHPOINT
#:wp-class WP_WRITE
#:internal #t))
(define process-kernel-debug-tables (lambda ()
(define build-dir (value-dereference (value-cast (make-value #x100008) (type-pointer (type-pointer btp-c8)))))
(when (not (value=? build-dir (make-value 0)))
(set! build-dir (value->string build-dir))
(if (value=? (value-dereference (value-cast (make-value #x100010) (type-pointer btp-bool))) 1)
((lambda () (define new-cckernel-binary-offset (value->integer (value-dereference (value-cast (make-value #x100018) (type-pointer btp-uptr)))))
(define new-cckernel-binary-location (string-append build-dir "/lcrash/cckernel"))
(when (not (null? cckernel-binary-location))
(gdbw-remove-symbol-file cckernel-binary-location)
(set! cckernel-binary-location '())
(set! cckernel-binary-offset '()))
(gdbw-add-symbol-file new-cckernel-binary-location #:offset new-cckernel-binary-offset)
(set! cckernel-binary-location new-cckernel-binary-location)
(set! cckernel-binary-offset new-cckernel-binary-offset)))
(display "TODO: CCKERNEL UNLOADED\n"))
(if (value=? (value-dereference (value-cast (make-value #x100020) (type-pointer btp-bool))) 1)
((lambda () (define new-kernel-binary-offset (value->integer (value-dereference (value-cast (make-value #x100028) (type-pointer btp-uptr)))))
(define new-kernel-binary-location (string-append build-dir "/lcrash/lcrashkern"))
(when (not (null? kernel-binary-location))
(gdbw-remove-symbol-file kernel-binary-location)
(set! kernel-binary-location '())
(set! kernel-binary-offset '()))
(gdbw-add-symbol-file new-kernel-binary-location #:offset new-kernel-binary-offset)
(set! kernel-binary-location new-kernel-binary-location)
(set! kernel-binary-offset new-kernel-binary-offset)))
(display "TODO: KERNEL UNLOADED\n")))))
; == Command prefixes ==
(register-command! (make-command "lwdbg" #:prefix? #t))
(register-command! (make-command "lwdbg internal" #:prefix? #t))
(register-command! (make-command "lwdbg kernel" #:prefix? #t))
; Called by gdb hookpost for target remote
(register-command! (make-command "lwdbg internal handle-target-remote-post"
#:invoke (lambda (self arg from-tty)
(debug-mode-transition
(let ((target (string-split (gdbw-info-connections-result-what (car (filter gdbw-info-connections-result-current (gdbw-info-connections)))) #\space)))
(if (string=? (car target) "remote")
DEBUG_MODE_KERNEL
DEBUG_MODE_INVALID))))))
; Command to forcefully refresh the thing
(register-command! (make-command "lwdbg kernel refresh-debug-tables"
#:command-class COMMAND_USER
#:doc "Refresh the kernel debug tables"
#:invoke (lambda (self arg from-tty) (process-kernel-debug-tables))))

3
config.h.in Normal file
View File

@ -0,0 +1,3 @@
#pragma once
#define LW_CONFIG_PROJECT_OUTPUT_DIR "@CMAKE_BINARY_DIR@"

View File

@ -19,6 +19,7 @@ set_property(TARGET lcrashkern PROPERTY LINK_DEPENDS
set_property(TARGET lcrashkern PROPERTY LINK_OPTIONS -nostdlib -Wl,-T,${CMAKE_CURRENT_SOURCE_DIR}/lcrash.ld ) set_property(TARGET lcrashkern PROPERTY LINK_OPTIONS -nostdlib -Wl,-T,${CMAKE_CURRENT_SOURCE_DIR}/lcrash.ld )
target_compile_options(lcrashkern PRIVATE -ggdb -fpic -pie -Wall -fanalyzer) target_compile_options(lcrashkern PRIVATE -ggdb -fpic -pie -Wall -fanalyzer)
target_link_options(lcrashkern PRIVATE -fpic -pie) target_link_options(lcrashkern PRIVATE -fpic -pie)
target_include_directories(lcrashkern PRIVATE ${CMAKE_BINARY_DIR} ${CMAKE_SOURCE_DIR})
add_custom_command( add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/lcrashkern.gz OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/lcrashkern.gz
@ -54,6 +55,7 @@ set_property(TARGET cckernel PROPERTY LINK_DEPENDS
) )
set(COMPRESSED_KERNEL_PATH ${CMAKE_CURRENT_BINARY_DIR}/lcrashkern.gz) set(COMPRESSED_KERNEL_PATH ${CMAKE_CURRENT_BINARY_DIR}/lcrashkern.gz)
configure_file(setup/compressed/cckernel.S.in setup/compressed/cckernel.S) configure_file(setup/compressed/cckernel.S.in setup/compressed/cckernel.S)
target_include_directories(cckernel PRIVATE ${CMAKE_BINARY_DIR} ${CMAKE_SOURCE_DIR})
# This is the actual bzImage used by linux/grub # This is the actual bzImage used by linux/grub
lw_add_executable(lcrash lw_add_executable(lcrash

View File

@ -11,6 +11,8 @@
bool AcpiIsPresent = false; bool AcpiIsPresent = false;
struct AcpiRSDP* AcpiTheRSDP = 0; struct AcpiRSDP* AcpiTheRSDP = 0;
struct AcpiXSDT* AcpiTheXSDT = 0;
u32 AcpiXSDTLength = 0;
void AcpiInitialize() { void AcpiInitialize() {
// Check boot params // Check boot params
@ -42,6 +44,15 @@ found: // Is this a valid ACPI table?
for (int i = 0; i < sizeof(struct AcpiRSDP); i++) Sum += ((u8*)AcpiTheRSDP)[i]; for (int i = 0; i < sizeof(struct AcpiRSDP); i++) Sum += ((u8*)AcpiTheRSDP)[i];
if (Sum != 0) goto fail; if (Sum != 0) goto fail;
// Grab the XSDT
AcpiTheXSDT = AcpiTheRSDP->XSDTAddress;
// Is this a valid XSDT?
if (!CompareMemory(AcpiTheXSDT->Header.Signature, "XSDT", 4)) goto fail;
// Get the XSDT length
AcpiXSDTLength = (AcpiTheXSDT->Header.Length - sizeof(struct AcpiXSDT)) / 8;
AcpiIsPresent = true; AcpiIsPresent = true;
return; return;
@ -51,3 +62,15 @@ fail: AcpiIsPresent = false;
bool AcpiPresent() { return AcpiIsPresent; } bool AcpiPresent() { return AcpiIsPresent; }
struct AcpiRSDP* AcpiGetRSDP() { return AcpiTheRSDP; } struct AcpiRSDP* AcpiGetRSDP() { return AcpiTheRSDP; }
struct AcpiXSDT* AcpiGetXSDT() { return AcpiTheXSDT; }
u32 AcpiGetXSDTLength() { return AcpiXSDTLength; }
void* AcpiGetTable(const char TableName[4]) {
// Let's not fuck this up this early
if (!AcpiPresent()) return NULL;
// Alright let's see here...
for (int i = 0; i < AcpiXSDTLength; i++) {
}
}

View File

@ -56,3 +56,23 @@ void AcpiInitialize();
* Check if ACPI is present * Check if ACPI is present
*/ */
bool AcpiPresent(); bool AcpiPresent();
/**
* Get the ACPI RSDP, only call if ACPI is present
*/
struct AcpiRSDP* AcpiGetRSDP();
/**
* Get the ACPI XSDT, only call if ACPI is present
*/
struct AcpiXSDT* AcpiGetXSDT();
/**
* Get the ACPI XSDT entry count, only call if ACPI is present
*/
u32 AcpiGetXSDTLength();
/**
* Get the ACPI table with the specified name, returns NULL if not found
*/
void* AcpiGetTable(const char TableName[4]);

View File

@ -1,5 +1,7 @@
#pragma once #pragma once
#include "../types.h"
#define DEFINE_GDB_PY_SCRIPT(name) \ #define DEFINE_GDB_PY_SCRIPT(name) \
asm("\ asm("\
.pushsection \".debug_gdb_scripts\", \"MS\", @progbits, 1\n\ .pushsection \".debug_gdb_scripts\", \"MS\", @progbits, 1\n\
@ -7,3 +9,26 @@
.asciz \""name"\"\n\ .asciz \""name"\"\n\
.popsection \n\ .popsection \n\
"); ");
/// Fixed data block placed at the address 0x100000
/// Please update the initialization code in /lcrash/setup/compressed/main.c when this is changed
struct GdbDataBlock {
/// Update tick thing stored at the beginning of the fixed data block for debuggers
/// TODO: Optimization may break this, should this be volatile?
u64 Update;
/// Base of the cmake build directory. This is needed for lwdbg to configure itself
c8* BuildDirectory;
/// cckernel is present
bool CCKernelLoaded;
/// Base address of the cckernel
void* CCKernelBase;
/// Full kernel is present
bool KernelLoaded;
/// Base address of the kernel
void* KernelBase;
};

View File

@ -1,6 +1,8 @@
/// lcrash entry point /// lcrash entry point
/// super awesome... /// super awesome...
#include <lcrash/gdb/gdb.h>
#include "types.h" #include "types.h"
#include "lnxboot.h" #include "lnxboot.h"
@ -11,6 +13,12 @@ void entry64(struct boot_params* BootParams) {
BootBootParams = BootParams; BootBootParams = BootParams;
BootSetupInfo = (void*)BootParams + 0x1f1; BootSetupInfo = (void*)BootParams + 0x1f1;
// Notify the debugger that we're ready
struct GdbDataBlock* GdbDataBlock = (struct GdbDataBlock*)0x100000;
GdbDataBlock->KernelLoaded = true;
GdbDataBlock->KernelBase = BootSetupInfo->code32_start;
GdbDataBlock->Update++;
// Initialize EFI code if we had EFI // Initialize EFI code if we had EFI
EfiInitialize(); EfiInitialize();

View File

@ -7,6 +7,10 @@ SECTIONS {
*(.text) *(.text)
} }
.rodata : {
*(.rodata)
}
.cckernel : { .cckernel : {
*(.cckernel) *(.cckernel)
} }

View File

@ -1,4 +1,6 @@
#include "../../lnxboot.h" #include "../../lnxboot.h"
#include "../../gdb/gdb.h"
#include <config.h>
#include "gzip.h" #include "gzip.h"
#include "elf.h" #include "elf.h"
@ -11,8 +13,18 @@ extern void* _CC_ecckernel;
void ccmain(void* imgbase, struct boot_params* boot_params) { void ccmain(void* imgbase, struct boot_params* boot_params) {
struct setup_info* setup_info = (struct setup_info*)((void*)boot_params + 0x1f1); struct setup_info* setup_info = (struct setup_info*)((void*)boot_params + 0x1f1);
// Fill the lcrash debug data block
struct GdbDataBlock* GdbDataBlock = (struct GdbDataBlock*)0x100000;
GdbDataBlock->BuildDirectory = LW_CONFIG_PROJECT_OUTPUT_DIR + (uptr)imgbase;
GdbDataBlock->CCKernelLoaded = true;
GdbDataBlock->CCKernelBase = imgbase;
GdbDataBlock->KernelLoaded = false;
GdbDataBlock->KernelBase = NULL;
GdbDataBlock->Update++;
// Determine the kernel load address
char* kernel_load_addr = (char*)setup_info->code32_start; char* kernel_load_addr = (char*)setup_info->code32_start;
if (kernel_load_addr == 0) kernel_load_addr = (char*)0x100000; if (kernel_load_addr == 0) kernel_load_addr = (char*)0x101000;
char* cckernel = imgbase + (uptr)&_CC_cckernel; char* cckernel = imgbase + (uptr)&_CC_cckernel;
//for (long i = 0; i < setup_info->payload_length; i++) { //for (long i = 0; i < setup_info->payload_length; i++) {

View File

@ -27,3 +27,6 @@ typedef u8 bool;
#else #else
#error What the fuck? [pointers are not 4 or 8 bytes long] #error What the fuck? [pointers are not 4 or 8 bytes long]
#endif #endif
/// Null may become part of the nullptr type, so it's defined here
#define NULL 0