Exploring the mailbox on the raspberry pi4 with qemu and qnx, using the debugger over an emulated serial device.
In two previous articles we investigated on how to run an emulated raspberry pi in qemu. We explored SD and UART. Here we investigate how to debug with this setup and try using the emulated mbox functionality.
https://olof-astrand.medium.com/more-experiments-with-qnx-and-qemu-d24fa1961d9c
The mailbox is the way the main cpu communicates with the VideoCore GPU. This mechanism is essential for coordinating various system-level functions and accessing hardware features.
Start qemu
qemu-system-aarch64 -s -M raspi4b \
-kernel ifs-rpi4.bin \
-append "startup-bcm2711-rpi4 -v -D miniuart" \
-drive if=none,file=sd.img,format=raw,id=sdcard \
-dtb bcm2711-rpi-4-b.dtb \
-serial tcp::12345,server,nowait -serial stdio \
-monitor telnet::45454,server,nowait \
-trace "bcm2835_mbox*" -trace file=trace_output.log \
-netdev user,id=bcm2838-genet,hostfwd=tcp::5555-:22 \
-object filter-dump,id=fdump1,netdev=bcm2838-genet,file=genet.pcap
Run Gdb
Put yourself in the BSP_ROOT dir.
$QNX_HOST/usr/bin/ntoaarch64-gdb -ex 'target qnx 127.0.0.1:12345'
If you got, Couldn’t establish connection to remote target when connecting with the debugger, you might not have started pdebug in the IFS. You can then start it manually in qnx, # pdebug /dev/ser3,57600 Another possibility is that you forgot -serial tcp::12345,server,nowait when starting qemu.
In gdb run these
(gdb) set nto-executable /bin/mbox-bcm2711
(gdb) file prebuilt/aarch64le/bin/mbox-bcm2711
(gdb) sym binary_files_with_symbols/aarch64le/bin/mbox-bcm2711.sym
(gdb) set substitute-path /builds/workspace/BSP_raspberrypi-bcm2711-rpi4_br-700_be-700/code src
(gdb) set nto-cwd /
(gdb) b main
(gdb) run with-args
Wait for it …….
Breakpoint 1, main (argc=2, argv=0x100c7cc8) at /builds/workspace/BSP_raspberrypi-bcm2711-rpi4_br-700_be-700/code/hardware/support/bcm2711/mbox/mbox.c:166
166 int main(int argc, char **argv) {
(gdb) l
161 for (unsigned i = 0; i < len / sizeof (uint32_t); i++)
162 printf("%08x ", ((uint32_t *) buf)[i]);
163 printf("\n");
It is a bit slow but we want to inspect the (gdb) info meminfo after we have made a call to these functions,
(gdb) b mbox_msg
(gdb) b posix_typed_mem_open
(gdb) b mmap_device_memory
Do note that things might not execute in the order of the source file as it was compiled with -O2
(gdb) where
#0 posix_typed_mem_open (name=0x100c98d8 "/sysram&below1G", oflag=2, tflag=2) at /builds/workspace/BC700_6762_sdp704/build_dir/lib/c/1j/typed_mem_open.c:37
#1 0x00000000100c96e4 in mbox_msg (tag=327681, buf=0x100c7b78, bufsiz=320)
at /builds/workspace/BSP_raspberrypi-bcm2711-rpi4_br-700_be-700/code/hardware/support/bcm2711/mbox/mbox.c:55
#2 0x00000000100c93b8 in main (argc=<optimized out>, argv=0x100c7cd0)
at /builds/workspace/BSP_raspberrypi-bcm2711-rpi4_br-700_be-700/code/hardware/support/bcm2711/mbox/mbox.c:224
Although the process is stopped in the debugger, we can view in within qnx.
# pidin
pid tid name prio STATE Blocked
1 1 /procnto-smp-instr 0f RUNNING
12 1 sbin/devc-pty 10r RECEIVE 1
69646 1 usr/bin/pdebug 10r REPLY 7
69646 2 usr/bin/pdebug 10r REPLY 12
73741 1 bin/ksh 10r SIGSUSPEND
86031 1 bin/mbox-bcm2711 10r STOPPED
# cat /proc/86031/pmap
0x00000000100c8000,0x0000000000001000,0x00010071,0x00000005,0x0000000f,0x00000802,0x00000000c00000a6,0x0000000001805000,0x0000000000000000,0x0000000000000000,0x00000095,0x00000093,/bin/mbox-bcm2711
0x00000000100da000,0x0000000000001000,0x00010032,0x00000003,0x0000000f,0x00000802,0x00000000c00000a6,0x0000000001807000,0x0000000000001000,0x0000000000000000,0x00000095,0x00000093,/bin/mbox-bcm2711
0x00000000100db000,0x000000000000b000,0x00080002,0x00000003,0x0000000f,0x00000001,0x0000000000000003,0x0000000000000000,0x000000000000b000,0x0000000000000000,0x00000000,0x00000096,{heap}
0x0000000028000000,0x0000000000001000,0x00010001,0x0000000b,0x0000000f,0x00000001,0x00000000331cd476,0x0000000000000000,0x0000000000000000,0x0000000000000000
,0x00000002,0x00000001,/dev/tymem/sysram&below1G
0x0000000078000000,0x0000000000001000,0x00010071,0x00000005,0x0000000f,0x00000802,0x00000000c000000f,0x0000000000228000,0x0000000000000000,0x0000000000000000,0x00000095,0x00000093,/usr/lib/libstringsa64.so.1
0x0000000078001000,0x000000000000f000,0x080800a2,0x00000000,0x0000000f,0x00000001,0x0000000000000003,0x0000000000001000,0x0000000000000000,0x0000000000000000,0x00000000,0x00000096,{bss}
Compare this with the info from (gdb),
(gdb) info meminfo
/bin/mbox-bcm2711
text=00001000 bytes @ 0x100c8000
flags=00010571
debug=00000000
offset=0000000001805000
data=00001000 bytes @ 0x100da000
flags=00010332
debug=00012000
offset=0000000001807000
dev=0x802
ino=0xc00000a6
/dev/tymem/sysram&below1G
text=00001000 bytes @ 0x28000000
flags=01010b01
debug=28000000
offset=0000000000000000
dev=0x1
ino=0x331cd476
If you want to connect a debugger to qemu directly you could use.
(gdb-multiarch) add-symbol-file -s .text 0x100c8000 -s .data 0x100da000 a.out
The way the process mbox-bcm2711 maps memory
As the QNX process must write to the mailbox registers located at 0xfe00b880 some mapping of the virtual memory must be done.
Here is the condensed QNX code without error handling,
#define MBOX_SEND_CHANNEL 8
#define MBOX_SEND_CHANNEL_MASK 0xf
fd = posix_typed_mem_open("/sysram&below1G", O_RDWR, POSIX_TYPED_MEM_ALLOCATE_CONTIG);
out = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE | PROT_NOCACHE, MAP_SHARED, fd, 0);
if (mem_offset(out, NOFD, 1, &offset, 0) != EOK) {
...
}
physaddr = (uint32_t)offset;
msg = out;
msg->hdrsize = sizeof *msg;
msg->hdrcode = MBOX_PROCESS_REQUEST;
msg->tag = tag;
msg->tagsize = bufsiz;
msg->tagcode = MBOX_TAG_REQUEST;
memcpy(msg->buf, buf, bufsiz);
msg->tagend = MBOX_TAG_NULL;
#define MBOX_REGS 0xfe00b880
mbox = mbox ?: mmap_device_memory(0, 0x80, PROT_NOCACHE|PROT_READ|PROT_WRITE, 0, MBOX_REGS);
mbox[1].rdwr = (physaddr & ~MBOX_SEND_CHANNEL_MASK) | MBOX_SEND_CHANNEL;
Do not be fooled my the other mbox.c src/hardware/startup/boards/bcm2711/mbox.c This is used by the startup program where MBOX_REGS are mapped to address 0x100, I am not sure how this is done.
#define MBOX_TAG_GET_CMDLINE 0x00050001
In the qemu trace file you can see,
bcm2835_mbox_read mbox read sz:4 addr:0x98 data:0x40000000
bcm2835_mbox_irq mbox irq:ARM level:0
bcm2835_mbox_write mbox write sz:4 addr:0xa0 data:0x27008
bcm2835_mbox_property mbox property tag:0x00050001 in_sz:320 out_sz:37
bcm2835_mbox_irq mbox irq:ARM level:0
bcm2835_mbox_irq mbox irq:ARM level:0
bcm2835_mbox_read mbox read sz:4 addr:0x98 data:0x0
bcm2835_mbox_irq mbox irq:ARM level:0
bcm2835_mbox_read mbox read sz:4 addr:0x80 data:0x27008
bcm2835_mbox_irq mbox irq:ARM level:0
Connecting to the qemu debug stub.
This is not really necessary as we already have a working debug connection, but can allow other possibilities. I documentsome tips here.
Gdb
# Start GDB with your executable
gdb build/aarch64le-debug/test-proj
# Set sysroot and solib-search-path
(gdb) set sysroot ~/qnx700/target/qnx7/aarch64le
(gdb) set solib-search-path ~/qnx700/target/qnx7/aarch64le/usr/lib
# Connect to the target
(gdb) target remote localhost:1234
# Unload current symbols
(gdb) symbol-file
# Load symbols at correct address
(gdb) add-symbol-file build/aarch64le-debug/test-proj 0x100c8000 -s .text 0x100c8000 -s .data 0x100dc000 -s .bss 0x100cc000
# Verify that 'main' is correct
(gdb) info address main
Symbol "main(int, char**)" is a function at address 0x100d3714
# Load symbols for dynamic linker (if needed)
(gdb) add-symbol-file /home/olof/qnx700/target/qnx7/aarch64le/usr/lib/ldqnx-64.so.2 0xLD_ADDRESS
# Enable auto-loading of shared library symbols
(gdb) set auto-solib-add on
# Now, check the backtrace
(gdb) where
#0 main () at /path/to/source/main.c:10
(gdb) maintenance info sections
Look at where the process is loaded in memory,
/proc/20495/pmap
addr,size,flags,prot,maxprot,dev,ino,offset,rsv,guardsize,refcnt,mapcnt,path
0x0000000001000000,0x000000000009b000,0x00010071,0x00000005,0x0000000f,0x00000802,0x00000000c0000008,0x00000000000cd000,0x0000000000000000,0x0000000000000000,0x0000008a,0x00000088,/lib/libc.so.4
0x000000000109b000,0x0000000000010000,0x080800a2,0x00000000,0x0000000f,0x00000001,0x0000000000000003,0x000000000009b000,0x0000000000000000,0x0000000000000000,0x00000000,0x00000088,{bss}
0x00000000010ab000,0x0000000000004000,0x00010072,0x00000001,0x0000000f,0x00000802,0x00000000c0000008,0x0000000000168000,0x0000000000004000,0x0000000000000000,0x0000008a,0x00000088,/lib/libc.so.4
0x00000000010af000,0x0000000000002000,0x00010072,0x00000003,0x0000000f,0x00000802,0x00000000c0000008,0x000000000016c000,0x0000000000002000,0x0000000000000000,0x0000008a,0x00000088,/lib/libc.so.4
0x00000000010b1000,0x0000000000002000,0x00080032,0x00000003,0x0000000f,0x00000001,0x0000000000000003,0x0000000000000000,0x0000000000002000,0x0000000000000000,0x00000000,0x00000088,{bss}
0x0000000010048000,0x0000000000080000,0x00083082,0x00000007,0x0000000f,0x00000001,0x0000000000000003,0x0000000000000000,0x0000000000001000,0x0000000000001000,0x00000000,0x00000088,{stack}
0x00000000100c8000,0x0000000000004000,0x00010071,0x00000005,0x0000000f,0x00000802,0x00000000c000006b,0x000000000323a000,0x0000000000000000,0x0000000000000000,0x0000008a,0x00000088,/test-proj
0x00000000100cc000,0x000000000000f000,0x080800a2,0x00000000,0x0000000f,0x00000001,0x0000000000000003,0x0000000000004000,0x0000000000000000,0x0000000000000000,0x00000000,0x00000088,{bss}
0x00000000100db000,0x0000000000001000,0x00010032,0x00000001,0x0000000f,0x00000802,0x00000000c000006b,0x000000000323d000,0x0000000000001000,0x0000000000000000,0x0000008a,0x00000088,/test-proj
0x00000000100dc000,0x0000000000001000,0x00010032,0x00000003,0x0000000f,0x00000802,0x00000000c000006b,0x000000000323e000,0x0000000000001000,0x0000000000000000,0x0000008a,0x00000088,/test-proj
0x00000000100dd000,0x0000000000013000,0x00080002,0x00000003,0x0000000f,0x00000001,0x0000000000000003,0x0000000000000000,0x0000000000013000,0x0000000000000000,0x00000000,0x00000088,{heap}
0x0000000078000000,0x0000000000001000,0x00010071,0x00000005,0x0000000f,0x00000802,0x00000000c0000012,0x000000000031f000,0x0000000000000000,0x0000000000000000,0x0000008a,0x00000088,/usr/lib/libstringsa64.so.1
0x0000000078001000,0x000000000000f000,0x080800a2,0x00000000,0x0000000f,0x00000001,0x0000000000000003,0x0000000000001000,0x0000000000000000,0x0000000000000000,0x00000000,0x00000088,{bss}
0x0000000078010000,0x0000000000001000,0x000800b2,0x00000001,0x0000000f,0x00000001,0x0000000000000003,0x0000000000000000,0x0000000000001000,0x0000000000000000,0x00000000,0x00000088,{bss}
0x0000000078011000,0x0000000000001000,0x00010072,0x00000003,0x0000000f,0x00000802,0x00000000c0000012,0x0000000000320000,0x0000000000001000,0x0000000000000000,0x0000008a,0x00000088,/usr/lib/libstringsa6 there are no section groups in this file.
From this information you can identify the segments where to load the file
(gdb) add-symbol-file build/aarch64le-debug/test-proj 0x100c8000 -s .text 0x100c8000 -s .data 0x100dc000 -s .bss 0x100cc000
If we want to use a gdb debugger that does not know the process mapping used by qnx we, must know how qnx has mapped the process virtual memoryspace. This can be done like this.
(gdb) info proc mappings
(gdb) maintenance info sections
But the patched qnx version of gdb uses,
(gdb) info meminfo
I started to do a small program the extracts this information from the pmap, but it is not quite working. I will maybe fix this one day, but a better solution could be to
#include <stdlib.h>
#include <cstdio>
#include <vector>
#include <cstdlib>
#include <string>
#include <locale.h>
#include <thread>
#include <atomic>
#include <mutex>
#include <iostream>
#include <chrono>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
// q++ -g -Vgcc_ntoaarch64le test.cpp -L . -o test
#define PMAP_PATH "/proc/self/pmap"
#define MAX_LINE_LENGTH 512
// Structure to hold pmap entry data
typedef struct {
uintptr_t vaddr;
size_t size;
uint32_t flags;
uint32_t prot;
uint32_t maxprot;
uint32_t dev;
uint64_t ino;
uint64_t offset;
size_t rsv;
size_t guardsize;
uint32_t refcnt;
uint32_t mapcnt;
char path[MAX_LINE_LENGTH];
std::string lib_name;
} pmap_entry_t;
// q++ -Vgcc_ntoaarch64le -g test.cpp -L lib -I include -o test
#define FRAME_SIZE 1600*1296
#define MAX_FRAMES 4
// #define UNIT_TEST 1
#ifdef UNIT_TEST
char unit_test_text[]="vaddr,size,flags,prot,maxprot,dev,ino,offset,rsv,guardsize,refcnt,mapcnt,path\n\
0x0000000001000000,0x000000000009b000,0x00010071,0x00000005,0x0000000f,0x00000802,0x00000000c0000008,0x00000000000ce000,0x0000000000000000,0x0000000000000000,0x000000f1,0x000000ef,/lib/libc.so.4\n\
0x000000000109b000,0x0000000000010000,0x080800a2,0x00000000,0x0000000f,0x00000001,0x0000000000000003,0x000000000009b000,0x0000000000000000,0x0000000000000000,0x00000000,0x00000152,{bss}\n\
0x00000000010ab000,0x0000000000004000,0x00010072,0x00000001,0x0000000f,0x00000802,0x00000000c0000008,0x0000000000169000,0x0000000000004000,0x0000000000000000,0x000000f1,0x000000ef,/lib/libc.so.4\n\
0x00000000010af000,0x0000000000002000,0x00010072,0x00000003,0x0000000f,0x00000802,0x00000000c0000008,0x000000000016d000,0x0000000000002000,0x0000000000000000,0x000000f1,0x000000ef,/lib/libc.so.4\n\
0x00000000010b1000,0x0000000000002000,0x00080032,0x00000003,0x0000000f,0x00000001,0x0000000000000003,0x0000000000000000,0x0000000000002000,0x0000000000000000,0x00000000,0x00000152,{bss}\n\
0x0000000010048000,0x0000000000080000,0x00083082,0x00000007,0x0000000f,0x00000001,0x0000000000000003,0x0000000000000000,0x0000000000011000,0x0000000000001000,0x00000000,0x00000152,{stack}\n\
0x00000000105fb000,0x000000000000f000,0x080800a2,0x00000000,0x0000000f,0x00000001,0x0000000000000003,0x0000000000533000,0x0000000000000000,0x0000000000000000,0x00000000,0x00000152,{bss}\n\
0x000000001060e000,0x0000000000020000,0x00080032,0x00000003,0x0000000f,0x00000001,0x0000000000000003,0x0000000000000000,0x0000000000020000,0x0000000000000000,0x00000000,0x00000152,{bss}\n\
0x000000001062e000,0x00000000001d7000,0x00080002,0x00000003,0x0000000f,0x00000001,0x0000000000000003,0x0000000000000000,0x00000000001d7000,0x0000000000000000,0x00000000,0x00000152,{heap}\n\
0x000000001080a000,0x0000000000008000,0x00080002,0x00000003,0x0000000f,0x00000001,0x0000000000000003,0x0000000000000000,0x0000000000008000,0x0000000000000000,0x00000000,0x00000152,{heap}\n\
0x0000000010814000,0x0000000000114000,0x00080002,0x00000003,0x0000000f,0x00000001,0x0000000000000003,0x0000000000000000,0x0000000000114000,0x0000000000000000,0x00000000,0x00000152,{heap}\n\
0x0000000078db3000,0x0000000000010000,0x080800a2,0x00000000,0x0000000f,0x00000001,0x0000000000000003,0x0000000000007000,0x0000000000000000,0x0000000000000000,0x00000000,0x00000152,{bss}\n\
0x0000000078dc3000,0x0000000000001000,0x000800b2,0x00000001,0x0000000f,0x00000001,0x0000000000000003,0x0000000000000000,0x0000000000001000,0x0000000000000000,0x00000000,0x00000152,{bss}\n\
0x0000000078dc4000,0x0000000000001000,0x00010072,0x00000003,0x0000000f,0x00000802,0x00000000c000001c,0x0000000000346000,0x0000000000001000,0x0000000000000000,0x000000f1,0x000000ef,/lib/libslog2.so.1";
#endif
#include <string>
std::string basename(const std::string &input_path) {
// Start from the given path
std::string path = input_path;
// Handle empty strings by returning "."
if (path.empty()) {
return ".";
}
// Strip trailing slashes
while (!path.empty() && path.back() == '/') {
path.pop_back();
}
// If the string became empty after stripping, return "/"
// according to the behavior commonly expected of basename.
if (path.empty()) {
return "/";
}
// Find the last slash in the path
std::string::size_type pos = path.find_last_of('/');
if (pos == std::string::npos) {
// No slash found, entire path is the basename
return path;
}
// If the slash is the last character (e.g. "dir/"), return "."
if (pos == path.size() - 1) {
return ".";
}
// Return the substring after the last slash
return path.substr(pos + 1);
}
std::vector<double> timeMeasurements;
// read raw frame from file
void read_file(const char *filename, uint8_t *image, int max_data)
{
FILE *file = fopen(filename, "rb");
if (file)
{
fseek(file, 0, SEEK_END);
int data_length = ftell(file);
fseek(file, 0, SEEK_SET);
if (data_length > max_data)
{
data_length = max_data;
}
fread(image, 1, data_length, file);
fclose(file);
} else {
printf("Error: Could not open file %s\n", filename);
exit(1);
}
}
// Structure to hold section information
typedef struct {
uintptr_t vaddr;
char *section_name;
std::string lib_name;
} section_info_t;
// Function declarations
int read_pmap_entries(pmap_entry_t **entries, size_t *count);
const char *get_section_name(uint32_t flags);
void generate_gdb_command(const pmap_entry_t *entries, size_t count);
#define MAX_ENTRIES 100
pmap_entry_t g_entries[MAX_ENTRIES];
char buf[3*4096];
int read_pmap_entries(size_t *count) {
#ifndef UNIT_TEST
int fd = open(PMAP_PATH, O_RDONLY);
if (fd == -1) {
perror("Error opening pmap file");
return -1;
}
#endif
char *pmap_data = NULL;
size_t data_size = 0;
ssize_t bytes_read;
// Read the entire file into a dynamically allocated buffer
//unit_test_text
#ifdef UNIT_TEST
printf("unit_test_len=%d\n", sizeof(unit_test_text));
printf("malloc=%d\n", sizeof(unit_test_text)+1);
pmap_data=(char *)malloc(sizeof(unit_test_text)+1);
memcpy(buf, unit_test_text, sizeof(unit_test_text));
memcpy(pmap_data, unit_test_text, sizeof(unit_test_text));
data_size=sizeof(unit_test_text);
#else
while ((bytes_read = read(fd, buf, sizeof(buf))) > 0)
{
char *new_data = (char *)realloc(pmap_data, data_size + bytes_read + 1); // +1 for null terminator
if (new_data == NULL)
{
perror("Error reallocating memory");
free(pmap_data);
close(fd);
return -1;
}
pmap_data = new_data;
memcpy(pmap_data + data_size, buf, bytes_read);
data_size += bytes_read;
if (bytes_read == -1)
{
perror("Error reading pmap file");
free(pmap_data);
close(fd);
return -1;
}
}
close(fd);
#endif
if (pmap_data == NULL) {
fprintf(stderr, "Error: pmap file is empty\n");
return 0;
}
pmap_data[data_size] = '\0'; // Null-terminate the data
// Count the number of lines (entries)
*count = 0;
char *ptr = pmap_data;
while ((ptr = strchr(ptr, '\n'))) {
(*count)++;
ptr++;
}
// Parse each line
ptr = pmap_data;
int parsed_items=0;
for (size_t i = 0; i < *count; i++) {
char line[MAX_LINE_LENGTH];
char *next_line = strchr(ptr, '\n');
if (next_line == NULL) {
fprintf(stderr, "Error: unexpected end of data while parsing\n");
free(pmap_data);
return -1;
}
size_t line_length = next_line - ptr;
if (line_length >= MAX_LINE_LENGTH) {
fprintf(stderr, "Error: line too long\n");
free(pmap_data);
return -1;
}
strncpy(line, ptr, line_length);
line[line_length] = '\0';
ptr = next_line + 1;
if (i>0)
parsed_items = sscanf(line, "%lx,%lx,%lx,%lx,%lx,%lx,%llx,%llx,%lx,%lx,%lx,%lx,%s",
&g_entries[i].vaddr,
&g_entries[i].size,
&g_entries[i].flags,
&g_entries[i].prot,
&g_entries[i].maxprot,
&g_entries[i].dev,
&g_entries[i].ino,
&g_entries[i].offset,
&g_entries[i].rsv,
&g_entries[i].guardsize,
&g_entries[i].refcnt,
&g_entries[i].mapcnt,
&g_entries[i].path);
// Check if parsing was successful, allow for 12 or 13 parsed items (path is optional)
if (parsed_items < 12 ) {
fprintf(stderr, "Error parsing line: %s\n", line);
// First line is vaddr,size,flags,prot,maxprot,dev,ino,offset,rsv,guardsize,refcnt,mapcnt,path
if (i>0) {
//return -1;
}
parsed_items = 12;
} else {
fprintf(stderr, "%s\n", line);
}
if (parsed_items == 12) {
g_entries[i].path[0] = '\0';
}
}
free(pmap_data);
return 0;
}
typedef enum section_type {
TEXT,
DATA,
BSS,
STACK,
HEAP
} section_type_t;
const char *section_name[] = {
".text",
".data",
".bss",
".stack",
".heap"
};
const char *get_section_name(uint32_t flags) {
if (flags & 0x0001) return section_name[TEXT];
if (flags & 0x0002) return section_name[DATA];
if (flags & 0x0008) return section_name[BSS];
if (flags & 0x0080) return section_name[STACK];
if (flags & 0x0100) return section_name[HEAP];
return NULL;
}
void generate_gdb_command(size_t count) {
std::string program_name ;
std::string first_program_name ;
int section_count = 0;
uintptr_t base_addr = 0;
// Get the name of the program and base address (.text section)
for (size_t i = 0; i < count; i++) {
const char *section = get_section_name(g_entries[i].flags);
if (section && strcmp(section, ".text") == 0) {
base_addr = g_entries[i].vaddr;
if (g_entries[i].path[0] != '\0') {
char *path_copy = strdup(g_entries[i].path);
if (first_program_name.empty()) {
first_program_name = basename(path_copy);
}
program_name = basename(path_copy);
free(path_copy);
}
}
//printf("p %d=%s\n",i, program_name.c_str());
g_entries[i].lib_name = program_name;
}
if (!program_name.size()>0 || base_addr == 0) {
fprintf(stderr, "Could not determine program name or base address %d\n",count);
// return;
}
// Generate GDB comman
printf("add-symbol-file %s 0x%lx+0x9810\\", first_program_name.c_str(), g_entries[0].vaddr);
// Add each section
for (int i = 1; i < count; i++) {
if ((g_entries[i].flags & 0x1) != 0x1 ) { // Skip .text as it's handled in the base command
if ((g_entries[i+1].flags & 0x1) != 0x1 ) {
printf("-s %s 0x%lx \\\n", get_section_name(g_entries[i].flags) , g_entries[i].vaddr);
} else {
printf("-s %s 0x%lx ", get_section_name(g_entries[i].flags) , g_entries[i].vaddr);
}
} else {
printf("\nadd-symbol-file %s 0x%lx ", g_entries[i].lib_name.c_str(), g_entries[i].vaddr);
}
}
}
int pause_here() {
printf("Pause\n");
}
volatile int stop_here = 0;
int main(int argc, char *argv[])
{
(void) argc;
(void) argv;
// Write to null to crash, this forces the qnx crashlog to give us th e location on where to set the debugger
//char *p = NULL;
//*p = 0;
// setlocale(LC_ALL, "");
size_t count = 0;
std::cout << "Reading pmap entries\n";
while(stop_here == 1) {
pause_here();
}
if (read_pmap_entries(&count) == 0) {
generate_gdb_command(count);
}
}
Thats all folks. In the future I might turn this snippet in to a working program. In the next article I will try to get the frame buffer from the mailbox and make some simple drawing on the screen.