1
+ const std = @import ("std" );
2
+
3
+ const Allocator = std .mem .Allocator ;
4
+ const ArenaAllocator = std .heap .ArenaAllocator ;
5
+ const ArrayList = std .ArrayList ;
6
+ const Builder = std .build .Builder ;
7
+ const File = std .fs .File ;
8
+ const InstallDir = std .build .InstallDir ;
9
+ const LibExeObjStep = std .build .LibExeObjStep ;
10
+ const Step = std .build .Step ;
11
+ const elf = std .elf ;
12
+ const fs = std .fs ;
13
+ const io = std .io ;
14
+ const sort = std .sort ;
15
+ const warn = std .debug .warn ;
16
+
17
+ const BinOutStream = io .OutStream (anyerror );
18
+ const BinSeekStream = io .SeekableStream (anyerror , anyerror );
19
+ const ElfSeekStream = io .SeekableStream (anyerror , anyerror );
20
+ const ElfInStream = io .InStream (anyerror );
21
+
22
+ const BinaryElfSection = struct {
23
+ elfOffset : u64 ,
24
+ binaryOffset : u64 ,
25
+ fileSize : usize ,
26
+ segment : ? * BinaryElfSegment ,
27
+ };
28
+
29
+ const BinaryElfSegment = struct {
30
+ physicalAddress : u64 ,
31
+ virtualAddress : u64 ,
32
+ elfOffset : u64 ,
33
+ binaryOffset : u64 ,
34
+ fileSize : usize ,
35
+ firstSection : ? * BinaryElfSection ,
36
+ };
37
+
38
+ const BinaryElfOutput = struct {
39
+ segments : ArrayList (* BinaryElfSegment ),
40
+ sections : ArrayList (* BinaryElfSection ),
41
+
42
+ const Self = @This ();
43
+
44
+ pub fn init (allocator : * Allocator ) Self {
45
+ return Self {
46
+ .segments = ArrayList (* BinaryElfSegment ).init (allocator ),
47
+ .sections = ArrayList (* BinaryElfSection ).init (allocator ),
48
+ };
49
+ }
50
+
51
+ pub fn deinit (self : * Self ) void {
52
+ self .sections .deinit ();
53
+ self .segments .deinit ();
54
+ }
55
+
56
+ pub fn parseElf (self : * Self , elfFile : elf.Elf ) ! void {
57
+ const allocator = self .segments .allocator ;
58
+
59
+ for (elfFile .section_headers ) | section , i | {
60
+ if (sectionValidForOutput (section )) {
61
+ const newSection = try allocator .create (BinaryElfSection );
62
+
63
+ newSection .binaryOffset = 0 ;
64
+ newSection .elfOffset = section .sh_offset ;
65
+ newSection .fileSize = @intCast (usize , section .sh_size );
66
+ newSection .segment = null ;
67
+
68
+ try self .sections .append (newSection );
69
+ }
70
+ }
71
+
72
+ for (elfFile .program_headers ) | programHeader , i | {
73
+ if (programHeader .p_type == elf .PT_LOAD ) {
74
+ const newSegment = try allocator .create (BinaryElfSegment );
75
+
76
+ newSegment .physicalAddress = if (programHeader .p_paddr != 0 ) programHeader .p_paddr else programHeader .p_vaddr ;
77
+ newSegment .virtualAddress = programHeader .p_vaddr ;
78
+ newSegment .fileSize = @intCast (usize , programHeader .p_filesz );
79
+ newSegment .elfOffset = programHeader .p_offset ;
80
+ newSegment .binaryOffset = 0 ;
81
+ newSegment .firstSection = null ;
82
+
83
+ for (self .sections .toSlice ()) | section | {
84
+ if (sectionWithinSegment (section , programHeader )) {
85
+ if (section .segment ) | sectionSegment | {
86
+ if (sectionSegment .elfOffset > newSegment .elfOffset ) {
87
+ section .segment = newSegment ;
88
+ }
89
+ } else {
90
+ section .segment = newSegment ;
91
+ }
92
+
93
+ if (newSegment .firstSection == null ) {
94
+ newSegment .firstSection = section ;
95
+ }
96
+ }
97
+ }
98
+
99
+ try self .segments .append (newSegment );
100
+ }
101
+ }
102
+
103
+ sort .sort (* BinaryElfSegment , self .segments .toSlice (), segmentSortCompare );
104
+
105
+ if (self .segments .len > 0 ) {
106
+ const firstSegment = self .segments .at (0 );
107
+ if (firstSegment .firstSection ) | firstSection | {
108
+ const diff = firstSection .elfOffset - firstSegment .elfOffset ;
109
+
110
+ firstSegment .elfOffset += diff ;
111
+ firstSegment .fileSize += diff ;
112
+ firstSegment .physicalAddress += diff ;
113
+
114
+ const basePhysicalAddress = firstSegment .physicalAddress ;
115
+
116
+ for (self .segments .toSlice ()) | segment | {
117
+ segment .binaryOffset = segment .physicalAddress - basePhysicalAddress ;
118
+ }
119
+ }
120
+ }
121
+
122
+ for (self .sections .toSlice ()) | section | {
123
+ if (section .segment ) | segment | {
124
+ section .binaryOffset = segment .binaryOffset + (section .elfOffset - segment .elfOffset );
125
+ }
126
+ }
127
+
128
+ sort .sort (* BinaryElfSection , self .sections .toSlice (), sectionSortCompare );
129
+ }
130
+
131
+ fn sectionWithinSegment (section : * BinaryElfSection , segment : elf.ProgramHeader ) bool {
132
+ return segment .p_offset <= section .elfOffset and (segment .p_offset + segment .p_filesz ) >= (section .elfOffset + section .fileSize );
133
+ }
134
+
135
+ fn sectionValidForOutput (section : elf.SectionHeader ) bool {
136
+ return section .sh_size > 0 and section .sh_type != elf .SHT_NOBITS and ((section .sh_flags & elf .SHF_ALLOC ) == elf .SHF_ALLOC );
137
+ }
138
+
139
+ fn segmentSortCompare (left : * BinaryElfSegment , right : * BinaryElfSegment ) bool {
140
+ if (left .physicalAddress < right .physicalAddress ) {
141
+ return true ;
142
+ }
143
+ if (left .physicalAddress > right .physicalAddress ) {
144
+ return false ;
145
+ }
146
+ return false ;
147
+ }
148
+
149
+ fn sectionSortCompare (left : * BinaryElfSection , right : * BinaryElfSection ) bool {
150
+ return left .binaryOffset < right .binaryOffset ;
151
+ }
152
+ };
153
+
154
+ const WriteContext = struct {
155
+ inStream : * ElfInStream ,
156
+ inSeekStream : * ElfSeekStream ,
157
+ outStream : * BinOutStream ,
158
+ outSeekStream : * BinSeekStream ,
159
+ };
160
+
161
+ fn writeBinaryElfSection (allocator : * Allocator , context : WriteContext , section : * BinaryElfSection ) ! void {
162
+ var readBuffer = try allocator .alloc (u8 , section .fileSize );
163
+ defer allocator .free (readBuffer );
164
+
165
+ try context .inSeekStream .seekTo (section .elfOffset );
166
+ _ = try context .inStream .read (readBuffer );
167
+
168
+ try context .outSeekStream .seekTo (section .binaryOffset );
169
+ try context .outStream .write (readBuffer );
170
+ }
171
+
172
+ fn emit_raw (allocator : * Allocator , elf_path : []const u8 , raw_path : []const u8 ) ! void {
173
+ var arenaAlloc = ArenaAllocator .init (allocator );
174
+ errdefer arenaAlloc .deinit ();
175
+ var arena_allocator = & arenaAlloc .allocator ;
176
+
177
+ const currentDir = fs .cwd ();
178
+
179
+ var file = try currentDir .openFile (elf_path , File.OpenFlags {});
180
+ defer file .close ();
181
+
182
+ var fileInStream = file .inStream ();
183
+ var fileSeekStream = file .seekableStream ();
184
+
185
+ var elfFile = try elf .Elf .openStream (allocator , @ptrCast (* ElfSeekStream , & fileSeekStream .stream ), @ptrCast (* ElfInStream , & fileInStream .stream ));
186
+ defer elfFile .close ();
187
+
188
+ var outFile = try currentDir .createFile (raw_path , File.CreateFlags {});
189
+ defer outFile .close ();
190
+
191
+ var outFileOutStream = outFile .outStream ();
192
+ var outFileSeekStream = outFile .seekableStream ();
193
+
194
+ const writeContext = WriteContext {
195
+ .inStream = @ptrCast (* ElfInStream , & fileInStream .stream ),
196
+ .inSeekStream = @ptrCast (* ElfSeekStream , & fileSeekStream .stream ),
197
+ .outStream = @ptrCast (* BinOutStream , & outFileOutStream .stream ),
198
+ .outSeekStream = @ptrCast (* BinSeekStream , & outFileSeekStream .stream ),
199
+ };
200
+
201
+ var binaryElfOutput = BinaryElfOutput .init (arena_allocator );
202
+ defer binaryElfOutput .deinit ();
203
+
204
+ try binaryElfOutput .parseElf (elfFile );
205
+
206
+ for (binaryElfOutput .sections .toSlice ()) | section | {
207
+ try writeBinaryElfSection (allocator , writeContext , section );
208
+ }
209
+ }
210
+
211
+ pub const InstallRawStep = struct {
212
+ step : Step ,
213
+ builder : * Builder ,
214
+ artifact : * LibExeObjStep ,
215
+ dest_dir : InstallDir ,
216
+ dest_filename : [] const u8 ,
217
+
218
+ const Self = @This ();
219
+
220
+ pub fn create (builder : * Builder , artifact : * LibExeObjStep , dest_filename : [] const u8 ) * Self {
221
+ const self = builder .allocator .create (Self ) catch unreachable ;
222
+ self .* = Self {
223
+ .step = Step .init (builder .fmt ("install raw binary {}" , .{artifact .step .name }), builder .allocator , make ),
224
+ .builder = builder ,
225
+ .artifact = artifact ,
226
+ .dest_dir = switch (artifact .kind ) {
227
+ .Obj = > unreachable ,
228
+ .Test = > unreachable ,
229
+ .Exe = > .Bin ,
230
+ .Lib = > unreachable ,
231
+ },
232
+ .dest_filename = dest_filename ,
233
+ };
234
+ self .step .dependOn (& artifact .step );
235
+
236
+ builder .pushInstalledFile (self .dest_dir , dest_filename );
237
+ return self ;
238
+ }
239
+
240
+ fn make (step : * Step ) ! void {
241
+ const self = @fieldParentPtr (Self , "step" , step );
242
+ const builder = self .builder ;
243
+
244
+ if (self .artifact .target .getObjectFormat () != .elf ) {
245
+ warn ("InstallRawStep only works with ELF format.\n " , .{});
246
+ return error .InvalidObjectFormat ;
247
+ }
248
+
249
+ const full_src_path = self .artifact .getOutputPath ();
250
+ const full_dest_path = builder .getInstallPath (self .dest_dir , self .dest_filename );
251
+
252
+ fs .makePath (builder .allocator , builder .getInstallPath (self .dest_dir , "" )) catch unreachable ;
253
+ try emit_raw (builder .allocator , full_src_path , full_dest_path );
254
+ }
255
+ };
0 commit comments