1/*
2 * NASM-style list format
3 *
4 *  Copyright (C) 2004-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#include <libyasm.h>
30
31/* NOTE: For this code to generate relocation information, the relocations
32 * have to be added by the object format to each section in program source
33 * order.
34 *
35 * This should not be an issue, as program source order == section bytecode
36 * order, so unless the object formats are very obtuse with their bytecode
37 * iteration, this should just happen.
38 */
39
40#define REGULAR_BUF_SIZE    1024
41
42yasm_listfmt_module yasm_nasm_LTX_listfmt;
43
44typedef struct sectreloc {
45    /*@reldef@*/ SLIST_ENTRY(sectreloc) link;
46    yasm_section *sect;
47    /*@null@*/ yasm_reloc *next_reloc;  /* next relocation in section */
48    unsigned long next_reloc_addr;
49} sectreloc;
50
51typedef struct bcreloc {
52    /*@reldef@*/ STAILQ_ENTRY(bcreloc) link;
53    unsigned long offset;       /* start of reloc from start of bytecode */
54    size_t size;                /* size of reloc in bytes */
55    int rel;                    /* PC/IP-relative or "absolute" */
56} bcreloc;
57
58typedef struct nasm_listfmt_output_info {
59    yasm_arch *arch;
60    /*@reldef@*/ STAILQ_HEAD(bcrelochead, bcreloc) bcrelocs;
61    /*@null@*/ yasm_reloc *next_reloc;  /* next relocation in section */
62    unsigned long next_reloc_addr;
63} nasm_listfmt_output_info;
64
65
66static /*@null@*/ /*@only@*/ yasm_listfmt *
67nasm_listfmt_create(const char *in_filename, const char *obj_filename)
68{
69    yasm_listfmt_base *listfmt = yasm_xmalloc(sizeof(yasm_listfmt_base));
70    listfmt->module = &yasm_nasm_LTX_listfmt;
71    return (yasm_listfmt *)listfmt;
72}
73
74static void
75nasm_listfmt_destroy(/*@only@*/ yasm_listfmt *listfmt)
76{
77    yasm_xfree(listfmt);
78}
79
80static int
81nasm_listfmt_output_value(yasm_value *value, unsigned char *buf,
82                          unsigned int destsize, unsigned long offset,
83                          yasm_bytecode *bc, int warn, /*@null@*/ void *d)
84{
85    /*@null@*/ nasm_listfmt_output_info *info = (nasm_listfmt_output_info *)d;
86    /*@dependent@*/ /*@null@*/ yasm_intnum *intn;
87    unsigned int valsize = value->size;
88
89    assert(info != NULL);
90
91    /* Output */
92    switch (yasm_value_output_basic(value, buf, destsize, bc, warn,
93                                    info->arch)) {
94        case -1:
95            return 1;
96        case 0:
97            break;
98        default:
99            return 0;
100    }
101
102    /* Generate reloc if needed */
103    if (info->next_reloc && info->next_reloc_addr == bc->offset+offset) {
104        bcreloc *reloc = yasm_xmalloc(sizeof(bcreloc));
105        reloc->offset = offset;
106        reloc->size = destsize;
107        reloc->rel = value->curpos_rel;
108        STAILQ_INSERT_TAIL(&info->bcrelocs, reloc, link);
109
110        /* Get next reloc's info */
111        info->next_reloc = yasm_section_reloc_next(info->next_reloc);
112        if (info->next_reloc) {
113            yasm_intnum *addr;
114            yasm_symrec *sym;
115            yasm_reloc_get(info->next_reloc, &addr, &sym);
116            info->next_reloc_addr = yasm_intnum_get_uint(addr);
117        }
118    }
119
120    if (value->abs) {
121        intn = yasm_expr_get_intnum(&value->abs, 0);
122        if (intn)
123            return yasm_arch_intnum_tobytes(info->arch, intn, buf, destsize,
124                                            valsize, 0, bc, 0);
125        else {
126            yasm_error_set(YASM_ERROR_TOO_COMPLEX,
127                           N_("relocation too complex"));
128            return 1;
129        }
130    } else {
131        int retval;
132        intn = yasm_intnum_create_uint(0);
133        retval = yasm_arch_intnum_tobytes(info->arch, intn, buf, destsize,
134                                          valsize, 0, bc, 0);
135        yasm_intnum_destroy(intn);
136        return retval;
137    }
138
139    return 0;
140}
141
142static void
143nasm_listfmt_output(yasm_listfmt *listfmt, FILE *f, yasm_linemap *linemap,
144                    yasm_arch *arch)
145{
146    yasm_bytecode *bc;
147    const char *source;
148    unsigned long line = 1;
149    unsigned long listline = 1;
150    /*@only@*/ unsigned char *buf;
151    nasm_listfmt_output_info info;
152    /*@reldef@*/ SLIST_HEAD(sectrelochead, sectreloc) reloc_hist;
153    /*@null@*/ sectreloc *last_hist = NULL;
154    /*@null@*/ bcreloc *reloc = NULL;
155    yasm_section *sect;
156
157    SLIST_INIT(&reloc_hist);
158
159    info.arch = arch;
160
161    buf = yasm_xmalloc(REGULAR_BUF_SIZE);
162
163    while (!yasm_linemap_get_source(linemap, line, &bc, &source)) {
164        if (!bc) {
165            fprintf(f, "%6lu %*s%s\n", listline++, 32, "", source);
166        } else {
167            /* get the next relocation for the bytecode's section */
168            sect = yasm_bc_get_section(bc);
169            if (!last_hist || last_hist->sect != sect) {
170                int found = 0;
171
172                /* look through reloc_hist for matching section */
173                SLIST_FOREACH(last_hist, &reloc_hist, link) {
174                    if (last_hist->sect == sect) {
175                        found = 1;
176                        break;
177                    }
178                }
179
180                if (!found) {
181                    /* not found, add to list*/
182                    last_hist = yasm_xmalloc(sizeof(sectreloc));
183                    last_hist->sect = sect;
184                    last_hist->next_reloc = yasm_section_relocs_first(sect);
185
186                    if (last_hist->next_reloc) {
187                        yasm_intnum *addr;
188                        yasm_symrec *sym;
189                        yasm_reloc_get(last_hist->next_reloc, &addr, &sym);
190                        last_hist->next_reloc_addr =
191                            yasm_intnum_get_uint(addr);
192                    }
193
194                    SLIST_INSERT_HEAD(&reloc_hist, last_hist, link);
195                }
196            }
197
198            info.next_reloc = last_hist->next_reloc;
199            info.next_reloc_addr = last_hist->next_reloc_addr;
200            STAILQ_INIT(&info.bcrelocs);
201
202            /* loop over bytecodes on this line (usually only one) */
203            while (bc && bc->line == line) {
204                /*@null@*/ /*@only@*/ unsigned char *bigbuf;
205                unsigned long size = REGULAR_BUF_SIZE;
206                long multiple;
207                unsigned long offset = bc->offset;
208                unsigned char *origp, *p;
209                int gap;
210
211                /* convert bytecode into bytes, recording relocs along the
212                 * way
213                 */
214                bigbuf = yasm_bc_tobytes(bc, buf, &size, &gap, &info,
215                                         nasm_listfmt_output_value, NULL);
216                yasm_bc_get_multiple(bc, &multiple, 1);
217                if (multiple <= 0)
218                    size = 0;
219                else
220                    size /= multiple;
221
222                /* output bytes with reloc information */
223                origp = bigbuf ? bigbuf : buf;
224                p = origp;
225                reloc = STAILQ_FIRST(&info.bcrelocs);
226                if (gap) {
227                    fprintf(f, "%6lu %08lX <gap>%*s%s\n", listline++, offset,
228                            18, "", source ? source : "");
229                } else while (size > 0) {
230                    int i;
231
232                    fprintf(f, "%6lu %08lX ", listline++, offset);
233                    for (i=0; i<18 && size > 0; size--) {
234                        if (reloc && (unsigned long)(p-origp) ==
235                                     reloc->offset) {
236                            fprintf(f, "%c", reloc->rel ? '(' : '[');
237                            i++;
238                        }
239                        fprintf(f, "%02X", *(p++));
240                        i+=2;
241                        if (reloc && (unsigned long)(p-origp) ==
242                                     reloc->offset+reloc->size) {
243                            fprintf(f, "%c", reloc->rel ? ')' : ']');
244                            i++;
245                            reloc = STAILQ_NEXT(reloc, link);
246                        }
247                    }
248                    if (size > 0)
249                        fprintf(f, "-");
250                    else {
251                        if (multiple > 1) {
252                            fprintf(f, "<rept>");
253                            i += 6;
254                        }
255                        fprintf(f, "%*s", 18-i+1, "");
256                    }
257                    if (source) {
258                        fprintf(f, "    %s", source);
259                        source = NULL;
260                    }
261                    fprintf(f, "\n");
262                }
263
264                if (bigbuf)
265                    yasm_xfree(bigbuf);
266                bc = STAILQ_NEXT(bc, link);
267            }
268
269            /* delete bcrelocs (newly generated next bytecode if any) */
270            reloc = STAILQ_FIRST(&info.bcrelocs);
271            while (reloc) {
272                bcreloc *reloc2 = STAILQ_NEXT(reloc, link);
273                yasm_xfree(reloc);
274                reloc = reloc2;
275            }
276
277            /* save reloc context */
278            last_hist->next_reloc = info.next_reloc;
279            last_hist->next_reloc_addr = info.next_reloc_addr;
280        }
281        line++;
282    }
283
284    /* delete reloc history */
285    while (!SLIST_EMPTY(&reloc_hist)) {
286        last_hist = SLIST_FIRST(&reloc_hist);
287        SLIST_REMOVE_HEAD(&reloc_hist, link);
288        yasm_xfree(last_hist);
289    }
290
291    yasm_xfree(buf);
292}
293
294/* Define listfmt structure -- see listfmt.h for details */
295yasm_listfmt_module yasm_nasm_LTX_listfmt = {
296    "NASM-style list format",
297    "nasm",
298    nasm_listfmt_create,
299    nasm_listfmt_destroy,
300    nasm_listfmt_output
301};
302