Loading an ELF File into Qemu - debugging

I have an .elf file created for a cortex-m3 processor. I want to run this in Qemu.
The .elf should start execution with this assembly file:
.thumb
.syntax unified
.global ResetHandler
ResetHandler:
LDR SP, =stack_top
NOP
BL main
B .
the associated linker script:
ENTRY(ResetHandler)
SECTIONS {
. = 0x08000000;
.startup : { startup.o(.text) }
.text : { *(.text) }
. = 0x20000000;
__bss_start__ = .;
.bss : { *(.bss) }
__bss_end__ = .;
.data : { *(.data) }
. = . + 0x100;
stack_top = .;
}
If I run the following command:
qemu-system-arm -s -S -machine stm32vldiscovery -cpu cortex-m3 -nographic -kernel myfile.elf
Qemu starts up and halts (as it should). However, when I connect gdb like so...
arm-none-eabi-gdb
(gdb) file myfile.elf
Reading symbols from myfile.elf...
(gdb) target remote localhost:1234
Remote debugging using localhost:1234
0xf002bf00 in ?? ()
(gdb) si
0x200001f8 in stack_top ()
You can see that GDB doesn't understand the .elf file. If I step through this, Qemu interprets my assembly language incorrectly and it will error and exit. But if I load the .elf file in GDB...
(gdb) load myfile.elf
Start address 0x08000000, load size 21891
Transfer rate: 16 KB/sec, 266 bytes/write.
(gdb) si
ResetHandler () at startup.s:7
7 NOP
(gdb) si
8 BL main
You can see that the .elf file is loaded correctly and can be stepped through.
My overall questions are:
What is load doing? The docs state:
Where it exists, it is meant to make filename (an executable) available for debugging on the remote system
But that is not clear to me. How assembly code is being executed changes, so I have to imagine "making a file available for debugging" is doing quite a bit.
edit (adding compilation steps and versions):
assembly and compilation...
arm-none-eabi-as -mcpu=cortex-m3 startup.s -g -o startup.o
arm-none-eabi-gcc \
-Tcortex-m3-tests.ld \
-mcpu=cortex-m3 \
-mthumb \
mysrcfile.c \
-g -o myfile.elf
versions...
qemu-system-arm --version
QEMU emulator version 6.2.0
Copyright (c) 2003-2021 Fabrice Bellard and the QEMU Project developers
arm-none-eabi-gcc --version
arm-none-eabi-gcc (GNU Toolchain for the Arm Architecture 11.2-2022.02 (arm-11.14)) 11.2.1 20220111
Copyright (C) 2021 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

What is happening here is that your ELF file does not include a valid exception vector table at the correct address, and QEMU requires one. gdb is correctly showing you the results of the emulated CPU crashing as a result.
When QEMU starts for an M-profile Arm CPU, it tries to load the starting PC and SP values out of the vector table (this is how real hardware M-profile CPUs start). On this particular board, the vector table is at address 0x0000_0000, and address 0x0800_0000 is an alias for this. The initial SP and PC are at word offsets 0 and 1 in the table; it happens that your object file has words 0xd008f8df and 0xf002bf00 at those offsets, and you can see in gdb that gdb is correctly telling you that the initial PC is that bogus 0xf002bf00 value.
When you single-step, QEMU tries to load from 0xf002bf00, which has no memory there. It therefore takes a BusFault exception, which at this point in M-profile startup will always escalate to HardFault. That's exception number 3, whose entry point is stored at offset 3 in the vector table. As it happens with the way you've written your assembly, the word there is the address of stack_top, so QEMU will try to execute from there next. Since that's data and not a valid instruction, it goes downhill from there -- we will take another exception, which results in the CPU going into the Lockup state, which is fatal. You can see some of this if you tell QEMU to execute without talking to gdb and with some extra debug logging:
$ qemu-system-arm -machine stm32vldiscovery -cpu cortex-m3 -display none -serial stdio -kernel myfile.elf -d in_asm,cpu,exec,int
Taking exception 3 [Prefetch Abort] on CPU 0
...with CFSR.IACCVIOL
...BusFault with BFSR.STKERR
...taking pending nonsecure exception 3
----------------
IN:
0x20000558: 08000079 stmdaeq r0, {r0, r3, r4, r5, r6}
Trace 0: 0x7fd68be0e100 [00000401/20000558/00000130/ff000000]
R00=00000000 R01=00000000 R02=00000000 R03=00000000
R04=00000000 R05=00000000 R06=00000000 R07=00000000
R08=00000000 R09=00000000 R10=00000000 R11=00000000
R12=00000000 R13=d008f8b8 R14=fffffff9 R15=20000558
XPSR=40000003 -Z-- A handler
Taking exception 18 [v7M INVSTATE UsageFault] on CPU 0
qemu: fatal: Lockup: can't escalate 3 to HardFault (current priority -1)
R00=00000000 R01=00000000 R02=00000000 R03=00000000
R04=00000000 R05=00000000 R06=00000000 R07=00000000
R08=00000000 R09=00000000 R10=00000000 R11=00000000
R12=00000000 R13=d008f8b8 R14=fffffff9 R15=20000558
XPSR=40000003 -Z-- A handler
FPSCR: 00000000
Aborted (core dumped)
(You can't see all of the steps I describe above, you have to infer them, because QEMU doesn't currently log all the exception table loads or the initial PC/SP values. But you can see the BusFault, the attempt to execute at 0x20000558, the second exception and the Lockup. I've submitted some QEMU patches which improve the logging a little so that QEMU 7.0 and up should print the PC values being loaded from the vector table.)
The difference when you use the gdb 'load' command is that gdb both downloads the ELF file data into memory and also sets the initial PC value to the ELF file's entry-point address. So execution starts at 08000000 and continues from there.
Anyway, the way to fix this is to make sure the start of your ELF file that gets loaded at address 0 (or the 0x0800_0000 alias to 0) has a valid vector table. For an example look at https://git.linaro.org/people/peter.maydell/semihosting-tests.git/tree/start-microbit.S or https://git.linaro.org/people/peter.maydell/m-profile-tests.git/tree/init-m.S for instance.

Related

Bypass ptrace anti-debugging trick

I'm having some trouble bypassing calls to ptrace when debugging a 32-bit Linux executable.
I have this binary: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.26, BuildID[sha1]=b1579d4c55e90110491da76331c9a158b77a5729, stripped
and i have been trying to debug it. So due to it is a stripped binary i located the entry point using gdb.
gef➤ info file
Entry point: 0x804849c
.
.
.
other stuff
Once I got to the entry point, I printed out the next instructions:
gef➤ x/15i 0x804849c
0x804849c: xor ebp,ebp
0x804849e: pop esi
0x804849f: mov ecx,esp
0x80484a1: and esp,0xfffffff0
0x80484a4: push eax
0x80484a5: push esp
0x80484a6: push edx
0x80484a7: push 0x8048580
0x80484ac: push 0x8048590
0x80484b1: push ecx
0x80484b2: push esi
0x80484b3: push 0x8048480
0x80484b8: call 0x8048350 <__libc_start_main#plt>
0x80484bd: hlt
0x80484be: nop
I know 0x8048480 is the address of the main function. Now i placed a breakpoint at main (0x8048480) and then ran the program but i can't step into the breakpoint and the program exit with code 01. Therefore i decided to run strace ./binary command and ptrace calls inhibit me from debugging any further:
ptrace(PTRACE_TRACEME) = -1 EPERM (Operation not permitted)
To bypass this, i tried to use the LD_PRELOAD environment variable.
Hence i created a simple .c file:
long ptrace(int request, int pid, void *addr, void *data) {
return 0;
}
And compiled it as a shared library with the following command:
gcc -fPIC -shared -m32 ptrace.c -o ptrace.so
Here the output of the command file ptrace.so
ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), dynamically linked, BuildID[sha1]=a9de523da44299f76ad94373a07c9c5f6f3c76db, not stripped
Next I set the environment variable LD_PRELOAD in the shell using export LD_PRELOAD=./ptrace.so command first, and then within gdb set environment LD_PRELOAD=./ptrace.so
but this is the output:
ERROR: ld.so: object './ptrace.so' from LD_PRELOAD cannot be preloaded (wrong ELF class: ELFCLASS32): ignored.
How can i bypass the ptrace call??
Thank you.
To bypass this, i tried to use the LD_PRELOAD environment variable.
If the application is employing anti-debugging tricks, it is exceedingly likely that it executes ptrace system call directly, rather than via libc wrapper. LD_PRELOAD will not work in that case.
This command:
gcc -shared ptrace.c -o ptrace.so
builds a 64-bit binary on a 64-bit system. You want:
gcc -fPIC -shared -m32 ptrace.c -o ptrace.so

How to solve qemu gdb debug error: Remote 'g' packet reply is too long?

I'm currently getting into bootloaders and kernel development (very much beginning)
I'm following a combination of
https://www.cs.bham.ac.uk/~exr/lectures/opsys/10_11/lectures/os-dev.pdf
and the code found in
https://github.com/cfenollosa/os-tutorial
The only thing im doing differently is that I'm targeting x86_64 instead of i386. Also I'm using qemu to emulate (qemu-system-x86_64).
Now after following the GitHub repo to part 16-video-driver I get stuck as the screen driver does print some stuff to the screen but something's going on with a misalignment of data or something. So next I wanted to try to debug my program. This is also covered in part 14-checkpoint of the repo. So I built gdb for target x86_64-elf. But when I try to run qemu and gdb using system-qemu-x86_64 -s -S -fda os-image
and then just run gdb and try to connect to qemu by running target remote localhost:1234, as soon as I run that I get the following error message
Remote debugging using localhost:1234
warning: No executable has been specified and target does not support
determining executable automatically. Try using the "file" command.
Remote 'g' packet reply is too long (expected 308 bytes, got 536 bytes):
000000000000000000000000000000000000000000000000630600000000000000000000
000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000f0ff0000000000000200000000f00000
000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000007f03000000000000
000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000801f0000
Any ideas on what I'm missing / doing wrong? If more information is needed please let me know.
Thanks
EDIT:
I've applied the patch mentioned by #MichaelPetch and now the g packet error is gone. However it looks like gdb can't interpret my executable as after running target remote localhost:1234 and then symbol-file kernel.elf, the terminal now returns
Remote debugging using localhost:1234 warning: No executable has been
specified and target does not support determining executable automatically.
Try using the "file" command. 0x0000fb38 in ?? ()
I am however able to set breakpoints on functions and line numbers. But when trying to print variables that should be available at the current location using print terminal_buffer I get
No symbol "terminal_buffer" in current context. terminal_buffer being a variable declared in the current scope.
However when I print a variable declared outside the scope of the function in which I have put my breakpoint, an int for example, print does return a value but the value is 0 (I presume that is the initial value of the type), however it should already have been set to a new value according to my code. Also when trying next it returns Cannot find bounds of current function which leads to me thinking it's not able to interpret some part.
In my bootloader I change into protected protected mode to run the 64-bit kernel using this method:
[bits 16]
switch_to_pm:
cli ; 1. disable interrupts
lgdt [gdt_descriptor] ; 2. load the GDT descriptor
mov eax, cr0
or eax, 0x1 ; 3. set 32-bit mode bit in cr0
mov cr0, eax
jmp CODE_SEG:init_pm ; 4. far jump by using a different segment
[bits 32]
init_pm: ; we are now using 32-bit instructions
mov ax, DATA_SEG ; 5. update the segment registers
mov ds, ax
mov ss, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ebp, 0x90000 ; 6. update the stack right at the top of the free space
mov esp, ebp
call BEGIN_PM ; 7. Call a well-known label with useful code
Any thoughts?
The reason for all the issues is that you are compiling 64-bit code and running it in 32-bit protected mode. 64-bit code will not run in that environment properly. Oddly enough it often manifests itself at first when trying to write to the video display. Often things will print but not quite the way you want. Improperly decoded instructions will cause the debugger to work erratically as you observed.
One way to fix your problem is to compile and link the kernel as a 32-bit executable. You are using a 64-bit compiler so you'll need to add -m32 CFLAGS (or your GCC command line). if using LD to link -melf_i386 will be needed. Assembling with NASM should be -felf32 rather than -felf64.
Alternatively you would have to place the processor into 64-bit long mode in the bootloader. You can read more about that process on the OSDev wiki.
If debugging 32-bit code you will probably want to use qemu-system-i386 . You will have fewer hassles.
Connect and disconnect
I got it working as detailed at: How to debug the Linux kernel with GDB and QEMU?
The key thing was connect and disconnect on GDB as:
gdb \
-ex "add-auto-load-safe-path $(pwd)" \
-ex "file vmlinux" \
-ex 'set arch i386:x86-64:intel' \
-ex 'target remote localhost:1234' \
-ex 'break start_kernel' \
-ex 'continue' \
-ex 'disconnect' \
-ex 'set arch i386:x86-64' \
-ex 'target remote localhost:1234'
Related: Remote 'g' packet reply is too long

Debugging Linux Kernel using GDB in qemu unable to hit function or given address

I am trying to understand kernel bootup sequence step by step using GDB in qemu environment.
Below is my setting:
In one terminal im running
~/Qemu_arm/bin/qemu-system-arm -M vexpress-a9 -dtb ./arch/arm/boot/dts/vexpress-v2p-ca9.dtb -kernel ./arch/arm/boot/zImage -append "root=/dev/mmcblk0 console=ttyAMA0" -sd ../Images/RootFS.ext3 -serial stdio -s -S
In other terminal
arm-none-linux-gnueabi-gdb vmlinux
Reading symbols from vmlinux...done.
(gdb) target remote :1234
Remote debugging using :1234
0x60000000 in ?? ()
My question is how setup breakpoint for the code in /arch/arm/boot/compressed/* files .
e.g I tried to setup break point for decompress_kernel defined in misc.c .
Case 1:
(gdb) b decompress_kernel
Function "decompress_kernel" not defined.
Make breakpoint pending on future shared library load? (y or [n]) y
Breakpoint 2 (decompress_kernel) pending.
(gdb) c
Continuing.
The above one is not able to hit the function qemu is booting.
Case 2:
(gdb) b *0x80008000
Breakpoint 1 at 0x80008000: file arch/arm/kernel/head.S, line 89.
(gdb) c
Continuing.
In this case also its not able to hit instead qemu is booting up.
Case 3:
(gdb) b start_kernel
Breakpoint 1 at 0x8064d8d8: file init/main.c, line 498.
(gdb) c
Continuing.
Breakpoint 1, start_kernel () at init/main.c:498
498 {
(gdb)
In this case function is hitting and i am able debug step by step.
Note: I have enabled debug,Early printk and tried hbreak
So my query is:
why some functions are not able to hit break points?
Is this qemu limitation or do I need enable something more?
do I need to append any extra parameters?
how to Debug early kernel booting
You are not able to put breakpoints on any function preceding start_kernel because you are not loading symbols for them. In fact you are starting qemu with a zImage of the kernel but loading the symbols from vmlinux. They are not the same: zImage is basically vmlinux compressed as a data payload which is then attached to a stub which decompresses it in memory then jumps to start_kernel.
start_kernel is the entry point of vmlinux, any function preceding it, including decompress_kernel, are part of the stub and not present in vmlinux.
I don't know if doing "arm-none-linux-gnueabi-gdb zImage" instead allows you to debug the stub, I have always done early debug of ARM kernels with JTAG debuggers on real hardware, and never used qemu for that, sorry

gdb ignores breakpoint in Qemu bootloader

I am trying to step through the simple bootloader shown in this tutorial: http://mikeos.berlios.de/write-your-own-os.html - so I can use the Qemu monitor to inspect the general registers for educational purposes.
Eventhough I am able to connect Qemu and gdb and the breakpoint is set at the beginning of the bootloader (0x7c0), after hitting "c" on gdb the code just runs all the way till the end.
I have read kvm may "confuse" gbd with virtual memory addresses, so I disabled it. This didn't work.
I also read (Debugging bootloader with gdb in qemu) things worked when debugging Freedos boot after compiling gdb from HEAD. Instead of recompiling gdb, I tried debugging the Freedos boot - It worked!
So, I do believe my problem is actually getting the tutorial's bootloader to go through a step-by-step execution.
Other things I tried (none of them worked):
Use dozens of "si" before inserting the breakpoint
Try different breakpoint addresses
Use the -singlestep key on qemu
Here is my qemu command line:
qemu-system-i386 -fda disquete.img -boot a -s -S -monitor stdio
Here is my command sequence inside gdb:
(gdb) target remote localhost:1234
(gdb) set architecture i8086
(gdb) br *0x7c0
Then I hit "c" and it just passes the breakpoint all the way.
Versions:
$ uname -a
Linux Brod 3.8.0-30-generic #44-Ubuntu SMP Thu Aug 22 20:52:24 UTC 2013 x86_64 x86_64 x86_64 GNU/Linux
$ gdb --version
GNU gdb (GDB) 7.5.91.20130417-cvs-ubuntu
$ qemu --version
QEMU emulator version 1.4.0 (Debian 1.4.0+dfsg-1expubuntu4), Copyright (c) 2003-2008 Fabrice Bellard
As I am able to step through the Freedos boot, I do believe my setup is fine and I must be failing within some conceptual misunderstanding of the boot process for the bootloader tutorial I mentioned in the beginning of this post.
All help is welcome!
Because of hardware virtualization, it may be necessary to use a hardware breakpoint:
(gdb) hbreak *0x7c00
Also watch out for the correct architecture in gdb, even when using a 64-bit CPU (or kvm): The bootloader needs (gdb) set architecture i8086 as the CPU is still in real mode.
I was actually able to debug the sample bootloader I took from mikeos.berlios.de/write-your-own-os.html after rewriting it to specifically load at 0x7c00. My sources of information (other than the contributions here) were:
http://en.wikibooks.org/wiki/X86_Assembly/Bootloaders
http://viralpatel.net/taj/tutorial/hello_world_bootloader.php
The final code is this:
[BITS 16] ; Tells nasm to build 16 bits code
[ORG 0x7C00] ; The address the code will start
start:
mov ax, 0 ; Reserves 4Kbytes after the bootloader
add ax, 288 ; (4096 + 512)/ 16 bytes per paragraph
mov ss, ax
mov sp, 4096
mov ax, 0 ; Sets the data segment
mov ds, ax
mov si, texto ; Sets the text position
call imprime ; Calls the printing routine
jmp $ ; Infinite loop
texto db 'It works! :-D', 0
imprime: ; Prints the text on screen
mov ah, 0Eh ; int 10h - printing function
.repeat:
lodsb ; Grabs one char
cmp al, 0
je .done ; If char is zero, ends
int 10h ; Else prints char
jmp .repeat
.done:
ret
times 510-($-$$) db 0 ; Fills the remaining boot sector with 0s
dw 0xAA55 ; Standard boot signature
Now I can step through the program and see the registers changing.

programming with NASM in Windows XP

I have the following code which assembles and runs fine on Windows XP 32 bit, 2.09.08 NASM:
; how to compile: nasm -f elf test.asm
; how to link: ld -o test.exe test.o
section .data
section .text
;global _WinMain#16
;_WinMain#16:
;global _start
_start:
mov ax,4
jmp $
According to many tutorials on NASM the asm file needs the following in it:
global _WinMain#16
_WinMain#16:
...
As you can see my asm file doesn't have that in it. (it's commented out, All it has is _start). So what is with all of these tutorials mentioning the need for the global _WinMain#16 stuff when my assembly program doesn't have that and works?
this is the command to assemble: nasm -f elf test.asm
this is the command to link: ld -o test.exe test.o
There are several types of application on Windows with different entry points depending on which type they are. By link.exe option:
/SUBSYSTEM:CONSOLE - requires main and linking with msvcrXX.dll. These applications run in console windows; if you aren't running an instance of cmd.exe, one will be opened.
/SUBSYSTEM:WINDOWS - WinMain is the starting point. See here. Usually in C, these #include <windows.h> and are linked directly to kernel32.dll. These a gui apps and are almost definitely linked with user32.dll and possibly advapi32.dll as well.
/SUBSYSTEM:NATIVE - there are two types of application here; drivers and applications. Native NT apps run during windows startup and require NtProcessSStartup as an entry point. There is no libc in native applications. Drivers are different again.
A full list of supported windows subsystems by link.exe is available here.
_start is the symbol windows will actually start your code running at. Normally, libc or the like actually handles _start and does some initial setup, so your program doesn't actually quite start at _main. If you wanted to link with libc you would have problems, since you'd have conflicting symbols with the libc library. If however you never intend to call any functions that are part of the C or C++ standard libraries, you are ok using _start.
Edit yikes I've just noticed this:
; how to compile: nasm -f elf test.asm
; how to link: ld -o test.exe test.o
I assume you're not using the -f elf one. ELF (executable and linkable format) is the linux format for executables; Windows requires Portable Executable (PE) images. The nasm option is -f win32, or for dos nasm -f coff.
Edit 2 just to check, I assembled the code and disassembled it again. I also used mingw. Anyway, I got:
SECTION .text align=16 execute ; section number 1, code
Entry_point:; Function begin
; Note: Length-changing prefix causes delay on Intel processors
mov ax, 4 ; 00401000 _ 66: B8, 0004
?_001: jmp ?_001 ; 00401004 _ EB, FE
; Entry_point End of function
; Note: Length-changing prefix causes delay on Intel processors
mov ax, 4 ; 00401006 _ 66: B8, 0004
?_002: jmp ?_002 ; 0040100A _ EB, FE
The rest of the header appears to be a valid PE format executable with no Entry point specification. I believe therefore that the code is simply "falling through" to the first piece of assembly code to start. I wouldn't advise this behaviour, especially when linking multiple objects as I've no idea what would happen. Do use -entry.
Disassembling the elf object file I get this:
SECTION .data align=4 noexecute ; section number 1, data
SECTION .text align=16 execute ; section number 2, code
_start_here:; Local function
; Note: Length-changing prefix causes delay on Intel processors
mov ax, 4 ; 0000 _ 66: B8, 0004
?_001: jmp ?_001 ; 0004 _ EB, FE
_another_symbol:; Local function
; Note: Length-changing prefix causes delay on Intel processors
mov ax, 4 ; 0006 _ 66: B8, 0004
?_002: jmp ?_002
In other words, there aren't any specific ELF-format headers in it. I believe you're getting lucky on this one; start importing or trying to link with other code modules and things will start to get more tricky.
For Windows / mingw, you want:
nasm -f win32 file.asm
for each file you want to assemble. Substitute win32 for win64 when needed. ld will do fine for linking.
Just a thought - I never explained the #16 part. The functions are 16-byte aligned on Windows, whereas, as you can see, the data is only four-byte aligned. See this explanation for the why.

Resources