aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIlya Leoshkevich <iii@linux.ibm.com>2024-01-29 10:32:14 +0100
committerRichard Henderson <richard.henderson@linaro.org>2024-01-29 20:48:49 +1000
commit87ab270429618c13a6bf6dfc90d5edf6a3fa99b9 (patch)
tree15511588c261ce025f2bdcb3ce60f2788bfb8d4a
parentfd3f7d24d48d3a70afe30af60bdbacdb993af945 (diff)
downloadqemu-87ab270429618c13a6bf6dfc90d5edf6a3fa99b9.zip
qemu-87ab270429618c13a6bf6dfc90d5edf6a3fa99b9.tar.gz
qemu-87ab270429618c13a6bf6dfc90d5edf6a3fa99b9.tar.bz2
linux-user: Allow gdbstub to ignore page protection
gdbserver ignores page protection by virtue of using /proc/$pid/mem. Teach qemu gdbstub to do this too. This will not work if /proc is not mounted; accept this limitation. One alternative is to temporarily grant the missing PROT_* bit, but this is inherently racy. Another alternative is self-debugging with ptrace(POKE), which will break if QEMU itself is being debugged - a much more severe limitation. Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Ilya Leoshkevich <iii@linux.ibm.com> Message-Id: <20240129093410.3151-2-iii@linux.ibm.com> Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
-rw-r--r--cpu-target.c78
1 files changed, 63 insertions, 15 deletions
diff --git a/cpu-target.c b/cpu-target.c
index f6e07c3..958d63e 100644
--- a/cpu-target.c
+++ b/cpu-target.c
@@ -382,6 +382,9 @@ int cpu_memory_rw_debug(CPUState *cpu, vaddr addr,
vaddr l, page;
void * p;
uint8_t *buf = ptr;
+ ssize_t written;
+ int ret = -1;
+ int fd = -1;
while (len > 0) {
page = addr & TARGET_PAGE_MASK;
@@ -389,30 +392,75 @@ int cpu_memory_rw_debug(CPUState *cpu, vaddr addr,
if (l > len)
l = len;
flags = page_get_flags(page);
- if (!(flags & PAGE_VALID))
- return -1;
+ if (!(flags & PAGE_VALID)) {
+ goto out_close;
+ }
if (is_write) {
- if (!(flags & PAGE_WRITE))
- return -1;
- /* XXX: this code should not depend on lock_user */
- if (!(p = lock_user(VERIFY_WRITE, addr, l, 0)))
- return -1;
- memcpy(p, buf, l);
- unlock_user(p, addr, l);
- } else {
- if (!(flags & PAGE_READ))
- return -1;
+ if (flags & PAGE_WRITE) {
+ /* XXX: this code should not depend on lock_user */
+ p = lock_user(VERIFY_WRITE, addr, l, 0);
+ if (!p) {
+ goto out_close;
+ }
+ memcpy(p, buf, l);
+ unlock_user(p, addr, l);
+ } else {
+ /* Bypass the host page protection using ptrace. */
+ if (fd == -1) {
+ fd = open("/proc/self/mem", O_WRONLY);
+ if (fd == -1) {
+ goto out;
+ }
+ }
+ /*
+ * If there is a TranslationBlock and we weren't bypassing the
+ * host page protection, the memcpy() above would SEGV,
+ * ultimately leading to page_unprotect(). So invalidate the
+ * translations manually. Both invalidation and pwrite() must
+ * be under mmap_lock() in order to prevent the creation of
+ * another TranslationBlock in between.
+ */
+ mmap_lock();
+ tb_invalidate_phys_range(addr, addr + l - 1);
+ written = pwrite(fd, buf, l,
+ (off_t)(uintptr_t)g2h_untagged(addr));
+ mmap_unlock();
+ if (written != l) {
+ goto out_close;
+ }
+ }
+ } else if (flags & PAGE_READ) {
/* XXX: this code should not depend on lock_user */
- if (!(p = lock_user(VERIFY_READ, addr, l, 1)))
- return -1;
+ p = lock_user(VERIFY_READ, addr, l, 1);
+ if (!p) {
+ goto out_close;
+ }
memcpy(buf, p, l);
unlock_user(p, addr, 0);
+ } else {
+ /* Bypass the host page protection using ptrace. */
+ if (fd == -1) {
+ fd = open("/proc/self/mem", O_RDONLY);
+ if (fd == -1) {
+ goto out;
+ }
+ }
+ if (pread(fd, buf, l,
+ (off_t)(uintptr_t)g2h_untagged(addr)) != l) {
+ goto out_close;
+ }
}
len -= l;
buf += l;
addr += l;
}
- return 0;
+ ret = 0;
+out_close:
+ if (fd != -1) {
+ close(fd);
+ }
+out:
+ return ret;
}
#endif