请选择 进入手机版 | 继续访问电脑版

前馈科技

 找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 3414|回复: 0

Windows原生编译生成ELF符号表问题

[复制链接]

97

主题

97

帖子

539

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
539
发表于 2021-10-27 10:50:58 | 显示全部楼层 |阅读模式
近日捣鼓Windows原生环境下编译使能ELF动态加载的NuttX,虽然年初已经实现了原生Windows下编译NuttX,但当时未使能ELF文件的动态加载,这次使能ELF加载功能后竟然出现了一些错误。大概交代下,本次ELF的符号表是采用NuttX提供所有接口函数符号表的方式。即NuttX根据syscall.csv,libc.csv,math.csv生成符号表。编译过程报的错误如下图所示:
1.png
问题出在./libs/libc/Makefile中如下部分:
  1. modlib_symtab.c : $(CSVFILES) $(MKSYMTAB)
  2.         $(Q) cat $(CSVFILES) | LC_ALL=C sort >$@.csv
  3.         $(Q) $(MKSYMTAB) $@.csv $@ $(CONFIG_MODLIB_SYMTAB_ARRAY) $(CONFIG_MODLIB_NSYMBOLS_VAR)
  4.         $(Q) rm -f $@.csv
复制代码
这句:$(Q) cat $(CSVFILES) | LC_ALL=C sort >$@.csv
Windows环境下识别不了LC_ALL=C表达式!
在Linux中通过locale来设置程序运行的不同语言环境,locale由ANSI C提供支持。locale的命名规则为<语言>_<地区>.<字符集编码>,如zh_CN.UTF-8,zh代表中文,CN代表大陆地区,UTF-8表示字符集。LC_ALL=C 是为了去除所有本地化的设置,让命令能正确执行。在使用uniq的时候会遇到不能排重的问题。原因是LC_ALL可能指定的是urf8编码,设置LC_ALL=C,可以让sort按照字节排序!
由于在Windows下暂时不知道有啥对应的处理方式,为了解决问题,只能采取粗暴的方式直接删除LC_ALL=C,代码改为如下:
  1. exec_symtab.c : $(CSVFILES) $(MKSYMTAB)
  2.         $(Q) cat $(CSVFILES) | sort >$@.csv
  3.         $(Q) $(MKSYMTAB) $@.csv $@ $(CONFIG_EXECFUNCS_SYMTAB_ARRAY) $(CONFIG_EXECFUNCS_NSYMBOLS_VAR)
  4.         $(Q) rm -f $@.csv
复制代码
编译反正没报错,会有啥影响后面再说吧!
改后重新编译,又报新的错误:
2.png
这次的错误就有点狠了,分析了半天毫无头绪!编译过程大概如下:
./libs/libc/Makefile先将syscall.csv,libc.csv,math.csv合并成一个临时的exec_symtab.c.csv,再用通过./tools/mksymtab.c编译出来的主机应用程序mksymtab.exe由exec_symtab.c.csv中提取接口函数生成符号表c文件exec_symtab.c,问题就出在mksymtab.exe对exec_symtab.c.csv的解析!具体定位在exec_symtab.c.csv的188行:
  1. "posix_spawn","spawn.h","!defined(CONFIG_BINFMT_DISABLE) && defined(CONFIG_LIBC_EXECFUNCS) && !defined(CONFIG_LIB_ENVPATH)","int","FAR pid_t *","FAR const char *","FAR const posix_spawn_file_actions_t *","FAR const posix_spawnattr_t *","FAR char *const []|FAR char *const *","FAR char *const []|FAR char *const *"
复制代码
起初怀疑其中的"FAR char *const []|FAR char *const *"存在|符号不被mksymtab.exe识别,但经过分析,一下两点不支持该假定:
1、在188行掐面也有大量的的|运算符;
2、mksymtab.exe是通过主机gcc编译出来的程序,Linux和Windows公用一个mksymtab.c,而Linux下不存在问题。
继续观察发现出问题的188行这个posix_spawn函数原型和exec_symtab.c.csv中的所有其他函数原型相比特别的长,算上空格都有三百多个字符了!于是怀疑是否长度超过了mksymtab.exe中允许的最大长度。
4.png
分析mksymtab.exe的源程序mksymtab.c并没发现啥限制,但mksymtab.c是通过调用csvparser.c中的函数来解析csv文件的!而分析csvparser.c发现它里面通过一个全局数组来存放读取的每一行csv文本,该数组定义为:extern char g_line[LINESIZE+1];
  1. /****************************************************************************
  2. * Public Data
  3. ****************************************************************************/
  4. extern bool g_debug;
  5. extern char g_line[LINESIZE+1];
  6. extern char g_parm[MAX_FIELDS][MAX_PARMSIZE];
  7. extern int  g_lineno;
复制代码
而 LINESIZE的定义为:#define LINESIZE      (PATH_MAX > 256 ? PATH_MAX : 256),即:若PATH_MAX > 256若大于256则LINESIZE取PATH_MAX ,否则LINESIZE取256!
3.png
显然这里可能存在猫腻,正如前面分析的posix_spawn函数原型算上空格都有三百多个字符了!极有可能超过 LINESIZE而导致无法被解析。这里关键就取决于PATH_MAX这个值!
观察发现PATH_MAX并不在./tools中,它是系统相关的,csvparser.c通过#include <limits.h>包含了主机gcc系统的limits.h,而我的主机gcc用的是CodeBlocks中的MinGW
观察MinGW的include中的limits.h发现如下定义:
  1. /*
  2. * File system limits
  3. *
  4. * TODO: NAME_MAX and OPEN_MAX are file system limits or not? Are they the
  5. *       same as FILENAME_MAX and FOPEN_MAX from stdio.h?
  6. * NOTE: PATH_MAX is the POSIX equivalent for Microsoft's MAX_PATH; the two
  7. *       are semantically identical, with a limit of 259 characters for the
  8. *       path name, plus one for a terminating NUL, for a total of 260.
  9. */
  10. #define PATH_MAX        260
复制代码
5.png
综上所属,LINESIZE的实际值为 260,极有可能导致数组char g_line[LINESIZE+1]偏小,不够posix_spawn函数原型的长度。
关于系统相关的参数PATH_MAX的备注如下:
在处理文件系统路径的时候,我们一般会先开辟一块内存区,用来接收路径、或者拼接好路径传递给系统调用。这是因为路径在各个系统上都有最大长度限制,在 Windows 上这个值是 MAX_PATH,一般不能超过 260;在 Linux 上这个值是 PATH_MAX,一般不能超过 4096 (或者通过 pathconf (_PC_PATH_MAX, ...) 来获取,但是一般也是 4096)。
这也解释了为何在Linux下编译未报该错误。
虽然csv文件中的每一行都是函数原型,并不是路径,但显然csvparser.c借用了系统参数PATH_MAX来定义数组,PATH_MAX是系统相关,我们肯定不能修改它,只能采用简单粗暴的方式:修改csvparser.h中对LINESIZE的定义:
  1. /****************************************************************************
  2. * Pre-processor Definitions
  3. ****************************************************************************/
  4. #define LINESIZE      360//(PATH_MAX > 256 ? PATH_MAX : 256)
复制代码
改后重新编译,果然不再报错,看来问题定位准确!

关于超出260会不会溢出见下面这面高手写的文章:
https://www.cnblogs.com/goodcitizen/p/13178034.html

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|计算机控制

GMT+8, 2024-3-29 13:52 , Processed in 0.054173 second(s), 21 queries .

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表