File: using.md

package info (click to toggle)
picolibc 1.8.11-2
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 50,064 kB
  • sloc: ansic: 404,031; asm: 24,984; sh: 2,585; python: 2,289; perl: 680; pascal: 329; exp: 287; makefile: 222; cpp: 71; xml: 40
file content (212 lines) | stat: -rw-r--r-- 7,966 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
# Using Picolibc in Embedded Systems

Picolibc is designed to be used in deeply embedded systems, those
running either no operating system or a small RTOS. It is designed to
be linked statically along with any operating system and application
code.

### Prerequisites

It is expected that the toolchain used for compilation of applicatons
against Picolibc provides the following headers in its default search
paths:

 * float.h
 * iso646.h
 * stdalign.h
 * stdarg.h
 * stdbool.h
 * stddef.h
 * tgmath.h

Vanilla GCC & Clang/LLVM toolchains take care of that so most of
the users have everything in place. But some third-party toolchains
may expect these headers to be provided by the C library. Unfortunately
it is not possible as these headers have compiler-specific parts and
so whatever version could be imported in the Picolibc, it won't work
nicely with certain compilers. Moreover even different versions of the
same compiler may not work well with the same version of one of the
headers mentioned above.

## Compiling with Picolibc

To compile source code to use Picolibc, you can use the GCC .specs
file delivered with Picolibc. This will set the system header file
path and the linker library path to point at Picolibc. For example, to
compile a single file and generate a .o file:

	$ gcc --specs=picolibc.specs -c foo.c

When installed directly into the system, picolibc.specs is placed in
the gcc directory so that it will be found using just the base name of
the file. If installed in a separate location, you will need to
provide an absolute pathname to the picolibc.specs file:

	$ gcc --specs=/usr/local/picolibc/picolibc.specs -c foo.c

When building for an embedded system, you'll probably need to use a
longer name for the compiler, something like `riscv-unknown-elf-gcc`
or `arm-none-eabi-gcc`.

## Picolibc startup

Initializing an embedded system usually requires a combination of
hardware, run-time, library and application setup. You may want to
perform all of these in your own code, or you be able to use the
Picolibc startup code. Either works fine, using the Picolibc code
means writing less of your own.

### Picocrt — Picolibc startup code

Picocrt is the crt0.o code provided by Picolibc. This is enabled by
default when using -specs=picolibc.specs:

	$ gcc -specs=picolibc.specs -o foo.elf foo.c

Picocrt goes through a sequence of initialization steps, many of which
you can plug your own code into:

 1) Architecture-specific runtime initialization. For instance, RISC-V
    needs the `gp` register initialized for quick access to global
    variables while ARM with hardware floating point needs to have the
    FPSCR register set up to match C semantics for rounding.
    
 2) Data initialization. Here's the code inside Picocrt:\
    \
    `memcpy(__data_start, __data_source, (uintptr_t) __data_size);`\
    \
    For this to work, the linker script must assign correct values to
    each of these symbols:

    * __data_start points to the RAM location where the .data segment
      starts.
    * __data_source points to the Flash location where the .data segment
      initialization values are stored.
    * __data_size is an absolute symbol noting the size of the
      initialized data segment

 3) BSS initialization. Here's the code inside Picocrt:\
    \
    `memset(__bss_start, '\0', (uintptr_t) __bss_size);`\
    \
    Assign these symbols in the linker script as follows:

    * __bss_start points to the RAM location where the .bss segment
      starts.
    * __bss_size is an absolute symbol noting the size of the cleared
      data segment

 4) Optionally call constructors:

    * The default and hosted crt0 variants call
      [initializers/constructors](init.md) using `__libc_init_array()`

    * The minimal crt0 variant doesn't call any constructors

 5) Call `main()`

 6) After main returns:

    * The default and minimal crt0 variants run an infinite
      loop if main returns.

    * The hosted crt0 variant calls `exit`, passing it the value
      returned from main.

## Linking

Picolibc provides two sample linker scripts: `picolibc.ld` for C
applications and `picolibcpp.ld` for C++ applications. These are
designed to be useful for fairly simple applications running on
embedded hardware which need read-only code and data stored in flash
and read-write data allocated in RAM and initialized by picolibc at
boot time. You can read more about this on the [linking](linking.md)
page.

## Semihosting

“Semihosting” is a mechanism used when the application is run under a
debugger or emulation environment that provides access to some
functionality of the OS running the debugger or emulator. Picolibc
has semihosting support for console I/O and the exit() call.

One common use for semihosting is to generate debug messages before
any of the target hardware has been set up.

Picolibc distributes the semihosting implementation as a separate
library, `libsemihost.a`. Because it provides interfaces that are used
by libc itself, it must be included in the linker command line *after*
libc. You can do this by using the GCC --oslib=semihost
command line flag defined by picolibc.specs:

	$ gcc --specs=picolibc.specs --oslib=semihost -o program.elf program.o

You can also list libc and libsemihost in the correct order
explicitly:

	$ gcc --specs=picolibc.specs -o program.elf program.o -lc -lsemihost

## Crt0 variants

The default `crt0` version provided by Picolibc calls any constructors
before it calls main and then goes into an infinite loop after main
returns to avoid requiring an `_exit` function.

In an environment which provides a useful `_exit` implementation, applications
may want to use the `crt0-hosted` variant that calls `exit` when main
returns, resulting in a clean return to the hosting environment (this
conforms to a hosted execution environment as per the C
specification). Select this using the `--crt0=hosted` flag:

	$ gcc --specs=picolibc.specs --crt0=hosted -o program.elf program.o

In a smaller environment which is not using any constructors (or any
Picolibc code which requires constructors) may want to use the
`crt0-minimal` variant that removes the call to
`__libc_init_array()`. This variant also runs an infinite loop in case
main returns. Select this using the `--crt0=minimal` flag:

	$ gcc --specs=picolibc.specs --crt0=minimal -o program.elf program.o

To avoid the complexity of arranging the TLS segments between the
.data and .bss segments in your linker script, use the `crt0-inittls`
variant along with the `picolibc-inittls.ld` linker script. This
allocates space for TLS data above the regular `.bss` data and
initializes it at program startup time. Select this using the
`--crt0=inittls` flag along with the modified linker script:

	$ gcc --specs=picolibc.specs --crt0=inittls -Tpicolibc_inittls -o program.elf program.o

To modify an existing linker script for use with `--crt0=inittls`,
add lines like this any place within the read-only data sections:

	.tdata : {
		*(.tdata .tdata.* .gnu.linkonce.td.*)
	} >flash

	PROVIDE( __tdata_source = LOADADDR(.tdata) );
	PROVIDE( __tdata_size = SIZEOF(.tdata) );

	.tbss (NOLOAD) : {
		*(.tbss .tbss.* .gnu.linkonce.tb.*)
		*(.tcommon)
	} >flash

	.tls_space (NOLOAD) : {
		. = ALIGN(@PREFIX@__tls_align);
		PROVIDE( @PREFIX@__tls_space = .);
		. = . + @PREFIX@__tls_data_size;
	} >ram AT>ram :ram

	PROVIDE( __tbss_offset = ADDR(.tbss) - ADDR(.tdata) );
	PROVIDE( __tbss_size = SIZEOF(.tbss) );
	PROVIDE( __tls_align = MAX(ALIGNOF(.tdata), ALIGNOF(.tbss)) );
	PROVIDE( __tls_size = ALIGN(ADDR(.tbss) + SIZEOF(.tbss) - ADDR(.tdata), __tls_align) );

	PROVIDE( __arm32_tls_tcb_offset = MAX(8, __tls_align) );
	PROVIDE( __arm64_tls_tcb_offset = MAX(16, __tls_align) );
	PROVIDE( __or1k_tls_tcb_offset = MAX(16, __tls_align) );
	PROVIDE( __sh32_tls_tcb_offset = MAX(8, __tls_align) );

The last few symbols are for specific architectures, so you only need
add those relevant to your project.