summaryrefslogtreecommitdiff
path: root/src/libexec/xcoff.c
blob: a9a6da48bb166ac3efe2b248f394fad1e5beef16 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
/*
 * <xcoff.c>
 *
 * Open Hack'Ware BIOS XCOFF executable file loader
 * 
 * Copyright (c) 2004-2005 Jocelyn Mayer
 * 
 *   This program is free software; you can redistribute it and/or
 *   modify it under the terms of the GNU General Public License V2
 *   as published by the Free Software Foundation
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include <stdlib.h>
#include <stdio.h>
#include "bios.h"
#include "exec.h"

uint32_t fs_inode_get_size (inode_t *inode);

/* XCOFF executable loader */
typedef struct COFF_filehdr_t {
    uint16_t f_magic;	/* magic number			*/
    uint16_t f_nscns;	/* number of sections		*/
    uint32_t f_timdat;	/* time & date stamp		*/
    uint32_t f_symptr;	/* file pointer to symtab	*/
    uint32_t f_nsyms;	/* number of symtab entries	*/
    uint16_t f_opthdr;	/* sizeof(optional hdr)		*/
    uint16_t f_flags;	/* flags			*/
} COFF_filehdr_t;

/* IBM RS/6000 */
#define U802WRMAGIC     0730    /* writeable text segments **chh**      */
#define U802ROMAGIC     0735    /* readonly sharable text segments      */
#define U802TOCMAGIC    0737    /* readonly text segments and TOC       */

/*
 *   Bits for f_flags:
 *
 *	F_RELFLG	relocation info stripped from file
 *	F_EXEC		file is executable  (i.e. no unresolved external
 *			references)
 *	F_LNNO		line numbers stripped from file
 *	F_LSYMS		local symbols stripped from file
 *	F_MINMAL	this is a minimal object file (".m") output of fextract
 *	F_UPDATE	this is a fully bound update file, output of ogen
 *	F_SWABD		this file has had its bytes swabbed (in names)
 *	F_AR16WR	this file has the byte ordering of an AR16WR
 *			(e.g. 11/70) machine
 *	F_AR32WR	this file has the byte ordering of an AR32WR machine
 *			(e.g. vax and iNTEL 386)
 *	F_AR32W		this file has the byte ordering of an AR32W machine
 *			(e.g. 3b,maxi)
 *	F_PATCH		file contains "patch" list in optional header
 *	F_NODF		(minimal file only) no decision functions for
 *			replaced functions
 */

#define  COFF_F_RELFLG		0000001
#define  COFF_F_EXEC		0000002
#define  COFF_F_LNNO		0000004
#define  COFF_F_LSYMS		0000010
#define  COFF_F_MINMAL		0000020
#define  COFF_F_UPDATE		0000040
#define  COFF_F_SWABD		0000100
#define  COFF_F_AR16WR		0000200
#define  COFF_F_AR32WR		0000400
#define  COFF_F_AR32W		0001000
#define  COFF_F_PATCH		0002000
#define  COFF_F_NODF		0002000

typedef struct COFF_aouthdr_t {
    uint16_t magic;      /* type of file			  */
    uint16_t vstamp;     /* version stamp		          */
    uint32_t tsize;      /* text size in bytes, padded to FW bdry */
    uint32_t dsize;      /* initialized data "  "	          */
    uint32_t bsize;      /* uninitialized data "   "	          */
    uint32_t entry;	 /* entry pt.			          */
    uint32_t text_start; /* base of text used for this file       */
    uint32_t data_start; /* base of data used for this file       */
    uint32_t o_toc;	 /* address of TOC                        */
    uint16_t o_snentry;	 /* section number of entry point         */
    uint16_t o_sntext;	 /* section number of .text section       */
    uint16_t o_sndata;	 /* section number of .data section       */
    uint16_t o_sntoc;	 /* section number of TOC                 */
    uint16_t o_snloader; /* section number of .loader section     */
    uint16_t o_snbss;	 /* section number of .bss section        */
    uint16_t o_algntext; /* .text alignment                       */
    uint16_t o_algndata; /* .data alignment                       */
    uint16_t o_modtype;	 /* module type (??)                      */
    uint16_t o_cputype;	 /* cpu type                              */
    uint32_t o_maxstack; /* max stack size (??)                   */
    uint32_t o_maxdata;	 /* max data size (??)                    */
    char o_resv2[12];	 /* reserved                              */
} COFF_aouthdr_t;

#define AOUT_MAGIC	0x010b

typedef struct COFF_scnhdr_t {
    char s_name[8];	/* section name			    */
    uint32_t s_paddr;	/* physical address, aliased s_nlib */
    uint32_t s_vaddr;	/* virtual address		    */
    uint32_t s_size;	/* section size			    */
    uint32_t s_scnptr;	/* file ptr to raw data for section */
    uint32_t s_relptr;	/* file ptr to relocation	    */
    uint32_t s_lnnoptr;	/* file ptr to line numbers	    */
    uint16_t s_nreloc;	/* number of relocation entries	    */
    uint16_t s_nlnno;	/* number of line number entries    */
    uint32_t s_flags;	/* flags			    */
} COFF_scnhdr_t;

int exec_load_xcoff (inode_t *file, void **dest, void **entry, void **end,
                     uint32_t loffset)
{
    COFF_filehdr_t fhdr;
    COFF_aouthdr_t ahdr;
    COFF_scnhdr_t shdr;
    void *first, *last;
    uint32_t offset;
    int i;

    file_seek(file, loffset);
    if (fs_read(file, &fhdr, sizeof(COFF_filehdr_t)) < 0) {
        ERROR("Cannot load first bloc of file...\n");
        return -1;
    }
    if (fhdr.f_magic != U802WRMAGIC && fhdr.f_magic != U802ROMAGIC &&
        fhdr.f_magic != U802TOCMAGIC && fhdr.f_magic != 0x01DF) {
        DPRINTF("Not a XCOFF file %02x %08x\n", fhdr.f_magic,
                *(uint32_t *)&fhdr.f_magic);
        return -2;
    }
    if (fhdr.f_magic != 0x01DF && (fhdr.f_flags & COFF_F_EXEC) == 0) {
        ERROR("Not an executable XCOFF file %02x\n", fhdr.f_flags);
        return -2;
    }
    if (fhdr.f_opthdr != sizeof(COFF_aouthdr_t)) {
        ERROR("AOUT optional error size missmactch in XCOFF file\n");
        return -2;
    }
    if (fs_read(file, &ahdr, sizeof(COFF_aouthdr_t)) < 0) {
        ERROR("Cannot load XCOFF AOUT header...\n");
        return -1;
    }
    if (ahdr.magic != AOUT_MAGIC) {
        ERROR("Invalid AOUT optional header\n");
        return -2;
    }
#if 0 // XXX: buggy: this makes NetBSD fail to boot
    if (fhdr.f_magic == 0x01DF) {
        /* Load embedded file */
        return _bootfile_load(file, dest, entry, end, loffset +
                              sizeof(COFF_filehdr_t) + sizeof(COFF_aouthdr_t) +
                              (fhdr.f_nscns * sizeof(COFF_scnhdr_t)),
                              -1);
    }
#endif
    *entry = (void *)ahdr.entry + 0xC;
    last = NULL;
    first = last - 4;
    offset = sizeof(COFF_filehdr_t) + sizeof(COFF_aouthdr_t);
    DPRINTF("XCOFF file with %d sections entry:%p\n", fhdr.f_nscns, *entry);
    for (i = 0; i < fhdr.f_nscns; i++) {
        DPRINTF("Read next header (%0x)\n", offset);
        file_seek(file, offset + loffset);
        if (fs_read(file, &shdr, sizeof(COFF_scnhdr_t)) < 0) {
            ERROR("Cannot load section header %d...\n", i);
            return -1;
        }
	if (strcmp(shdr.s_name, ".text") == 0 ||
            strcmp(shdr.s_name, ".data") == 0) {
            if ((void *)shdr.s_vaddr < first)
                first = (void *)shdr.s_vaddr;
            if ((void *)shdr.s_vaddr > last)
                last = (void *)shdr.s_vaddr;
            DPRINTF("Load '%s' section from %0x %0x to %0x (%0x)\n",
                    shdr.s_name, offset, shdr.s_scnptr,
                    shdr.s_vaddr, shdr.s_size);
#if 0
            if (shdr.s_scnptr + shdr.s_size > fs_inode_get_size(file)) {
                ERROR("Section %d data offset > file size\n", i);
                return -1;
            }
#endif
            file_seek(file, shdr.s_scnptr + loffset);
            set_loadinfo((void *)first, last - first);
            if (fs_read(file, (void *)shdr.s_vaddr, shdr.s_size) < 0) {
                ERROR("Cannot load section %d...\n", i);
                return -1;
            }
        } else if (strcmp(shdr.s_name, ".bss") == 0) {
            if ((void *)shdr.s_vaddr < first)
                first = (void *)shdr.s_vaddr;
            if ((void *)shdr.s_vaddr > last)
                last = (void *)shdr.s_vaddr;
            DPRINTF("Erase '%s' section at %0x size: %0x\n",
                    shdr.s_name, shdr.s_vaddr, shdr.s_size);
            memset((void *)shdr.s_vaddr, 0, shdr.s_size);
        } else {
            DPRINTF("Skip '%s' section\n", shdr.s_name);
        }
        offset += sizeof(COFF_scnhdr_t);
    }
    *dest = first;
    *end = last;

    return 0;
}