Port-arm archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
Re: gdb on ODROID-C1
On 05/19/17 10:08, Gopikrishnan S wrote:
Hello,
I have NetBSD 7.1 running on an Odroid-C1.
diablo:~$ uname -a
NetBSD diablo 7.1 NetBSD 7.1 (ODROID-C1.201703111743Z) evbarm
I am trying to debug a simple "Hello, world!" program using gdb:
Reading symbols from ./hello_world...done.
(gdb) b main
Breakpoint 1 at 0x107a4: file test.c, line 6.
(gdb) r
Starting program: /home//C/hello_world
Program received signal SIGILL, Illegal instruction.
0x400257a8 in _rtld () from /usr/libexec/ld.elf_so
(gdb) n
Single stepping until exit from function _rtld,
which has no line number information.
Program terminated with signal SIGILL, Illegal instruction.
The program no longer exists.
The program hits an illegal instruction before it enters main, in the loader itself. This happens about 9 times out of 10 when debugging this program. In the 10th instance, the program hits the breakpoint at main(). But further debugging is impossible:
(gdb) r
Starting program: /home//C/hello_world
Breakpoint 1, main () at test.c:6
6 printf("Hello, world!\n");
(gdb) n
Hello, world!
[Inferior 1 (process 1428) exited normally]
As you can see, I tried to step the program with "n", but it did not stop at the next line. The program continued without stopping and ran to completion.
On x86, gdb is usable and the same program can be debugged without any issues.
I did some debugging on gdb and with some modification to gdb it can be made usable (note: this is not a patch for gdb. This change is to work around what
seems to be a kernel bug).
diff --git a/external/gpl3/gdb/dist/gdb/mem-break.c b/external/gpl3/gdb/dist/gdb/mem-break.c
index c206687..5a920fe 100644
--- a/external/gpl3/gdb/dist/gdb/mem-break.c
+++ b/external/gpl3/gdb/dist/gdb/mem-break.c
@@ -26,6 +26,7 @@
#include "target.h"
#include <string.h>
/* Insert a breakpoint on targets that don't have any better
breakpoint support. We read the contents of the target location
@@ -61,6 +62,9 @@ default_memory_insert_breakpoint (struct gdbarch *gdbarch,
memcpy (bp_tgt->shadow_contents, readbuf, bp_tgt->placed_size);
val = target_write_raw_memory (bp_tgt->placed_address, bp,
bp_tgt->placed_size);
+ target_read_memory (bp_tgt->placed_address, readbuf, bp_tgt->placed_size);
}
return val;
@@ -71,8 +75,19 @@ int
default_memory_remove_breakpoint (struct gdbarch *gdbarch,
struct bp_target_info *bp_tgt)
{
- return target_write_raw_memory (bp_tgt->placed_address, bp_tgt->shadow_contents,
+ int ret = 0;
+ gdb_byte *readbuf = NULL;
+
+ readbuf = alloca (bp_tgt->placed_size);
+ ret = target_write_raw_memory (bp_tgt->placed_address, bp_tgt->shadow_contents,
bp_tgt->placed_size);
+ target_read_memory (bp_tgt->placed_address, readbuf, bp_tgt->placed_size);
+ return ret;
}
Basically, I am reading back the value gdb writes to the inferior's memory whenever gdb inserts or removes a breakpoint. target_read_memory() and
target_write_memory() ultimately devolve into calls to ptrace(PT_IO,...). With this change, my debugging session goes fine:
Reading symbols from hello_world...done.
(gdb) b main
Breakpoint 1 at 0x107a4: file test.c, line 6.
(gdb) r
Starting program: /home//C/hello_world
Breakpoint 1, main () at test.c:6
6 printf("Hello, world!\n");
(gdb) n
Hello, world!
7 return EXIT_SUCCESS;
(gdb)
8 }
(gdb)
0x00010708 in ___start ()
(gdb)
Single stepping until exit from function ___start,
which has no line number information.
[Inferior 1 (process 802) exited normally]
(gdb)
To restate: Every time gdb writes to the inferior's memory with ptrace(), the value written has to be read back before it is executed. Otherwise, there is no
guarantee that the instructions that gdb writes are actually executed.
The only thing the ARM port does differently in this work-flow when compared to x86 is the following bit of code in sys/kern/sys_process.c:process_domem(),
which is called from sys_ptrace():
#ifdef PMAP_NEED_PROCWR
if (error == 0 && uio->uio_rw == UIO_WRITE)
pmap_procwr(p, addr, len);
#endif
pmap_procwr() calls armv7_icache_sync_range(). As per pmap(9), this function is used to flush the i-cache after code modifictation.
My hypothesis is that pmap_procwr()/armv7_icache_sync_range() is not working as expected on the Odroid-C1.
How do I confirm this hypothesis and debug this issue further?
Thanks and regards,--Gopi
Please try the attached patch
Thanks,
Nick
Index: sys/arch/arm/arm32/pmap.c
===================================================================
RCS file: /cvsroot/src/sys/arch/arm/arm32/pmap.c,v
retrieving revision 1.295.2.10
diff -u -p -r1.295.2.10 pmap.c
--- sys/arch/arm/arm32/pmap.c 11 Mar 2017 16:03:54 -0000 1.295.2.10
+++ sys/arch/arm/arm32/pmap.c 19 May 2017 10:54:35 -0000
@@ -424,13 +424,13 @@ static struct evcnt pmap_ev_exec_synced
PMAP_EVCNT_INITIALIZER("exec pages synced");
static struct evcnt pmap_ev_exec_synced_map =
PMAP_EVCNT_INITIALIZER("exec pages synced (MP)");
-#ifndef ARM_MMU_EXTENDED
static struct evcnt pmap_ev_exec_synced_unmap =
PMAP_EVCNT_INITIALIZER("exec pages synced (UM)");
static struct evcnt pmap_ev_exec_synced_remap =
PMAP_EVCNT_INITIALIZER("exec pages synced (RM)");
static struct evcnt pmap_ev_exec_synced_clearbit =
PMAP_EVCNT_INITIALIZER("exec pages synced (DG)");
+#ifndef ARM_MMU_EXTENDED
static struct evcnt pmap_ev_exec_synced_kremove =
PMAP_EVCNT_INITIALIZER("exec pages synced (KU)");
#endif
@@ -1066,12 +1066,10 @@ pmap_enter_pv(struct vm_page_md *md, pad
* for this page, make sure to sync the I-cache.
*/
if (PV_IS_EXEC_P(flags)) {
-#ifndef ARM_MMU_EXTENDED
if (!PV_IS_EXEC_P(md->pvh_attrs)) {
pmap_syncicache_page(md, pa);
PMAPCOUNT(exec_synced_map);
}
-#endif
PMAPCOUNT(exec_mappings);
}
#endif
@@ -1145,26 +1143,19 @@ pmap_remove_pv(struct vm_page_md *md, pa
PMAPCOUNT(unmappings);
#ifdef PMAP_CACHE_VIPT
- if (!(pv->pv_flags & PVF_WRITE))
- break;
/*
* If this page has had an exec mapping, then if
* this was the last mapping, discard the contents,
* otherwise sync the i-cache for this page.
*/
if (PV_IS_EXEC_P(md->pvh_attrs)) {
-#ifdef ARM_MMU_EXTENDED
- md->pvh_attrs &= ~PVF_EXEC;
- PMAPCOUNT(exec_discarded_unmap);
-#else
if (SLIST_EMPTY(&md->pvh_list)) {
md->pvh_attrs &= ~PVF_EXEC;
PMAPCOUNT(exec_discarded_unmap);
- } else {
+ } else if (pv->pv_flags & PVF_WRITE) {
pmap_syncicache_page(md, pa);
PMAPCOUNT(exec_synced_unmap);
}
-#endif /* ARM_MMU_EXTENDED */
}
#endif /* PMAP_CACHE_VIPT */
break;
@@ -1276,7 +1267,6 @@ pmap_modify_pv(struct vm_page_md *md, pa
md->pvh_attrs |= PVF_WRITE;
}
}
-#ifndef ARM_MMU_EXTENDED
/*
* We have two cases here: the first is from enter_pv (new exec
* page), the second is a combined pmap_remove_pv/pmap_enter_pv.
@@ -1289,6 +1279,7 @@ pmap_modify_pv(struct vm_page_md *md, pa
pmap_syncicache_page(md, pa);
PMAPCOUNT(exec_synced_remap);
}
+#ifndef ARM_MMU_EXTENDED
KASSERT((md->pvh_attrs & PVF_DMOD) == 0 || (md->pvh_attrs & (PVF_DIRTY|PVF_NC)));
#endif /* !ARM_MMU_EXTENDED */
#endif /* PMAP_CACHE_VIPT */
@@ -2338,12 +2329,12 @@ pmap_clearbit(struct vm_page_md *md, pad
struct pv_entry *pv;
#ifdef PMAP_CACHE_VIPT
const bool want_syncicache = PV_IS_EXEC_P(md->pvh_attrs);
+ bool need_syncicache = false;
#ifdef ARM_MMU_EXTENDED
const u_int execbits = (maskbits & PVF_EXEC) ? L2_XS_XN : 0;
#else
const u_int execbits = 0;
bool need_vac_me_harder = false;
- bool need_syncicache = false;
#endif
#else
const u_int execbits = 0;
@@ -2359,12 +2350,9 @@ pmap_clearbit(struct vm_page_md *md, pad
* then we know we definitely need to sync or discard it.
*/
if (want_syncicache) {
-#ifdef ARM_MMU_EXTENDED
- if (md->pvh_attrs & PVF_MOD)
- md->pvh_attrs &= ~PVF_EXEC;
-#else
- need_syncicache = md->pvh_attrs & PVF_MOD;
-#endif
+ if (md->pvh_attrs & PVF_MOD) {
+ need_syncicache = true;
+ }
}
#endif
KASSERT(pmap_page_locked_p(md));
@@ -2375,7 +2363,7 @@ pmap_clearbit(struct vm_page_md *md, pad
md->pvh_attrs &= ~(maskbits & (PVF_MOD | PVF_REF));
if (SLIST_EMPTY(&md->pvh_list)) {
-#if defined(PMAP_CACHE_VIPT) && !defined(ARM_MMU_EXTENDED)
+#if defined(PMAP_CACHE_VIPT)
if (need_syncicache) {
/*
* No one has it mapped, so just discard it. The next
@@ -2484,9 +2472,9 @@ pmap_clearbit(struct vm_page_md *md, pad
PMAP_VALIDATE_MD_PAGE(md);
}
}
-#ifndef ARM_MMU_EXTENDED
if (want_syncicache)
need_syncicache = true;
+#ifndef ARM_MMU_EXTENDED
need_vac_me_harder = true;
#endif
#endif /* PMAP_CACHE_VIPT */
@@ -2540,7 +2528,7 @@ pmap_clearbit(struct vm_page_md *md, pad
pm, va, opte, npte));
}
-#if defined(PMAP_CACHE_VIPT) && !defined(ARM_MMU_EXTENDED)
+#if defined(PMAP_CACHE_VIPT)
/*
* If we need to sync the I-cache and we haven't done it yet, do it.
*/
@@ -2550,7 +2538,7 @@ pmap_clearbit(struct vm_page_md *md, pad
pmap_acquire_page_lock(md);
PMAPCOUNT(exec_synced_clearbit);
}
-
+#ifndef ARM_MMU_EXTENDED
/*
* If we are changing this to read-only, we need to call vac_me_harder
* so we can change all the read-only pages to cacheable. We pretend
@@ -2560,7 +2548,8 @@ pmap_clearbit(struct vm_page_md *md, pad
if (md->pvh_attrs & PVF_NC)
pmap_vac_me_harder(md, pa, NULL, 0);
}
-#endif /* PMAP_CACHE_VIPT && !ARM_MMU_EXTENDED */
+#endif /* !ARM_MMU_EXTENDED */
+#endif /* PMAP_CACHE_VIPT */
}
/*
@@ -4707,9 +4696,12 @@ out:
void
pmap_procwr(struct proc *p, vaddr_t va, int len)
{
+#ifndef ARM_MMU_EXTENDED
+
/* We only need to do anything if it is the current process. */
if (p == curproc)
cpu_icache_sync_range(va, len);
+#endif
}
/*
Home |
Main Index |
Thread Index |
Old Index