/**************************************************************** -*- C -*- ** ** Copyright (C) 1999 ** Patryk Zadarnowski, University of New South Wales. All rights reserved. ** ** Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are met: ** (1) Redistributions of source code must retain the above copyright notice, ** this list of conditions and the following disclaimer. (2) Redistributions ** in binary form must reproduce the above copyright notice, this list of ** conditions and the following disclaimer in the documentation and/or other ** materials provided with the distribution. (3) The name of the author may ** not be used to endorse or promote products derived from this software ** without specific prior written permission. ** ** THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, ** INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY ** AND FITNESS FOR A PARTICULAR PURPOSE, ARE DISCLAIMED. IN NO EVENT SHALL ** THE AUTHOR OR THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY ** OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ** NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ** * -*- Created: Mon Feb 8 09:20:04 1999 -*- * -*- Last modified: Mon Feb 8 12:43:07 1999 -*- * * Any comments and bug reports should be sent to patrykz@cse.unsw.edu.au. */ #include <stdio.h> #include <stdlib.h> #include <string.h> /***************************************************************************** * This is a quick and dirty port of the BootLinker.java file to C. * BootLinker.java is part of the original Topsy distribution. */ const char *outfile = "segmap.bin"; int completed = 0; /* non-zero when processing completed */ static void cleanup(void); static void write_integer(FILE *fp, const char *name, unsigned long x); static void parse_size_file(FILE *in, const char *in_name, FILE *out, const char *out_name); static void parse_size_line(FILE *in, const char *in_name, const char *keyword, int *addr, int *size); int main(int argc, char *argv[]) { unsigned long x; char *endptr; char s[2048]; FILE *segmap_file, *ksize_file, *isize_file, *user_srec; if (argc != 6) { fprintf(stderr, "Usage:\n" "\tbootlinker <kSizeFile> <iSizeFile> <user.srec> \n" "\t <segmap address> <user in kernel at>\n", (argc < 5 ? "Not enough" : "Too many")); return EXIT_FAILURE; } /* Create files. */ ksize_file = fopen(argv[1], "r"); if (ksize_file == NULL) { perror(argv[1]); return EXIT_FAILURE; } isize_file = fopen(argv[2], "r"); if (isize_file == NULL) { perror(argv[2]); return EXIT_FAILURE; } user_srec = fopen(argv[3], "r"); if (user_srec == NULL) { perror(argv[3]); return EXIT_FAILURE; } /* Open the output file and register cleanup handler. */ segmap_file = fopen(outfile, "w+"); if (segmap_file == NULL) { perror(outfile); return EXIT_FAILURE; } atexit(cleanup); /* Write the segment map (sizes and start addresses of kernel and * user segments). We assume that the size file was generated by * gnu-size with options -Ax (addresses, hex) ex.: size -Ax */ parse_size_file(ksize_file, argv[1], segmap_file, outfile); parse_size_file(isize_file, argv[2], segmap_file, outfile); /* Fetch the S7 record from user.srec and dump it to the segment map. */ while (fgets(s, sizeof(s), user_srec)) { if (s[0] == 'S' && s[1] == '7') { /* Write the following 8-digit hexadecimal integer. */ s[12] = '\0'; endptr = NULL; x = strtoul(s + 4, &endptr, 16); if (endptr == NULL || *endptr != '\0') { fprintf(stderr, "%s: invalid srec file\n", argv[3]); return EXIT_FAILURE; } write_integer(segmap_file, outfile, x); } } if (ferror(user_srec)) { perror(argv[3]); return EXIT_FAILURE; } /* Write the user-in-kernel address at the end. */ endptr = NULL; x = strtoul(argv[5], &endptr, 16); if (endptr == NULL || *endptr != '\0') { fprintf(stderr, "bootlinker: invalid user-in-kernel-addr \"%s\"\n", argv[5]); return EXIT_FAILURE; } write_integer(segmap_file, outfile, x); completed = 1; return EXIT_SUCCESS; } /* Write a 32 bit big-endian integer. */ static void write_integer(FILE *fp, const char *name, unsigned long x) { fputc((x >> 24) & 0xff, fp); fputc((x >> 16) & 0xff, fp); fputc((x >> 8) & 0xff, fp); fputc((x) & 0xff, fp); if (ferror(fp)) { perror(name); exit(EXIT_FAILURE); } } /* Remove a partial output file. */ static void cleanup(void) { if (!completed) remove(outfile); } /* Parse a size file. */ static void parse_size_file(FILE *in, const char *in_name, FILE *out, const char *out_name) { int text_addr = 0, addr = 0; int text_size = 0, size = 0; /* Read the text segment and store the information in the segment map. */ parse_size_line(in, in_name, ".text", &text_addr, &text_size); write_integer(out, out_name, text_size); write_integer(out, out_name, text_addr); /* Read data segments, add up the sizes and find the start address * as a minimum of all start addresses. */ parse_size_line(in, in_name, ".data", &addr, &size); parse_size_line(in, in_name, ".rdata", &addr, &size); parse_size_line(in, in_name, ".bss", &addr, &size); parse_size_line(in, in_name, ".sbss", &addr, &size); /* Write the summary to the segment map. */ write_integer(out, out_name, size); write_integer(out, out_name, addr); } static void parse_size_line(FILE *in, const char *in_name, const char *keyword, int *addr, int *size) { size_t n = strlen(keyword); char s[2048]; /* Find the keyword in file. */ if (fseek(in, 0, SEEK_SET)) { perror(in_name); exit(EXIT_FAILURE); } while (fgets(s, sizeof(s), in)) { int x, y; if (memcmp(keyword, s, n) == 0) { /* Match found. Read the following two integers. */ if (sscanf(s + n, "%x%x", &x, &y) != 2) { fprintf(stderr, "%s: size-file syntax error\n", in_name); exit(EXIT_FAILURE); } /* Adjust size. */ *size += x; /* If new minimum start address found, adjust addr. */ if (*addr == 0 || (unsigned)*addr > (unsigned)y) *addr = y; return; } } if (ferror(in)) { perror(in_name); exit(EXIT_FAILURE); } }