[PATCH RFC 05/40] string: add strdup_const and kstrdup_const
Casey Connolly
casey.connolly at linaro.org
Thu Mar 19 21:56:27 CET 2026
Extend Linux compat by adding kstrdup_const(), backed by lib/string.c.
This leverages U-Boots .rodata section on ARM64 to avoid pointlessly
duplicating const strings.
This is used by the Linux CCF_FULL port and may be useful elsewhere
in U-Boot.
Signed-off-by: Casey Connolly <casey.connolly at linaro.org>
---
include/asm-generic/sections.h | 19 +++++++++++++++++++
include/linux/compat.h | 13 +++++++++++++
include/linux/string.h | 2 ++
lib/string.c | 31 +++++++++++++++++++++++++++++++
4 files changed, 65 insertions(+)
diff --git a/include/asm-generic/sections.h b/include/asm-generic/sections.h
index d59787948fd1..48bd4fa86043 100644
--- a/include/asm-generic/sections.h
+++ b/include/asm-generic/sections.h
@@ -8,8 +8,9 @@
#ifndef _ASM_GENERIC_SECTIONS_H_
#define _ASM_GENERIC_SECTIONS_H_
#include <linux/types.h>
+#include <stdbool.h>
/* References to section boundaries */
extern char _text[], _stext[], _etext[];
@@ -61,8 +62,26 @@ static inline int arch_is_kernel_data(unsigned long addr)
return 0;
}
#endif
+/**
+ * is_kernel_rodata - checks if the pointer address is located in the
+ * .rodata section
+ *
+ * @addr: address to check
+ *
+ * Returns: true if the address is located in .rodata, false otherwise.
+ */
+static inline bool is_kernel_rodata(unsigned long addr)
+{
+#ifdef CONFIG_ARM64
+ return addr >= (unsigned long)__start_rodata &&
+ addr < (unsigned long)__end_rodata;
+#else
+ return false;
+#endif
+}
+
/* U-Boot-specific things begin here */
/* Start of U-Boot text region */
extern char __text_start[];
diff --git a/include/linux/compat.h b/include/linux/compat.h
index 623814516175..d4ba4d0088a0 100644
--- a/include/linux/compat.h
+++ b/include/linux/compat.h
@@ -66,8 +66,21 @@ static inline void vfree(const void *addr)
{
free((void *)addr);
}
+/**
+ * kstrdup_const - conditionally duplicate an existing const string
+ * @s: the string to duplicate
+ * @gfp: the GFP mask used in the kmalloc() call when allocating memory
+ *
+ * Note: Strings allocated by kstrdup_const should be freed by kfree_const and
+ * must not be passed to krealloc().
+ *
+ * Return: source string if it is in .rodata section otherwise
+ * fallback to kstrdup.
+ */
+#define kstrdup_const(s, gfp) strdup_const(s)
+
struct kmem_cache { int sz; };
struct kmem_cache *get_mem(int element_sz);
#define kmem_cache_create(a, sz, c, d, e) get_mem(sz)
diff --git a/include/linux/string.h b/include/linux/string.h
index d943fcce690c..a8a6cf4af505 100644
--- a/include/linux/string.h
+++ b/include/linux/string.h
@@ -103,8 +103,10 @@ size_t strcspn(const char *s, const char *reject);
#ifndef __HAVE_ARCH_STRDUP
extern char * strdup(const char *);
extern char * strndup(const char *, size_t);
+extern const char *strdup_const(const char *s);
+extern void kfree_const(const void *x);
#endif
#ifndef __HAVE_ARCH_STRSWAB
extern char * strswab(const char *);
#endif
diff --git a/lib/string.c b/lib/string.c
index d56f88d4a847..302efe048b07 100644
--- a/lib/string.c
+++ b/lib/string.c
@@ -378,8 +378,39 @@ char * strndup(const char *s, size_t n)
new[len] = '\0';
return new;
}
+
+/**
+ * strdup_const - conditionally duplicate an existing const string
+ * @s: the string to duplicate
+ *
+ * Note: Strings allocated by kstrdup_const should be freed by kfree_const and
+ * must not be passed to krealloc().
+ *
+ * Return: source string if it is in .rodata section otherwise
+ * fallback to kstrdup.
+ */
+const char *strdup_const(const char *s)
+{
+ if (is_kernel_rodata((unsigned long)s))
+ return s;
+
+ return strdup(s);
+}
+
+/**
+ * kfree_const - conditionally free memory
+ * @x: pointer to the memory
+ *
+ * Function calls kfree only if @x is not in .rodata section.
+ */
+void kfree_const(const void *x)
+{
+ if (!is_kernel_rodata((unsigned long)x))
+ free((void *)x);
+}
+
#endif
#ifndef __HAVE_ARCH_STRSPN
/**
--
2.51.0
More information about the U-Boot
mailing list