Can't set breakpoint in assembler code

I’m a newbie just getting started with the following toolchain:

Emacs – GDB – OpenOCD – Olimex ARM-USB-TINY JTAG adapter – Olimex LPC2378-STK dev board

I’m working through James Lynch’s tutorial and a basic blinking LED program.

I can set a breakpoint with GDB in the C module main.c where the LED blinking happens, and step through code, etc.

But I can’t set a breakpoint in the Assembly module crt.s

Here’s a snippet from crt.s :

.text
.arm

.global	Reset_Handler
.global _startup
.func   _startup

_startup:

# Exception Vectors

_vectors: 	ldr	PC, Reset_Addr         
                ldr     PC, Undef_Addr
                ldr     PC, SWI_Addr
                ldr     PC, PAbt_Addr
                ldr     PC, DAbt_Addr
                nop
                ldr     PC, [PC,#-0xFF0]
                ldr     PC, FIQ_Addr

Reset_Addr:     .word   Reset_Handler
Undef_Addr:     .word   UNDEF_Routine
SWI_Addr:       .word   SWI_Routine
PAbt_Addr:      .word   UNDEF_Routine
DAbt_Addr:      .word   UNDEF_Routine
IRQ_Addr:       .word   IRQ_Routine
FIQ_Addr:       .word   FIQ_Routine
                .word   0

# Reset Handler

Reset_Handler:  
		.extern TargetResetInit
		ldr     SP, =_stack_end

When I try to set a breakpoint on that last line (the LDR instruction), gdb says:

(gdb) b crt.s:68
No source file named crt.s.
Make breakpoint pending on future shared library load? (y or [n])

My Makefile executes the following command to assemble crt.s:

arm-elf-as -ahls -mapcs-32 -o crt.o crt.s > crt.lst

The GNU assember manual isn’t crystal clear about this, but the -ahls will “enable listings”; I see no other arguments that would give debug info the same way that “-g” works for the gcc command.

Should I be able to set a breakpoint there?

How should I build the program in order for crt.s source code to be available to gdb?

You can set a breakpoint at a particular address with e.g. “b *0x40” to break at 0x40. The * designates you want to break at a specific address. You can also break at Reset_Handler in your case. I usually disassemble to find the right address to break on.

However, you may not be able to break on the reset handler if your JTAG device combines SRST+TRST. You need to hardware breakpoint in flash, and hw breakpoints don’t survive a reset. So if the two signals are combined the CPU will come out of reset running with no hw breakpoints installed. OpenOCD (if this is what you use) will then halt the device, restore the breakpoints, and resume. So there is a window after reset where you can’t breakpoint.

If you absolutely want to step through the reset handler, insert a loop up front:

Reset_Handler
      b Reset_Handler

Then after reset (use “monitor reset halt” from gdb) exit the loop with “set $pc = 0x44” and start stepping. Note that gdb doesn’t list instructions as you step them, so it can be helpful to disassemble once in a while to keep track of where you are. “p $pc” will show the current location, and there is an OpenOCD command to print all registers (I never remember openocd commands off-hand, but it’s “regs_v4_5 core” or some such - wrap it in a gdb define).

The CPU has two hw breakpoints, and gdb needs one for internal use to step a statement, continue until a function finishes, and so on. So in practice you can set two hw breakpoints but need to disable one (like the one that just triggered) to usefully debug C/C++ code.

JJ:
You can set a breakpoint at a particular address with e.g. “b *0x40” to break at 0x40. The * designates you want to break at a specific address. You can also break at Reset_Handler in your case. I usually disassemble to find the right address to break on.

However, you may not be able to break on the reset handler if your JTAG device combines SRST+TRST. You need to hardware breakpoint in flash, and hw breakpoints don’t survive a reset. So if the two signals are combined the CPU will come out of reset running with no hw breakpoints installed. OpenOCD (if this is what you use) will then halt the device, restore the breakpoints, and resume. So there is a window after reset where you can’t breakpoint.

If you absolutely want to step through the reset handler, insert a loop up front:

Reset_Handler
  b Reset_Handler


Then after reset (use "monitor reset halt" from gdb) exit the loop with "set $pc = 0x44" and start stepping. Note that gdb doesn't list instructions as you step them, so it can be helpful to disassemble once in a while to keep track of where you are. "p $pc" will show the current location, and there is an OpenOCD command to print all registers (I never remember openocd commands off-hand, but it's "regs_v4_5 core" or some such - wrap it in a gdb define).



The CPU has two hw breakpoints, and gdb needs one for internal use to step a statement, continue until a function finishes, and so on. So in practice you can set two hw breakpoints but need to disable one (like the one that just triggered) to usefully debug C/C++ code.

Thank you very much for the detailed and practical information. Using that loop worked great. I didn’t know that OpenOCD took time to restore the breakpoints after a reset (yes, my LPC2378 board requires combining SRST+TRST).

My expectation was that I could set a breakpoint at a line number in an assembler file - but as you’ve pointed out, I can only specify the breakpoint at a symbol or a memory location. Several other sites have confirmed that gdb’s line-number breakpoint is only supported for higher-level languages.