vfprintf() is complex and so far did not have proper tests.
This series is based on the "dev" branch of the RCU tree.
Signed-off-by: Thomas Weißschuh <[email protected]>
---
Changes in v3:
- also provide and use fflush/fclose.
- reject fileno(NULL).
- provide compatability with buffered streams from glibc.
- Link to v2: https://lore.kernel.org/r/[email protected]
Changes in v2:
- Include <sys/mman.h> for tests.
- Implement FILE* in terms of integer pointers.
- Provide fdopen() and fileno().
- Link to v1: https://lore.kernel.org/lkml/[email protected]/
---
Thomas Weißschuh (4):
tools/nolibc: add libc-test binary
tools/nolibc: add wrapper for memfd_create
tools/nolibc: implement fd-based FILE streams
tools/nolibc: add testcases for vfprintf
tools/include/nolibc/stdio.h | 95 ++++++++++++++++++++--------
tools/include/nolibc/sys.h | 23 +++++++
tools/testing/selftests/nolibc/.gitignore | 1 +
tools/testing/selftests/nolibc/Makefile | 5 ++
tools/testing/selftests/nolibc/nolibc-test.c | 86 +++++++++++++++++++++++++
5 files changed, 183 insertions(+), 27 deletions(-)
---
base-commit: a63baab5f60110f3631c98b55d59066f1c68c4f7
change-id: 20230328-nolibc-printf-test-052d5abc2118
Best regards,
--
Thomas Weißschuh <[email protected]>
This enables the usage of the stream APIs with arbitrary filedescriptors.
It will be used by a future testcase.
Signed-off-by: Thomas Weißschuh <[email protected]>
---
Willy:
This uses intptr_t instead of uintptr_t as proposed because uintptr_t
can not be negative.
---
tools/include/nolibc/stdio.h | 95 +++++++++++++++++++++++++++++++-------------
1 file changed, 68 insertions(+), 27 deletions(-)
diff --git a/tools/include/nolibc/stdio.h b/tools/include/nolibc/stdio.h
index 96ac8afc5aee..4add736c07aa 100644
--- a/tools/include/nolibc/stdio.h
+++ b/tools/include/nolibc/stdio.h
@@ -21,17 +21,75 @@
#define EOF (-1)
#endif
-/* just define FILE as a non-empty type */
+/* just define FILE as a non-empty type. The value of the pointer gives
+ * the FD: FILE=~fd for fd>=0 or NULL for fd<0. This way positive FILE
+ * are immediately identified as abnormal entries (i.e. possible copies
+ * of valid pointers to something else).
+ */
typedef struct FILE {
char dummy[1];
} FILE;
-/* We define the 3 common stdio files as constant invalid pointers that
- * are easily recognized.
- */
-static __attribute__((unused)) FILE* const stdin = (FILE*)-3;
-static __attribute__((unused)) FILE* const stdout = (FILE*)-2;
-static __attribute__((unused)) FILE* const stderr = (FILE*)-1;
+static __attribute__((unused)) FILE* const stdin = (FILE*)(intptr_t)~STDIN_FILENO;
+static __attribute__((unused)) FILE* const stdout = (FILE*)(intptr_t)~STDOUT_FILENO;
+static __attribute__((unused)) FILE* const stderr = (FILE*)(intptr_t)~STDERR_FILENO;
+
+/* provides a FILE* equivalent of fd. The mode is ignored. */
+static __attribute__((unused))
+FILE *fdopen(int fd, const char *mode __attribute__((unused)))
+{
+ if (fd < 0) {
+ SET_ERRNO(EBADF);
+ return NULL;
+ }
+ return (FILE*)(intptr_t)~fd;
+}
+
+/* provides the fd of stream. */
+static __attribute__((unused))
+int fileno(FILE *stream)
+{
+ intptr_t i = (intptr_t)stream;
+
+ if (i >= 0) {
+ SET_ERRNO(EBADF);
+ return -1;
+ }
+ return ~i;
+}
+
+/* flush a stream. */
+static __attribute__((unused))
+int fflush(FILE *stream)
+{
+ intptr_t i = (intptr_t)stream;
+
+ /* NULL is valid here. */
+ if (i > 0) {
+ SET_ERRNO(EBADF);
+ return -1;
+ }
+
+ /* Don't do anything, nolibc does not support buffering. */
+ return 0;
+}
+
+/* flush a stream. */
+static __attribute__((unused))
+int fclose(FILE *stream)
+{
+ intptr_t i = (intptr_t)stream;
+
+ if (i >= 0) {
+ SET_ERRNO(EBADF);
+ return -1;
+ }
+
+ if (close(~i))
+ return EOF;
+
+ return 0;
+}
/* getc(), fgetc(), getchar() */
@@ -41,14 +99,8 @@ static __attribute__((unused))
int fgetc(FILE* stream)
{
unsigned char ch;
- int fd;
- if (stream < stdin || stream > stderr)
- return EOF;
-
- fd = 3 + (long)stream;
-
- if (read(fd, &ch, 1) <= 0)
+ if (read(fileno(stream), &ch, 1) <= 0)
return EOF;
return ch;
}
@@ -68,14 +120,8 @@ static __attribute__((unused))
int fputc(int c, FILE* stream)
{
unsigned char ch = c;
- int fd;
-
- if (stream < stdin || stream > stderr)
- return EOF;
-
- fd = 3 + (long)stream;
- if (write(fd, &ch, 1) <= 0)
+ if (write(fileno(stream), &ch, 1) <= 0)
return EOF;
return ch;
}
@@ -96,12 +142,7 @@ static __attribute__((unused))
int _fwrite(const void *buf, size_t size, FILE *stream)
{
ssize_t ret;
- int fd;
-
- if (stream < stdin || stream > stderr)
- return EOF;
-
- fd = 3 + (long)stream;
+ int fd = fileno(stream);
while (size) {
ret = write(fd, buf, size);
--
2.40.0
On Sun, Apr 02, 2023 at 06:04:33PM +0000, Thomas Wei?schuh wrote:
> vfprintf() is complex and so far did not have proper tests.
>
> This series is based on the "dev" branch of the RCU tree.
So just to confirm, it's all good, thank you very much. I've passed it
to Paul.
Have a nice end of week-end ;-)
Willy
On Sun, Apr 02, 2023 at 06:04:36PM +0000, Thomas Wei?schuh wrote:
> This enables the usage of the stream APIs with arbitrary filedescriptors.
>
> It will be used by a future testcase.
This breaks the explicit use of standard streams:
> -static __attribute__((unused)) FILE* const stdin = (FILE*)-3;
> -static __attribute__((unused)) FILE* const stdout = (FILE*)-2;
> -static __attribute__((unused)) FILE* const stderr = (FILE*)-1;
> +static __attribute__((unused)) FILE* const stdin = (FILE*)(intptr_t)~STDIN_FILENO;
> +static __attribute__((unused)) FILE* const stdout = (FILE*)(intptr_t)~STDOUT_FILENO;
> +static __attribute__((unused)) FILE* const stderr = (FILE*)(intptr_t)~STDERR_FILENO;
Nothing in this change (or anything else in the series AFAICT) causes
STDx_FILENO to be declared so we get errors like below in -next when a
kselftest is built with this version of nolibc:
clang --target=aarch64-linux-gnu -fintegrated-as -Werror=unknown-warning-option -Werror=ignored-optimization-argument -Werror=option-ignored -Werror=unused-command-line-argument --target=aarch64-linux-gnu -fintegrated-as -fno-asynchronous-unwind-tables -fno-ident -s -Os -nostdlib \
-include ../../../../include/nolibc/nolibc.h -I../..\
-static -ffreestanding -Wall za-fork.c /tmp/kci/linux/build/kselftest/arm64/fp/za-fork-asm.o -o /tmp/kci/linux/build/kselftest/arm64/fp/za-fork
In file included from <built-in>:1:
In file included from ./../../../../include/nolibc/nolibc.h:97:
In file included from ./../../../../include/nolibc/arch.h:25:
./../../../../include/nolibc/arch-aarch64.h:176:35: warning: unknown attribute 'optimize' ignored [-Wunknown-attributes]
void __attribute__((weak,noreturn,optimize("omit-frame-pointer"))) _start(void)
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from <built-in>:1:
In file included from ./../../../../include/nolibc/nolibc.h:102:
./../../../../include/nolibc/stdio.h:33:71: error: use of undeclared identifier 'STDIN_FILENO'
static __attribute__((unused)) FILE* const stdin = (FILE*)(intptr_t)~STDIN_FILENO;
^
./../../../../include/nolibc/stdio.h:34:71: error: use of undeclared identifier 'STDOUT_FILENO'
static __attribute__((unused)) FILE* const stdout = (FILE*)(intptr_t)~STDOUT_FILENO;
^
./../../../../include/nolibc/stdio.h:35:71: error: use of undeclared identifier 'STDERR_FILENO'
static __attribute__((unused)) FILE* const stderr = (FILE*)(intptr_t)~STDERR_FILENO;
^
1 warning and 3 errors generated.
The kselftest branch itself is fine, the issues manifest when it is
merged with the nolibc branch. I'm not quite sure what the implicit
include that's been missed here is?
Hi Mark,
Apr 12, 2023 17:58:45 Mark Brown <[email protected]>:
> On Sun, Apr 02, 2023 at 06:04:36PM +0000, Thomas Weißschuh wrote:
>
>> This enables the usage of the stream APIs with arbitrary filedescriptors.
>>
>> It will be used by a future testcase.
>
> This breaks the explicit use of standard streams:
>
>> -static __attribute__((unused)) FILE* const stdin = (FILE*)-3;
>> -static __attribute__((unused)) FILE* const stdout = (FILE*)-2;
>> -static __attribute__((unused)) FILE* const stderr = (FILE*)-1;
>> +static __attribute__((unused)) FILE* const stdin = (FILE*)(intptr_t)~STDIN_FILENO;
>> +static __attribute__((unused)) FILE* const stdout = (FILE*)(intptr_t)~STDOUT_FILENO;
>> +static __attribute__((unused)) FILE* const stderr = (FILE*)(intptr_t)~STDERR_FILENO;
>
> Nothing in this change (or anything else in the series AFAICT) causes
> STDx_FILENO to be declared so we get errors like below in -next when a
> kselftest is built with this version of nolibc:
These definitions come from
"tools/nolibc: add definitions for standard fds".
This patch was part of the nolibc stack protector series which is older than this series and went through the same channels.
So I'm not sure how one series made it into next and the other didn't.
This would also have been noticed by Willy and Paul running their tests.
>
> clang --target=aarch64-linux-gnu -fintegrated-as -Werror=unknown-warning-option -Werror=ignored-optimization-argument -Werror=option-ignored -Werror=unused-command-line-argument --target=aarch64-linux-gnu -fintegrated-as -fno-asynchronous-unwind-tables -fno-ident -s -Os -nostdlib \
> -include ../../../../include/nolibc/nolibc.h -I../..\
> -static -ffreestanding -Wall za-fork.c /tmp/kci/linux/build/kselftest/arm64/fp/za-fork-asm.o -o /tmp/kci/linux/build/kselftest/arm64/fp/za-fork
> In file included from <built-in>:1:
> In file included from ./../../../../include/nolibc/nolibc.h:97:
> In file included from ./../../../../include/nolibc/arch.h:25:
> ./../../../../include/nolibc/arch-aarch64.h:176:35: warning: unknown attribute 'optimize' ignored [-Wunknown-attributes]
> void __attribute__((weak,noreturn,optimize("omit-frame-pointer"))) _start(void)
> ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> In file included from <built-in>:1:
> In file included from ./../../../../include/nolibc/nolibc.h:102:
> ./../../../../include/nolibc/stdio.h:33:71: error: use of undeclared identifier 'STDIN_FILENO'
> static __attribute__((unused)) FILE* const stdin = (FILE*)(intptr_t)~STDIN_FILENO;
> ^
> ./../../../../include/nolibc/stdio.h:34:71: error: use of undeclared identifier 'STDOUT_FILENO'
> static __attribute__((unused)) FILE* const stdout = (FILE*)(intptr_t)~STDOUT_FILENO;
> ^
> ./../../../../include/nolibc/stdio.h:35:71: error: use of undeclared identifier 'STDERR_FILENO'
> static __attribute__((unused)) FILE* const stderr = (FILE*)(intptr_t)~STDERR_FILENO;
> ^
> 1 warning and 3 errors generated.
>
> The kselftest branch itself is fine, the issues manifest when it is
> merged with the nolibc branch. I'm not quite sure what the implicit> include that's been missed here is?
These defines should be available to all users of nolibc.
It seems more of an issue with the patchsets going through different trees.
I can investigate this more tomorrow with my proper development setup.
Thomas
On Thu, Apr 13, 2023 at 03:09:27PM +0200, [email protected] wrote:
> Apr 12, 2023 17:58:45 Mark Brown <[email protected]>:
> > Nothing in this change (or anything else in the series AFAICT) causes
> > STDx_FILENO to be declared so we get errors like below in -next when a
> > kselftest is built with this version of nolibc:
> These definitions come from
> "tools/nolibc: add definitions for standard fds".
> This patch was part of the nolibc stack protector series which is older than this series and went through the same channels.
> So I'm not sure how one series made it into next and the other didn't.
> This would also have been noticed by Willy and Paul running their tests.
Hrm, that commit is actually in -next and Paul's pull request, not sure
why it wasn't showing up in greps. The issue is that you've added a
dependency from nolibc's stdio.h to unistd.h but nolibc.h includes
unistd.h last and there's no other include, meaning that at the time
that stdio.h is compiled there's no definition of the constants visible.
The below fixes the issue, I'll submit it properly later today:
diff --git a/tools/include/nolibc/nolibc.h b/tools/include/nolibc/nolibc.h
index 04739a6293c4..05a228a6ee78 100644
--- a/tools/include/nolibc/nolibc.h
+++ b/tools/include/nolibc/nolibc.h
@@ -99,11 +99,11 @@
#include "sys.h"
#include "ctype.h"
#include "signal.h"
+#include "unistd.h"
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "time.h"
-#include "unistd.h"
#include "stackprotector.h"
/* Used by programs to avoid std includes */