This is the mail archive of the gdb@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: RISC-V: decr_pc_after_break causing problems


On Wed, Jul 11, 2018 at 10:46 AM, Andrew Burgess
<andrew.burgess@embecosm.com> wrote:
> * Jim Wilson <jimw@sifive.com> [2018-06-25 19:54:14 -0700]:
>
>> The RISC-V port in the riscv-tdep.c file has
>>   set_gdbarch_decr_pc_after_break (gdbarch, (has_compressed_isa ? 2 : 4));
>>
>> The privileged architecture spec v1.10 states in section 3.2.1 that
>> the ebreak instruction causes the receiving privilege mode's epc
>> register to be set to the address of the ebreak instruction, not the
>> address of the following instruction.  So gdb should not be
>> decrementing from the pc after a breakpoint is hit.
>
> Maybe I'm going to look completely silly here, but....
>
> ....aren't epc and pc different registers?

When you take a trap in linux, pc gets copied to sepc, and pc is set
to the linux kernel trap handler code.  The trap handler then stores
sepc into the process context, as it is the application pc.  When gdb
uses ptrace to get the pc, it is given the sepc, because that is the
application pc value.  So as far as gdb is concerned, there is only
one program counter.  The kernel just happens to call it sepc instead
of pc because that is where the application pc lives in the kernel.
When the kernel returns from a trap, executing the sret instruction,
the sepc is copied back into the pc thus returning us to the original
user code.

In the linux kernel sources, in arch/riscv/include/asm/ptrace.h, it has
struct pt_regs {
        unsigned long sepc;
        unsigned long ra;
        unsigned long sp;
        unsigned long gp;
       ...
};
so the kernel stores the pc where x0 would go, because we don't need
to save x0, and calls it sepc.

> The epc is really many register, right? one for each privilege level,
> uepc, sepc, and mepc.

Yes, but since linux runs at supervisor level, there is only sepc that
is interesting in this case.

> The spec is clear that when an ebreak or c.ebreak is hit the contents
> of *epc are the contents of the breaking instruction, but nothing is
> said about the contents of pc.

pc gets set to the trap handler address specified in the trap vector
table.  For linux, the address of that is presumably held in the stvec
CSR, and then either you go to a default offset from stvec, or it is
vectored by the exception number, or whatever, I haven't really looked
at that code.

> Assuming that my above observations are correct, then when we hit a
> breakpoint *epc is defined, but pc is not, an implementation could do
> anything, right?  The two obvious choices are not increment pc, or
> increment pc.

No, the pc has to be the address of a trap handler in the kernel,
because we took a trap.

> Right now my understanding is that GDB doesn't have special support
> for fetching the program counter from different places depending on
> the current inferior state (stopped at breakpoint, not stopped at
> breakpoint).  So, somehow the value in *epc needs to be passed back
> into the pc register (I guess).

The kernel takes care of sepc.  Gdb only needs to worry about the pc.
At least, that is how it works when running an OS like linux.  I don't
know how this is going to work in an embedded system.  Perhaps it is
OpenOCD that takes care of mapping the appropriate xepc to pc
depending on processor mode, and maybe OpenOCD also sets the
appropriate xtvec register to point at its trap handler code.

At no point in this process do we need to add or subtract anything from the pc.

Jim


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]