Source code analysis of Hongmeng light kernel: Newlib C

Posted by miancu on Wed, 26 Jan 2022 08:29:35 +0100

Abstract: This paper introduces the implementation of LiteOS-M kernel Newlib C, especially the file system and memory allocation release part, and finally introduces the Newlib hook function.

This article is shared from Huawei cloud community< Hongmeng light kernel M core source code analysis series 20 Newlib C >, author: zhushy.

When using Musl C library, the kernel provides Los based_ XXX adapts to the posix interface (/ / kernel/liteos_m/kal/posix) of pthread, mqeue, fs, semaphore, time and other modules. The posix interface provided by the kernel and the standard C library interface in musl form the LibC of LiteOS-M. Arm none EABI GCC is used for compilation, but only the compilation function of its tool chain is used. By adding - nostdinc and - nostdlib, we force to use our own modified musl-C.

Community and third-party manufacturers often use the public version tool chain arm none EABI GCC and private customization optimization for compilation. The LiteOS-M kernel also supports the public version arm none EABI GCC C library to compile and run the kernel. Newlib is a small C library. For the part of posix interface involving system calls, newlib provides some hook functions that need system adaptation, such as_ exit(),_ open(),_ close(),_ gettimeofday(), etc. if the operating system adapts these hooks, you can compile and run the program using the public newlib tool chain.

1. Newlib C file system

When using Newlib C and enabling POSIX FS API support (you can execute make meuconfig in the kernel\liteos-m \ directory to pop up the configuration interface, and the path is compact choose libc Implementation), as shown in the following figure. You can use the file Kal \ libc \ newlib \ porting \ SRC \ FS File system operation interface defined in C. These are standard POSIX interfaces. If you want to know the usage of POSIX, you can enter the name of man -a function on linux platform, such as man -a opendir, to open the manual of the function.

1.1 functions mount, umount and umount2

The usage and implementation of these functions are consistent with those of musl c.

int mount(const char *source, const char *target,
          const char *filesystemtype, unsigned long mountflags,
          const void *data)
{
    return LOS_FsMount(source, target, filesystemtype, mountflags, data);
}

int umount(const char *target)
{
    return LOS_FsUmount(target);
}

int umount2(const char *target, int flag)
{
    return LOS_FsUmount2(target, flag);
}

1.2 document operation interface

The function implementation starting with an underscore is the hook function implementation of newlib c. The hook function calling process of newlib is specially analyzed below.

int _open(const char *path, int oflag, ...)
{
    va_list vaList;
    va_start(vaList, oflag);
    int ret;
    ret = LOS_Open(path, oflag);
    va_end(vaList);
    return ret;
}

int _close(int fd)
{
    return LOS_Close(fd);
}

ssize_t _read(int fd, void *buf, size_t nbyte)
{
    return LOS_Read(fd, buf, nbyte);
}

ssize_t _write(int fd, const void *buf, size_t nbyte)
{
    return LOS_Write(fd, buf, nbyte);
}

off_t _lseek(int fd, off_t offset, int whence)
{
    return LOS_Lseek(fd, offset, whence);
}

int _unlink(const char *path)
{
    return LOS_Unlink(path);
}

int _fstat(int fd, struct stat *buf)
{
    return LOS_Fstat(fd, buf);
}

int _stat(const char *path, struct stat *buf)
{
    return LOS_Stat(path, buf);
}

int fsync(int fd)
{
    return LOS_Fsync(fd);
}

int mkdir(const char *path, mode_t mode)
{
    return LOS_Mkdir(path, mode);
}

DIR *opendir(const char *dirName)
{
    return LOS_Opendir(dirName);
}

struct dirent *readdir(DIR *dir)
{
    return LOS_Readdir(dir);
}

int closedir(DIR *dir)
{
    return LOS_Closedir(dir);
}

int rmdir(const char *path)
{
    return LOS_Unlink(path);
}

int rename(const char *oldName, const char *newName)
{
    return LOS_Rename(oldName, newName);
}

int statfs(const char *path, struct statfs *buf)
{
    return LOS_Statfs(path, buf);
}

int ftruncate(int fd, off_t length)
{
    return LOS_Ftruncate(fd, length);
}

When newlib is not enabled to support POSIX FS API, you need to provide an empty implementation of these hook functions and return - 1 error code.

int _open(const char *path, int oflag, ...)
{
    return -1;
}

int _close(int fd)
{
    return -1;
}

ssize_t _read(int fd, void *buf, size_t nbyte)
{
    return -1;
}

ssize_t _write(int fd, const void *buf, size_t nbyte)
{
    return -1;
}

off_t _lseek(int fd, off_t offset, int whence)
{
    return -1;
}

int _unlink(const char *path)
{
    return -1;
}

int _fstat(int fd, struct stat *buf)
{
    return -1;
}

int _stat(const char *path, struct stat *buf)
{
    return -1;
}

2. Newlib C memory allocation free

malloc adaptation reference of newlibc The Red Hat newlib C Library-malloc . malloc can be implemented in the following two ways:

  • Realize_ sbrk_r function. In this method, the memory allocation function uses the in newlib.
  • Realize_ malloc_r, _realloc_r, _free_r, _memalign_r, _malloc_usable_size_r et al. In this method, the memory allocation function can use the kernel.

In order to easily optimize the memory allocation algorithm and locate the problem according to the business, the latter is recommended. The kernel memory functions are defined in the file Kal \ libc \ newlib \ porting \ SRC \ malloc C. The source code fragment is as follows. The code implementation is relatively simple, and the source code is no longer analyzed.

......
void __wrap__free_r(struct _reent *reent, void *aptr)
{
    if (aptr == NULL) {
        return;
    }

    LOS_MemFree(OS_SYS_MEM_ADDR, aptr);
}

size_t __wrap__malloc_usable_size_r(struct _reent *reent, void *aptr)
{
    return 0;
}

void *__wrap__malloc_r(struct _reent *reent, size_t nbytes)
{
    if (nbytes == 0) {
        return NULL;
    }

    return LOS_MemAlloc(OS_SYS_MEM_ADDR, nbytes);
}

void *__wrap__memalign_r(struct _reent *reent, size_t align, size_t nbytes)
{
    if (nbytes == 0) {
        return NULL;
    }

    return LOS_MemAllocAlign(OS_SYS_MEM_ADDR, nbytes, align);
}
......

You may have noticed that the function is named by__ wrap_ Plus the name of the hook function. This is because the symbols of these functions already exist in newlib, so you need to use the wrap link option of gcc to replace these function symbols with the implementation of the kernel. In the configuration file of the device development board, such as / / device/board/fnlink/v200zr/liteos_m/config.gni, add the wrap link option of these functions, for example:

board_ld_flags += [
     "-Wl,--wrap=_malloc_r",
     "-Wl,--wrap=_realloc_r",
     "-Wl,--wrap=_free_r",
     "-Wl,--wrap=_memalign_r",
     "-Wl,--wrap=_malloc_usable_size_r",
]

3. Introduction to Newlib hook function

Hook function with open function_ Open as an example to introduce the calling process of newlib's hook function. The open() function is implemented in newlib cygwin \ newlib \ libc \ syscalls \ sysopen C, the function will call the function further_ open_r. This is a Reentrant Function that supports running in multiple threads.

int
open (const char *file,
        int flags, ...)
{
  va_list ap;
  int ret;

  va_start (ap, flags);
  ret = _open_r (_REENT, file, flags, va_arg (ap, int));
  va_end (ap);
  return ret;
}

All reentrant functions are defined in the folder newlib cygwin \ newlib \ libc \ reent_ open_r defined in the file newlib cygwin \ newlib \ libc \ reent \ openr C. The function code is as follows:

int
_open_r (struct _reent *ptr,
     const char *file,
     int flags,
     int mode)
{
  int ret;

  errno = 0;
  if ((ret = _open (file, flags, mode)) == -1 && errno != 0)
    ptr->_errno = errno;
  return ret;
}

Function_ open_r as shown in the above code, the function will be called further_ Open, taking the arm hardware platform as an example, this function is implemented in newlib cygwin \ libgloss \ arm \ syscalls C file. The newlib directory is the function implementation that has nothing to do with the hardware platform. The libloss directory is the underlying driver implementation, which is organized with each hardware platform as a folder. Syscalls in the directory of a specific hardware platform C file implements various pile functions required by newlib:

/* Forward prototypes.  */
int	_system		(const char *);
int	_rename		(const char *, const char *);
int	_isatty		(int);
clock_t _times		(struct tms *);
int	_gettimeofday	(struct timeval *, void *);
int	_unlink		(const char *);
int	_link		(const char *, const char *);
int	_stat		(const char *, struct stat *);
int	_fstat		(int, struct stat *);
int	_swistat	(int fd, struct stat * st);
void *	_sbrk		(ptrdiff_t);
pid_t	_getpid		(void);
int	_close		(int);
clock_t	_clock		(void);
int	_swiclose	(int);
int	_open		(const char *, int, ...);
int	_swiopen	(const char *, int);
int	_write		(int, const void *, size_t);
int	_swiwrite	(int, const void *, size_t);
_off_t	_lseek		(int, _off_t, int);
_off_t	_swilseek	(int, _off_t, int);
int	_read		(int, void *, size_t);
int	_swiread	(int, void *, size_t);
void	initialise_monitor_handles (void);

For the functions mentioned above_ open, the source code is as follows. The analysis will not be continued in the future. The LiteOS-M kernel will provide the implementation of these hook functions.

int
_open (const char * path, int flags, ...)
{
  return _swiopen (path, flags);
}

Click follow to learn about Huawei's new cloud technology for the first time~

Topics: kernel harmonyos