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