1/*
2 * Flat-format binary object format
3 *
4 *  Copyright (C) 2002-2007  Peter Johnson
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND OTHER CONTRIBUTORS ``AS IS''
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR OTHER CONTRIBUTORS BE
19 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25 * POSSIBILITY OF SUCH DAMAGE.
26 */
27#include <util.h>
28
29#ifdef HAVE_UNISTD_H
30#include <unistd.h>
31#endif
32#include <libyasm.h>
33
34
35#define REGULAR_OUTBUF_SIZE     1024
36
37typedef struct bin_section_data {
38    int bss;                    /* aka nobits */
39
40    /* User-provided alignment */
41    yasm_intnum *align, *valign;
42
43    /* User-provided starts */
44    /*@null@*/ /*@owned@*/ yasm_expr *start, *vstart;
45
46    /* User-provided follows */
47    /*@null@*/ /*@owned@*/ char *follows, *vfollows;
48
49    /* Calculated (final) starts, used only during output() */
50    /*@null@*/ /*@owned@*/ yasm_intnum *istart, *ivstart;
51
52    /* Calculated (final) length, used only during output() */
53    /*@null@*/ /*@owned@*/ yasm_intnum *length;
54} bin_section_data;
55
56typedef struct yasm_objfmt_bin {
57    yasm_objfmt_base objfmt;            /* base structure */
58
59    enum {
60        NO_MAP = 0,
61        MAP_NONE = 0x01,
62        MAP_BRIEF = 0x02,
63        MAP_SECTIONS = 0x04,
64        MAP_SYMBOLS = 0x08
65    } map_flags;
66    /*@null@*/ /*@only@*/ char *map_filename;
67
68    /*@null@*/ /*@only@*/ yasm_expr *org;
69} yasm_objfmt_bin;
70
71/* symrec data is used only for the special symbols section<sectname>.start,
72 * section<sectname>.vstart, and section<sectname>.length
73 */
74typedef struct bin_symrec_data {
75    yasm_section *section;          /* referenced section */
76    enum bin_ssym {
77        SSYM_START,
78        SSYM_VSTART,
79        SSYM_LENGTH
80    } which;
81} bin_symrec_data;
82
83static void bin_section_data_destroy(/*@only@*/ void *d);
84static void bin_section_data_print(void *data, FILE *f, int indent_level);
85
86static const yasm_assoc_data_callback bin_section_data_cb = {
87    bin_section_data_destroy,
88    bin_section_data_print
89};
90
91static void bin_symrec_data_destroy(/*@only@*/ void *d);
92static void bin_symrec_data_print(void *data, FILE *f, int indent_level);
93
94static const yasm_assoc_data_callback bin_symrec_data_cb = {
95    bin_symrec_data_destroy,
96    bin_symrec_data_print
97};
98
99yasm_objfmt_module yasm_bin_LTX_objfmt;
100
101
102static yasm_objfmt *
103bin_objfmt_create(yasm_object *object)
104{
105    yasm_objfmt_bin *objfmt_bin = yasm_xmalloc(sizeof(yasm_objfmt_bin));
106    objfmt_bin->objfmt.module = &yasm_bin_LTX_objfmt;
107
108    objfmt_bin->map_flags = NO_MAP;
109    objfmt_bin->map_filename = NULL;
110    objfmt_bin->org = NULL;
111
112    return (yasm_objfmt *)objfmt_bin;
113}
114
115typedef TAILQ_HEAD(bin_group_head, bin_group) bin_groups;
116
117typedef struct bin_group {
118    TAILQ_ENTRY(bin_group) link;
119    yasm_section *section;
120    bin_section_data *bsd;
121
122    /* Groups that (in parallel) logically come immediately after this
123     * group's section.
124     */
125    bin_groups follow_groups;
126} bin_group;
127
128/* Recursive function to find group containing named section. */
129static bin_group *
130find_group_by_name(bin_groups *groups, const char *name)
131{
132    bin_group *group, *found;
133    TAILQ_FOREACH(group, groups, link) {
134        if (strcmp(yasm_section_get_name(group->section), name) == 0)
135            return group;
136        /* Recurse to loop through follow groups */
137        found = find_group_by_name(&group->follow_groups, name);
138        if (found)
139            return found;
140    }
141    return NULL;
142}
143
144/* Recursive function to find group.  Returns NULL if not found. */
145static bin_group *
146find_group_by_section(bin_groups *groups, yasm_section *section)
147{
148    bin_group *group, *found;
149    TAILQ_FOREACH(group, groups, link) {
150        if (group->section == section)
151            return group;
152        /* Recurse to loop through follow groups */
153        found = find_group_by_section(&group->follow_groups, section);
154        if (found)
155            return found;
156    }
157    return NULL;
158}
159
160#if 0
161/* Debugging function */
162static void
163print_groups(const bin_groups *groups, int indent_level)
164{
165    bin_group *group;
166    TAILQ_FOREACH(group, groups, link) {
167        printf("%*sSection `%s':\n", indent_level, "",
168               yasm_section_get_name(group->section));
169        bin_section_data_print(group->bsd, stdout, indent_level+1);
170        if (!TAILQ_EMPTY(&group->follow_groups)) {
171            printf("%*sFollowing groups:\n", indent_level, "");
172            print_groups(&group->follow_groups, indent_level+1);
173        }
174    }
175}
176#endif
177
178static void
179bin_group_destroy(/*@only@*/ bin_group *group)
180{
181    bin_group *follow, *group_temp;
182    TAILQ_FOREACH_SAFE(follow, &group->follow_groups, link, group_temp)
183        bin_group_destroy(follow);
184    yasm_xfree(group);
185}
186
187typedef struct bin_objfmt_output_info {
188    yasm_object *object;
189    yasm_errwarns *errwarns;
190    /*@dependent@*/ FILE *f;
191    /*@only@*/ unsigned char *buf;
192    /*@observer@*/ const yasm_section *sect;
193    unsigned long start;        /* what normal variables go against */
194
195    yasm_intnum *origin;
196    yasm_intnum *tmp_intn;      /* temporary working intnum */
197
198    bin_groups lma_groups, vma_groups;
199} bin_objfmt_output_info;
200
201static int
202bin_objfmt_check_sym(yasm_symrec *sym, /*@null@*/ void *d)
203{
204    /*@null@*/ bin_objfmt_output_info *info = (bin_objfmt_output_info *)d;
205    yasm_sym_vis vis = yasm_symrec_get_visibility(sym);
206    assert(info != NULL);
207
208    /* Don't check internally-generated symbols.  Only internally generated
209     * symbols have symrec data, so simply check for its presence.
210     */
211    if (yasm_symrec_get_data(sym, &bin_symrec_data_cb))
212        return 0;
213
214    if (vis & YASM_SYM_EXTERN) {
215        yasm_warn_set(YASM_WARN_GENERAL,
216            N_("binary object format does not support extern variables"));
217        yasm_errwarn_propagate(info->errwarns, yasm_symrec_get_decl_line(sym));
218    } else if (vis & YASM_SYM_GLOBAL) {
219        yasm_warn_set(YASM_WARN_GENERAL,
220            N_("binary object format does not support global variables"));
221        yasm_errwarn_propagate(info->errwarns, yasm_symrec_get_decl_line(sym));
222    } else if (vis & YASM_SYM_COMMON) {
223        yasm_error_set(YASM_ERROR_TYPE,
224            N_("binary object format does not support common variables"));
225        yasm_errwarn_propagate(info->errwarns, yasm_symrec_get_decl_line(sym));
226    }
227    return 0;
228}
229
230static int
231bin_lma_create_group(yasm_section *sect, /*@null@*/ void *d)
232{
233    bin_objfmt_output_info *info = (bin_objfmt_output_info *)d;
234    bin_section_data *bsd = yasm_section_get_data(sect, &bin_section_data_cb);
235    unsigned long align = yasm_section_get_align(sect);
236    bin_group *group;
237
238    assert(info != NULL);
239    assert(bsd != NULL);
240
241    group = yasm_xmalloc(sizeof(bin_group));
242    group->section = sect;
243    group->bsd = bsd;
244    TAILQ_INIT(&group->follow_groups);
245
246    /* Determine section alignment as necessary. */
247    if (!bsd->align)
248        bsd->align = yasm_intnum_create_uint(align > 4 ? align : 4);
249    else {
250        yasm_intnum *align_intn = yasm_intnum_create_uint(align);
251        if (yasm_intnum_compare(align_intn, bsd->align) > 0) {
252            yasm_warn_set(YASM_WARN_GENERAL,
253                N_("section `%s' internal align of %lu is greater than `%s' of %lu; using `%s'"),
254                yasm_section_get_name(sect),
255                yasm_intnum_get_uint(align_intn),
256                N_("align"),
257                yasm_intnum_get_uint(bsd->align),
258                N_("align"));
259            yasm_errwarn_propagate(info->errwarns, 0);
260        }
261        yasm_intnum_destroy(align_intn);
262    }
263
264    /* Calculate section integer start. */
265    if (bsd->start) {
266        bsd->istart = yasm_expr_get_intnum(&bsd->start, 0);
267        if (!bsd->istart) {
268            yasm_error_set(YASM_ERROR_TOO_COMPLEX,
269                           N_("start expression is too complex"));
270            yasm_errwarn_propagate(info->errwarns, bsd->start->line);
271            return 1;
272        } else
273            bsd->istart = yasm_intnum_copy(bsd->istart);
274    } else
275        bsd->istart = NULL;
276
277    /* Calculate section integer vstart. */
278    if (bsd->vstart) {
279        bsd->ivstart = yasm_expr_get_intnum(&bsd->vstart, 0);
280        if (!bsd->ivstart) {
281            yasm_error_set(YASM_ERROR_TOO_COMPLEX,
282                           N_("vstart expression is too complex"));
283            yasm_errwarn_propagate(info->errwarns, bsd->vstart->line);
284            return 1;
285        } else
286            bsd->ivstart = yasm_intnum_copy(bsd->ivstart);
287    } else
288        bsd->ivstart = NULL;
289
290    /* Calculate section integer length. */
291    bsd->length = yasm_calc_bc_dist(yasm_section_bcs_first(sect),
292                                    yasm_section_bcs_last(sect));
293
294    TAILQ_INSERT_TAIL(&info->lma_groups, group, link);
295    return 0;
296}
297
298static int
299bin_vma_create_group(yasm_section *sect, /*@null@*/ void *d)
300{
301    bin_objfmt_output_info *info = (bin_objfmt_output_info *)d;
302    bin_section_data *bsd = yasm_section_get_data(sect, &bin_section_data_cb);
303    bin_group *group;
304
305    assert(info != NULL);
306    assert(bsd != NULL);
307
308    group = yasm_xmalloc(sizeof(bin_group));
309    group->section = sect;
310    group->bsd = bsd;
311    TAILQ_INIT(&group->follow_groups);
312
313    TAILQ_INSERT_TAIL(&info->vma_groups, group, link);
314    return 0;
315}
316
317/* Calculates new start address based on alignment constraint.
318 * Start is modified (rounded up) to the closest aligned value greater than
319 * what was passed in.
320 * Align must be a power of 2.
321 */
322static void
323bin_objfmt_align(yasm_intnum *start, const yasm_intnum *align)
324{
325    /* Because alignment is always a power of two, we can use some bit
326     * trickery to do this easily.
327     */
328    yasm_intnum *align_intn =
329        yasm_intnum_create_uint(yasm_intnum_get_uint(align)-1);
330    yasm_intnum_calc(align_intn, YASM_EXPR_AND, start);
331    if (!yasm_intnum_is_zero(align_intn)) {
332        /* start = (start & ~(align-1)) + align; */
333        yasm_intnum_set_uint(align_intn, yasm_intnum_get_uint(align)-1);
334        yasm_intnum_calc(align_intn, YASM_EXPR_NOT, NULL);
335        yasm_intnum_calc(align_intn, YASM_EXPR_AND, start);
336        yasm_intnum_set(start, align);
337        yasm_intnum_calc(start, YASM_EXPR_ADD, align_intn);
338    }
339    yasm_intnum_destroy(align_intn);
340}
341
342/* Recursive function to assign start addresses.
343 * Updates start, last, and vdelta parameters as it goes along.
344 * The tmp parameter is just a working intnum so one doesn't have to be
345 * locally allocated for this purpose.
346 */
347static void
348group_assign_start_recurse(bin_group *group, yasm_intnum *start,
349                           yasm_intnum *last, yasm_intnum *vdelta,
350                           yasm_intnum *tmp, yasm_errwarns *errwarns)
351{
352    bin_group *follow_group;
353
354    /* Determine LMA */
355    if (group->bsd->istart) {
356        yasm_intnum_set(group->bsd->istart, start);
357        if (group->bsd->align) {
358            bin_objfmt_align(group->bsd->istart, group->bsd->align);
359            if (yasm_intnum_compare(start, group->bsd->istart) != 0) {
360                yasm_warn_set(YASM_WARN_GENERAL,
361                    N_("start inconsistent with align; using aligned value"));
362                yasm_errwarn_propagate(errwarns, group->bsd->start->line);
363            }
364        }
365    } else {
366        group->bsd->istart = yasm_intnum_copy(start);
367        if (group->bsd->align != 0)
368            bin_objfmt_align(group->bsd->istart, group->bsd->align);
369    }
370
371    /* Determine VMA if either just valign specified or if no v* specified */
372    if (!group->bsd->vstart) {
373        if (!group->bsd->vfollows && !group->bsd->valign) {
374            /* No v* specified, set VMA=LMA+vdelta. */
375            group->bsd->ivstart = yasm_intnum_copy(group->bsd->istart);
376            yasm_intnum_calc(group->bsd->ivstart, YASM_EXPR_ADD, vdelta);
377        } else if (!group->bsd->vfollows) {
378            /* Just valign specified: set VMA=LMA+vdelta, align VMA, then add
379             * delta between unaligned and aligned to vdelta parameter.
380             */
381            group->bsd->ivstart = yasm_intnum_copy(group->bsd->istart);
382            yasm_intnum_calc(group->bsd->ivstart, YASM_EXPR_ADD, vdelta);
383            yasm_intnum_set(tmp, group->bsd->ivstart);
384            bin_objfmt_align(group->bsd->ivstart, group->bsd->valign);
385            yasm_intnum_calc(vdelta, YASM_EXPR_ADD, group->bsd->ivstart);
386            yasm_intnum_calc(vdelta, YASM_EXPR_SUB, tmp);
387        }
388    }
389
390    /* Find the maximum end value */
391    yasm_intnum_set(tmp, group->bsd->istart);
392    yasm_intnum_calc(tmp, YASM_EXPR_ADD, group->bsd->length);
393    if (yasm_intnum_compare(tmp, last) > 0)     /* tmp > last */
394        yasm_intnum_set(last, tmp);
395
396    /* Recurse for each following group. */
397    TAILQ_FOREACH(follow_group, &group->follow_groups, link) {
398        /* Following sections have to follow this one,
399         * so add length to start.
400         */
401        yasm_intnum_set(start, group->bsd->istart);
402        yasm_intnum_calc(start, YASM_EXPR_ADD, group->bsd->length);
403
404        group_assign_start_recurse(follow_group, start, last, vdelta, tmp,
405                                   errwarns);
406    }
407}
408
409/* Recursive function to assign start addresses.
410 * Updates start parameter as it goes along.
411 * The tmp parameter is just a working intnum so one doesn't have to be
412 * locally allocated for this purpose.
413 */
414static void
415group_assign_vstart_recurse(bin_group *group, yasm_intnum *start,
416                            yasm_errwarns *errwarns)
417{
418    bin_group *follow_group;
419
420    /* Determine VMA section alignment as necessary.
421     * Default to LMA alignment if not specified.
422     */
423    if (!group->bsd->valign)
424        group->bsd->valign = yasm_intnum_copy(group->bsd->align);
425    else {
426        unsigned long align = yasm_section_get_align(group->section);
427        yasm_intnum *align_intn = yasm_intnum_create_uint(align);
428        if (yasm_intnum_compare(align_intn, group->bsd->valign) > 0) {
429            yasm_warn_set(YASM_WARN_GENERAL,
430                N_("section `%s' internal align of %lu is greater than `%s' of %lu; using `%s'"),
431                yasm_section_get_name(group->section),
432                yasm_intnum_get_uint(align_intn),
433                N_("valign"),
434                yasm_intnum_get_uint(group->bsd->valign),
435                N_("valign"));
436            yasm_errwarn_propagate(errwarns, 0);
437        }
438        yasm_intnum_destroy(align_intn);
439    }
440
441    /* Determine VMA */
442    if (group->bsd->ivstart) {
443        yasm_intnum_set(group->bsd->ivstart, start);
444        if (group->bsd->valign) {
445            bin_objfmt_align(group->bsd->ivstart, group->bsd->valign);
446            if (yasm_intnum_compare(start, group->bsd->ivstart) != 0) {
447                yasm_error_set(YASM_ERROR_VALUE,
448                               N_("vstart inconsistent with valign"));
449                yasm_errwarn_propagate(errwarns, group->bsd->vstart->line);
450            }
451        }
452    } else {
453        group->bsd->ivstart = yasm_intnum_copy(start);
454        if (group->bsd->valign)
455            bin_objfmt_align(group->bsd->ivstart, group->bsd->valign);
456    }
457
458    /* Recurse for each following group. */
459    TAILQ_FOREACH(follow_group, &group->follow_groups, link) {
460        /* Following sections have to follow this one,
461         * so add length to start.
462         */
463        yasm_intnum_set(start, group->bsd->ivstart);
464        yasm_intnum_calc(start, YASM_EXPR_ADD, group->bsd->length);
465
466        group_assign_vstart_recurse(follow_group, start, errwarns);
467    }
468}
469
470static /*@null@*/ const yasm_intnum *
471get_ssym_value(yasm_symrec *sym)
472{
473    bin_symrec_data *bsymd = yasm_symrec_get_data(sym, &bin_symrec_data_cb);
474    bin_section_data *bsd;
475
476    if (!bsymd)
477        return NULL;
478
479    bsd = yasm_section_get_data(bsymd->section, &bin_section_data_cb);
480    assert(bsd != NULL);
481
482    switch (bsymd->which) {
483        case SSYM_START: return bsd->istart;
484        case SSYM_VSTART: return bsd->ivstart;
485        case SSYM_LENGTH: return bsd->length;
486    }
487    return NULL;
488}
489
490static /*@only@*/ yasm_expr *
491bin_objfmt_expr_xform(/*@returned@*/ /*@only@*/ yasm_expr *e,
492                      /*@unused@*/ /*@null@*/ void *d)
493{
494    int i;
495    for (i=0; i<e->numterms; i++) {
496        /*@dependent@*/ yasm_section *sect;
497        /*@dependent@*/ /*@null@*/ yasm_bytecode *precbc;
498        /*@null@*/ yasm_intnum *dist;
499        /*@null@*/ const yasm_intnum *ssymval;
500
501        /* Transform symrecs or precbcs that reference sections into
502         * vstart + intnum(dist).
503         */
504        if (((e->terms[i].type == YASM_EXPR_SYM &&
505             yasm_symrec_get_label(e->terms[i].data.sym, &precbc)) ||
506            (e->terms[i].type == YASM_EXPR_PRECBC &&
507             (precbc = e->terms[i].data.precbc))) &&
508            (sect = yasm_bc_get_section(precbc)) &&
509            (dist = yasm_calc_bc_dist(yasm_section_bcs_first(sect), precbc))) {
510            bin_section_data *bsd;
511            bsd = yasm_section_get_data(sect, &bin_section_data_cb);
512            assert(bsd != NULL);
513            yasm_intnum_calc(dist, YASM_EXPR_ADD, bsd->ivstart);
514            e->terms[i].type = YASM_EXPR_INT;
515            e->terms[i].data.intn = dist;
516        }
517
518        /* Transform our special symrecs into the appropriate value */
519        if (e->terms[i].type == YASM_EXPR_SYM &&
520            (ssymval = get_ssym_value(e->terms[i].data.sym))) {
521            e->terms[i].type = YASM_EXPR_INT;
522            e->terms[i].data.intn = yasm_intnum_copy(ssymval);
523        }
524    }
525
526    return e;
527}
528
529typedef struct map_output_info {
530    /* address width */
531    int bytes;
532
533    /* intnum output static data areas */
534    unsigned char *buf;
535    yasm_intnum *intn;
536
537    /* symrec output information */
538    unsigned long count;
539    yasm_section *section;  /* NULL for EQUs */
540
541    yasm_object *object;    /* object */
542    FILE *f;                /* map output file */
543} map_output_info;
544
545static int
546map_prescan_bytes(yasm_section *sect, void *d)
547{
548    bin_section_data *bsd = yasm_section_get_data(sect, &bin_section_data_cb);
549    map_output_info *info = (map_output_info *)d;
550
551    assert(bsd != NULL);
552    assert(info != NULL);
553
554    while (!yasm_intnum_check_size(bsd->length, info->bytes * 8, 0, 0))
555        info->bytes *= 2;
556    while (!yasm_intnum_check_size(bsd->istart, info->bytes * 8, 0, 0))
557        info->bytes *= 2;
558    while (!yasm_intnum_check_size(bsd->ivstart, info->bytes * 8, 0, 0))
559        info->bytes *= 2;
560
561    return 0;
562}
563
564static void
565map_print_intnum(const yasm_intnum *intn, map_output_info *info)
566{
567    size_t i;
568    yasm_intnum_get_sized(intn, info->buf, info->bytes, info->bytes*8, 0, 0,
569                          0);
570    for (i=info->bytes; i != 0; i--)
571        fprintf(info->f, "%02X", info->buf[i-1]);
572}
573
574static void
575map_sections_summary(bin_groups *groups, map_output_info *info)
576{
577    bin_group *group;
578    TAILQ_FOREACH(group, groups, link) {
579        bin_section_data *bsd = group->bsd;
580
581        assert(bsd != NULL);
582        assert(info != NULL);
583
584        map_print_intnum(bsd->ivstart, info);
585        fprintf(info->f, "  ");
586
587        yasm_intnum_set(info->intn, bsd->ivstart);
588        yasm_intnum_calc(info->intn, YASM_EXPR_ADD, bsd->length);
589        map_print_intnum(info->intn, info);
590        fprintf(info->f, "  ");
591
592        map_print_intnum(bsd->istart, info);
593        fprintf(info->f, "  ");
594
595        yasm_intnum_set(info->intn, bsd->istart);
596        yasm_intnum_calc(info->intn, YASM_EXPR_ADD, bsd->length);
597        map_print_intnum(info->intn, info);
598        fprintf(info->f, "  ");
599
600        map_print_intnum(bsd->length, info);
601        fprintf(info->f, "  ");
602
603        fprintf(info->f, "%-*s", 10, bsd->bss ? "nobits" : "progbits");
604        fprintf(info->f, "%s\n", yasm_section_get_name(group->section));
605
606        /* Recurse to loop through follow groups */
607        map_sections_summary(&group->follow_groups, info);
608    }
609}
610
611static void
612map_sections_detail(bin_groups *groups, map_output_info *info)
613{
614    bin_group *group;
615    TAILQ_FOREACH(group, groups, link) {
616        bin_section_data *bsd = group->bsd;
617        size_t i;
618        const char *s;
619
620        s = yasm_section_get_name(group->section);
621        fprintf(info->f, "---- Section %s ", s);
622        for (i=0; i<(65-strlen(s)); i++)
623            fputc('-', info->f);
624
625        fprintf(info->f, "\n\nclass:     %s",
626                bsd->bss ? "nobits" : "progbits");
627        fprintf(info->f, "\nlength:    ");
628        map_print_intnum(bsd->length, info);
629        fprintf(info->f, "\nstart:     ");
630        map_print_intnum(bsd->istart, info);
631        fprintf(info->f, "\nalign:     ");
632        map_print_intnum(bsd->align, info);
633        fprintf(info->f, "\nfollows:   %s",
634                bsd->follows ? bsd->follows : "not defined");
635        fprintf(info->f, "\nvstart:    ");
636        map_print_intnum(bsd->ivstart, info);
637        fprintf(info->f, "\nvalign:    ");
638        map_print_intnum(bsd->valign, info);
639        fprintf(info->f, "\nvfollows:  %s\n\n",
640                bsd->vfollows ? bsd->vfollows : "not defined");
641
642        /* Recurse to loop through follow groups */
643        map_sections_detail(&group->follow_groups, info);
644    }
645}
646
647static int
648map_symrec_count(yasm_symrec *sym, void *d)
649{
650    map_output_info *info = (map_output_info *)d;
651    /*@dependent@*/ yasm_bytecode *precbc;
652
653    assert(info != NULL);
654
655    /* TODO: autodetect wider size */
656    if (!info->section && yasm_symrec_get_equ(sym)) {
657        info->count++;
658    } else if (yasm_symrec_get_label(sym, &precbc) &&
659               yasm_bc_get_section(precbc) == info->section) {
660        info->count++;
661    }
662    return 0;
663}
664
665static int
666map_symrec_output(yasm_symrec *sym, void *d)
667{
668    map_output_info *info = (map_output_info *)d;
669    const yasm_expr *equ;
670    /*@dependent@*/ yasm_bytecode *precbc;
671    /*@only@*/ char *name = yasm_symrec_get_global_name(sym, info->object);
672
673    assert(info != NULL);
674
675    if (!info->section && (equ = yasm_symrec_get_equ(sym))) {
676        yasm_expr *realequ = yasm_expr_copy(equ);
677        realequ = yasm_expr__level_tree
678            (realequ, 1, 1, 1, 0, bin_objfmt_expr_xform, NULL);
679        yasm_intnum_set(info->intn, yasm_expr_get_intnum(&realequ, 0));
680        yasm_expr_destroy(realequ);
681        map_print_intnum(info->intn, info);
682        fprintf(info->f, "  %s\n", name);
683    } else if (yasm_symrec_get_label(sym, &precbc) &&
684               yasm_bc_get_section(precbc) == info->section) {
685        bin_section_data *bsd =
686            yasm_section_get_data(info->section, &bin_section_data_cb);
687
688        /* Real address */
689        yasm_intnum_set_uint(info->intn, yasm_bc_next_offset(precbc));
690        yasm_intnum_calc(info->intn, YASM_EXPR_ADD, bsd->istart);
691        map_print_intnum(info->intn, info);
692        fprintf(info->f, "  ");
693
694        /* Virtual address */
695        yasm_intnum_set_uint(info->intn, yasm_bc_next_offset(precbc));
696        yasm_intnum_calc(info->intn, YASM_EXPR_ADD, bsd->ivstart);
697        map_print_intnum(info->intn, info);
698
699        /* Name */
700        fprintf(info->f, "  %s\n", name);
701    }
702    yasm_xfree(name);
703    return 0;
704}
705
706static void
707map_sections_symbols(bin_groups *groups, map_output_info *info)
708{
709    bin_group *group;
710    TAILQ_FOREACH(group, groups, link) {
711        info->count = 0;
712        info->section = group->section;
713        yasm_symtab_traverse(info->object->symtab, info, map_symrec_count);
714
715        if (info->count > 0) {
716            const char *s = yasm_section_get_name(group->section);
717            size_t i;
718            fprintf(info->f, "---- Section %s ", s);
719            for (i=0; i<(65-strlen(s)); i++)
720                fputc('-', info->f);
721            fprintf(info->f, "\n\n%-*s%-*s%s\n",
722                    info->bytes*2+2, "Real",
723                    info->bytes*2+2, "Virtual",
724                    "Name");
725            yasm_symtab_traverse(info->object->symtab, info,
726                                 map_symrec_output);
727            fprintf(info->f, "\n\n");
728        }
729
730        /* Recurse to loop through follow groups */
731        map_sections_symbols(&group->follow_groups, info);
732    }
733}
734
735static void
736output_map(bin_objfmt_output_info *info)
737{
738    yasm_objfmt_bin *objfmt_bin = (yasm_objfmt_bin *)info->object->objfmt;
739    FILE *f;
740    int i;
741    map_output_info mapinfo;
742
743    if (objfmt_bin->map_flags == NO_MAP)
744        return;
745
746    if (objfmt_bin->map_flags == MAP_NONE)
747        objfmt_bin->map_flags = MAP_BRIEF;          /* default to brief */
748
749    if (!objfmt_bin->map_filename)
750        f = stdout;                                 /* default to stdout */
751    else {
752        f = fopen(objfmt_bin->map_filename, "wt");
753        if (!f) {
754            yasm_warn_set(YASM_WARN_GENERAL,
755                          N_("unable to open map file `%s'"),
756                          objfmt_bin->map_filename);
757            yasm_errwarn_propagate(info->errwarns, 0);
758            return;
759        }
760    }
761
762    mapinfo.object = info->object;
763    mapinfo.f = f;
764
765    /* Temporary intnum */
766    mapinfo.intn = info->tmp_intn;
767
768    /* Prescan all values to figure out what width we should make the output
769     * fields.  Start with a minimum of 4.
770     */
771    mapinfo.bytes = 4;
772    while (!yasm_intnum_check_size(info->origin, mapinfo.bytes * 8, 0, 0))
773        mapinfo.bytes *= 2;
774    yasm_object_sections_traverse(info->object, &mapinfo, map_prescan_bytes);
775    mapinfo.buf = yasm_xmalloc(mapinfo.bytes);
776
777    fprintf(f, "\n- YASM Map file ");
778    for (i=0; i<63; i++)
779        fputc('-', f);
780    fprintf(f, "\n\nSource file:  %s\n", info->object->src_filename);
781    fprintf(f, "Output file:  %s\n\n", info->object->obj_filename);
782
783    fprintf(f, "-- Program origin ");
784    for (i=0; i<61; i++)
785        fputc('-', f);
786    fprintf(f, "\n\n");
787    map_print_intnum(info->origin, &mapinfo);
788    fprintf(f, "\n\n");
789
790    if (objfmt_bin->map_flags & MAP_BRIEF) {
791        fprintf(f, "-- Sections (summary) ");
792        for (i=0; i<57; i++)
793            fputc('-', f);
794        fprintf(f, "\n\n%-*s%-*s%-*s%-*s%-*s%-*s%s\n",
795                mapinfo.bytes*2+2, "Vstart",
796                mapinfo.bytes*2+2, "Vstop",
797                mapinfo.bytes*2+2, "Start",
798                mapinfo.bytes*2+2, "Stop",
799                mapinfo.bytes*2+2, "Length",
800                10, "Class", "Name");
801
802        map_sections_summary(&info->lma_groups, &mapinfo);
803        fprintf(f, "\n");
804    }
805
806    if (objfmt_bin->map_flags & MAP_SECTIONS) {
807        fprintf(f, "-- Sections (detailed) ");
808        for (i=0; i<56; i++)
809            fputc('-', f);
810        fprintf(f, "\n\n");
811        map_sections_detail(&info->lma_groups, &mapinfo);
812    }
813
814    if (objfmt_bin->map_flags & MAP_SYMBOLS) {
815        fprintf(f, "-- Symbols ");
816        for (i=0; i<68; i++)
817            fputc('-', f);
818        fprintf(f, "\n\n");
819
820        /* We do two passes for EQU and each section; the first pass
821         * determines the byte width to use for the value and whether any
822         * symbols are present, the second pass actually outputs the text.
823         */
824
825        /* EQUs */
826        mapinfo.count = 0;
827        mapinfo.section = NULL;
828        yasm_symtab_traverse(info->object->symtab, &mapinfo, map_symrec_count);
829
830        if (mapinfo.count > 0) {
831            fprintf(f, "---- No Section ");
832            for (i=0; i<63; i++)
833                fputc('-', f);
834            fprintf(f, "\n\n%-*s%s\n", mapinfo.bytes*2+2, "Value", "Name");
835            yasm_symtab_traverse(info->object->symtab, &mapinfo,
836                                 map_symrec_output);
837            fprintf(f, "\n\n");
838        }
839
840        /* Other sections */
841        map_sections_symbols(&info->lma_groups, &mapinfo);
842    }
843
844    if (f != stdout)
845        fclose(f);
846
847    yasm_xfree(mapinfo.buf);
848}
849
850/* Check for LMA overlap using a simple N^2 algorithm. */
851static int
852check_lma_overlap(yasm_section *sect, /*@null@*/ void *d)
853{
854    bin_section_data *bsd, *bsd2;
855    yasm_section *other = (yasm_section *)d;
856    yasm_intnum *overlap;
857
858    if (!d)
859        return yasm_object_sections_traverse(yasm_section_get_object(sect),
860                                             sect, check_lma_overlap);
861    if (sect == other)
862        return 0;
863
864    bsd = yasm_section_get_data(sect, &bin_section_data_cb);
865    bsd2 = yasm_section_get_data(other, &bin_section_data_cb);
866
867    if (yasm_intnum_is_zero(bsd->length) ||
868        yasm_intnum_is_zero(bsd2->length))
869        return 0;
870
871    if (yasm_intnum_compare(bsd->istart, bsd2->istart) <= 0) {
872        overlap = yasm_intnum_copy(bsd->istart);
873        yasm_intnum_calc(overlap, YASM_EXPR_ADD, bsd->length);
874        yasm_intnum_calc(overlap, YASM_EXPR_SUB, bsd2->istart);
875    } else {
876        overlap = yasm_intnum_copy(bsd2->istart);
877        yasm_intnum_calc(overlap, YASM_EXPR_ADD, bsd2->length);
878        yasm_intnum_calc(overlap, YASM_EXPR_SUB, bsd->istart);
879    }
880
881    if (yasm_intnum_sign(overlap) > 0) {
882        yasm_error_set(YASM_ERROR_GENERAL,
883                       N_("sections `%s' and `%s' overlap by %lu bytes"),
884                       yasm_section_get_name(sect),
885                       yasm_section_get_name(other),
886                       yasm_intnum_get_uint(overlap));
887        yasm_intnum_destroy(overlap);
888        return -1;
889    }
890
891    yasm_intnum_destroy(overlap);
892    return 0;
893}
894
895static int
896bin_objfmt_output_value(yasm_value *value, unsigned char *buf,
897                        unsigned int destsize,
898                        /*@unused@*/ unsigned long offset, yasm_bytecode *bc,
899                        int warn, /*@null@*/ void *d)
900{
901    /*@null@*/ bin_objfmt_output_info *info = (bin_objfmt_output_info *)d;
902    /*@dependent@*/ /*@null@*/ yasm_bytecode *precbc;
903    /*@dependent@*/ yasm_section *sect;
904
905    assert(info != NULL);
906
907    /* Binary objects we need to resolve against object, not against section. */
908    if (value->rel) {
909        unsigned int rshift = (unsigned int)value->rshift;
910        yasm_expr *syme;
911        /*@null@*/ const yasm_intnum *ssymval;
912
913        if (yasm_symrec_is_abs(value->rel)) {
914            syme = yasm_expr_create_ident(yasm_expr_int(
915                yasm_intnum_create_uint(0)), bc->line);
916        } else if (yasm_symrec_get_label(value->rel, &precbc)
917                   && (sect = yasm_bc_get_section(precbc))) {
918            syme = yasm_expr_create_ident(yasm_expr_sym(value->rel), bc->line);
919        } else if ((ssymval = get_ssym_value(value->rel))) {
920            syme = yasm_expr_create_ident(yasm_expr_int(
921                yasm_intnum_copy(ssymval)), bc->line);
922        } else
923            goto done;
924
925        /* Handle PC-relative */
926        if (value->curpos_rel) {
927            yasm_expr *sube;
928            sube = yasm_expr_create(YASM_EXPR_SUB, yasm_expr_precbc(bc),
929                yasm_expr_int(yasm_intnum_create_uint(bc->len*bc->mult_int)),
930                bc->line);
931            syme = yasm_expr_create(YASM_EXPR_SUB, yasm_expr_expr(syme),
932                                    yasm_expr_expr(sube), bc->line);
933            value->curpos_rel = 0;
934            value->ip_rel = 0;
935        }
936
937        if (value->rshift > 0)
938            syme = yasm_expr_create(YASM_EXPR_SHR, yasm_expr_expr(syme),
939                yasm_expr_int(yasm_intnum_create_uint(rshift)), bc->line);
940
941        /* Add into absolute portion */
942        if (!value->abs)
943            value->abs = syme;
944        else
945            value->abs =
946                yasm_expr_create(YASM_EXPR_ADD, yasm_expr_expr(value->abs),
947                                 yasm_expr_expr(syme), bc->line);
948        value->rel = NULL;
949        value->rshift = 0;
950    }
951done:
952    /* Simplify absolute portion of value, transforming symrecs */
953    if (value->abs)
954        value->abs = yasm_expr__level_tree
955            (value->abs, 1, 1, 1, 0, bin_objfmt_expr_xform, NULL);
956
957    /* Output */
958    switch (yasm_value_output_basic(value, buf, destsize, bc, warn,
959                                    info->object->arch)) {
960        case -1:
961            return 1;
962        case 0:
963            break;
964        default:
965            return 0;
966    }
967
968    /* Couldn't output, assume it contains an external reference. */
969    yasm_error_set(YASM_ERROR_GENERAL,
970        N_("binary object format does not support external references"));
971    return 1;
972}
973
974static int
975bin_objfmt_output_bytecode(yasm_bytecode *bc, /*@null@*/ void *d)
976{
977    /*@null@*/ bin_objfmt_output_info *info = (bin_objfmt_output_info *)d;
978    /*@null@*/ /*@only@*/ unsigned char *bigbuf;
979    unsigned long size = REGULAR_OUTBUF_SIZE;
980    int gap;
981
982    assert(info != NULL);
983
984    bigbuf = yasm_bc_tobytes(bc, info->buf, &size, &gap, info,
985                             bin_objfmt_output_value, NULL);
986
987    /* Don't bother doing anything else if size ended up being 0. */
988    if (size == 0) {
989        if (bigbuf)
990            yasm_xfree(bigbuf);
991        return 0;
992    }
993
994    /* Warn that gaps are converted to 0 and write out the 0's. */
995    if (gap) {
996        unsigned long left;
997        yasm_warn_set(YASM_WARN_UNINIT_CONTENTS,
998            N_("uninitialized space declared in code/data section: zeroing"));
999        /* Write out in chunks */
1000        memset(info->buf, 0, REGULAR_OUTBUF_SIZE);
1001        left = size;
1002        while (left > REGULAR_OUTBUF_SIZE) {
1003            fwrite(info->buf, REGULAR_OUTBUF_SIZE, 1, info->f);
1004            left -= REGULAR_OUTBUF_SIZE;
1005        }
1006        fwrite(info->buf, left, 1, info->f);
1007    } else {
1008        /* Output buf (or bigbuf if non-NULL) to file */
1009        fwrite(bigbuf ? bigbuf : info->buf, (size_t)size, 1, info->f);
1010    }
1011
1012    /* If bigbuf was allocated, free it */
1013    if (bigbuf)
1014        yasm_xfree(bigbuf);
1015
1016    return 0;
1017}
1018
1019/* Check to ensure bytecode is res* (for BSS sections) */
1020static int
1021bin_objfmt_no_output_bytecode(yasm_bytecode *bc, /*@null@*/ void *d)
1022{
1023    /*@null@*/ bin_objfmt_output_info *info = (bin_objfmt_output_info *)d;
1024    /*@null@*/ /*@only@*/ unsigned char *bigbuf;
1025    unsigned long size = REGULAR_OUTBUF_SIZE;
1026    int gap;
1027
1028    assert(info != NULL);
1029
1030    bigbuf = yasm_bc_tobytes(bc, info->buf, &size, &gap, info,
1031                             bin_objfmt_output_value, NULL);
1032
1033    /* If bigbuf was allocated, free it */
1034    if (bigbuf)
1035        yasm_xfree(bigbuf);
1036
1037    /* Don't bother doing anything else if size ended up being 0. */
1038    if (size == 0)
1039        return 0;
1040
1041    /* Warn if not a gap. */
1042    if (!gap) {
1043        yasm_warn_set(YASM_WARN_GENERAL,
1044            N_("initialized space declared in nobits section: ignoring"));
1045    }
1046
1047    return 0;
1048}
1049
1050static int
1051bin_objfmt_output_section(yasm_section *sect, /*@null@*/ void *d)
1052{
1053    bin_section_data *bsd = yasm_section_get_data(sect, &bin_section_data_cb);
1054    /*@null@*/ bin_objfmt_output_info *info = (bin_objfmt_output_info *)d;
1055
1056    assert(bsd != NULL);
1057    assert(info != NULL);
1058
1059    if (bsd->bss) {
1060        yasm_section_bcs_traverse(sect, info->errwarns,
1061                                  info, bin_objfmt_no_output_bytecode);
1062    } else {
1063        yasm_intnum_set(info->tmp_intn, bsd->istart);
1064        yasm_intnum_calc(info->tmp_intn, YASM_EXPR_SUB, info->origin);
1065        if (yasm_intnum_sign(info->tmp_intn) < 0) {
1066            yasm_error_set(YASM_ERROR_VALUE,
1067                           N_("section `%s' starts before origin (ORG)"),
1068                           yasm_section_get_name(sect));
1069            yasm_errwarn_propagate(info->errwarns, 0);
1070            return 0;
1071        }
1072        if (!yasm_intnum_check_size(info->tmp_intn, sizeof(long)*8, 0, 1)) {
1073            yasm_error_set(YASM_ERROR_VALUE,
1074                           N_("section `%s' start value too large"),
1075                           yasm_section_get_name(sect));
1076            yasm_errwarn_propagate(info->errwarns, 0);
1077            return 0;
1078        }
1079        if (fseek(info->f, yasm_intnum_get_int(info->tmp_intn) + info->start,
1080                  SEEK_SET) < 0)
1081            yasm__fatal(N_("could not seek on output file"));
1082        yasm_section_bcs_traverse(sect, info->errwarns,
1083                                  info, bin_objfmt_output_bytecode);
1084    }
1085
1086    return 0;
1087}
1088
1089static void
1090bin_objfmt_cleanup(bin_objfmt_output_info *info)
1091{
1092    bin_group *group, *group_temp;
1093
1094    yasm_xfree(info->buf);
1095    yasm_intnum_destroy(info->origin);
1096    yasm_intnum_destroy(info->tmp_intn);
1097
1098    TAILQ_FOREACH_SAFE(group, &info->lma_groups, link, group_temp)
1099        bin_group_destroy(group);
1100
1101    TAILQ_FOREACH_SAFE(group, &info->vma_groups, link, group_temp)
1102        bin_group_destroy(group);
1103}
1104
1105static void
1106bin_objfmt_output(yasm_object *object, FILE *f, /*@unused@*/ int all_syms,
1107                  yasm_errwarns *errwarns)
1108{
1109    yasm_objfmt_bin *objfmt_bin = (yasm_objfmt_bin *)object->objfmt;
1110    bin_objfmt_output_info info;
1111    bin_group *group, *lma_group, *vma_group, *group_temp;
1112    yasm_intnum *start, *last, *vdelta;
1113    bin_groups unsorted_groups, bss_groups;
1114
1115    info.start = ftell(f);
1116
1117    /* Set ORG to 0 unless otherwise specified */
1118    if (objfmt_bin->org) {
1119        info.origin = yasm_expr_get_intnum(&objfmt_bin->org, 0);
1120        if (!info.origin) {
1121            yasm_error_set(YASM_ERROR_TOO_COMPLEX,
1122                           N_("ORG expression is too complex"));
1123            yasm_errwarn_propagate(errwarns, objfmt_bin->org->line);
1124            return;
1125        }
1126        if (yasm_intnum_sign(info.origin) < 0) {
1127            yasm_error_set(YASM_ERROR_VALUE, N_("ORG expression is negative"));
1128            yasm_errwarn_propagate(errwarns, objfmt_bin->org->line);
1129            return;
1130        }
1131        info.origin = yasm_intnum_copy(info.origin);
1132    } else
1133        info.origin = yasm_intnum_create_uint(0);
1134
1135    info.object = object;
1136    info.errwarns = errwarns;
1137    info.f = f;
1138    info.buf = yasm_xmalloc(REGULAR_OUTBUF_SIZE);
1139    info.tmp_intn = yasm_intnum_create_uint(0);
1140    TAILQ_INIT(&info.lma_groups);
1141    TAILQ_INIT(&info.vma_groups);
1142
1143    /* Check symbol table */
1144    yasm_symtab_traverse(object->symtab, &info, bin_objfmt_check_sym);
1145
1146    /* Create section groups */
1147    if (yasm_object_sections_traverse(object, &info, bin_lma_create_group)) {
1148        bin_objfmt_cleanup(&info);
1149        return;     /* error detected */
1150    }
1151
1152    /* Determine section order according to LMA.
1153     * Sections can be ordered either by (priority):
1154     *  - follows
1155     *  - start
1156     *  - progbits/nobits setting
1157     *  - order in the input file
1158     */
1159
1160    /* Look at each group with follows specified, and find the section
1161     * that group is supposed to follow.
1162     */
1163    TAILQ_FOREACH_SAFE(lma_group, &info.lma_groups, link, group_temp) {
1164        if (lma_group->bsd->follows) {
1165            bin_group *found;
1166            /* Need to find group containing section this section follows. */
1167            found =
1168                find_group_by_name(&info.lma_groups, lma_group->bsd->follows);
1169            if (!found) {
1170                yasm_error_set(YASM_ERROR_VALUE,
1171                               N_("section `%s' follows an invalid or unknown section `%s'"),
1172                               yasm_section_get_name(lma_group->section),
1173                               lma_group->bsd->follows);
1174                yasm_errwarn_propagate(errwarns, 0);
1175                bin_objfmt_cleanup(&info);
1176                return;
1177            }
1178
1179            /* Check for loops */
1180            if (lma_group->section == found->section ||
1181                find_group_by_section(&lma_group->follow_groups,
1182                                      found->section)) {
1183                yasm_error_set(YASM_ERROR_VALUE,
1184                               N_("follows loop between section `%s' and section `%s'"),
1185                               yasm_section_get_name(lma_group->section),
1186                               yasm_section_get_name(found->section));
1187                yasm_errwarn_propagate(errwarns, 0);
1188                bin_objfmt_cleanup(&info);
1189                return;
1190            }
1191
1192            /* Remove this section from main lma groups list */
1193            TAILQ_REMOVE(&info.lma_groups, lma_group, link);
1194            /* Add it after the section it's supposed to follow. */
1195            TAILQ_INSERT_TAIL(&found->follow_groups, lma_group, link);
1196        }
1197    }
1198
1199    /* Sort the top-level groups according to their start address.
1200     * Use Shell sort for ease of implementation.
1201     * If no start address is specified for a section, don't change the order,
1202     * and move BSS sections to a separate list so they can be moved to the
1203     * end of the lma list after all other sections are sorted.
1204     */
1205    unsorted_groups = info.lma_groups;  /* structure copy */
1206    TAILQ_INIT(&info.lma_groups);
1207    TAILQ_INIT(&bss_groups);
1208    TAILQ_FOREACH_SAFE(lma_group, &unsorted_groups, link, group_temp) {
1209        bin_group *before;
1210
1211        if (!lma_group->bsd->istart) {
1212            if (lma_group->bsd->bss)
1213                TAILQ_INSERT_TAIL(&bss_groups, lma_group, link);
1214            else
1215                TAILQ_INSERT_TAIL(&info.lma_groups, lma_group, link);
1216            continue;
1217        }
1218
1219        before = NULL;
1220        TAILQ_FOREACH(group, &info.lma_groups, link) {
1221            if (!group->bsd->istart)
1222                continue;
1223            if (yasm_intnum_compare(group->bsd->istart,
1224                                    lma_group->bsd->istart) > 0) {
1225                before = group;
1226                break;
1227            }
1228        }
1229        if (before)
1230            TAILQ_INSERT_BEFORE(before, lma_group, link);
1231        else
1232            TAILQ_INSERT_TAIL(&info.lma_groups, lma_group, link);
1233    }
1234
1235    /* Move the pure-BSS sections to the end of the LMA list. */
1236    TAILQ_FOREACH_SAFE(group, &bss_groups, link, group_temp)
1237        TAILQ_INSERT_TAIL(&info.lma_groups, group, link);
1238    TAILQ_INIT(&bss_groups);    /* For sanity */
1239
1240    /* Assign a LMA start address to every section.
1241     * Also assign VMA=LMA unless otherwise specified.
1242     *
1243     * We need to assign VMA=LMA here (while walking the tree) for the case:
1244     *  sect1 start=0 (size=0x11)
1245     *  sect2 follows=sect1 valign=16 (size=0x104)
1246     *  sect3 follows=sect2 valign=16
1247     * Where the valign of sect2 will result in a sect3 vaddr higher than a
1248     * naive segment-by-segment interpretation (where sect3 and sect2 would
1249     * have a VMA overlap).
1250     *
1251     * Algorithm for VMA=LMA setting:
1252     * Start with delta=0.
1253     * If there's no virtual attributes, we simply set VMA = LMA+delta.
1254     * If there's only valign specified, we set VMA = aligned LMA, and add
1255     * any new alignment difference to delta.
1256     *
1257     * We could do the LMA start and VMA=LMA steps in two separate steps,
1258     * but it's easier to just recurse once.
1259     */
1260    start = yasm_intnum_copy(info.origin);
1261    last = yasm_intnum_copy(info.origin);
1262    vdelta = yasm_intnum_create_uint(0);
1263    TAILQ_FOREACH(lma_group, &info.lma_groups, link) {
1264        if (lma_group->bsd->istart)
1265            yasm_intnum_set(start, lma_group->bsd->istart);
1266        group_assign_start_recurse(lma_group, start, last, vdelta,
1267                                   info.tmp_intn, errwarns);
1268        yasm_intnum_set(start, last);
1269    }
1270    yasm_intnum_destroy(last);
1271    yasm_intnum_destroy(vdelta);
1272
1273    /*
1274     * Determine section order according to VMA
1275     */
1276
1277    /* Create section groups */
1278    if (yasm_object_sections_traverse(object, &info, bin_vma_create_group)) {
1279        yasm_intnum_destroy(start);
1280        bin_objfmt_cleanup(&info);
1281        return;     /* error detected */
1282    }
1283
1284    /* Look at each group with vfollows specified, and find the section
1285     * that group is supposed to follow.
1286     */
1287    TAILQ_FOREACH_SAFE(vma_group, &info.vma_groups, link, group_temp) {
1288        if (vma_group->bsd->vfollows) {
1289            bin_group *found;
1290            /* Need to find group containing section this section follows. */
1291            found = find_group_by_name(&info.vma_groups,
1292                                       vma_group->bsd->vfollows);
1293            if (!found) {
1294                yasm_error_set(YASM_ERROR_VALUE,
1295                               N_("section `%s' vfollows an invalid or unknown section `%s'"),
1296                               yasm_section_get_name(vma_group->section),
1297                               vma_group->bsd->vfollows);
1298                yasm_errwarn_propagate(errwarns, 0);
1299                yasm_intnum_destroy(start);
1300                bin_objfmt_cleanup(&info);
1301                return;
1302            }
1303
1304            /* Check for loops */
1305            if (vma_group->section == found->section ||
1306                find_group_by_section(&vma_group->follow_groups,
1307                                      found->section)) {
1308                yasm_error_set(YASM_ERROR_VALUE,
1309                               N_("vfollows loop between section `%s' and section `%s'"),
1310                               yasm_section_get_name(vma_group->section),
1311                               yasm_section_get_name(found->section));
1312                yasm_errwarn_propagate(errwarns, 0);
1313                bin_objfmt_cleanup(&info);
1314                return;
1315            }
1316
1317            /* Remove this section from main lma groups list */
1318            TAILQ_REMOVE(&info.vma_groups, vma_group, link);
1319            /* Add it after the section it's supposed to follow. */
1320            TAILQ_INSERT_TAIL(&found->follow_groups, vma_group, link);
1321        }
1322    }
1323
1324    /* Due to the combination of steps above, we now know that all top-level
1325     * groups have integer ivstart:
1326     * Vstart Vfollows Valign   Handled by
1327     *     No       No     No   group_assign_start_recurse()
1328     *     No       No    Yes   group_assign_start_recurse()
1329     *     No      Yes    -     vfollows loop (above)
1330     *    Yes      -      -     bin_lma_create_group()
1331     */
1332    TAILQ_FOREACH(vma_group, &info.vma_groups, link) {
1333        yasm_intnum_set(start, vma_group->bsd->ivstart);
1334        group_assign_vstart_recurse(vma_group, start, errwarns);
1335    }
1336
1337    /* Output map file */
1338    output_map(&info);
1339
1340    /* Ensure we don't have overlapping progbits LMAs.
1341     * Use a dumb O(N^2) algorithm as the number of sections is essentially
1342     * always low.
1343     */
1344    if (yasm_object_sections_traverse(object, NULL, check_lma_overlap)) {
1345        yasm_errwarn_propagate(errwarns, 0);
1346        yasm_intnum_destroy(start);
1347        bin_objfmt_cleanup(&info);
1348        return;
1349    }
1350
1351    /* Output sections */
1352    yasm_object_sections_traverse(object, &info, bin_objfmt_output_section);
1353
1354    /* Clean up */
1355    yasm_intnum_destroy(start);
1356    bin_objfmt_cleanup(&info);
1357}
1358
1359static void
1360bin_objfmt_destroy(yasm_objfmt *objfmt)
1361{
1362    yasm_objfmt_bin *objfmt_bin = (yasm_objfmt_bin *)objfmt;
1363    if (objfmt_bin->map_filename)
1364        yasm_xfree(objfmt_bin->map_filename);
1365    yasm_expr_destroy(objfmt_bin->org);
1366    yasm_xfree(objfmt);
1367}
1368
1369static void
1370define_section_symbol(yasm_symtab *symtab, yasm_section *sect,
1371                      const char *sectname, const char *suffix,
1372                      enum bin_ssym which, unsigned long line)
1373{
1374    yasm_symrec *sym;
1375    bin_symrec_data *bsymd = yasm_xmalloc(sizeof(bin_symrec_data));
1376    char *symname = yasm_xmalloc(8+strlen(sectname)+strlen(suffix)+1);
1377
1378    strcpy(symname, "section.");
1379    strcat(symname, sectname);
1380    strcat(symname, suffix);
1381
1382    bsymd->section = sect;
1383    bsymd->which = which;
1384
1385    sym = yasm_symtab_declare(symtab, symname, YASM_SYM_EXTERN, line);
1386    yasm_xfree(symname);
1387    yasm_symrec_add_data(sym, &bin_symrec_data_cb, bsymd);
1388}
1389
1390static void
1391bin_objfmt_init_new_section(yasm_section *sect, unsigned long line)
1392{
1393    yasm_object *object = yasm_section_get_object(sect);
1394    const char *sectname = yasm_section_get_name(sect);
1395    /*yasm_objfmt_bin *objfmt_bin = (yasm_objfmt_bin *)object->objfmt;*/
1396    bin_section_data *data;
1397
1398    data = yasm_xmalloc(sizeof(bin_section_data));
1399    data->bss = 0;
1400    data->align = NULL;
1401    data->valign = NULL;
1402    data->start = NULL;
1403    data->vstart = NULL;
1404    data->follows = NULL;
1405    data->vfollows = NULL;
1406    data->istart = NULL;
1407    data->ivstart = NULL;
1408    data->length = NULL;
1409    yasm_section_add_data(sect, &bin_section_data_cb, data);
1410
1411    define_section_symbol(object->symtab, sect, sectname, ".start",
1412                          SSYM_START, line);
1413    define_section_symbol(object->symtab, sect, sectname, ".vstart",
1414                          SSYM_VSTART, line);
1415    define_section_symbol(object->symtab, sect, sectname, ".length",
1416                          SSYM_LENGTH, line);
1417}
1418
1419static yasm_section *
1420bin_objfmt_add_default_section(yasm_object *object)
1421{
1422    yasm_section *retval;
1423    int isnew;
1424
1425    retval = yasm_object_get_general(object, ".text", 0, 1, 0, &isnew, 0);
1426    if (isnew)
1427        yasm_section_set_default(retval, 1);
1428    return retval;
1429}
1430
1431/* GAS-style flags */
1432static int
1433bin_helper_gasflags(void *obj, yasm_valparam *vp, unsigned long line, void *d,
1434                    /*@unused@*/ uintptr_t arg)
1435{
1436    /* TODO */
1437    return 0;
1438}
1439
1440static /*@observer@*/ /*@null@*/ yasm_section *
1441bin_objfmt_section_switch(yasm_object *object, yasm_valparamhead *valparams,
1442                          /*@unused@*/ /*@null@*/
1443                          yasm_valparamhead *objext_valparams,
1444                          unsigned long line)
1445{
1446    yasm_valparam *vp;
1447    yasm_section *retval;
1448    int isnew;
1449    int flags_override = 0;
1450    const char *sectname;
1451    bin_section_data *bsd = NULL;
1452
1453    struct bin_section_switch_data {
1454        /*@only@*/ /*@null@*/ char *follows;
1455        /*@only@*/ /*@null@*/ char *vfollows;
1456        /*@only@*/ /*@null@*/ yasm_expr *start;
1457        /*@only@*/ /*@null@*/ yasm_expr *vstart;
1458        /*@only@*/ /*@null@*/ yasm_intnum *align;
1459        /*@only@*/ /*@null@*/ yasm_intnum *valign;
1460        unsigned long bss;
1461        unsigned long code;
1462    } data;
1463
1464    static const yasm_dir_help help[] = {
1465        { "follows", 1, yasm_dir_helper_string,
1466          offsetof(struct bin_section_switch_data, follows), 0 },
1467        { "vfollows", 1, yasm_dir_helper_string,
1468          offsetof(struct bin_section_switch_data, vfollows), 0 },
1469        { "start", 1, yasm_dir_helper_expr,
1470          offsetof(struct bin_section_switch_data, start), 0 },
1471        { "vstart", 1, yasm_dir_helper_expr,
1472          offsetof(struct bin_section_switch_data, vstart), 0 },
1473        { "align", 1, yasm_dir_helper_intn,
1474          offsetof(struct bin_section_switch_data, align), 0 },
1475        { "valign", 1, yasm_dir_helper_intn,
1476          offsetof(struct bin_section_switch_data, valign), 0 },
1477        { "nobits", 0, yasm_dir_helper_flag_set,
1478          offsetof(struct bin_section_switch_data, bss), 1 },
1479        { "progbits", 0, yasm_dir_helper_flag_set,
1480          offsetof(struct bin_section_switch_data, bss), 0 },
1481        { "code", 0, yasm_dir_helper_flag_set,
1482          offsetof(struct bin_section_switch_data, code), 1 },
1483        { "data", 0, yasm_dir_helper_flag_set,
1484          offsetof(struct bin_section_switch_data, code), 0 },
1485        { "execute", 0, yasm_dir_helper_flag_set,
1486          offsetof(struct bin_section_switch_data, code), 1 },
1487        { "noexecute", 0, yasm_dir_helper_flag_set,
1488          offsetof(struct bin_section_switch_data, code), 0 },
1489        { "gasflags", 1, bin_helper_gasflags, 0, 0 }
1490    };
1491
1492    vp = yasm_vps_first(valparams);
1493    sectname = yasm_vp_string(vp);
1494    if (!sectname)
1495        return NULL;
1496    vp = yasm_vps_next(vp);
1497
1498    retval = yasm_object_find_general(object, sectname);
1499    if (retval) {
1500        bsd = yasm_section_get_data(retval, &bin_section_data_cb);
1501        assert(bsd != NULL);
1502        data.follows = bsd->follows;
1503        data.vfollows = bsd->vfollows;
1504        data.start = bsd->start;
1505        data.vstart = bsd->vstart;
1506        data.align = NULL;
1507        data.valign = NULL;
1508        data.bss = bsd->bss;
1509        data.code = yasm_section_is_code(retval);
1510    } else {
1511        data.follows = NULL;
1512        data.vfollows = NULL;
1513        data.start = NULL;
1514        data.vstart = NULL;
1515        data.align = NULL;
1516        data.valign = NULL;
1517        data.bss = strcmp(sectname, ".bss") == 0;
1518        data.code = strcmp(sectname, ".text") == 0;
1519    }
1520
1521    flags_override = yasm_dir_helper(object, vp, line, help, NELEMS(help),
1522                                     &data, yasm_dir_helper_valparam_warn);
1523    if (flags_override < 0)
1524        return NULL;    /* error occurred */
1525
1526    if (data.start && data.follows) {
1527        yasm_error_set(YASM_ERROR_GENERAL,
1528            N_("cannot combine `start' and `follows' section attributes"));
1529        return NULL;
1530    }
1531
1532    if (data.vstart && data.vfollows) {
1533        yasm_error_set(YASM_ERROR_GENERAL,
1534            N_("cannot combine `vstart' and `vfollows' section attributes"));
1535        return NULL;
1536    }
1537
1538    if (data.align) {
1539        unsigned long align = yasm_intnum_get_uint(data.align);
1540
1541        /* Alignments must be a power of two. */
1542        if (!is_exp2(align)) {
1543            yasm_error_set(YASM_ERROR_VALUE,
1544                           N_("argument to `%s' is not a power of two"),
1545                           "align");
1546            return NULL;
1547        }
1548    } else
1549        data.align = bsd ? bsd->align : NULL;
1550
1551    if (data.valign) {
1552        unsigned long valign = yasm_intnum_get_uint(data.valign);
1553
1554        /* Alignments must be a power of two. */
1555        if (!is_exp2(valign)) {
1556            yasm_error_set(YASM_ERROR_VALUE,
1557                           N_("argument to `%s' is not a power of two"),
1558                           "valign");
1559            return NULL;
1560        }
1561    } else
1562        data.valign = bsd ? bsd->valign : NULL;
1563
1564    retval = yasm_object_get_general(object, sectname, 0, (int)data.code,
1565                                     (int)data.bss, &isnew, line);
1566
1567    bsd = yasm_section_get_data(retval, &bin_section_data_cb);
1568
1569    if (isnew || yasm_section_is_default(retval)) {
1570        yasm_section_set_default(retval, 0);
1571    }
1572
1573    /* Update section flags */
1574    bsd->bss = data.bss;
1575    bsd->align = data.align;
1576    bsd->valign = data.valign;
1577    bsd->start = data.start;
1578    bsd->vstart = data.vstart;
1579    bsd->follows = data.follows;
1580    bsd->vfollows = data.vfollows;
1581
1582    return retval;
1583}
1584
1585static /*@observer@*/ /*@null@*/ yasm_symrec *
1586bin_objfmt_get_special_sym(yasm_object *object, const char *name,
1587                           const char *parser)
1588{
1589    return NULL;
1590}
1591
1592static void
1593bin_objfmt_dir_org(yasm_object *object,
1594                   /*@null@*/ yasm_valparamhead *valparams,
1595                   /*@unused@*/ /*@null@*/
1596                   yasm_valparamhead *objext_valparams, unsigned long line)
1597{
1598    yasm_objfmt_bin *objfmt_bin = (yasm_objfmt_bin *)object->objfmt;
1599    yasm_valparam *vp;
1600
1601    /* We only allow a single ORG in a program. */
1602    if (objfmt_bin->org) {
1603        yasm_error_set(YASM_ERROR_GENERAL, N_("program origin redefined"));
1604        return;
1605    }
1606
1607    /* ORG takes just a simple expression as param */
1608    vp = yasm_vps_first(valparams);
1609    objfmt_bin->org = yasm_vp_expr(vp, object->symtab, line);
1610    if (!objfmt_bin->org) {
1611        yasm_error_set(YASM_ERROR_SYNTAX,
1612                       N_("argument to ORG must be expression"));
1613        return;
1614    }
1615}
1616
1617struct bin_dir_map_data {
1618    unsigned long flags;
1619    /*@only@*/ /*@null@*/ char *filename;
1620};
1621
1622static int
1623dir_map_filename(void *obj, yasm_valparam *vp, unsigned long line, void *data)
1624{
1625    struct bin_dir_map_data *mdata = (struct bin_dir_map_data *)data;
1626    const char *filename;
1627
1628    if (mdata->filename) {
1629        yasm_warn_set(YASM_WARN_GENERAL, N_("map file already specified"));
1630        return 0;
1631    }
1632
1633    filename = yasm_vp_string(vp);
1634    if (!filename) {
1635        yasm_error_set(YASM_ERROR_SYNTAX,
1636                       N_("unexpected expression in [map]"));
1637        return -1;
1638    }
1639    mdata->filename = yasm__xstrdup(filename);
1640
1641    return 1;
1642}
1643
1644static void
1645bin_objfmt_dir_map(yasm_object *object,
1646                   /*@null@*/ yasm_valparamhead *valparams,
1647                   /*@unused@*/ /*@null@*/
1648                   yasm_valparamhead *objext_valparams, unsigned long line)
1649{
1650    yasm_objfmt_bin *objfmt_bin = (yasm_objfmt_bin *)object->objfmt;
1651
1652    struct bin_dir_map_data data;
1653
1654    static const yasm_dir_help help[] = {
1655        { "all", 0, yasm_dir_helper_flag_or,
1656          offsetof(struct bin_dir_map_data, flags),
1657          MAP_BRIEF|MAP_SECTIONS|MAP_SYMBOLS },
1658        { "brief", 0, yasm_dir_helper_flag_or,
1659          offsetof(struct bin_dir_map_data, flags), MAP_BRIEF },
1660        { "sections", 0, yasm_dir_helper_flag_or,
1661          offsetof(struct bin_dir_map_data, flags), MAP_SECTIONS },
1662        { "segments", 0, yasm_dir_helper_flag_or,
1663          offsetof(struct bin_dir_map_data, flags), MAP_SECTIONS },
1664        { "symbols", 0, yasm_dir_helper_flag_or,
1665          offsetof(struct bin_dir_map_data, flags), MAP_SYMBOLS }
1666    };
1667
1668    data.flags = objfmt_bin->map_flags | MAP_NONE;
1669    data.filename = objfmt_bin->map_filename;
1670
1671    if (valparams && yasm_dir_helper(object, yasm_vps_first(valparams), line, help,
1672                                     NELEMS(help), &data, dir_map_filename) < 0)
1673        return;     /* error occurred */
1674
1675    objfmt_bin->map_flags = data.flags;
1676    objfmt_bin->map_filename = data.filename;
1677}
1678
1679static void
1680bin_section_data_destroy(void *data)
1681{
1682    bin_section_data *bsd = (bin_section_data *)data;
1683    if (bsd->start)
1684        yasm_expr_destroy(bsd->start);
1685    if (bsd->vstart)
1686        yasm_expr_destroy(bsd->vstart);
1687    if (bsd->follows)
1688        yasm_xfree(bsd->follows);
1689    if (bsd->vfollows)
1690        yasm_xfree(bsd->vfollows);
1691    if (bsd->istart)
1692        yasm_intnum_destroy(bsd->istart);
1693    if (bsd->ivstart)
1694        yasm_intnum_destroy(bsd->ivstart);
1695    if (bsd->length)
1696        yasm_intnum_destroy(bsd->length);
1697    yasm_xfree(data);
1698}
1699
1700static void
1701bin_section_data_print(void *data, FILE *f, int indent_level)
1702{
1703    bin_section_data *bsd = (bin_section_data *)data;
1704
1705    fprintf(f, "%*sbss=%d\n", indent_level, "", bsd->bss);
1706
1707    fprintf(f, "%*salign=", indent_level, "");
1708    if (bsd->align)
1709        yasm_intnum_print(bsd->align, f);
1710    else
1711        fprintf(f, "(nil)");
1712    fprintf(f, "\n%*svalign=", indent_level, "");
1713    if (bsd->valign)
1714        yasm_intnum_print(bsd->valign, f);
1715    else
1716        fprintf(f, "(nil)");
1717
1718    fprintf(f, "\n%*sstart=", indent_level, "");
1719    yasm_expr_print(bsd->start, f);
1720    fprintf(f, "\n%*svstart=", indent_level, "");
1721    yasm_expr_print(bsd->vstart, f);
1722
1723    fprintf(f, "\n%*sfollows=", indent_level, "");
1724    if (bsd->follows)
1725        fprintf(f, "\"%s\"", bsd->follows);
1726    else
1727        fprintf(f, "(nil)");
1728    fprintf(f, "\n%*svfollows=", indent_level, "");
1729    if (bsd->vfollows)
1730        fprintf(f, "\"%s\"", bsd->vfollows);
1731    else
1732        fprintf(f, "(nil)");
1733
1734    fprintf(f, "\n%*sistart=", indent_level, "");
1735    if (bsd->istart)
1736        yasm_intnum_print(bsd->istart, f);
1737    else
1738        fprintf(f, "(nil)");
1739    fprintf(f, "\n%*sivstart=", indent_level, "");
1740    if (bsd->ivstart)
1741        yasm_intnum_print(bsd->ivstart, f);
1742    else
1743        fprintf(f, "(nil)");
1744
1745    fprintf(f, "\n%*slength=", indent_level, "");
1746    if (bsd->length)
1747        yasm_intnum_print(bsd->length, f);
1748    else
1749        fprintf(f, "(nil)");
1750    fprintf(f, "\n");
1751}
1752
1753static void
1754bin_symrec_data_destroy(void *data)
1755{
1756    yasm_xfree(data);
1757}
1758
1759static void
1760bin_symrec_data_print(void *data, FILE *f, int indent_level)
1761{
1762    bin_symrec_data *bsymd = (bin_symrec_data *)data;
1763
1764    fprintf(f, "%*ssection=\"%s\"\n", indent_level, "",
1765            yasm_section_get_name(bsymd->section));
1766    fprintf(f, "%*swhich=", indent_level, "");
1767    switch (bsymd->which) {
1768        case SSYM_START: fprintf(f, "START"); break;
1769        case SSYM_VSTART: fprintf(f, "VSTART"); break;
1770        case SSYM_LENGTH: fprintf(f, "LENGTH"); break;
1771    }
1772    fprintf(f, "\n");
1773}
1774
1775
1776/* Define valid debug formats to use with this object format */
1777static const char *bin_objfmt_dbgfmt_keywords[] = {
1778    "null",
1779    NULL
1780};
1781
1782static const yasm_directive bin_objfmt_directives[] = {
1783    { "org",    "nasm", bin_objfmt_dir_org,     YASM_DIR_ARG_REQUIRED },
1784    { "map",    "nasm", bin_objfmt_dir_map,     YASM_DIR_ANY },
1785    { NULL, NULL, NULL, 0 }
1786};
1787
1788static const char *bin_nasm_stdmac[] = {
1789    "%imacro org 1+.nolist",
1790    "[org %1]",
1791    "%endmacro",
1792    NULL
1793};
1794
1795static const yasm_stdmac bin_objfmt_stdmacs[] = {
1796    { "nasm", "nasm", bin_nasm_stdmac },
1797    { "tasm", "tasm", bin_nasm_stdmac },
1798    { NULL, NULL, NULL }
1799};
1800
1801/* Define objfmt structure -- see objfmt.h for details */
1802yasm_objfmt_module yasm_bin_LTX_objfmt = {
1803    "Flat format binary",
1804    "bin",
1805    NULL,
1806    16,
1807    0,
1808    bin_objfmt_dbgfmt_keywords,
1809    "null",
1810    bin_objfmt_directives,
1811    bin_objfmt_stdmacs,
1812    bin_objfmt_create,
1813    bin_objfmt_output,
1814    bin_objfmt_destroy,
1815    bin_objfmt_add_default_section,
1816    bin_objfmt_init_new_section,
1817    bin_objfmt_section_switch,
1818    bin_objfmt_get_special_sym
1819};
1820
1821#define EXE_HEADER_SIZE 0x200
1822
1823/* DOS .EXE binaries are just raw binaries with a header */
1824yasm_objfmt_module yasm_dosexe_LTX_objfmt;
1825
1826static yasm_objfmt *
1827dosexe_objfmt_create(yasm_object *object)
1828{
1829    yasm_objfmt_bin *objfmt_bin = (yasm_objfmt_bin *) bin_objfmt_create(object);
1830    objfmt_bin->objfmt.module = &yasm_dosexe_LTX_objfmt;
1831    return (yasm_objfmt *)objfmt_bin;
1832}
1833
1834static unsigned long
1835get_sym(yasm_object *object, const char *name) {
1836    yasm_symrec *symrec = yasm_symtab_get(object->symtab, name);
1837    yasm_bytecode *prevbc;
1838    if (!symrec)
1839        return 0;
1840    if (!yasm_symrec_get_label(symrec, &prevbc))
1841        return 0;
1842    return prevbc->offset + prevbc->len;
1843}
1844
1845static void
1846dosexe_objfmt_output(yasm_object *object, FILE *f, /*@unused@*/ int all_syms,
1847                  yasm_errwarns *errwarns)
1848{
1849    unsigned long tot_size, size, bss_size;
1850    unsigned long start, bss;
1851    unsigned char c;
1852
1853    fseek(f, EXE_HEADER_SIZE, SEEK_SET);
1854
1855    bin_objfmt_output(object, f, all_syms, errwarns);
1856
1857    tot_size = ftell(f);
1858
1859    /* if there is a __bss_start symbol, data after it is 0, no need to write
1860     * it.  */
1861    bss = get_sym(object, "__bss_start");
1862    if (bss)
1863        size = bss;
1864    else
1865        size = tot_size;
1866    bss_size = tot_size - size;
1867#ifdef HAVE_FTRUNCATE
1868    if (size != tot_size)
1869        ftruncate(fileno(f), EXE_HEADER_SIZE + size);
1870#endif
1871    fseek(f, 0, SEEK_SET);
1872
1873    /* magic */
1874    fwrite("MZ", 1, 2, f);
1875
1876    /* file size */
1877    c = size & 0xff;
1878    fwrite(&c, 1, 1, f);
1879    c = !!(size & 0x100);
1880    fwrite(&c, 1, 1, f);
1881    c = ((size + 511) >> 9) & 0xff;
1882    fwrite(&c, 1, 1, f);
1883    c = ((size + 511) >> 17) & 0xff;
1884    fwrite(&c, 1, 1, f);
1885
1886    /* relocation # */
1887    c = 0;
1888    fwrite(&c, 1, 1, f);
1889    fwrite(&c, 1, 1, f);
1890
1891    /* header size */
1892    c = EXE_HEADER_SIZE / 16;
1893    fwrite(&c, 1, 1, f);
1894    c = 0;
1895    fwrite(&c, 1, 1, f);
1896
1897    /* minimum paragraph # */
1898    bss_size = (bss_size + 15) >> 4;
1899    c = bss_size & 0xff;
1900    fwrite(&c, 1, 1, f);
1901    c = (bss_size >> 8) & 0xff;
1902    fwrite(&c, 1, 1, f);
1903
1904    /* maximum paragraph # */
1905    c = 0xFF;
1906    fwrite(&c, 1, 1, f);
1907    fwrite(&c, 1, 1, f);
1908
1909    /* relative value of stack segment */
1910    c = 0;
1911    fwrite(&c, 1, 1, f);
1912    fwrite(&c, 1, 1, f);
1913
1914    /* SP at start */
1915    c = 0;
1916    fwrite(&c, 1, 1, f);
1917    fwrite(&c, 1, 1, f);
1918
1919    /* header checksum */
1920    c = 0;
1921    fwrite(&c, 1, 1, f);
1922    fwrite(&c, 1, 1, f);
1923
1924    /* IP at start */
1925    start = get_sym(object, "start");
1926    if (!start) {
1927        yasm_error_set(YASM_ERROR_GENERAL,
1928                N_("%s: could not find symbol `start'"));
1929        return;
1930    }
1931    c = start & 0xff;
1932    fwrite(&c, 1, 1, f);
1933    c = (start >> 8) & 0xff;
1934    fwrite(&c, 1, 1, f);
1935
1936    /* CS start */
1937    c = 0;
1938    fwrite(&c, 1, 1, f);
1939    fwrite(&c, 1, 1, f);
1940
1941    /* reloc start */
1942    c = 0x22;
1943    fwrite(&c, 1, 1, f);
1944    c = 0;
1945    fwrite(&c, 1, 1, f);
1946
1947    /* Overlay number */
1948    c = 0;
1949    fwrite(&c, 1, 1, f);
1950    fwrite(&c, 1, 1, f);
1951}
1952
1953
1954/* Define objfmt structure -- see objfmt.h for details */
1955yasm_objfmt_module yasm_dosexe_LTX_objfmt = {
1956    "DOS .EXE format binary",
1957    "dosexe",
1958    "exe",
1959    16,
1960    0,
1961    bin_objfmt_dbgfmt_keywords,
1962    "null",
1963    bin_objfmt_directives,
1964    bin_objfmt_stdmacs,
1965    dosexe_objfmt_create,
1966    dosexe_objfmt_output,
1967    bin_objfmt_destroy,
1968    bin_objfmt_add_default_section,
1969    bin_objfmt_init_new_section,
1970    bin_objfmt_section_switch,
1971    bin_objfmt_get_special_sym
1972};
1973