diff --git a/bsd-user/bsd-mem.h b/bsd-user/bsd-mem.h index 0c8d96d9a4..b296c5c6f0 100644 --- a/bsd-user/bsd-mem.h +++ b/bsd-user/bsd-mem.h @@ -212,4 +212,49 @@ static inline abi_long do_bsd_mincore(abi_ulong target_addr, abi_ulong len, return ret; } +/* do_brk() must return target values and target errnos. */ +static inline abi_long do_obreak(abi_ulong brk_val) +{ + abi_long mapped_addr; + abi_ulong new_brk; + abi_ulong old_brk; + + /* brk pointers are always untagged */ + + /* do not allow to shrink below initial brk value */ + if (brk_val < initial_target_brk) { + return target_brk; + } + + new_brk = TARGET_PAGE_ALIGN(brk_val); + old_brk = TARGET_PAGE_ALIGN(target_brk); + + /* new and old target_brk might be on the same page */ + if (new_brk == old_brk) { + target_brk = brk_val; + return target_brk; + } + + /* Release heap if necesary */ + if (new_brk < old_brk) { + target_munmap(new_brk, old_brk - new_brk); + + target_brk = brk_val; + return target_brk; + } + + mapped_addr = target_mmap(old_brk, new_brk - old_brk, + PROT_READ | PROT_WRITE, + MAP_FIXED | MAP_EXCL | MAP_ANON | MAP_PRIVATE, + -1, 0); + + if (mapped_addr == old_brk) { + target_brk = brk_val; + return target_brk; + } + + /* For everything else, return the previous break. */ + return target_brk; +} + #endif /* BSD_USER_BSD_MEM_H */ diff --git a/bsd-user/freebsd/os-syscall.c b/bsd-user/freebsd/os-syscall.c index f054241cd6..92793ab1fb 100644 --- a/bsd-user/freebsd/os-syscall.c +++ b/bsd-user/freebsd/os-syscall.c @@ -855,6 +855,13 @@ static abi_long freebsd_syscall(void *cpu_env, int num, abi_long arg1, break; #endif + /* + * Misc + */ + case TARGET_FREEBSD_NR_break: + ret = do_obreak(arg1); + break; + /* * sys{ctl, arch, call} */