mirror of
https://github.com/netwide-assembler/nasm.git
synced 2024-11-27 08:10:07 +08:00
2e53f27e9d
There is absolutely no reason not to include <string.h> globally, and with the inline function for mempcpy() we need it there anyway. Signed-off-by: H. Peter Anvin <hpa@zytor.com>
431 lines
10 KiB
C
431 lines
10 KiB
C
/* ----------------------------------------------------------------------- *
|
|
*
|
|
* Copyright 1996-2009 The NASM Authors - All Rights Reserved
|
|
* See the file AUTHORS included with the NASM distribution for
|
|
* the specific copyright holders.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following
|
|
* conditions are met:
|
|
*
|
|
* * Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* * Redistributions in binary form must reproduce the above
|
|
* copyright notice, this list of conditions and the following
|
|
* disclaimer in the documentation and/or other materials provided
|
|
* with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
|
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
|
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
|
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
* ----------------------------------------------------------------------- */
|
|
|
|
/*
|
|
* rdf2bin.c - convert an RDOFF object file to flat binary
|
|
*/
|
|
|
|
#include "compiler.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <ctype.h>
|
|
#include <errno.h>
|
|
|
|
#include "rdfload.h"
|
|
#include "nasmlib.h"
|
|
|
|
const char *progname;
|
|
|
|
static uint32_t origin = 0;
|
|
static bool origin_def = false;
|
|
static uint32_t align = 16;
|
|
static bool align_def = false;
|
|
|
|
struct output_format {
|
|
const char *name;
|
|
const char *mode;
|
|
int (*init)(FILE *f);
|
|
int (*output)(FILE *f, void *data, uint32_t bytes, uint32_t where);
|
|
int (*fini)(FILE *f);
|
|
};
|
|
|
|
static int null_init_fini(FILE *f)
|
|
{
|
|
(void)f;
|
|
return 0;
|
|
}
|
|
|
|
static int com_init(FILE *f)
|
|
{
|
|
(void)f;
|
|
if (!origin_def)
|
|
origin = 0x100;
|
|
return 0;
|
|
}
|
|
|
|
static int output_bin(FILE *f, void *data, uint32_t bytes, uint32_t where)
|
|
{
|
|
static uint32_t offset = 0; /* Current file offset, if applicable */
|
|
size_t pad;
|
|
|
|
if (where-origin < offset) {
|
|
fprintf(stderr, "%s: internal error: backwards movement\n", progname);
|
|
exit(1);
|
|
}
|
|
|
|
pad = (where-origin) - offset;
|
|
fwritezero(pad, f);
|
|
offset += pad;
|
|
|
|
if (fwrite(data, 1, bytes, f) != bytes)
|
|
return -1;
|
|
offset += bytes;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int write_ith_record(FILE *f, unsigned int len, uint16_t addr,
|
|
uint8_t type, void *data)
|
|
{
|
|
char buf[1+2+4+2+255*2+2+2];
|
|
char *p = buf;
|
|
uint8_t csum, *dptr = data;
|
|
unsigned int i;
|
|
|
|
if (len > 255) {
|
|
fprintf(stderr, "%s: internal error: invalid ith record size\n",
|
|
progname);
|
|
exit(1);
|
|
}
|
|
|
|
csum = len + addr + (addr >> 8) + type;
|
|
for (i = 0; i < len; i++)
|
|
csum += dptr[i];
|
|
csum = -csum;
|
|
|
|
p += sprintf(p, ":%02X%04X%02X", len, addr, type);
|
|
for (i = 0; i < len; i++)
|
|
p += sprintf(p, "%02X", dptr[i]);
|
|
p += sprintf(p, "%02X\n", csum);
|
|
|
|
if (fwrite(buf, 1, p-buf, f) != (size_t)(p-buf))
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int output_ith(FILE *f, void *data, uint32_t bytes, uint32_t where)
|
|
{
|
|
static uint32_t last = 0; /* Last address written */
|
|
uint8_t abuf[2];
|
|
uint8_t *dbuf = data;
|
|
uint32_t chunk;
|
|
|
|
while (bytes) {
|
|
if ((where ^ last) & ~0xffff) {
|
|
abuf[0] = where >> 24;
|
|
abuf[1] = where >> 16;
|
|
if (write_ith_record(f, 2, 0, 4, abuf))
|
|
return -1;
|
|
}
|
|
|
|
/* Output up to 32 bytes, but always end on an aligned boundary */
|
|
chunk = 32 - (where & 31);
|
|
if (bytes < chunk)
|
|
chunk = bytes;
|
|
|
|
if (write_ith_record(f, chunk, (uint16_t)where, 0, dbuf))
|
|
return -1;
|
|
|
|
dbuf += chunk;
|
|
last = where + chunk - 1;
|
|
where += chunk;
|
|
bytes -= chunk;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int fini_ith(FILE *f)
|
|
{
|
|
/* XXX: entry point? */
|
|
return write_ith_record(f, 0, 0, 1, NULL);
|
|
}
|
|
|
|
static int write_srecord(FILE *f, unsigned int len, unsigned int alen,
|
|
uint32_t addr, uint8_t type, void *data)
|
|
{
|
|
char buf[2+2+8+255*2+2+2];
|
|
char *p = buf;
|
|
uint8_t csum, *dptr = data;
|
|
unsigned int i;
|
|
|
|
if (len > 255) {
|
|
fprintf(stderr, "%s: internal error: invalid srec record size\n",
|
|
progname);
|
|
exit(1);
|
|
}
|
|
|
|
switch (alen) {
|
|
case 2:
|
|
addr &= 0xffff;
|
|
break;
|
|
case 3:
|
|
addr &= 0xffffff;
|
|
break;
|
|
case 4:
|
|
break;
|
|
default:
|
|
fprintf(stderr, "%s: internal error: invalid srec address length\n",
|
|
progname);
|
|
exit(1);
|
|
}
|
|
|
|
csum = (len+alen+1) + addr + (addr >> 8) + (addr >> 16) + (addr >> 24);
|
|
for (i = 0; i < len; i++)
|
|
csum += dptr[i];
|
|
csum = 0xff-csum;
|
|
|
|
p += sprintf(p, "S%c%02X%0*X", type, len+alen+1, alen*2, addr);
|
|
for (i = 0; i < len; i++)
|
|
p += sprintf(p, "%02X", dptr[i]);
|
|
p += sprintf(p, "%02X\n", csum);
|
|
|
|
if (fwrite(buf, 1, p-buf, f) != (size_t)(p-buf))
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int init_srec(FILE *f)
|
|
{
|
|
return write_srecord(f, 0, 2, 0, '0', NULL);
|
|
}
|
|
|
|
static int fini_srec(FILE *f)
|
|
{
|
|
/* XXX: entry point? */
|
|
return write_srecord(f, 0, 4, 0, '7', NULL);
|
|
}
|
|
|
|
static int output_srec(FILE *f, void *data, uint32_t bytes, uint32_t where)
|
|
{
|
|
uint8_t *dbuf = data;
|
|
unsigned int chunk;
|
|
|
|
while (bytes) {
|
|
/* Output up to 32 bytes, but always end on an aligned boundary */
|
|
chunk = 32 - (where & 31);
|
|
if (bytes < chunk)
|
|
chunk = bytes;
|
|
|
|
if (write_srecord(f, chunk, 4, where, '3', dbuf))
|
|
return -1;
|
|
|
|
dbuf += chunk;
|
|
where += chunk;
|
|
bytes -= chunk;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static struct output_format output_formats[] = {
|
|
{ "bin", "wb", null_init_fini, output_bin, null_init_fini },
|
|
{ "com", "wb", com_init, output_bin, null_init_fini },
|
|
{ "ith", "wt", null_init_fini, output_ith, fini_ith },
|
|
{ "ihx", "wt", null_init_fini, output_ith, fini_ith },
|
|
{ "srec", "wt", init_srec, output_srec, fini_srec },
|
|
{ NULL, NULL, NULL, NULL, NULL }
|
|
};
|
|
|
|
static const char *getformat(const char *pathname)
|
|
{
|
|
const char *p;
|
|
static char fmt_buf[16];
|
|
|
|
/*
|
|
* Search backwards for the string "rdf2" followed by a string
|
|
* of alphanumeric characters. This should handle path prefixes,
|
|
* as well as extensions (e.g. C:\FOO\RDF2SREC.EXE).
|
|
*/
|
|
for (p = strchr(pathname, '\0')-1 ; p >= pathname ; p--) {
|
|
if (!nasm_stricmp(p, "rdf2")) {
|
|
const char *q = p+4;
|
|
char *r = fmt_buf;
|
|
while (isalnum(*q) && r < fmt_buf+sizeof fmt_buf-1)
|
|
*r++ = *q++;
|
|
*r = '\0';
|
|
if (fmt_buf[0])
|
|
return fmt_buf;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static void usage(void)
|
|
{
|
|
fprintf(stderr,
|
|
"Usage: %s [options] input-file output-file\n"
|
|
"Options:\n"
|
|
" -o origin Specify the relocation origin\n"
|
|
" -p alignment Specify minimum segment alignment\n"
|
|
" -f format Select format (bin, com, ith, srec)\n"
|
|
" -q Run quiet\n"
|
|
" -v Run verbose\n",
|
|
progname);
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
rdfmodule *m;
|
|
bool err;
|
|
FILE *of;
|
|
int codepad, datapad;
|
|
const char *format = NULL;
|
|
const struct output_format *fmt;
|
|
bool quiet = false;
|
|
|
|
progname = argv[0];
|
|
|
|
if (argc < 2) {
|
|
usage();
|
|
return 1;
|
|
}
|
|
|
|
rdoff_init();
|
|
|
|
argv++, argc--;
|
|
|
|
while (argc > 2) {
|
|
if (argv[0][0] == '-' && argv[0][1] && !argv[0][2]) {
|
|
switch (argv[0][1]) {
|
|
case 'o':
|
|
argv++, argc--;
|
|
origin = readnum(*argv, &err);
|
|
if (err) {
|
|
fprintf(stderr, "%s: invalid parameter: %s\n",
|
|
progname, *argv);
|
|
return 1;
|
|
}
|
|
origin_def = true;
|
|
break;
|
|
case 'p':
|
|
argv++, argc--;
|
|
align = readnum(*argv, &err);
|
|
if (err) {
|
|
fprintf(stderr, "%s: invalid parameter: %s\n",
|
|
progname, *argv);
|
|
return 1;
|
|
}
|
|
align_def = true;
|
|
break;
|
|
case 'f':
|
|
argv++, argc--;
|
|
format = *argv;
|
|
break;
|
|
case 'q':
|
|
quiet = true;
|
|
break;
|
|
case 'v':
|
|
quiet = false;
|
|
break;
|
|
case 'h':
|
|
usage();
|
|
return 0;
|
|
default:
|
|
fprintf(stderr, "%s: unknown option: %s\n",
|
|
progname, *argv);
|
|
return 1;
|
|
}
|
|
}
|
|
argv++, argc--;
|
|
}
|
|
|
|
if (argc < 2) {
|
|
usage();
|
|
return 1;
|
|
}
|
|
|
|
if (!format)
|
|
format = getformat(progname);
|
|
|
|
if (!format) {
|
|
fprintf(stderr, "%s: unable to determine desired output format\n",
|
|
progname);
|
|
return 1;
|
|
}
|
|
|
|
for (fmt = output_formats; fmt->name; fmt++) {
|
|
if (!nasm_stricmp(format, fmt->name))
|
|
break;
|
|
}
|
|
|
|
if (!fmt->name) {
|
|
fprintf(stderr, "%s: unknown output format: %s\n", progname, format);
|
|
return 1;
|
|
}
|
|
|
|
m = rdfload(*argv);
|
|
|
|
if (!m) {
|
|
rdfperror(progname, *argv);
|
|
return 1;
|
|
}
|
|
|
|
if (!quiet)
|
|
printf("relocating %s: origin=%"PRIx32", align=%d\n",
|
|
*argv, origin, align);
|
|
|
|
m->textrel = origin;
|
|
m->datarel = origin + m->f.seg[0].length;
|
|
if (m->datarel % align != 0) {
|
|
codepad = align - (m->datarel % align);
|
|
m->datarel += codepad;
|
|
} else
|
|
codepad = 0;
|
|
|
|
m->bssrel = m->datarel + m->f.seg[1].length;
|
|
if (m->bssrel % align != 0) {
|
|
datapad = align - (m->bssrel % align);
|
|
m->bssrel += datapad;
|
|
} else
|
|
datapad = 0;
|
|
|
|
if (!quiet)
|
|
printf("code: %08"PRIx32"\ndata: %08"PRIx32"\nbss: %08"PRIx32"\n",
|
|
m->textrel, m->datarel, m->bssrel);
|
|
|
|
rdf_relocate(m);
|
|
|
|
argv++;
|
|
|
|
of = fopen(*argv, fmt->mode);
|
|
if (!of) {
|
|
fprintf(stderr, "%s: could not open output file %s: %s\n",
|
|
progname, *argv, strerror(errno));
|
|
return 1;
|
|
}
|
|
|
|
if (fmt->init(of) ||
|
|
fmt->output(of, m->t, m->f.seg[0].length, m->textrel) ||
|
|
fmt->output(of, m->d, m->f.seg[1].length, m->datarel) ||
|
|
fmt->fini(of)) {
|
|
fprintf(stderr, "%s: error writing to %s: %s\n",
|
|
progname, *argv, strerror(errno));
|
|
return 1;
|
|
}
|
|
|
|
fclose(of);
|
|
return 0;
|
|
}
|