@@ -13,6 +13,7 @@ const fs = std.fs;
13
13
const io = std .io ;
14
14
const sort = std .sort ;
15
15
const warn = std .debug .warn ;
16
+ const nl = std .cstr .line_sep ;
16
17
17
18
const BinaryElfSection = struct {
18
19
elfOffset : u64 ,
@@ -159,7 +160,146 @@ fn writeBinaryElfSection(elf_file: File, out_file: File, section: *BinaryElfSect
159
160
});
160
161
}
161
162
162
- fn emitRaw (allocator : * Allocator , elf_path : []const u8 , raw_path : []const u8 ) ! void {
163
+ const HexWriter = struct {
164
+ prev_addr : ? u32 = null ,
165
+ out_file : File ,
166
+
167
+ /// Max data bytes per line of output
168
+ const MAX_PAYLOAD_LEN : u8 = 16 ;
169
+
170
+ fn addressParts (address : u16 ) [2 ]u8 {
171
+ const msb = @truncate (u8 , address >> 8 );
172
+ const lsb = @truncate (u8 , address );
173
+ return [2 ]u8 { msb , lsb };
174
+ }
175
+
176
+ const Record = struct {
177
+ const Type = enum (u8 ) {
178
+ Data = 0 ,
179
+ EOF = 1 ,
180
+ ExtendedSegmentAddress = 2 ,
181
+ ExtendedLinearAddress = 4 ,
182
+ };
183
+
184
+ address : u16 ,
185
+ payload : union (Type ) {
186
+ Data : []const u8 ,
187
+ EOF : void ,
188
+ ExtendedSegmentAddress : [2 ]u8 ,
189
+ ExtendedLinearAddress : [2 ]u8 ,
190
+ },
191
+
192
+ fn EOF () Record {
193
+ return Record {
194
+ .address = 0 ,
195
+ .payload = .EOF ,
196
+ };
197
+ }
198
+
199
+ fn Data (address : u32 , data : []const u8 ) Record {
200
+ return Record {
201
+ .address = @intCast (u16 , address % 0x10000 ),
202
+ .payload = .{ .Data = data },
203
+ };
204
+ }
205
+
206
+ fn Address (address : u32 ) Record {
207
+ std .debug .assert (address > 0xFFFF );
208
+ const segment = @intCast (u16 , address / 0x10000 );
209
+ if (address > 0xFFFFF ) {
210
+ return Record {
211
+ .address = 0 ,
212
+ .payload = .{ .ExtendedLinearAddress = addressParts (segment ) },
213
+ };
214
+ } else {
215
+ return Record {
216
+ .address = 0 ,
217
+ .payload = .{ .ExtendedSegmentAddress = addressParts (segment << 12 ) },
218
+ };
219
+ }
220
+ }
221
+
222
+ fn getPayloadBytes (self : Record ) []const u8 {
223
+ return switch (self .payload ) {
224
+ .Data = > | d | d ,
225
+ .EOF = > @as ([]const u8 , &.{}),
226
+ .ExtendedSegmentAddress , .ExtendedLinearAddress = > | * seg | seg ,
227
+ };
228
+ }
229
+
230
+ fn checksum (self : Record ) u8 {
231
+ const payload_bytes = self .getPayloadBytes ();
232
+
233
+ var sum : u8 = @intCast (u8 , payload_bytes .len );
234
+ const parts = addressParts (self .address );
235
+ sum +%= parts [0 ];
236
+ sum +%= parts [1 ];
237
+ sum +%= @enumToInt (self .payload );
238
+ for (payload_bytes ) | byte | {
239
+ sum +%= byte ;
240
+ }
241
+ return (sum ^ 0xFF ) +% 1 ;
242
+ }
243
+
244
+ fn write (self : Record , file : File ) File.WriteError ! void {
245
+ // colon, (length, address, type, payload, checksum) as hex, newline
246
+ const BUFSIZE = 1 + (1 + 2 + 1 + MAX_PAYLOAD_LEN + 1 ) * 2 + nl .len ;
247
+ var outbuf : [BUFSIZE ]u8 = undefined ;
248
+ const payload_bytes = self .getPayloadBytes ();
249
+ std .debug .assert (payload_bytes .len <= MAX_PAYLOAD_LEN );
250
+
251
+ const line = try std .fmt .bufPrint (& outbuf , ":{0X:0>2}{1X:0>4}{2X:0>2}{3s}{4X:0>2}" ++ nl , .{
252
+ @intCast (u8 , payload_bytes .len ),
253
+ self .address ,
254
+ @enumToInt (self .payload ),
255
+ std .fmt .fmtSliceHexUpper (payload_bytes ),
256
+ self .checksum (),
257
+ });
258
+ try file .writeAll (line );
259
+ }
260
+ };
261
+
262
+ pub fn writeSegment (self : * HexWriter , segment : * const BinaryElfSegment , elf_file : File ) ! void {
263
+ var buf : [MAX_PAYLOAD_LEN ]u8 = undefined ;
264
+ var bytes_read : usize = 0 ;
265
+ while (bytes_read < segment .fileSize ) {
266
+ const row_address = @intCast (u32 , segment .physicalAddress + bytes_read );
267
+
268
+ const remaining = segment .fileSize - bytes_read ;
269
+ const to_read = @minimum (remaining , MAX_PAYLOAD_LEN );
270
+ const did_read = try elf_file .preadAll (buf [0.. to_read ], segment .elfOffset + bytes_read );
271
+ if (did_read < to_read ) return error .UnexpectedEOF ;
272
+
273
+ try self .writeDataRow (row_address , buf [0.. did_read ]);
274
+
275
+ bytes_read += did_read ;
276
+ }
277
+ }
278
+
279
+ fn writeDataRow (self : * HexWriter , address : u32 , data : []const u8 ) File.WriteError ! void {
280
+ const record = Record .Data (address , data );
281
+ if (address > 0xFFFF and (self .prev_addr == null or record .address != self .prev_addr .? )) {
282
+ try Record .Address (address ).write (self .out_file );
283
+ }
284
+ try record .write (self .out_file );
285
+ self .prev_addr = @intCast (u32 , record .address + data .len );
286
+ }
287
+
288
+ fn writeEOF (self : HexWriter ) File.WriteError ! void {
289
+ try Record .EOF ().write (self .out_file );
290
+ }
291
+ };
292
+
293
+ fn containsValidAddressRange (segments : []* BinaryElfSegment ) bool {
294
+ const max_address = std .math .maxInt (u32 );
295
+ for (segments ) | segment | {
296
+ if (segment .fileSize > max_address or
297
+ segment .physicalAddress > max_address - segment .fileSize ) return false ;
298
+ }
299
+ return true ;
300
+ }
301
+
302
+ fn emitRaw (allocator : * Allocator , elf_path : []const u8 , raw_path : []const u8 , format : RawFormat ) ! void {
163
303
var elf_file = try fs .cwd ().openFile (elf_path , .{});
164
304
defer elf_file .close ();
165
305
@@ -169,22 +309,53 @@ fn emitRaw(allocator: *Allocator, elf_path: []const u8, raw_path: []const u8) !v
169
309
var binary_elf_output = try BinaryElfOutput .parse (allocator , elf_file );
170
310
defer binary_elf_output .deinit ();
171
311
172
- for (binary_elf_output .sections .items ) | section | {
173
- try writeBinaryElfSection (elf_file , out_file , section );
312
+ switch (format ) {
313
+ .bin = > {
314
+ for (binary_elf_output .sections .items ) | section | {
315
+ try writeBinaryElfSection (elf_file , out_file , section );
316
+ }
317
+ },
318
+ .hex = > {
319
+ if (binary_elf_output .segments .items .len == 0 ) return ;
320
+ if (! containsValidAddressRange (binary_elf_output .segments .items )) {
321
+ return error .InvalidHexfileAddressRange ;
322
+ }
323
+
324
+ var hex_writer = HexWriter { .out_file = out_file };
325
+ for (binary_elf_output .sections .items ) | section | {
326
+ if (section .segment ) | segment | {
327
+ try hex_writer .writeSegment (segment , elf_file );
328
+ }
329
+ }
330
+ try hex_writer .writeEOF ();
331
+ },
174
332
}
175
333
}
176
334
177
335
const InstallRawStep = @This ();
178
336
179
337
pub const base_id = .install_raw ;
180
338
339
+ pub const RawFormat = enum {
340
+ bin ,
341
+ hex ,
342
+ };
343
+
181
344
step : Step ,
182
345
builder : * Builder ,
183
346
artifact : * LibExeObjStep ,
184
347
dest_dir : InstallDir ,
185
348
dest_filename : []const u8 ,
349
+ format : RawFormat ,
350
+
351
+ fn detectFormat (filename : []const u8 ) RawFormat {
352
+ if (std .mem .endsWith (u8 , filename , ".hex" ) or std .mem .endsWith (u8 , filename , ".ihex" )) {
353
+ return .hex ;
354
+ }
355
+ return .bin ;
356
+ }
186
357
187
- pub fn create (builder : * Builder , artifact : * LibExeObjStep , dest_filename : []const u8 ) * InstallRawStep {
358
+ pub fn create (builder : * Builder , artifact : * LibExeObjStep , dest_filename : []const u8 , format : ? RawFormat ) * InstallRawStep {
188
359
const self = builder .allocator .create (InstallRawStep ) catch unreachable ;
189
360
self .* = InstallRawStep {
190
361
.step = Step .init (.install_raw , builder .fmt ("install raw binary {s}" , .{artifact .step .name }), builder .allocator , make ),
@@ -197,6 +368,7 @@ pub fn create(builder: *Builder, artifact: *LibExeObjStep, dest_filename: []cons
197
368
.lib = > unreachable ,
198
369
},
199
370
.dest_filename = dest_filename ,
371
+ .format = if (format ) | f | f else detectFormat (dest_filename ),
200
372
};
201
373
self .step .dependOn (& artifact .step );
202
374
@@ -217,9 +389,48 @@ fn make(step: *Step) !void {
217
389
const full_dest_path = builder .getInstallPath (self .dest_dir , self .dest_filename );
218
390
219
391
fs .cwd ().makePath (builder .getInstallPath (self .dest_dir , "" )) catch unreachable ;
220
- try emitRaw (builder .allocator , full_src_path , full_dest_path );
392
+ try emitRaw (builder .allocator , full_src_path , full_dest_path , self . format );
221
393
}
222
394
223
395
test {
224
396
std .testing .refAllDecls (InstallRawStep );
225
397
}
398
+
399
+ test "Detect format from filename" {
400
+ try std .testing .expectEqual (RawFormat .hex , detectFormat ("foo.hex" ));
401
+ try std .testing .expectEqual (RawFormat .hex , detectFormat ("foo.ihex" ));
402
+ try std .testing .expectEqual (RawFormat .bin , detectFormat ("foo.bin" ));
403
+ try std .testing .expectEqual (RawFormat .bin , detectFormat ("foo.bar" ));
404
+ try std .testing .expectEqual (RawFormat .bin , detectFormat ("a" ));
405
+ }
406
+
407
+ test "containsValidAddressRange" {
408
+ var segment = BinaryElfSegment {
409
+ .physicalAddress = 0 ,
410
+ .virtualAddress = 0 ,
411
+ .elfOffset = 0 ,
412
+ .binaryOffset = 0 ,
413
+ .fileSize = 0 ,
414
+ .firstSection = null ,
415
+ };
416
+ var buf : [1 ]* BinaryElfSegment = .{& segment };
417
+
418
+ // segment too big
419
+ segment .fileSize = std .math .maxInt (u32 ) + 1 ;
420
+ try std .testing .expect (! containsValidAddressRange (& buf ));
421
+
422
+ // start address too big
423
+ segment .physicalAddress = std .math .maxInt (u32 ) + 1 ;
424
+ segment .fileSize = 2 ;
425
+ try std .testing .expect (! containsValidAddressRange (& buf ));
426
+
427
+ // max address too big
428
+ segment .physicalAddress = std .math .maxInt (u32 ) - 1 ;
429
+ segment .fileSize = 2 ;
430
+ try std .testing .expect (! containsValidAddressRange (& buf ));
431
+
432
+ // is ok
433
+ segment .physicalAddress = std .math .maxInt (u32 ) - 1 ;
434
+ segment .fileSize = 1 ;
435
+ try std .testing .expect (containsValidAddressRange (& buf ));
436
+ }
0 commit comments