Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

GCC - Label address return current EIP rather than real label address

Tags:

c

gcc

osdev

In an attempt to write an OS, I need to get the address of the current function's end (right before epilogue) for task switching.
Concretely my problem is to get an EIP to assign to my newly created task (process) inside the copied stack. I have already managed to save/restore registers for a process, but I need to find what value the child process will have in it's EIP.

I used GCC's extensions to C standard : Labels as Values and Local Labels
From the documentation : You can get the address of a label defined in the current function (or a containing function) with the unary operator ‘&&’. The value has type void *.
and : GCC allows you to declare local labels in any nested block scope. A local label is just like an ordinary label, but you can only reference it (with a goto statement, or by taking its address) within the block in which it is declared.

pid_t fork(void)
{
    __label__ fork_end;
    ...
    task->regs.eip = (uintptr_t)&&fork_end;
    ...
    return task->pid;
    fork_end:;
}

GCC does compile it, with just warnings about non-standard code.
However when disassembled, gdb shows :

    task->regs.eip = (uintptr_t)&&fork_end;
0x00105008 <+87>:   mov    $0x105008,%edx
0x0010500d <+92>:   mov    -0xc(%ebp),%eax
0x00105010 <+95>:   mov    %edx,0x40(%eax)
...
    fork_end:;
    }
0x00105096 <+229>:  leave  
0x00105097 <+230>:  ret 

I expect task->regs.eip = (uintptr_t)&&fork_endl to save0x00105096 rather than 0x00105008.
CFLAGS are -O0 -std=gnu99 -fgnu89-inline -DDEBUG -ggdb3 -ffreestanding -fbuiltin (warnings related options not shown here).

Commenting __label__ fork_end; changes nothing.

like image 220
Aerath Avatar asked Aug 16 '16 21:08

Aerath


1 Answers

It appears that the compiler is optimizing out the label entirely as no code path ever leads to it. I have confirmed that moving the label to before the return statement and ensuring that there is actual code in-between the assignment and the label results in what I believe is your desired behaviour. Here is the code I put together to test:

void *fork(void) {
  __label__ fork_end;
  void *test = &&fork_end;

  test++;

  fork_end:
  return test;
}

Based on this, I would expect that you can indeed get what you want by re-working your code path slightly to ensure that the label point is reachable by any code path.

like image 67
Joel C Avatar answered Sep 21 '22 10:09

Joel C