OSDev.org

The Place to Start for Operating System Developers
It is currently Sat Jan 20, 2018 11:07 pm

All times are UTC - 6 hours




Post new topic Reply to topic  [ 15 posts ] 
Author Message
 Post subject: Another happy day in the wonderful world of GNU toolchain...
PostPosted: Sat Jun 24, 2017 1:24 pm 
Offline
Member
Member
User avatar

Joined: Thu Oct 13, 2016 4:55 pm
Posts: 118
Okay, here's the thing I sucked with hard. I share the solution in case somebody will have the same problem.

I wanted to set up a breakpoint in gdb, and since it can't handle the symbols correctly, I decided to use the "jmp $" and "set $pc+=2" trick to step through. My code is as follows:
Code:
    /*** endless loop ***/
dbg_printf("dispatch loop\n");
ee: goto ee;
    while(1) {
        /* get work */
        msg = mq_recv();

Code:
(gdb) symbol-file bin/initrd/lib/libc.so
Reading symbols from bin/initrd/lib/libc.so...done.
(gdb) c
Continuing.
^C
Program received signal SIGINT, Interrupt.
0x0000000000209b02 in ?? ()
(gdb) x /16i $pc
=> 0x209b02:   jmp    0x209b02
   0x209b04:   leaveq
   0x209b05:   retq   
   0x209b06:   push   %rbp
   0x209b07:   mov    %rsp,%rbp

That disassembly made me wonder, how on earth is this possible?

It shouldn't been a leaveq+retq there! So I've investigated a bit. I'm compiling with "-g -O0" flags, that should disable all optimizations. When I comment out the "ee: goto ee;", I got:
Code:
objdump -d libc.so
...
    1b0e:       e8 dd 34 00 00          callq  4ff0 <dbg_printf@plt>
    1b13:       b8 00 00 00 00          mov    $0x0,%eax
    1b18:       e8 63 34 00 00          callq  4f80 <mq_recv@plt>
...

Which is fine. Now if I put that goto back there, it compiles to:
Code:
objdump -d libc.so
...
    1b0b:       e8 00 33 00 00          callq  4e10 <dbg_printf@plt>
    1b10:       eb fe                   jmp    1b10 <mq_dispatch+0x1bc>
    1b12:       c9                      leaveq
    1b13:       c3                      retq   

0000000000001b14 <atexit>:

Meaning the entire code I wanted to step through gone for good! <angry> Man, I hate software so much that thinks it's smarter than a man, especially when there's no way to tell it "no, you are dumb as hell, and I have already told you not to override me, so just do as I say!" </angry>

Solution, ughly it is, but works (at least on x86_64):
Code:
    /*** endless loop ***/
dbg_printf("dispatch loop\n");
__asm__ __volatile__ ("1:jmp 1b");
    while(1) {
        /* get work */
        msg = mq_recv();

Code:
objdump -d libc.so
...
    1b0e:       e8 ed 34 00 00          callq  5000 <dbg_printf@plt>
    1b13:       eb fe                   jmp    1b13 <mq_dispatch+0x1bf>
    1b15:       b8 00 00 00 00          mov    $0x0,%eax
    1b1a:       e8 71 34 00 00          callq  4f90 <mq_recv@plt>
...

Hurray!


Top
 Profile  
 
 Post subject: Re: Another happy day in the wonderful world of GNU toolchai
PostPosted: Sat Jun 24, 2017 1:56 pm 
Offline
Member
Member
User avatar

Joined: Sat Mar 31, 2012 3:07 am
Posts: 3041
Location: Chichester, UK
Why would you expect a compiler to emit instructions for unreachable code?


Top
 Profile  
 
 Post subject: Re: Another happy day in the wonderful world of GNU toolchai
PostPosted: Sat Jun 24, 2017 4:24 pm 
Offline
Member
Member
User avatar

Joined: Thu Oct 13, 2016 4:55 pm
Posts: 118
iansjack wrote:
Why would you expect a compiler to emit instructions for unreachable code?

Because I've explicitly told the compiler not to optimize. Just because it "thinks" the code is unreachable, doesn't mean it is unnecessary.


Top
 Profile  
 
 Post subject: Re: Another happy day in the wonderful world of GNU toolchai
PostPosted: Sat Jun 24, 2017 11:09 pm 
Offline
Member
Member
User avatar

Joined: Sat Mar 31, 2012 3:07 am
Posts: 3041
Location: Chichester, UK
1. You haven't told the compiler not to optimize. You need to read the GCC manual.

2. Unreachable code isn't unnecessary? It's logic, but not as we know it Jim.


Top
 Profile  
 
 Post subject: Re: Another happy day in the wonderful world of GNU toolchai
PostPosted: Sun Jun 25, 2017 5:07 am 
Offline
Member
Member
User avatar

Joined: Tue Aug 02, 2016 1:52 pm
Posts: 286
Location: East Riding of Yorkshire, UK
bzt wrote:
Because I've explicitly told the compiler not to optimize.

There's still quite a few optimisations enabled when you pass -O0.

Code:
$ gcc --version
gcc (GCC) 7.1.1 20170526 (Red Hat 7.1.1-2)
Copyright (C) 2017 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.

$ gcc -Q --help=optimizer -O0 | grep enabled | wc -l
56

_________________
com.sun.java.swing.plaf.nimbus.InternalFrameInternalFrameTitlePaneInternalFrameTitlePaneMaximizeButtonWindowNotFocusedState
Compiler Development Forum


Top
 Profile  
 
 Post subject: Re: Another happy day in the wonderful world of GNU toolchai
PostPosted: Sun Jun 25, 2017 8:52 am 
Offline
Member
Member

Joined: Tue Nov 08, 2011 11:35 am
Posts: 453
Don't fight against your tools, just learn how to use them properly. If you don't care about portability, asm volatile ("int3") is what you need instead. Better solution is to use something like this snippet or this header


Top
 Profile  
 
 Post subject: Re: Another happy day in the wonderful world of GNU toolchai
PostPosted: Sun Jun 25, 2017 8:54 am 
Offline
Member
Member

Joined: Thu May 17, 2007 1:27 pm
Posts: 426
There are probably some passes that need to be done in order for GCC's code generators to work. Seems like the dead-code elimination pass is one of time. Things like SSA generation might need accurate control flow graphs.


Top
 Profile  
 
 Post subject: Re: Another happy day in the wonderful world of GNU toolchai
PostPosted: Sun Jun 25, 2017 1:37 pm 
Offline
Member
Member
User avatar

Joined: Thu Oct 13, 2016 4:55 pm
Posts: 118
@iansjack: well, when you're debugging, everything is necessary. Btw, from the manual,
Code:
-O0
Reduce compilation time and make debugging produce the expected results. This is the default.

I expect no code elimination when I debug :-) Besides, there's no way to tell gcc not to do so, and it's not just me, there are other programmers:
https://stackoverflow.com/questions/28565914/can-gcc-be-instructed-not-to-eliminate-dead-code

@matt11235: yep, you're right.

@Nable: I can't. I was already hacking computers by the time gcc had it's first beta release... :-) Your links are interesting, that's exactly what I do too https://github.com/bztsrc/osz/blob/master/src/lib/libc/x86_64/platform.h#L31. But the problem with int 3 is that it runs INSIDE the vm, and does not stop execution or instruct gdb for a debug prompt (instead invokes my internal debugger). I didn't wanted to run my debugger because it's also running inside the vm, and I wanted to observe from a distance without any possible interference. Since the problem only appeared when I booted from EFI, I couldn't use bochs either (I can only use TianoCore with qemu, it's not working with bochs for some reason).

@Korona: you're right. I've learned that there's a portable way, called asm goto. On the stackoverflow link above they also mentioning it with branch prediction.

Anyway, it turned out that the problem was caused by a bug in my pmm, when EFI gave a memory map that was interpreted badly and at a certain point pmm_alloc() gave 0xFF000 instead of 9F000 to the vmm. Unluckily for me it did that when my init process' stack was allocated. Therefore init tried to save return pointer in the ROM... Needless to say it didn't worked. It was a really nasty bug and hard to trace, but now I've fixed it :-)


Top
 Profile  
 
 Post subject: Re: Another happy day in the wonderful world of GNU toolchai
PostPosted: Sun Jun 25, 2017 2:44 pm 
Offline
Member
Member
User avatar

Joined: Sat Mar 31, 2012 3:07 am
Posts: 3041
Location: Chichester, UK
No.

If you are going to manually change registers then you can't expect the compiler to generate code that will allow for that possibility. Has it generated code to deal with the possibility of you changing the stack pointer to an invalid value (for example) - something that you might also decide to do manually in gdb? It's reasonable for the compiler to expect you to at least provide a hint that you are going to do something funny. Inserting the "jmp ." explicitly does this. (Well, at least you can't expect the compiler to take manually inserted instructions into account when optimizing code.)

You say there is no way to tell gcc not to discard the unreachable code, and yet you have demonstrated exactly how to tell it!


Top
 Profile  
 
 Post subject: Re: Another happy day in the wonderful world of GNU toolchai
PostPosted: Sun Jun 25, 2017 4:36 pm 
Offline
Member
Member
User avatar

Joined: Thu Oct 13, 2016 4:55 pm
Posts: 118
@iansjack: I wasn't talking about changing registers and other low level stuff. I just wanted to use a high level "stop execution" without any side-effects. Also I've meant there's no command line option to force that, or at least it doesn't do what you expect it to do. What I've shown with asm("1:jmp 1b") is not a platform independent way, so it's not a 100% equivalent solution. Frankly I've never thought there's such a brainf*cked thing in gcc like asm goto, which is an oxymoron by itself :-) But it works, avoids code removal and it is portable :-D


Top
 Profile  
 
 Post subject: Re: Another happy day in the wonderful world of GNU toolchai
PostPosted: Sun Jun 25, 2017 11:46 pm 
Offline
Member
Member
User avatar

Joined: Sat Mar 31, 2012 3:07 am
Posts: 3041
Location: Chichester, UK
And yet you talk about manually changing a register in gdb.


Top
 Profile  
 
 Post subject: Re: Another happy day in the wonderful world of GNU toolchai
PostPosted: Mon Jun 26, 2017 8:51 am 
Offline
Member
Member
User avatar

Joined: Thu Oct 13, 2016 4:55 pm
Posts: 118
iansjack wrote:
And yet you talk about manually changing a register in gdb.
I see you have a problem with understanding the concept, so let's make this clear.

There are two "instructions":

1. at source level, a platform independent, portable way of saying "stop execution". This should not involve any registers or any architecture specific stuff as it's in the C source. If it's capable of invoking the debugger prompt it's the best, but at least it must suspend all activities until the user enters remote debugger prompt manually. It's okay if it's a macro that specified differently for each architecture, but the source must be the same for all platforms. At a first glance I've thought "label:goto label;" fulfills those requirements without the need to specify it differently for every platform.

2. at debugger level, we need to say "continue execution". As it's at debugger level, it clearly cannot be platform independent, as debugging is always tied to a specific architecture you running your code on. But it's semantic is the same for all architectures: move the instruction pointer (or program counter) to the next instruction. after the one that caused the stop.

Sidenotes: if you use int $1 for "stop", the instruction pointer is already set up at the next instruction, so you don't need to adjust it manually. If you use some hardware assistance for stopping (like single stepping in x86), you don't have to adjust it either, as IP is already pointing at the next instruction. With other words the pre-requirement of continuing is already fulfilled, no actions needed. Now using hardware assistance for stopping requires at least macros, as it clearly cannot be described in C.


Top
 Profile  
 
 Post subject: Re: Another happy day in the wonderful world of GNU toolchai
PostPosted: Mon Jun 26, 2017 9:05 am 
Offline
Member
Member
User avatar

Joined: Sat Mar 31, 2012 3:07 am
Posts: 3041
Location: Chichester, UK
You are ignoring the fact that the instruction pointer is a register. By changing it manually you are changing the program in a way that the compiler can't predict. It's no different, in essence, from manually changing the stack pointer, CR3, or any other register.

I have no problems with the concept; I am quite familiar with using gdb to debug an os. And I know how to use breakpoints.


Top
 Profile  
 
 Post subject: Re: Another happy day in the wonderful world of GNU toolchai
PostPosted: Mon Jun 26, 2017 10:00 am 
Offline
Member
Member
User avatar

Joined: Thu Aug 11, 2005 11:00 pm
Posts: 1055
Location: Tartu, Estonia
This might be a too simple solution, and it might have some problems because of which it might not work, but this is what comes to my mind if I wanted to have a "closed lift gate" that my code cannot pass unless the debugger opens it:

Code:
volatile bool lift_gate = false;

...

while(!lift_gate) ;

...

This should stop execution (or rather run into an endless loop, since you never change lift_gate in the code), but still tell the compiler that the code might continue after this point, when lift_gate gets changed externally. So in your debugger you can just flip the variable and your code continues. And this is perfectly portable.

Of course, setting a breakpoint in the debugger before you start execution is another option, unless you want to avoid this for some reason. All you need here is to figure out where exactly to set the breakpoint.

_________________
Programmers' Hardware Database // GitHub user: xenos1984; OS project: NOS


Top
 Profile  
 
 Post subject: Re: Another happy day in the wonderful world of GNU toolchai
PostPosted: Mon Jun 26, 2017 10:40 am 
Offline
Member
Member
User avatar

Joined: Sat Mar 31, 2012 3:07 am
Posts: 3041
Location: Chichester, UK
I'd agree that setting a breakpoint is preferable to altering the code. I can't see any reason not to take this approach. Or, you could even set a watchpoint on a variable that you know is going to change.


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 15 posts ] 

All times are UTC - 6 hours


Who is online

Users browsing this forum: No registered users and 1 guest


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group