Re: [PATCH 2/3] selftests/nolibc: cast execve() argv string to character pointer
From: David Laight
Date: Sat May 23 2026 - 05:58:49 EST
On Fri, 22 May 2026 23:40:17 +0200
Thomas Weißschuh <linux@xxxxxxxxxxxxxx> wrote:
> On 2026-05-22 19:48:07+0100, David Laight wrote:
> > On Fri, 22 May 2026 16:39:58 +0200
> > Thomas Weißschuh <linux@xxxxxxxxxxxxxx> wrote:
> > > On 2026-05-21 19:15:58+0100, David Laight wrote:
> > > > On Thu, 21 May 2026 18:29:30 +0200
> > > > Thomas Weißschuh <linux@xxxxxxxxxxxxxx> wrote:
> > > >
> > > > > The existing code would trigger a warning under -Wwrite-strings which is
> > > > > about to be enabled. execve() is specified as not modifying the argv
> > > > > array, but the exact semantics are not representable in the type system.
> > > >
> > > > I suspect you'll have to fix it again to avoid 'casting away const'.
> > >
> > > Where would this warning be coming from? Which compiler flags are needed?
> > > Afaik it is legal to cast away const.
> >
> > IIRC -Wcast-qual
>
> Yes that's it, thanks.
>
> > Lots of things are legal :-)
> > The problem with enabling -Wcast-qual (NetBSD's kernel does/did) it is makes
> > life annoying when you really do have to do it.
> > (From what I remember there weren't really that many.)
> > You sort of want an (unconst foo *) cast that won't generate a warning when
> > a simple (foo *) cast would.
>
> There seem to be a fair amount of standard C APIs which require such
> casts, for example strstr(). Also the UAPI headers currently emit such
> warnings. So I am not sure if it makes sense to try to get nolibc
> compile with this warning.
It ought to be an aim :-)
Very recent headers use _Generic() so that the return type of strchr()
and strstr() is the same as the argument.
It should be possibly to get the linker to use the same symbol for both
so the code only exists once.
There is also the problem that 'const foo *' can either mean 'the data
area cannot be written to through this pointer' or 'the data area cannot
be written to at all'.
I've seen gcc assume the latter and then 'miscompile':
int f(const struct foo *foo)
{
int n = foo->n;
g();
return foo->n == n;
}
because it assumes that g() cannot change the contents of foo.
>
> > > > Can you use something like (char[]){"/"} ?
> > >
> > > That looks good.
Given that the exec() test does the same for argv[] (mostly to get
it all one one line) and object size doesn't really matter there
that one could be done that was - almost for consistency.
> > > However if this issue is real we will also have it in
> > > nolibc's errno.h. There I don't want to use this pattern, as it requires
> > > more memory.
> >
> > You can move a string from .rodata to .data easily enough.
> > Doesn't change the memory footprint.
>
> When using the proposed pattern in errno.h I get plus 4 bytes of .bss
> usage for each variable. While it doesn't make a difference in the
> binary, at runtime these bytes are quite wasted.
> I didn't look closely at it yet, but the compiler could be reusing a
> single empty string in .rodata for all different users, while the .data
> one needs to be duplicated for each one.
That will happen, and the linker might merge the string with another '\0'
in .rodata.str.1
You'd need to have a named 'char null[] = ""'.
> So I think the current version, casting to (char *), is still the
> best aproach for now.
Avoiding casting away const is hard.
ISTR that there are also oddities with volatile.
-- David