Re: Malloc: unusable area at the end of the heap section

Jeff Johnston <[email protected]>
Newsgroups gmane.comp.lib.newlib
Message-ID <CAOox84v9QBHuqWn8n2bx-5XOsb48g9-w59EVzSTKJ4SNP7OhHA@mail.gmail.com>
Ok,

I am attaching the modified patch after discussing with Torbjorn about the
licensing.  Torbjorn, please
review and let me know if there are changes you would like, otherwise, I
will push.

-- Jeff J.


On Wed, Sep 14, 2022 at 4:52 PM Jeff Johnston <[email protected]> wrote:

> Hi Torbjorn,
>
> I took a look at what would be needed to make this more generic.  I have a
> patch almost ready, but
> the one issue is that your libc/sys/arm/sysconf.c file does not have a
> license.  Could you please append a
> newer version of the file with a license and I will complete the patch for
> you to review?
>
> I decided to go with a simple HAVE_xxxx macro rather than a configure
> option so any platform can
> simply set the flag in configure.host.
>
> -- Jeff J.
>
> On Tue, Jun 7, 2022 at 12:03 PM Jeff Johnston <[email protected]> wrote:
>
>> Hi Torbjorn,
>>
>> I think it would be useful.  Do you want to modify the patch to be more
>> generic?  I am thinking of a var set in configure.host that sets a compile
>> flag at the end such as _USE_SYSCONF_FOR_PAGESIZE.  Then,
>> the default sysconf.c can be put in the newlib/libc/unix directory.  It
>> is then a straight-forward exercise to add this as an enablement
>> configuration option.  What do you think?
>>
>> -- Jeff J.
>>
>> On Tue, Jun 7, 2022 at 2:18 AM Torbjorn SVENSSON via Newlib <
>> [email protected]> wrote:
>>
>>> Hello,
>>>
>>> A while back, I provided a patch[1] to newlib that would allow the
>>> application to override the pagesize for malloc, but the patch got stalled.
>>> Maybe this would be a good time to take another look at the patch and see
>>> if it would actually fix the generic newlib usage or if it's still
>>> something that is only applicable for small embedded targets.
>>>
>>> [1] https://ecos.sourceware.org/ml/newlib/current/017616.html
>>>
>>> Kind regards,
>>> Torbjörn
>>>
>>> > -----Original Message-----
>>> > From: Newlib <newlib-
>>> > [email protected]> On Behalf Of Jerome
>>> > Leroux
>>> > Sent: den 6 juni 2022 23:18
>>> > To: [email protected]
>>> > Subject: Malloc: unusable area at the end of the heap section
>>> >
>>> > Hello Newlib developers,
>>> >
>>> > I am a user of Newlib in a project that runs on an NXP MCU.
>>> > I am using MCUXpressoIDE_11.3.0_5180_prc3, which comes with GCC “arm-
>>> > none-eabi-gcc.exe (GNU Arm Embedded Toolchain
>>> > 9-2020-q2-update) 9.3.1 20200408 (release)” and Newlib 3.3.0.
>>> >
>>> > I have identified an issue in malloc, and I think the problem is still
>>> present in
>>> > the latest version of Newlib. I could
>>> > not see any changes in the incriminated code since Newlib 3.3.0.
>>> >
>>> > I noticed this issue only in the standard malloc implementation and
>>> not in the
>>> > nano-malloc version.
>>> >
>>> > Here is a description of the problem:
>>> > The allocator splits the heap into pages. When a page is full, it
>>> increases the
>>> > heap size by reserving a new page in the
>>> > heap section. When reserving a new page, the allocator keeps the page
>>> end
>>> > address aligned with malloc_getpagesize, which
>>> > is set to 4096 by default. If there is not enough space to reserve the
>>> full page,
>>> > the allocation fails even if there is
>>> > enough space in the heap to allocate the chunk of memory.
>>> > Because the issue is related to the heap end address and how the linker
>>> > positions the heap, the same sequence of
>>> > allocations may lead to different results (failure or success)
>>> depending on the
>>> > location of the heap, even if the heap
>>> > size is constant. Typically, adding a new C global variable can shift
>>> the start
>>> > address of the heap section and cause a
>>> > malloc error.
>>> >
>>> > For example, with a heap section of 4096 bytes (0x1000 bytes):
>>> > If the heap section address is 0x20100-0x21100, during the
>>> initialization, the
>>> > page end address is set to 0x21000
>>> > (aligned on 4096). We will be able to allocate until the address
>>> 0x21000. After
>>> > that, the allocator will try to reserve
>>> > a new page, but it will fail because it won’t be able to reserve a
>>> 4096 bytes
>>> > page from 0x21000 to 0x22000. The
>>> > following allocations will fail. The usable heap size is 3840 bytes
>>> (0x21000 -
>>> > 0x20100) instead of 4096.
>>> > If the heap section address is 0x20F00-0x21F00 (same size), with the
>>> same
>>> > scenario, the usable heap size is 256 bytes
>>> > (0x21000 - 0x20F00).
>>> > Here are two examples of heap configurations:
>>> > https://gist.github.com/jerome-
>>> > leroux/759159fbd3e7bb5e189dbceb04636914?permalink_comment_id=4191
>>> > 266#gistcomment-4191266
>>> >
>>> > I did not dig into the implementation so much. From my understanding,
>>> the
>>> > problem comes from the usage of
>>> > "malloc_getpagesize" (see
>>> > https://github.com/bminor/newlib/blob/830a9b707caa5e343b6ffce7fcb2d3c
>>> > a97e3259c/newlib/libc/stdlib/_mallocr.c#L198) in
>>> > "malloc_extend_top" (probably here
>>> > https://github.com/bminor/newlib/blob/830a9b707caa5e343b6ffce7fcb2d3c
>>> > a97e3259c/newlib/libc/stdlib/_mallocr.c#L2166).
>>> > I can understand it makes sense to keep the pages aligned when running
>>> in a
>>> > system that implements virtual memory.
>>> > Still, on an MCU, the heap is just a contiguous chunk of memory
>>> allocated at
>>> > link time. Furthermore, the heap size is
>>> > usually pretty small (a few kilobytes), so potentially wasting 4 KB of
>>> memory
>>> > is unacceptable. Using the default
>>> > implementation of "sbrk" documented at
>>> > https://sourceware.org/newlib/libc.html#index-sbrk will lead to the
>>> > problem.
>>> >
>>> > I have written a simple example that demonstrates the issue (see
>>> > https://gist.github.com/jerome-
>>> > leroux/759159fbd3e7bb5e189dbceb04636914 ). To reproduce the problem,
>>> > define the macros
>>> > HEAP_SECTION_START_SYMBOL and HEAP_SECTION_END_SYMBOL, which
>>> > are specific to your environment. Then call the function
>>> > "test_malloc()".
>>> >
>>> > I tried to find someone with the same issue, but I couldn’t. The
>>> related
>>> > commits/discussions I found are:
>>> > -
>>> > https://github.com/bminor/newlib/commit/4a3d0a5a5d829c05868a34658eb
>>> > 45731dbb5112b
>>> > -
>>> https://stackoverflow.com/questions/39088598/malloc-in-newlib-does-it-
>>> > waste-memory-after-one-big-failure-allocation
>>> >
>>> > Can anyone confirm what I have noticed?
>>> >
>>> > Thanks.
>>> >
>>> > --
>>> > Jerome Leroux
>>>
>>>
0001-Implement-sysconf-for-Arm.patch (text/x-patch, 3.6 KB)
From aae3831e6334eed571e8c2eb04295e53cf24f024 Mon Sep 17 00:00:00 2001
From: Jeff Johnston <[email protected]>
Date: Fri, 16 Sep 2022 16:04:21 -0400
Subject: [PATCH] Implement sysconf for Arm

- add support for using sysconf to get page size in _mallocr.c via
  HAVE_SYSCONF_PAGESIZE flag set in configure.host
- set flag in configure.host for arm and add a default sysconf implementation
  in libc/sys/arm that returns the page size
- the default implementation can be overridden outside newlib to allow a
  different page size to improve malloc on devices with a small footprint
  without needing to rebuild newlib
- this patch is based on a contribution from Torbjorn Svensson and
  Niklas Dahlquist (https://ecos.sourceware.org/ml/newlib/current/017616.html)
---
 newlib/configure.host            |  2 ++
 newlib/libc/stdlib/_mallocr.c    |  2 ++
 newlib/libc/sys/arm/Makefile.inc |  2 +-
 newlib/libc/sys/arm/sysconf.c    | 30 ++++++++++++++++++++++++++++++
 4 files changed, 35 insertions(+), 1 deletion(-)
 create mode 100644 newlib/libc/sys/arm/sysconf.c

diff --git a/newlib/configure.host b/newlib/configure.host
index 98ce07d..32d1436 100644
--- a/newlib/configure.host
+++ b/newlib/configure.host
@@ -628,6 +628,7 @@ newlib_cflags="${newlib_cflags} -DCLOCK_PROVIDED -DMALLOC_PROVIDED -DEXIT_PROVID
 	;;
   arm*-*-pe)
 	syscall_dir=syscalls
+	newlib_cflags="${newlib_cflags} -DHAVE_SYSCONF_PAGESIZE"
 	;;
   arm*-*-*)
 	syscall_dir=syscalls
@@ -642,6 +643,7 @@ newlib_cflags="${newlib_cflags} -DCLOCK_PROVIDED -DMALLOC_PROVIDED -DEXIT_PROVID
 #         newlib_cflags="${newlib_cflags} -DARM_RDP_MONITOR"
 	  newlib_cflags="${newlib_cflags} -DARM_RDI_MONITOR"
 	fi
+	newlib_cflags="${newlib_cflags} -DHAVE_SYSCONF_PAGESIZE"
 	;;
   avr*)
 	newlib_cflags="${newlib_cflags} -DNO_EXEC -DSMALL_MEMORY -DMISSING_SYSCALL_NAMES"
diff --git a/newlib/libc/stdlib/_mallocr.c b/newlib/libc/stdlib/_mallocr.c
index 4b53997..1997b6d 100644
--- a/newlib/libc/stdlib/_mallocr.c
+++ b/newlib/libc/stdlib/_mallocr.c
@@ -320,12 +320,14 @@ extern "C" {
 #endif
 
 #ifndef _WIN32
+#ifndef HAVE_SYSCONF_PAGESIZE
 #ifdef SMALL_MEMORY
 #define malloc_getpagesize (128)
 #else
 #define malloc_getpagesize (4096)
 #endif
 #endif
+#endif
 
 #if __STD_C
 extern void __malloc_lock(struct _reent *);
diff --git a/newlib/libc/sys/arm/Makefile.inc b/newlib/libc/sys/arm/Makefile.inc
index 490a963..0122956 100644
--- a/newlib/libc/sys/arm/Makefile.inc
+++ b/newlib/libc/sys/arm/Makefile.inc
@@ -1,6 +1,6 @@
 AM_CPPFLAGS_%C% = -I$(srcdir)/libc/machine/arm
 
-libc_a_SOURCES += %D%/access.c %D%/aeabi_atexit.c
+libc_a_SOURCES += %D%/access.c %D%/aeabi_atexit.c %D%/sysconf.c
 if MAY_SUPPLY_SYSCALLS
 libc_a_SOURCES += %D%/libcfunc.c %D%/trap.S %D%/syscalls.c
 endif
diff --git a/newlib/libc/sys/arm/sysconf.c b/newlib/libc/sys/arm/sysconf.c
new file mode 100644
index 0000000..dbed7d7
--- /dev/null
+++ b/newlib/libc/sys/arm/sysconf.c
@@ -0,0 +1,30 @@
+/* libc/sys/arm/sysconf.c - The sysconf function */
+
+/* Copyright 2020, STMicroelectronics
+ *
+ * All rights reserved.
+ *
+ * Redistribution, modification, and use in source and binary forms is permitted
+ * provided that the above copyright notice and following paragraph are
+ * duplicated in all such forms.
+ *
+ * This file is distributed WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#include <unistd.h>
+#include <errno.h>
+
+long sysconf(int name)
+{
+  switch (name)
+  {
+  case _SC_PAGESIZE:
+    return 4096;
+
+  default:
+    errno = EINVAL;
+    return -1;
+  }
+  return -1; /* Can't get here */
+}
-- 
1.8.3.1
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.