* [PULL 01/13] linux-user: Introduce PGBRange
2026-07-10 23:01 [PULL 00/13] user patch queue Richard Henderson
@ 2026-07-10 23:01 ` Richard Henderson
2026-07-10 23:01 ` [PULL 02/13] linux-user: Use PGBRange in load_elf_image Richard Henderson
` (12 subsequent siblings)
13 siblings, 0 replies; 15+ messages in thread
From: Richard Henderson @ 2026-07-10 23:01 UTC (permalink / raw)
To: qemu-devel; +Cc: Warner Losh, Helge Deller, Philippe Mathieu-Daudé
Create a structure to hold a beginning/end range for guest virtual
addresses, for use by probe_guest_base. Use vaddr for clarity.
Reviewed-by: Warner Losh <imp@bsdimp.com>
Reviewed-by: Helge Deller <deller@gmx.de>
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
---
linux-user/user-internals.h | 5 +++++
linux-user/elfload.c | 36 ++++++++++++++++++------------------
2 files changed, 23 insertions(+), 18 deletions(-)
diff --git a/linux-user/user-internals.h b/linux-user/user-internals.h
index 0380d44fe9..b04ca43f02 100644
--- a/linux-user/user-internals.h
+++ b/linux-user/user-internals.h
@@ -75,6 +75,11 @@ void clone_fork_end(bool child);
void fork_start(void);
void fork_end(pid_t pid);
+typedef struct PGBRange {
+ vaddr lo;
+ vaddr hi;
+} PGBRange;
+
/**
* probe_guest_base:
* @image_name: the executable being loaded
diff --git a/linux-user/elfload.c b/linux-user/elfload.c
index 8049c8ae62..2566c2f54c 100644
--- a/linux-user/elfload.c
+++ b/linux-user/elfload.c
@@ -828,15 +828,15 @@ static int pgb_try_mmap_skip_brk(uintptr_t addr, uintptr_t addr_last,
*/
typedef struct PGBAddrs {
- uintptr_t bounds[3][2]; /* start/last pairs */
+ PGBRange bounds[3];
int nbounds;
} PGBAddrs;
static bool pgb_try_mmap_set(const PGBAddrs *ga, uintptr_t base, uintptr_t brk)
{
for (int i = ga->nbounds - 1; i >= 0; --i) {
- if (pgb_try_mmap_skip_brk(ga->bounds[i][0] + base,
- ga->bounds[i][1] + base,
+ if (pgb_try_mmap_skip_brk(ga->bounds[i].lo + base,
+ ga->bounds[i].hi + base,
brk, i == 0 && reserved_va) <= 0) {
return false;
}
@@ -875,26 +875,26 @@ static bool pgb_addr_set(PGBAddrs *ga, abi_ulong guest_loaddr,
n = 0;
if (reserved_va) {
- ga->bounds[n][0] = try_identity ? mmap_min_addr : 0;
- ga->bounds[n][1] = reserved_va;
+ ga->bounds[n].lo = try_identity ? mmap_min_addr : 0;
+ ga->bounds[n].hi = reserved_va;
n++;
/* LO_COMMPAGE and NULL handled by reserving from 0. */
} else {
/* Add any LO_COMMPAGE or NULL page. */
if (LO_COMMPAGE != -1) {
- ga->bounds[n][0] = 0;
- ga->bounds[n][1] = LO_COMMPAGE + TARGET_PAGE_SIZE - 1;
+ ga->bounds[n].lo = 0;
+ ga->bounds[n].hi = LO_COMMPAGE + TARGET_PAGE_SIZE - 1;
n++;
} else if (!try_identity) {
- ga->bounds[n][0] = 0;
- ga->bounds[n][1] = TARGET_PAGE_SIZE - 1;
+ ga->bounds[n].lo = 0;
+ ga->bounds[n].hi = TARGET_PAGE_SIZE - 1;
n++;
}
/* Add the guest image for ET_EXEC. */
if (guest_loaddr) {
- ga->bounds[n][0] = guest_loaddr;
- ga->bounds[n][1] = guest_hiaddr;
+ ga->bounds[n].lo = guest_loaddr;
+ ga->bounds[n].hi = guest_hiaddr;
n++;
}
}
@@ -909,8 +909,8 @@ static bool pgb_addr_set(PGBAddrs *ga, abi_ulong guest_loaddr,
/* Add any HI_COMMPAGE not covered by reserved_va. */
if (reserved_va < HI_COMMPAGE) {
- ga->bounds[n][0] = HI_COMMPAGE & qemu_real_host_page_mask();
- ga->bounds[n][1] = HI_COMMPAGE + TARGET_PAGE_SIZE - 1;
+ ga->bounds[n].lo = HI_COMMPAGE & qemu_real_host_page_mask();
+ ga->bounds[n].hi = HI_COMMPAGE + TARGET_PAGE_SIZE - 1;
n++;
}
@@ -976,8 +976,8 @@ static uintptr_t pgb_try_itree(const PGBAddrs *ga, uintptr_t base,
IntervalTreeRoot *root)
{
for (int i = ga->nbounds - 1; i >= 0; --i) {
- uintptr_t s = base + ga->bounds[i][0];
- uintptr_t l = base + ga->bounds[i][1];
+ uintptr_t s = base + ga->bounds[i].lo;
+ uintptr_t l = base + ga->bounds[i].hi;
IntervalTreeNode *n;
if (l < s) {
@@ -1076,9 +1076,9 @@ static void pgb_dynamic(const char *image_name, uintptr_t guest_loaddr,
"guest address mapping requirements", image_name);
for (int i = 0; i < ga.nbounds; ++i) {
- error_printf(" %0*" PRIx64 "-%0*" PRIx64 "\n",
- w, (uint64_t)ga.bounds[i][0],
- w, (uint64_t)ga.bounds[i][1]);
+ error_printf(" %0*" VADDR_PRIx "-%0*" VADDR_PRIx "\n",
+ w, ga.bounds[i].lo,
+ w, ga.bounds[i].hi);
}
exit(EXIT_FAILURE);
}
--
2.43.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* [PULL 02/13] linux-user: Use PGBRange in load_elf_image
2026-07-10 23:01 [PULL 00/13] user patch queue Richard Henderson
2026-07-10 23:01 ` [PULL 01/13] linux-user: Introduce PGBRange Richard Henderson
@ 2026-07-10 23:01 ` Richard Henderson
2026-07-10 23:01 ` [PULL 03/13] linux-user: Drop hiaddr out-of-range check in probe_guest_base Richard Henderson
` (11 subsequent siblings)
13 siblings, 0 replies; 15+ messages in thread
From: Richard Henderson @ 2026-07-10 23:01 UTC (permalink / raw)
To: qemu-devel; +Cc: Warner Losh, Helge Deller, Philippe Mathieu-Daudé
Collect into range instead of loaddr+hiaddr.
Reviewed-by: Warner Losh <imp@bsdimp.com>
Reviewed-by: Helge Deller <deller@gmx.de>
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
---
linux-user/elfload.c | 24 ++++++++++++------------
1 file changed, 12 insertions(+), 12 deletions(-)
diff --git a/linux-user/elfload.c b/linux-user/elfload.c
index 2566c2f54c..30d7beb562 100644
--- a/linux-user/elfload.c
+++ b/linux-user/elfload.c
@@ -1278,7 +1278,8 @@ static void load_elf_image(const char *image_name, const ImageSource *src,
char **pinterp_name)
{
g_autofree struct elf_phdr *phdr = NULL;
- abi_ulong load_addr, load_bias, loaddr, hiaddr, error, align;
+ PGBRange range = { -1, 0 };
+ abi_ulong load_addr, load_bias, error, align;
size_t reserve_size, align_size;
int i, prot_exec;
Error *err = NULL;
@@ -1318,19 +1319,18 @@ static void load_elf_image(const char *image_name, const ImageSource *src,
* Find the maximum size of the image and allocate an appropriate
* amount of memory to handle that. Locate the interpreter, if any.
*/
- loaddr = -1, hiaddr = 0;
align = 0;
info->exec_stack = EXSTACK_DEFAULT;
for (i = 0; i < ehdr->e_phnum; ++i) {
struct elf_phdr *eppnt = phdr + i;
if (eppnt->p_type == PT_LOAD) {
abi_ulong a = eppnt->p_vaddr & TARGET_PAGE_MASK;
- if (a < loaddr) {
- loaddr = a;
+ if (a < range.lo) {
+ range.lo = a;
}
a = eppnt->p_vaddr + eppnt->p_memsz - 1;
- if (a > hiaddr) {
- hiaddr = a;
+ if (a > range.hi) {
+ range.hi = a;
}
++info->nsegs;
align |= eppnt->p_align;
@@ -1361,7 +1361,7 @@ static void load_elf_image(const char *image_name, const ImageSource *src,
}
}
- load_addr = loaddr;
+ load_addr = range.lo;
align = pow2ceil(align);
@@ -1371,13 +1371,13 @@ static void load_elf_image(const char *image_name, const ImageSource *src,
* Make sure that the low address does not conflict with
* MMAP_MIN_ADDR or the QEMU application itself.
*/
- probe_guest_base(image_name, loaddr, hiaddr);
+ probe_guest_base(image_name, range.lo, range.hi);
} else {
/*
* The binary is dynamic, but we still need to
* select guest_base. In this case we pass a size.
*/
- probe_guest_base(image_name, 0, hiaddr - loaddr);
+ probe_guest_base(image_name, 0, range.hi - range.lo);
/*
* Avoid collision with the loader by providing a different
@@ -1414,7 +1414,7 @@ static void load_elf_image(const char *image_name, const ImageSource *src,
* In both cases, we will overwrite pages in this range with mappings
* from the executable.
*/
- reserve_size = (size_t)hiaddr - loaddr + 1;
+ reserve_size = range.hi - range.lo + 1;
align_size = reserve_size;
if (ehdr->e_type != ET_EXEC && align > qemu_real_host_page_size()) {
@@ -1443,7 +1443,7 @@ static void load_elf_image(const char *image_name, const ImageSource *src,
load_addr = align_addr;
}
- load_bias = load_addr - loaddr;
+ load_bias = load_addr - range.lo;
if (elf_is_fdpic(ehdr)) {
struct elf32_fdpic_loadseg *loadsegs = info->loadsegs =
@@ -1480,7 +1480,7 @@ static void load_elf_image(const char *image_name, const ImageSource *src,
info->start_data = -1;
info->end_data = 0;
/* Usual start for brk is after all sections of the main executable. */
- info->brk = TARGET_PAGE_ALIGN(hiaddr + load_bias);
+ info->brk = TARGET_PAGE_ALIGN(range.hi + load_bias);
info->elf_flags = ehdr->e_flags;
#ifdef TARGET_MIPS
info->use_k0_tls = (ehdr->e_flags & EF_MIPS_MACH) == EF_MIPS_MACH_OCTEON;
--
2.43.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* [PULL 03/13] linux-user: Drop hiaddr out-of-range check in probe_guest_base
2026-07-10 23:01 [PULL 00/13] user patch queue Richard Henderson
2026-07-10 23:01 ` [PULL 01/13] linux-user: Introduce PGBRange Richard Henderson
2026-07-10 23:01 ` [PULL 02/13] linux-user: Use PGBRange in load_elf_image Richard Henderson
@ 2026-07-10 23:01 ` Richard Henderson
2026-07-10 23:01 ` [PULL 04/13] linux-user: Pass image_range to probe_guest_base Richard Henderson
` (10 subsequent siblings)
13 siblings, 0 replies; 15+ messages in thread
From: Richard Henderson @ 2026-07-10 23:01 UTC (permalink / raw)
To: qemu-devel; +Cc: Warner Losh, Helge Deller
Since dropping 32-bit host support, a guest address cannot
overflow a host pointer. This means guest_hiaddr is unused
for relocatable images, so don't pass guest_hiaddr as size.
Reviewed-by: Warner Losh <imp@bsdimp.com>
Reviewed-by: Helge Deller <deller@gmx.de>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
---
linux-user/elfload.c | 26 +++++++-------------------
linux-user/flatload.c | 3 +--
2 files changed, 8 insertions(+), 21 deletions(-)
diff --git a/linux-user/elfload.c b/linux-user/elfload.c
index 30d7beb562..1367657d58 100644
--- a/linux-user/elfload.c
+++ b/linux-user/elfload.c
@@ -1092,20 +1092,11 @@ void probe_guest_base(const char *image_name, abi_ulong guest_loaddr,
uintptr_t align = MAX(SHMLBA, TARGET_PAGE_SIZE);
/* Sanity check the guest binary. */
- if (reserved_va) {
- if (guest_hiaddr > reserved_va) {
- error_report("%s: requires more than reserved virtual "
- "address space (0x%" PRIx64 " > 0x%lx)",
- image_name, (uint64_t)guest_hiaddr, reserved_va);
- exit(EXIT_FAILURE);
- }
- } else {
- if (guest_hiaddr != (uintptr_t)guest_hiaddr) {
- error_report("%s: requires more virtual address space "
- "than the host can provide (0x%" PRIx64 ")",
- image_name, (uint64_t)guest_hiaddr + 1);
- exit(EXIT_FAILURE);
- }
+ if (reserved_va && guest_hiaddr > reserved_va) {
+ error_report("%s: requires more than reserved virtual "
+ "address space (0x%" PRIx64 " > 0x%lx)",
+ image_name, (uint64_t)guest_hiaddr, reserved_va);
+ exit(EXIT_FAILURE);
}
if (have_guest_base) {
@@ -1373,11 +1364,8 @@ static void load_elf_image(const char *image_name, const ImageSource *src,
*/
probe_guest_base(image_name, range.lo, range.hi);
} else {
- /*
- * The binary is dynamic, but we still need to
- * select guest_base. In this case we pass a size.
- */
- probe_guest_base(image_name, 0, range.hi - range.lo);
+ /* The binary is dynamic; we still need to select guest_base. */
+ probe_guest_base(image_name, 0, 0);
/*
* Avoid collision with the loader by providing a different
diff --git a/linux-user/flatload.c b/linux-user/flatload.c
index 4beb3ed1b9..8abdd2aef2 100644
--- a/linux-user/flatload.c
+++ b/linux-user/flatload.c
@@ -261,8 +261,7 @@ static int load_flat_file(struct linux_binprm * bprm,
/*
* Allocate the address space.
*/
- probe_guest_base(bprm->filename, 0,
- text_len + data_len + extra + indx_len - 1);
+ probe_guest_base(bprm->filename, 0, 0);
/*
* there are a couple of cases here, the separate code/data
--
2.43.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* [PULL 04/13] linux-user: Pass image_range to probe_guest_base
2026-07-10 23:01 [PULL 00/13] user patch queue Richard Henderson
` (2 preceding siblings ...)
2026-07-10 23:01 ` [PULL 03/13] linux-user: Drop hiaddr out-of-range check in probe_guest_base Richard Henderson
@ 2026-07-10 23:01 ` Richard Henderson
2026-07-10 23:01 ` [PULL 05/13] linux-user: Use PGBRange for commpage Richard Henderson
` (9 subsequent siblings)
13 siblings, 0 replies; 15+ messages in thread
From: Richard Henderson @ 2026-07-10 23:01 UTC (permalink / raw)
To: qemu-devel; +Cc: Helge Deller, Warner Losh, Philippe Mathieu-Daudé
Pass a PGBRange structure instead of separate guest_loaddr
and guest_hiaddr parameters. This allows NULL to indicate
that the image is relocatable, so that image_range->lo == 0
is a valid fixed setting.
Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/1890
Tested-by: Helge Deller <deller@gmx.de>
Reviewed-by: Warner Losh <imp@bsdimp.com>
Reviewed-by: Helge Deller <deller@gmx.de>
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
---
linux-user/user-internals.h | 13 ++++-------
linux-user/elfload.c | 46 +++++++++++++++++--------------------
linux-user/flatload.c | 2 +-
3 files changed, 27 insertions(+), 34 deletions(-)
diff --git a/linux-user/user-internals.h b/linux-user/user-internals.h
index b04ca43f02..d7f346b0d0 100644
--- a/linux-user/user-internals.h
+++ b/linux-user/user-internals.h
@@ -83,24 +83,21 @@ typedef struct PGBRange {
/**
* probe_guest_base:
* @image_name: the executable being loaded
- * @loaddr: the lowest fixed address within the executable
- * @hiaddr: the highest fixed address within the executable
+ * @image_range: the fixed addresses within the executable
*
* Creates the initial guest address space in the host memory space.
*
- * If @loaddr == 0, then no address in the executable is fixed, i.e.
- * it is fully relocatable. In that case @hiaddr is the size of the
- * executable minus one.
+ * If @image_range is NULL, then no address in the executable is fixed,
+ * i.e. it is fully relocatable.
*
* This function will not return if a valid value for guest_base
* cannot be chosen. On return, the executable loader can expect
*
- * target_mmap(loaddr, hiaddr - loaddr + 1, ...)
+ * target_mmap(i->lo, i->hi - i->lo + 1, ...)
*
* to succeed.
*/
-void probe_guest_base(const char *image_name,
- abi_ulong loaddr, abi_ulong hiaddr);
+void probe_guest_base(const char *image_name, const PGBRange *image_range);
/* syscall.c */
int host_to_target_waitstatus(int status);
diff --git a/linux-user/elfload.c b/linux-user/elfload.c
index 1367657d58..19d2bad581 100644
--- a/linux-user/elfload.c
+++ b/linux-user/elfload.c
@@ -847,14 +847,13 @@ static bool pgb_try_mmap_set(const PGBAddrs *ga, uintptr_t base, uintptr_t brk)
/**
* pgb_addr_set:
* @ga: output set of guest addrs
- * @guest_loaddr: guest image low address
- * @guest_hiaddr: guest image high address
+ * @image_range: fixed guest image addresses
* @identity: create for identity mapping
*
* Fill in @ga with the image, COMMPAGE and NULL page.
*/
-static bool pgb_addr_set(PGBAddrs *ga, abi_ulong guest_loaddr,
- abi_ulong guest_hiaddr, bool try_identity)
+static bool pgb_addr_set(PGBAddrs *ga, const PGBRange *image_range,
+ bool try_identity)
{
int n;
@@ -866,7 +865,7 @@ static bool pgb_addr_set(PGBAddrs *ga, abi_ulong guest_loaddr,
if (LO_COMMPAGE != -1 && LO_COMMPAGE < mmap_min_addr) {
return false;
}
- if (guest_loaddr != 0 && guest_loaddr < mmap_min_addr) {
+ if (image_range && image_range->lo < mmap_min_addr) {
return false;
}
}
@@ -892,10 +891,8 @@ static bool pgb_addr_set(PGBAddrs *ga, abi_ulong guest_loaddr,
}
/* Add the guest image for ET_EXEC. */
- if (guest_loaddr) {
- ga->bounds[n].lo = guest_loaddr;
- ga->bounds[n].hi = guest_hiaddr;
- n++;
+ if (image_range) {
+ ga->bounds[n++] = *image_range;
}
}
@@ -928,8 +925,8 @@ static void pgb_fail_in_use(const char *image_name)
exit(EXIT_FAILURE);
}
-static void pgb_fixed(const char *image_name, uintptr_t guest_loaddr,
- uintptr_t guest_hiaddr, uintptr_t align)
+static void pgb_fixed(const char *image_name, const PGBRange *image_range,
+ uintptr_t align)
{
PGBAddrs ga;
uintptr_t brk = (uintptr_t)sbrk(0);
@@ -941,7 +938,7 @@ static void pgb_fixed(const char *image_name, uintptr_t guest_loaddr,
exit(EXIT_FAILURE);
}
- if (!pgb_addr_set(&ga, guest_loaddr, guest_hiaddr, !guest_base)
+ if (!pgb_addr_set(&ga, image_range, !guest_base)
|| !pgb_try_mmap_set(&ga, guest_base, brk)) {
pgb_fail_in_use(image_name);
}
@@ -1026,15 +1023,15 @@ static uintptr_t pgb_find_itree(const PGBAddrs *ga, IntervalTreeRoot *root,
return pgb_try_mmap_set(ga, base, brk) ? base : -1;
}
-static void pgb_dynamic(const char *image_name, uintptr_t guest_loaddr,
- uintptr_t guest_hiaddr, uintptr_t align)
+static void pgb_dynamic(const char *image_name, const PGBRange *image_range,
+ uintptr_t align)
{
IntervalTreeRoot *root;
uintptr_t brk, ret;
PGBAddrs ga;
/* Try the identity map first. */
- if (pgb_addr_set(&ga, guest_loaddr, guest_hiaddr, true)) {
+ if (pgb_addr_set(&ga, image_range, true)) {
brk = (uintptr_t)sbrk(0);
if (pgb_try_mmap_set(&ga, 0, brk)) {
guest_base = 0;
@@ -1046,7 +1043,7 @@ static void pgb_dynamic(const char *image_name, uintptr_t guest_loaddr,
* Rebuild the address set for non-identity map.
* This differs in the mapping of the guest NULL page.
*/
- pgb_addr_set(&ga, guest_loaddr, guest_hiaddr, false);
+ pgb_addr_set(&ga, image_range, false);
root = read_self_maps();
@@ -1085,24 +1082,23 @@ static void pgb_dynamic(const char *image_name, uintptr_t guest_loaddr,
guest_base = ret;
}
-void probe_guest_base(const char *image_name, abi_ulong guest_loaddr,
- abi_ulong guest_hiaddr)
+void probe_guest_base(const char *image_name, const PGBRange *image_range)
{
/* In order to use host shmat, we must be able to honor SHMLBA. */
uintptr_t align = MAX(SHMLBA, TARGET_PAGE_SIZE);
/* Sanity check the guest binary. */
- if (reserved_va && guest_hiaddr > reserved_va) {
+ if (reserved_va && image_range && image_range->hi > reserved_va) {
error_report("%s: requires more than reserved virtual "
- "address space (0x%" PRIx64 " > 0x%lx)",
- image_name, (uint64_t)guest_hiaddr, reserved_va);
+ "address space (0x%" VADDR_PRIx " > 0x%lx)",
+ image_name, image_range->hi, reserved_va);
exit(EXIT_FAILURE);
}
if (have_guest_base) {
- pgb_fixed(image_name, guest_loaddr, guest_hiaddr, align);
+ pgb_fixed(image_name, image_range, align);
} else {
- pgb_dynamic(image_name, guest_loaddr, guest_hiaddr, align);
+ pgb_dynamic(image_name, image_range, align);
}
/* Reserve and initialize the commpage. */
@@ -1362,10 +1358,10 @@ static void load_elf_image(const char *image_name, const ImageSource *src,
* Make sure that the low address does not conflict with
* MMAP_MIN_ADDR or the QEMU application itself.
*/
- probe_guest_base(image_name, range.lo, range.hi);
+ probe_guest_base(image_name, &range);
} else {
/* The binary is dynamic; we still need to select guest_base. */
- probe_guest_base(image_name, 0, 0);
+ probe_guest_base(image_name, NULL);
/*
* Avoid collision with the loader by providing a different
diff --git a/linux-user/flatload.c b/linux-user/flatload.c
index 8abdd2aef2..45d69040c6 100644
--- a/linux-user/flatload.c
+++ b/linux-user/flatload.c
@@ -261,7 +261,7 @@ static int load_flat_file(struct linux_binprm * bprm,
/*
* Allocate the address space.
*/
- probe_guest_base(bprm->filename, 0, 0);
+ probe_guest_base(bprm->filename, NULL);
/*
* there are a couple of cases here, the separate code/data
--
2.43.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* [PULL 05/13] linux-user: Use PGBRange for commpage
2026-07-10 23:01 [PULL 00/13] user patch queue Richard Henderson
` (3 preceding siblings ...)
2026-07-10 23:01 ` [PULL 04/13] linux-user: Pass image_range to probe_guest_base Richard Henderson
@ 2026-07-10 23:01 ` Richard Henderson
2026-07-10 23:01 ` [PULL 06/13] common-user: Move mmap_min_addr from linux-user Richard Henderson
` (8 subsequent siblings)
13 siblings, 0 replies; 15+ messages in thread
From: Richard Henderson @ 2026-07-10 23:01 UTC (permalink / raw)
To: qemu-devel; +Cc: Warner Losh, Helge Deller, Philippe Mathieu-Daudé
This simplifies check for no commpage to a NULL pointer
check, rather than reserved values for LO_COMMPAGE and
HI_COMMPAGE.
Unify {LO,HI}_COMMPAGE into a single COMMPAGE define.
Acked-by: Warner Losh <imp@bsdimp.com>
Tested-by: Helge Deller <deller@gmx.de>
Reviewed-by: Helge Deller <deller@gmx.de>
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
---
linux-user/arm/target_elf.h | 2 +-
linux-user/hppa/target_elf.h | 2 +-
linux-user/arm/elfload.c | 2 +-
linux-user/elfload.c | 64 ++++++++++++++----------------------
linux-user/hppa/elfload.c | 4 +--
5 files changed, 30 insertions(+), 44 deletions(-)
diff --git a/linux-user/arm/target_elf.h b/linux-user/arm/target_elf.h
index 12cdc8e5a7..531b486bf9 100644
--- a/linux-user/arm/target_elf.h
+++ b/linux-user/arm/target_elf.h
@@ -20,7 +20,7 @@
#define HAVE_ELF_CORE_DUMP 1
#define HAVE_VDSO_IMAGE_INFO 1
-#define HI_COMMPAGE ((intptr_t)0xffff0f00u)
+#define COMMPAGE ((intptr_t)0xffff0f00u)
/*
* See linux kernel: arch/arm/include/asm/elf.h, where
diff --git a/linux-user/hppa/target_elf.h b/linux-user/hppa/target_elf.h
index e1c5033242..22547b1437 100644
--- a/linux-user/hppa/target_elf.h
+++ b/linux-user/hppa/target_elf.h
@@ -34,7 +34,7 @@ typedef struct target_elf_gregset_t {
abi_ulong pad[16]; /* pad to 80 elements [64..79] */
} target_elf_gregset_t;
-#define LO_COMMPAGE 0
+#define COMMPAGE 0
#define STACK_GROWS_DOWN 0
#define STACK_ALIGNMENT 64
#define VDSO_HEADER "vdso.c.inc"
diff --git a/linux-user/arm/elfload.c b/linux-user/arm/elfload.c
index 13b589ea2c..ce69bf0542 100644
--- a/linux-user/arm/elfload.c
+++ b/linux-user/arm/elfload.c
@@ -220,7 +220,7 @@ bool init_guest_commpage(void)
return true;
}
- commpage = HI_COMMPAGE & -host_page_size;
+ commpage = COMMPAGE & -host_page_size;
want = g2h_untagged(commpage);
addr = mmap(want, host_page_size, PROT_READ | PROT_WRITE,
MAP_ANONYMOUS | MAP_PRIVATE |
diff --git a/linux-user/elfload.c b/linux-user/elfload.c
index 19d2bad581..557ed20dbf 100644
--- a/linux-user/elfload.c
+++ b/linux-user/elfload.c
@@ -757,17 +757,9 @@ static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc,
return sp;
}
-#if defined(HI_COMMPAGE)
-#define LO_COMMPAGE -1
-#elif defined(LO_COMMPAGE)
-#define HI_COMMPAGE 0
-#else
-#define HI_COMMPAGE 0
-#define LO_COMMPAGE -1
-#ifndef HAVE_GUEST_COMMPAGE
+#if !defined(COMMPAGE) && !defined(HAVE_GUEST_COMMPAGE)
bool init_guest_commpage(void) { return true; }
#endif
-#endif
/**
* pgb_try_mmap:
@@ -853,7 +845,7 @@ static bool pgb_try_mmap_set(const PGBAddrs *ga, uintptr_t base, uintptr_t brk)
* Fill in @ga with the image, COMMPAGE and NULL page.
*/
static bool pgb_addr_set(PGBAddrs *ga, const PGBRange *image_range,
- bool try_identity)
+ const PGBRange *commpage_range, bool try_identity)
{
int n;
@@ -862,7 +854,7 @@ static bool pgb_addr_set(PGBAddrs *ga, const PGBRange *image_range,
* we may not be able to use the identity map.
*/
if (try_identity) {
- if (LO_COMMPAGE != -1 && LO_COMMPAGE < mmap_min_addr) {
+ if (commpage_range && commpage_range->lo < mmap_min_addr) {
return false;
}
if (image_range && image_range->lo < mmap_min_addr) {
@@ -877,14 +869,10 @@ static bool pgb_addr_set(PGBAddrs *ga, const PGBRange *image_range,
ga->bounds[n].lo = try_identity ? mmap_min_addr : 0;
ga->bounds[n].hi = reserved_va;
n++;
- /* LO_COMMPAGE and NULL handled by reserving from 0. */
+ /* Low COMMPAGE and NULL handled by reserving from 0. */
} else {
- /* Add any LO_COMMPAGE or NULL page. */
- if (LO_COMMPAGE != -1) {
- ga->bounds[n].lo = 0;
- ga->bounds[n].hi = LO_COMMPAGE + TARGET_PAGE_SIZE - 1;
- n++;
- } else if (!try_identity) {
+ /* Add any low COMMPAGE or NULL page. */
+ if (!try_identity || (commpage_range && commpage_range->lo == 0)) {
ga->bounds[n].lo = 0;
ga->bounds[n].hi = TARGET_PAGE_SIZE - 1;
n++;
@@ -896,23 +884,13 @@ static bool pgb_addr_set(PGBAddrs *ga, const PGBRange *image_range,
}
}
- /*
- * Temporarily disable
- * "comparison is always false due to limited range of data type"
- * due to comparison between unsigned and (possible) 0.
- */
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wtype-limits"
-
- /* Add any HI_COMMPAGE not covered by reserved_va. */
- if (reserved_va < HI_COMMPAGE) {
- ga->bounds[n].lo = HI_COMMPAGE & qemu_real_host_page_mask();
- ga->bounds[n].hi = HI_COMMPAGE + TARGET_PAGE_SIZE - 1;
+ /* Add any high COMMPAGE not covered by reserved_va. */
+ if (commpage_range && reserved_va < commpage_range->hi) {
+ ga->bounds[n].lo = commpage_range->lo & qemu_real_host_page_mask();
+ ga->bounds[n].hi = commpage_range->hi;
n++;
}
-#pragma GCC diagnostic pop
-
ga->nbounds = n;
return true;
}
@@ -926,7 +904,7 @@ static void pgb_fail_in_use(const char *image_name)
}
static void pgb_fixed(const char *image_name, const PGBRange *image_range,
- uintptr_t align)
+ const PGBRange *commpage_range, uintptr_t align)
{
PGBAddrs ga;
uintptr_t brk = (uintptr_t)sbrk(0);
@@ -938,7 +916,7 @@ static void pgb_fixed(const char *image_name, const PGBRange *image_range,
exit(EXIT_FAILURE);
}
- if (!pgb_addr_set(&ga, image_range, !guest_base)
+ if (!pgb_addr_set(&ga, image_range, commpage_range, !guest_base)
|| !pgb_try_mmap_set(&ga, guest_base, brk)) {
pgb_fail_in_use(image_name);
}
@@ -1024,14 +1002,14 @@ static uintptr_t pgb_find_itree(const PGBAddrs *ga, IntervalTreeRoot *root,
}
static void pgb_dynamic(const char *image_name, const PGBRange *image_range,
- uintptr_t align)
+ const PGBRange *commpage_range, uintptr_t align)
{
IntervalTreeRoot *root;
uintptr_t brk, ret;
PGBAddrs ga;
/* Try the identity map first. */
- if (pgb_addr_set(&ga, image_range, true)) {
+ if (pgb_addr_set(&ga, image_range, commpage_range, true)) {
brk = (uintptr_t)sbrk(0);
if (pgb_try_mmap_set(&ga, 0, brk)) {
guest_base = 0;
@@ -1043,7 +1021,7 @@ static void pgb_dynamic(const char *image_name, const PGBRange *image_range,
* Rebuild the address set for non-identity map.
* This differs in the mapping of the guest NULL page.
*/
- pgb_addr_set(&ga, image_range, false);
+ pgb_addr_set(&ga, image_range, commpage_range, false);
root = read_self_maps();
@@ -1084,6 +1062,14 @@ static void pgb_dynamic(const char *image_name, const PGBRange *image_range,
void probe_guest_base(const char *image_name, const PGBRange *image_range)
{
+#ifdef COMMPAGE
+ const PGBRange * const commpage_range = &(PGBRange){
+ COMMPAGE, COMMPAGE + TARGET_PAGE_SIZE - 1
+ };
+#else
+ const PGBRange * const commpage_range = NULL;
+#endif
+
/* In order to use host shmat, we must be able to honor SHMLBA. */
uintptr_t align = MAX(SHMLBA, TARGET_PAGE_SIZE);
@@ -1096,9 +1082,9 @@ void probe_guest_base(const char *image_name, const PGBRange *image_range)
}
if (have_guest_base) {
- pgb_fixed(image_name, image_range, align);
+ pgb_fixed(image_name, image_range, commpage_range, align);
} else {
- pgb_dynamic(image_name, image_range, align);
+ pgb_dynamic(image_name, image_range, commpage_range, align);
}
/* Reserve and initialize the commpage. */
diff --git a/linux-user/hppa/elfload.c b/linux-user/hppa/elfload.c
index 2ebb9924f1..ff4301b2ed 100644
--- a/linux-user/hppa/elfload.c
+++ b/linux-user/hppa/elfload.c
@@ -36,7 +36,7 @@ bool init_guest_commpage(void)
if (!reserved_va) {
void *want, *addr;
- want = g2h_untagged(LO_COMMPAGE);
+ want = g2h_untagged(COMMPAGE);
addr = mmap(want, TARGET_PAGE_SIZE, PROT_NONE,
MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED_NOREPLACE, -1, 0);
if (addr == MAP_FAILED) {
@@ -55,7 +55,7 @@ bool init_guest_commpage(void)
* and implement syscalls. Here, simply mark the page executable.
* Special case the entry points during translation (see do_page_zero).
*/
- page_set_flags(LO_COMMPAGE, LO_COMMPAGE | ~TARGET_PAGE_MASK,
+ page_set_flags(COMMPAGE, COMMPAGE | ~TARGET_PAGE_MASK,
PAGE_EXEC | PAGE_VALID, PAGE_VALID);
return true;
}
--
2.43.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* [PULL 06/13] common-user: Move mmap_min_addr from linux-user
2026-07-10 23:01 [PULL 00/13] user patch queue Richard Henderson
` (4 preceding siblings ...)
2026-07-10 23:01 ` [PULL 05/13] linux-user: Use PGBRange for commpage Richard Henderson
@ 2026-07-10 23:01 ` Richard Henderson
2026-07-10 23:01 ` [PULL 07/13] common-user: Initialize mmap_min_addr for FreeBSD Richard Henderson
` (7 subsequent siblings)
13 siblings, 0 replies; 15+ messages in thread
From: Richard Henderson @ 2026-07-10 23:01 UTC (permalink / raw)
To: qemu-devel; +Cc: Helge Deller, Warner Losh
Introduce user/mmap-min-addr.h. Initialize the variable
from a constructor instead of main.
Reviewed-by: Helge Deller <deller@gmx.de>
Reviewed-by: Warner Losh <imp@bsdimp.com>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
---
include/user/mmap-min-addr.h | 12 ++++++++++++
linux-user/user-internals.h | 1 -
common-user/mmap-min-addr.c | 37 ++++++++++++++++++++++++++++++++++++
linux-user/elfload.c | 1 +
linux-user/main.c | 33 +++-----------------------------
linux-user/mmap.c | 1 +
common-user/meson.build | 1 +
7 files changed, 55 insertions(+), 31 deletions(-)
create mode 100644 include/user/mmap-min-addr.h
create mode 100644 common-user/mmap-min-addr.c
diff --git a/include/user/mmap-min-addr.h b/include/user/mmap-min-addr.h
new file mode 100644
index 0000000000..ded0b6909d
--- /dev/null
+++ b/include/user/mmap-min-addr.h
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#ifndef USER_MMAP_MIN_ADDR_H
+#define USER_MMAP_MIN_ADDR_H
+
+#ifndef CONFIG_USER_ONLY
+#error Cannot include this header from system emulation
+#endif
+
+extern uintptr_t mmap_min_addr;
+
+#endif
diff --git a/linux-user/user-internals.h b/linux-user/user-internals.h
index d7f346b0d0..74d8eaabbe 100644
--- a/linux-user/user-internals.h
+++ b/linux-user/user-internals.h
@@ -29,7 +29,6 @@ void init_task_state(TaskState *ts);
void task_settid(TaskState *);
void stop_all_tasks(void);
extern const char *qemu_uname_release;
-extern unsigned long mmap_min_addr;
typedef struct IOCTLEntry IOCTLEntry;
diff --git a/common-user/mmap-min-addr.c b/common-user/mmap-min-addr.c
new file mode 100644
index 0000000000..b2b3762386
--- /dev/null
+++ b/common-user/mmap-min-addr.c
@@ -0,0 +1,37 @@
+/*
+ * Utility function to get the minimum mmap address.
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "user/mmap-min-addr.h"
+
+uintptr_t mmap_min_addr;
+
+static void __attribute__((constructor)) init(void)
+{
+#ifdef __linux__
+ /*
+ * We prefer to not make NULL pointers accessible to QEMU.
+ * If something goes wrong below, fall back to 1 page.
+ */
+ size_t min_addr = qemu_real_host_page_size();
+ /*
+ * Read in mmap_min_addr kernel parameter. This value is used
+ * When loading the ELF image to determine whether guest_base
+ * is needed. It is also used in mmap_find_vma.
+ */
+ FILE *fp = fopen("/proc/sys/vm/mmap_min_addr", "r");
+
+ if (fp) {
+ unsigned long tmp;
+ if (fscanf(fp, "%lu", &tmp) == 1 && tmp != 0) {
+ min_addr = MAX(min_addr, tmp);
+ }
+ fclose(fp);
+ }
+ mmap_min_addr = min_addr;
+#else
+# error
+#endif
+}
diff --git a/linux-user/elfload.c b/linux-user/elfload.c
index 557ed20dbf..0671aba537 100644
--- a/linux-user/elfload.c
+++ b/linux-user/elfload.c
@@ -14,6 +14,7 @@
#include "exec/translation-block.h"
#include "exec/tswap.h"
#include "user/guest-base.h"
+#include "user/mmap-min-addr.h"
#include "user-internals.h"
#include "signal-common.h"
#include "loader.h"
diff --git a/linux-user/main.c b/linux-user/main.c
index c08c73fd80..01cf5bf079 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -39,6 +39,7 @@
#include "qemu/module.h"
#include "qemu/plugin.h"
#include "user/guest-base.h"
+#include "user/mmap-min-addr.h"
#include "user/page-protection.h"
#include "exec/gdbstub.h"
#include "gdbstub/user.h"
@@ -78,7 +79,6 @@ static envlist_t *envlist;
static const char *cpu_model;
static const char *cpu_type;
static const char *seed_optarg;
-unsigned long mmap_min_addr;
uintptr_t guest_base;
bool have_guest_base;
@@ -914,35 +914,8 @@ int main(int argc, char **argv, char **envp)
target_environ = envlist_to_environ(envlist, NULL);
envlist_free(envlist);
- /*
- * Read in mmap_min_addr kernel parameter. This value is used
- * When loading the ELF image to determine whether guest_base
- * is needed. It is also used in mmap_find_vma.
- */
- {
- FILE *fp;
-
- if ((fp = fopen("/proc/sys/vm/mmap_min_addr", "r")) != NULL) {
- unsigned long tmp;
- if (fscanf(fp, "%lu", &tmp) == 1 && tmp != 0) {
- mmap_min_addr = MAX(tmp, host_page_size);
- qemu_log_mask(CPU_LOG_PAGE, "host mmap_min_addr=0x%lx\n",
- mmap_min_addr);
- }
- fclose(fp);
- }
- }
-
- /*
- * We prefer to not make NULL pointers accessible to QEMU.
- * If we're in a chroot with no /proc, fall back to 1 page.
- */
- if (mmap_min_addr == 0) {
- mmap_min_addr = host_page_size;
- qemu_log_mask(CPU_LOG_PAGE,
- "host mmap_min_addr=0x%lx (fallback)\n",
- mmap_min_addr);
- }
+ qemu_log_mask(CPU_LOG_PAGE, "host mmap_min_addr=0x%" PRIxPTR "\n",
+ mmap_min_addr);
/*
* Prepare copy of argv vector for target.
diff --git a/linux-user/mmap.c b/linux-user/mmap.c
index 55277f7c3e..cc0c2ee6c2 100644
--- a/linux-user/mmap.c
+++ b/linux-user/mmap.c
@@ -24,6 +24,7 @@
#include "exec/mmap-lock.h"
#include "qemu.h"
#include "user/page-protection.h"
+#include "user/mmap-min-addr.h"
#include "user-internals.h"
#include "user-mmap.h"
#include "target_mman.h"
diff --git a/common-user/meson.build b/common-user/meson.build
index ac9de5b9e3..f9e2e83f9a 100644
--- a/common-user/meson.build
+++ b/common-user/meson.build
@@ -5,6 +5,7 @@ endif
common_user_inc += include_directories('host/' / host_arch)
user_ss.add(files(
+ 'mmap-min-addr.c',
'safe-syscall.S',
'safe-syscall-error.c',
))
--
2.43.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* [PULL 07/13] common-user: Initialize mmap_min_addr for FreeBSD
2026-07-10 23:01 [PULL 00/13] user patch queue Richard Henderson
` (5 preceding siblings ...)
2026-07-10 23:01 ` [PULL 06/13] common-user: Move mmap_min_addr from linux-user Richard Henderson
@ 2026-07-10 23:01 ` Richard Henderson
2026-07-10 23:01 ` [PULL 08/13] include/user/guest-host: Include missing cpu.h Richard Henderson
` (6 subsequent siblings)
13 siblings, 0 replies; 15+ messages in thread
From: Richard Henderson @ 2026-07-10 23:01 UTC (permalink / raw)
To: qemu-devel; +Cc: Helge Deller, Warner Losh
Use sysctl to fetch the vm layout of the current process.
Reviewed-by: Helge Deller <deller@gmx.de>
Reviewed-by: Warner Losh <imp@bsdimp.com>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
---
common-user/mmap-min-addr.c | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/common-user/mmap-min-addr.c b/common-user/mmap-min-addr.c
index b2b3762386..830c6f66a0 100644
--- a/common-user/mmap-min-addr.c
+++ b/common-user/mmap-min-addr.c
@@ -5,6 +5,10 @@
#include "qemu/osdep.h"
#include "user/mmap-min-addr.h"
+#ifdef __FreeBSD__
+#include <sys/sysctl.h>
+#include <sys/user.h>
+#endif
uintptr_t mmap_min_addr;
@@ -31,6 +35,15 @@ static void __attribute__((constructor)) init(void)
fclose(fp);
}
mmap_min_addr = min_addr;
+#elif defined(__FreeBSD__)
+ int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_VM_LAYOUT, getpid() };
+ struct kinfo_vm_layout info;
+ size_t info_len = sizeof(info);
+
+ mmap_min_addr =
+ (sysctl(mib, ARRAY_SIZE(mib), &info, &info_len, NULL, 0) < 0
+ ? qemu_real_host_page_size()
+ : info.kvm_min_user_addr);
#else
# error
#endif
--
2.43.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* [PULL 08/13] include/user/guest-host: Include missing cpu.h
2026-07-10 23:01 [PULL 00/13] user patch queue Richard Henderson
` (6 preceding siblings ...)
2026-07-10 23:01 ` [PULL 07/13] common-user: Initialize mmap_min_addr for FreeBSD Richard Henderson
@ 2026-07-10 23:01 ` Richard Henderson
2026-07-10 23:01 ` [PULL 09/13] common-user: Move selfmap from util Richard Henderson
` (5 subsequent siblings)
13 siblings, 0 replies; 15+ messages in thread
From: Richard Henderson @ 2026-07-10 23:01 UTC (permalink / raw)
To: qemu-devel; +Cc: Helge Deller, Warner Losh, Philippe Mathieu-Daudé
This file dereferences CPUState without including the required header.
Reviewed-by: Helge Deller <deller@gmx.de>
Reviewed-by: Warner Losh <imp@bsdimp.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
---
include/user/guest-host.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/include/user/guest-host.h b/include/user/guest-host.h
index ef83ad8a18..506efc097e 100644
--- a/include/user/guest-host.h
+++ b/include/user/guest-host.h
@@ -10,6 +10,7 @@
#include "exec/vaddr.h"
#include "user/guest-base.h"
+#include "hw/core/cpu.h"
#include "accel/tcg/cpu-ops.h"
/*
--
2.43.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* [PULL 09/13] common-user: Move selfmap from util
2026-07-10 23:01 [PULL 00/13] user patch queue Richard Henderson
` (7 preceding siblings ...)
2026-07-10 23:01 ` [PULL 08/13] include/user/guest-host: Include missing cpu.h Richard Henderson
@ 2026-07-10 23:01 ` Richard Henderson
2026-07-10 23:01 ` [PULL 10/13] common-user: Implement read_self_maps for FreeBSD Richard Henderson
` (4 subsequent siblings)
13 siblings, 0 replies; 15+ messages in thread
From: Richard Henderson @ 2026-07-10 23:01 UTC (permalink / raw)
To: qemu-devel; +Cc: Helge Deller, Warner Losh, Philippe Mathieu-Daudé
This interface is only used for user-only. It's more usefully
placed within common-user than util. Temporarily add stub
implementation for bsd-user.
Reviewed-by: Helge Deller <deller@gmx.de>
Reviewed-by: Warner Losh <imp@bsdimp.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
---
include/{qemu => user}/selfmap.h | 0
{util => common-user}/selfmap.c | 6 +++++-
linux-user/elfload.c | 2 +-
linux-user/syscall.c | 2 +-
common-user/meson.build | 1 +
util/meson.build | 4 ----
6 files changed, 8 insertions(+), 7 deletions(-)
rename include/{qemu => user}/selfmap.h (100%)
rename {util => common-user}/selfmap.c (97%)
diff --git a/include/qemu/selfmap.h b/include/user/selfmap.h
similarity index 100%
rename from include/qemu/selfmap.h
rename to include/user/selfmap.h
diff --git a/util/selfmap.c b/common-user/selfmap.c
similarity index 97%
rename from util/selfmap.c
rename to common-user/selfmap.c
index 483cb617e2..a1b88dee66 100644
--- a/util/selfmap.c
+++ b/common-user/selfmap.c
@@ -8,10 +8,11 @@
#include "qemu/osdep.h"
#include "qemu/cutils.h"
-#include "qemu/selfmap.h"
+#include "user/selfmap.h"
IntervalTreeRoot *read_self_maps(void)
{
+#ifdef __linux__
IntervalTreeRoot *root;
gchar *maps, **lines;
guint i, nlines;
@@ -80,6 +81,9 @@ IntervalTreeRoot *read_self_maps(void)
g_free(maps);
return root;
+#else
+ return NULL;
+#endif
}
/**
diff --git a/linux-user/elfload.c b/linux-user/elfload.c
index 0671aba537..207f814e15 100644
--- a/linux-user/elfload.c
+++ b/linux-user/elfload.c
@@ -25,7 +25,7 @@
#include "qemu/queue.h"
#include "qemu/guest-random.h"
#include "qemu/units.h"
-#include "qemu/selfmap.h"
+#include "user/selfmap.h"
#include "qemu/lockable.h"
#include "qapi/error.h"
#include "qemu/error-report.h"
diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index d257fb9ca9..b3572a6e36 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -141,7 +141,7 @@
#include "user/safe-syscall.h"
#include "user/signal.h"
#include "qemu/guest-random.h"
-#include "qemu/selfmap.h"
+#include "user/selfmap.h"
#include "special-errno.h"
#include "qapi/error.h"
#include "fd-trans.h"
diff --git a/common-user/meson.build b/common-user/meson.build
index f9e2e83f9a..831a7273fb 100644
--- a/common-user/meson.build
+++ b/common-user/meson.build
@@ -8,4 +8,5 @@ user_ss.add(files(
'mmap-min-addr.c',
'safe-syscall.S',
'safe-syscall-error.c',
+ 'selfmap.c',
))
diff --git a/util/meson.build b/util/meson.build
index e29cbd948a..fa174c07a5 100644
--- a/util/meson.build
+++ b/util/meson.build
@@ -67,10 +67,6 @@ util_ss.add(files('memalign.c'))
util_ss.add(files('interval-tree.c'))
util_ss.add(files('lockcnt.c'))
-if have_user
- util_ss.add(files('selfmap.c'))
-endif
-
if have_system
util_ss.add(files('crc-ccitt.c'))
util_ss.add(when: gio, if_true: files('dbus.c'))
--
2.43.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* [PULL 10/13] common-user: Implement read_self_maps for FreeBSD
2026-07-10 23:01 [PULL 00/13] user patch queue Richard Henderson
` (8 preceding siblings ...)
2026-07-10 23:01 ` [PULL 09/13] common-user: Move selfmap from util Richard Henderson
@ 2026-07-10 23:01 ` Richard Henderson
2026-07-10 23:01 ` [PULL 11/13] common-user: Move probe_guest_base from linux-user Richard Henderson
` (3 subsequent siblings)
13 siblings, 0 replies; 15+ messages in thread
From: Richard Henderson @ 2026-07-10 23:01 UTC (permalink / raw)
To: qemu-devel; +Cc: Helge Deller, Warner Losh
Use sysctl to fetch the vm map of the current process.
Reviewed-by: Helge Deller <deller@gmx.de>
Reviewed-by: Warner Losh <imp@bsdimp.com>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
---
common-user/selfmap.c | 45 ++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 44 insertions(+), 1 deletion(-)
diff --git a/common-user/selfmap.c b/common-user/selfmap.c
index a1b88dee66..139dae15e8 100644
--- a/common-user/selfmap.c
+++ b/common-user/selfmap.c
@@ -9,6 +9,10 @@
#include "qemu/osdep.h"
#include "qemu/cutils.h"
#include "user/selfmap.h"
+#ifdef __FreeBSD__
+#include <sys/sysctl.h>
+#include <sys/user.h>
+#endif
IntervalTreeRoot *read_self_maps(void)
{
@@ -80,9 +84,48 @@ IntervalTreeRoot *read_self_maps(void)
g_strfreev(lines);
g_free(maps);
+ return root;
+#elif defined(__FreeBSD__)
+ int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_VMMAP, getpid() };
+ size_t len = 0;
+ g_autofree void *buf = NULL;
+ IntervalTreeRoot *root;
+
+ /* Probe for buffer size. */
+ if (sysctl(mib, ARRAY_SIZE(mib), NULL, &len, NULL, 0) < 0) {
+ return NULL;
+ }
+
+ buf = g_malloc(len);
+ if (sysctl(mib, ARRAY_SIZE(mib), buf, &len, NULL, 0) < 0) {
+ return NULL;
+ }
+
+ root = g_new0(IntervalTreeRoot, 1);
+
+ for (size_t i = 0; i < len; ) {
+ struct kinfo_vmentry *k = buf + i;
+ MapInfo *e = g_new0(MapInfo, 1);
+
+ e->itree.start = k->kve_start;
+ e->itree.last = k->kve_end - 1;
+
+ /*
+ * TODO: The rest of the fields in MapInfo are used by linux-user
+ * for the implementation of open_self_maps(). These fields are
+ * quite specific to the textual format of /proc/self/maps.
+ *
+ * We may need something different to emulate KERN_PROC_VMMAP
+ * in bsd-user, but so far they're unused -- leave them zeroed.
+ */
+
+ interval_tree_insert(&e->itree, root);
+ i += k->kve_structsize;
+ }
+
return root;
#else
- return NULL;
+# error
#endif
}
--
2.43.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* [PULL 11/13] common-user: Move probe_guest_base from linux-user
2026-07-10 23:01 [PULL 00/13] user patch queue Richard Henderson
` (9 preceding siblings ...)
2026-07-10 23:01 ` [PULL 10/13] common-user: Implement read_self_maps for FreeBSD Richard Henderson
@ 2026-07-10 23:01 ` Richard Henderson
2026-07-10 23:01 ` [PULL 12/13] bsd-user: Use probe_guest_base Richard Henderson
` (2 subsequent siblings)
13 siblings, 0 replies; 15+ messages in thread
From: Richard Henderson @ 2026-07-10 23:01 UTC (permalink / raw)
To: qemu-devel; +Cc: Warner Losh
Prepare to share probe_guest_base with bsd-user.
Create a linux_probe_guest_base wrapper with the portions
of probe_guest_base that are linux specific: managing the
commpage for various targets.
Reviewed-by: Warner Losh <imp@bsdimp.com>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
---
include/user/probe-guest-base.h | 37 ++++
linux-user/user-internals.h | 10 +-
common-user/probe-guest-base.c | 343 ++++++++++++++++++++++++++++++++
linux-user/elfload.c | 337 +------------------------------
linux-user/flatload.c | 2 +-
common-user/meson.build | 1 +
6 files changed, 392 insertions(+), 338 deletions(-)
create mode 100644 include/user/probe-guest-base.h
create mode 100644 common-user/probe-guest-base.c
diff --git a/include/user/probe-guest-base.h b/include/user/probe-guest-base.h
new file mode 100644
index 0000000000..d7492ef46c
--- /dev/null
+++ b/include/user/probe-guest-base.h
@@ -0,0 +1,37 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#ifndef USER_PROBE_GUEST_BASE_H
+#define USER_PROBE_GUEST_BASE_H
+
+#ifndef CONFIG_USER_ONLY
+#error Cannot include this header from system emulation
+#endif
+
+#include "exec/vaddr.h"
+
+typedef struct PGBRange {
+ vaddr lo;
+ vaddr hi;
+} PGBRange;
+
+/**
+ * probe_guest_base:
+ * @image_name: the executable being loaded
+ * @image_range: the fixed addresses within the executable
+ *
+ * Creates the initial guest address space in the host memory space.
+ *
+ * If @image_range is NULL, then no address in the executable is fixed,
+ * i.e. it is fully relocatable.
+ *
+ * This function will not return if a valid value for guest_base
+ * cannot be chosen. On return, the executable loader can expect
+ *
+ * target_mmap(i->lo, i->hi - i->lo + 1, ...)
+ *
+ * to succeed.
+ */
+void probe_guest_base(const char *image_name, const PGBRange *image_range,
+ const PGBRange *commpage_range);
+
+#endif
diff --git a/linux-user/user-internals.h b/linux-user/user-internals.h
index 74d8eaabbe..e7fbddd0e6 100644
--- a/linux-user/user-internals.h
+++ b/linux-user/user-internals.h
@@ -22,6 +22,7 @@
#include "qemu/log.h"
#include "exec/tb-flush.h"
#include "exec/translation-block.h"
+#include "user/probe-guest-base.h"
extern char *exec_path;
extern char real_exec_path[PATH_MAX];
@@ -74,13 +75,8 @@ void clone_fork_end(bool child);
void fork_start(void);
void fork_end(pid_t pid);
-typedef struct PGBRange {
- vaddr lo;
- vaddr hi;
-} PGBRange;
-
/**
- * probe_guest_base:
+ * linux_probe_guest_base:
* @image_name: the executable being loaded
* @image_range: the fixed addresses within the executable
*
@@ -96,7 +92,7 @@ typedef struct PGBRange {
*
* to succeed.
*/
-void probe_guest_base(const char *image_name, const PGBRange *image_range);
+void linux_probe_guest_base(const char *image_name, const PGBRange *image_range);
/* syscall.c */
int host_to_target_waitstatus(int status);
diff --git a/common-user/probe-guest-base.c b/common-user/probe-guest-base.c
new file mode 100644
index 0000000000..77722fe63f
--- /dev/null
+++ b/common-user/probe-guest-base.c
@@ -0,0 +1,343 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "qemu/osdep.h"
+#include "qemu/error-report.h"
+#include "qemu/units.h"
+#include "qemu/target-info.h"
+#include "qemu/log.h"
+#include "user/guest-base.h"
+#include "user/mmap-min-addr.h"
+#include "user/guest-host.h"
+#include "user/probe-guest-base.h"
+#include "user/selfmap.h"
+#include "exec/target_page.h"
+#include <sys/shm.h>
+
+/* Linux and FreeBSD use different flags to express NOREPLACE. */
+#ifdef __FreeBSD__
+#define MAP_FIXED_NOREPLACE (MAP_FIXED | MAP_EXCL)
+#endif
+
+/**
+ * pgb_try_mmap:
+ * @addr: host start address
+ * @addr_last: host last address
+ * @keep: do not unmap the probe region
+ *
+ * Return 1 if [@addr, @addr_last] is not mapped in the host,
+ * return 0 if it is not available to map, and -1 on mmap error.
+ * If @keep, the region is left mapped on success, otherwise unmapped.
+ */
+static int pgb_try_mmap(uintptr_t addr, uintptr_t addr_last, bool keep)
+{
+ size_t size = addr_last - addr + 1;
+ void *p = mmap((void *)addr, size, PROT_NONE,
+ MAP_ANONYMOUS | MAP_PRIVATE |
+ MAP_NORESERVE | MAP_FIXED_NOREPLACE, -1, 0);
+ int ret;
+
+ if (p == MAP_FAILED) {
+ return errno == EEXIST ? 0 : -1;
+ }
+ ret = p == (void *)addr;
+ if (!keep || !ret) {
+ munmap(p, size);
+ }
+ return ret;
+}
+
+/**
+ * pgb_try_mmap_skip_brk(uintptr_t addr, uintptr_t size, uintptr_t brk)
+ * @addr: host address
+ * @addr_last: host last address
+ * @brk: host brk
+ *
+ * Like pgb_try_mmap, but additionally reserve some memory following brk.
+ */
+static int pgb_try_mmap_skip_brk(uintptr_t addr, uintptr_t addr_last,
+ uintptr_t brk, bool keep)
+{
+ uintptr_t brk_last = brk + 16 * MiB - 1;
+
+ /* Do not map anything close to the host brk. */
+ if (addr <= brk_last && brk <= addr_last) {
+ return 0;
+ }
+ return pgb_try_mmap(addr, addr_last, keep);
+}
+
+/**
+ * pgb_try_mmap_set:
+ * @ga: set of guest addrs
+ * @base: guest_base
+ * @brk: host brk
+ *
+ * Return true if all @ga can be mapped by the host at @base.
+ * On success, retain the mapping at index 0 for reserved_va.
+ */
+
+typedef struct PGBAddrs {
+ PGBRange bounds[3];
+ int nbounds;
+} PGBAddrs;
+
+static bool pgb_try_mmap_set(const PGBAddrs *ga, uintptr_t base, uintptr_t brk)
+{
+ for (int i = ga->nbounds - 1; i >= 0; --i) {
+ if (pgb_try_mmap_skip_brk(ga->bounds[i].lo + base,
+ ga->bounds[i].hi + base,
+ brk, i == 0 && reserved_va) <= 0) {
+ return false;
+ }
+ }
+ return true;
+}
+
+/**
+ * pgb_addr_set:
+ * @ga: output set of guest addrs
+ * @image_range: fixed guest image addresses
+ * @identity: create for identity mapping
+ *
+ * Fill in @ga with the image, COMMPAGE and NULL page.
+ */
+static bool pgb_addr_set(PGBAddrs *ga, const PGBRange *image_range,
+ const PGBRange *commpage_range, bool try_identity)
+{
+ int n;
+
+ /*
+ * With a low commpage, or a guest mapped very low,
+ * we may not be able to use the identity map.
+ */
+ if (try_identity) {
+ if (commpage_range && commpage_range->lo < mmap_min_addr) {
+ return false;
+ }
+ if (image_range && image_range->lo < mmap_min_addr) {
+ return false;
+ }
+ }
+
+ memset(ga, 0, sizeof(*ga));
+ n = 0;
+
+ if (reserved_va) {
+ ga->bounds[n].lo = try_identity ? mmap_min_addr : 0;
+ ga->bounds[n].hi = reserved_va;
+ n++;
+ /* Low COMMPAGE and NULL handled by reserving from 0. */
+ } else {
+ /* Add any low COMMPAGE or NULL page. */
+ if (!try_identity || (commpage_range && commpage_range->lo == 0)) {
+ ga->bounds[n].lo = 0;
+ ga->bounds[n].hi = TARGET_PAGE_SIZE - 1;
+ n++;
+ }
+
+ /* Add the guest image for ET_EXEC. */
+ if (image_range) {
+ ga->bounds[n++] = *image_range;
+ }
+ }
+
+ /* Add any high COMMPAGE not covered by reserved_va. */
+ if (commpage_range && reserved_va < commpage_range->hi) {
+ ga->bounds[n].lo = commpage_range->lo & qemu_real_host_page_mask();
+ ga->bounds[n].hi = commpage_range->hi;
+ n++;
+ }
+
+ ga->nbounds = n;
+ return true;
+}
+
+static void pgb_fail_in_use(const char *image_name)
+{
+ error_report("%s: requires virtual address space that is in use "
+ "(omit the -B option or choose a different value)",
+ image_name);
+ exit(EXIT_FAILURE);
+}
+
+static void pgb_fixed(const char *image_name, const PGBRange *image_range,
+ const PGBRange *commpage_range, uintptr_t align)
+{
+ PGBAddrs ga;
+ uintptr_t brk = (uintptr_t)sbrk(0);
+
+ if (!QEMU_IS_ALIGNED(guest_base, align)) {
+ fprintf(stderr, "Requested guest base %p does not satisfy "
+ "host minimum alignment (0x%" PRIxPTR ")\n",
+ (void *)guest_base, align);
+ exit(EXIT_FAILURE);
+ }
+
+ if (!pgb_addr_set(&ga, image_range, commpage_range, !guest_base)
+ || !pgb_try_mmap_set(&ga, guest_base, brk)) {
+ pgb_fail_in_use(image_name);
+ }
+}
+
+/**
+ * pgb_find_fallback:
+ *
+ * This is a fallback method for finding holes in the host address space
+ * if we don't have the benefit of being able to access /proc/self/map.
+ * It can potentially take a very long time as we can only dumbly iterate
+ * up the host address space seeing if the allocation would work.
+ */
+static uintptr_t pgb_find_fallback(const PGBAddrs *ga, uintptr_t align,
+ uintptr_t brk)
+{
+ /* TODO: come up with a better estimate of how much to skip. */
+ uintptr_t skip = sizeof(uintptr_t) == 4 ? MiB : GiB;
+
+ for (uintptr_t base = skip; ; base += skip) {
+ base = ROUND_UP(base, align);
+ if (pgb_try_mmap_set(ga, base, brk)) {
+ return base;
+ }
+ if (base >= -skip) {
+ return -1;
+ }
+ }
+}
+
+static uintptr_t pgb_try_itree(const PGBAddrs *ga, uintptr_t base,
+ IntervalTreeRoot *root)
+{
+ for (int i = ga->nbounds - 1; i >= 0; --i) {
+ uintptr_t s = base + ga->bounds[i].lo;
+ uintptr_t l = base + ga->bounds[i].hi;
+ IntervalTreeNode *n;
+
+ if (l < s) {
+ /* Wraparound. Skip to advance S to mmap_min_addr. */
+ return mmap_min_addr - s;
+ }
+
+ n = interval_tree_iter_first(root, s, l);
+ if (n != NULL) {
+ /* Conflict. Skip to advance S to LAST + 1. */
+ return n->last - s + 1;
+ }
+ }
+ return 0; /* success */
+}
+
+static uintptr_t pgb_find_itree(const PGBAddrs *ga, IntervalTreeRoot *root,
+ uintptr_t align, uintptr_t brk)
+{
+ uintptr_t last = sizeof(uintptr_t) == 4 ? MiB : GiB;
+ uintptr_t base, skip;
+
+ while (true) {
+ base = ROUND_UP(last, align);
+ if (base < last) {
+ return -1;
+ }
+
+ skip = pgb_try_itree(ga, base, root);
+ if (skip == 0) {
+ break;
+ }
+
+ last = base + skip;
+ if (last < base) {
+ return -1;
+ }
+ }
+
+ /*
+ * We've chosen 'base' based on holes in the interval tree,
+ * but we don't yet know if it is a valid host address.
+ * Because it is the first matching hole, if the host addresses
+ * are invalid we know there are no further matches.
+ */
+ return pgb_try_mmap_set(ga, base, brk) ? base : -1;
+}
+
+static void pgb_dynamic(const char *image_name, const PGBRange *image_range,
+ const PGBRange *commpage_range, uintptr_t align)
+{
+ IntervalTreeRoot *root;
+ uintptr_t brk, ret;
+ PGBAddrs ga;
+
+ /* Try the identity map first. */
+ if (pgb_addr_set(&ga, image_range, commpage_range, true)) {
+ brk = (uintptr_t)sbrk(0);
+ if (pgb_try_mmap_set(&ga, 0, brk)) {
+ guest_base = 0;
+ return;
+ }
+ }
+
+ /*
+ * Rebuild the address set for non-identity map.
+ * This differs in the mapping of the guest NULL page.
+ */
+ pgb_addr_set(&ga, image_range, commpage_range, false);
+
+ root = read_self_maps();
+
+ /* Read brk after we've read the maps, which will malloc. */
+ brk = (uintptr_t)sbrk(0);
+
+ if (!root) {
+ ret = pgb_find_fallback(&ga, align, brk);
+ } else {
+ /*
+ * Reserve the area close to the host brk.
+ * This will be freed with the rest of the tree.
+ */
+ IntervalTreeNode *b = g_new0(IntervalTreeNode, 1);
+ b->start = brk;
+ b->last = brk + 16 * MiB - 1;
+ interval_tree_insert(b, root);
+
+ ret = pgb_find_itree(&ga, root, align, brk);
+ free_self_maps(root);
+ }
+
+ if (ret == -1) {
+ int w = target_long_bits() / 4;
+
+ error_report("%s: Unable to find a guest_base to satisfy all "
+ "guest address mapping requirements", image_name);
+
+ for (int i = 0; i < ga.nbounds; ++i) {
+ error_printf(" %0*" VADDR_PRIx "-%0*" VADDR_PRIx "\n",
+ w, ga.bounds[i].lo,
+ w, ga.bounds[i].hi);
+ }
+ exit(EXIT_FAILURE);
+ }
+ guest_base = ret;
+}
+
+void probe_guest_base(const char *image_name, const PGBRange *image_range,
+ const PGBRange *commpage_range)
+{
+ /* In order to use host shmat, we must be able to honor SHMLBA. */
+ uintptr_t align = MAX(SHMLBA, TARGET_PAGE_SIZE);
+
+ /* Sanity check the guest binary. */
+ if (reserved_va && image_range && image_range->hi > reserved_va) {
+ error_report("%s: requires more than reserved virtual "
+ "address space (0x%" VADDR_PRIx " > 0x%lx)",
+ image_name, image_range->hi, reserved_va);
+ exit(EXIT_FAILURE);
+ }
+
+ if (have_guest_base) {
+ pgb_fixed(image_name, image_range, commpage_range, align);
+ } else {
+ pgb_dynamic(image_name, image_range, commpage_range, align);
+ }
+
+ assert(QEMU_IS_ALIGNED(guest_base, align));
+ qemu_log_mask(CPU_LOG_PAGE, "Locating guest address space "
+ "@ 0x%" PRIx64 "\n", (uint64_t)guest_base);
+}
diff --git a/linux-user/elfload.c b/linux-user/elfload.c
index 207f814e15..e7c56af8ed 100644
--- a/linux-user/elfload.c
+++ b/linux-user/elfload.c
@@ -4,7 +4,6 @@
#include <sys/prctl.h>
#include <sys/resource.h>
-#include <sys/shm.h>
#include "qemu.h"
#include "user/tswap-target.h"
@@ -13,8 +12,6 @@
#include "exec/mmap-lock.h"
#include "exec/translation-block.h"
#include "exec/tswap.h"
-#include "user/guest-base.h"
-#include "user/mmap-min-addr.h"
#include "user-internals.h"
#include "signal-common.h"
#include "loader.h"
@@ -25,7 +22,6 @@
#include "qemu/queue.h"
#include "qemu/guest-random.h"
#include "qemu/units.h"
-#include "user/selfmap.h"
#include "qemu/lockable.h"
#include "qapi/error.h"
#include "qemu/error-report.h"
@@ -758,310 +754,8 @@ static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc,
return sp;
}
-#if !defined(COMMPAGE) && !defined(HAVE_GUEST_COMMPAGE)
-bool init_guest_commpage(void) { return true; }
-#endif
-
-/**
- * pgb_try_mmap:
- * @addr: host start address
- * @addr_last: host last address
- * @keep: do not unmap the probe region
- *
- * Return 1 if [@addr, @addr_last] is not mapped in the host,
- * return 0 if it is not available to map, and -1 on mmap error.
- * If @keep, the region is left mapped on success, otherwise unmapped.
- */
-static int pgb_try_mmap(uintptr_t addr, uintptr_t addr_last, bool keep)
-{
- size_t size = addr_last - addr + 1;
- void *p = mmap((void *)addr, size, PROT_NONE,
- MAP_ANONYMOUS | MAP_PRIVATE |
- MAP_NORESERVE | MAP_FIXED_NOREPLACE, -1, 0);
- int ret;
-
- if (p == MAP_FAILED) {
- return errno == EEXIST ? 0 : -1;
- }
- ret = p == (void *)addr;
- if (!keep || !ret) {
- munmap(p, size);
- }
- return ret;
-}
-
-/**
- * pgb_try_mmap_skip_brk(uintptr_t addr, uintptr_t size, uintptr_t brk)
- * @addr: host address
- * @addr_last: host last address
- * @brk: host brk
- *
- * Like pgb_try_mmap, but additionally reserve some memory following brk.
- */
-static int pgb_try_mmap_skip_brk(uintptr_t addr, uintptr_t addr_last,
- uintptr_t brk, bool keep)
-{
- uintptr_t brk_last = brk + 16 * MiB - 1;
-
- /* Do not map anything close to the host brk. */
- if (addr <= brk_last && brk <= addr_last) {
- return 0;
- }
- return pgb_try_mmap(addr, addr_last, keep);
-}
-
-/**
- * pgb_try_mmap_set:
- * @ga: set of guest addrs
- * @base: guest_base
- * @brk: host brk
- *
- * Return true if all @ga can be mapped by the host at @base.
- * On success, retain the mapping at index 0 for reserved_va.
- */
-
-typedef struct PGBAddrs {
- PGBRange bounds[3];
- int nbounds;
-} PGBAddrs;
-
-static bool pgb_try_mmap_set(const PGBAddrs *ga, uintptr_t base, uintptr_t brk)
-{
- for (int i = ga->nbounds - 1; i >= 0; --i) {
- if (pgb_try_mmap_skip_brk(ga->bounds[i].lo + base,
- ga->bounds[i].hi + base,
- brk, i == 0 && reserved_va) <= 0) {
- return false;
- }
- }
- return true;
-}
-
-/**
- * pgb_addr_set:
- * @ga: output set of guest addrs
- * @image_range: fixed guest image addresses
- * @identity: create for identity mapping
- *
- * Fill in @ga with the image, COMMPAGE and NULL page.
- */
-static bool pgb_addr_set(PGBAddrs *ga, const PGBRange *image_range,
- const PGBRange *commpage_range, bool try_identity)
-{
- int n;
-
- /*
- * With a low commpage, or a guest mapped very low,
- * we may not be able to use the identity map.
- */
- if (try_identity) {
- if (commpage_range && commpage_range->lo < mmap_min_addr) {
- return false;
- }
- if (image_range && image_range->lo < mmap_min_addr) {
- return false;
- }
- }
-
- memset(ga, 0, sizeof(*ga));
- n = 0;
-
- if (reserved_va) {
- ga->bounds[n].lo = try_identity ? mmap_min_addr : 0;
- ga->bounds[n].hi = reserved_va;
- n++;
- /* Low COMMPAGE and NULL handled by reserving from 0. */
- } else {
- /* Add any low COMMPAGE or NULL page. */
- if (!try_identity || (commpage_range && commpage_range->lo == 0)) {
- ga->bounds[n].lo = 0;
- ga->bounds[n].hi = TARGET_PAGE_SIZE - 1;
- n++;
- }
-
- /* Add the guest image for ET_EXEC. */
- if (image_range) {
- ga->bounds[n++] = *image_range;
- }
- }
-
- /* Add any high COMMPAGE not covered by reserved_va. */
- if (commpage_range && reserved_va < commpage_range->hi) {
- ga->bounds[n].lo = commpage_range->lo & qemu_real_host_page_mask();
- ga->bounds[n].hi = commpage_range->hi;
- n++;
- }
-
- ga->nbounds = n;
- return true;
-}
-
-static void pgb_fail_in_use(const char *image_name)
-{
- error_report("%s: requires virtual address space that is in use "
- "(omit the -B option or choose a different value)",
- image_name);
- exit(EXIT_FAILURE);
-}
-
-static void pgb_fixed(const char *image_name, const PGBRange *image_range,
- const PGBRange *commpage_range, uintptr_t align)
-{
- PGBAddrs ga;
- uintptr_t brk = (uintptr_t)sbrk(0);
-
- if (!QEMU_IS_ALIGNED(guest_base, align)) {
- fprintf(stderr, "Requested guest base %p does not satisfy "
- "host minimum alignment (0x%" PRIxPTR ")\n",
- (void *)guest_base, align);
- exit(EXIT_FAILURE);
- }
-
- if (!pgb_addr_set(&ga, image_range, commpage_range, !guest_base)
- || !pgb_try_mmap_set(&ga, guest_base, brk)) {
- pgb_fail_in_use(image_name);
- }
-}
-
-/**
- * pgb_find_fallback:
- *
- * This is a fallback method for finding holes in the host address space
- * if we don't have the benefit of being able to access /proc/self/map.
- * It can potentially take a very long time as we can only dumbly iterate
- * up the host address space seeing if the allocation would work.
- */
-static uintptr_t pgb_find_fallback(const PGBAddrs *ga, uintptr_t align,
- uintptr_t brk)
-{
- /* TODO: come up with a better estimate of how much to skip. */
- uintptr_t skip = sizeof(uintptr_t) == 4 ? MiB : GiB;
-
- for (uintptr_t base = skip; ; base += skip) {
- base = ROUND_UP(base, align);
- if (pgb_try_mmap_set(ga, base, brk)) {
- return base;
- }
- if (base >= -skip) {
- return -1;
- }
- }
-}
-
-static uintptr_t pgb_try_itree(const PGBAddrs *ga, uintptr_t base,
- IntervalTreeRoot *root)
-{
- for (int i = ga->nbounds - 1; i >= 0; --i) {
- uintptr_t s = base + ga->bounds[i].lo;
- uintptr_t l = base + ga->bounds[i].hi;
- IntervalTreeNode *n;
-
- if (l < s) {
- /* Wraparound. Skip to advance S to mmap_min_addr. */
- return mmap_min_addr - s;
- }
-
- n = interval_tree_iter_first(root, s, l);
- if (n != NULL) {
- /* Conflict. Skip to advance S to LAST + 1. */
- return n->last - s + 1;
- }
- }
- return 0; /* success */
-}
-
-static uintptr_t pgb_find_itree(const PGBAddrs *ga, IntervalTreeRoot *root,
- uintptr_t align, uintptr_t brk)
-{
- uintptr_t last = sizeof(uintptr_t) == 4 ? MiB : GiB;
- uintptr_t base, skip;
-
- while (true) {
- base = ROUND_UP(last, align);
- if (base < last) {
- return -1;
- }
-
- skip = pgb_try_itree(ga, base, root);
- if (skip == 0) {
- break;
- }
-
- last = base + skip;
- if (last < base) {
- return -1;
- }
- }
-
- /*
- * We've chosen 'base' based on holes in the interval tree,
- * but we don't yet know if it is a valid host address.
- * Because it is the first matching hole, if the host addresses
- * are invalid we know there are no further matches.
- */
- return pgb_try_mmap_set(ga, base, brk) ? base : -1;
-}
-
-static void pgb_dynamic(const char *image_name, const PGBRange *image_range,
- const PGBRange *commpage_range, uintptr_t align)
-{
- IntervalTreeRoot *root;
- uintptr_t brk, ret;
- PGBAddrs ga;
-
- /* Try the identity map first. */
- if (pgb_addr_set(&ga, image_range, commpage_range, true)) {
- brk = (uintptr_t)sbrk(0);
- if (pgb_try_mmap_set(&ga, 0, brk)) {
- guest_base = 0;
- return;
- }
- }
-
- /*
- * Rebuild the address set for non-identity map.
- * This differs in the mapping of the guest NULL page.
- */
- pgb_addr_set(&ga, image_range, commpage_range, false);
-
- root = read_self_maps();
-
- /* Read brk after we've read the maps, which will malloc. */
- brk = (uintptr_t)sbrk(0);
-
- if (!root) {
- ret = pgb_find_fallback(&ga, align, brk);
- } else {
- /*
- * Reserve the area close to the host brk.
- * This will be freed with the rest of the tree.
- */
- IntervalTreeNode *b = g_new0(IntervalTreeNode, 1);
- b->start = brk;
- b->last = brk + 16 * MiB - 1;
- interval_tree_insert(b, root);
-
- ret = pgb_find_itree(&ga, root, align, brk);
- free_self_maps(root);
- }
-
- if (ret == -1) {
- int w = TARGET_LONG_BITS / 4;
-
- error_report("%s: Unable to find a guest_base to satisfy all "
- "guest address mapping requirements", image_name);
-
- for (int i = 0; i < ga.nbounds; ++i) {
- error_printf(" %0*" VADDR_PRIx "-%0*" VADDR_PRIx "\n",
- w, ga.bounds[i].lo,
- w, ga.bounds[i].hi);
- }
- exit(EXIT_FAILURE);
- }
- guest_base = ret;
-}
-
-void probe_guest_base(const char *image_name, const PGBRange *image_range)
+void linux_probe_guest_base(const char *image_name,
+ const PGBRange *image_range)
{
#ifdef COMMPAGE
const PGBRange * const commpage_range = &(PGBRange){
@@ -1071,32 +765,15 @@ void probe_guest_base(const char *image_name, const PGBRange *image_range)
const PGBRange * const commpage_range = NULL;
#endif
- /* In order to use host shmat, we must be able to honor SHMLBA. */
- uintptr_t align = MAX(SHMLBA, TARGET_PAGE_SIZE);
-
- /* Sanity check the guest binary. */
- if (reserved_va && image_range && image_range->hi > reserved_va) {
- error_report("%s: requires more than reserved virtual "
- "address space (0x%" VADDR_PRIx " > 0x%lx)",
- image_name, image_range->hi, reserved_va);
- exit(EXIT_FAILURE);
- }
-
- if (have_guest_base) {
- pgb_fixed(image_name, image_range, commpage_range, align);
- } else {
- pgb_dynamic(image_name, image_range, commpage_range, align);
- }
+ probe_guest_base(image_name, image_range, commpage_range);
/* Reserve and initialize the commpage. */
+#if defined(COMMPAGE) || defined(HAVE_GUEST_COMMPAGE)
if (!init_guest_commpage()) {
/* We have already probed for the commpage being free. */
g_assert_not_reached();
}
-
- assert(QEMU_IS_ALIGNED(guest_base, align));
- qemu_log_mask(CPU_LOG_PAGE, "Locating guest address space "
- "@ 0x%" PRIx64 "\n", (uint64_t)guest_base);
+#endif
}
enum {
@@ -1345,10 +1022,10 @@ static void load_elf_image(const char *image_name, const ImageSource *src,
* Make sure that the low address does not conflict with
* MMAP_MIN_ADDR or the QEMU application itself.
*/
- probe_guest_base(image_name, &range);
+ linux_probe_guest_base(image_name, &range);
} else {
/* The binary is dynamic; we still need to select guest_base. */
- probe_guest_base(image_name, NULL);
+ linux_probe_guest_base(image_name, NULL);
/*
* Avoid collision with the loader by providing a different
diff --git a/linux-user/flatload.c b/linux-user/flatload.c
index 45d69040c6..a551f083a4 100644
--- a/linux-user/flatload.c
+++ b/linux-user/flatload.c
@@ -261,7 +261,7 @@ static int load_flat_file(struct linux_binprm * bprm,
/*
* Allocate the address space.
*/
- probe_guest_base(bprm->filename, NULL);
+ linux_probe_guest_base(bprm->filename, NULL);
/*
* there are a couple of cases here, the separate code/data
diff --git a/common-user/meson.build b/common-user/meson.build
index 831a7273fb..78dea613d0 100644
--- a/common-user/meson.build
+++ b/common-user/meson.build
@@ -6,6 +6,7 @@ common_user_inc += include_directories('host/' / host_arch)
user_ss.add(files(
'mmap-min-addr.c',
+ 'probe-guest-base.c',
'safe-syscall.S',
'safe-syscall-error.c',
'selfmap.c',
--
2.43.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* [PULL 12/13] bsd-user: Use probe_guest_base
2026-07-10 23:01 [PULL 00/13] user patch queue Richard Henderson
` (10 preceding siblings ...)
2026-07-10 23:01 ` [PULL 11/13] common-user: Move probe_guest_base from linux-user Richard Henderson
@ 2026-07-10 23:01 ` Richard Henderson
2026-07-10 23:01 ` [PULL 13/13] common-user: Move guest_base, have_guest_base to probe-guest-base.c Richard Henderson
2026-07-11 8:39 ` [PULL 00/13] user patch queue Stefan Hajnoczi
13 siblings, 0 replies; 15+ messages in thread
From: Richard Henderson @ 2026-07-10 23:01 UTC (permalink / raw)
To: qemu-devel
Merge the PT_LOAD loop with the PT_INTERP loop, as the XXX
comment suggests. Pass the probed range to probe_guest_base.
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
---
bsd-user/elfload.c | 37 +++++++++++++++++++------------------
bsd-user/main.c | 39 ++++-----------------------------------
2 files changed, 23 insertions(+), 53 deletions(-)
diff --git a/bsd-user/elfload.c b/bsd-user/elfload.c
index 3bca0cc9ed..a87a892eb8 100644
--- a/bsd-user/elfload.c
+++ b/bsd-user/elfload.c
@@ -22,6 +22,7 @@
#include "qemu.h"
#include "disas/disas.h"
#include "qemu/path.h"
+#include "user/probe-guest-base.h"
static abi_ulong target_auxents; /* Where the AUX entries are in target */
static size_t target_auxents_sz; /* Size of AUX entries including AT_NULL */
@@ -610,8 +611,9 @@ int load_elf_binary(struct bsd_binprm *bprm, struct target_pt_regs *regs,
abi_ulong elf_brk;
int error, retval;
char *elf_interpreter;
- abi_ulong baddr, elf_entry, et_dyn_addr, interp_load_addr = 0;
+ abi_ulong elf_entry, et_dyn_addr, interp_load_addr = 0;
abi_ulong reloc_func_desc = 0;
+ PGBRange range = { -1, 0 };
load_addr = 0;
elf_ex = *((struct elfhdr *) bprm->buf); /* exec-header */
@@ -654,10 +656,10 @@ int load_elf_binary(struct bsd_binprm *bprm, struct target_pt_regs *regs,
elf_brk = 0;
-
elf_interpreter = NULL;
- for (i = 0; i < elf_ex.e_phnum; i++) {
- if (elf_ppnt->p_type == PT_INTERP) {
+ for (i = 0; i < elf_ex.e_phnum; i++, elf_ppnt++) {
+ switch (elf_ppnt->p_type) {
+ case PT_INTERP:
if (elf_interpreter != NULL) {
free(elf_phdata);
free(elf_interpreter);
@@ -709,8 +711,14 @@ int load_elf_binary(struct bsd_binprm *bprm, struct target_pt_regs *regs,
close(bprm->fd);
return retval;
}
+ break;
+
+ case PT_LOAD:
+ range.lo = MIN(range.lo, elf_ppnt->p_vaddr);
+ range.hi = MAX(range.hi,
+ elf_ppnt->p_vaddr + elf_ppnt->p_memsz - 1);
+ break;
}
- elf_ppnt++;
}
/* Some simple consistency checks for the interpreter */
@@ -740,19 +748,12 @@ int load_elf_binary(struct bsd_binprm *bprm, struct target_pt_regs *regs,
info->end_code = 0;
elf_entry = (abi_ulong) elf_ex.e_entry;
- /* XXX Join this with PT_INTERP search? */
- baddr = 0;
- for (i = 0, elf_ppnt = elf_phdata; i < elf_ex.e_phnum; i++, elf_ppnt++) {
- if (elf_ppnt->p_type != PT_LOAD) {
- continue;
- }
- baddr = elf_ppnt->p_vaddr;
- break;
- }
-
et_dyn_addr = 0;
- if (elf_ex.e_type == ET_DYN && baddr == 0) {
- et_dyn_addr = ELF_ET_DYN_LOAD_ADDR;
+ if (elf_ex.e_type == ET_DYN) {
+ probe_guest_base(bprm->filename, NULL, NULL);
+ et_dyn_addr = ELF_ET_DYN_LOAD_ADDR - range.lo;
+ } else {
+ probe_guest_base(bprm->filename, &range, NULL);
}
/*
@@ -766,7 +767,7 @@ int load_elf_binary(struct bsd_binprm *bprm, struct target_pt_regs *regs,
info->elf_flags = elf_ex.e_flags;
error = load_elf_sections(&elf_ex, elf_phdata, bprm->fd, et_dyn_addr,
- &load_addr);
+ &load_addr);
for (i = 0, elf_ppnt = elf_phdata; i < elf_ex.e_phnum; i++, elf_ppnt++) {
if (elf_ppnt->p_type != PT_LOAD) {
continue;
diff --git a/bsd-user/main.c b/bsd-user/main.c
index 4f1544342e..5277b4e099 100644
--- a/bsd-user/main.c
+++ b/bsd-user/main.c
@@ -541,41 +541,10 @@ int main(int argc, char **argv)
}
}
- /*
- * If reserving host virtual address space, do so now.
- * Combined with '-B', ensure that the chosen range is free.
- */
- if (reserved_va) {
- void *p;
-
- if (have_guest_base) {
- p = mmap((void *)guest_base, reserved_va + 1, PROT_NONE,
- MAP_ANON | MAP_PRIVATE | MAP_FIXED | MAP_EXCL, -1, 0);
- } else {
- p = mmap(NULL, reserved_va + 1, PROT_NONE,
- MAP_ANON | MAP_PRIVATE, -1, 0);
- }
- if (p == MAP_FAILED) {
- const char *err = strerror(errno);
- char *sz = size_to_str(reserved_va + 1);
-
- if (have_guest_base) {
- error_report("Cannot allocate %s bytes at -B %p for guest "
- "address space: %s", sz, (void *)guest_base, err);
- } else {
- error_report("Cannot allocate %s bytes for guest "
- "address space: %s", sz, err);
- }
- exit(1);
- }
- guest_base = (uintptr_t)p;
- have_guest_base = true;
-
- /* Ensure that mmap_next_start is within range. */
- if (reserved_va <= mmap_next_start) {
- mmap_next_start = (reserved_va / 4 * 3)
- & TARGET_PAGE_MASK & qemu_host_page_mask;
- }
+ /* Ensure that mmap_next_start is within range. */
+ if (reserved_va && reserved_va <= mmap_next_start) {
+ mmap_next_start = ((reserved_va / 4 * 3)
+ & TARGET_PAGE_MASK & qemu_host_page_mask);
}
if (loader_exec(filename, argv + optind, target_environ, regs, info,
--
2.43.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* [PULL 13/13] common-user: Move guest_base, have_guest_base to probe-guest-base.c
2026-07-10 23:01 [PULL 00/13] user patch queue Richard Henderson
` (11 preceding siblings ...)
2026-07-10 23:01 ` [PULL 12/13] bsd-user: Use probe_guest_base Richard Henderson
@ 2026-07-10 23:01 ` Richard Henderson
2026-07-11 8:39 ` [PULL 00/13] user patch queue Stefan Hajnoczi
13 siblings, 0 replies; 15+ messages in thread
From: Richard Henderson @ 2026-07-10 23:01 UTC (permalink / raw)
To: qemu-devel; +Cc: Helge Deller, Warner Losh
Unify the definitions of guest_base and have_guest_base.
Reviewed-by: Helge Deller <deller@gmx.de>
Reviewed-by: Warner Losh <imp@bsdimp.com>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
---
bsd-user/main.c | 3 +--
common-user/probe-guest-base.c | 4 ++++
linux-user/main.c | 2 --
3 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/bsd-user/main.c b/bsd-user/main.c
index 5277b4e099..694481ae3e 100644
--- a/bsd-user/main.c
+++ b/bsd-user/main.c
@@ -63,8 +63,7 @@ intptr_t qemu_host_page_mask;
static bool opt_one_insn_per_tb;
static unsigned long opt_tb_size;
-uintptr_t guest_base;
-bool have_guest_base;
+
/*
* When running 32-on-64 we should make sure we can fit all of the possible
* guest address space into a contiguous chunk of virtual host memory.
diff --git a/common-user/probe-guest-base.c b/common-user/probe-guest-base.c
index 77722fe63f..6f1247cf1a 100644
--- a/common-user/probe-guest-base.c
+++ b/common-user/probe-guest-base.c
@@ -7,6 +7,7 @@
#include "qemu/log.h"
#include "user/guest-base.h"
#include "user/mmap-min-addr.h"
+#include "user/guest-base.h"
#include "user/guest-host.h"
#include "user/probe-guest-base.h"
#include "user/selfmap.h"
@@ -18,6 +19,9 @@
#define MAP_FIXED_NOREPLACE (MAP_FIXED | MAP_EXCL)
#endif
+uintptr_t guest_base;
+bool have_guest_base;
+
/**
* pgb_try_mmap:
* @addr: host start address
diff --git a/linux-user/main.c b/linux-user/main.c
index 01cf5bf079..60a695b7ca 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -79,8 +79,6 @@ static envlist_t *envlist;
static const char *cpu_model;
static const char *cpu_type;
static const char *seed_optarg;
-uintptr_t guest_base;
-bool have_guest_base;
/*
* Used to implement backwards-compatibility for the `-strace`, and
--
2.43.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* Re: [PULL 00/13] user patch queue
2026-07-10 23:01 [PULL 00/13] user patch queue Richard Henderson
` (12 preceding siblings ...)
2026-07-10 23:01 ` [PULL 13/13] common-user: Move guest_base, have_guest_base to probe-guest-base.c Richard Henderson
@ 2026-07-11 8:39 ` Stefan Hajnoczi
13 siblings, 0 replies; 15+ messages in thread
From: Stefan Hajnoczi @ 2026-07-11 8:39 UTC (permalink / raw)
To: Richard Henderson; +Cc: qemu-devel
[-- Attachment #1: Type: text/plain, Size: 116 bytes --]
Applied, thanks.
Please update the changelog at https://wiki.qemu.org/ChangeLog/11.1 for any user-visible changes.
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 15+ messages in thread