diff options
Diffstat (limited to 'arch/i386/boot/compressed')
-rw-r--r-- | arch/i386/boot/compressed/Makefile | 25 | ||||
-rw-r--r-- | arch/i386/boot/compressed/head.S | 128 | ||||
-rw-r--r-- | arch/i386/boot/compressed/misc.c | 382 | ||||
-rw-r--r-- | arch/i386/boot/compressed/vmlinux.scr | 9 |
4 files changed, 544 insertions, 0 deletions
diff --git a/arch/i386/boot/compressed/Makefile b/arch/i386/boot/compressed/Makefile new file mode 100644 index 000000000000..258ea95224f6 --- /dev/null +++ b/arch/i386/boot/compressed/Makefile | |||
@@ -0,0 +1,25 @@ | |||
1 | # | ||
2 | # linux/arch/i386/boot/compressed/Makefile | ||
3 | # | ||
4 | # create a compressed vmlinux image from the original vmlinux | ||
5 | # | ||
6 | |||
7 | targets := vmlinux vmlinux.bin vmlinux.bin.gz head.o misc.o piggy.o | ||
8 | EXTRA_AFLAGS := -traditional | ||
9 | |||
10 | LDFLAGS_vmlinux := -Ttext $(IMAGE_OFFSET) -e startup_32 | ||
11 | |||
12 | $(obj)/vmlinux: $(obj)/head.o $(obj)/misc.o $(obj)/piggy.o FORCE | ||
13 | $(call if_changed,ld) | ||
14 | @: | ||
15 | |||
16 | $(obj)/vmlinux.bin: vmlinux FORCE | ||
17 | $(call if_changed,objcopy) | ||
18 | |||
19 | $(obj)/vmlinux.bin.gz: $(obj)/vmlinux.bin FORCE | ||
20 | $(call if_changed,gzip) | ||
21 | |||
22 | LDFLAGS_piggy.o := -r --format binary --oformat elf32-i386 -T | ||
23 | |||
24 | $(obj)/piggy.o: $(obj)/vmlinux.scr $(obj)/vmlinux.bin.gz FORCE | ||
25 | $(call if_changed,ld) | ||
diff --git a/arch/i386/boot/compressed/head.S b/arch/i386/boot/compressed/head.S new file mode 100644 index 000000000000..c5e80b69e7d4 --- /dev/null +++ b/arch/i386/boot/compressed/head.S | |||
@@ -0,0 +1,128 @@ | |||
1 | /* | ||
2 | * linux/boot/head.S | ||
3 | * | ||
4 | * Copyright (C) 1991, 1992, 1993 Linus Torvalds | ||
5 | */ | ||
6 | |||
7 | /* | ||
8 | * head.S contains the 32-bit startup code. | ||
9 | * | ||
10 | * NOTE!!! Startup happens at absolute address 0x00001000, which is also where | ||
11 | * the page directory will exist. The startup code will be overwritten by | ||
12 | * the page directory. [According to comments etc elsewhere on a compressed | ||
13 | * kernel it will end up at 0x1000 + 1Mb I hope so as I assume this. - AC] | ||
14 | * | ||
15 | * Page 0 is deliberately kept safe, since System Management Mode code in | ||
16 | * laptops may need to access the BIOS data stored there. This is also | ||
17 | * useful for future device drivers that either access the BIOS via VM86 | ||
18 | * mode. | ||
19 | */ | ||
20 | |||
21 | /* | ||
22 | * High loaded stuff by Hans Lermen & Werner Almesberger, Feb. 1996 | ||
23 | */ | ||
24 | .text | ||
25 | |||
26 | #include <linux/linkage.h> | ||
27 | #include <asm/segment.h> | ||
28 | |||
29 | .globl startup_32 | ||
30 | |||
31 | startup_32: | ||
32 | cld | ||
33 | cli | ||
34 | movl $(__BOOT_DS),%eax | ||
35 | movl %eax,%ds | ||
36 | movl %eax,%es | ||
37 | movl %eax,%fs | ||
38 | movl %eax,%gs | ||
39 | |||
40 | lss stack_start,%esp | ||
41 | xorl %eax,%eax | ||
42 | 1: incl %eax # check that A20 really IS enabled | ||
43 | movl %eax,0x000000 # loop forever if it isn't | ||
44 | cmpl %eax,0x100000 | ||
45 | je 1b | ||
46 | |||
47 | /* | ||
48 | * Initialize eflags. Some BIOS's leave bits like NT set. This would | ||
49 | * confuse the debugger if this code is traced. | ||
50 | * XXX - best to initialize before switching to protected mode. | ||
51 | */ | ||
52 | pushl $0 | ||
53 | popfl | ||
54 | /* | ||
55 | * Clear BSS | ||
56 | */ | ||
57 | xorl %eax,%eax | ||
58 | movl $_edata,%edi | ||
59 | movl $_end,%ecx | ||
60 | subl %edi,%ecx | ||
61 | cld | ||
62 | rep | ||
63 | stosb | ||
64 | /* | ||
65 | * Do the decompression, and jump to the new kernel.. | ||
66 | */ | ||
67 | subl $16,%esp # place for structure on the stack | ||
68 | movl %esp,%eax | ||
69 | pushl %esi # real mode pointer as second arg | ||
70 | pushl %eax # address of structure as first arg | ||
71 | call decompress_kernel | ||
72 | orl %eax,%eax | ||
73 | jnz 3f | ||
74 | popl %esi # discard address | ||
75 | popl %esi # real mode pointer | ||
76 | xorl %ebx,%ebx | ||
77 | ljmp $(__BOOT_CS), $0x100000 | ||
78 | |||
79 | /* | ||
80 | * We come here, if we were loaded high. | ||
81 | * We need to move the move-in-place routine down to 0x1000 | ||
82 | * and then start it with the buffer addresses in registers, | ||
83 | * which we got from the stack. | ||
84 | */ | ||
85 | 3: | ||
86 | movl $move_routine_start,%esi | ||
87 | movl $0x1000,%edi | ||
88 | movl $move_routine_end,%ecx | ||
89 | subl %esi,%ecx | ||
90 | addl $3,%ecx | ||
91 | shrl $2,%ecx | ||
92 | cld | ||
93 | rep | ||
94 | movsl | ||
95 | |||
96 | popl %esi # discard the address | ||
97 | popl %ebx # real mode pointer | ||
98 | popl %esi # low_buffer_start | ||
99 | popl %ecx # lcount | ||
100 | popl %edx # high_buffer_start | ||
101 | popl %eax # hcount | ||
102 | movl $0x100000,%edi | ||
103 | cli # make sure we don't get interrupted | ||
104 | ljmp $(__BOOT_CS), $0x1000 # and jump to the move routine | ||
105 | |||
106 | /* | ||
107 | * Routine (template) for moving the decompressed kernel in place, | ||
108 | * if we were high loaded. This _must_ PIC-code ! | ||
109 | */ | ||
110 | move_routine_start: | ||
111 | movl %ecx,%ebp | ||
112 | shrl $2,%ecx | ||
113 | rep | ||
114 | movsl | ||
115 | movl %ebp,%ecx | ||
116 | andl $3,%ecx | ||
117 | rep | ||
118 | movsb | ||
119 | movl %edx,%esi | ||
120 | movl %eax,%ecx # NOTE: rep movsb won't move if %ecx == 0 | ||
121 | addl $3,%ecx | ||
122 | shrl $2,%ecx | ||
123 | rep | ||
124 | movsl | ||
125 | movl %ebx,%esi # Restore setup pointer | ||
126 | xorl %ebx,%ebx | ||
127 | ljmp $(__BOOT_CS), $0x100000 | ||
128 | move_routine_end: | ||
diff --git a/arch/i386/boot/compressed/misc.c b/arch/i386/boot/compressed/misc.c new file mode 100644 index 000000000000..fa67045234a3 --- /dev/null +++ b/arch/i386/boot/compressed/misc.c | |||
@@ -0,0 +1,382 @@ | |||
1 | /* | ||
2 | * misc.c | ||
3 | * | ||
4 | * This is a collection of several routines from gzip-1.0.3 | ||
5 | * adapted for Linux. | ||
6 | * | ||
7 | * malloc by Hannu Savolainen 1993 and Matthias Urlichs 1994 | ||
8 | * puts by Nick Holloway 1993, better puts by Martin Mares 1995 | ||
9 | * High loaded stuff by Hans Lermen & Werner Almesberger, Feb. 1996 | ||
10 | */ | ||
11 | |||
12 | #include <linux/linkage.h> | ||
13 | #include <linux/vmalloc.h> | ||
14 | #include <linux/tty.h> | ||
15 | #include <video/edid.h> | ||
16 | #include <asm/io.h> | ||
17 | |||
18 | /* | ||
19 | * gzip declarations | ||
20 | */ | ||
21 | |||
22 | #define OF(args) args | ||
23 | #define STATIC static | ||
24 | |||
25 | #undef memset | ||
26 | #undef memcpy | ||
27 | |||
28 | /* | ||
29 | * Why do we do this? Don't ask me.. | ||
30 | * | ||
31 | * Incomprehensible are the ways of bootloaders. | ||
32 | */ | ||
33 | static void* memset(void *, int, size_t); | ||
34 | static void* memcpy(void *, __const void *, size_t); | ||
35 | #define memzero(s, n) memset ((s), 0, (n)) | ||
36 | |||
37 | typedef unsigned char uch; | ||
38 | typedef unsigned short ush; | ||
39 | typedef unsigned long ulg; | ||
40 | |||
41 | #define WSIZE 0x8000 /* Window size must be at least 32k, */ | ||
42 | /* and a power of two */ | ||
43 | |||
44 | static uch *inbuf; /* input buffer */ | ||
45 | static uch window[WSIZE]; /* Sliding window buffer */ | ||
46 | |||
47 | static unsigned insize = 0; /* valid bytes in inbuf */ | ||
48 | static unsigned inptr = 0; /* index of next byte to be processed in inbuf */ | ||
49 | static unsigned outcnt = 0; /* bytes in output buffer */ | ||
50 | |||
51 | /* gzip flag byte */ | ||
52 | #define ASCII_FLAG 0x01 /* bit 0 set: file probably ASCII text */ | ||
53 | #define CONTINUATION 0x02 /* bit 1 set: continuation of multi-part gzip file */ | ||
54 | #define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ | ||
55 | #define ORIG_NAME 0x08 /* bit 3 set: original file name present */ | ||
56 | #define COMMENT 0x10 /* bit 4 set: file comment present */ | ||
57 | #define ENCRYPTED 0x20 /* bit 5 set: file is encrypted */ | ||
58 | #define RESERVED 0xC0 /* bit 6,7: reserved */ | ||
59 | |||
60 | #define get_byte() (inptr < insize ? inbuf[inptr++] : fill_inbuf()) | ||
61 | |||
62 | /* Diagnostic functions */ | ||
63 | #ifdef DEBUG | ||
64 | # define Assert(cond,msg) {if(!(cond)) error(msg);} | ||
65 | # define Trace(x) fprintf x | ||
66 | # define Tracev(x) {if (verbose) fprintf x ;} | ||
67 | # define Tracevv(x) {if (verbose>1) fprintf x ;} | ||
68 | # define Tracec(c,x) {if (verbose && (c)) fprintf x ;} | ||
69 | # define Tracecv(c,x) {if (verbose>1 && (c)) fprintf x ;} | ||
70 | #else | ||
71 | # define Assert(cond,msg) | ||
72 | # define Trace(x) | ||
73 | # define Tracev(x) | ||
74 | # define Tracevv(x) | ||
75 | # define Tracec(c,x) | ||
76 | # define Tracecv(c,x) | ||
77 | #endif | ||
78 | |||
79 | static int fill_inbuf(void); | ||
80 | static void flush_window(void); | ||
81 | static void error(char *m); | ||
82 | static void gzip_mark(void **); | ||
83 | static void gzip_release(void **); | ||
84 | |||
85 | /* | ||
86 | * This is set up by the setup-routine at boot-time | ||
87 | */ | ||
88 | static unsigned char *real_mode; /* Pointer to real-mode data */ | ||
89 | |||
90 | #define RM_EXT_MEM_K (*(unsigned short *)(real_mode + 0x2)) | ||
91 | #ifndef STANDARD_MEMORY_BIOS_CALL | ||
92 | #define RM_ALT_MEM_K (*(unsigned long *)(real_mode + 0x1e0)) | ||
93 | #endif | ||
94 | #define RM_SCREEN_INFO (*(struct screen_info *)(real_mode+0)) | ||
95 | |||
96 | extern char input_data[]; | ||
97 | extern int input_len; | ||
98 | |||
99 | static long bytes_out = 0; | ||
100 | static uch *output_data; | ||
101 | static unsigned long output_ptr = 0; | ||
102 | |||
103 | static void *malloc(int size); | ||
104 | static void free(void *where); | ||
105 | |||
106 | static void putstr(const char *); | ||
107 | |||
108 | extern int end; | ||
109 | static long free_mem_ptr = (long)&end; | ||
110 | static long free_mem_end_ptr; | ||
111 | |||
112 | #define INPLACE_MOVE_ROUTINE 0x1000 | ||
113 | #define LOW_BUFFER_START 0x2000 | ||
114 | #define LOW_BUFFER_MAX 0x90000 | ||
115 | #define HEAP_SIZE 0x3000 | ||
116 | static unsigned int low_buffer_end, low_buffer_size; | ||
117 | static int high_loaded =0; | ||
118 | static uch *high_buffer_start /* = (uch *)(((ulg)&end) + HEAP_SIZE)*/; | ||
119 | |||
120 | static char *vidmem = (char *)0xb8000; | ||
121 | static int vidport; | ||
122 | static int lines, cols; | ||
123 | |||
124 | #ifdef CONFIG_X86_NUMAQ | ||
125 | static void * xquad_portio = NULL; | ||
126 | #endif | ||
127 | |||
128 | #include "../../../../lib/inflate.c" | ||
129 | |||
130 | static void *malloc(int size) | ||
131 | { | ||
132 | void *p; | ||
133 | |||
134 | if (size <0) error("Malloc error"); | ||
135 | if (free_mem_ptr <= 0) error("Memory error"); | ||
136 | |||
137 | free_mem_ptr = (free_mem_ptr + 3) & ~3; /* Align */ | ||
138 | |||
139 | p = (void *)free_mem_ptr; | ||
140 | free_mem_ptr += size; | ||
141 | |||
142 | if (free_mem_ptr >= free_mem_end_ptr) | ||
143 | error("Out of memory"); | ||
144 | |||
145 | return p; | ||
146 | } | ||
147 | |||
148 | static void free(void *where) | ||
149 | { /* Don't care */ | ||
150 | } | ||
151 | |||
152 | static void gzip_mark(void **ptr) | ||
153 | { | ||
154 | *ptr = (void *) free_mem_ptr; | ||
155 | } | ||
156 | |||
157 | static void gzip_release(void **ptr) | ||
158 | { | ||
159 | free_mem_ptr = (long) *ptr; | ||
160 | } | ||
161 | |||
162 | static void scroll(void) | ||
163 | { | ||
164 | int i; | ||
165 | |||
166 | memcpy ( vidmem, vidmem + cols * 2, ( lines - 1 ) * cols * 2 ); | ||
167 | for ( i = ( lines - 1 ) * cols * 2; i < lines * cols * 2; i += 2 ) | ||
168 | vidmem[i] = ' '; | ||
169 | } | ||
170 | |||
171 | static void putstr(const char *s) | ||
172 | { | ||
173 | int x,y,pos; | ||
174 | char c; | ||
175 | |||
176 | x = RM_SCREEN_INFO.orig_x; | ||
177 | y = RM_SCREEN_INFO.orig_y; | ||
178 | |||
179 | while ( ( c = *s++ ) != '\0' ) { | ||
180 | if ( c == '\n' ) { | ||
181 | x = 0; | ||
182 | if ( ++y >= lines ) { | ||
183 | scroll(); | ||
184 | y--; | ||
185 | } | ||
186 | } else { | ||
187 | vidmem [ ( x + cols * y ) * 2 ] = c; | ||
188 | if ( ++x >= cols ) { | ||
189 | x = 0; | ||
190 | if ( ++y >= lines ) { | ||
191 | scroll(); | ||
192 | y--; | ||
193 | } | ||
194 | } | ||
195 | } | ||
196 | } | ||
197 | |||
198 | RM_SCREEN_INFO.orig_x = x; | ||
199 | RM_SCREEN_INFO.orig_y = y; | ||
200 | |||
201 | pos = (x + cols * y) * 2; /* Update cursor position */ | ||
202 | outb_p(14, vidport); | ||
203 | outb_p(0xff & (pos >> 9), vidport+1); | ||
204 | outb_p(15, vidport); | ||
205 | outb_p(0xff & (pos >> 1), vidport+1); | ||
206 | } | ||
207 | |||
208 | static void* memset(void* s, int c, size_t n) | ||
209 | { | ||
210 | int i; | ||
211 | char *ss = (char*)s; | ||
212 | |||
213 | for (i=0;i<n;i++) ss[i] = c; | ||
214 | return s; | ||
215 | } | ||
216 | |||
217 | static void* memcpy(void* __dest, __const void* __src, | ||
218 | size_t __n) | ||
219 | { | ||
220 | int i; | ||
221 | char *d = (char *)__dest, *s = (char *)__src; | ||
222 | |||
223 | for (i=0;i<__n;i++) d[i] = s[i]; | ||
224 | return __dest; | ||
225 | } | ||
226 | |||
227 | /* =========================================================================== | ||
228 | * Fill the input buffer. This is called only when the buffer is empty | ||
229 | * and at least one byte is really needed. | ||
230 | */ | ||
231 | static int fill_inbuf(void) | ||
232 | { | ||
233 | if (insize != 0) { | ||
234 | error("ran out of input data"); | ||
235 | } | ||
236 | |||
237 | inbuf = input_data; | ||
238 | insize = input_len; | ||
239 | inptr = 1; | ||
240 | return inbuf[0]; | ||
241 | } | ||
242 | |||
243 | /* =========================================================================== | ||
244 | * Write the output window window[0..outcnt-1] and update crc and bytes_out. | ||
245 | * (Used for the decompressed data only.) | ||
246 | */ | ||
247 | static void flush_window_low(void) | ||
248 | { | ||
249 | ulg c = crc; /* temporary variable */ | ||
250 | unsigned n; | ||
251 | uch *in, *out, ch; | ||
252 | |||
253 | in = window; | ||
254 | out = &output_data[output_ptr]; | ||
255 | for (n = 0; n < outcnt; n++) { | ||
256 | ch = *out++ = *in++; | ||
257 | c = crc_32_tab[((int)c ^ ch) & 0xff] ^ (c >> 8); | ||
258 | } | ||
259 | crc = c; | ||
260 | bytes_out += (ulg)outcnt; | ||
261 | output_ptr += (ulg)outcnt; | ||
262 | outcnt = 0; | ||
263 | } | ||
264 | |||
265 | static void flush_window_high(void) | ||
266 | { | ||
267 | ulg c = crc; /* temporary variable */ | ||
268 | unsigned n; | ||
269 | uch *in, ch; | ||
270 | in = window; | ||
271 | for (n = 0; n < outcnt; n++) { | ||
272 | ch = *output_data++ = *in++; | ||
273 | if ((ulg)output_data == low_buffer_end) output_data=high_buffer_start; | ||
274 | c = crc_32_tab[((int)c ^ ch) & 0xff] ^ (c >> 8); | ||
275 | } | ||
276 | crc = c; | ||
277 | bytes_out += (ulg)outcnt; | ||
278 | outcnt = 0; | ||
279 | } | ||
280 | |||
281 | static void flush_window(void) | ||
282 | { | ||
283 | if (high_loaded) flush_window_high(); | ||
284 | else flush_window_low(); | ||
285 | } | ||
286 | |||
287 | static void error(char *x) | ||
288 | { | ||
289 | putstr("\n\n"); | ||
290 | putstr(x); | ||
291 | putstr("\n\n -- System halted"); | ||
292 | |||
293 | while(1); /* Halt */ | ||
294 | } | ||
295 | |||
296 | #define STACK_SIZE (4096) | ||
297 | |||
298 | long user_stack [STACK_SIZE]; | ||
299 | |||
300 | struct { | ||
301 | long * a; | ||
302 | short b; | ||
303 | } stack_start = { & user_stack [STACK_SIZE] , __BOOT_DS }; | ||
304 | |||
305 | static void setup_normal_output_buffer(void) | ||
306 | { | ||
307 | #ifdef STANDARD_MEMORY_BIOS_CALL | ||
308 | if (RM_EXT_MEM_K < 1024) error("Less than 2MB of memory"); | ||
309 | #else | ||
310 | if ((RM_ALT_MEM_K > RM_EXT_MEM_K ? RM_ALT_MEM_K : RM_EXT_MEM_K) < 1024) error("Less than 2MB of memory"); | ||
311 | #endif | ||
312 | output_data = (char *)0x100000; /* Points to 1M */ | ||
313 | free_mem_end_ptr = (long)real_mode; | ||
314 | } | ||
315 | |||
316 | struct moveparams { | ||
317 | uch *low_buffer_start; int lcount; | ||
318 | uch *high_buffer_start; int hcount; | ||
319 | }; | ||
320 | |||
321 | static void setup_output_buffer_if_we_run_high(struct moveparams *mv) | ||
322 | { | ||
323 | high_buffer_start = (uch *)(((ulg)&end) + HEAP_SIZE); | ||
324 | #ifdef STANDARD_MEMORY_BIOS_CALL | ||
325 | if (RM_EXT_MEM_K < (3*1024)) error("Less than 4MB of memory"); | ||
326 | #else | ||
327 | if ((RM_ALT_MEM_K > RM_EXT_MEM_K ? RM_ALT_MEM_K : RM_EXT_MEM_K) < | ||
328 | (3*1024)) | ||
329 | error("Less than 4MB of memory"); | ||
330 | #endif | ||
331 | mv->low_buffer_start = output_data = (char *)LOW_BUFFER_START; | ||
332 | low_buffer_end = ((unsigned int)real_mode > LOW_BUFFER_MAX | ||
333 | ? LOW_BUFFER_MAX : (unsigned int)real_mode) & ~0xfff; | ||
334 | low_buffer_size = low_buffer_end - LOW_BUFFER_START; | ||
335 | high_loaded = 1; | ||
336 | free_mem_end_ptr = (long)high_buffer_start; | ||
337 | if ( (0x100000 + low_buffer_size) > ((ulg)high_buffer_start)) { | ||
338 | high_buffer_start = (uch *)(0x100000 + low_buffer_size); | ||
339 | mv->hcount = 0; /* say: we need not to move high_buffer */ | ||
340 | } | ||
341 | else mv->hcount = -1; | ||
342 | mv->high_buffer_start = high_buffer_start; | ||
343 | } | ||
344 | |||
345 | static void close_output_buffer_if_we_run_high(struct moveparams *mv) | ||
346 | { | ||
347 | if (bytes_out > low_buffer_size) { | ||
348 | mv->lcount = low_buffer_size; | ||
349 | if (mv->hcount) | ||
350 | mv->hcount = bytes_out - low_buffer_size; | ||
351 | } else { | ||
352 | mv->lcount = bytes_out; | ||
353 | mv->hcount = 0; | ||
354 | } | ||
355 | } | ||
356 | |||
357 | |||
358 | asmlinkage int decompress_kernel(struct moveparams *mv, void *rmode) | ||
359 | { | ||
360 | real_mode = rmode; | ||
361 | |||
362 | if (RM_SCREEN_INFO.orig_video_mode == 7) { | ||
363 | vidmem = (char *) 0xb0000; | ||
364 | vidport = 0x3b4; | ||
365 | } else { | ||
366 | vidmem = (char *) 0xb8000; | ||
367 | vidport = 0x3d4; | ||
368 | } | ||
369 | |||
370 | lines = RM_SCREEN_INFO.orig_video_lines; | ||
371 | cols = RM_SCREEN_INFO.orig_video_cols; | ||
372 | |||
373 | if (free_mem_ptr < 0x100000) setup_normal_output_buffer(); | ||
374 | else setup_output_buffer_if_we_run_high(mv); | ||
375 | |||
376 | makecrc(); | ||
377 | putstr("Uncompressing Linux... "); | ||
378 | gunzip(); | ||
379 | putstr("Ok, booting the kernel.\n"); | ||
380 | if (high_loaded) close_output_buffer_if_we_run_high(mv); | ||
381 | return high_loaded; | ||
382 | } | ||
diff --git a/arch/i386/boot/compressed/vmlinux.scr b/arch/i386/boot/compressed/vmlinux.scr new file mode 100644 index 000000000000..1ed9d791f863 --- /dev/null +++ b/arch/i386/boot/compressed/vmlinux.scr | |||
@@ -0,0 +1,9 @@ | |||
1 | SECTIONS | ||
2 | { | ||
3 | .data : { | ||
4 | input_len = .; | ||
5 | LONG(input_data_end - input_data) input_data = .; | ||
6 | *(.data) | ||
7 | input_data_end = .; | ||
8 | } | ||
9 | } | ||