Skip to content

Commit c35c9ec

Browse files
committed
os: fdtable: provide z_libc_file_alloc() abstraction
Previously, there was an implicit assumption that Zephyr's internal struct fd_entry * was synonymous with FILE * from the C library. This is generally not the case and aliasing these two distinct types was preventing a fair bit of functionality from Just Working - namely stdio function calls like fgets() and fopen(). The problem can be seen directly when trying to use a function like zvfs_fdopen(). Instead of aliasing the two types, require that all Zephyr C libraries provide a z_libc_file_alloc() function that allocates and populates the required fields of a FILE object. Zephyr currently only provides the integer file descriptor to the C library for initializing FILE objects. Signed-off-by: Chris Friedt <[email protected]>
1 parent 5839020 commit c35c9ec

File tree

5 files changed

+178
-5
lines changed

5 files changed

+178
-5
lines changed

lib/libc/newlib/CMakeLists.txt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
# SPDX-License-Identifier: Apache-2.0
22

33
zephyr_library()
4-
zephyr_library_sources(libc-hooks.c)
4+
zephyr_library_sources(
5+
libc-hooks.c
6+
z_libc.c
7+
)
58

69
# Do not allow LTO when compiling libc-hooks.c file
710
set_source_files_properties(libc-hooks.c PROPERTIES COMPILE_OPTIONS $<TARGET_PROPERTY:compiler,prohibit_lto>)

lib/libc/newlib/z_libc.c

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/*
2+
* Copyright (c) 2024 Tenstorrent AI ULC
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#include <stddef.h>
8+
#include <stdio.h>
9+
#include <stdlib.h>
10+
11+
#include <zephyr/sys/fdtable.h>
12+
13+
extern void *_read_r;
14+
extern void *_write_r;
15+
extern void *_lseek_r;
16+
extern void *_close_r;
17+
18+
static int z_libc_sflags(const char *mode)
19+
{
20+
int ret = 0;
21+
22+
switch (mode[0]) {
23+
case 'r':
24+
ret = ZVFS_O_RDONLY;
25+
break;
26+
27+
case 'w':
28+
ret = ZVFS_O_WRONLY | ZVFS_O_CREAT | ZVFS_O_TRUNC;
29+
break;
30+
31+
case 'a':
32+
ret = ZVFS_O_WRONLY | ZVFS_O_CREAT | ZVFS_O_APPEND;
33+
break;
34+
default:
35+
return 0;
36+
}
37+
38+
while (*++mode) {
39+
switch (*mode) {
40+
case '+':
41+
ret |= ZVFS_O_RDWR;
42+
break;
43+
case 'x':
44+
ret |= ZVFS_O_EXCL;
45+
break;
46+
default:
47+
break;
48+
}
49+
}
50+
51+
return ret;
52+
}
53+
54+
FILE *z_libc_file_alloc(int fd, const char *mode)
55+
{
56+
FILE *fp;
57+
58+
fp = calloc(1, sizeof(*fp));
59+
if (fp == NULL) {
60+
return NULL;
61+
}
62+
63+
fp->_flags = z_libc_sflags(mode);
64+
fp->_file = fd;
65+
fp->_cookie = (void *)fp;
66+
67+
*((void **)fp->_read) = _read_r;
68+
*((void **)fp->_write) = _write_r;
69+
*((void **)fp->_seek) = _lseek_r;
70+
*((void **)fp->_close) = _close_r;
71+
72+
return fp;
73+
}
74+
75+
int z_libc_file_get_fd(FILE *fp)
76+
{
77+
if (fp == NULL) {
78+
return -EINVAL;
79+
}
80+
81+
return fp->_file;
82+
}

lib/libc/picolibc/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ zephyr_library_sources(
99
exit.c
1010
locks.c
1111
stdio.c
12+
z_libc.c
1213
)
1314

1415
zephyr_library_compile_options($<TARGET_PROPERTY:compiler,prohibit_lto>)

lib/libc/picolibc/z_libc.c

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*
2+
* Copyright (c) 2024 Tenstorrent AI ULC
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#include "stdio-bufio.h"
8+
9+
#include <stddef.h>
10+
#include <stdio.h>
11+
#include <stdlib.h>
12+
13+
#include <zephyr/sys/fdtable.h>
14+
15+
#define FDEV_SETUP_ZVFS(fd, buf, size, rwflags, bflags) \
16+
FDEV_SETUP_BUFIO(fd, buf, size, zvfs_read_wrap, zvfs_write_wrap, zvfs_lseek, zvfs_close, \
17+
rwflags, bflags)
18+
19+
int __posix_sflags(const char *mode, int *optr);
20+
21+
/* FIXME: do not use ssize_t or off_t */
22+
ssize_t zvfs_read(int fd, void *buf, size_t sz, const size_t *from_offset);
23+
ssize_t zvfs_write(int fd, const void *buf, size_t sz, const size_t *from_offset);
24+
off_t zvfs_lseek(int fd, off_t offset, int whence);
25+
int zvfs_close(int fd);
26+
27+
static ssize_t zvfs_read_wrap(int fd, void *buf, size_t sz)
28+
{
29+
return zvfs_read(fd, buf, sz, NULL);
30+
}
31+
32+
static ssize_t zvfs_write_wrap(int fd, const void *buf, size_t sz)
33+
{
34+
return zvfs_write(fd, buf, sz, NULL);
35+
}
36+
37+
FILE *z_libc_file_alloc(int fd, const char *mode)
38+
{
39+
struct __file_bufio *bf;
40+
int __maybe_unused unused;
41+
42+
bf = calloc(1, sizeof(struct __file_bufio) + BUFSIZ);
43+
if (bf == NULL) {
44+
return NULL;
45+
}
46+
47+
*bf = (struct __file_bufio)FDEV_SETUP_ZVFS(fd, (char *)(bf + 1), BUFSIZ,
48+
__posix_sflags(mode, &unused), __BFALL);
49+
50+
__bufio_lock_init(&(bf->xfile.cfile.file));
51+
52+
return &(bf->xfile.cfile.file);
53+
}
54+
55+
int z_libc_file_get_fd(const FILE *fp)
56+
{
57+
const struct __file_bufio *const bf = (const struct __file_bufio *)fp;
58+
59+
return POINTER_TO_INT(bf->ptr);
60+
}

lib/os/fdtable.c

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@
2424
#include <zephyr/internal/syscall_handler.h>
2525
#include <zephyr/sys/atomic.h>
2626

27+
#ifndef CONFIG_MINIMAL_LIBC
28+
extern FILE *z_libc_file_alloc(int fd, const char *mode);
29+
extern int z_libc_file_get_fd(const FILE *fp);
30+
#endif
31+
2732
struct stat;
2833

2934
struct fd_entry {
@@ -75,6 +80,20 @@ static struct fd_entry fdtable[CONFIG_ZVFS_OPEN_MAX] = {
7580

7681
static K_MUTEX_DEFINE(fdtable_lock);
7782

83+
#ifdef CONFIG_MINIMAL_LIBC
84+
static ALWAYS_INLINE inline FILE *z_libc_file_alloc(int fd, const char *mode)
85+
{
86+
ARG_UNUSED(mode);
87+
88+
return (FILE *)&fdtable[fd];
89+
}
90+
91+
static ALWAYS_INLINE inline int z_libc_file_get_fd(const FILE *fp)
92+
{
93+
return (const struct fd_entry *)fp - fdtable;
94+
}
95+
#endif
96+
7897
static int z_fd_ref(int fd)
7998
{
8099
return atomic_inc(&fdtable[fd].refcount) + 1;
@@ -313,6 +332,7 @@ static bool supports_pread_pwrite(uint32_t mode)
313332
{
314333
switch (mode & ZVFS_MODE_IFMT) {
315334
case ZVFS_MODE_IFSHM:
335+
case ZVFS_MODE_IFREG:
316336
return true;
317337
default:
318338
return false;
@@ -413,23 +433,30 @@ int zvfs_close(int fd)
413433

414434
FILE *zvfs_fdopen(int fd, const char *mode)
415435
{
416-
ARG_UNUSED(mode);
436+
FILE *ret;
417437

418438
if (_check_fd(fd) < 0) {
419439
return NULL;
420440
}
421441

422-
return (FILE *)&fdtable[fd];
442+
ret = z_libc_file_alloc(fd, mode);
443+
if (ret == NULL) {
444+
errno = ENOMEM;
445+
}
446+
447+
return ret;
423448
}
424449

425450
int zvfs_fileno(FILE *file)
426451
{
427-
if (!IS_ARRAY_ELEMENT(fdtable, file)) {
452+
int fd = z_libc_file_get_fd(file);
453+
454+
if (fd < 0 || fd >= ARRAY_SIZE(fdtable)) {
428455
errno = EBADF;
429456
return -1;
430457
}
431458

432-
return (struct fd_entry *)file - fdtable;
459+
return fd;
433460
}
434461

435462
int zvfs_fstat(int fd, struct stat *buf)

0 commit comments

Comments
 (0)