ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
emu_elf.c
Go to the documentation of this file.
1
12
13#include "emu_elf.h"
14
15#include <stdint.h>
16#include <string.h>
17
20#include "emu_memory_access.h"
21
29
31typedef struct {
32 uint32_t offset;
33 uint16_t entry_size;
34 uint16_t count;
36
50RA8_INTERNAL static uint16_t internal_u16(const uint8_t* bytes)
51{
52 return (uint16_t)((uint16_t)bytes[0] | ((uint16_t)bytes[1] << 8U));
53}
54
68RA8_INTERNAL static uint32_t internal_u32(const uint8_t* bytes)
69{
70 return (uint32_t)bytes[0] | ((uint32_t)bytes[1] << 8U) | ((uint32_t)bytes[2] << 16U) |
71 ((uint32_t)bytes[3] << k_elf_word_high_shift);
72}
73
92 uint16_t* machine)
93{
94 uint8_t bytes[k_elf_ehdr_size] = {};
95 emu_elf_view_t view = {};
96 if (priv_emu_elf_read(source, 0U, sizeof(bytes), bytes, sizeof(bytes), &view).status !=
98 return false;
99 }
100 *machine = internal_u16(&bytes[k_elf_e_machine_off]);
101 if ((memcmp(bytes,
102 "\x7F"
103 "ELF",
104 4U) != 0) ||
105 (bytes[4] != 1U) || (bytes[k_elf_data_offset] != 1U) ||
106 (*machine != (uint16_t)k_elf_em_arm)) {
107 return false;
108 }
109 const emu_elf_program_table_t decoded = {
110 .offset = internal_u32(&bytes[k_elf_e_phoff_off]),
111 .entry_size = internal_u16(&bytes[k_elf_e_phentsize_off]),
112 .count = internal_u16(&bytes[k_elf_e_phnum_off]),
113 };
114 const uint64_t table_bytes = (uint64_t)decoded.entry_size * decoded.count;
115 if (((decoded.count != 0U) && (decoded.entry_size < k_elf_program_header_min)) ||
116 ((uint64_t)decoded.offset > source->length) ||
117 (table_bytes > (source->length - decoded.offset))) {
118 return false;
119 }
120 *table = decoded;
121 return true;
122}
123
143 const emu_elf_program_table_t* table,
144 uint16_t index,
145 elf_exec_segment_t* segment)
146{
147 uint8_t bytes[k_elf_program_header_min] = {};
148 emu_elf_view_t view = {};
149 const uint64_t entry = (uint64_t)table->offset + ((uint64_t)index * table->entry_size);
150 if (priv_emu_elf_read(source, entry, sizeof(bytes), bytes, sizeof(bytes), &view).status !=
152 return false;
153 }
154 const uint32_t type = internal_u32(bytes);
155 const uint32_t offset = internal_u32(&bytes[k_elf_ph_offset_off]);
156 const uint32_t filesz = internal_u32(&bytes[k_elf_ph_filesz_off]);
157 if ((type != (uint32_t)k_elf_pt_load) || (filesz == 0U) || ((uint64_t)offset > source->length) ||
158 ((uint64_t)filesz > (source->length - offset))) {
159 return false;
160 }
161 *segment = (elf_exec_segment_t){
162 .source = source,
163 .offset = offset,
164 .vaddr = internal_u32(&bytes[k_elf_ph_vaddr_off]),
165 .paddr = internal_u32(&bytes[k_elf_ph_paddr_off]),
166 .filesz = filesz,
167 .flags = internal_u32(&bytes[k_elf_ph_flags_off]),
168 };
169 return true;
170}
171
173{
174 if ((elf == nullptr) || (fn == nullptr)) {
175 return 0U;
176 }
177 emu_elf_program_table_t table = {};
178 uint16_t machine = 0U;
179 if (!internal_program_table(elf, &table, &machine)) {
180 return 0U;
181 }
182 uint32_t visited = 0U;
183 for (uint16_t index = 0U; index < table.count; index++) {
184 elf_exec_segment_t segment = {};
185 if (!internal_load_segment(elf, &table, index, &segment)) {
186 continue;
187 }
188 visited++;
189 if (!fn(&segment, ctx)) {
190 break;
191 }
192 }
193 return visited;
194}
195
197typedef struct {
198 uc_engine* uc;
199 uint32_t loaded;
200 bool failed;
202
218RA8_INTERNAL static bool internal_stream_segment(const elf_exec_segment_t* segment, void* opaque)
219{
220 emu_elf_load_ctx_t* const ctx = (emu_elf_load_ctx_t*)opaque;
221 uint8_t scratch[k_elf_stream_scratch];
222 uint32_t done = 0U;
223 while (done < segment->filesz) {
224 const uint32_t remain = segment->filesz - done;
225 const size_t chunk = (remain < sizeof(scratch)) ? (size_t)remain : sizeof(scratch);
226 emu_elf_view_t view = {};
227 const emu_elf_io_result_t read = priv_emu_elf_read(segment->source,
228 (uint64_t)segment->offset + done,
229 chunk,
230 scratch,
231 sizeof(scratch),
232 &view);
233 if ((read.status != k_emu_elf_io_ok) ||
234 (emu_mem_write(ctx->uc, (uint64_t)segment->paddr + done, view.bytes, chunk) != UC_ERR_OK)) {
235 (void)priv_emu_io_errf("uc_mem_write seg @0x%08X (%u bytes) failed\n",
236 segment->paddr,
237 segment->filesz);
238 ctx->failed = true;
239 return false;
240 }
241 done += (uint32_t)chunk;
242 }
243 (void)priv_emu_io_errf(" loaded %u bytes @ 0x%08X\n", segment->filesz, segment->paddr);
244 ctx->loaded++;
245 return true;
246}
247
248int load_elf(uc_engine* uc, const emu_elf_source_t* elf)
249{
250 emu_elf_program_table_t table = {};
251 uint16_t machine = 0U;
252 if (!internal_program_table(elf, &table, &machine)) {
253 if ((machine == 0U) || (machine == (uint16_t)k_elf_em_arm)) {
254 (void)priv_emu_io_errf("not a 32-bit ELF\n");
255 } else {
256 (void)priv_emu_io_errf("ELF e_machine %u != ARM(40)\n", machine);
257 }
258 return -1;
259 }
260 emu_elf_load_ctx_t ctx = {.uc = uc};
262 return (!ctx.failed && (ctx.loaded > 0U)) ? 0 : -1;
263}
264
266{
267 if ((elf == nullptr) || (fn == nullptr)) {
268 return 0U;
269 }
270 emu_elf_program_table_t table = {};
271 uint16_t machine = 0U;
272 if (!internal_program_table(elf, &table, &machine)) {
273 return 0U;
274 }
275 uint32_t visited = 0U;
276 for (uint16_t index = 0U; index < table.count; index++) {
277 elf_exec_segment_t segment = {};
278 if (!internal_load_segment(elf, &table, index, &segment) ||
279 ((segment.flags & (uint32_t)k_elf_pf_x) == 0U)) {
280 continue;
281 }
282 visited++;
283 if (!fn(&segment, ctx)) {
284 break;
285 }
286 }
287 return visited;
288}
289
291typedef struct {
292 uint32_t base;
293 bool found;
295
310RA8_INTERNAL static bool internal_vector_segment(const elf_exec_segment_t* segment, void* opaque)
311{
312 emu_elf_vector_ctx_t* const ctx = (emu_elf_vector_ctx_t*)opaque;
313 if (!ctx->found || (segment->vaddr < ctx->base)) {
314 ctx->base = segment->vaddr;
315 ctx->found = true;
316 }
317 return true;
318}
319
321{
322 emu_elf_vector_ctx_t ctx = {};
324 return ctx.found ? ctx.base : 0U;
325}
uint32_t elf_foreach_load_segment(const emu_elf_source_t *elf, elf_exec_segment_fn fn, void *ctx)
Walk every non-empty, bounds-checked PT_LOAD segment.
Definition emu_elf.c:172
emu_elf_stream_limit_t
Bounded parser and PT_LOAD transfer dimensions.
Definition emu_elf.c:23
@ k_elf_data_offset
ELF identification data-byte offset.
Definition emu_elf.c:27
@ k_elf_stream_scratch
Maximum transient segment bytes.
Definition emu_elf.c:25
@ k_elf_word_high_shift
Shift of byte three in a word.
Definition emu_elf.c:26
@ k_elf_program_header_min
ELF32 program-header bytes consumed.
Definition emu_elf.c:24
static bool internal_vector_segment(const elf_exec_segment_t *segment, void *opaque)
Fold one executable segment into the lowest-VMA accumulator.
Definition emu_elf.c:310
static bool internal_load_segment(const emu_elf_source_t *source, const emu_elf_program_table_t *table, uint16_t index, elf_exec_segment_t *segment)
Decode one bounds-checked PT_LOAD entry.
Definition emu_elf.c:142
static uint16_t internal_u16(const uint8_t *bytes)
Decode one little-endian 16-bit ELF field.
Definition emu_elf.c:50
static bool internal_program_table(const emu_elf_source_t *source, emu_elf_program_table_t *table, uint16_t *machine)
Read and validate the ELF32 ARM header and program-table geometry.
Definition emu_elf.c:90
static uint32_t internal_u32(const uint8_t *bytes)
Decode one little-endian 32-bit ELF field.
Definition emu_elf.c:68
uint32_t elf_foreach_exec_segment(const emu_elf_source_t *elf, elf_exec_segment_fn fn, void *ctx)
Walk every executable PT_LOAD segment of an ELF32 image.
Definition emu_elf.c:265
int load_elf(uc_engine *uc, const emu_elf_source_t *elf)
Load ELF32 PT_LOAD segments into emulated memory at their LMA.
Definition emu_elf.c:248
uint32_t elf_vector_base(const emu_elf_source_t *elf)
Vector-table base of an ELF: lowest executable PT_LOAD VMA.
Definition emu_elf.c:320
static bool internal_stream_segment(const elf_exec_segment_t *segment, void *opaque)
Stream one PT_LOAD segment into Unicorn in bounded chunks.
Definition emu_elf.c:218
ELF32 image services for the board emulator (load / symbols / vectors).
bool(* elf_exec_segment_fn)(const elf_exec_segment_t *seg, void *ctx)
Per-segment callback for elf_foreach_exec_segment.
Definition emu_elf.h:255
@ k_elf_pf_x
PF_X: segment is executable.
Definition emu_elf.h:63
@ k_elf_em_arm
e_machine == EM_ARM.
Definition emu_elf.h:53
@ k_elf_ph_paddr_off
p_paddr in a program header.
Definition emu_elf.h:60
@ k_elf_ph_flags_off
p_flags in a program header.
Definition emu_elf.h:62
@ k_elf_e_machine_off
e_machine in the file header.
Definition emu_elf.h:54
@ k_elf_ph_vaddr_off
p_vaddr (VMA) in a program header.
Definition emu_elf.h:59
@ k_elf_e_phentsize_off
e_phentsize in the file header.
Definition emu_elf.h:56
@ k_elf_e_phoff_off
e_phoff in the file header.
Definition emu_elf.h:55
@ k_elf_e_phnum_off
e_phnum in the file header.
Definition emu_elf.h:57
@ k_elf_ph_filesz_off
p_filesz in a program header.
Definition emu_elf.h:61
@ k_elf_ehdr_size
ELF32 file-header size.
Definition emu_elf.h:52
@ k_elf_ph_offset_off
p_offset in a program header.
Definition emu_elf.h:58
@ k_elf_pt_load
p_type == PT_LOAD.
Definition emu_elf.h:64
@ k_emu_elf_io_ok
The complete operation succeeded.
Definition emu_elf.h:75
Private raw-descriptor ELF source operations.
emu_elf_io_result_t priv_emu_elf_read(const emu_elf_source_t *source, uint64_t offset, size_t required_bytes, void *scratch, size_t supplied_bytes, emu_elf_view_t *view)
Read one exact source range into caller-owned bounded scratch.
Bounded raw-descriptor I/O seam for the RA8 emulator.
emu_io_result_t priv_emu_io_errf(const char *format,...)
Format bounded text and write it to the injected error descriptor.
Central first-party Unicorn memory access seam.
uc_err emu_mem_write(uc_engine *uc, uint64_t address, const void *bytes, size_t count)
Write guest memory through the central access seam.
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
int memcmp(const void *a, const void *b, size_t n)
Compare bytes in two memory areas.
One executable PT_LOAD segment, already bounds-checked against the image.
Definition emu_elf.h:238
uint32_t offset
Segment file offset.
Definition emu_elf.h:240
uint32_t filesz
Segment file byte count.
Definition emu_elf.h:243
const emu_elf_source_t * source
Open source owning segment bytes.
Definition emu_elf.h:239
uint32_t paddr
Segment physical load address.
Definition emu_elf.h:242
uint32_t vaddr
Segment virtual address.
Definition emu_elf.h:241
uint32_t flags
ELF program-header flags.
Definition emu_elf.h:244
Exact result for source open and bounded view acquisition.
Definition emu_elf.h:83
emu_elf_io_status_t status
Semantic completion status.
Definition emu_elf.h:84
Context for streaming PT_LOAD bytes into one Unicorn engine.
Definition emu_elf.c:197
uc_engine * uc
Destination engine.
Definition emu_elf.c:198
uint32_t loaded
Completely transferred segments.
Definition emu_elf.c:199
bool failed
Sticky read/write failure.
Definition emu_elf.c:200
Decoded program-header table geometry.
Definition emu_elf.c:31
uint16_t count
Program-header entry count.
Definition emu_elf.c:34
uint32_t offset
First program-header file offset.
Definition emu_elf.c:32
uint16_t entry_size
Bytes per program-header entry.
Definition emu_elf.c:33
One independently owned immutable raw-descriptor ELF source.
Definition emu_elf.h:91
uint64_t length
Stat-derived parsing bound.
Definition emu_elf.h:93
Accumulator for the lowest executable segment VMA.
Definition emu_elf.c:291
uint32_t base
Current lowest VMA.
Definition emu_elf.c:292
bool found
Whether any segment was seen.
Definition emu_elf.c:293
Transient view into caller-owned bounded scratch.
Definition emu_elf.h:97
const uint8_t * bytes
Scratch containing the exact source bytes.
Definition emu_elf.h:98