Skip to content

libs/libc/unistd: Make optarg, optind, opterr and optopt variables. - #20568

Draft
casaroli wants to merge 2 commits into
apache:masterfrom
casaroli:libc-getopt-variables
Draft

casaroli wants to merge 2 commits into
apache:masterfrom
casaroli:libc-getopt-variables

Conversation

@casaroli

@casaroli casaroli commented Oct 9, 2026

Copy link
Copy Markdown
Contributor

Summary

POSIX specifies optarg, optind, opterr and optopt as variables. NuttX defines them as macros over per-task storage, because the tasks of a flat build share one copy of the C library data.

A program that brings its own getopt() and defines these variables does not compile: char *optarg = NULL; expands to char *(*(getoptargp())) = NULL;. A program that only declares them reads a different optarg than its getopt() writes.

In the user space of a kernel build every process has its own copy of the C library data, so there this PR makes them plain variables. getopt_common() copies them to and from its internal state on each call, and getoptargp() and the other accessors return their addresses, so programs built against the old macros keep working. The variables are in a file of their own, so a program that defines them gets no second definition from the library. Flat and protected builds do not change.

The first commit fixes the old nxstyle errors in lib_getopt_common.c (two comments moved above their case labels, one blank line), with no code change: the object code is identical before and after (qemu-armv8a:nsh).

Impact

Kernel builds only: optarg, optind, opterr and optopt are variables in user space. Programs that use them through the macros, or call getopt() from the C library, work as before. Flat and protected builds: no change.

Testing

qemu-armv8a:knsh in QEMU 11. The test programs were built outside the tree against the export package.

A program that defines the four variables and calls getopt(), run as optvars -f file -v x:

Before:

optvars.c:6:1: error: function 'getoptargp' is initialized like a variable

After:

optvars: option f arg file
optvars: option v arg file
optvars: optind 4

The same program without its own definitions gives the same output before and after.

Kernel builds with -Wno-cpp -Werror, each followed by make export and the apps make import (all apps of the configuration compiled for user space): qemu-armv8a:knsh, imx93-evk:knsh, qemu-armv7a:knsh, sabre-6quad:netknsh, sama5d4-ek:knsh, rv-virt:knsh, rv-virt:knsh64, rv-virt:ksmp64, rv-virt:knsh_paging, rv-virt:knetnsh64, rv-virt:kfb64, icicle:knsh, canmv230:knsh, esp32s3-devkit:kernel_n8r2.

Flat builds with -Wno-cpp -Werror, size unchanged: lm3s6965-ek:qemu-flat, mps2-an500:nsh.

tools/checkpatch.sh -c -u -m -g passes.

Move the comments that introduce two case labels above the labels, and
add a blank line after a declaration.  No code change: the object code
is identical before and after on qemu-armv8a:nsh.

Assisted-by: Claude Code:claude-opus-5-5
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
POSIX specifies optarg, optind, opterr and optopt as variables.  NuttX
defines them as macros over per-task storage, because the tasks of a
flat build share one copy of the C library data.

A program that brings its own getopt() and defines these variables then
reads a different optarg than its getopt() writes: the option argument
of every option is NULL.

In the user space of a kernel build every process has its own copy of
the C library data, so there they are now plain variables.
getopt_common() copies them to and from its internal state on each call,
and getoptargp() and the other accessors return their addresses, so
programs built against the old macros keep working.  The variables are
in a file of their own, so a program that defines them gets no second
definition from the library.  Flat and protected builds do not change.

Assisted-by: Claude Code:claude-opus-5-5
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
Comment thread include/unistd.h
#define opterr (*(getopterrp()))
#define optind (*(getoptindp()))
#define optopt (*(getoptoptp()))
#if !defined(CONFIG_BUILD_KERNEL) || defined(__KERNEL__)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

kernel/flat should keep the same behavior. anyway, we have a similar and better fix, will provide soon.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, please link your PR here when it is ready and I will close this one. I can test it with a program that defines optarg itself.

@github-actions github-actions Bot added Area: OS Components OS Components issues Size: M The size of the change in this PR is medium labels Oct 9, 2026
@github-actions

github-actions Bot commented Oct 9, 2026 •

Copy link
Copy Markdown

MemBrowse Memory Report

esp32-devkitc

  • ROM: .flash.text +8 B (+0.0%, 125,096 B / 4,194,272 B, total: 3% used)
  • irom0_0_seg: .flash.text +8 B (+0.0%, 89,300 B / 3,342,304 B, total: 3% used)

hifive1-revb

  • flash: .text +16 B (+0.0%, 84,252 B / 4,194,304 B, total: 2% used)

qemu-armv8a

  • Code: .rodata +8 B, .text.gmtime_r +12 B, .text.init_builtin_run +60 B, .text.work_qqueue +24 B (+0.0%, 347,480 B)

qemu-intel64

  • Code: .text +84 B (+0.0%, 8,682,514 B)

rx65n-rsk2mb

  • ROM: .rodata +4 B (+0.0%, 87,044 B / 2,097,152 B, total: 4% used)

s698pm-dkit

@casaroli
casaroli marked this pull request as draft October 11, 2026 05:56
@casaroli casaroli closed this Oct 11, 2026
@casaroli casaroli reopened this Oct 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Area: OS Components OS Components issues Size: M The size of the change in this PR is medium

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants