Skip to content

Commit 45d656d

Browse files
committed
zvfs: improve libc FILE to integer fd 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 count 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 1. FILE *z_libc_file_alloc(int fd, const char *mode) Allocate and populate the required fields of a FILE object 2. int z_libc_file_get_fd(FILE *fp) Convert a FILE* object to an integer file descriptor. For Picolibc and Newlib-based C libraries, these functions set and get the integer file descriptor from a field of the internal FILE object representation. For the minimal C library, these functions convert between array index and struct fd_entry pointers. Signed-off-by: Chris Friedt <cfriedt@tenstorrent.com>
1 parent 307eca4 commit 45d656d

File tree

5 files changed

+176
-5
lines changed

5 files changed

+176
-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: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
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+
static int z_libc_sflags(const char *mode)
14+
{
15+
int ret = 0;
16+
17+
switch (mode[0]) {
18+
case 'r':
19+
ret = ZVFS_O_RDONLY;
20+
break;
21+
22+
case 'w':
23+
ret = ZVFS_O_WRONLY | ZVFS_O_CREAT | ZVFS_O_TRUNC;
24+
break;
25+
26+
case 'a':
27+
ret = ZVFS_O_WRONLY | ZVFS_O_CREAT | ZVFS_O_APPEND;
28+
break;
29+
default:
30+
return 0;
31+
}
32+
33+
while (*++mode) {
34+
switch (*mode) {
35+
case '+':
36+
ret |= ZVFS_O_RDWR;
37+
break;
38+
case 'x':
39+
ret |= ZVFS_O_EXCL;
40+
break;
41+
default:
42+
break;
43+
}
44+
}
45+
46+
return ret;
47+
}
48+
49+
FILE *z_libc_file_alloc(int fd, const char *mode)
50+
{
51+
FILE *fp;
52+
/*
53+
* These symbols have conflicting declarations in upstream headers.
54+
* Use uintptr_t and a cast to assign cleanly.
55+
*/
56+
extern uintptr_t _close_r;
57+
extern uintptr_t _lseek_r;
58+
extern uintptr_t _read_r;
59+
extern uintptr_t _write_r;
60+
61+
fp = calloc(1, sizeof(*fp));
62+
if (fp == NULL) {
63+
return NULL;
64+
}
65+
66+
fp->_flags = z_libc_sflags(mode);
67+
fp->_file = fd;
68+
fp->_cookie = (void *)fp;
69+
70+
*(uintptr_t *)fp->_read = _read_r;
71+
*(uintptr_t *)fp->_write = _write_r;
72+
*(uintptr_t *)fp->_seek = _lseek_r;
73+
*(uintptr_t *)fp->_close = _close_r;
74+
75+
return fp;
76+
}
77+
78+
int z_libc_file_get_fd(FILE *fp)
79+
{
80+
return fp->_file;
81+
}

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(-fno-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: 30 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;
@@ -413,23 +432,30 @@ int zvfs_close(int fd)
413432

414433
FILE *zvfs_fdopen(int fd, const char *mode)
415434
{
416-
ARG_UNUSED(mode);
435+
FILE *ret;
417436

418437
if (_check_fd(fd) < 0) {
419438
return NULL;
420439
}
421440

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

425449
int zvfs_fileno(FILE *file)
426450
{
427-
if (!IS_ARRAY_ELEMENT(fdtable, file)) {
451+
int fd = z_libc_file_get_fd(file);
452+
453+
if (fd < 0 || fd >= ARRAY_SIZE(fdtable)) {
428454
errno = EBADF;
429455
return -1;
430456
}
431457

432-
return (struct fd_entry *)file - fdtable;
458+
return fd;
433459
}
434460

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

0 commit comments

Comments
 (0)