Mercurial > hg-old > index.cgi
changeset 378:1c31e9005ff7
Brought forward lwlink, lwar, and lwobjdump along with some misc junk
author | lost@starbug |
---|---|
date | Mon, 26 Apr 2010 19:30:44 -0600 |
parents | 55ed7d06b136 |
children | 85b592c8b8f6 |
files | .hgignore ChangeLog Makefile.am configure.ac extra/README extra/ar extra/as extra/ld lwar/Makefile.am lwar/add.c lwar/extract.c lwar/list.c lwar/lwar.c lwar/lwar.h lwar/main.c lwar/remove.c lwar/replace.c lwar/util.c lwar/util.h lwlink/Makefile.am lwlink/expr.c lwlink/expr.h lwlink/link.c lwlink/lwlink.c lwlink/lwlink.h lwlink/main.c lwlink/map.c lwlink/objdump.c lwlink/output.c lwlink/readfiles.c lwlink/script.c lwlink/util.c lwlink/util.h |
diffstat | 33 files changed, 4437 insertions(+), 3 deletions(-) [+] |
line wrap: on
line diff
--- a/.hgignore Mon Apr 26 18:37:06 2010 -0600 +++ b/.hgignore Mon Apr 26 19:30:44 2010 -0600 @@ -2,6 +2,9 @@ *~ *.o lwasm/lwasm +lwlink/lwlink +lwlink/lwobjdump +lwar/lwar lwlib/liblw.a link-warning.h
--- a/ChangeLog Mon Apr 26 18:37:06 2010 -0600 +++ b/ChangeLog Mon Apr 26 19:30:44 2010 -0600 @@ -15,4 +15,4 @@ Version 3.0 -[*] Completely new architecture +[*] Completely new architecture for lwasm
--- a/Makefile.am Mon Apr 26 18:37:06 2010 -0600 +++ b/Makefile.am Mon Apr 26 19:30:44 2010 -0600 @@ -1,4 +1,4 @@ ACLOCAL_AMFLAGS = -I m4 -SUBDIRS = lib lwlib lwasm -DIST_SUBDIRS = lib lwlib lwasm +SUBDIRS = lib lwlib lwasm lwlink lwar +DIST_SUBDIRS = lib lwlib lwasm lwlink lwar EXTRA_DIST = m4/gnulib-cache.m4
--- a/configure.ac Mon Apr 26 18:37:06 2010 -0600 +++ b/configure.ac Mon Apr 26 19:30:44 2010 -0600 @@ -10,5 +10,7 @@ lib/Makefile lwlib/Makefile lwasm/Makefile + lwlink/Makefile + lwar/Makefile ]) AC_OUTPUT
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/extra/README Mon Apr 26 19:30:44 2010 -0600 @@ -0,0 +1,43 @@ +These files are extra utility type scripts that can be used for various +purposes. + +as + +This is a sort of front-end script that makes lwasm look approximately like +gnu as which is useful for using lwasm as a backend to gcc. You will +probably need to edit it to make it work fully. Simply put this in place +of whatever gcc6809 installed for "as" (in the "m6809/bin" folder in +"--prefix") after editing it to point to the real location of the "lwasm" +binary. + + +ld + +Similar to the "as" script above except for lwlink. + + +ar + +Similar to the "as" script above except for lwar. + + +To use these scripts, you really need to understand how to build a gcc as a +cross compiler. The basics are that you put the as, ld, and ar scripts +whereever you plan to put your cross-development binaries. Then, when +building the cross compiler, you tell it where the scripts are. + +You should probably name them m6809-unknown-none-{ar,as,ld} or similar +depending on your gcc build target. Then you'll want to get the gcc6809 +patch and patch the correct gcc source code. Then use a configure line +similar to the following: + +configure --enable-languages=c --target=m6809-coco +--program-prefix=m6809-coco-lwos- --enable-obsolete +--srcdir=/home/lost/gcc6809/src/gcc-4.3.3 --disable-threads --disable-nls +--disable-libssp --prefix=/usr/local/coco --with-as=/usr/local/coco/bin/as +--with-ld=/usr/local/coco/bin/ld --with-sysroot=/usr/local/coco + +Obviously adjust various paths to match what you're doing. + +The exact mechanics of configuring and getting gcc to install correctly is +left as an exercise to the dedicated masochist.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/extra/ar Mon Apr 26 19:30:44 2010 -0600 @@ -0,0 +1,64 @@ +#!/bin/sh +# +# +# Copyright 2009 by William Astle <lost@l-w.ca> +# +#This file is part of LWTOOLS. +# +#LWTOOLS is free software: you can redistribute it and/or modify it under the +#terms of the GNU General Public License as published by the Free Software +#Foundation, either version 3 of the License, or (at your option) any later +#version. +# +#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, see <http://www.gnu.org/licenses/>. + +# this was based somewhat on the "ar" script from gcc6809 + +# This script is a frontend to the lwar library manager, to make it +# look more like GNU ar. Not all ar features are supported here. +# It basically translates ar style options into lwar format. + +# Parse and translate command-line options + +# ar options cheatsheet: +# r: insert (with replace) +# c: create archive +# u: only replace newer files +# v: verbose mode +# x: extract files from archive + +options=$1; shift +case $options in + rc|cru|-rc|-cru) + options="--replace --create" + ;; + rv) + options="--replace" + ;; + x|-x) + options="--extract" + ;; + -C|--cache) + exit 0 + ;; + *) + options="--replace --create $options" + if [ "$libname" = "" ]; then + libname=$options + fi + ;; +esac + +if [ "x$options" = "x" ]; then + echo "ar (m6809): no options given" + exit 1 +fi + +# Run the real lwar with translated options. +exec lwar $options $*
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/extra/as Mon Apr 26 19:30:44 2010 -0600 @@ -0,0 +1,144 @@ +#!/bin/sh +# +# Copyright 2009 by William Astle <lost@l-w.ca> +# +#This file is part of LWASM. +# +#LWASM is free software: you can redistribute it and/or modify it under the +#terms of the GNU General Public License as published by the Free Software +#Foundation, either version 3 of the License, or (at your option) any later +#version. +# +#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, see <http://www.gnu.org/licenses/>. + +# this was based somewhat on the "as" script from gcc6809 + +#echo "LWASM-as $0 $*" + +show_version () { +cat <<END +LWASM (GNU assembler frontend) 2.3 +This program is free software; you may redistribute it under the terms of +the GNU General Public License. This program has absolutely no warranty. +END +} + +fatal_error () { + echo $* 1>&2 + exit 1 +} + +# Assume nothing. + +input_file= +output_file= +list_file= +options= +list_file_enabled=n + +# Parse the command-line options. See the GNU 'as' man page for +# an explanation of all these options. Our goal is to translate +# them into lwasm form. + +while [ "$1" != "" ]; do + arg=$1; shift + case $arg in + -m6809) + true + ;; + -gn) + # Generate NoICE debug symbols + # ignored - no output formats support debugging symbols + ;; + -gs) + # Generate SDCC debug symbols + # ignored - no output formats supprt debugging symbols + ;; +# --globalize-symbols) +# # Make all symbols global +# # lwasm does not support globalizing everything by default +# ;; + -m*) + fatal_error "invalid CPU option '$arg'" + ;; + --) + fatal_error "standard input not supported" + ;; +# -a*) +# options="${options}lc" +# list_file_enabled=y +# ;; + -I*) + #include_file=${arg#-I} + #echo "warning: include path '$include_file' ignored" + ;; + -MD) + fatal_error "assembler option '$arg' not supported" + ;; + -o) + output_file=$1; shift + ;; + -v|-version) + show_version + ;; + --version) + show_version + exit 0 + ;; + -D|-f|-K|--traditional-format|-w|-x|-Z|-W|--no-warn) + # These options are accepted but ignored by GNU as. + true + ;; +# =*) +# # Set the name of the listing file +# list_file=${arg#=} +# ;; + -*) + echo "as (m6809): unrecognized option $arg" + exit 1 + ;; + *) + input_file=$arg + ;; + esac +done + +# Complain if no input files given. We don't support redirecting +# from standard input. + +if [ "x$input_file" = "x" ]; then + fatal_error "no input file specified" +fi + +# Invoke the real (lwasm) assembler. +# The -o option specifies the output file name +# --obj creates object files +# --pragma=undefextern causes undefined symbols to be assumed external +# --pragma=cescapes allows C escape syntax in strings +#echo lwasm -o "$output_file" $options --obj --pragma=undefextern --pragma=cescapes $input_file +lwasm -o "$output_file" $options --obj --pragma=undefextern --pragma=cescapes --pragma=importundefexport $input_file +rc=$? + +# OK, see if the assembler succeeded or not. +# If it failed, the source is copied to /tmp/as6809_error.s +# so that it can be inspected. GCC will normally delete any +# temporary .s files that it generates. This makes debugging +# the compiler easier. +# +# lwasm does not create an output file if it errors out but it also doesn't +# remove an existing file if it fails so we remove it anyway... + +if [ "$rc" != "0" ]; then + cp -p $input_file /tmp/as6809_error.s + rm -f $asoutput_file + exit $rc +fi + +# we don't need anything fancy here since lwasm supports specifying output +# file names....
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/extra/ld Mon Apr 26 19:30:44 2010 -0600 @@ -0,0 +1,127 @@ +#!/bin/sh +# +# +# Copyright 2009 by William Astle <lost@l-w.ca> +# +#This file is part of LWTOOLS. +# +#LWTOOLS is free software: you can redistribute it and/or modify it under the +#terms of the GNU General Public License as published by the Free Software +#Foundation, either version 3 of the License, or (at your option) any later +#version. +# +#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, see <http://www.gnu.org/licenses/>. + +# this was based somewhat on the "as" script from gcc6809 + +echo "LWTOOLS-as $0 $*" +path_to_lwlink=lwlink + +# Set defaults. Some are based on the target type, which is +# determined by the name by which the program was invoked. +output_file=a.out +libpaths= +libs= +verbose= +case $0 in + *m6809-coco-*) + options="--format=decb" +# options="-b .text=0x2000 -b .data=0x7000 -b .bss=0x7C00 -b .ctors=0x7F00 -b .dtors=0x7F80 -b vector=0x7FF0" +# aslink_options="-nwxst" +# exe_suffix=.bin + ;; + *) + options="--format=decb" +# options="-b .text=0x8000 -b .data=0x1000 -b .bss=0x0100 -b .ctors=0xFD00 -b .dtors=0xFE00 -b vector=0xFFF0" +# aslink_options="-nwxso" +# exe_suffix=.s19 + ;; +esac + + +while [ "$1" != "" ]; do + arg=$1; shift + case $arg in + -gn) + # Generate NoICE debug file + # ignored because debugging not supported by targets + ;; + -gs) + # Generate SDCC debug file + # ignored because debugging not supported by targets + ;; + -o) + output_file=$1; shift + ;; + -L*) + arg=${arg#-L} + libpaths="$libpaths --library-path=$arg" + ;; + -l*) + arg=${arg#-l} + libs="$libs --library=$arg" + ;; + --section-start) + section_value=$1; shift + options="$options --section-start=$section_value" + ;; + -Tbss) + options="$options --section-start=.bss=$1"; shift + ;; + -Tdata) + options="$options --section-start=.data=$1"; shift + ;; + -Ttext|-Tcode) + options="$options --section-start=.text=$1"; shift + ;; + -v|--verbose) + verbose=1 + ;; + *crt0.o) + startup_files=$arg + ;; + -g) + # Ignored by GNU ld; we should do the same + true + ;; + -h|--help) + echo "ld (m6809)" + exit 0 + ;; + -T) + echo "-T scripts not supported"; + exit 1; + ;; + --format-lwex) + options="$options --format=lwex" + ;; + + -*) + echo "ld (m6809): unknown option $arg" + exit 1 + ;; + *) + input_files="$input_files $arg" + ;; + esac +done + +options="$options -o $output_file" + +if [ "$verbose" = "1" ]; then + echo "$path_to_lwlink $options $input_files $startup_files $libpaths $libs" +fi + +$path_to_lwlink $options $input_files $startup_files $libpaths $libs +rc=$? + +if [ "$rc" != "0" ]; then + rm -f $output_file + exit $rc +fi
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lwar/Makefile.am Mon Apr 26 19:30:44 2010 -0600 @@ -0,0 +1,5 @@ +AM_CPPFLAGS = -I$(top_builddir)/lib -I$(top_srcdir)/lib +bin_PROGRAMS = lwar +lwar_SOURCES = main.c util.c lwar.c list.c add.c remove.c replace.c extract.c +lwar_LDADD = -L$(top_builddir)/lib -L$(top_srcdir)/lib -lgnu +EXTRA_DIST = lwar.h util.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lwar/add.c Mon Apr 26 19:30:44 2010 -0600 @@ -0,0 +1,195 @@ +/* +add.c +Copyright © 2009 William Astle + +This file is part of LWAR. + +LWAR is free software: you can redistribute it and/or modify it under the +terms of the GNU General Public License as published by the Free Software +Foundation, either version 3 of the License, or (at your option) any later +version. + +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, see <http://www.gnu.org/licenses/>. + + +Implements the program startup code + +*/ + +#include <config.h> + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> + +#include "lwar.h" + +void do_add(void) +{ + FILE *f; + unsigned char buf[8]; + long l; + int c; + FILE *f2; + int i; + + f = fopen(archive_file, "r+"); + if (!f) + { + if (errno == ENOENT) + { + f = fopen(archive_file, "w"); + if (f) + { + fputs("LWAR1V", f); + goto doadd; + } + } + perror("Cannot open archive file"); + } + + fread(buf, 1, 6, f); + if (memcmp("LWAR1V", buf, 6)) + { + fprintf(stderr, "%s is not a valid archive file.\n", archive_file); + exit(1); + } + + for (;;) + { + c = fgetc(f); + if (c == EOF && ferror(f)) + { + perror("Reading archive file"); + exit(1); + } + if (c == EOF) + goto doadd; + + if (!c) + { + fseek(f, -1, SEEK_CUR); + goto doadd; + } + + // find the end of the file name + while (c) + { + c = fgetc(f); + if (c == EOF || ferror(f)) + { + fprintf(stderr, "Bad archive file\n"); + exit(1); + } + } + + // get length of archive member + l = 0; + c = fgetc(f); + l = c << 24; + c = fgetc(f); + l |= c << 16; + c = fgetc(f); + l |= c << 8; + c = fgetc(f); + l |= c; + + fseek(f, l, SEEK_CUR); + } + // back up to the NUL byte at the end of the file + fseek(f, -1, SEEK_CUR); +doadd: + for (i = 0; i < nfiles; i++) + { + f2 = fopen(files[i], "r"); + if (!f2) + { + fprintf(stderr, "Cannot open file %s:", files[i]); + perror(""); + exit(1); + } + fread(buf, 1, 6, f2); + if (mergeflag && !memcmp("LWAR1V", buf, 6)) + { + // add archive contents... + for (;;) + { + c = fgetc(f2); + if (c == EOF || ferror(f2)) + { + perror("Reading input archive file"); + exit(1); + } + if (c == EOF) + break; + + if (!c) + { + break; + } + + // find the end of the file name + while (c) + { + fputc(c, f); + c = fgetc(f2); + if (c == EOF || ferror(f)) + { + fprintf(stderr, "Bad input archive file\n"); + exit(1); + } + } + fputc(0, f); + + // get length of archive member + l = 0; + c = fgetc(f2); + fputc(c, f); + l = c << 24; + c = fgetc(f2); + fputc(c, f); + l |= c << 16; + c = fgetc(f2); + fputc(c, f); + l |= c << 8; + c = fgetc(f2); + fputc(c, f); + l |= c; + + while (l) + { + c = fgetc(f2); + fputc(c, f); + l--; + } + } + + fclose(f2); + continue; + } + fseek(f2, 0, SEEK_END); + l = ftell(f2); + fseek(f2, 0, SEEK_SET); + fputs(files[i], f); + fputc(0, f); + fputc(l >> 24, f); + fputc((l >> 16) & 0xff, f); + fputc((l >> 8) & 0xff, f); + fputc(l & 0xff, f); + while (l) + { + c = fgetc(f2); + fputc(c, f); + l--; + } + } + + // flag end of file + fputc(0, f); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lwar/extract.c Mon Apr 26 19:30:44 2010 -0600 @@ -0,0 +1,123 @@ +/* +extract.c +Copyright © 2009 William Astle + +This file is part of LWAR. + +LWAR is free software: you can redistribute it and/or modify it under the +terms of the GNU General Public License as published by the Free Software +Foundation, either version 3 of the License, or (at your option) any later +version. + +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, see <http://www.gnu.org/licenses/>. + +*/ + +#include <config.h> + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "lwar.h" + +void do_extract(void) +{ + FILE *f; + char buf[8]; + long l; + int c; + char fnbuf[1024]; + int i; + FILE *nf; + + f = fopen(archive_file, "r"); + if (!f) + { + perror("Opening archive file"); + exit(1); + } + + fread(buf, 1, 6, f); + if (memcmp("LWAR1V", buf, 6)) + { + fprintf(stderr, "%s is not a valid archive file.\n", archive_file); + exit(1); + } + + for (;;) + { + c = fgetc(f); + if (ferror(f)) + { + perror("Reading archive file"); + exit(1); + } + if (c == EOF) + return; + + + // find the end of the file name + if (!c) + return; + + i = 0; + while (c) + { + fnbuf[i++] = c; + c = fgetc(f); + if (c == EOF || ferror(f)) + { + fprintf(stderr, "Bad archive file\n"); + exit(1); + } + } + fnbuf[i] = 0; + + // get length of archive member + l = 0; + c = fgetc(f); + l = c << 24; + c = fgetc(f); + l |= c << 16; + c = fgetc(f); + l |= c << 8; + c = fgetc(f); + l |= c; + + for (i = 0; i < nfiles; i++) + { + if (!strcmp(files[i], fnbuf)) + break; + } + if (i < nfiles || nfiles == 0) + { + // extract the file + nf = fopen(fnbuf, "w"); + if (!nf) + { + fprintf(stderr, "Cannot extract '%s': %s\n", fnbuf, strerror(errno)); + exit(1); + } + while (l) + { + c = fgetc(f); + fputc(c, nf); + l--; + } + fclose(nf); + } + else + { + // skip the file + fseek(f, l, SEEK_CUR); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lwar/list.c Mon Apr 26 19:30:44 2010 -0600 @@ -0,0 +1,95 @@ +/* +list.c +Copyright © 2009 William Astle + +This file is part of LWAR. + +LWAR is free software: you can redistribute it and/or modify it under the +terms of the GNU General Public License as published by the Free Software +Foundation, either version 3 of the License, or (at your option) any later +version. + +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, see <http://www.gnu.org/licenses/>. + + +Implements the program startup code + +*/ + +#include <config.h> + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "lwar.h" + +void do_list(void) +{ + FILE *f; + char buf[8]; + long l; + int c; + + f = fopen(archive_file, "r"); + if (!f) + { + perror("Opening archive file"); + exit(1); + } + + fread(buf, 1, 6, f); + if (memcmp("LWAR1V", buf, 6)) + { + fprintf(stderr, "%s is not a valid archive file.\n", archive_file); + exit(1); + } + + for (;;) + { + c = fgetc(f); + if (ferror(f)) + { + perror("Reading archive file"); + exit(1); + } + if (c == EOF) + return; + + + // find the end of the file name + if (!c) + return; + + while (c) + { + putchar(c); + c = fgetc(f); + if (c == EOF || ferror(f)) + { + fprintf(stderr, "Bad archive file\n"); + exit(1); + } + } + + // get length of archive member + l = 0; + c = fgetc(f); + l = c << 24; + c = fgetc(f); + l |= c << 16; + c = fgetc(f); + l |= c << 8; + c = fgetc(f); + l |= c; + printf(": %04lx bytes\n", l); + fseek(f, l, SEEK_CUR); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lwar/lwar.c Mon Apr 26 19:30:44 2010 -0600 @@ -0,0 +1,55 @@ +/* +lwar.c +Copyright © 2009 William Astle + +This file is part of LWAR. + +LWAR is free software: you can redistribute it and/or modify it under the +terms of the GNU General Public License as published by the Free Software +Foundation, either version 3 of the License, or (at your option) any later +version. + +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, see <http://www.gnu.org/licenses/>. + + +Implements the program startup code + +*/ + +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#define __lwar_c_seen__ +#include "lwar.h" +#include "util.h" + +typedef struct +{ + FILE *f; +} arhandle_real; + +int debug_level = 0; +int operation = 0; +int nfiles = 0; +char *archive_file = NULL; +int mergeflag = 0; + +char **files = NULL; + +void add_file_name(char *fn) +{ + files = lw_realloc(files, sizeof(char *) * (nfiles + 1)); + files[nfiles] = fn; + nfiles++; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lwar/lwar.h Mon Apr 26 19:30:44 2010 -0600 @@ -0,0 +1,62 @@ +/* +lwar.h +Copyright © 2009 William Astle + +This file is part of LWAR. + +LWAR is free software: you can redistribute it and/or modify it under the +terms of the GNU General Public License as published by the Free Software +Foundation, either version 3 of the License, or (at your option) any later +version. + +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, see <http://www.gnu.org/licenses/>. + +Contains the main defs used by the linker +*/ + + +#define LWAR_OP_LIST 1 +#define LWAR_OP_ADD 2 +#define LWAR_OP_REMOVE 3 +#define LWAR_OP_CREATE 4 +#define LWAR_OP_EXTRACT 5 +#define LWAR_OP_REPLACE 6 + +#ifndef __lwar_h_seen__ +#define __lwar_h_seen__ + +#ifndef __lwar_c_seen__ + +extern char *archive_file; +extern int debug_level; +extern int operation; +extern int nfiles; +extern char **files; +extern int mergeflag; + +//typedef void * ARHANDLE; + +#define AR_MODE_RD 1 +#define AR_MODE_WR 2 +#define AR_MODE_RW 3 +#define AR_MODE_CREATE 4 + + +#define __lwar_E__ extern +#else +#define __lwar_E__ +#endif // __lwar_c_seen__ + +__lwar_E__ void add_file_name(char *fn); + +//__lwar_E__ ARHANDLE open_archive(char *fn, int mode); + +#undef __lwar_E__ + +#endif //__lwar_h_seen__
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lwar/main.c Mon Apr 26 19:30:44 2010 -0600 @@ -0,0 +1,208 @@ +/* +main.c +Copyright © 2009 William Astle + +This file is part of LWAR. + +LWAR is free software: you can redistribute it and/or modify it under the +terms of the GNU General Public License as published by the Free Software +Foundation, either version 3 of the License, or (at your option) any later +version. + +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, see <http://www.gnu.org/licenses/>. + + +Implements the program startup code + +*/ + +#include <config.h> + +#include <argp.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> + +#include "lwar.h" + +// command line option handling +const char *argp_program_version = "LWAR from " PACKAGE_STRING; +const char *argp_program_bug_address = PACKAGE_BUGREPORT; +char *program_name; + +static error_t parse_opts(int key, char *arg, struct argp_state *state) +{ + switch (key) + { + case 'd': + // debug + debug_level++; + break; + + case 'a': + // add members + operation = LWAR_OP_ADD; + break; + + case 'c': + // create archive + operation = LWAR_OP_CREATE; + break; + + case 'm': + mergeflag = 1; + break; + + case 'r': + // replace members + operation = LWAR_OP_REPLACE; + break; + + case 'l': + // list members + operation = LWAR_OP_LIST; + break; + + case 'x': + // extract members + operation = LWAR_OP_EXTRACT; + break; + + case ARGP_KEY_ARG: + if (archive_file) + { + // add archive member to list + add_file_name(arg); + } + else + archive_file = arg; + break; + + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + +static struct argp_option options[] = +{ + { "replace", 'r', 0, 0, + "Add or replace archive members" }, + { "extract", 'x', 0, 0, + "Extract members from the archive" }, + { "add", 'a', 0, 0, + "Add members to the archive" }, + { "list", 'l', 0, 0, + "List the contents of the archive" }, + { "create", 'c', 0, 0, + "Create new archive (or truncate existing one)" }, + { "merge", 'm', 0, 0, + "Add the contents of archive arguments instead of the archives themselves" }, + { "debug", 'd', 0, 0, + "Set debug mode"}, + { 0 } +}; + +static struct argp argp = +{ + options, + parse_opts, + "<archive> [<file> ...]", + "LWAR, a library file manager for LWLINK" +}; + +extern void do_list(void); +extern void do_add(void); +extern void do_remove(void); +extern void do_replace(void); +extern void do_extract(void); + +// main function; parse command line, set up assembler state, and run the +// assembler on the first file +int main(int argc, char **argv) +{ + program_name = argv[0]; + argp_parse(&argp, argc, argv, 0, 0, NULL); + if (archive_file == NULL) + { + fprintf(stderr, "You must specify an archive file.\n"); + exit(1); + } + + if (operation == 0) + { + fprintf(stderr, "You must specify an operation.\n"); + exit(1); + } + + if (operation == LWAR_OP_LIST || operation == LWAR_OP_REMOVE || operation == LWAR_OP_EXTRACT) + { + struct stat stbuf; + // make sure the archive exists + if (stat(archive_file, &stbuf) < 0) + { + fprintf(stderr, "Cannot open archive file %s:\n", archive_file); + perror(""); + exit(2); + } + } + if (operation == LWAR_OP_CREATE) + { + struct stat stbuf; + // check if the archive exists + if (stat(archive_file, &stbuf) < 0) + { + if (errno != ENOENT) + { + fprintf(stderr, "Cannot create archive file %s:\n", archive_file); + perror(""); + exit(2); + } + } + else + { + if (unlink(archive_file) < 0) + { + fprintf(stderr, "Cannot create archive file %s:\n", archive_file); + perror(""); + exit(2); + } + + } + } + + switch (operation) + { + case LWAR_OP_LIST: + do_list(); + break; + + case LWAR_OP_ADD: + case LWAR_OP_CREATE: + do_add(); + break; + + case LWAR_OP_REMOVE: + do_remove(); + break; + + case LWAR_OP_REPLACE: + do_replace(); + break; + + case LWAR_OP_EXTRACT: + do_extract(); + break; + } + + exit(0); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lwar/remove.c Mon Apr 26 19:30:44 2010 -0600 @@ -0,0 +1,31 @@ +/* +remove.c +Copyright © 2009 William Astle + +This file is part of LWAR. + +LWAR is free software: you can redistribute it and/or modify it under the +terms of the GNU General Public License as published by the Free Software +Foundation, either version 3 of the License, or (at your option) any later +version. + +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, see <http://www.gnu.org/licenses/>. + + +Implements the program startup code + +*/ + +#include <config.h> + +#include "lwar.h" + +void do_remove(void) +{ +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lwar/replace.c Mon Apr 26 19:30:44 2010 -0600 @@ -0,0 +1,243 @@ +/* +replace.c +Copyright © 2009 William Astle + +This file is part of LWAR. + +LWAR is free software: you can redistribute it and/or modify it under the +terms of the GNU General Public License as published by the Free Software +Foundation, either version 3 of the License, or (at your option) any later +version. + +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, see <http://www.gnu.org/licenses/>. + +*/ + +#include <config.h> + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "lwar.h" + +void do_replace(void) +{ + FILE *f; + FILE *nf; + unsigned char buf[8]; + long l; + int c; + FILE *f2; + int i; + char fnbuf[1024]; + char fnbuf2[1024]; + + sprintf(fnbuf, "%s.tmp", archive_file); + + f = fopen(archive_file, "r+"); + if (!f) + { + if (errno == ENOENT) + { + nf = fopen(fnbuf, "w"); + if (nf) + { + fputs("LWAR1V", nf); + goto doadd; + } + } + perror("Cannot open archive file"); + } + + fread(buf, 1, 6, f); + if (memcmp("LWAR1V", buf, 6)) + { + fprintf(stderr, "%s is not a valid archive file.\n", archive_file); + exit(1); + } + + nf = fopen(fnbuf, "w"); + if (!nf) + { + perror("Cannot create temp archive file"); + exit(1); + } + + fputs("LWAR1V", nf); + + for (;;) + { + c = fgetc(f); + if (c == EOF && ferror(f)) + { + perror("Reading archive file"); + exit(1); + } + if (c == EOF) + goto doadd; + + if (!c) + { + goto doadd; + } + + // find the end of the file name + i = 0; + while (c) + { + fnbuf2[i++] = c; + c = fgetc(f); + if (c == EOF || ferror(f)) + { + fprintf(stderr, "Bad archive file\n"); + exit(1); + } + } + fnbuf2[i] = 0; + + // get length of archive member + l = 0; + c = fgetc(f); + l = c << 24; + c = fgetc(f); + l |= c << 16; + c = fgetc(f); + l |= c << 8; + c = fgetc(f); + l |= c; + + // is it a file we are replacing? if so, do not copy it + for (i = 0; i < nfiles; i++) + { + if (!strcmp(files[i], fnbuf2)) + break; + } + if (i < nfiles) + { + fseek(f, l, SEEK_CUR); + } + else + { + // otherwise, copy it + fprintf(nf, "%s", fnbuf2); + fputc(0, nf); + fputc(l >> 24, nf); + fputc((l >> 16) & 0xff, nf); + fputc((l >> 8) & 0xff, nf); + fputc(l & 0xff, nf); + while (l) + { + c = fgetc(f); + fputc(c, nf); + l--; + } + } + } + + // done with the original file + fclose(f); +doadd: + for (i = 0; i < nfiles; i++) + { + f2 = fopen(files[i], "r"); + if (!f2) + { + fprintf(stderr, "Cannot open file %s:", files[i]); + perror(""); + exit(1); + } + fread(buf, 1, 6, f2); + if (mergeflag && !memcmp("LWAR1V", buf, 6)) + { + // add archive contents... + for (;;) + { + c = fgetc(f2); + if (c == EOF || ferror(f2)) + { + perror("Reading input archive file"); + exit(1); + } + if (c == EOF) + break; + + if (!c) + { + break; + } + + // find the end of the file name + while (c) + { + fputc(c, nf); + c = fgetc(f2); + if (c == EOF || ferror(f)) + { + fprintf(stderr, "Bad input archive file\n"); + exit(1); + } + } + fputc(0, nf); + + // get length of archive member + l = 0; + c = fgetc(f2); + fputc(c, nf); + l = c << 24; + c = fgetc(f2); + fputc(c, nf); + l |= c << 16; + c = fgetc(f2); + fputc(c, nf); + l |= c << 8; + c = fgetc(f2); + fputc(c, nf); + l |= c; + + while (l) + { + c = fgetc(f2); + fputc(c, nf); + l--; + } + } + + fclose(f2); + continue; + } + fseek(f2, 0, SEEK_END); + l = ftell(f2); + fseek(f2, 0, SEEK_SET); + fputs(files[i], nf); + fputc(0, nf); + fputc(l >> 24, nf); + fputc((l >> 16) & 0xff, nf); + fputc((l >> 8) & 0xff, nf); + fputc(l & 0xff, nf); + while (l) + { + c = fgetc(f2); + fputc(c, nf); + l--; + } + } + + // flag end of file + fputc(0, nf); + + fclose(nf); + + if (rename(fnbuf, archive_file) < 0) + { + perror("Cannot replace old archive file"); + unlink(fnbuf); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lwar/util.c Mon Apr 26 19:30:44 2010 -0600 @@ -0,0 +1,88 @@ +/* +util.c +Copyright © 2009 William Astle + +This file is part of LWAR. + +LWAR is free software: you can redistribute it and/or modify it under the +terms of the GNU General Public License as published by the Free Software +Foundation, either version 3 of the License, or (at your option) any later +version. + +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, see <http://www.gnu.org/licenses/>. +*/ + +/* +Utility functions +*/ + +#define __util_c_seen__ +#include <config.h> + +#include <malloc.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "util.h" + +void *lw_malloc(int size) +{ + void *ptr; + + ptr = malloc(size); + if (!ptr) + { + // bail out; memory allocation error + fprintf(stderr, "lw_malloc(): Memory allocation error\n"); + exit(1); + } + return ptr; +} + +void *lw_realloc(void *optr, int size) +{ + void *ptr; + + if (size == 0) + { + lw_free(optr); + return; + } + + ptr = realloc(optr, size); + if (!ptr) + { + fprintf(stderr, "lw_realloc(): memory allocation error\n"); + exit(1); + } +} + +void lw_free(void *ptr) +{ + if (ptr) + free(ptr); +} + +char *lw_strdup(const char *s) +{ + char *d; + + if (!s) + return NULL; + + d = strdup(s); + if (!d) + { + fprintf(stderr, "lw_strdup(): memory allocation error\n"); + exit(1); + } + + return d; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lwar/util.h Mon Apr 26 19:30:44 2010 -0600 @@ -0,0 +1,44 @@ +/* +util.h +Copyright © 2009 William Astle + +This file is part of LWAR. + +LWAR is free software: you can redistribute it and/or modify it under the +terms of the GNU General Public License as published by the Free Software +Foundation, either version 3 of the License, or (at your option) any later +version. + +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, see <http://www.gnu.org/licenses/>. +*/ + +/* +Utility functions +*/ + +#ifndef __util_h_seen__ +#define __util_h_seen__ + +#ifndef __util_c_seen__ +#define __util_E__ extern +#else +#define __util_E__ +#endif + +// allocate memory +__util_E__ void *lw_malloc(int size); +__util_E__ void lw_free(void *ptr); +__util_E__ void *lw_realloc(void *optr, int size); + +// string stuff +__util_E__ char *lw_strdup(const char *s); + +#undef __util_E__ + +#endif // __util_h_seen__
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lwlink/Makefile.am Mon Apr 26 19:30:44 2010 -0600 @@ -0,0 +1,7 @@ +AM_CPPFLAGS = -I$(top_builddir)/lib -I$(top_srcdir)/lib +bin_PROGRAMS = lwlink lwobjdump +lwlink_SOURCES = main.c lwlink.c util.c readfiles.c expr.c script.c link.c output.c map.c +lwlink_LDADD = -L$(top_builddir)/lib -L$(top_srcdir)/lib -lgnu +lwobjdump_SOURCES = objdump.c util.c +lwobjdump_LDADD = -L$(top_builddir)/lib -L$(top_srcdir)/lib -lgnu +EXTRA_DIST = lwlink.h util.h expr.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lwlink/expr.c Mon Apr 26 19:30:44 2010 -0600 @@ -0,0 +1,370 @@ +/* +expr.c +Copyright © 2009 William Astle + +This file is part of LWLINK. + +LWLINK is free software: you can redistribute it and/or modify it under the +terms of the GNU General Public License as published by the Free Software +Foundation, either version 3 of the License, or (at your option) any later +version. + +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, see <http://www.gnu.org/licenses/>. +*/ + +/* +This file contains the actual expression evaluator +*/ + +#define __expr_c_seen__ +#include <config.h> + +#include <ctype.h> +#include <stdlib.h> +#include <string.h> + +#include "expr.h" +#include "util.h" + +lw_expr_stack_t *lw_expr_stack_create(void) +{ + lw_expr_stack_t *s; + + s = lw_malloc(sizeof(lw_expr_stack_t)); + s -> head = NULL; + s -> tail = NULL; + return s; +} + +void lw_expr_stack_free(lw_expr_stack_t *s) +{ + while (s -> head) + { + s -> tail = s -> head; + s -> head = s -> head -> next; + lw_expr_term_free(s -> tail -> term); + lw_free(s -> tail); + } + lw_free(s); +} + +lw_expr_stack_t *lw_expr_stack_dup(lw_expr_stack_t *s) +{ + lw_expr_stack_node_t *t; + lw_expr_stack_t *s2; + + s2 = lw_expr_stack_create(); + for (t = s -> head; t; t = t -> next) + { + lw_expr_stack_push(s2, t -> term); + } + return s2; +} + +void lw_expr_term_free(lw_expr_term_t *t) +{ + if (t) + { + if (t -> term_type == LW_TERM_SYM) + lw_free(t -> symbol); + lw_free(t); + } +} + +lw_expr_term_t *lw_expr_term_create_oper(int oper) +{ + lw_expr_term_t *t; + + t = lw_malloc(sizeof(lw_expr_term_t)); + t -> term_type = LW_TERM_OPER; + t -> value = oper; + return t; +} + +lw_expr_term_t *lw_expr_term_create_int(int val) +{ + lw_expr_term_t *t; + + t = lw_malloc(sizeof(lw_expr_term_t)); + t -> term_type = LW_TERM_INT; + t -> value = val; + return t; +} + +lw_expr_term_t *lw_expr_term_create_sym(char *sym, int symtype) +{ + lw_expr_term_t *t; + + t = lw_malloc(sizeof(lw_expr_term_t)); + t -> term_type = LW_TERM_SYM; + t -> symbol = lw_strdup(sym); + t -> value = symtype; + return t; +} + +lw_expr_term_t *lw_expr_term_dup(lw_expr_term_t *t) +{ + switch (t -> term_type) + { + case LW_TERM_INT: + return lw_expr_term_create_int(t -> value); + + case LW_TERM_OPER: + return lw_expr_term_create_oper(t -> value); + + case LW_TERM_SYM: + return lw_expr_term_create_sym(t -> symbol, t -> value); + + default: + exit(1); + } +// can't get here +} + +void lw_expr_stack_push(lw_expr_stack_t *s, lw_expr_term_t *t) +{ + lw_expr_stack_node_t *n; + + if (!s) + { + exit(1); + } + + n = lw_malloc(sizeof(lw_expr_stack_node_t)); + n -> next = NULL; + n -> prev = s -> tail; + n -> term = lw_expr_term_dup(t); + + if (s -> head) + { + s -> tail -> next = n; + s -> tail = n; + } + else + { + s -> head = n; + s -> tail = n; + } +} + +lw_expr_term_t *lw_expr_stack_pop(lw_expr_stack_t *s) +{ + lw_expr_term_t *t; + lw_expr_stack_node_t *n; + + if (!(s -> tail)) + return NULL; + + n = s -> tail; + s -> tail = n -> prev; + if (!(n -> prev)) + { + s -> head = NULL; + } + + t = n -> term; + n -> term = NULL; + + lw_free(n); + + return t; +} + +/* +take an expression stack s and scan for operations that can be completed + +return -1 on error, 0 on no error + +possible errors are: division by zero or unknown operator + +theory of operation: + +scan the stack for an operator which has two constants preceding it (binary) +or 1 constant preceding it (unary) and if found, perform the calculation +and replace the operator and its operands with the result + +repeat the scan until no futher simplications are found or if there are no +further operators or only a single term remains + +*/ +int lw_expr_reval(lw_expr_stack_t *s, lw_expr_stack_t *(*sfunc)(char *sym, int stype, void *state), void *state) +{ + lw_expr_stack_node_t *n, *n2; + lw_expr_stack_t *ss; + int c; + +next_iter_sym: + // resolve symbols + // symbols that do not resolve to an expression are left alone + for (c = 0, n = s -> head; n; n = n -> next) + { + if (n -> term -> term_type == LW_TERM_SYM) + { + ss = sfunc(n -> term -> symbol, n -> term -> value, state); + if (ss) + { + c++; + // splice in the result stack + if (n -> prev) + { + n -> prev -> next = ss -> head; + } + else + { + s -> head = ss -> head; + } + ss -> head -> prev = n -> prev; + ss -> tail -> next = n -> next; + if (n -> next) + { + n -> next -> prev = ss -> tail; + } + else + { + s -> tail = ss -> tail; + } + lw_expr_term_free(n -> term); + lw_free(n); + n = ss -> tail; + + ss -> head = NULL; + ss -> tail = NULL; + lw_expr_stack_free(ss); + } + } + } + if (c) + goto next_iter_sym; + +next_iter: + // a single term + if (s -> head == s -> tail) + return 0; + + // search for an operator + for (n = s -> head; n; n = n -> next) + { + if (n -> term -> term_type == LW_TERM_OPER) + { + if (n -> term -> value == LW_OPER_NEG + || n -> term -> value == LW_OPER_COM + ) + { + // unary operator + if (n -> prev && n -> prev -> term -> term_type == LW_TERM_INT) + { + // a unary operator we can resolve + // we do the op then remove the term "n" is pointing at + if (n -> term -> value == LW_OPER_NEG) + { + n -> prev -> term -> value = -(n -> prev -> term -> value); + } + else if (n -> term -> value == LW_OPER_COM) + { + n -> prev -> term -> value = ~(n -> prev -> term -> value); + } + n -> prev -> next = n -> next; + if (n -> next) + n -> next -> prev = n -> prev; + else + s -> tail = n -> prev; + + lw_expr_term_free(n -> term); + lw_free(n); + break; + } + } + else + { + // binary operator + if (n -> prev && n -> prev -> prev && n -> prev -> term -> term_type == LW_TERM_INT && n -> prev -> prev -> term -> term_type == LW_TERM_INT) + { + // a binary operator we can resolve + switch (n -> term -> value) + { + case LW_OPER_PLUS: + n -> prev -> prev -> term -> value += n -> prev -> term -> value; + break; + + case LW_OPER_MINUS: + n -> prev -> prev -> term -> value -= n -> prev -> term -> value; + break; + + case LW_OPER_TIMES: + n -> prev -> prev -> term -> value *= n -> prev -> term -> value; + break; + + case LW_OPER_DIVIDE: + if (n -> prev -> term -> value == 0) + return -1; + n -> prev -> prev -> term -> value /= n -> prev -> term -> value; + break; + + case LW_OPER_MOD: + if (n -> prev -> term -> value == 0) + return -1; + n -> prev -> prev -> term -> value %= n -> prev -> term -> value; + break; + + case LW_OPER_INTDIV: + if (n -> prev -> term -> value == 0) + return -1; + n -> prev -> prev -> term -> value /= n -> prev -> term -> value; + break; + + case LW_OPER_BWAND: + n -> prev -> prev -> term -> value &= n -> prev -> term -> value; + break; + + case LW_OPER_BWOR: + n -> prev -> prev -> term -> value |= n -> prev -> term -> value; + break; + + case LW_OPER_BWXOR: + n -> prev -> prev -> term -> value ^= n -> prev -> term -> value; + break; + + case LW_OPER_AND: + n -> prev -> prev -> term -> value = (n -> prev -> term -> value && n -> prev -> prev -> term -> value) ? 1 : 0; + break; + + case LW_OPER_OR: + n -> prev -> prev -> term -> value = (n -> prev -> term -> value || n -> prev -> prev -> term -> value) ? 1 : 0; + break; + + default: + // return error if unknown operator! + return -1; + } + + // now remove the two unneeded entries from the stack + n -> prev -> prev -> next = n -> next; + if (n -> next) + n -> next -> prev = n -> prev -> prev; + else + s -> tail = n -> prev -> prev; + + lw_expr_term_free(n -> term); + lw_expr_term_free(n -> prev -> term); + lw_free(n -> prev); + lw_free(n); + break; + } + } + } + } + // note for the terminally confused about dynamic memory and pointers: + // n will not be NULL even after the lw_free calls above so + // this test will still work (n will be a dangling pointer) + // (n will only be NULL if we didn't find any operators to simplify) + if (n) + goto next_iter; + + return 0; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lwlink/expr.h Mon Apr 26 19:30:44 2010 -0600 @@ -0,0 +1,108 @@ +/* +expr.h +Copyright © 2009 William Astle + +This file is part of LWLINK. + +LWLINK is free software: you can redistribute it and/or modify it under the +terms of the GNU General Public License as published by the Free Software +Foundation, either version 3 of the License, or (at your option) any later +version. + +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, see <http://www.gnu.org/licenses/>. +*/ + +/* +Definitions for expression evaluator +*/ + +#ifndef __expr_h_seen__ +#define __expr_h_seen__ + +#ifndef __expr_c_seen__ +#define __expr_E__ extern +#else +#define __expr_E__ +#endif + +// term types +#define LW_TERM_NONE 0 +#define LW_TERM_OPER 1 // an operator +#define LW_TERM_INT 2 // 32 bit signed integer +#define LW_TERM_SYM 3 // symbol reference + +// operator types +#define LW_OPER_NONE 0 +#define LW_OPER_PLUS 1 // + +#define LW_OPER_MINUS 2 // - +#define LW_OPER_TIMES 3 // * +#define LW_OPER_DIVIDE 4 // / +#define LW_OPER_MOD 5 // % +#define LW_OPER_INTDIV 6 // \ (don't end line with \) +#define LW_OPER_BWAND 7 // bitwise AND +#define LW_OPER_BWOR 8 // bitwise OR +#define LW_OPER_BWXOR 9 // bitwise XOR +#define LW_OPER_AND 10 // boolean AND +#define LW_OPER_OR 11 // boolean OR +#define LW_OPER_NEG 12 // - unary negation (2's complement) +#define LW_OPER_COM 13 // ^ unary 1's complement + + +// term structure +typedef struct lw_expr_term_s +{ + int term_type; // type of term (see above) + char *symbol; // name of a symbol + int value; // value of the term (int) or operator number (OPER) +} lw_expr_term_t; + +// type for an expression evaluation stack +typedef struct lw_expr_stack_node_s lw_expr_stack_node_t; +struct lw_expr_stack_node_s +{ + lw_expr_term_t *term; + lw_expr_stack_node_t *prev; + lw_expr_stack_node_t *next; +}; + +typedef struct lw_expr_stack_s +{ + lw_expr_stack_node_t *head; + lw_expr_stack_node_t *tail; +} lw_expr_stack_t; + +__expr_E__ void lw_expr_term_free(lw_expr_term_t *t); +__expr_E__ lw_expr_term_t *lw_expr_term_create_oper(int oper); +__expr_E__ lw_expr_term_t *lw_expr_term_create_sym(char *sym, int symtype); +__expr_E__ lw_expr_term_t *lw_expr_term_create_int(int val); +__expr_E__ lw_expr_term_t *lw_expr_term_dup(lw_expr_term_t *t); + +__expr_E__ void lw_expr_stack_free(lw_expr_stack_t *s); +__expr_E__ lw_expr_stack_t *lw_expr_stack_create(void); +__expr_E__ lw_expr_stack_t *lw_expr_stack_dup(lw_expr_stack_t *s); + +__expr_E__ void lw_expr_stack_push(lw_expr_stack_t *s, lw_expr_term_t *t); +__expr_E__ lw_expr_term_t *lw_expr_stack_pop(lw_expr_stack_t *s); + +// simplify expression +__expr_E__ int lw_expr_reval(lw_expr_stack_t *s, lw_expr_stack_t *(*sfunc)(char *sym, int symtype, void *state), void *state); + +// useful macros +// is the expression "simple" (one term)? +#define lw_expr_is_simple(s) ((s) -> head == (s) -> tail) + +// is the expression constant? +#define lw_expr_is_constant(s) (lw_expr_is_simple(s) && (!((s) -> head) || (s) -> head -> term -> term_type == LW_TERM_INT)) + +// get the constant value of an expression or 0 if not constant +#define lw_expr_get_value(s) (lw_expr_is_constant(s) ? ((s) -> head ? (s) -> head -> term -> value : 0) : 0) + +#undef __expr_E__ + +#endif // __expr_h_seen__
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lwlink/link.c Mon Apr 26 19:30:44 2010 -0600 @@ -0,0 +1,462 @@ +/* +link.c +Copyright © 2009 William Astle + +This file is part of LWLINK. + +LWLINK is free software: you can redistribute it and/or modify it under the +terms of the GNU General Public License as published by the Free Software +Foundation, either version 3 of the License, or (at your option) any later +version. + +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, see <http://www.gnu.org/licenses/>. + + +Resolve section and symbol addresses; handle incomplete references +*/ + +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> + +#include "expr.h" +#include "lwlink.h" +#include "util.h" + +struct section_list *sectlist = NULL; +int nsects = 0; +static int nforced = 0; + +void check_section_name(char *name, int *base, fileinfo_t *fn) +{ + int sn; + + if (fn -> forced == 0) + return; + + for (sn = 0; sn < fn -> nsections; sn++) + { + if (!strcmp(name, fn -> sections[sn].name)) + { + // we have a match + sectlist = lw_realloc(sectlist, sizeof(struct section_list) * (nsects + 1)); + sectlist[nsects].ptr = &(fn -> sections[sn]); + + fn -> sections[sn].processed = 1; + fn -> sections[sn].loadaddress = *base; + *base += fn -> sections[sn].codesize; + nsects++; + } + } + for (sn = 0; sn < fn -> nsubs; sn++) + { + check_section_name(name, base, fn -> subs[sn]); + } +} + +void add_matching_sections(char *name, int yesflags, int noflags, int *base); +void check_section_flags(int yesflags, int noflags, int *base, fileinfo_t *fn) +{ + int sn; + + if (fn -> forced == 0) + return; + + for (sn = 0; sn < fn -> nsections; sn++) + { + // ignore if the noflags tell us to + if (noflags && (fn -> sections[sn].flags & noflags)) + continue; + // ignore unless the yesflags tell us not to + if (yesflags && (fn -> sections[sn].flags & yesflags == 0)) + continue; + // ignore it if already processed + if (fn -> sections[sn].processed) + continue; + + // we have a match - now collect *all* sections of the same name! + add_matching_sections(fn -> sections[sn].name, 0, 0, base); + + // and then continue looking for sections + } + for (sn = 0; sn < fn -> nsubs; sn++) + { + check_section_flags(yesflags, noflags, base, fn -> subs[sn]); + } +} + + + +void add_matching_sections(char *name, int yesflags, int noflags, int *base) +{ + int fn; + if (name) + { + // named section + // look for all instances of a section by the specified name + // and resolve base addresses and add to the list + for (fn = 0; fn < ninputfiles; fn++) + { + check_section_name(name, base, inputfiles[fn]); + } + } + else + { + // wildcard section + // named section + // look for all instances of a section by the specified name + // and resolve base addresses and add to the list + for (fn = 0; fn < ninputfiles; fn++) + { + check_section_flags(yesflags, noflags, base, inputfiles[fn]); + } + } +} + +// work out section load order and resolve base addresses for each section +// make a list of sections to load in order +void resolve_sections(void) +{ + int laddr = 0; + int ln, sn, fn; + + for (ln = 0; ln < linkscript.nlines; ln++) + { + if (linkscript.lines[ln].loadat >= 0) + laddr = linkscript.lines[ln].loadat; + add_matching_sections(linkscript.lines[ln].sectname, linkscript.lines[ln].yesflags, linkscript.lines[ln].noflags, &laddr); + + if (linkscript.lines[ln].sectname) + { + } + else + { + // wildcard section + // look for all sections not yet processed that match flags + + int f = 0; + int fn0, sn0; + char *sname; + + // named section + // look for all instances of a section by the specified name + // and resolve base addresses and add to the list + for (fn0 = 0; fn0 < ninputfiles; fn0++) + { + for (sn0 = 0; sn0 < inputfiles[fn0] -> nsections; sn0++) + { + // ignore if the "no flags" bit says to + if (linkscript.lines[ln].noflags && (inputfiles[fn0] -> sections[sn0].flags & linkscript.lines[ln].noflags)) + continue; + // ignore unless the yes flags tell us not to + if (linkscript.lines[ln].yesflags && (inputfiles[fn0] -> sections[sn0].flags & linkscript.lines[ln].yesflags == 0)) + continue; + if (inputfiles[fn0] -> sections[sn0].processed == 0) + { + sname = inputfiles[fn0] -> sections[sn0].name; + for (fn = 0; fn < ninputfiles; fn++) + { + for (sn = 0; sn < inputfiles[fn] -> nsections; sn++) + { + if (!strcmp(sname, inputfiles[fn] -> sections[sn].name)) + { + // we have a match + sectlist = lw_realloc(sectlist, sizeof(struct section_list) * (nsects + 1)); + sectlist[nsects].ptr = &(inputfiles[fn] -> sections[sn]); + + inputfiles[fn] -> sections[sn].processed = 1; + if (!f && linkscript.lines[ln].loadat >= 0) + { + f = 1; + sectlist[nsects].forceaddr = 1; + laddr = linkscript.lines[ln].loadat; + } + else + { + sectlist[nsects].forceaddr = 0; + } + inputfiles[fn] -> sections[sn].loadaddress = laddr; + laddr += inputfiles[fn] -> sections[sn].codesize; + nsects++; + } + } + } + } + } + } + } + } + + // theoretically, all the base addresses are set now +} + +lw_expr_stack_t *find_external_sym_recurse(char *sym, fileinfo_t *fn) +{ + int sn; + lw_expr_stack_t *r; + lw_expr_term_t *term; + symtab_t *se; + int val; + + for (sn = 0; sn < fn -> nsections; sn++) + { + for (se = fn -> sections[sn].exportedsyms; se; se = se -> next) + { + if (!strcmp(sym, se -> sym)) + { + if (!(fn -> forced)) + { + fn -> forced = 1; + nforced = 1; + } + val = se -> offset + fn -> sections[sn].loadaddress; + r = lw_expr_stack_create(); + term = lw_expr_term_create_int(val & 0xffff); + lw_expr_stack_push(r, term); + lw_expr_term_free(term); + return r; + } + } + } + + for (sn = 0; sn < fn -> nsubs; sn++) + { + r = find_external_sym_recurse(sym, fn -> subs[sn]); + if (r) + { + if (!(fn -> forced)) + { + nforced = 1; + fn -> forced = 1; + } + return r; + } + } + return NULL; +} + +// resolve all incomplete references now +// anything that is unresolvable at this stage will throw an error +// because we know the load address of every section now +lw_expr_stack_t *resolve_sym(char *sym, int symtype, void *state) +{ + section_t *sect = state; + lw_expr_term_t *term; + int val = 0, i, fn; + lw_expr_stack_t *s; + symtab_t *se; + fileinfo_t *fp; + + if (symtype == 1) + { + // local symbol + if (!sym) + { + val = sect -> loadaddress; + goto out; + } + + // start with this section + for (se = sect -> localsyms; se; se = se -> next) + { + if (!strcmp(se -> sym, sym)) + { + val = se -> offset + sect -> loadaddress; + goto out; + } + } + // not in this section - check all sections in this file + for (i = 0; i < sect -> file -> nsections; i++) + { + for (se = sect -> file -> sections[i].localsyms; se; se = se -> next) + { + if (!strcmp(se -> sym, sym)) + { + val = se -> offset + sect -> file -> sections[i].loadaddress; + goto out; + } + } + } + // not found + symerr = 1; + fprintf(stderr, "Local symbol %s not found in %s:%s\n", sym, sect -> file -> filename, sect -> name); + goto outerr; + } + else + { + // external symbol + // read all files in order until found (or not found) + if (sect) + { + for (fp = sect -> file; fp; fp = fp -> parent) + { + s = find_external_sym_recurse(sym, fp); + if (s) + return s; + } + } + + for (fn = 0; fn < ninputfiles; fn++) + { + s = find_external_sym_recurse(sym, inputfiles[fn]); + if (s) + return s; + } + if (sect) + { + fprintf(stderr, "External symbol %s not found in %s:%s\n", sym, sect -> file -> filename, sect -> name); + } + else + { + fprintf(stderr, "External symbol %s not found\n", sym); + } + symerr = 1; + goto outerr; + } + fprintf(stderr, "Shouldn't ever get here!!!\n"); + exit(88); +out: + s = lw_expr_stack_create(); + term = lw_expr_term_create_int(val & 0xffff); + lw_expr_stack_push(s, term); + lw_expr_term_free(term); + return s; +outerr: + return NULL; +} + +void resolve_references(void) +{ + int sn; + reloc_t *rl; + int rval; + + // resolve entry point if required + // this must resolve to an *exported* symbol and will resolve to the + // first instance of that symbol + if (linkscript.execsym) + { + lw_expr_stack_t *s; + + s = resolve_sym(linkscript.execsym, 0, NULL); + if (!s) + { + fprintf(stderr, "Cannot resolve exec address '%s'\n", linkscript.execsym); + symerr = 1; + } + else + { + linkscript.execaddr = lw_expr_get_value(s); + lw_expr_stack_free(s); + } + } + + for (sn = 0; sn < nsects; sn++) + { + for (rl = sectlist[sn].ptr -> incompletes; rl; rl = rl -> next) + { + // do a "simplify" on the expression + rval = lw_expr_reval(rl -> expr, resolve_sym, sectlist[sn].ptr); + + // is it constant? error out if not + if (rval != 0 || !lw_expr_is_constant(rl -> expr)) + { + fprintf(stderr, "Incomplete reference at %s:%s+%02X\n", sectlist[sn].ptr -> file -> filename, sectlist[sn].ptr -> name, rl -> offset); + symerr = 1; + } + else + { + // put the value into the relocation address + rval = lw_expr_get_value(rl -> expr); + if (rl -> flags & RELOC_8BIT) + { + sectlist[sn].ptr -> code[rl -> offset] = rval & 0xff; + } + else + { + sectlist[sn].ptr -> code[rl -> offset] = (rval >> 8) & 0xff; + sectlist[sn].ptr -> code[rl -> offset + 1] = rval & 0xff; + } + } + } + } + + if (symerr) + exit(1); +} + +/* +This is just a pared down version of the algo for resolving references. +*/ +void resolve_files(void) +{ + int sn; + int fn; + reloc_t *rl; + lw_expr_stack_t *te; + + int rval; + + // resolve entry point if required + // this must resolve to an *exported* symbol and will resolve to the + // first instance of that symbol + if (linkscript.execsym) + { + lw_expr_stack_t *s; + + s = resolve_sym(linkscript.execsym, 0, NULL); + if (!s) + { + fprintf(stderr, "Cannot resolve exec address '%s'\n", linkscript.execsym); + symerr = 1; + } + } + + do + { + nforced = 0; + for (fn = 0; fn < ninputfiles; fn++) + { + if (inputfiles[fn] -> forced == 0) + continue; + + for (sn = 0; sn < inputfiles[fn] -> nsections; sn++) + { + for (rl = inputfiles[fn] -> sections[sn].incompletes; rl; rl = rl -> next) + { + // do a "simplify" on the expression + te = lw_expr_stack_dup(rl -> expr); + rval = lw_expr_reval(te, resolve_sym, &(inputfiles[fn] -> sections[sn])); + + // is it constant? error out if not + if (rval != 0 || !lw_expr_is_constant(te)) + { + fprintf(stderr, "Incomplete reference at %s:%s+%02X\n", inputfiles[fn] -> filename, inputfiles[fn] -> sections[sn].name, rl -> offset); + symerr = 1; + } + lw_expr_stack_free(te); + } + } + } + } + while (nforced == 1); + + if (symerr) + exit(1); + + // theoretically, all files referenced by other files now have "forced" set to 1 + for (fn = 0; fn < ninputfiles; fn++) + { + if (inputfiles[fn] -> forced == 1) + continue; + + fprintf(stderr, "Warning: %s (%d) does not resolve any symbols\n", inputfiles[fn] -> filename, fn); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lwlink/lwlink.c Mon Apr 26 19:30:44 2010 -0600 @@ -0,0 +1,107 @@ +/* +lwlink.c +Copyright © 2009 William Astle + +This file is part of LWLINK. + +LWLINK is free software: you can redistribute it and/or modify it under the +terms of the GNU General Public License as published by the Free Software +Foundation, either version 3 of the License, or (at your option) any later +version. + +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, see <http://www.gnu.org/licenses/>. + + + +*/ + +#include <config.h> + +#define __lwlink_c_seen__ + +#include <argp.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "lwlink.h" +#include "util.h" + +int debug_level = 0; +int outformat = OUTPUT_DECB; +char *outfile = NULL; +char *scriptfile = NULL; +int symerr = 0; +char *map_file = NULL; + +fileinfo_t **inputfiles = NULL; +int ninputfiles = 0; + +int nlibdirs = 0; +char **libdirs = NULL; + +int nscriptls = 0; +char **scriptls = NULL; + +void add_input_file(char *fn) +{ + inputfiles = lw_realloc(inputfiles, sizeof(fileinfo_t *) * (ninputfiles + 1)); + inputfiles[ninputfiles] = lw_malloc(sizeof(fileinfo_t)); + memset(inputfiles[ninputfiles], 0, sizeof(fileinfo_t)); + inputfiles[ninputfiles] -> forced = 1; + inputfiles[ninputfiles++] -> filename = lw_strdup(fn); +} + +void add_input_library(char *libname) +{ + inputfiles = lw_realloc(inputfiles, sizeof(fileinfo_t *) * (ninputfiles + 1)); + inputfiles[ninputfiles] = lw_malloc(sizeof(fileinfo_t)); + memset(inputfiles[ninputfiles], 0, sizeof(fileinfo_t)); + inputfiles[ninputfiles] -> islib = 1; + inputfiles[ninputfiles] -> forced = 0; + inputfiles[ninputfiles++] -> filename = lw_strdup(libname); +} + +void add_library_search(char *libdir) +{ + libdirs = lw_realloc(libdirs, sizeof(char*) * (nlibdirs + 1)); + libdirs[nlibdirs] = lw_strdup(libdir); + nlibdirs++; +} + +void add_section_base(char *sectspec) +{ + char *base; + int baseaddr; + char *t; + int l; + + base = strchr(sectspec, '='); + if (!base) + { + l = strlen(sectspec); + baseaddr = 0; + } + else + { + baseaddr = strtol(base + 1, NULL, 16); + l = base - sectspec; + *base = '\0'; + } + baseaddr = baseaddr & 0xffff; + + t = lw_malloc(l + 25); + sprintf(t, "section %s load %04X", sectspec, baseaddr); + if (base) + *base = '='; + + scriptls = lw_realloc(scriptls, sizeof(char *) * (nscriptls + 1)); + scriptls[nscriptls++] = t; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lwlink/lwlink.h Mon Apr 26 19:30:44 2010 -0600 @@ -0,0 +1,155 @@ +/* +lwlink.h +Copyright © 2009 William Astle + +This file is part of LWLINK. + +LWLINK is free software: you can redistribute it and/or modify it under the +terms of the GNU General Public License as published by the Free Software +Foundation, either version 3 of the License, or (at your option) any later +version. + +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, see <http://www.gnu.org/licenses/>. + +Contains the main defs used by the linker +*/ + + +#ifndef __lwlink_h_seen__ +#define __lwlink_h_seen__ + +#include "expr.h" + +#define OUTPUT_DECB 0 // DECB multirecord format +#define OUTPUT_RAW 1 // raw sequence of bytes +#define OUTPUT_LWEX0 2 // LWOS LWEX binary version 0 + +typedef struct symtab_s symtab_t; +struct symtab_s +{ + unsigned char *sym; // symbol name + int offset; // local offset +// int realval; // resolved value + symtab_t *next; // next symbol +}; + +#define RELOC_NORM 0 // all default (16 bit) +#define RELOC_8BIT 1 // only use the low 8 bits for the reloc +typedef struct reloc_s reloc_t; +struct reloc_s +{ + int offset; // where in the section + int flags; // flags for the relocation + lw_expr_stack_t *expr; // the expression to calculate it + reloc_t *next; // ptr to next relocation +}; + +typedef struct fileinfo_s fileinfo_t; + +#define SECTION_BSS 1 +typedef struct +{ + unsigned char *name; // name of the section + int flags; // section flags + int codesize; // size of the code + unsigned char *code; // pointer to the code + int loadaddress; // the actual load address of the section + int processed; // was the section processed yet? + + symtab_t *localsyms; // local symbol table + symtab_t *exportedsyms; // exported symbols table + + reloc_t *incompletes; // table of incomplete references + + fileinfo_t *file; // the file we are in +} section_t; + +struct fileinfo_s +{ + char *filename; + unsigned char *filedata; + long filesize; + section_t *sections; + int nsections; + int islib; // set to true if the file is a "-l" option + + int forced; // set to true if the file is a "forced" include + + // "sub" files (like in archives or libraries) + int nsubs; + fileinfo_t **subs; + fileinfo_t *parent; +}; + +struct section_list +{ + section_t *ptr; // ptr to section structure + int forceaddr; // was this force to an address by the link script? +}; + +#ifndef __link_c_seen__ +extern struct section_list *sectlist; +extern int nsects; +#endif + + +#ifndef __lwlink_c_seen__ + +extern int debug_level; +extern int outformat; +extern char *outfile; +extern int ninputfiles; +extern fileinfo_t **inputfiles; +extern char *scriptfile; + +extern int nlibdirs; +extern char **libdirs; + +extern int nscriptls; +extern char **scriptls; + +extern int symerr; + +extern char *map_file; + +#define __lwlink_E__ extern +#else +#define __lwlink_E__ +#endif // __lwlink_c_seen__ + +__lwlink_E__ void add_input_file(char *fn); +__lwlink_E__ void add_input_library(char *fn); +__lwlink_E__ void add_library_search(char *fn); +__lwlink_E__ void add_section_base(char *fn); + +#undef __lwlink_E__ + +struct scriptline_s +{ + char *sectname; // name of section, NULL for wildcard + int loadat; // address to load at (or -1) + int noflags; // flags to NOT have + int yesflags; // flags to HAVE +}; + +typedef struct +{ + int nlines; // number of lines in the script + struct scriptline_s *lines; // the parsed script lines (section) + int padsize; // the size to pad to, -1 for none + char *execsym; // entry point symbol + int execaddr; // execution address (entry point) + int stacksize; // stack size +} linkscript_t; + +#ifndef __script_c_seen__ +extern linkscript_t linkscript; +#endif + +#endif //__lwlink_h_seen__
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lwlink/main.c Mon Apr 26 19:30:44 2010 -0600 @@ -0,0 +1,196 @@ +/* +main.c +Copyright © 2009 William Astle + +This file is part of LWLINK. + +LWLINK is free software: you can redistribute it and/or modify it under the +terms of the GNU General Public License as published by the Free Software +Foundation, either version 3 of the License, or (at your option) any later +version. + +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, see <http://www.gnu.org/licenses/>. + + +Implements the program startup code + +*/ + +#include <config.h> + +#include <argp.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include "lwlink.h" + +char *program_name; + +// command line option handling +const char *argp_program_version = "LWLINK from " PACKAGE_STRING; +const char *argp_program_bug_address = PACKAGE_BUGREPORT; + +static error_t parse_opts(int key, char *arg, struct argp_state *state) +{ + switch (key) + { + case 'o': + // output + outfile = arg; + break; + + case 's': + // script file + scriptfile = arg; + break; + + case 'd': + // debug + debug_level++; + break; + + case 'b': + // decb output + outformat = OUTPUT_DECB; + break; + + case 'r': + // raw binary output + outformat = OUTPUT_RAW; + break; + + case 'f': + // output format + if (!strcasecmp(arg, "decb")) + outformat = OUTPUT_DECB; + else if (!strcasecmp(arg, "raw")) + outformat = OUTPUT_RAW; + else if (!strcasecmp(arg, "lwex0") || !strcasecmp(arg, "lwex")) + outformat = OUTPUT_LWEX0; + else + { + fprintf(stderr, "Invalid output format: %s\n", arg); + exit(1); + } + break; + case ARGP_KEY_END: + // done; sanity check + if (!outfile) + outfile = "a.out"; + break; + + case 'l': + add_input_library(arg); + break; + + case 'L': + add_library_search(arg); + break; + + case 0x100: + add_section_base(arg); + break; + + case 'm': + map_file = arg; + break; + + case ARGP_KEY_ARG: + add_input_file(arg); + break; + + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + +static struct argp_option options[] = +{ + { "output", 'o', "FILE", 0, + "Output to FILE"}, + { "debug", 'd', 0, 0, + "Set debug mode"}, + { "format", 'f', "TYPE", 0, + "Select output format: decb, raw, lwex"}, + { "decb", 'b', 0, 0, + "Generate DECB .bin format output, equivalent of --format=decb"}, + { "raw", 'r', 0, 0, + "Generate raw binary format output, equivalent of --format=raw"}, + { "script", 's', "FILE", 0, + "Specify the linking script (overrides the built in defaults)"}, + { "library", 'l', "LIBSPEC", 0, + "Read library libLIBSPEC.a from the search path" }, + { "library-path", 'L', "DIR", 0, + "Add DIR to the library search path" }, + { "section-base", 0x100, "SECT=BASE", 0, + "Load section SECT at BASE" }, + { "map", 'm', "FILE", 0, + "Output informaiton about the link" }, + { 0 } +}; + +static struct argp argp = +{ + options, + parse_opts, + "<input file> ...", + "LWLINK, a HD6309 and MC6809 cross-linker" +}; + +extern void read_files(void); +extern void setup_script(void); +extern void resolve_files(void); +extern void resolve_sections(void); +extern void resolve_references(void); +extern void do_output(void); +extern void display_map(void); + +// main function; parse command line, set up assembler state, and run the +// assembler on the first file +int main(int argc, char **argv) +{ + program_name = argv[0]; + + argp_parse(&argp, argc, argv, 0, 0, NULL); + if (ninputfiles == 0) + { + fprintf(stderr, "No input files\n"); + exit(1); + } + + unlink(outfile); + + // handle the linker script + setup_script(); + + // read the input files + read_files(); + + // trace unresolved references and determine which non-forced + // objects must be included + resolve_files(); + + // resolve section bases and section order + resolve_sections(); + + // resolve incomplete references + resolve_references(); + + // do the actual output + do_output(); + + // display/output the link map + if (map_file) + display_map(); + + exit(0); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lwlink/map.c Mon Apr 26 19:30:44 2010 -0600 @@ -0,0 +1,115 @@ +/* +map.c +Copyright © 2009 William Astle + +This file is part of LWLINK. + +LWLINK is free software: you can redistribute it and/or modify it under the +terms of the GNU General Public License as published by the Free Software +Foundation, either version 3 of the License, or (at your option) any later +version. + +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, see <http://www.gnu.org/licenses/>. + + +Output information about the linking process +*/ + +#include <config.h> + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> + +#include "lwlink.h" +#include "util.h" + +struct symliste +{ + char *name; + char *fn; + int addr; + int ext; + struct symliste *next; +}; + +void display_map(void) +{ + FILE *of; + int sn; + int std = 0; + struct symliste *slist = NULL; + struct symliste *ce, *pe, *ne; + symtab_t *sym; + int i; + + if (!strcmp(map_file, "-")) + { + std = 1; + of = stdout; + } + else + { + of = fopen(map_file, "w"); + if (!of) + { + fprintf(stderr, "Cannot open map file - using stdout\n"); + std = 1; + of = stdout; + } + } + + // display section list + for (sn = 0; sn < nsects; sn++) + { + fprintf(of, "Section: %s (%s) load at %04X, length %04X\n", + sectlist[sn].ptr -> name, + sectlist[sn].ptr -> file -> filename, + sectlist[sn].ptr -> loadaddress, + sectlist[sn].ptr -> codesize + ); + } + + // generate a sorted list of symbols and display it + for (sn = 0; sn < nsects; sn++) + { + for (sym = sectlist[sn].ptr -> localsyms; sym; sym = sym -> next) + { + for (pe = NULL, ce = slist; ce; ce = ce -> next) + { + i = strcmp(ce -> name, sym -> sym); + if (i == 0) + { + i = strcmp(ce -> fn, sectlist[sn].ptr -> file -> filename); + } + if (i > 0) + break; + pe = ce; + } + ne = lw_malloc(sizeof(struct symliste)); + ne -> ext = 0; + ne -> addr = sym -> offset + sectlist[sn].ptr -> loadaddress; + ne -> next = ce; + ne -> name = sym -> sym; + ne -> fn = sectlist[sn].ptr -> file -> filename; + if (pe) + pe -> next = ne; + else + slist = ne; + } + } + + for (ce = slist; ce; ce = ce -> next) + { + fprintf(of, "Symbol: %s (%s) = %04X\n", ce -> name, ce -> fn, ce -> addr); + } + + if (!std) + fclose(of); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lwlink/objdump.c Mon Apr 26 19:30:44 2010 -0600 @@ -0,0 +1,311 @@ +/* +objdump.c +Copyright © 2009 William Astle + +This file is part of LWLINK + +LWLINK is free software: you can redistribute it and/or modify it under the +terms of the GNU General Public License as published by the Free Software +Foundation, either version 3 of the License, or (at your option) any later +version. + +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, see <http://www.gnu.org/licenses/>. + + +A standalone program to dump an object file in a text form to stdout + +*/ +#include <config.h> +#include <stdio.h> +#include <stdlib.h> + +#include "util.h" + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +void read_lwobj16v0(unsigned char *filedata, long filesize); +char *program_name; + +/* +The logic of reading the entire file into memory is simple. All the symbol +names in the file are NUL terminated strings and can be used directly without +making additional copies. +*/ +int main(int argc, char **argv) +{ + int i; + long size; + FILE *f; + long bread; + unsigned char *filedata; + + program_name = argv[0]; + if (argc != 2) + { + fprintf(stderr, "Must specify exactly one input file.\n"); + exit(1); + } + + f = fopen(argv[1], "rb"); + if (!f) + { + fprintf(stderr, "Can't open file %s:", argv[1]); + perror(""); + exit(1); + } + fseek(f, 0, SEEK_END); + size = ftell(f); + rewind(f); + + filedata = lw_malloc(size); + + bread = fread(filedata, 1, size, f); + if (bread < size) + { + fprintf(stderr, "Short read on file %s (%ld/%ld):", argv[1], bread, size); + perror(""); + exit(1); + } + + fclose(f); + + if (!memcmp(filedata, "LWOBJ16", 8)) + { + // read v0 LWOBJ16 file + read_lwobj16v0(filedata, size); + } + else + { + fprintf(stderr, "%s: unknown file format\n", argv[1]); + exit(1); + } + exit(0); +} + +// this macro is used to bail out if we run off the end of the file data +// while parsing - it keeps the code below cleaner +#define NEXTBYTE() do { cc++; if (cc > filesize) { fprintf(stderr, "***invalid file format\n"); exit(1); } } while (0) +// this macro is used to refer to the current byte in the stream +#define CURBYTE() (filedata[cc < filesize ? cc : filesize - 1]) +// this one will leave the input pointer past the trailing NUL +#define CURSTR() read_lwobj16v0_str(&cc, &filedata, filesize) +unsigned char *read_lwobj16v0_str(long *cc1, unsigned char **filedata1, long filesize) +{ + int cc = *cc1; + unsigned char *filedata = *filedata1; + unsigned char *fp; + fp = &CURBYTE(); + while (CURBYTE()) + NEXTBYTE(); + NEXTBYTE(); + *cc1 = cc; + *filedata1 = filedata; + return fp; +} +// the function below can be switched to dealing with data coming from a +// source other than an in-memory byte pool by adjusting the input data +// in "fn" and the above two macros +void read_lwobj16v0(unsigned char *filedata, long filesize) +{ + unsigned char *fp; + long cc; + int val; + int bss; + + static char *opernames[] = { + "?", + "PLUS", + "MINUS", + "TIMES", + "DIVIDE", + "MOD", + "INTDIV", + "BWAND", + "BWOR", + "BWXOR", + "AND", + "OR", + "NEG", + "COM" + }; + static const int numopers = 13; + + // start reading *after* the magic number + cc = 8; + + while (1) + { + bss = 0; + + // bail out if no more sections + if (!(CURBYTE())) + break; + + fp = CURSTR(); + + printf("SECTION %s\n", fp); + + // read flags + while (CURBYTE()) + { + switch (CURBYTE()) + { + case 0x01: + printf(" FLAG: BSS\n"); + bss = 1; + break; + + default: + printf(" FLAG: %02X (unknown)\n", CURBYTE()); + break; + } + NEXTBYTE(); + } + // skip NUL terminating flags + NEXTBYTE(); + + printf(" Local symbols:\n"); + // now parse the local symbol table + while (CURBYTE()) + { + fp = CURSTR(); + + // fp is the symbol name + val = (CURBYTE()) << 8; + NEXTBYTE(); + val |= (CURBYTE()); + NEXTBYTE(); + // val is now the symbol value + + printf(" %s=%04X\n", fp, val); + + } + // skip terminating NUL + NEXTBYTE(); + + printf(" Exported symbols\n"); + + // now parse the exported symbol table + while (CURBYTE()) + { + fp = CURSTR(); + + // fp is the symbol name + val = (CURBYTE()) << 8; + NEXTBYTE(); + val |= (CURBYTE()); + NEXTBYTE(); + // val is now the symbol value + + printf(" %s=%04X\n", fp, val); + } + // skip terminating NUL + NEXTBYTE(); + + // now parse the incomplete references and make a list of + // external references that need resolution + printf(" Incomplete references\n"); + while (CURBYTE()) + { + printf(" ("); + // parse the expression + while (CURBYTE()) + { + int tt = CURBYTE(); + NEXTBYTE(); + switch (tt) + { + case 0x01: + // 16 bit integer + tt = CURBYTE() << 8; + NEXTBYTE(); + tt |= CURBYTE(); + NEXTBYTE(); + // normalize for negatives... + if (tt > 0x7fff) + tt -= 0x10000; + printf(" I16=%d", tt); + break; + + case 0x02: + // external symbol reference + printf(" ES=%s", CURSTR()); + break; + + case 0x03: + // internal symbol reference + printf(" IS=%s", CURSTR()); + break; + + case 0x04: + // operator + if (CURBYTE() > 0 && CURBYTE() <= numopers) + printf(" OP=%s", opernames[CURBYTE()]); + else + printf(" OP=?"); + NEXTBYTE(); + break; + + case 0x05: + // section base reference (NULL internal reference is + // the section base address + printf(" SB"); + break; + + case 0xFF: + // section flags + printf(" FLAGS=%02X", CURBYTE()); + NEXTBYTE(); + break; + + default: + printf(" ERR"); + } + } + // skip the NUL + NEXTBYTE(); + + // fetch the offset + val = CURBYTE() << 8; + NEXTBYTE(); + val |= CURBYTE() & 0xff; + NEXTBYTE(); + printf(" ) @ %04X\n", val); + } + // skip the NUL terminating the relocations + NEXTBYTE(); + + // now set code location and size and verify that the file + // contains data going to the end of the code (if !SECTION_BSS) + val = CURBYTE() << 8; + NEXTBYTE(); + val |= CURBYTE(); + NEXTBYTE(); + + printf(" CODE %04X bytes", val); + + // skip the code if we're not in a BSS section + if (!bss) + { + int i; + for (i = 0; i < val; i++) + { + if (! (i % 16)) + { + printf("\n %04X ", i); + } + printf("%02X", CURBYTE()); + NEXTBYTE(); + } + } + printf("\n"); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lwlink/output.c Mon Apr 26 19:30:44 2010 -0600 @@ -0,0 +1,217 @@ +/* +output.c +Copyright © 2009 William Astle + +This file is part of LWLINK. + +LWLINK is free software: you can redistribute it and/or modify it under the +terms of the GNU General Public License as published by the Free Software +Foundation, either version 3 of the License, or (at your option) any later +version. + +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, see <http://www.gnu.org/licenses/>. + + +Actually output the binary +*/ + +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "lwlink.h" + +// this prevents warnings about not using the return value of fwrite() +// and, theoretically, can be replaced with a function that handles things +// better in the future +#define writebytes(s, l, c, f) do { int r; r = fwrite((s), (l), (c), (f)); } while (0) + +void do_output_decb(FILE *of); +void do_output_raw(FILE *of); +void do_output_lwex0(FILE *of); + +void do_output(void) +{ + FILE *of; + + of = fopen(outfile, "wb"); + if (!of) + { + fprintf(stderr, "Cannot open output file %s: ", outfile); + perror(""); + exit(1); + } + + switch (outformat) + { + case OUTPUT_DECB: + do_output_decb(of); + break; + + case OUTPUT_RAW: + do_output_raw(of); + break; + + case OUTPUT_LWEX0: + do_output_lwex0(of); + break; + + default: + fprintf(stderr, "Unknown output format doing output!\n"); + exit(111); + } + + fclose(of); +} + +void do_output_decb(FILE *of) +{ + int sn, sn2; + int cloc, olen; + unsigned char buf[5]; + + for (sn = 0; sn < nsects; sn++) + { + if (sectlist[sn].ptr -> flags & SECTION_BSS) + { + // no output for a BSS section + continue; + } + if (sectlist[sn].ptr -> codesize == 0) + { + // don't generate output for a zero size section + continue; + } + + // calculate the length of this output block + cloc = sectlist[sn].ptr -> loadaddress; + olen = 0; + for (sn2 = sn; sn2 < nsects; sn2++) + { + // ignore BSS sections + if (sectlist[sn2].ptr -> flags & SECTION_BSS) + continue; + // ignore zero length sections + if (sectlist[sn2].ptr -> codesize == 0) + continue; + if (cloc != sectlist[sn2].ptr -> loadaddress) + break; + olen += sectlist[sn2].ptr -> codesize; + cloc += sectlist[sn2].ptr -> codesize; + } + + // write a preamble + buf[0] = 0x00; + buf[1] = olen >> 8; + buf[2] = olen & 0xff; + buf[3] = sectlist[sn].ptr -> loadaddress >> 8; + buf[4] = sectlist[sn].ptr -> loadaddress & 0xff; + writebytes(buf, 1, 5, of); + for (; sn < sn2; sn++) + { + if (sectlist[sn].ptr -> flags & SECTION_BSS) + continue; + if (sectlist[sn].ptr -> codesize == 0) + continue; + writebytes(sectlist[sn].ptr -> code, 1, sectlist[sn].ptr -> codesize, of); + } + sn--; + } + // write a postamble + buf[0] = 0xff; + buf[1] = 0x00; + buf[2] = 0x00; + buf[3] = linkscript.execaddr >> 8; + buf[4] = linkscript.execaddr & 0xff; + writebytes(buf, 1, 5, of); +} + +void do_output_raw(FILE *of) +{ + int nskips = 0; // used to output blanks for BSS inline + int sn; + + for (sn = 0; sn < nsects; sn++) + { + if (sectlist[sn].ptr -> flags & SECTION_BSS) + { + // no output for a BSS section + nskips += sectlist[sn].ptr -> codesize; + continue; + } + while (nskips > 0) + { + // the "" is not an error - it turns into a single NUL byte! + writebytes("", 1, 1, of); + nskips--; + } + writebytes(sectlist[sn].ptr -> code, 1, sectlist[sn].ptr -> codesize, of); + } +} + +void do_output_lwex0(FILE *of) +{ + int nskips = 0; // used to output blanks for BSS inline + int sn; + int codedatasize = 0; + unsigned char buf[32]; + + // calculate items for the file header + for (sn = 0; sn < nsects; sn++) + { + if (sectlist[sn].ptr -> flags & SECTION_BSS) + { + // no output for a BSS section + nskips += sectlist[sn].ptr -> codesize; + continue; + } + codedatasize += nskips; + nskips = 0; + codedatasize += sectlist[sn].ptr -> codesize; + } + + // output the file header + buf[0] = 'L'; + buf[1] = 'W'; + buf[2] = 'E'; + buf[3] = 'X'; + buf[4] = 0; // version 0 + buf[5] = 0; // low stack + buf[6] = linkscript.stacksize / 256; + buf[7] = linkscript.stacksize & 0xff; + buf[8] = nskips / 256; + buf[9] = nskips & 0xff; + buf[10] = codedatasize / 256; + buf[11] = codedatasize & 0xff; + buf[12] = linkscript.execaddr / 256; + buf[13] = linkscript.execaddr & 0xff; + memset(buf + 14, 0, 18); + + writebytes(buf, 1, 32, of); + // output the data + // NOTE: disjoint load addresses will not work correctly!!!!! + for (sn = 0; sn < nsects; sn++) + { + if (sectlist[sn].ptr -> flags & SECTION_BSS) + { + // no output for a BSS section + nskips += sectlist[sn].ptr -> codesize; + continue; + } + while (nskips > 0) + { + // the "" is not an error - it turns into a single NUL byte! + writebytes("", 1, 1, of); + nskips--; + } + writebytes(sectlist[sn].ptr -> code, 1, sectlist[sn].ptr -> codesize, of); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lwlink/readfiles.c Mon Apr 26 19:30:44 2010 -0600 @@ -0,0 +1,425 @@ +/* +readfiles.c +Copyright © 2009 William Astle + +This file is part of LWLINK. + +LWLINK is free software: you can redistribute it and/or modify it under the +terms of the GNU General Public License as published by the Free Software +Foundation, either version 3 of the License, or (at your option) any later +version. + +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, see <http://www.gnu.org/licenses/>. + + +Reads input files + +*/ + +#include <config.h> + +#include <argp.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "lwlink.h" +#include "util.h" + +void read_lwobj16v0(fileinfo_t *fn); +void read_lwar1v(fileinfo_t *fn); + +/* +The logic of reading the entire file into memory is simple. All the symbol +names in the file are NUL terminated strings and can be used directly without +making additional copies. +*/ +void read_file(fileinfo_t *fn) +{ + if (!memcmp(fn -> filedata, "LWOBJ16", 8)) + { + // read v0 LWOBJ16 file + read_lwobj16v0(fn); + } + else if (!memcmp(fn -> filedata, "LWAR1V", 6)) + { + // archive file + read_lwar1v(fn); + } + else + { + fprintf(stderr, "%s: unknown file format\n", fn -> filename); + exit(1); + } +} + +void read_files(void) +{ + int i; + long size; + FILE *f; + long bread; + for (i = 0; i < ninputfiles; i++) + { + if (inputfiles[i] -> islib) + { + char *tf; + int s; + int j; + + f = NULL; + + for (j = 0; j < nlibdirs; j++) + { + s = strlen(libdirs[j]) + 7 + strlen(inputfiles[i] -> filename); + tf = lw_malloc(s + 1); + sprintf(tf, "%s/lib%s.a", libdirs[j], inputfiles[i] -> filename); + f = fopen(tf, "rb"); + if (!f) + { + free(tf); + continue; + } + free(tf); + break; + } + if (!f) + { + fprintf(stderr, "Can't open library: -l%s\n", inputfiles[i] -> filename); + exit(1); + } + } + else + { + f = fopen(inputfiles[i] -> filename, "rb"); + if (!f) + { + fprintf(stderr, "Can't open file %s:", inputfiles[i] -> filename); + perror(""); + exit(1); + } + } + fseek(f, 0, SEEK_END); + size = ftell(f); + rewind(f); + + inputfiles[i] -> filedata = lw_malloc(size); + inputfiles[i] -> filesize = size; + + bread = fread(inputfiles[i] -> filedata, 1, size, f); + if (bread < size) + { + fprintf(stderr, "Short read on file %s (%ld/%ld):", inputfiles[i] -> filename, bread, size); + perror(""); + exit(1); + } + + fclose(f); + + read_file(inputfiles[i]); + } +} + +// this macro is used to bail out if we run off the end of the file data +// while parsing - it keeps the code below cleaner +#define NEXTBYTE() do { cc++; if (cc > fn -> filesize) { fprintf(stderr, "%s: invalid file format\n", fn -> filename); exit(1); } } while (0) +// this macro is used to refer to the current byte in the stream +#define CURBYTE() (fn -> filedata[cc < fn -> filesize ? cc : fn -> filesize - 1]) +// this one will leave the input pointer past the trailing NUL +#define CURSTR() read_lwobj16v0_str(&cc, fn) +unsigned char *read_lwobj16v0_str(long *cc1, fileinfo_t *fn) +{ + int cc = *cc1; + unsigned char *fp; + fp = &CURBYTE(); + while (CURBYTE()) + NEXTBYTE(); + NEXTBYTE(); + *cc1 = cc; + return fp; +} +// the function below can be switched to dealing with data coming from a +// source other than an in-memory byte pool by adjusting the input data +// in "fn" and the above two macros + +void read_lwobj16v0(fileinfo_t *fn) +{ + unsigned char *fp; + long cc; + section_t *s; + int val; + symtab_t *se; + + // start reading *after* the magic number + cc = 8; + + // init data + fn -> sections = NULL; + fn -> nsections = 0; + + while (1) + { +// NEXTBYTE(); + // bail out if no more sections + if (!(CURBYTE())) + break; + + fp = CURSTR(); + + // we now have a section name in fp + // create new section entry + fn -> sections = lw_realloc(fn -> sections, sizeof(section_t) * (fn -> nsections + 1)); + s = &(fn -> sections[fn -> nsections]); + fn -> nsections += 1; + + s -> localsyms = NULL; + s -> flags = 0; + s -> codesize = 0; + s -> name = fp; + s -> loadaddress = 0; + s -> localsyms = NULL; + s -> exportedsyms = NULL; + s -> incompletes = NULL; + s -> processed = 0; + s -> file = fn; + + // read flags + while (CURBYTE()) + { + switch (CURBYTE()) + { + case 0x01: + s -> flags |= SECTION_BSS; + break; + + default: + fprintf(stderr, "%s (%s): unrecognized section flag %02X\n", fn -> filename, s -> name, (int)(CURBYTE())); + exit(1); + } + NEXTBYTE(); + } + // skip NUL terminating flags + NEXTBYTE(); + + // now parse the local symbol table + while (CURBYTE()) + { + fp = CURSTR(); + + // fp is the symbol name + val = (CURBYTE()) << 8; + NEXTBYTE(); + val |= (CURBYTE()); + NEXTBYTE(); + // val is now the symbol value + + // create symbol table entry + se = lw_malloc(sizeof(symtab_t)); + se -> next = s -> localsyms; + s -> localsyms = se; + se -> sym = fp; + se -> offset = val; + } + // skip terminating NUL + NEXTBYTE(); + + // now parse the exported symbol table + while (CURBYTE()) + { + fp = CURSTR(); + + // fp is the symbol name + val = (CURBYTE()) << 8; + NEXTBYTE(); + val |= (CURBYTE()); + NEXTBYTE(); + // val is now the symbol value + + // create symbol table entry + se = lw_malloc(sizeof(symtab_t)); + se -> next = s -> exportedsyms; + s -> exportedsyms = se; + se -> sym = fp; + se -> offset = val; + } + // skip terminating NUL + NEXTBYTE(); + + // now parse the incomplete references and make a list of + // external references that need resolution + while (CURBYTE()) + { + reloc_t *rp; + lw_expr_term_t *term; + + // we have a reference + rp = lw_malloc(sizeof(reloc_t)); + rp -> next = s -> incompletes; + s -> incompletes = rp; + rp -> offset = 0; + rp -> expr = lw_expr_stack_create(); + rp -> flags = RELOC_NORM; + + // parse the expression + while (CURBYTE()) + { + int tt = CURBYTE(); + NEXTBYTE(); + switch (tt) + { + case 0xFF: + // a flag specifier + tt = CURBYTE(); + rp -> flags = tt; + NEXTBYTE(); + term = NULL; + break; + + case 0x01: + // 16 bit integer + tt = CURBYTE() << 8; + NEXTBYTE(); + tt |= CURBYTE(); + NEXTBYTE(); + // normalize for negatives... + if (tt > 0x7fff) + tt -= 0x10000; + term = lw_expr_term_create_int(tt); + break; + + case 0x02: + // external symbol reference + term = lw_expr_term_create_sym(CURSTR(), 0); + break; + + case 0x03: + // internal symbol reference + term = lw_expr_term_create_sym(CURSTR(), 1); + break; + + case 0x04: + // operator + term = lw_expr_term_create_oper(CURBYTE()); + NEXTBYTE(); + break; + + case 0x05: + // section base reference (NULL internal reference is + // the section base address + term = lw_expr_term_create_sym(NULL, 1); + break; + + default: + fprintf(stderr, "%s (%s): bad relocation expression (%02X)\n", fn -> filename, s -> name, tt); + exit(1); + } + if (term) + { + lw_expr_stack_push(rp -> expr, term); + lw_expr_term_free(term); + } + } + // skip the NUL + NEXTBYTE(); + + // fetch the offset + rp -> offset = CURBYTE() << 8; + NEXTBYTE(); + rp -> offset |= CURBYTE() & 0xff; + NEXTBYTE(); + } + // skip the NUL terminating the relocations + NEXTBYTE(); + + // now set code location and size and verify that the file + // contains data going to the end of the code (if !SECTION_BSS) + s -> codesize = CURBYTE() << 8; + NEXTBYTE(); + s -> codesize |= CURBYTE(); + NEXTBYTE(); + + s -> code = &(CURBYTE()); + + // skip the code if we're not in a BSS section + if (!(s -> flags & SECTION_BSS)) + { + int i; + for (i = 0; i < s -> codesize; i++) + NEXTBYTE(); + } + } +} + +/* +Read an archive file - this will create a "sub" record and farm out the +parsing of the sub files to the regular file parsers + +The archive file format consists of the 6 byte magic number followed by a +series of records as follows: + +- NUL terminated file name +- 32 bit file length in big endian order +- the file data + +An empty file name indicates the end of the file. + +*/ +void read_lwar1v(fileinfo_t *fn) +{ + unsigned long cc = 6; + unsigned long flen; + unsigned long l; + for (;;) + { + if (cc >= fn -> filesize || !(fn -> filedata[cc])) + return; + + for (l = cc; cc < fn -> filesize && fn -> filedata[cc]; cc++) + /* do nothing */ ; + + cc++; + + if (cc >= fn -> filesize) + { + fprintf(stderr, "Malformed archive file %s.\n", fn -> filename); + exit(1); + } + + if (cc + 4 > fn -> filesize) + return; + + flen = (fn -> filedata[cc++] << 24); + flen |= (fn -> filedata[cc++] << 16); + flen |= (fn -> filedata[cc++] << 8); + flen |= (fn -> filedata[cc++]); + + if (flen == 0) + return; + + if (cc + flen > fn -> filesize) + { + fprintf(stderr, "Malformed archive file %s.\n", fn -> filename); + exit(1); + } + + // add the "sub" input file + fn -> subs = lw_realloc(fn -> subs, sizeof(fileinfo_t *) * (fn -> nsubs + 1)); + fn -> subs[fn -> nsubs] = lw_malloc(sizeof(fileinfo_t)); + memset(fn -> subs[fn -> nsubs], 0, sizeof(fileinfo_t)); + fn -> subs[fn -> nsubs] -> filedata = fn -> filedata + cc; + fn -> subs[fn -> nsubs] -> filesize = flen; + fn -> subs[fn -> nsubs] -> filename = lw_strdup(fn -> filedata + l); + fn -> subs[fn -> nsubs] -> parent = fn; + fn -> subs[fn -> nsubs] -> forced = fn -> forced; + read_file(fn -> subs[fn -> nsubs]); + fn -> nsubs++; + cc += flen; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lwlink/script.c Mon Apr 26 19:30:44 2010 -0600 @@ -0,0 +1,297 @@ +/* +script.c +Copyright © 2009 William Astle + +This file is part of LWLINK. + +LWLINK is free software: you can redistribute it and/or modify it under the +terms of the GNU General Public License as published by the Free Software +Foundation, either version 3 of the License, or (at your option) any later +version. + +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, see <http://www.gnu.org/licenses/>. + + +Read and parse linker scripts +*/ + +#include <config.h> + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "lwlink.h" +#include "util.h" + +// the built-in DECB target linker script +static char *decb_script = + "section init load 2000\n" + "section code\n" + "section *,!bss\n" + "section *,bss\n" + "entry 2000\n" + ; + +// the built-in RAW target linker script +static char *raw_script = + "section init load 0000\n" + "section code\n" + "section *,!bss\n" + "section *,bss\n" + ; + +static char *lwex0_script = + "section init load 0100\n" + "section .text\n" + "section .data\n" + "section *,!bss\n" + "section *,bss\n" + "entry __start\n" + "stacksize 0100\n" // default 256 byte stack + ; + +// the "simple" script +static char *simple_script = + "section *,!bss\n" + "section *,bss\n" + ; + +linkscript_t linkscript = { 0, NULL, -1 }; + +void setup_script() +{ + char *script, *oscript; + long size; + + // read the file if needed + if (scriptfile) + { + FILE *f; + long bread; + + f = fopen(scriptfile, "rb"); + if (!f) + { + fprintf(stderr, "Can't open file %s:", scriptfile); + perror(""); + exit(1); + } + fseek(f, 0, SEEK_END); + size = ftell(f); + rewind(f); + + script = lw_malloc(size + 2); + + bread = fread(script, 1, size, f); + if (bread < size) + { + fprintf(stderr, "Short read on file %s (%ld/%ld):", scriptfile, bread, size); + perror(""); + exit(1); + } + fclose(f); + + script[size] = '\n'; + script[size + 1] = '\0'; + } + else + { + // fetch defaults based on output mode + switch (outformat) + { + case OUTPUT_RAW: + script = raw_script; + break; + + case OUTPUT_DECB: + script = decb_script; + break; + + case OUTPUT_LWEX0: + script = lwex0_script; + break; + + default: + script = simple_script; + break; + } + + size = strlen(script); + if (nscriptls) + { + char *rscript; + int i; + // prepend the "extra" script lines + for (i = 0; i < nscriptls; i++) + size += strlen(scriptls[i]) + 1; + + rscript = lw_malloc(size + 1); + oscript = rscript; + for (i = 0; i < nscriptls; i++) + { + oscript += sprintf(oscript, "%s\n", scriptls[i]); + } + strcpy(oscript, script); + script = rscript; + } + } + + if (outformat == OUTPUT_LWEX0) + linkscript.stacksize = 0x100; + + oscript = script; + // now parse the script file + while (*script) + { + char *ptr, *ptr2, *line; + + for (ptr = script; *ptr && *ptr != '\n' && *ptr != '\r'; ptr++) + /* do nothing */ ; + + line = lw_malloc(ptr - script + 1); + memcpy(line, script, ptr - script); + line[ptr - script] = '\0'; + + // skip line terms + for (script = ptr + 1; *script == '\n' || *script == '\r'; script++) + /* do nothing */ ; + + // ignore leading whitespace + for (ptr = line; *ptr && isspace(*ptr); ptr++) + /* do nothing */ ; + + // ignore blank lines + if (!*ptr) + continue; + + for (ptr = line; *ptr && !isspace(*ptr); ptr++) + /* do nothing */ ; + + // now ptr points to the char past the first word + // NUL it out + if (*ptr) + *ptr++ = '\0'; + + // skip spaces after the first word + for ( ; *ptr && isspace(*ptr); ptr++) + /* do nothing */ ; + + if (!strcmp(line, "pad")) + { + // padding + // parse the hex number and stow it + linkscript.padsize = strtol(ptr, NULL, 16); + if (linkscript.padsize < 0) + linkscript.padsize = 0; + } + else if (!strcmp(line, "stacksize")) + { + // stack size for targets that support it + // parse the hex number and stow it + linkscript.padsize = strtol(ptr, NULL, 16); + if (linkscript.stacksize < 0) + linkscript.stacksize = 0x100; + } + else if (!strcmp(line, "entry")) + { + int eaddr; + + eaddr = strtol(ptr, &ptr2, 16); + if (*ptr2) + { + linkscript.execaddr = -1; + linkscript.execsym = lw_strdup(ptr); + } + else + { + linkscript.execaddr = eaddr; + linkscript.execsym = NULL; + } + } + else if (!strcmp(line, "section")) + { + // section + // parse out the section name and flags + for (ptr2 = ptr; *ptr2 && !isspace(*ptr2); ptr2++) + /* do nothing */ ; + + if (*ptr2) + *ptr2++ = '\0'; + + while (*ptr2 && isspace(*ptr2)) + ptr2++; + + // ptr now points to the section name and flags and ptr2 + // to the first non-space character following + + // then look for "load <addr>" clause + if (*ptr2) + { + if (!strncmp(ptr2, "load", 4)) + { + ptr2 += 4; + while (*ptr2 && isspace(*ptr2)) + ptr2++; + + } + else + { + fprintf(stderr, "%s: bad script\n", scriptfile); + exit(1); + } + } + + // now ptr2 points to the load address if there is one + // or NUL if not + linkscript.lines = lw_realloc(linkscript.lines, sizeof(struct scriptline_s) * (linkscript.nlines + 1)); + + linkscript.lines[linkscript.nlines].noflags = 0; + linkscript.lines[linkscript.nlines].yesflags = 0; + if (*ptr2) + linkscript.lines[linkscript.nlines].loadat = strtol(ptr2, NULL, 16); + else + linkscript.lines[linkscript.nlines].loadat = -1; + for (ptr2 = ptr; *ptr2 && *ptr2 != ','; ptr2++) + /* do nothing */ ; + if (*ptr2) + { + *ptr2++ = '\0'; + if (!strcmp(ptr2, "!bss")) + { + linkscript.lines[linkscript.nlines].noflags = SECTION_BSS; + } + else if (!strcmp(ptr2, "bss")) + { + linkscript.lines[linkscript.nlines].yesflags = SECTION_BSS; + } + else + { + fprintf(stderr, "%s: bad script\n", scriptfile); + exit(1); + } + } + if (ptr[0] == '*' && ptr[1] == '\0') + linkscript.lines[linkscript.nlines].sectname = NULL; + else + linkscript.lines[linkscript.nlines].sectname = lw_strdup(ptr); + linkscript.nlines++; + } + else + { + fprintf(stderr, "%s: bad script\n", scriptfile); + exit(1); + } + lw_free(line); + } + + if (scriptfile || nscriptls) + lw_free(oscript); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lwlink/util.c Mon Apr 26 19:30:44 2010 -0600 @@ -0,0 +1,88 @@ +/* +util.c +Copyright © 2009 William Astle + +This file is part of LWLINK. + +LWLINK is free software: you can redistribute it and/or modify it under the +terms of the GNU General Public License as published by the Free Software +Foundation, either version 3 of the License, or (at your option) any later +version. + +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, see <http://www.gnu.org/licenses/>. +*/ + +/* +Utility functions +*/ + +#define __util_c_seen__ +#include <config.h> + +#include <malloc.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "util.h" + +void *lw_malloc(int size) +{ + void *ptr; + + ptr = malloc(size); + if (!ptr) + { + // bail out; memory allocation error + fprintf(stderr, "lw_malloc(): Memory allocation error\n"); + exit(1); + } + return ptr; +} + +void *lw_realloc(void *optr, int size) +{ + void *ptr; + + if (size == 0) + { + lw_free(optr); + return; + } + + ptr = realloc(optr, size); + if (!ptr) + { + fprintf(stderr, "lw_realloc(): memory allocation error\n"); + exit(1); + } +} + +void lw_free(void *ptr) +{ + if (ptr) + free(ptr); +} + +char *lw_strdup(const char *s) +{ + char *d; + + if (!s) + return NULL; + + d = strdup(s); + if (!d) + { + fprintf(stderr, "lw_strdup(): memory allocation error\n"); + exit(1); + } + + return d; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lwlink/util.h Mon Apr 26 19:30:44 2010 -0600 @@ -0,0 +1,44 @@ +/* +util.h +Copyright © 2009 William Astle + +This file is part of LWLINK. + +LWLINK is free software: you can redistribute it and/or modify it under the +terms of the GNU General Public License as published by the Free Software +Foundation, either version 3 of the License, or (at your option) any later +version. + +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, see <http://www.gnu.org/licenses/>. +*/ + +/* +Utility functions +*/ + +#ifndef __util_h_seen__ +#define __util_h_seen__ + +#ifndef __util_c_seen__ +#define __util_E__ extern +#else +#define __util_E__ +#endif + +// allocate memory +__util_E__ void *lw_malloc(int size); +__util_E__ void lw_free(void *ptr); +__util_E__ void *lw_realloc(void *optr, int size); + +// string stuff +__util_E__ char *lw_strdup(const char *s); + +#undef __util_E__ + +#endif // __util_h_seen__