BL libc

1. 背景

问题

  • 在使用编译器自带的newlibc中的C库函数时,会出现stack溢出的现象
  • 一些客户在不熟悉sdk api的情况下,错误的使用C库函数自带的API,比如malloc

为了解决这些问题,将编译器的newlibc改为newlibc-nano,并使用sdk现有API适配部分C库函数,以提高sdk接口通用性,减少客户使用过程中可能出现的错误。

2. API

2.1 Support

mkdir

  • 创建目录
int  mkdir (const char *_path, mode_t __mode )
{
#ifndef CONF_VFS_ENABLE
    return -1;
#else
    int rc;

    rc = aos_mkdir(_path);
    return rc;
#endif
}

open

  • 打开文件
int _open_r(struct _reent *ptr, const char *file, int flags, int mode)
{
#ifndef CONF_VFS_ENABLE
    /* return "not supported" */
    ptr->_errno = ENOSYS;
    return -1;
#else
    int rc;

    rc = aos_open(file, flags);
    return rc;
#endif
}

clsoe

  • 关闭文件
int _close_r(struct _reent *ptr, int fd)
{
#ifndef CONF_VFS_ENABLE
    /* return "not supported" */
    ptr->_errno = ENOSYS;
    return -1;
#else
    return aos_close(fd);
#endif
}

read

  • 读取数据到buf
_ssize_t _read_r(struct _reent *ptr, int fd, void *buf, size_t nbytes)
{
#ifndef CONF_VFS_ENABLE
    /* return "not supported" */
    ptr->_errno = ENOSYS;
    return -1;
#else
    _ssize_t rc;

    rc = aos_read(fd, buf, nbytes);
    return rc;
#endif
}

write

  • 写数据到文件
_ssize_t _write_r(struct _reent *ptr, int fd, const void *buf, size_t nbytes)
{
#ifndef CONF_VFS_ENABLE
    /* return "not supported" */
    ptr->_errno = ENOSYS;
    return -1;
#else
    _ssize_t rc;

    rc = aos_write(fd, buf, nbytes);
    return rc;
#endif
}

rename

  • 重命名文件
int _rename_r(struct _reent *ptr, const char *old, const char *new)
{
#ifndef CONF_VFS_ENABLE
    /* return "not supported" */
    ptr->_errno = ENOSYS;
    return -1;
#else
    int rc;

    rc = aos_rename(old, new);
    return rc;
#endif
}

lseek

  • 改变读写一个文件时读写指针位置
_off_t _lseek_r(struct _reent *ptr, int fd, _off_t pos, int whence)
{
#ifndef CONF_VFS_ENABLE
    /* return "not supported" */
    ptr->_errno = ENOSYS;
    return -1;
#else
    _off_t rc;

    rc = aos_lseek(fd, pos, whence);
    return rc;
#endif
}

stat

  • 获取文件状态
int _stat_r(struct _reent *ptr, const char *file, struct stat *pstat)
{
#ifndef CONF_VFS_ENABLE
    /* return "not supported" */
    ptr->_errno = ENOSYS;
    return -1;
#else
    int rc;

    rc = aos_stat(file, pstat);
    return rc;
#endif
}

malloc

  • 申请内存
void *_malloc_r(struct _reent *ptr, size_t size)
{
    void* result;

    result = (void*)pvPortMalloc(size);
    if (result == NULL)
    {
        ptr->_errno = ENOMEM;
    }

    return result;
}

free

  • 释放内存
void _free_r(struct _reent *ptr, void *addr)
{
    vPortFree(addr);
}

realloc

  • 重新申请内存
void *_realloc_r(struct _reent *ptr, void *old, size_t newlen)
{
    void* result;

    result = (void*)pvPortRealloc(old, newlen);
    if (result == NULL)
    {
        ptr->_errno = ENOMEM;
    }

    return result;
}

calloc

  • 申请空内存
void *_calloc_r(struct _reent *ptr, size_t size, size_t len)
{
    void* result;

    result = (void*)pvPortCalloc(size, len);
    if (result == NULL)
    {
        ptr->_errno = ENOMEM;
    }

    return result;
}

abort

  • 退出程序abort()exit()
void __attribute__ ((noreturn)) _exit (int status)
{
    portABORT();
    while (1);
}

random

  • 生成一个1 < num < RAND_MAX的随机数
使用RNG模块
long random (void)
{
    long result = 0;
    if (g_bl_sec_sha_mutex == NULL)
    {
        bl_sec_init();
    }

    result = (long)bl_rand();

    return result;
}

2.2 Not support

以下函数仅提供符号,未实现实际功能

sbrk

  • 调整data segment大小
大部分我们使用的是malloc和free函数来分配和释放内存。这样能够提高程序的性能,不是每次分配内存都调用brk或sbrk,而是重用前面空闲的内存空间。brk和sbrk分配的堆空间类似于缓冲池,每次malloc从缓冲池获得内存,如果缓冲池不够了,再调用brk或sbrk扩充缓冲池,直到达到缓冲池大小的上限,free则将应用程序使用的内存空间归还给缓冲池。
void *_sbrk_r(struct _reent *ptr, ptrdiff_t incr)
{
    return 0;
}

getpid

  • 取得进程识别码
void *_sbrk_r(struct _reent *ptr, ptrdiff_t incr)
{
    void *ret;
    ptr->_errno = ENOMEM;
    ret = (void *)-1;
    return ret;
}

execve

  • execve(执行文件)在父进程中fork一个子进程,在子进程中调用exec函数启动新的程序
int _execve_r(struct _reent *ptr, const char * name, char *const *argv, char *const *env)
{
    /* return "not supported" */
    ptr->_errno = ENOSYS;
    return -1;
}

fcntl

  • 改变文件性质
int _fcntl_r(struct _reent *ptr, int fd, int cmd, int arg)
{
    /* return "not supported" */
    ptr->_errno = ENOSYS;
    return -1;
}

fstat

  • 由文件描述词取得文件状态
fstat与stat区别在于fstat系统调用接受的是 一个“文件描述符”,stat可以调用完整的文件路径
int _fstat_r(struct _reent *ptr, int fd, struct stat *pstat)
{
    /* return "not supported" */
    ptr->_errno = ENOSYS;
    return -1;
}

srandom

  • srandom 函数将其参数设置为由random返回的新伪随机整数序列的种子,同一个种子会得到相同的随机数

3. 测试

$BL_IOT_SDK_PATH/customer_app/sdk_app_libc

3.1 stack used

  • Task stack: 10*1024(Kb)
Func newlibc-nano newlibc
none 69 69
printf 137 195
strstr/printf 137 195
strstr/printf/sscanf 250 1403

3.2 IO

Write data to the serial port

Func result
open ok
write ok
read ok
fsync ok
close ok
static void cmd_write(char *buf, int len, int argc, char **argv)
{
    if (argc != 2)
    {
        printf("Usage: write DATA to /dev/ttyS0\r\n");
        return;
    }

    int fd = open("/dev/ttyS0", 0);
    if (fd)
    {
        write(fd, argv[1], strlen(argv[1]));

        fsync(fd);

        close(fd);
    }
}

3.3 random

Generate random numbers

Func result
random ok
static void cmd_random(char *buf, int len, int argc, char **argv)
{
    long result = 0;

    result = random();

    printf("\r\n**********random test rand[%08x]**************\r\n", result);
}

3.4 memory

Malloc memory and free

Func result
malloc ok
free ok
realloc ok
static void cmd_mem(char *buf, int len, int argc, char **argv)
{
    void *p;

    p = malloc(10);
    if (p == NULL)
        merror("malloc (10) failed.");

    /* realloc (p, 0) == free (p).  */
    p = realloc(p, 0);
    if (p != NULL)
        merror("realloc (p, 0) failed.");
}