0
votes

I have the following assmebly (NASM) code I am writing that sets the mouse pointer to the location 100,100:

section .text
    global _start:

_start:
    mov eax, 4
    mov ecx, 100
    mov edx, 100
    int 33h

    mov eax, 1
    int 0x80

I compile it with the following make file:

DEPS = Mouse.asm

Mouse: $(DEPS)
    nasm -f elf $(DEPS)
    ld -m elf_i386 -s -o Mouse Mouse.o

Then I open it up in GDB using $sudo gdb Mouse. Then I input "break _start" and press "r" to run. When I do that I get the following output:
"Starting program: /home/tyler/ASM/Mouse/Mouse

Program received signal SIGSEGV, Segmentation fault. 0x0804806f in ?? () "

It looks to me that it never even hits the breakpoint at _start so I don't know what I could be doing wrong. How can I go about debugging this?

1
You won't be able to use int 33h from linux (that I assume this is).Jester
Jester is right that this code which seems like real-mode BIOS mouse code. Won't work from Linux. On an unrelated note global _start: should be global _start without the colonMichael Petch
PS: once you got the fault, you can do e.g. x/i 0x0804806f to see the problematic instruction. It should be the int 33h.Jester
As for the lack of the ability to set debug breakpoints on symbols - you need to remove the -s option from the linker (that is removing the symbols)Michael Petch
You can set invalid breakpoint in case you want to start on first instruction. Do b *0 to set breakpoint to address zero, then upon "r" the gdb will try to set it up, fail, and will stop immediately (on the first instruction of the loaded file!). Delete the invalid breakpoint now d 1, and you can stepi further...Ped7g

1 Answers

2
votes

To sort of answer the question you asked (rather than the problem you actually have), use @Ped7g's suggestion to "abuse" GDB's error-handling to get it to stop before the first instruction without knowing the right address:

  • b *0 sets a breakpoint at address zero (which is impossible).
  • r runs the program, at which point GDB tries to actually set the breakpoint. It fails, and stops before any instructions are executed.
  • d 1 deletes the invalid breakpoint
  • si (or stepi) will single step from there.

This is a useful trick for debugging a program that has no symbols, even if it's dynamically linked using ASLR (gcc -pie) so you can't get the real ELF entry-point address using readelf -a.


What you did:

Then I input "break _start" and press "r" to run.

When I do that after building with the same commands you used, I get

Reading symbols from ./Mouse...(no debugging symbols found)...done.
(gdb) b _start
No symbol table is loaded.  Use the "file" command.
Make breakpoint pending on future shared library load? (y or [n]) y
Breakpoint 1 (_start) pending.

I had to type y or n before I could enter r to run the program.

So I don't actually have a breakpoint at the instruction labeled _start: in the asm source. All I have is a pending breakpoint that will be set somewhere if I give GDB a symbol table. (It is possible to strip debug symbols into a separate file, so this gdb behaviour makes sense. But it unfortunately confused you in this case. :/)

Your binary is stripped because you built with ld -s. This is the opposite of what you want for debugging. Use nasm -g -Fdwarf to use the modern dwarf debug-info format instead of STABS for even better symbol info.


(gdb) r
Starting program: /home/peter/src/SO/Mouse

Program received signal SIGSEGV, Segmentation fault.
0x0804806f in ?? ()

The instruction at 0x0804806f is your int 33h.

It's not segfaulting before _start, it just didn't stop because you never actually set a breakpoint. In a statically-linked binary, the first instruction to run in user-space is the one at your ELF entry point. (In a dynamically linked binary, the ELF interpreter runs first and eventually jumps to your _start, or whatever you called your entry point.)

Use layout reg to show registers and disassembly. Use set disassembly-flavor intel to get GNU's MASM-like syntax which is close enough to NASM to read when you know what it's supposed to be. Put these in your ~/.gdbinit. See also the bottom of the tag wiki for more debugging tips with gdb and strace.


As commenters have pointed out, there's no way your program can run natively under Linux. Linux doesn't natively support the BIOS int 33h ABI, only its own system-call ABI. What are the calling conventions for UNIX & Linux system calls on i386 and x86-64. This is why int 33h segfaults.

If you want to write MS-DOS or PC-BIOS code, use an emulator like BOCHS (which has a built-in debugger that will let you single-step anything, even a bootloader).