diff --git a/usr.bin/genassym/Makefile b/usr.bin/genassym/Makefile new file mode 100644 index 000000000000..c85dc9b4be98 --- /dev/null +++ b/usr.bin/genassym/Makefile @@ -0,0 +1,9 @@ +# $FreeBSD$ + +TARGET_ARCH?= ${MACHINE_ARCH} + +PROG= genassym +MAN8= genassym.8 +CFLAGS+= -Wall -Darch_${TARGET_ARCH} + +.include diff --git a/usr.bin/genassym/genassym.8 b/usr.bin/genassym/genassym.8 new file mode 100644 index 000000000000..8ee2581eacda --- /dev/null +++ b/usr.bin/genassym/genassym.8 @@ -0,0 +1,68 @@ +.\" +.\" $FreeBSD$ +.\" +.Dd December 20, 1999 +.Dt GENASSYM 8 +.Os +.Sh NAME +.Nm genassym +.Nd generate assembler symbols from C +.Sh SYNOPSIS +.Nm genassym +.Op Fl o Ar outfile +.Ar objfile +.Sh DESCRIPTION +The +.Nm +command is a special-purpose program to generate assembler +symbols from C code and is used to interface the low-level +assembly code with the C code. This, for example, is used +to build a FreeBSD kernel or module. +Its +.Ar objfile +argument is the name of an ELF object file that holds the +symbol definitions. These definitions are extracted from +the object file and written to standard output or to the +file specified with +.Ar outfile , +suitable for inclusion in assembler source files. +.Pp +The +.Nm +command only extracts symbols from the object file if they +are prefixed by +.Nm assym_ +and are global data types, whose value is the value given +to the symbol. The following C declaration +.Bd -literal -offset indent -compact +int assym_MY_SYMBOL = 3; +.Ed +is used to create the following assembler symbol. +.Bd -literal -offset indent -compact +#define MY_SYMBOL 0x3 +.Ed +Note that the size of the symbol is extracted from the +object file, which means that the symbol may have any type +that is wide enough to hold the value. +.Sh SEE ALSO +.Xr config 8 +.Xr gensetdefs 8 +.Sh AUTHORS +The +.Nm +command was written by +.An Marcel Moolenaar Aq marcel@FreeBSD.org +and was based on the +.Dv gensetdefs +command. +.Sh BUGS +Not all linkers store the size of the symbol in the ELF +object file. The GNU linker for Alpha has this bug for +example (binutils 2.9.1). In those cases the size of the +symbol is assumed to be equal to the word size of the ELF +object file. For Alpha this is 64 bits and for i386 this +is 32 bits. +.Sh HISTORY +The +.Nm +command first appeared in FreeBSD 4.0. diff --git a/usr.bin/genassym/genassym.c b/usr.bin/genassym/genassym.c new file mode 100644 index 000000000000..52a1877d0222 --- /dev/null +++ b/usr.bin/genassym/genassym.c @@ -0,0 +1,271 @@ +/*- + * Copyright (c) 1999 Marcel Moolenaar + * 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 + * in this position and unchanged. + * 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 withough specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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. + * + * $FreeBSD$ + */ + +#include +#if defined(arch_i386) +#include +#define __ELF_WORD_SIZE 32 +#elif defined(arch_alpha) +#include +#define __ELF_WORD_SIZE 64 +#else +#error unknown or missing architecture +#endif +#include + +#include +#include +#include +#include +#include +#include + +const char s_data[] = ".data"; +const char s_strtab[] = ".strtab"; +const char s_symtab[] = ".symtab"; +const char assym[] = "assym_"; + +int fd; +char *objfile; +Elf_Ehdr ehdr; +Elf_Shdr *shdr; +char *shstr; + +int +my_byte_order(void) +{ + static unsigned short s = 0xbbaa; + int byte0; + + byte0 = *(unsigned char *)&s; + if (byte0 == 0xaa) + return (ELFDATA2LSB); + else if (byte0 == 0xbb) + return (ELFDATA2MSB); + return (ELFDATANONE); +} + +void * +read_section(int index) +{ + void *buf; + size_t size; + ssize_t bytes; + + size = shdr[index].sh_size; + buf = malloc(size); + if (buf == NULL) + return (NULL); + if (lseek(fd, shdr[index].sh_offset, SEEK_SET) == -1) + return (NULL); + bytes = read(fd, buf, size); + if (bytes == -1) + return (NULL); + if (bytes != size) + warnx("%s: section %d partially read", objfile, index); + return (buf); +} + +int section_index(const char *section) +{ + int i; + + for (i = 1; i < ehdr.e_shnum; i++) + if (!strcmp(section, shstr + shdr[i].sh_name)) + return (i); + return (-1); +} + +char * +filter(char *name) +{ + char *dot; + + name += sizeof(assym) - 1; + dot = strchr(name, '.'); + if (dot != NULL) + *dot = '\0'; + return (name); +} + +void usage(void) +{ + fprintf(stderr, "usage: genassym [-o outfile] objfile\n"); + exit(1); + /* NOT REACHED */ +} + +int +main(int argc, char *argv[]) +{ + Elf_Sym *sym; + char *data, *name, *symbols; + char *outfile; + void *valp; + size_t size; + ssize_t bytes; + int ch, i, numsym, warn_ld_bug; + int si_data, si_strtab, si_symtab; + u_int64_t value; + + outfile = NULL; + warn_ld_bug = 1; + + while ((ch = getopt(argc, argv, "o:")) != -1) { + switch (ch) { + case 'o': + outfile = optarg; + break; + default: + usage(); + /* NOT REACHED */ + } + } + argc -= optind; + argv += optind; + + if (argc == 0) { + usage(); + /* NOT REACHED */ + } + if (argc > 1) + warnx("ignoring trailing arguments"); + + objfile = argv[0]; + fd = open(objfile, O_RDONLY); + if (fd == -1) + err(1, "%s", objfile); + + bytes = read(fd, &ehdr, sizeof(ehdr)); + if (bytes == -1) + err(1, "%s", objfile); + if (bytes != sizeof(ehdr) || + ehdr.e_ident[EI_MAG0] != ELFMAG0 || + ehdr.e_ident[EI_MAG1] != ELFMAG1 || + ehdr.e_ident[EI_MAG2] != ELFMAG2 || + ehdr.e_ident[EI_MAG3] != ELFMAG3) + errx(1, "%s: not an ELF file", objfile); + if (ehdr.e_ident[EI_VERSION] != EV_CURRENT) + errx(1, "%s: unsupported ELF version", objfile); + if (ehdr.e_ident[EI_DATA] != my_byte_order()) + errx(1, "%s: unsupported byte order", objfile); + if (ehdr.e_shoff == 0) + errx(1, "%s: no section table", objfile); + if (ehdr.e_shstrndx == SHN_UNDEF) + errx(1, "%s: no section name string table", objfile); + + size = sizeof(*shdr) * ehdr.e_shnum; + shdr = malloc(size); + if (shdr == NULL) + err(1, "malloc"); + if (lseek(fd, ehdr.e_shoff, SEEK_SET) == -1) + err(1, "%s", objfile); + bytes = read(fd, shdr, size); + if (bytes == -1) + err(1, "%s", objfile); + if (bytes != size) + errx(1, "%s: truncated section table", objfile); + + shstr = read_section(ehdr.e_shstrndx); + if (shstr == NULL) + err(1, "%s[%d]", objfile, ehdr.e_shstrndx); + + si_data = section_index(s_data); + if (si_data == -1) + errx(1, "%s: section %s not present", objfile, s_data); + data = read_section(si_data); + if (data == NULL) + err(1, "%s[%d]", objfile, si_data); + + si_strtab = section_index(s_strtab); + if (si_strtab == -1) + errx(1, "%s: section %s not present", objfile, s_strtab); + symbols = read_section(si_strtab); + if (symbols == NULL) + err(1, "%s[%d]", objfile, si_strtab); + + si_symtab = section_index(s_symtab); + if (si_symtab == -1) + errx(1, "%s: section %s not present", objfile, s_symtab); + sym = read_section(si_symtab); + if (sym == NULL) + err(1, "%s[%d]", objfile, si_symtab); + + numsym = shdr[si_symtab].sh_size / sizeof(*sym); + + if (outfile != NULL) + freopen(outfile, "w", stdout); + + for (i = 0; i < numsym; i++) { + name = symbols + sym[i].st_name; + if (sym[i].st_shndx == si_data && + !strncmp(name, assym, sizeof(assym) - 1)) { + valp = (void*)(data + sym[i].st_value); + /* + * XXX - ld(1) on Alpha doesn't store the size of + * the symbol in the object file. The following + * fix handles this case quite genericly. It + * assumes that the symbols have the same size as + * a word on that architecture, determined by the + * word size in the ELF object file. + */ + if (sym[i].st_size == 0) { + sym[i].st_size = __ELF_WORD_SIZE >> 3; + if (warn_ld_bug) { + warnx("%s: symbol sizes not properly" + " set", objfile); + warn_ld_bug = 0; + } + } + switch (sym[i].st_size) { + case 1: + value = *(u_int8_t*)valp; + break; + case 2: + value = *(u_int16_t*)valp; + break; + case 4: + value = *(u_int32_t*)valp; + break; + case 8: + value = *(u_int64_t*)valp; + break; + default: + warnx("unsupported size (%lld) for symbol %s", + (long long)sym[i].st_size, filter(name)); + continue; + } + fprintf(stdout, "#define\t%s 0x%llx\n", filter(name), + (long long)value); + } + } + + return (0); +}