Port-arm archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

gdb on ODROID-C1



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



Home | Main Index | Thread Index | Old Index