1 /* writex86.c - write binary file for linker */
3 /* Copyright (C) 1994 Bruce Evans */
15 #define btextoffset (text_base_value)
16 #define bdataoffset (data_base_value)
17 #define page_size() ((bin_off_t)4096)
24 # define FILEHEADERLENGTH (headerless?0:A_MINHDR)
26 # define FILEHEADERLENGTH (headerless?0:(cpm86?CPM86_HEADERLEN:A_MINHDR))
28 /* part of header not counted in offsets */
32 #define MODIFY_MASK 0x3F
37 #define CM_ABSOLUTE 0x40
38 #define CM_OFFSET_RELOC 0x80
39 #define CM_SYMBOL_RELOC 0xC0
42 #define CM_BYTE_SIZE 1
43 #define CM_WORD_SIZE 2
44 #define CM_LONG_SIZE 3
50 #define ABS_TEXT_MAX 64
52 #define offsetof(struc, mem) ((int) &((struc *) 0)->mem)
53 #define memsizeof(struc, mem) sizeof(((struc *) 0)->mem)
55 PRIVATE bool_t bits32
; /* nonzero for 32-bit executable */
56 PRIVATE bin_off_t combase
[NSEG
];/* bases of common parts of segments */
57 PRIVATE bin_off_t comsz
[NSEG
]; /* sizes of common parts of segments */
58 PRIVATE fastin_t curseg
; /* current segment, 0 to $F */
59 PRIVATE bin_off_t edataoffset
; /* end of data */
60 PRIVATE bin_off_t endoffset
; /* end of bss */
61 PRIVATE bin_off_t etextoffset
; /* end of text */
62 PRIVATE bin_off_t etextpadoff
; /* end of padded text */
63 PRIVATE
unsigned nsym
; /* number of symbols written */
64 PRIVATE
unsigned relocsize
; /* current relocation size 1, 2 or 4 */
65 PRIVATE bin_off_t segadj
[NSEG
]; /* adjusts (file offset - seg offset) */
66 /* depends on zero init */
67 PRIVATE bin_off_t segbase
[NSEG
];/* bases of data parts of segments */
68 PRIVATE
char segboundary
[9] = "__seg0DH";
69 /* name of seg boundary __seg0DL to __segfCH */
70 PRIVATE bin_off_t segpos
[NSEG
]; /* segment positions for current module */
71 PRIVATE bin_off_t segsz
[NSEG
]; /* sizes of data parts of segments */
72 /* depends on zero init */
73 PRIVATE bool_t sepid
; /* nonzero for separate I & D */
74 PRIVATE bool_t stripflag
; /* nonzero to strip symbols */
75 PRIVATE bin_off_t spos
; /* position in current seg */
76 PRIVATE bool_t uzp
; /* nonzero for unmapped zero page */
77 PRIVATE bool_t xsym
; /* extended symbol table */
79 FORWARD
void linkmod
P((struct modstruct
*modptr
));
80 FORWARD
void padmod
P((struct modstruct
*modptr
));
81 FORWARD
void setsym
P((char *name
, bin_off_t value
));
82 FORWARD
void symres
P((char *name
));
83 FORWARD
void setseg
P((fastin_pt newseg
));
84 FORWARD
void skip
P((unsigned countsize
));
85 FORWARD
void writeheader
P((void));
87 FORWARD
void cpm86header
P((void));
89 FORWARD
void writenulls
P((bin_off_t count
));
91 EXTERN bool_t reloc_output
;
93 /* write binary file */
95 PUBLIC
void write_elks(outfilename
, argsepid
, argbits32
, argstripflag
, arguzp
, argxsym
)
107 struct modstruct
*modptr
;
110 bin_off_t tempoffset
;
114 fatalerror("Output binformat not configured relocatable, use -N");
116 fatalerror("Cannot use -r under MSDOS, sorry");
121 stripflag
= argstripflag
;
126 if (btextoffset
== 0)
127 btextoffset
= page_size();
128 if (bdataoffset
== 0 && sepid
)
129 bdataoffset
= page_size();
132 /* reserve special symbols use curseg to pass parameter to symres() */
133 for (curseg
= 0; curseg
< NSEG
; ++curseg
)
135 segboundary
[5] = hexdigit
[curseg
]; /* to __segX?H */
136 segboundary
[6] = 'D';
137 symres(segboundary
); /* __segXDH */
138 segboundary
[7] = 'L';
139 symres(segboundary
); /* __segXDL */
140 segboundary
[6] = 'C';
141 symres(segboundary
); /* __segXCL */
142 segboundary
[7] = 'H';
143 symres(segboundary
); /* __segXCH */
147 segboundary
[6] = 'S';
148 segboundary
[7] = 'O';
149 symres(segboundary
); /* __segXSO */
156 symres("__heap_top");
157 curseg
= 0; /* text seg, s.b. variable */
161 /* calculate segment and common sizes (sum over loaded modules) */
162 /* use zero init of segsz[] */
163 /* also relocate symbols relative to starts of their segments */
164 for (modptr
= modfirst
; modptr
!= NUL_PTR
; modptr
= modptr
->modnext
)
165 if (modptr
->loadflag
)
167 register struct symstruct
**symparray
;
168 register struct symstruct
*symptr
;
170 for (symparray
= modptr
->symparray
;
171 (symptr
= *symparray
) != NUL_PTR
; ++symparray
)
172 if (symptr
->modptr
== modptr
&& !(symptr
->flags
& A_MASK
))
174 if (!(symptr
->flags
& (I_MASK
| SA_MASK
)))
176 /* relocate by offset of module in segment later */
177 /* relocate by offset of segment in memory special */
178 /* symbols get relocated improperly */
179 symptr
->value
+= segsz
[symptr
->flags
& SEGM_MASK
];
181 else if (symptr
->value
== 0)
183 undefined(symptr
->name
);
187 tempoffset
= ld_roundup(symptr
->value
, 4, bin_off_t
);
188 /* temp kludge quad alignment for 386 */
189 symptr
->value
= comsz
[seg
= symptr
->flags
& SEGM_MASK
];
190 comsz
[seg
] += tempoffset
;
191 if (!(symptr
->flags
& SA_MASK
))
192 symptr
->flags
|= C_MASK
;
195 for (seg
= 0, cptr
= modptr
->segsize
; seg
< NSEG
; ++seg
)
197 segsz
[seg
] += cntooffset(cptr
,
198 sizecount
= segsizecount((unsigned) seg
, modptr
));
200 /* adjust sizes to even to get quad boundaries */
201 /* this should be specifiable dynamically */
202 segsz
[seg
] = ld_roundup(segsz
[seg
], 4, bin_off_t
);
203 comsz
[seg
] = ld_roundup(comsz
[seg
], 4, bin_off_t
);
208 /* calculate seg positions now their sizes are known */
211 * Assume seg 0 is text and rest are data
213 * Assume seg 1..3 are data, Seg 0 is real text, seg 4+ are far text
216 segpos
[0] = segbase
[0] = spos
= btextoffset
;
217 combase
[0] = segbase
[0] + segsz
[0];
218 segadj
[1] = segadj
[0] = -btextoffset
;
219 etextpadoff
= etextoffset
= combase
[0] + comsz
[0];
222 etextpadoff
= ld_roundup(etextoffset
, 0x10, bin_off_t
);
223 segadj
[1] += etextpadoff
- bdataoffset
;
225 else if (bdataoffset
== 0)
226 bdataoffset
= etextpadoff
;
227 segpos
[1] = segbase
[1] = edataoffset
= bdataoffset
;
228 combase
[1] = segbase
[1] + segsz
[1];
230 for (seg
= 4; seg
< NSEG
; ++seg
)
232 segpos
[seg
] = segbase
[seg
] = 0;
233 combase
[seg
] = segbase
[seg
] + segsz
[seg
];
234 segadj
[seg
] = etextpadoff
;
236 etextpadoff
+= ld_roundup(segsz
[seg
] + comsz
[seg
], 0x10, bin_off_t
);
237 segadj
[1] += ld_roundup(segsz
[seg
] + comsz
[seg
], 0x10, bin_off_t
);
239 for (seg
= 2; seg
< 4; ++seg
)
241 for (seg
= 2; seg
< NSEG
; ++seg
)
244 segpos
[seg
] = segbase
[seg
] = combase
[seg
- 1] + comsz
[seg
- 1];
248 /* temporarily have fixed DP seg */
249 /* adjust if nec so it only spans 1 page */
250 tempoffset
= segsz
[seg
] + comsz
[seg
];
251 if (tempoffset
> 0x100)
252 fatalerror("direct page segment too large");
253 if ((((segbase
[seg
] + tempoffset
) ^ segbase
[seg
])
254 & ~(bin_off_t
) 0xFF) != 0)
255 segpos
[seg
] = segbase
[seg
] = (segbase
[seg
] + 0xFF)
259 combase
[seg
] = segbase
[seg
] + segsz
[seg
];
260 segadj
[seg
] = segadj
[seg
- 1];
263 /* relocate symbols by offsets of segments in memory */
264 for (modptr
= modfirst
; modptr
!= NUL_PTR
; modptr
= modptr
->modnext
)
265 if (modptr
->loadflag
)
267 register struct symstruct
**symparray
;
268 register struct symstruct
*symptr
;
270 for (symparray
= modptr
->symparray
;
271 (symptr
= *symparray
) != NUL_PTR
; ++symparray
)
272 if (symptr
->modptr
== modptr
&& !(symptr
->flags
& A_MASK
))
274 if (symptr
->flags
& (C_MASK
| SA_MASK
))
275 symptr
->value
+= combase
[symptr
->flags
& SEGM_MASK
];
277 symptr
->value
+= segbase
[symptr
->flags
& SEGM_MASK
];
281 /* adjust special symbols */
282 for (seg
= 0; seg
< NSEG
; ++seg
)
286 /* only count data of nonzero length */
288 if (segsz
[seg
] != 0 && seg
< 4)
290 edataoffset
= segbase
[seg
] + segsz
[seg
];
291 segboundary
[5] = hexdigit
[seg
]; /* to __segX?H */
292 segboundary
[6] = 'D';
293 setsym(segboundary
, (tempoffset
= segbase
[seg
]) + segsz
[seg
]);
295 segboundary
[7] = 'L';
296 setsym(segboundary
, tempoffset
); /* __segXDL */
297 segboundary
[6] = 'C';
298 setsym(segboundary
, tempoffset
= combase
[seg
]);
300 segboundary
[7] = 'H';
301 setsym(segboundary
, tempoffset
+ comsz
[seg
]);
306 segboundary
[6] = 'S';
307 segboundary
[7] = 'O';
308 setsym(segboundary
, (bin_off_t
)(segadj
[seg
]-segadj
[0])/0x10);
313 setsym("__etext", etextoffset
);
314 setsym("__edata", edataoffset
);
316 setsym("__end", endoffset
= combase
[NSEG
- 1] + comsz
[NSEG
- 1]);
318 setsym("__end", endoffset
= combase
[3] + comsz
[3]);
320 setsym("__segoff", (bin_off_t
)(segadj
[1]-segadj
[0])/0x10);
323 if( etextoffset
> 65536L )
324 fatalerror("text segment too large for 16bit");
325 if( endoffset
> 65536L )
326 fatalerror("data segment too large for 16bit");
329 if( heap_top_value
< 0x100 || endoffset
> heap_top_value
-0x100)
330 heap_top_value
= endoffset
+ 0x8000;
331 if( heap_top_value
> 0x10000 && !bits32
) heap_top_value
= 0x10000;
332 setsym("__heap_top", (bin_off_t
)heap_top_value
);
334 openout(outfilename
);
336 if (cpm86
) cpm86header();
340 for (modptr
= modfirst
; modptr
!= NUL_PTR
; modptr
= modptr
->modnext
)
341 if (modptr
->loadflag
)
347 /* dump symbol table */
350 seekout(FILEHEADERLENGTH
351 + (unsigned long) (etextpadoff
- btextoffset
)
352 + (unsigned long) (edataoffset
- bdataoffset
)
354 extsym
.n_numaux
= extsym
.n_type
= 0;
355 for (modptr
= modfirst
; modptr
!= NUL_PTR
; modptr
= modptr
->modnext
)
356 if (modptr
->loadflag
)
358 register struct symstruct
**symparray
;
359 register struct symstruct
*symptr
;
361 for (symparray
= modptr
->symparray
;
362 (symptr
= *symparray
) != NUL_PTR
; ++symparray
)
363 if (symptr
->modptr
== modptr
)
366 if (symptr
->name
[0] == '_' && symptr
->name
[1] )
367 strncpy((char *) extsym
.n_name
, symptr
->name
+1,
368 sizeof extsym
.n_name
);
371 memcpy((char *) extsym
.n_name
, "$", 1);
372 strncpy((char *) extsym
.n_name
+1, symptr
->name
,
373 sizeof(extsym
.n_name
)-1);
376 strncpy((char *) extsym
.n_name
, symptr
->name
,
377 sizeof extsym
.n_name
);
379 u4cn((char *) &extsym
.n_value
, (u4_t
) symptr
->value
,
380 sizeof extsym
.n_value
);
381 if ((flags
= symptr
->flags
) & A_MASK
)
382 extsym
.n_sclass
= N_ABS
;
383 else if (flags
& (E_MASK
| I_MASK
))
384 extsym
.n_sclass
= C_EXT
;
386 extsym
.n_sclass
= C_STAT
;
387 if (!(flags
& I_MASK
) ||
389 switch (flags
& (A_MASK
| SEGM_MASK
))
396 extsym
.n_sclass
|= N_TEXT
;
402 case 1: case 2: case 3:
403 case A_MASK
|1: case A_MASK
|2: case A_MASK
|3:
405 if (flags
& (C_MASK
| SA_MASK
))
406 extsym
.n_sclass
|= N_BSS
;
408 extsym
.n_sclass
|= N_DATA
;
411 writeout((char *) &extsym
, sizeof extsym
);
418 memset((void*)&extsym
.n_value
,0,
419 sizeof(extsym
.n_value
));
421 for(i
=sizeof extsym
.n_name
; i
<strlen(symptr
->name
);
422 i
+=sizeof extsym
.n_name
)
424 strncpy((char *) extsym
.n_name
, symptr
->name
+i
,
425 sizeof extsym
.n_name
);
426 writeout((char *) &extsym
, sizeof extsym
);
433 seekout((unsigned long) offsetof(struct exec
, a_syms
));
434 u4cn(buf4
, (u4_t
) nsym
* sizeof extsym
,
435 memsizeof(struct exec
, a_syms
));
436 writeout(buf4
, memsizeof(struct exec
, a_syms
));
442 PRIVATE
void linkmod(modptr
)
443 struct modstruct
*modptr
;
445 char buf
[ABS_TEXT_MAX
];
447 unsigned char modify
;
450 struct symstruct
**symparray
;
451 struct symstruct
*symptr
;
455 symparray
= modptr
->symparray
;
456 openin(modptr
->filename
); /* does nothing if already open */
457 seekin(modptr
->textoffset
);
460 if ((command
= readchar()) < 0)
462 modify
= command
& MODIFY_MASK
;
463 switch (command
& CM_MASK
)
469 segpos
[curseg
] = spos
;
482 fatalerror("relocation by long offsets not implemented");
494 if ((modify
-= CM_0_SEG
) >= NSEG
)
495 inputerror("bad data in");
502 modify
= ABS_TEXT_MAX
;
503 readin(buf
, (unsigned) modify
);
504 writeout(buf
, (unsigned) modify
);
505 spos
+= (int) modify
;
507 case CM_OFFSET_RELOC
:
508 offset
= readsize(relocsize
);
512 int m
= (modify
& SEGM_MASK
);
513 if( curseg
!= m
&& m
!= SEGM_MASK
)
514 interseg(modptr
->filename
, modptr
->archentry
, (char*)0);
516 offset
-= (spos
+ relocsize
);
518 offtocn(buf
, segbase
[modify
& SEGM_MASK
] + offset
, relocsize
);
519 writeout(buf
, relocsize
);
522 case CM_SYMBOL_RELOC
:
523 symptr
= symparray
[symbolnum
= readconvsize((unsigned)
524 (modify
& S_MASK
? 2 : 1))];
525 offset
= readconvsize((unsigned) modify
& OF_MASK
);
529 int m
= (symptr
->flags
& SEGM_MASK
);
530 if( curseg
!= m
&& m
!= SEGM_MASK
)
531 interseg(modptr
->filename
, modptr
->archentry
, symptr
->name
);
533 offset
-= (spos
+ relocsize
);
535 offset
+= symptr
->value
;
536 offtocn(buf
, offset
, relocsize
);
537 writeout(buf
, relocsize
);
543 PRIVATE
void padmod(modptr
)
544 struct modstruct
*modptr
;
552 for (seg
= 0, sizeptr
= modptr
->segsize
; seg
< NSEG
; ++seg
)
554 size
= cntooffset(sizeptr
,
555 sizecount
= segsizecount((unsigned) seg
, modptr
));
556 sizeptr
+= sizecount
;
557 if ((count
= segpos
[seg
] - segbase
[seg
]) != size
)
558 size_error(seg
, count
, size
);
560 /* pad to quad boundary */
561 /* not padding in-between common areas which sometimes get into file */
562 if ((size
= ld_roundup(segpos
[seg
], 4, bin_off_t
) - segpos
[seg
]) != 0)
568 segbase
[seg
] = segpos
[seg
];
572 PRIVATE
void setsym(name
, value
)
576 struct symstruct
*symptr
;
578 if ((symptr
= findsym(name
)) != NUL_PTR
)
579 symptr
->value
= value
;
582 PRIVATE
void symres(name
)
585 register struct symstruct
*symptr
;
587 if ((symptr
= findsym(name
)) != NUL_PTR
)
589 if ((symptr
->flags
& SEGM_MASK
) == SEGM_MASK
)
590 symptr
->flags
&= ~SEGM_MASK
| curseg
;
591 if (symptr
->flags
!= (I_MASK
| curseg
) || symptr
->value
!= 0)
593 symptr
->flags
= E_MASK
| curseg
; /* show defined, not common */
597 /* set new segment */
599 PRIVATE
void setseg(newseg
)
602 if (newseg
!= curseg
)
604 segpos
[curseg
] = spos
;
605 spos
= segpos
[curseg
= newseg
];
606 seekout(FILEHEADERLENGTH
+ (unsigned long) spos
607 + (unsigned long) segadj
[curseg
]);
611 PRIVATE
void skip(countsize
)
614 writenulls((bin_off_t
) readsize(countsize
));
618 PRIVATE
void cpm86header()
620 struct cpm86_exec header
;
621 memset(&header
, 0, sizeof header
);
625 header
.ce_group
[0].cg_type
= CG_CODE
;
626 u2c2(header
.ce_group
[0].cg_len
, (15 + etextpadoff
) / 16);
627 u2c2(header
.ce_group
[0].cg_min
, (15 + etextpadoff
) / 16);
628 header
.ce_group
[1].cg_type
= CG_DATA
;
629 u2c2(header
.ce_group
[1].cg_len
, (15 + edataoffset
) / 16);
630 u2c2(header
.ce_group
[1].cg_min
, (15 + endoffset
) / 16);
631 u2c2(header
.ce_group
[1].cg_max
, 0x1000);
635 header
.ce_group
[0].cg_type
= CG_CODE
;
636 u2c2(header
.ce_group
[0].cg_len
, (15 + edataoffset
) / 16);
637 u2c2(header
.ce_group
[0].cg_min
, (15 + endoffset
) / 16);
639 if( FILEHEADERLENGTH
)
640 writeout((char *) &header
, FILEHEADERLENGTH
);
644 PRIVATE
void writeheader()
648 memset(&header
, 0, sizeof header
);
649 header
.a_magic
[0] = A_MAGIC0
;
650 header
.a_magic
[1] = A_MAGIC1
;
651 header
.a_flags
= sepid
? A_SEP
: A_EXEC
;
653 header
.a_flags
|= A_UZP
;
654 header
.a_cpu
= bits32
? A_I80386
: A_I8086
;
655 header
.a_hdrlen
= FILEHEADERLENGTH
;
656 offtocn((char *) &header
.a_text
, etextpadoff
- btextoffset
,
657 sizeof header
.a_text
);
658 offtocn((char *) &header
.a_data
, edataoffset
- bdataoffset
,
659 sizeof header
.a_data
);
660 offtocn((char *) &header
.a_bss
, endoffset
- edataoffset
,
661 sizeof header
.a_bss
);
663 offtocn((char *) &header
.a_entry
, page_size(),
664 sizeof header
.a_entry
);
666 offtocn((char *) &header
.a_total
, (bin_off_t
) heap_top_value
,
667 sizeof header
.a_total
);
668 if( FILEHEADERLENGTH
)
669 writeout((char *) &header
, FILEHEADERLENGTH
);
672 PRIVATE
void writenulls(count
)
678 /* This will only work if we record the highest spos found an seek there
679 * at the end of the generation
682 seekout(FILEHEADERLENGTH
+ (unsigned long) spos
683 + (unsigned long) segadj
[curseg
]);
688 fatalerror("org command requires reverse seek");