嵌入式实时操作系统支撑C++17运行时环境

C++概述

C语言是当今最流行的程序设计语言之一,它的功能丰富、表达力强、使用灵活方便、应用面广、目标程序高、可植入性好,既有高级语言的特点,又有低级语言的许多特点,适合作为系统描述语言,既可以用来编写系统软件,也可以用来编写应用软件。在C的基础上,一九八三年又由贝尔实验室的Bjarne Strou-strup推出了C++。 C++进一步扩充和完善了C语言,成为一种面向 对象的程序设计语言。C++目前流行的集成开发环境最新版本是Borland C++4.5,Symantec C++6.1,和Microsoft VisualC++2017。C++提出了一些更为深入的概念,它所支持的这些面向对象的概念容易将问题空间直接地映射到程序空间,为程序员提供了一种与传统结构程序设计不同的思维方式和编程方法。因而也增加了整个语言的复杂性,掌握起来有一定难度。

C++17 是继 C++14 之后,C++ 编程语言 ISO/IEC 标准的下一次修订的非正式名称。ISO C++ 委员会正式发布了 C++ 17 标准,官方名称为 ISO/IEC 14882:2017。

嵌入式实时操作系统支撑C++17运行时环境

基于 C++ 11,C++ 17 旨在使 C++ 成为一个不那么臃肿复杂的编程语言,以简化该语言的日常使用,使开发者可以更简单地编写和维护代码。C++ 17 是对 C++ 语言的重大更新,引入了许多新的语言特性。

嵌入式操作系统适配C++

GCC编译器编译 C++ 工程时, 如果存在全局对象, 那么全局对象的构建函数指针会放在可执行 elf 文件的 .ctors 节区(section), 析构函数会放在可执行 elf 文件的 .dtors 节区, 一般标准 gcc 库会引出四个符号:

__CTOR_LIST__

__CTOR_END__

__DTOR_LIST__

__DTOR_END__

其中 __CTOR_LIST__ 表示所有的全局对象构造函数指针数组的首地址, 起始指针为 0xFFFFFFFF, 之后的每一项为一个构造函数的入口, 直到 __CTOR_END__ 为止, __CTOR_END__ 指向的函数指针为 0x00000000。

其中 __DTOR_LIST__ 表示所有的全局对象析构函数指针数组的首地址, 起始指针为 0xFFFFFFFF, 之后的每一项为一个析构函数的入口, 直到 __DTOR_END__ 为止, __DTOR_END__ 指向的函数指针为 0x00000000。

系统在运行用户程序之前, 初始化 C++ 环境, 需运行全局对象的构造函数, 在系统 reboot 时, 运行系统的析构函数。

编译GCC工具链时,GCC源码路径下的libgcc/crtstuff.c会生成两个二进制文件crtbegin.o和crtend.o,其__CTOR_LIST__、__CTOR_END__、__DTOR_LIST__和__DTOR_END__位于上述两个文件。

嵌入式实时操作系统支撑C++17运行时环境

构造函数为__do_global_ctors_aux,其实现为循环调用各个全局构造函数,其简化代码如下:


static void __do_global_ctors_aux (void)

{

func_ptr *p, f;

for (p = __DTOR_LIST__ + 1; (f = *p); p++)

f ();

}


嵌入式实时操作系统支撑C++17运行时环境

但是构造函数__do_global_ctors_aux和析构函数__do_global_dtors_aux为静态函数,无法直接调用,构造函数被包含在.init段中,析构函数被包含在.fini段中。因此我们需要自己实现一个函数包含.init段调用__do_global_ctors_aux构造函数。其实现代码如下:

.section ".init_begin"

.global initCplusplus

initCplusplus:

push %ebp;

mov %esp,%ebp

.section .init_end

leave

ret

连接脚本中.init段位于.init_begin和.init_end中间。其连接示例脚本如下:

*(.init_begin);

*(.init);

*(.init_end);

因此编译出的代码如下:


4003295d <initcplusplus>:/<initcplusplus>

4003295d: 55 push %ebp

4003295e: 89 e5 mov %esp,%ebp

40032960: e8 7f ed fc ff call 400016e4 <frame>

40032965: e8 fa b1 ff ff call 4002db64

<__do_global_ctors_aux/>

4003296a: c9 leave

4003296b: c3 ret


initCplusplus函数在操作系统启动中调用后C++应用程序的全局构造函数就被调用了,具备了C++的基本运行环境。因为C++会引入很多段信息,因此链接脚本需要添加很多段,为了简化实现,可在代码段的添加 *( .text.* ) ,数据段增加*( .data.* ),bss段增加*( .bss.* )等。

操作系统cout适配

libstdc++库的ios_base::Init::Init()函数中会初始化输入输出流相关操作。代码如下:

new (&buf_cout_sync) stdio_sync_filebuf<char>(stdout);/<char>

new (&buf_cin_sync) stdio_sync_filebuf<char>(stdin);/<char>

new (&buf_cerr_sync) stdio_sync_filebuf<char>(stderr);/<char>

new (&cout) ostream(&buf_cout_sync);

new (&cin) istream(&buf_cin_sync);

new (&cerr) ostream(&buf_cerr_sync);

new (&clog) ostream(&buf_cerr_sync);

上述代码分析知道cout实际调用的是stdio_sync_filebuf类相关接口,最终cout会调用std::fwrite等操作系统相关接口。上述关键在于stdout、stdin、stderr与操作系统相关,因为GCC与newlib绑定。newlib把stdout、stdin、stderr三个指针设置到了_reent结构体里的__FILE *_stdin, *_stdout, *_stderr三个变量。

struct _reent _impure_data = _REENT_INIT(_impure_data);

struct _reent *_impure_ptr = &_impure_data;

_reent结构体里涉及__FILE *_stdin, *_stdout, *_stderr;三个变量,上述三个变量需要设置为自己操作系统的tdout、stdin、stderr。


分享到:


相關文章: