In C, BUFFER_SIZE
is a macro, this should be a compile-time memory allocation, and gdb does not break and skips the line.
#define BUFFER_SIZE 50
int main(){
const int sz = BUFFER_SIZE;
char buffer[BUFFER_SIZE]; // does not break when a breakpoint is specified here
}
This is a run-time allocation, gdb does break.
#define BUFFER_SIZE 50
int main(){
const int sz = BUFFER_SIZE;
char buffer[sz]; // it does break here
}
I think it's because compile-time evaluated instructions cannot be debugged because gdb debugs at runtime, is that correct or is there another explanation ?
You can set a breakpoint only on lines that have machine instructions.
On what else could a debugger break? The same is true for single stepping.
The array definition as a local variable needs only instructions when it is a VLA, which is constructed at run-time.
The following experiments are done with MinGW for a 64 bit Windows 10 machine version 13.2.0 that I have at hand.
I compiled with the options -Wall -pedantic -g
. Of course, the compiler gives me warnings because of the unused variables.
The assembly listings were generated with objdump -dS
from the executables.
Without optimization (-O0
), as you did, the compiler generates different code.
In the first program, you have a compile-time constant for the array size. Therefore, the compiler includes the array directly in the stack space allocated for the local variables. Consequently, there is no instruction for the defining line.
0000000140001450 <main>:
#define BUFFER_SIZE 50
int main(){
140001450: 55 push %rbp
140001451: 48 89 e5 mov %rsp,%rbp
140001454: 48 83 ec 60 sub $0x60,%rsp
140001458: e8 c3 00 00 00 call 140001520 <__main>
const int sz = BUFFER_SIZE;
14000145d: c7 45 fc 32 00 00 00 movl $0x32,-0x4(%rbp)
140001464: b8 00 00 00 00 mov $0x0,%eax
char buffer[BUFFER_SIZE]; // does not break when a breakpoint is specified here
}
140001469: 48 83 c4 60 add $0x60,%rsp
14000146d: 5d pop %rbp
14000146e: c3 ret
In the second program, the compiler see a VLA because of the variable. It generates instructions for the allocation, so you can set a breakpoint. (Side note: By default, the compiler checks that the stack has room for the array.)
0000000140001450 <main>:
#define BUFFER_SIZE 50
int main(){
140001450: 55 push %rbp
140001451: 48 89 e5 mov %rsp,%rbp
140001454: 48 83 ec 40 sub $0x40,%rsp
140001458: e8 03 01 00 00 call 140001560 <__main>
14000145d: 48 89 e0 mov %rsp,%rax
140001460: 48 89 c2 mov %rax,%rdx
const int sz = BUFFER_SIZE;
140001463: c7 45 fc 32 00 00 00 movl $0x32,-0x4(%rbp)
char buffer[sz]; // it does break here
14000146a: 8b 45 fc mov -0x4(%rbp),%eax
14000146d: 48 98 cltq
14000146f: 48 83 e8 01 sub $0x1,%rax
140001473: 48 89 45 f0 mov %rax,-0x10(%rbp)
140001477: 8b 45 fc mov -0x4(%rbp),%eax
14000147a: 48 98 cltq
14000147c: 48 83 c0 0f add $0xf,%rax
140001480: 48 c1 e8 04 shr $0x4,%rax
140001484: 48 c1 e0 04 shl $0x4,%rax
140001488: e8 a3 10 00 00 call 140002530 <___chkstk_ms>
14000148d: 48 29 c4 sub %rax,%rsp
140001490: 48 8d 44 24 20 lea 0x20(%rsp),%rax
140001495: 48 83 c0 00 add $0x0,%rax
140001499: 48 89 45 e8 mov %rax,-0x18(%rbp)
14000149d: 48 89 d4 mov %rdx,%rsp
1400014a0: b8 00 00 00 00 mov $0x0,%eax
}
1400014a5: 48 89 ec mov %rbp,%rsp
1400014a8: 5d pop %rbp
1400014a9: c3 ret
When compiled as C++, the constant variable sz
of the second program is semantically a compile-time constant, so there is no difference between both executables.
In both cases no breakpoint can be set.
0000000140001450 <main>:
#define BUFFER_SIZE 50
int main(){
140001450: 55 push %rbp
140001451: 48 89 e5 mov %rsp,%rbp
140001454: 48 83 ec 60 sub $0x60,%rsp
140001458: e8 c3 00 00 00 call 140001520 <__main>
const int sz = BUFFER_SIZE;
14000145d: c7 45 fc 32 00 00 00 movl $0x32,-0x4(%rbp)
char buffer[BUFFER_SIZE]; // does not break when a breakpoint is specified here
}
140001464: b8 00 00 00 00 mov $0x0,%eax
140001469: 48 83 c4 60 add $0x60,%rsp
14000146d: 5d pop %rbp
14000146e: c3 ret
0000000140001450 <main>:
#define BUFFER_SIZE 50
int main(){
140001450: 55 push %rbp
140001451: 48 89 e5 mov %rsp,%rbp
140001454: 48 83 ec 60 sub $0x60,%rsp
140001458: e8 c3 00 00 00 call 140001520 <__main>
const int sz = BUFFER_SIZE;
14000145d: c7 45 fc 32 00 00 00 movl $0x32,-0x4(%rbp)
char buffer[sz]; // it does break here
}
140001464: b8 00 00 00 00 mov $0x0,%eax
140001469: 48 83 c4 60 add $0x60,%rsp
14000146d: 5d pop %rbp
14000146e: c3 ret
Finally I compiled both programs with -O3
, which produced identical machine code. The compiler found that the variables are unused and removed them. Consequently, you cannot set a breakpoint.
0000000140002920 <main>:
#define BUFFER_SIZE 50
int main(){
140002920: 48 83 ec 28 sub $0x28,%rsp
140002924: e8 d7 eb ff ff call 140001500 <__main>
const int sz = BUFFER_SIZE;
char buffer[BUFFER_SIZE]; // does not break when a breakpoint is specified here
}
140002929: 31 c0 xor %eax,%eax
14000292b: 48 83 c4 28 add $0x28,%rsp
14000292f: c3 ret
0000000140002920 <main>:
#define BUFFER_SIZE 50
int main(){
140002920: 48 83 ec 28 sub $0x28,%rsp
140002924: e8 d7 eb ff ff call 140001500 <__main>
const int sz = BUFFER_SIZE;
char buffer[BUFFER_SIZE]; // does not break when a breakpoint is specified here
}
140002929: 31 c0 xor %eax,%eax
14000292b: 48 83 c4 28 add $0x28,%rsp
14000292f: c3 ret