rp_binary_info/
lib.rs

1//! Code and types for creating Picotool compatible "Binary Info" metadata
2//!
3//! ## Example usage
4//!
5//! Enable the Cargo feature `binary-info`.
6//!
7//! Add this to your linker script (usually named `memory.x`):
8//!
9//! ```not_rust,ignore
10//! SECTIONS {
11//!     /* ### Boot ROM info
12//!      *
13//!      * Goes after .vector_table, to keep it in the first 512 bytes of flash,
14//!      * where picotool can find it
15//!      */
16//!     .boot_info : ALIGN(4)
17//!     {
18//!         KEEP(*(.boot_info));
19//!     } > FLASH
20//!
21//! } INSERT AFTER .vector_table;
22//!
23//! /* move .text to start /after/ the boot info */
24//! _stext = ADDR(.boot_info) + SIZEOF(.boot_info);
25//!
26//! SECTIONS {
27//!     /* ### Picotool 'Binary Info' Entries
28//!      *
29//!      * Picotool looks through this block (as we have pointers to it in our
30//!      * header) to find interesting information.
31//!      */
32//!     .bi_entries : ALIGN(4)
33//!     {
34//!         /* We put this in the header */
35//!         __bi_entries_start = .;
36//!         /* Here are the entries */
37//!         KEEP(*(.bi_entries));
38//!         /* Keep this block a nice round size */
39//!         . = ALIGN(4);
40//!         /* We put this in the header */
41//!         __bi_entries_end = .;
42//!     } > FLASH
43//! } INSERT AFTER .text;
44//!
45//! SECTIONS {
46//!     .flash_end : {
47//!         __flash_binary_end = .;
48//!     } > FLASH
49//! } INSERT AFTER .uninit;
50//! ```
51//!
52//! Then, add this to your Rust code:
53//!
54//! ```
55//! #[link_section = ".bi_entries"]
56//! #[cfg(all(feature = "binary-info", target_os = "none"))]
57//! #[used]
58//! pub static PICOTOOL_ENTRIES: [rp_binary_info::EntryAddr; 4] = [
59//!     rp_binary_info::rp_program_name!(c"Program Name Here"),
60//!     rp_binary_info::rp_cargo_version!(),
61//!     rp_binary_info::rp_binary_end!(__flash_binary_end),
62//!     rp_binary_info::int!(
63//!         rp_binary_info::make_tag(b"JP"),
64//!         0x0000_0001,
65//!         0x12345678
66//!     ),
67//! ];
68//!
69//! extern "C" {
70//!     static __flash_binary_end: u32;
71//! }
72//! ```
73//!
74//! ## Cargo features
75//!
76//! The `binary-info` Cargo feature enables emitting the main `PICOTOOL_HEADER`
77//! static, which is what Picotool looks for to discover the binary info.
78//!
79//! It is optional to allow you to emit the static yourself differently, for e.g.
80//! compatibility with different linker scripts, while still allowing using the
81//! rest of the utilities in the crate to format the info.
82
83#![no_std]
84#![warn(missing_docs)]
85
86pub mod consts;
87
88mod types;
89pub use types::*;
90
91#[macro_use]
92mod macros;
93
94extern "C" {
95    /// The linker script sets this symbol to have the address of the first
96    /// entry in the `.bi_entries` section.
97    static __bi_entries_start: EntryAddr;
98    /// The linker script sets this symbol to have the address just past the
99    /// last entry in the `.bi_entries` section.
100    static __bi_entries_end: EntryAddr;
101    /// The linker script sets this symbol to have the address of the first
102    /// entry in the `.data` section.
103    static __sdata: u32;
104    /// The linker script sets this symbol to have the address just past the
105    /// first entry in the `.data` section.
106    static __edata: u32;
107    /// The linker script sets this symbol to have the address of the
108    /// initialisation data for the first entry in the `.data` section (i.e. a
109    /// flash address, not a RAM address).
110    static __sidata: u32;
111}
112
113/// Picotool can find this block in our ELF file and report interesting
114/// metadata.
115///
116/// The data here tells picotool the start and end flash addresses of our
117/// metadata.
118#[link_section = ".boot_info"]
119#[cfg(all(feature = "binary-info", target_os = "none"))]
120#[used]
121#[allow(unused_unsafe)] // addr_of! is safe since rust 1.82.0
122pub static PICOTOOL_HEADER: Header = unsafe {
123    Header::new(
124        core::ptr::addr_of!(__bi_entries_start),
125        core::ptr::addr_of!(__bi_entries_end),
126        &MAPPING_TABLE,
127    )
128};
129
130/// This tells picotool how to convert RAM addresses back into Flash addresses
131pub static MAPPING_TABLE: [MappingTableEntry; 2] = [
132    // This is the entry for .data
133    #[allow(unused_unsafe)]
134    MappingTableEntry {
135        source_addr_start: unsafe { core::ptr::addr_of!(__sidata) },
136        dest_addr_start: unsafe { core::ptr::addr_of!(__sdata) },
137        dest_addr_end: unsafe { core::ptr::addr_of!(__edata) },
138    },
139    // This is the terminating marker
140    MappingTableEntry::null(),
141];
142
143/// Create a 'Binary Info' entry containing the program name
144///
145/// This is well-known to picotool, and will be displayed if you run `picotool info`.
146///
147/// * Tag: [`consts::TAG_RASPBERRY_PI`]
148/// * ID: [`consts::ID_RP_PROGRAM_NAME`]
149pub const fn rp_program_name(name: &'static core::ffi::CStr) -> StringEntry {
150    StringEntry::new(consts::TAG_RASPBERRY_PI, consts::ID_RP_PROGRAM_NAME, name)
151}
152
153/// Create a 'Binary Info' entry containing the program version.
154///
155/// * Tag: [`consts::TAG_RASPBERRY_PI`]
156/// * Id: [`consts::ID_RP_PROGRAM_VERSION_STRING`]
157pub const fn rp_program_version(name: &'static core::ffi::CStr) -> StringEntry {
158    StringEntry::new(
159        consts::TAG_RASPBERRY_PI,
160        consts::ID_RP_PROGRAM_VERSION_STRING,
161        name,
162    )
163}
164
165/// Create a 'Binary Info' entry with a URL
166///
167/// * Tag: [`consts::TAG_RASPBERRY_PI`]
168/// * Id: [`consts::ID_RP_PROGRAM_URL`]
169pub const fn rp_program_url(url: &'static core::ffi::CStr) -> StringEntry {
170    StringEntry::new(consts::TAG_RASPBERRY_PI, consts::ID_RP_PROGRAM_URL, url)
171}
172
173/// Create a 'Binary Info' with the program build date
174///
175/// * Tag: [`consts::TAG_RASPBERRY_PI`]
176/// * Id: [`consts::ID_RP_PROGRAM_BUILD_DATE_STRING`]
177pub const fn rp_program_build_date_string(value: &'static core::ffi::CStr) -> StringEntry {
178    StringEntry::new(
179        consts::TAG_RASPBERRY_PI,
180        consts::ID_RP_PROGRAM_BUILD_DATE_STRING,
181        value,
182    )
183}
184
185/// Create a 'Binary Info' with the size of the binary
186///
187/// * Tag: [`consts::TAG_RASPBERRY_PI`]
188/// * Id: [`consts::ID_RP_BINARY_END`]
189pub const fn rp_binary_end(value: *const ()) -> PointerEntry {
190    PointerEntry::new(consts::TAG_RASPBERRY_PI, consts::ID_RP_BINARY_END, value)
191}
192
193/// Create a 'Binary Info' with a description of the program
194///
195/// * Tag: [`consts::TAG_RASPBERRY_PI`]
196/// * Id: [`consts::ID_RP_PROGRAM_DESCRIPTION`]
197pub const fn rp_program_description(value: &'static core::ffi::CStr) -> StringEntry {
198    StringEntry::new(
199        consts::TAG_RASPBERRY_PI,
200        consts::ID_RP_PROGRAM_DESCRIPTION,
201        value,
202    )
203}
204
205/// Create a 'Binary Info' with some feature of the program
206///
207/// * Tag: [`consts::TAG_RASPBERRY_PI`]
208/// * Id: [`consts::ID_RP_PROGRAM_FEATURE`]
209pub const fn rp_program_feature(value: &'static core::ffi::CStr) -> StringEntry {
210    StringEntry::new(
211        consts::TAG_RASPBERRY_PI,
212        consts::ID_RP_PROGRAM_FEATURE,
213        value,
214    )
215}
216
217/// Create a 'Binary Info' with some whether this was a Debug or Release build
218///
219/// * Tag: [`consts::TAG_RASPBERRY_PI`]
220/// * Id: [`consts::ID_RP_PROGRAM_BUILD_ATTRIBUTE`]
221pub const fn rp_program_build_attribute(value: &'static core::ffi::CStr) -> StringEntry {
222    StringEntry::new(
223        consts::TAG_RASPBERRY_PI,
224        consts::ID_RP_PROGRAM_BUILD_ATTRIBUTE,
225        value,
226    )
227}
228
229/// Create a 'Binary Info' with the Pico SDK version used
230///
231/// * Tag: [`consts::TAG_RASPBERRY_PI`]
232/// * Id: [`consts::ID_RP_SDK_VERSION`]
233pub const fn rp_sdk_version(value: &'static core::ffi::CStr) -> StringEntry {
234    StringEntry::new(consts::TAG_RASPBERRY_PI, consts::ID_RP_SDK_VERSION, value)
235}
236
237/// Create a 'Binary Info' with which board this program targets
238///
239/// * Tag: [`consts::TAG_RASPBERRY_PI`]
240/// * Id: [`consts::ID_RP_PICO_BOARD`]
241pub const fn rp_pico_board(value: &'static core::ffi::CStr) -> StringEntry {
242    StringEntry::new(consts::TAG_RASPBERRY_PI, consts::ID_RP_PICO_BOARD, value)
243}
244
245/// Create a 'Binary Info' with which `boot2` image this program uses
246///
247/// * Tag: [`consts::TAG_RASPBERRY_PI`]
248/// * Id: [`consts::ID_RP_BOOT2_NAME`]
249pub const fn rp_boot2_name(value: &'static core::ffi::CStr) -> StringEntry {
250    StringEntry::new(consts::TAG_RASPBERRY_PI, consts::ID_RP_BOOT2_NAME, value)
251}
252
253/// Create a tag from two ASCII letters.
254///
255/// ```
256/// let tag = rp_binary_info::make_tag(b"RP");
257/// assert_eq!(tag, 0x5052);
258/// ```
259pub const fn make_tag(c: &[u8; 2]) -> u16 {
260    u16::from_le_bytes(*c)
261}
262
263// End of file