diff options
author | DJ Delorie <dj@redhat.com> | 2010-11-02 15:20:47 +0000 |
---|---|---|
committer | DJ Delorie <dj@redhat.com> | 2010-11-02 15:20:47 +0000 |
commit | ffa54e5c48223b42840f93e56a5eff69998ab4e1 (patch) | |
tree | 3201d128b72a1f1d6eaa8599ff205444b1496888 | |
parent | 877791769e23126090bab077d41f4d6221a23045 (diff) | |
download | gdb-ffa54e5c48223b42840f93e56a5eff69998ab4e1.zip gdb-ffa54e5c48223b42840f93e56a5eff69998ab4e1.tar.gz gdb-ffa54e5c48223b42840f93e56a5eff69998ab4e1.tar.bz2 |
merge from gcc
-rw-r--r-- | include/ChangeLog | 4 | ||||
-rw-r--r-- | include/simple-object.h | 203 | ||||
-rw-r--r-- | libiberty/ChangeLog | 19 | ||||
-rw-r--r-- | libiberty/Makefile.in | 48 | ||||
-rw-r--r-- | libiberty/config.in | 3 | ||||
-rwxr-xr-x | libiberty/configure | 11 | ||||
-rw-r--r-- | libiberty/configure.ac | 1 | ||||
-rw-r--r-- | libiberty/functions.texi | 180 | ||||
-rw-r--r-- | libiberty/simple-object-coff.c | 804 | ||||
-rw-r--r-- | libiberty/simple-object-common.h | 355 | ||||
-rw-r--r-- | libiberty/simple-object-elf.c | 916 | ||||
-rw-r--r-- | libiberty/simple-object-mach-o.c | 1022 | ||||
-rw-r--r-- | libiberty/simple-object.c | 423 | ||||
-rw-r--r-- | libiberty/simple-object.txh | 168 |
14 files changed, 4152 insertions, 5 deletions
diff --git a/include/ChangeLog b/include/ChangeLog index 73ccb4d..83c6738 100644 --- a/include/ChangeLog +++ b/include/ChangeLog @@ -1,3 +1,7 @@ +2010-11-02 Ian Lance Taylor <iant@google.com> + + * simple-object.h: New file. + 2010-10-15 Dave Korn <dave.korn.cygwin@gmail.com> Sync LD plugin patch series (part 1/6) with src/include/. diff --git a/include/simple-object.h b/include/simple-object.h new file mode 100644 index 0000000..a72e4a1 --- /dev/null +++ b/include/simple-object.h @@ -0,0 +1,203 @@ +/* simple-object.h -- simple routines to read and write object files + Copyright 2010 Free Software Foundation, Inc. + Written by Ian Lance Taylor, Google. + +This program 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 2, 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, write to the Free Software +Foundation, 51 Franklin Street - Fifth Floor, +Boston, MA 02110-1301, USA. */ + +#ifndef SIMPLE_OBJECT_H +#define SIMPLE_OBJECT_H + +#include <stddef.h> +#include <sys/types.h> + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* This header file provides four types with associated functions. + They are used to read and write object files. This is a minimal + interface, intended to support the needs of gcc without bringing in + all the power and complexity of BFD. */ + +/* The type simple_object_read * is used to read an existing object + file. */ + +typedef struct simple_object_read_struct simple_object_read; + +/* Create an simple_object_read given DESCRIPTOR, an open file + descriptor, and OFFSET, an offset within the file. The offset is + for use with archives, and should be 0 for an ordinary object file. + The descriptor must remain open until done with the returned + simple_object_read. SEGMENT_NAME is used on Mach-O and is required + on that platform: it means to only look at sections within the + segment with that name. It is ignored for other object file + formats. On error, this function returns NULL, and sets *ERRMSG to + an error string and sets *ERR to an errno value or 0 if there is no + relevant errno. */ + +extern simple_object_read * +simple_object_start_read (int descriptor, off_t offset, + const char *segment_name, const char **errmsg, + int *err); + +/* Call PFN for each section in SIMPLE_OBJECT, passing it the section + name, offset within the file of the section contents, and length of + the section contents. The offset within the file is relative to + the offset passed to simple_object_start_read. The DATA argument + to simple_object_find_sections is passed on to PFN. If PFN returns + 0, the loop is stopped and simple_object_find_sections returns. If + PFN returns non-zero, the loop continues. On success this returns + NULL. On error it returns an error string, and sets *ERR to an + errno value or 0 if there is no relevant errno. */ + +extern const char * +simple_object_find_sections (simple_object_read *simple_object, + int (*pfn) (void *data, const char *, + off_t offset, off_t length), + void *data, + int *err); + +/* Look for the section NAME in SIMPLE_OBJECT. This returns + information for the first section NAME in SIMPLE_OBJECT. Note that + calling this multiple times is inefficient; use + simple_object_find_sections instead. + + If found, return 1 and set *OFFSET to the offset in the file of the + section contents and set *LENGTH to the length of the section + contents. *OFFSET will be relative to the offset passed to + simple_object_start_read. + + If the section is not found, and no error occurs, return 0 and set + *ERRMSG to NULL. + + If an error occurs, return 0, set *ERRMSG to an error message, and + set *ERR to an errno value or 0 if there is no relevant errno. */ + +extern int +simple_object_find_section (simple_object_read *simple_object, + const char *name, off_t *offset, off_t *length, + const char **errmsg, int *err); + +/* Release all resources associated with SIMPLE_OBJECT. This does not + close the file descriptor. */ + +extern void +simple_object_release_read (simple_object_read *); + +/* The type simple_object_attributes holds the attributes of an object + file that matter for creating a file or ensuring that two files are + compatible. This is a set of magic numbers. */ + +typedef struct simple_object_attributes_struct simple_object_attributes; + +/* Fetch the attributes of SIMPLE_OBJECT. This information will + persist until simple_object_attributes_release is called, even if + SIMPLE_OBJECT is closed. On error this returns NULL, sets *ERRMSG + to an error message, and sets *ERR to an errno value or 0 if there + isn't one. */ + +extern simple_object_attributes * +simple_object_fetch_attributes (simple_object_read *simple_object, + const char **errmsg, int *err); + +/* Compare ATTRS1 and ATTRS2. If they could be linked together + without error, return NULL. Otherwise, return an error message, + set *ERR to an errno value or 0 if there isn't one. */ + +extern const char * +simple_object_attributes_compare (simple_object_attributes *attrs1, + simple_object_attributes *attrs2, + int *err); + +/* Release all resources associated with ATTRS. */ + +extern void +simple_object_release_attributes (simple_object_attributes *attrs); + +/* The type simple_object_write is used to create a new object file. */ + +typedef struct simple_object_write_struct simple_object_write; + +/* Start creating a new object file which is like ATTRS. You must + fetch attribute information from an existing object file before you + can create a new one. There is currently no support for creating + an object file de novo. The segment name is only used on Mach-O, + where it is required. It means that all sections are created + within that segment. It is ignored for other object file formats. + On error this function returns NULL, sets *ERRMSG to an error + message, and sets *ERR to an errno value or 0 if there isn't + one. */ + +extern simple_object_write * +simple_object_start_write (simple_object_attributes *attrs, + const char *segment_name, + const char **errmsg, int *err); + +/* The type simple_object_write_section is a handle for a section + which is being written. */ + +typedef struct simple_object_write_section_struct simple_object_write_section; + +/* Add a section to SIMPLE_OBJECT. NAME is the name of the new + section. ALIGN is the required alignment expressed as the number + of required low-order 0 bits (e.g., 2 for alignment to a 32-bit + boundary). The section is created as containing data, readable, + not writable, not executable, not loaded at runtime. On error this + returns NULL, sets *ERRMSG to an error message, and sets *ERR to an + errno value or 0 if there isn't one. */ + +extern simple_object_write_section * +simple_object_write_create_section (simple_object_write *simple_object, + const char *name, unsigned int align, + const char **errmsg, int *err); + +/* Add data BUFFER/SIZE to SECTION in SIMPLE_OBJECT. If COPY is + non-zero, the data will be copied into memory if necessary. If + COPY is zero, BUFFER must persist until SIMPLE_OBJECT is released. + On success this returns NULL. On error this returns an error + message, and sets *ERR to an errno value or 0 if there isn't + one. */ + +extern const char * +simple_object_write_add_data (simple_object_write *simple_object, + simple_object_write_section *section, + const void *buffer, size_t size, + int copy, int *err); + +/* Write the complete object file to DESCRIPTOR, an open file + descriptor. This returns NULL on success. On error this returns + an error message, and sets *ERR to an errno value or 0 if there + isn't one. */ + +extern const char * +simple_object_write_to_file (simple_object_write *simple_object, + int descriptor, int *err); + +/* Release all resources associated with SIMPLE_OBJECT, including any + simple_object_write_section's that may have been created. */ + +extern void +simple_object_release_write (simple_object_write *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libiberty/ChangeLog b/libiberty/ChangeLog index 3d00793..fa38b09 100644 --- a/libiberty/ChangeLog +++ b/libiberty/ChangeLog @@ -1,3 +1,22 @@ +2010-11-02 Ian Lance Taylor <iant@google.com> + Dave Korn <dave.korn.cygwin@gmail.com> + Iain Sandoe <iains@gcc.gnu.org> + + * simple-object.c: New file. + * simple-object-common.h: New file. + * simple-object-elf.c: New file. + * simple-object-mach-o.c: New file. + * simple-object-coff.c: New file. + * simple-object.txh: New file. + * configure.ac: Add AC_TYPE_SSIZE_T. + * Makefile.in: Rebuild dependencies. + (CFILES): Add simple-object.c, simple-object-coff, + simple-object-elf.c, and simple-object-mach-o.c. + (REQUIRED_OFILES): Add corresponding object files. + * configure: Rebuild. + * config.in: Rebuild. + * functions.texi: Rebuild. + 2010-10-29 Ian Lance Taylor <iant@google.com> * setproctitle.c: Add space after function name in @deftypefn diff --git a/libiberty/Makefile.in b/libiberty/Makefile.in index 1893254..7a8bb02 100644 --- a/libiberty/Makefile.in +++ b/libiberty/Makefile.in @@ -2,8 +2,8 @@ # Originally written by K. Richard Pixley <rich@cygnus.com>. # # Copyright (C) 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, -# 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 Free Software -# Foundation +# 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 +# Free Software Foundation # # This file is part of the libiberty library. # Libiberty is free software; you can redistribute it and/or @@ -145,6 +145,8 @@ CFILES = alloca.c argv.c asprintf.c atexit.c \ physmem.c putenv.c \ random.c regex.c rename.c rindex.c \ safe-ctype.c setenv.c setproctitle.c sha1.c sigsetmask.c \ + simple-object.c simple-object-coff.c simple-object-elf.c \ + simple-object-mach-o.c \ snprintf.c sort.c \ spaces.c splay-tree.c stpcpy.c stpncpy.c strcasecmp.c \ strchr.c strdup.c strerror.c strncasecmp.c strncmp.c \ @@ -172,11 +174,15 @@ REQUIRED_OFILES = \ ./getruntime.$(objext) ./hashtab.$(objext) ./hex.$(objext) \ ./lbasename.$(objext) ./lrealpath.$(objext) \ ./make-relative-prefix.$(objext) ./make-temp-file.$(objext) \ - ./objalloc.$(objext) ./obstack.$(objext) \ + ./objalloc.$(objext) \ + ./obstack.$(objext) \ ./partition.$(objext) ./pexecute.$(objext) ./physmem.$(objext) \ ./pex-common.$(objext) ./pex-one.$(objext) \ ./@pexecute@.$(objext) \ - ./safe-ctype.$(objext) ./sort.$(objext) ./spaces.$(objext) \ + ./safe-ctype.$(objext) \ + ./simple-object.$(objext) ./simple-object-coff.$(objext) \ + ./simple-object-elf.$(objext) ./simple-object-mach-o.$(objext) \ + ./sort.$(objext) ./spaces.$(objext) \ ./splay-tree.$(objext) ./strerror.$(objext) \ ./strsignal.$(objext) ./unlink-if-ordinary.$(objext) \ ./xatexit.$(objext) ./xexit.$(objext) ./xmalloc.$(objext) \ @@ -312,7 +318,7 @@ TEXISRC = \ # Additional files that have texi snippets that need to be collected # and sorted. Some are here because the sources are imported from # elsewhere. Others represent headers in ../include. -TEXIFILES = fnmatch.txh pexecute.txh +TEXIFILES = fnmatch.txh pexecute.txh simple-object.txh libiberty.info : $(srcdir)/libiberty.texi $(TEXISRC) $(MAKEINFO) -I$(srcdir) $(srcdir)/libiberty.texi @@ -965,6 +971,38 @@ $(CONFIGURED_OFILES): stamp-picdir else true; fi $(COMPILE.c) $(srcdir)/sigsetmask.c $(OUTPUT_OPTION) +./simple-object-coff.$(objext): $(srcdir)/simple-object-coff.c config.h \ + $(INCDIR)/ansidecl.h $(INCDIR)/libiberty.h \ + $(srcdir)/simple-object-common.h $(INCDIR)/simple-object.h + if [ x"$(PICFLAG)" != x ]; then \ + $(COMPILE.c) $(PICFLAG) $(srcdir)/simple-object-coff.c -o pic/$@; \ + else true; fi + $(COMPILE.c) $(srcdir)/simple-object-coff.c $(OUTPUT_OPTION) + +./simple-object-elf.$(objext): $(srcdir)/simple-object-elf.c config.h \ + $(INCDIR)/ansidecl.h $(INCDIR)/libiberty.h \ + $(srcdir)/simple-object-common.h $(INCDIR)/simple-object.h + if [ x"$(PICFLAG)" != x ]; then \ + $(COMPILE.c) $(PICFLAG) $(srcdir)/simple-object-elf.c -o pic/$@; \ + else true; fi + $(COMPILE.c) $(srcdir)/simple-object-elf.c $(OUTPUT_OPTION) + +./simple-object-mach-o.$(objext): $(srcdir)/simple-object-mach-o.c config.h \ + $(INCDIR)/ansidecl.h $(INCDIR)/libiberty.h \ + $(srcdir)/simple-object-common.h $(INCDIR)/simple-object.h + if [ x"$(PICFLAG)" != x ]; then \ + $(COMPILE.c) $(PICFLAG) $(srcdir)/simple-object-mach-o.c -o pic/$@; \ + else true; fi + $(COMPILE.c) $(srcdir)/simple-object-mach-o.c $(OUTPUT_OPTION) + +./simple-object.$(objext): $(srcdir)/simple-object.c config.h \ + $(INCDIR)/ansidecl.h $(INCDIR)/libiberty.h \ + $(srcdir)/simple-object-common.h $(INCDIR)/simple-object.h + if [ x"$(PICFLAG)" != x ]; then \ + $(COMPILE.c) $(PICFLAG) $(srcdir)/simple-object.c -o pic/$@; \ + else true; fi + $(COMPILE.c) $(srcdir)/simple-object.c $(OUTPUT_OPTION) + ./snprintf.$(objext): $(srcdir)/snprintf.c $(INCDIR)/ansidecl.h if [ x"$(PICFLAG)" != x ]; then \ $(COMPILE.c) $(PICFLAG) $(srcdir)/snprintf.c -o pic/$@; \ diff --git a/libiberty/config.in b/libiberty/config.in index 02d93da..0e5f3d2 100644 --- a/libiberty/config.in +++ b/libiberty/config.in @@ -467,6 +467,9 @@ /* Define to `int' if <sys/types.h> does not define. */ #undef pid_t +/* Define to `int' if <sys/types.h> does not define. */ +#undef ssize_t + /* Define to the type of an unsigned integer type wide enough to hold a pointer, if such a type exists, and if the system does not define it. */ #undef uintptr_t diff --git a/libiberty/configure b/libiberty/configure index 142c81c..3d7ba30 100755 --- a/libiberty/configure +++ b/libiberty/configure @@ -5203,6 +5203,17 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi +ac_fn_c_check_type "$LINENO" "ssize_t" "ac_cv_type_ssize_t" "$ac_includes_default" +if test "x$ac_cv_type_ssize_t" = x""yes; then : + +else + +cat >>confdefs.h <<_ACEOF +#define ssize_t int +_ACEOF + +fi + # Given the above check, we always have uintptr_t or a fallback # definition. So define HAVE_UINTPTR_T in case any imported code diff --git a/libiberty/configure.ac b/libiberty/configure.ac index 73ea6c9..8136f25 100644 --- a/libiberty/configure.ac +++ b/libiberty/configure.ac @@ -290,6 +290,7 @@ fi AC_TYPE_INTPTR_T AC_TYPE_UINTPTR_T +AC_TYPE_SSIZE_T # Given the above check, we always have uintptr_t or a fallback # definition. So define HAVE_UINTPTR_T in case any imported code diff --git a/libiberty/functions.texi b/libiberty/functions.texi index af8c4bf..b3543cb 100644 --- a/libiberty/functions.texi +++ b/libiberty/functions.texi @@ -1181,6 +1181,186 @@ be the value @code{1}). @end deftypefn +@c simple-object.txh:87 +@deftypefn Extension {const char *} simple_object_attributes_compare (simple_object_attributes *@var{attrs1}, simple_object_attributes *@var{attrs2}, int *@var{err}) + +Compare @var{attrs1} and @var{attrs2}. If they could be linked +together without error, return @code{NULL}. Otherwise, return an +error message and set @code{*@var{err}} to an errno value or @code{0} +if there is no relevant errno. + +@end deftypefn + +@c simple-object.txh:73 +@deftypefn Extension {simple_object_attributes *} simple_object_fetch_attributes (simple_object_read *@var{simple_object}, const char **@var{errmsg}, int *@var{err}) + +Fetch the attributes of @var{simple_object}. The attributes are +internal information such as the format of the object file, or the +architecture it was compiled for. This information will persist until +@code{simple_object_attributes_release} is called, even if +@var{simple_object} itself is released. + +On error this returns @code{NULL}, sets @code{*@var{errmsg}} to an +error message, and sets @code{*@var{err}} to an errno value or +@code{0} if there is no relevant errno. + +@end deftypefn + +@c simple-object.txh:44 +@deftypefn Extension {int} simple_object_find_section (simple_object_read *@var{simple_object} off_t *@var{offset}, off_t *@var{length}, const char **@var{errmsg}, int *@var{err}) + +Look for the section @var{name} in @var{simple_object}. This returns +information for the first section with that name. + +If found, return 1 and set @code{*@var{offset}} to the offset in the +file of the section contents and set @code{*@var{length}} to the +length of the section contents. The value in @code{*@var{offset}} +will be relative to the offset passed to +@code{simple_object_open_read}. + +If the section is not found, and no error occurs, +@code{simple_object_find_section} returns @code{0} and set +@code{*@var{errmsg}} to @code{NULL}. + +If an error occurs, @code{simple_object_find_section} returns +@code{0}, sets @code{*@var{errmsg}} to an error message, and sets +@code{*@var{err}} to an errno value or @code{0} if there is no +relevant errno. + +@end deftypefn + +@c simple-object.txh:25 +@deftypefn Extension {const char *} simple_object_find_sections (simple_object_read *@var{simple_object}, int (*@var{pfn}) (void *@var{data}, const char *@var{name}, off_t @var{offset}, off_t @var{length}), void *@var{data}, int *@var{err}) + +This function calls @var{pfn} for each section in @var{simple_object}. +It calls @var{pfn} with the section name, the offset within the file +of the section contents, and the length of the section contents. The +offset within the file is relative to the offset passed to +@code{simple_object_open_read}. The @var{data} argument to this +function is passed along to @var{pfn}. + +If @var{pfn} returns @code{0}, the loop over the sections stops and +@code{simple_object_find_sections} returns. If @var{pfn} returns some +other value, the loop continues. + +On success @code{simple_object_find_sections} returns. On error it +returns an error string, and sets @code{*@var{err}} to an errno value +or @code{0} if there is no relevant errno. + +@end deftypefn + +@c simple-object.txh:2 +@deftypefn Extension {simple_object_read *} simple_object_open_read (int @var{descriptor}, off_t @var{offset}, const char *{segment_name}, const char **@var{errmsg}, int *@var{err}) + +Opens an object file for reading. Creates and returns an +@code{simple_object_read} pointer which may be passed to other +functions to extract data from the object file. + +@var{descriptor} holds a file descriptor which permits reading. + +@var{offset} is the offset into the file; this will be @code{0} in the +normal case, but may be a different value when reading an object file +in an archive file. + +@var{segment_name} is only used with the Mach-O file format used on +Darwin aka Mac OS X. It is required on that platform, and means to +only look at sections within the segment with that name. The +parameter is ignored on other systems. + +If an error occurs, this functions returns @code{NULL} and sets +@code{*@var{errmsg}} to an error string and sets @code{*@var{err}} to +an errno value or @code{0} if there is no relevant errno. + +@end deftypefn + +@c simple-object.txh:96 +@deftypefn Extension {void} simple_object_release_attributes (simple_object_attributes *@var{attrs}) + +Release all resources associated with @var{attrs}. + +@end deftypefn + +@c simple-object.txh:66 +@deftypefn Extension {void} simple_object_release_read (simple_object_read *@var{simple_object}) + +Release all resources associated with @var{simple_object}. This does +not close the file descriptor. + +@end deftypefn + +@c simple-object.txh:164 +@deftypefn Extension {void} simple_object_release_write (simple_object_write *@var{simple_object}) + +Release all resources associated with @var{simple_object}. + +@end deftypefn + +@c simple-object.txh:102 +@deftypefn Extension {simple_object_write *} simple_object_start_write (simple_object_attributes @var{attrs}, const char *@var{segment_name}, const char **@var{errmsg}, int *@var{err}) + +Start creating a new object file using the object file format +described in @var{attrs}. You must fetch attribute information from +an existing object file before you can create a new one. There is +currently no support for creating an object file de novo. + +@var{segment_name} is only used with Mach-O as found on Darwin aka Mac +OS X. The parameter is required on that target. It means that all +sections are created within the named segment. It is ignored for +other object file formats. + +On error @code{simple_object_start_write} returns @code{NULL}, sets +@code{*@var{ERRMSG}} to an error message, and sets @code{*@var{err}} +to an errno value or @code{0} if there is no relevant errno. + +@end deftypefn + +@c simple-object.txh:137 +@deftypefn Extension {const char *} simple_object_write_add_data (simple_object_write *@var{simple_object}, simple_object_write_section *@var{section}, const void *@var{buffer}, size_t @var{size}, int @var{copy}, int *@var{err}) + +Add data @var{buffer}/@var{size} to @var{section} in +@var{simple_object}. If @var{copy} is non-zero, the data will be +copied into memory if necessary. If @var{copy} is zero, @var{buffer} +must persist until @code{simple_object_write_to_file} is called. is +released. + +On success this returns @code{NULL}. On error this returns an error +message, and sets @code{*@var{err}} to an errno value or 0 if there is +no relevant erro. + +@end deftypefn + +@c simple-object.txh:120 +@deftypefn Extension {simple_object_write_section *} simple_object_write_create_section (simple_object_write *@var{simple_object}, const char *@var{name}, unsigned int @var{align}, const char **@var{errmsg}, int *@var{err}) + +Add a section to @var{simple_object}. @var{name} is the name of the +new section. @var{align} is the required alignment expressed as the +number of required low-order 0 bits (e.g., 2 for alignment to a 32-bit +boundary). + +The section is created as containing data, readable, not writable, not +executable, not loaded at runtime. The section is not written to the +file until @code{simple_object_write_to_file} is called. + +On error this returns @code{NULL}, sets @code{*@var{errmsg}} to an +error message, and sets @code{*@var{err}} to an errno value or +@code{0} if there is no relevant errno. + +@end deftypefn + +@c simple-object.txh:151 +@deftypefn Extension {const char *} simple_object_write_to_file (simple_object_write *@var{simple_object}, int @var{descriptor}, int *@var{err}) + +Write the complete object file to @var{descriptor}, an open file +descriptor. This writes out all the data accumulated by calls to +@code{simple_object_write_create_section} and +@var{simple_object_write_add_data}. + +This returns @code{NULL} on success. On error this returns an error +message and sets @code{*@var{err}} to an errno value or @code{0} if +there is no relevant errno. + +@end deftypefn + @c snprintf.c:28 @deftypefn Supplemental int snprintf (char *@var{buf}, size_t @var{n}, const char *@var{format}, ...) diff --git a/libiberty/simple-object-coff.c b/libiberty/simple-object-coff.c new file mode 100644 index 0000000..9ba1dd4 --- /dev/null +++ b/libiberty/simple-object-coff.c @@ -0,0 +1,804 @@ +/* simple-object-coff.c -- routines to manipulate COFF object files. + Copyright 2010 Free Software Foundation, Inc. + Written by Ian Lance Taylor, Google. + +This program 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 2, 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, write to the Free Software +Foundation, 51 Franklin Street - Fifth Floor, +Boston, MA 02110-1301, USA. */ + +#include "config.h" +#include "libiberty.h" +#include "simple-object.h" + +#include <errno.h> +#include <stddef.h> + +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif + +#ifdef HAVE_STDINT_H +#include <stdint.h> +#endif + +#ifdef HAVE_STRING_H +#include <string.h> +#endif + +#ifdef HAVE_INTTYPES_H +#include <inttypes.h> +#endif + +#include "simple-object-common.h" + +/* COFF structures and constants. */ + +/* COFF file header. */ + +struct external_filehdr +{ + unsigned char f_magic[2]; /* magic number */ + unsigned char f_nscns[2]; /* number of sections */ + unsigned char f_timdat[4]; /* time & date stamp */ + unsigned char f_symptr[4]; /* file pointer to symtab */ + unsigned char f_nsyms[4]; /* number of symtab entries */ + unsigned char f_opthdr[2]; /* sizeof(optional hdr) */ + unsigned char f_flags[2]; /* flags */ +}; + +/* Bits for filehdr f_flags field. */ + +#define F_EXEC (0x0002) +#define IMAGE_FILE_SYSTEM (0x1000) +#define IMAGE_FILE_DLL (0x2000) + +/* COFF section header. */ + +struct external_scnhdr +{ + unsigned char s_name[8]; /* section name */ + unsigned char s_paddr[4]; /* physical address, aliased s_nlib */ + unsigned char s_vaddr[4]; /* virtual address */ + unsigned char s_size[4]; /* section size */ + unsigned char s_scnptr[4]; /* file ptr to raw data for section */ + unsigned char s_relptr[4]; /* file ptr to relocation */ + unsigned char s_lnnoptr[4]; /* file ptr to line numbers */ + unsigned char s_nreloc[2]; /* number of relocation entries */ + unsigned char s_nlnno[2]; /* number of line number entries */ + unsigned char s_flags[4]; /* flags */ +}; + +/* The length of the s_name field in struct external_scnhdr. */ + +#define SCNNMLEN (8) + +/* Bits for scnhdr s_flags field. This includes some bits defined + only for PE. This may need to be moved into coff_magic. */ + +#define STYP_DATA (1 << 6) +#define IMAGE_SCN_MEM_DISCARDABLE (1 << 25) +#define IMAGE_SCN_MEM_SHARED (1 << 28) +#define IMAGE_SCN_MEM_READ (1 << 30) + +#define IMAGE_SCN_ALIGN_POWER_BIT_POS 20 +#define IMAGE_SCN_ALIGN_POWER_CONST(val) \ + (((val) + 1) << IMAGE_SCN_ALIGN_POWER_BIT_POS) + +/* COFF symbol table entry. */ + +#define E_SYMNMLEN 8 /* # characters in a symbol name */ + +struct external_syment +{ + union + { + unsigned char e_name[E_SYMNMLEN]; + + struct + { + unsigned char e_zeroes[4]; + unsigned char e_offset[4]; + } e; + } e; + + unsigned char e_value[4]; + unsigned char e_scnum[2]; + unsigned char e_type[2]; + unsigned char e_sclass[1]; + unsigned char e_numaux[1]; +}; + +/* Length allowed for filename in aux sym format 4. */ + +#define E_FILNMLEN 18 + +/* Omits x_sym and other unused variants. */ + +union external_auxent +{ + /* Aux sym format 4: file. */ + union + { + char x_fname[E_FILNMLEN]; + struct + { + unsigned char x_zeroes[4]; + unsigned char x_offset[4]; + } x_n; + } x_file; + /* Aux sym format 5: section. */ + struct + { + unsigned char x_scnlen[4]; /* section length */ + unsigned char x_nreloc[2]; /* # relocation entries */ + unsigned char x_nlinno[2]; /* # line numbers */ + unsigned char x_checksum[4]; /* section COMDAT checksum */ + unsigned char x_associated[2]; /* COMDAT assoc section index */ + unsigned char x_comdat[1]; /* COMDAT selection number */ + } x_scn; +}; + +/* Symbol-related constants. */ + +#define IMAGE_SYM_DEBUG (-2) +#define IMAGE_SYM_TYPE_NULL (0) +#define IMAGE_SYM_DTYPE_NULL (0) +#define IMAGE_SYM_CLASS_STATIC (3) +#define IMAGE_SYM_CLASS_FILE (103) + +#define IMAGE_SYM_TYPE \ + ((IMAGE_SYM_DTYPE_NULL << 4) | IMAGE_SYM_TYPE_NULL) + +/* Private data for an simple_object_read. */ + +struct simple_object_coff_read +{ + /* Magic number. */ + unsigned short magic; + /* Whether the file is big-endian. */ + unsigned char is_big_endian; + /* Number of sections. */ + unsigned short nscns; + /* File offset of symbol table. */ + off_t symptr; + /* Number of symbol table entries. */ + unsigned int nsyms; + /* Flags. */ + unsigned short flags; + /* Offset of section headers in file. */ + off_t scnhdr_offset; +}; + +/* Private data for an simple_object_attributes. */ + +struct simple_object_coff_attributes +{ + /* Magic number. */ + unsigned short magic; + /* Whether the file is big-endian. */ + unsigned char is_big_endian; + /* Flags. */ + unsigned short flags; +}; + +/* There is no magic number which indicates a COFF file as opposed to + any other sort of file. Instead, each COFF file starts with a + two-byte magic number which also indicates the type of the target. + This struct holds a magic number as well as characteristics of that + COFF format. */ + +struct coff_magic_struct +{ + /* Magic number. */ + unsigned short magic; + /* Whether this magic number is for a big-endian file. */ + unsigned char is_big_endian; + /* Flag bits, in the f_flags fields, which indicates that this file + is not a relocatable object file. There is no flag which + specifically indicates a relocatable object file, it is only + implied by the absence of these flags. */ + unsigned short non_object_flags; +}; + +/* This is a list of the COFF magic numbers which we recognize, namely + the ones used on Windows. More can be added as needed. */ + +static const struct coff_magic_struct coff_magic[] = +{ + /* i386. */ + { 0x14c, 0, F_EXEC | IMAGE_FILE_SYSTEM | IMAGE_FILE_DLL }, + /* x86_64. */ + { 0x8664, 0, F_EXEC | IMAGE_FILE_SYSTEM | IMAGE_FILE_DLL } +}; + +/* See if we have a COFF file. */ + +static void * +simple_object_coff_match (unsigned char header[SIMPLE_OBJECT_MATCH_HEADER_LEN], + int descriptor, off_t offset, + const char *segment_name ATTRIBUTE_UNUSED, + const char **errmsg, int *err) +{ + size_t c; + unsigned short magic_big; + unsigned short magic_little; + unsigned short magic; + size_t i; + int is_big_endian; + unsigned short (*fetch_16) (const unsigned char *); + unsigned int (*fetch_32) (const unsigned char *); + unsigned char hdrbuf[sizeof (struct external_filehdr)]; + unsigned short flags; + struct simple_object_coff_read *ocr; + + c = sizeof (coff_magic) / sizeof (coff_magic[0]); + magic_big = simple_object_fetch_big_16 (header); + magic_little = simple_object_fetch_little_16 (header); + for (i = 0; i < c; ++i) + { + if (coff_magic[i].is_big_endian + ? coff_magic[i].magic == magic_big + : coff_magic[i].magic == magic_little) + break; + } + if (i >= c) + { + *errmsg = NULL; + *err = 0; + return NULL; + } + is_big_endian = coff_magic[i].is_big_endian; + + magic = is_big_endian ? magic_big : magic_little; + fetch_16 = (is_big_endian + ? simple_object_fetch_big_16 + : simple_object_fetch_little_16); + fetch_32 = (is_big_endian + ? simple_object_fetch_big_32 + : simple_object_fetch_little_32); + + if (!simple_object_internal_read (descriptor, offset, hdrbuf, sizeof hdrbuf, + errmsg, err)) + return NULL; + + flags = fetch_16 (hdrbuf + offsetof (struct external_filehdr, f_flags)); + if ((flags & coff_magic[i].non_object_flags) != 0) + { + *errmsg = "not relocatable object file"; + *err = 0; + return NULL; + } + + ocr = XNEW (struct simple_object_coff_read); + ocr->magic = magic; + ocr->is_big_endian = is_big_endian; + ocr->nscns = fetch_16 (hdrbuf + offsetof (struct external_filehdr, f_nscns)); + ocr->symptr = fetch_32 (hdrbuf + + offsetof (struct external_filehdr, f_symptr)); + ocr->nsyms = fetch_32 (hdrbuf + offsetof (struct external_filehdr, f_nsyms)); + ocr->flags = flags; + ocr->scnhdr_offset = (sizeof (struct external_filehdr) + + fetch_16 (hdrbuf + offsetof (struct external_filehdr, + f_opthdr))); + + return (void *) ocr; +} + +/* Read the string table in a COFF file. */ + +static char * +simple_object_coff_read_strtab (simple_object_read *sobj, size_t *strtab_size, + const char **errmsg, int *err) +{ + struct simple_object_coff_read *ocr = + (struct simple_object_coff_read *) sobj->data; + off_t strtab_offset; + unsigned char strsizebuf[4]; + size_t strsize; + char *strtab; + + strtab_offset = ocr->symptr + ocr->nsyms * sizeof (struct external_syment); + if (!simple_object_internal_read (sobj->descriptor, strtab_offset, + strsizebuf, 4, errmsg, err)) + return NULL; + strsize = (ocr->is_big_endian + ? simple_object_fetch_big_32 (strsizebuf) + : simple_object_fetch_little_32 (strsizebuf)); + strtab = XNEWVEC (char, strsize); + if (!simple_object_internal_read (sobj->descriptor, strtab_offset, + (unsigned char *) strtab, strsize, errmsg, + err)) + { + XDELETEVEC (strtab); + return NULL; + } + *strtab_size = strsize; + return strtab; +} + +/* Find all sections in a COFF file. */ + +static const char * +simple_object_coff_find_sections (simple_object_read *sobj, + int (*pfn) (void *, const char *, + off_t offset, off_t length), + void *data, + int *err) +{ + struct simple_object_coff_read *ocr = + (struct simple_object_coff_read *) sobj->data; + size_t scnhdr_size; + unsigned char *scnbuf; + const char *errmsg; + unsigned int (*fetch_32) (const unsigned char *); + unsigned int nscns; + char *strtab; + size_t strtab_size; + unsigned int i; + + scnhdr_size = sizeof (struct external_scnhdr); + scnbuf = XNEWVEC (unsigned char, scnhdr_size * ocr->nscns); + if (!simple_object_internal_read (sobj->descriptor, + sobj->offset + ocr->scnhdr_offset, + scnbuf, scnhdr_size * ocr->nscns, &errmsg, + err)) + { + XDELETEVEC (scnbuf); + return errmsg; + } + + fetch_32 = (ocr->is_big_endian + ? simple_object_fetch_big_32 + : simple_object_fetch_little_32); + + nscns = ocr->nscns; + strtab = NULL; + strtab_size = 0; + for (i = 0; i < nscns; ++i) + { + unsigned char *scnhdr; + unsigned char *scnname; + char namebuf[SCNNMLEN + 1]; + char *name; + off_t scnptr; + unsigned int size; + + scnhdr = scnbuf + i * scnhdr_size; + scnname = scnhdr + offsetof (struct external_scnhdr, s_name); + memcpy (namebuf, scnname, SCNNMLEN); + namebuf[SCNNMLEN] = '\0'; + name = &namebuf[0]; + if (namebuf[0] == '/') + { + size_t strindex; + char *end; + + strindex = strtol (namebuf + 1, &end, 10); + if (*end == '\0') + { + /* The real section name is found in the string + table. */ + if (strtab == NULL) + { + strtab = simple_object_coff_read_strtab (sobj, + &strtab_size, + &errmsg, err); + if (strtab == NULL) + { + XDELETEVEC (scnbuf); + return errmsg; + } + } + + if (strindex < 4 || strindex >= strtab_size) + { + XDELETEVEC (strtab); + XDELETEVEC (scnbuf); + *err = 0; + return "section string index out of range"; + } + + name = strtab + strindex; + } + } + + scnptr = fetch_32 (scnhdr + offsetof (struct external_scnhdr, s_scnptr)); + size = fetch_32 (scnhdr + offsetof (struct external_scnhdr, s_size)); + + if (!(*pfn) (data, name, scnptr, size)) + break; + } + + if (strtab != NULL) + XDELETEVEC (strtab); + XDELETEVEC (scnbuf); + + return NULL; +} + +/* Fetch the attributes for an simple_object_read. */ + +static void * +simple_object_coff_fetch_attributes (simple_object_read *sobj, + const char **errmsg ATTRIBUTE_UNUSED, + int *err ATTRIBUTE_UNUSED) +{ + struct simple_object_coff_read *ocr = + (struct simple_object_coff_read *) sobj->data; + struct simple_object_coff_attributes *ret; + + ret = XNEW (struct simple_object_coff_attributes); + ret->magic = ocr->magic; + ret->is_big_endian = ocr->is_big_endian; + ret->flags = ocr->flags; + return ret; +} + +/* Release the private data for an simple_object_read. */ + +static void +simple_object_coff_release_read (void *data) +{ + XDELETE (data); +} + +/* Compare two attributes structures. */ + +static const char * +simple_object_coff_attributes_compare (void *data1, void *data2, int *err) +{ + struct simple_object_coff_attributes *attrs1 = + (struct simple_object_coff_attributes *) data1; + struct simple_object_coff_attributes *attrs2 = + (struct simple_object_coff_attributes *) data2; + + if (attrs1->magic != attrs2->magic + || attrs1->is_big_endian != attrs2->is_big_endian) + { + *err = 0; + return "COFF object format mismatch"; + } + return NULL; +} + +/* Release the private data for an attributes structure. */ + +static void +simple_object_coff_release_attributes (void *data) +{ + XDELETE (data); +} + +/* Prepare to write out a file. */ + +static void * +simple_object_coff_start_write (void *attributes_data, + const char **errmsg ATTRIBUTE_UNUSED, + int *err ATTRIBUTE_UNUSED) +{ + struct simple_object_coff_attributes *attrs = + (struct simple_object_coff_attributes *) attributes_data; + struct simple_object_coff_attributes *ret; + + /* We're just going to record the attributes, but we need to make a + copy because the user may delete them. */ + ret = XNEW (struct simple_object_coff_attributes); + *ret = *attrs; + return ret; +} + +/* Write out a COFF filehdr. */ + +static int +simple_object_coff_write_filehdr (simple_object_write *sobj, int descriptor, + unsigned int nscns, size_t symtab_offset, + unsigned int nsyms, const char **errmsg, + int *err) +{ + struct simple_object_coff_attributes *attrs = + (struct simple_object_coff_attributes *) sobj->data; + unsigned char hdrbuf[sizeof (struct external_filehdr)]; + unsigned char *hdr; + void (*set_16) (unsigned char *, unsigned short); + void (*set_32) (unsigned char *, unsigned int); + + hdr = &hdrbuf[0]; + + set_16 = (attrs->is_big_endian + ? simple_object_set_big_16 + : simple_object_set_little_16); + set_32 = (attrs->is_big_endian + ? simple_object_set_big_32 + : simple_object_set_little_32); + + memset (hdr, 0, sizeof (struct external_filehdr)); + + set_16 (hdr + offsetof (struct external_filehdr, f_magic), attrs->magic); + set_16 (hdr + offsetof (struct external_filehdr, f_nscns), nscns); + /* f_timdat left as zero. */ + set_32 (hdr + offsetof (struct external_filehdr, f_symptr), symtab_offset); + set_32 (hdr + offsetof (struct external_filehdr, f_nsyms), nsyms); + /* f_opthdr left as zero. */ + set_16 (hdr + offsetof (struct external_filehdr, f_flags), attrs->flags); + + return simple_object_internal_write (descriptor, 0, hdrbuf, + sizeof (struct external_filehdr), + errmsg, err); +} + +/* Write out a COFF section header. */ + +static int +simple_object_coff_write_scnhdr (simple_object_write *sobj, int descriptor, + const char *name, size_t *name_offset, + off_t scnhdr_offset, size_t scnsize, + off_t offset, unsigned int align, + const char **errmsg, int *err) +{ + struct simple_object_coff_attributes *attrs = + (struct simple_object_coff_attributes *) sobj->data; + void (*set_32) (unsigned char *, unsigned int); + unsigned char hdrbuf[sizeof (struct external_scnhdr)]; + unsigned char *hdr; + size_t namelen; + unsigned int flags; + + set_32 = (attrs->is_big_endian + ? simple_object_set_big_32 + : simple_object_set_little_32); + + memset (hdrbuf, 0, sizeof hdrbuf); + hdr = &hdrbuf[0]; + + namelen = strlen (name); + if (namelen <= SCNNMLEN) + strncpy ((char *) hdr + offsetof (struct external_scnhdr, s_name), name, + SCNNMLEN); + else + { + snprintf ((char *) hdr + offsetof (struct external_scnhdr, s_name), + SCNNMLEN, "/%lu", (unsigned long) *name_offset); + *name_offset += namelen + 1; + } + + /* s_paddr left as zero. */ + /* s_vaddr left as zero. */ + set_32 (hdr + offsetof (struct external_scnhdr, s_size), scnsize); + set_32 (hdr + offsetof (struct external_scnhdr, s_scnptr), offset); + /* s_relptr left as zero. */ + /* s_lnnoptr left as zero. */ + /* s_nreloc left as zero. */ + /* s_nlnno left as zero. */ + flags = (STYP_DATA | IMAGE_SCN_MEM_DISCARDABLE | IMAGE_SCN_MEM_SHARED + | IMAGE_SCN_MEM_READ); + /* PE can represent alignment up to 13. */ + if (align > 13) + align = 13; + flags |= IMAGE_SCN_ALIGN_POWER_CONST(align); + set_32 (hdr + offsetof (struct external_scnhdr, s_flags), flags); + + return simple_object_internal_write (descriptor, scnhdr_offset, hdrbuf, + sizeof (struct external_scnhdr), + errmsg, err); +} + +/* Write out a complete COFF file. */ + +static const char * +simple_object_coff_write_to_file (simple_object_write *sobj, int descriptor, + int *err) +{ + struct simple_object_coff_attributes *attrs = + (struct simple_object_coff_attributes *) sobj->data; + unsigned int nscns, secnum; + simple_object_write_section *section; + off_t scnhdr_offset; + size_t symtab_offset; + off_t secsym_offset; + unsigned int nsyms; + size_t offset; + size_t name_offset; + const char *errmsg; + unsigned char strsizebuf[4]; + /* The interface doesn't give us access to the name of the input file + yet. We want to use its basename for the FILE symbol. This is + what 'gas' uses when told to assemble from stdin. */ + const char *source_filename = "fake"; + size_t sflen; + union + { + struct external_syment sym; + union external_auxent aux; + } syms[2]; + void (*set_16) (unsigned char *, unsigned short); + void (*set_32) (unsigned char *, unsigned int); + + set_16 = (attrs->is_big_endian + ? simple_object_set_big_16 + : simple_object_set_little_16); + set_32 = (attrs->is_big_endian + ? simple_object_set_big_32 + : simple_object_set_little_32); + + nscns = 0; + for (section = sobj->sections; section != NULL; section = section->next) + ++nscns; + + scnhdr_offset = sizeof (struct external_filehdr); + offset = scnhdr_offset + nscns * sizeof (struct external_scnhdr); + name_offset = 4; + for (section = sobj->sections; section != NULL; section = section->next) + { + size_t mask; + size_t new_offset; + size_t scnsize; + struct simple_object_write_section_buffer *buffer; + + mask = (1U << section->align) - 1; + new_offset = offset & mask; + new_offset &= ~ mask; + while (new_offset > offset) + { + unsigned char zeroes[16]; + size_t write; + + memset (zeroes, 0, sizeof zeroes); + write = new_offset - offset; + if (write > sizeof zeroes) + write = sizeof zeroes; + if (!simple_object_internal_write (descriptor, offset, zeroes, write, + &errmsg, err)) + return errmsg; + } + + scnsize = 0; + for (buffer = section->buffers; buffer != NULL; buffer = buffer->next) + { + if (!simple_object_internal_write (descriptor, offset + scnsize, + ((const unsigned char *) + buffer->buffer), + buffer->size, &errmsg, err)) + return errmsg; + scnsize += buffer->size; + } + + if (!simple_object_coff_write_scnhdr (sobj, descriptor, section->name, + &name_offset, scnhdr_offset, + scnsize, offset, section->align, + &errmsg, err)) + return errmsg; + + scnhdr_offset += sizeof (struct external_scnhdr); + offset += scnsize; + } + + /* Symbol table is always half-word aligned. */ + offset += (offset & 1); + /* There is a file symbol and a section symbol per section, + and each of these has a single auxiliary symbol following. */ + nsyms = 2 * (nscns + 1); + symtab_offset = offset; + /* Advance across space reserved for symbol table to locate + start of string table. */ + offset += nsyms * sizeof (struct external_syment); + + /* Write out file symbol. */ + memset (&syms[0], 0, sizeof (syms)); + strcpy ((char *)&syms[0].sym.e.e_name[0], ".file"); + set_16 (&syms[0].sym.e_scnum[0], IMAGE_SYM_DEBUG); + set_16 (&syms[0].sym.e_type[0], IMAGE_SYM_TYPE); + syms[0].sym.e_sclass[0] = IMAGE_SYM_CLASS_FILE; + syms[0].sym.e_numaux[0] = 1; + /* The name need not be nul-terminated if it fits into the x_fname field + directly, but must be if it has to be placed into the string table. */ + sflen = strlen (source_filename); + if (sflen <= E_FILNMLEN) + memcpy (&syms[1].aux.x_file.x_fname[0], source_filename, sflen); + else + { + set_32 (&syms[1].aux.x_file.x_n.x_offset[0], name_offset); + if (!simple_object_internal_write (descriptor, offset + name_offset, + ((const unsigned char *) + source_filename), + sflen + 1, &errmsg, err)) + return errmsg; + name_offset += strlen (source_filename) + 1; + } + if (!simple_object_internal_write (descriptor, symtab_offset, + (const unsigned char *) &syms[0], + sizeof (syms), &errmsg, err)) + return errmsg; + + /* Write the string table length, followed by the strings and section + symbols in step with each other. */ + set_32 (strsizebuf, name_offset); + if (!simple_object_internal_write (descriptor, offset, strsizebuf, 4, + &errmsg, err)) + return errmsg; + + name_offset = 4; + secsym_offset = symtab_offset + sizeof (syms); + memset (&syms[0], 0, sizeof (syms)); + set_16 (&syms[0].sym.e_type[0], IMAGE_SYM_TYPE); + syms[0].sym.e_sclass[0] = IMAGE_SYM_CLASS_STATIC; + syms[0].sym.e_numaux[0] = 1; + secnum = 1; + + for (section = sobj->sections; section != NULL; section = section->next) + { + size_t namelen; + size_t scnsize; + struct simple_object_write_section_buffer *buffer; + + namelen = strlen (section->name); + set_16 (&syms[0].sym.e_scnum[0], secnum++); + scnsize = 0; + for (buffer = section->buffers; buffer != NULL; buffer = buffer->next) + scnsize += buffer->size; + set_32 (&syms[1].aux.x_scn.x_scnlen[0], scnsize); + if (namelen > SCNNMLEN) + { + set_32 (&syms[0].sym.e.e.e_zeroes[0], 0); + set_32 (&syms[0].sym.e.e.e_offset[0], name_offset); + if (!simple_object_internal_write (descriptor, offset + name_offset, + ((const unsigned char *) + section->name), + namelen + 1, &errmsg, err)) + return errmsg; + name_offset += namelen + 1; + } + else + { + memcpy (&syms[0].sym.e.e_name[0], section->name, + strlen (section->name)); + memset (&syms[0].sym.e.e_name[strlen (section->name)], 0, + E_SYMNMLEN - strlen (section->name)); + } + + if (!simple_object_internal_write (descriptor, secsym_offset, + (const unsigned char *) &syms[0], + sizeof (syms), &errmsg, err)) + return errmsg; + secsym_offset += sizeof (syms); + } + + if (!simple_object_coff_write_filehdr (sobj, descriptor, nscns, + symtab_offset, nsyms, &errmsg, err)) + return errmsg; + + return NULL; +} + +/* Release the private data for an simple_object_write structure. */ + +static void +simple_object_coff_release_write (void *data) +{ + XDELETE (data); +} + +/* The COFF functions. */ + +const struct simple_object_functions simple_object_coff_functions = +{ + simple_object_coff_match, + simple_object_coff_find_sections, + simple_object_coff_fetch_attributes, + simple_object_coff_release_read, + simple_object_coff_attributes_compare, + simple_object_coff_release_attributes, + simple_object_coff_start_write, + simple_object_coff_write_to_file, + simple_object_coff_release_write +}; diff --git a/libiberty/simple-object-common.h b/libiberty/simple-object-common.h new file mode 100644 index 0000000..8f74390 --- /dev/null +++ b/libiberty/simple-object-common.h @@ -0,0 +1,355 @@ +/* simple-object-common.h -- common structs for object file manipulation. + Copyright (C) 2010 Free Software Foundation, Inc. + +This file is part of the libiberty library. +Libiberty is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public +License as published by the Free Software Foundation; either +version 2 of the License, or (at your option) any later version. + +Libiberty 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 +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with libiberty; see the file COPYING.LIB. If not, +write to the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor, +Boston, MA 02110-1301, USA. */ + +/* Forward reference. */ +struct simple_object_functions; + +/* An object file opened for reading. */ + +struct simple_object_read_struct +{ + /* The file descriptor. */ + int descriptor; + /* The offset within the file. */ + off_t offset; + /* The functions which do the actual work. */ + const struct simple_object_functions *functions; + /* Private data for the object file format. */ + void *data; +}; + +/* Object file attributes. */ + +struct simple_object_attributes_struct +{ + /* The functions which do the actual work. */ + const struct simple_object_functions *functions; + /* Private data for the object file format. */ + void *data; +}; + +/* An object file being created. */ + +struct simple_object_write_struct +{ + /* The functions which do the actual work. */ + const struct simple_object_functions *functions; + /* The segment_name argument from the user. */ + char *segment_name; + /* The start of the list of sections. */ + simple_object_write_section *sections; + /* The last entry in the list of sections. */ + simple_object_write_section *last_section; + /* Private data for the object file format. */ + void *data; +}; + +/* A section in an object file being created. */ + +struct simple_object_write_section_struct +{ + /* Next in the list of sections attached to an + simple_object_write. */ + simple_object_write_section *next; + /* The name of this section. */ + char *name; + /* The required alignment. */ + unsigned int align; + /* The first data attached to this section. */ + struct simple_object_write_section_buffer *buffers; + /* The last data attached to this section. */ + struct simple_object_write_section_buffer *last_buffer; +}; + +/* Data attached to a section. */ + +struct simple_object_write_section_buffer +{ + /* The next data for this section. */ + struct simple_object_write_section_buffer *next; + /* The size of the buffer. */ + size_t size; + /* The actual bytes. */ + const void *buffer; + /* A buffer to free, or NULL. */ + void *free_buffer; +}; + +/* The number of bytes we read from the start of the file to pass to + the match function. */ +#define SIMPLE_OBJECT_MATCH_HEADER_LEN (16) + +/* Format-specific object file functions. */ + +struct simple_object_functions +{ + /* If this file matches these functions, return a new value for the + private data for an simple_object_read. HEADER is the first 16 + bytes of the file. DESCRIPTOR, OFFSET, SEGMENT_NAME, ERRMSG, and + ERR are as for simple_object_open_read. If this file does not + match, this function should return NULL with *ERRMSG set to + NULL. */ + void *(*match) (unsigned char header[SIMPLE_OBJECT_MATCH_HEADER_LEN], + int descriptor, off_t offset, const char *segment_name, + const char **errmsg, int *err); + + /* Implement simple_object_find_sections. */ + const char *(*find_sections) (simple_object_read *, + int (*pfn) (void *, const char *, + off_t offset, off_t length), + void *data, + int *err); + + /* Return the private data for the attributes for SOBJ. */ + void *(*fetch_attributes) (simple_object_read *sobj, const char **errmsg, + int *err); + + /* Release the private data for an simple_object_read. */ + void (*release_read) (void *); + + /* Compare the private data for the attributes of two files. If + they are the same, in the sense that they could be linked + together, return NULL. Otherwise return an error message. */ + const char *(*attributes_compare) (void *, void *, int *err); + + /* Release the private data for an simple_object_attributes. */ + void (*release_attributes) (void *); + + /* Start creating an object file. */ + void *(*start_write) (void *attributes_data, const char **errmsg, + int *err); + + /* Write the complete object file. */ + const char *(*write_to_file) (simple_object_write *sobj, int descriptor, + int *err); + + /* Release the private data for an simple_object_write. */ + void (*release_write) (void *); +}; + +/* The known object file formats. */ + +extern const struct simple_object_functions simple_object_coff_functions; +extern const struct simple_object_functions simple_object_elf_functions; +extern const struct simple_object_functions simple_object_mach_o_functions; + +/* Read SIZE bytes from DESCRIPTOR at file offset OFFSET into BUFFER. + Return non-zero on success. On failure return 0 and set *ERRMSG + and *ERR. */ + +extern int +simple_object_internal_read (int descriptor, off_t offset, + unsigned char *buffer, size_t size, + const char **errmsg, int *err); + +/* Write SIZE bytes from BUFFER to DESCRIPTOR at file offset OFFSET. + Return non-zero on success. On failure return 0 and set *ERRMSG + and *ERR. */ + +extern int +simple_object_internal_write (int descriptor, off_t offset, + const unsigned char *buffer, size_t size, + const char **errmsg, int *err); + +/* Define ulong_type as an unsigned 64-bit type if available. + Otherwise just make it unsigned long. */ + +#ifdef UNSIGNED_64BIT_TYPE +__extension__ typedef UNSIGNED_64BIT_TYPE ulong_type; +#else +typedef unsigned long ulong_type; +#endif + +/* Fetch a big-endian 16-bit value. */ + +static inline unsigned short +simple_object_fetch_big_16 (const unsigned char *buf) +{ + return ((unsigned short) buf[0] << 8) | (unsigned short) buf[1]; +} + +/* Fetch a little-endian 16-bit value. */ + +static inline unsigned short +simple_object_fetch_little_16 (const unsigned char *buf) +{ + return ((unsigned short) buf[1] << 8) | (unsigned short) buf[0]; +} + +/* Fetch a big-endian 32-bit value. */ + +static inline unsigned int +simple_object_fetch_big_32 (const unsigned char *buf) +{ + return (((unsigned int) buf[0] << 24) + | ((unsigned int) buf[1] << 16) + | ((unsigned int) buf[2] << 8) + | (unsigned int) buf[3]); +} + +/* Fetch a little-endian 32-bit value. */ + +static inline unsigned int +simple_object_fetch_little_32 (const unsigned char *buf) +{ + return (((unsigned int) buf[3] << 24) + | ((unsigned int) buf[2] << 16) + | ((unsigned int) buf[1] << 8) + | (unsigned int) buf[0]); +} + +/* Fetch a big-endian 32-bit value as a ulong_type. */ + +static inline ulong_type +simple_object_fetch_big_32_ulong (const unsigned char *buf) +{ + return (ulong_type) simple_object_fetch_big_32 (buf); +} + +/* Fetch a little-endian 32-bit value as a ulong_type. */ + +static inline ulong_type +simple_object_fetch_little_32_ulong (const unsigned char *buf) +{ + return (ulong_type) simple_object_fetch_little_32 (buf); +} + +#ifdef UNSIGNED_64BIT_TYPE + +/* Fetch a big-endian 64-bit value. */ + +static inline ulong_type +simple_object_fetch_big_64 (const unsigned char *buf) +{ + return (((ulong_type) buf[0] << 56) + | ((ulong_type) buf[1] << 48) + | ((ulong_type) buf[2] << 40) + | ((ulong_type) buf[3] << 32) + | ((ulong_type) buf[4] << 24) + | ((ulong_type) buf[5] << 16) + | ((ulong_type) buf[6] << 8) + | (ulong_type) buf[7]); +} + +/* Fetch a little-endian 64-bit value. */ + +static inline ulong_type +simple_object_fetch_little_64 (const unsigned char *buf) +{ + return (((ulong_type) buf[7] << 56) + | ((ulong_type) buf[6] << 48) + | ((ulong_type) buf[5] << 40) + | ((ulong_type) buf[4] << 32) + | ((ulong_type) buf[3] << 24) + | ((ulong_type) buf[2] << 16) + | ((ulong_type) buf[1] << 8) + | (ulong_type) buf[0]); +} + +#endif + +/* Store a big-endian 16-bit value. */ + +static inline void +simple_object_set_big_16 (unsigned char *buf, unsigned short val) +{ + buf[0] = (val >> 8) & 0xff; + buf[1] = val & 0xff; +} + +/* Store a little-endian 16-bit value. */ + +static inline void +simple_object_set_little_16 (unsigned char *buf, unsigned short val) +{ + buf[1] = (val >> 8) & 0xff; + buf[0] = val & 0xff; +} + +/* Store a big-endian 32-bit value. */ + +static inline void +simple_object_set_big_32 (unsigned char *buf, unsigned int val) +{ + buf[0] = (val >> 24) & 0xff; + buf[1] = (val >> 16) & 0xff; + buf[2] = (val >> 8) & 0xff; + buf[3] = val & 0xff; +} + +/* Store a little-endian 32-bit value. */ + +static inline void +simple_object_set_little_32 (unsigned char *buf, unsigned int val) +{ + buf[3] = (val >> 24) & 0xff; + buf[2] = (val >> 16) & 0xff; + buf[1] = (val >> 8) & 0xff; + buf[0] = val & 0xff; +} + +/* Store a big-endian 32-bit value coming in as a ulong_type. */ + +static inline void +simple_object_set_big_32_ulong (unsigned char *buf, ulong_type val) +{ + simple_object_set_big_32 (buf, val); +} + +/* Store a little-endian 32-bit value coming in as a ulong_type. */ + +static inline void +simple_object_set_little_32_ulong (unsigned char *buf, ulong_type val) +{ + simple_object_set_little_32 (buf, val); +} + +#ifdef UNSIGNED_64BIT_TYPE + +/* Store a big-endian 64-bit value. */ + +static inline void +simple_object_set_big_64 (unsigned char *buf, ulong_type val) +{ + buf[0] = (val >> 56) & 0xff; + buf[1] = (val >> 48) & 0xff; + buf[2] = (val >> 40) & 0xff; + buf[3] = (val >> 32) & 0xff; + buf[4] = (val >> 24) & 0xff; + buf[5] = (val >> 16) & 0xff; + buf[6] = (val >> 8) & 0xff; + buf[7] = val & 0xff; +} + +/* Store a little-endian 64-bit value. */ + +static inline void +simple_object_set_little_64 (unsigned char *buf, ulong_type val) +{ + buf[7] = (val >> 56) & 0xff; + buf[6] = (val >> 48) & 0xff; + buf[5] = (val >> 40) & 0xff; + buf[4] = (val >> 32) & 0xff; + buf[3] = (val >> 24) & 0xff; + buf[2] = (val >> 16) & 0xff; + buf[1] = (val >> 8) & 0xff; + buf[0] = val & 0xff; +} + +#endif diff --git a/libiberty/simple-object-elf.c b/libiberty/simple-object-elf.c new file mode 100644 index 0000000..5b8cfba --- /dev/null +++ b/libiberty/simple-object-elf.c @@ -0,0 +1,916 @@ +/* simple-object-elf.c -- routines to manipulate ELF object files. + Copyright 2010 Free Software Foundation, Inc. + Written by Ian Lance Taylor, Google. + +This program 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 2, 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, write to the Free Software +Foundation, 51 Franklin Street - Fifth Floor, +Boston, MA 02110-1301, USA. */ + +#include "config.h" +#include "libiberty.h" +#include "simple-object.h" + +#include <errno.h> +#include <stddef.h> + +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif + +#ifdef HAVE_STDINT_H +#include <stdint.h> +#endif + +#ifdef HAVE_STRING_H +#include <string.h> +#endif + +#ifdef HAVE_INTTYPES_H +#include <inttypes.h> +#endif + +#include "simple-object-common.h" + +/* ELF structures and constants. */ + +/* 32-bit ELF file header. */ + +typedef struct { + unsigned char e_ident[16]; /* ELF "magic number" */ + unsigned char e_type[2]; /* Identifies object file type */ + unsigned char e_machine[2]; /* Specifies required architecture */ + unsigned char e_version[4]; /* Identifies object file version */ + unsigned char e_entry[4]; /* Entry point virtual address */ + unsigned char e_phoff[4]; /* Program header table file offset */ + unsigned char e_shoff[4]; /* Section header table file offset */ + unsigned char e_flags[4]; /* Processor-specific flags */ + unsigned char e_ehsize[2]; /* ELF header size in bytes */ + unsigned char e_phentsize[2]; /* Program header table entry size */ + unsigned char e_phnum[2]; /* Program header table entry count */ + unsigned char e_shentsize[2]; /* Section header table entry size */ + unsigned char e_shnum[2]; /* Section header table entry count */ + unsigned char e_shstrndx[2]; /* Section header string table index */ +} Elf32_External_Ehdr; + +/* 64-bit ELF file header. */ + +typedef struct { + unsigned char e_ident[16]; /* ELF "magic number" */ + unsigned char e_type[2]; /* Identifies object file type */ + unsigned char e_machine[2]; /* Specifies required architecture */ + unsigned char e_version[4]; /* Identifies object file version */ + unsigned char e_entry[8]; /* Entry point virtual address */ + unsigned char e_phoff[8]; /* Program header table file offset */ + unsigned char e_shoff[8]; /* Section header table file offset */ + unsigned char e_flags[4]; /* Processor-specific flags */ + unsigned char e_ehsize[2]; /* ELF header size in bytes */ + unsigned char e_phentsize[2]; /* Program header table entry size */ + unsigned char e_phnum[2]; /* Program header table entry count */ + unsigned char e_shentsize[2]; /* Section header table entry size */ + unsigned char e_shnum[2]; /* Section header table entry count */ + unsigned char e_shstrndx[2]; /* Section header string table index */ +} Elf64_External_Ehdr; + +/* Indexes and values in e_ident field of Ehdr. */ + +#define EI_MAG0 0 /* File identification byte 0 index */ +#define ELFMAG0 0x7F /* Magic number byte 0 */ + +#define EI_MAG1 1 /* File identification byte 1 index */ +#define ELFMAG1 'E' /* Magic number byte 1 */ + +#define EI_MAG2 2 /* File identification byte 2 index */ +#define ELFMAG2 'L' /* Magic number byte 2 */ + +#define EI_MAG3 3 /* File identification byte 3 index */ +#define ELFMAG3 'F' /* Magic number byte 3 */ + +#define EI_CLASS 4 /* File class */ +#define ELFCLASSNONE 0 /* Invalid class */ +#define ELFCLASS32 1 /* 32-bit objects */ +#define ELFCLASS64 2 /* 64-bit objects */ + +#define EI_DATA 5 /* Data encoding */ +#define ELFDATANONE 0 /* Invalid data encoding */ +#define ELFDATA2LSB 1 /* 2's complement, little endian */ +#define ELFDATA2MSB 2 /* 2's complement, big endian */ + +#define EI_VERSION 6 /* File version */ +#define EV_CURRENT 1 /* Current version */ + +#define EI_OSABI 7 /* Operating System/ABI indication */ + +/* Values for e_type field of Ehdr. */ + +#define ET_REL 1 /* Relocatable file */ + +/* Special section index values. */ + +#define SHN_LORESERVE 0xFF00 /* Begin range of reserved indices */ +#define SHN_XINDEX 0xFFFF /* Section index is held elsewhere */ + +/* 32-bit ELF program header. */ + +typedef struct { + unsigned char p_type[4]; /* Identifies program segment type */ + unsigned char p_offset[4]; /* Segment file offset */ + unsigned char p_vaddr[4]; /* Segment virtual address */ + unsigned char p_paddr[4]; /* Segment physical address */ + unsigned char p_filesz[4]; /* Segment size in file */ + unsigned char p_memsz[4]; /* Segment size in memory */ + unsigned char p_flags[4]; /* Segment flags */ + unsigned char p_align[4]; /* Segment alignment, file & memory */ +} Elf32_External_Phdr; + +/* 64-bit ELF program header. */ + +typedef struct { + unsigned char p_type[4]; /* Identifies program segment type */ + unsigned char p_flags[4]; /* Segment flags */ + unsigned char p_offset[8]; /* Segment file offset */ + unsigned char p_vaddr[8]; /* Segment virtual address */ + unsigned char p_paddr[8]; /* Segment physical address */ + unsigned char p_filesz[8]; /* Segment size in file */ + unsigned char p_memsz[8]; /* Segment size in memory */ + unsigned char p_align[8]; /* Segment alignment, file & memory */ +} Elf64_External_Phdr; + +/* 32-bit ELF section header */ + +typedef struct { + unsigned char sh_name[4]; /* Section name, index in string tbl */ + unsigned char sh_type[4]; /* Type of section */ + unsigned char sh_flags[4]; /* Miscellaneous section attributes */ + unsigned char sh_addr[4]; /* Section virtual addr at execution */ + unsigned char sh_offset[4]; /* Section file offset */ + unsigned char sh_size[4]; /* Size of section in bytes */ + unsigned char sh_link[4]; /* Index of another section */ + unsigned char sh_info[4]; /* Additional section information */ + unsigned char sh_addralign[4]; /* Section alignment */ + unsigned char sh_entsize[4]; /* Entry size if section holds table */ +} Elf32_External_Shdr; + +/* 64-bit ELF section header. */ + +typedef struct { + unsigned char sh_name[4]; /* Section name, index in string tbl */ + unsigned char sh_type[4]; /* Type of section */ + unsigned char sh_flags[8]; /* Miscellaneous section attributes */ + unsigned char sh_addr[8]; /* Section virtual addr at execution */ + unsigned char sh_offset[8]; /* Section file offset */ + unsigned char sh_size[8]; /* Size of section in bytes */ + unsigned char sh_link[4]; /* Index of another section */ + unsigned char sh_info[4]; /* Additional section information */ + unsigned char sh_addralign[8]; /* Section alignment */ + unsigned char sh_entsize[8]; /* Entry size if section holds table */ +} Elf64_External_Shdr; + +/* Values for sh_type field. */ + +#define SHT_PROGBITS 1 /* Program data */ +#define SHT_STRTAB 3 /* A string table */ + +/* Functions to fetch and store different ELF types, depending on the + endianness and size. */ + +struct elf_type_functions +{ + unsigned short (*fetch_Elf_Half) (const unsigned char *); + unsigned int (*fetch_Elf_Word) (const unsigned char *); + ulong_type (*fetch_Elf_Addr) (const unsigned char *); + void (*set_Elf_Half) (unsigned char *, unsigned short); + void (*set_Elf_Word) (unsigned char *, unsigned int); + void (*set_Elf_Addr) (unsigned char *, ulong_type); +}; + +static const struct elf_type_functions elf_big_32_functions = +{ + simple_object_fetch_big_16, + simple_object_fetch_big_32, + simple_object_fetch_big_32_ulong, + simple_object_set_big_16, + simple_object_set_big_32, + simple_object_set_big_32_ulong +}; + +static const struct elf_type_functions elf_little_32_functions = +{ + simple_object_fetch_little_16, + simple_object_fetch_little_32, + simple_object_fetch_little_32_ulong, + simple_object_set_little_16, + simple_object_set_little_32, + simple_object_set_little_32_ulong +}; + +#ifdef UNSIGNED_64BIT_TYPE + +static const struct elf_type_functions elf_big_64_functions = +{ + simple_object_fetch_big_16, + simple_object_fetch_big_32, + simple_object_fetch_big_64, + simple_object_set_big_16, + simple_object_set_big_32, + simple_object_set_big_64 +}; + +static const struct elf_type_functions elf_little_64_functions = +{ + simple_object_fetch_little_16, + simple_object_fetch_little_32, + simple_object_fetch_little_64, + simple_object_set_little_16, + simple_object_set_little_32, + simple_object_set_little_64 +}; + +#endif + +/* Hideous macro to fetch the value of a field from an external ELF + struct of some sort. TYPEFUNCS is the set of type functions. + BUFFER points to the external data. STRUCTTYPE is the appropriate + struct type. FIELD is a field within the struct. TYPE is the type + of the field in the struct: Elf_Half, Elf_Word, or Elf_Addr. */ + +#define ELF_FETCH_STRUCT_FIELD(TYPEFUNCS, STRUCTTYPE, FIELD, BUFFER, TYPE) \ + ((TYPEFUNCS)->fetch_ ## TYPE ((BUFFER) + offsetof (STRUCTTYPE, FIELD))) + +/* Even more hideous macro to fetch the value of FIELD from BUFFER. + SIZE is 32 or 64. STRUCTTYPE is the name of the struct from + elf/external.h: Ehdr, Shdr, etc. FIELD is the name of a field in + the struct. TYPE is the type of the field in the struct: Elf_Half, + Elf_Word, or Elf_Addr. */ + +#define ELF_FETCH_SIZED_FIELD(TYPEFUNCS, SIZE, STRUCTTYPE, BUFFER, \ + FIELD, TYPE) \ + ELF_FETCH_STRUCT_FIELD (TYPEFUNCS, \ + Elf ## SIZE ## _External_ ## STRUCTTYPE, \ + FIELD, BUFFER, TYPE) + +/* Like ELF_FETCH_SIZED_FIELD but taking an ELFCLASS value. */ + +#define ELF_FETCH_FIELD(TYPEFUNCS, CLASS, STRUCTTYPE, BUFFER, \ + FIELD, TYPE) \ + ((CLASS) == ELFCLASS32 \ + ? ELF_FETCH_SIZED_FIELD (TYPEFUNCS, 32, STRUCTTYPE, BUFFER, FIELD, \ + TYPE) \ + : ELF_FETCH_SIZED_FIELD (TYPEFUNCS, 64, STRUCTTYPE, BUFFER, FIELD, \ + TYPE)) + +/* Hideous macro to set the value of a field in an external ELF + structure to VAL. TYPEFUNCS is the set of type functions. BUFFER + points to the external data. STRUCTTYPE is the appropriate + structure type. FIELD is a field within the struct. TYPE is the + type of the field in the struct: Elf_Half, Elf_Word, or + Elf_Addr. */ + +#define ELF_SET_STRUCT_FIELD(TYPEFUNCS, STRUCTTYPE, FIELD, BUFFER, TYPE, VAL) \ + (TYPEFUNCS)->set_ ## TYPE ((BUFFER) + offsetof (STRUCTTYPE, FIELD), (VAL)) + +/* Even more hideous macro to set the value of FIELD in BUFFER to VAL. + SIZE is 32 or 64. STRUCTTYPE is the name of the struct from + elf/external.h: Ehdr, Shdr, etc. FIELD is the name of a field in + the struct. TYPE is the type of the field in the struct: Elf_Half, + Elf_Word, or Elf_Addr. */ + +#define ELF_SET_SIZED_FIELD(TYPEFUNCS, SIZE, STRUCTTYPE, BUFFER, FIELD, \ + TYPE, VAL) \ + ELF_SET_STRUCT_FIELD (TYPEFUNCS, \ + Elf ## SIZE ## _External_ ## STRUCTTYPE, \ + FIELD, BUFFER, TYPE, VAL) + +/* Like ELF_SET_SIZED_FIELD but taking an ELFCLASS value. */ + +#define ELF_SET_FIELD(TYPEFUNCS, CLASS, STRUCTTYPE, BUFFER, FIELD, \ + TYPE, VAL) \ + ((CLASS) == ELFCLASS32 \ + ? ELF_SET_SIZED_FIELD (TYPEFUNCS, 32, STRUCTTYPE, BUFFER, FIELD, \ + TYPE, VAL) \ + : ELF_SET_SIZED_FIELD (TYPEFUNCS, 64, STRUCTTYPE, BUFFER, FIELD, \ + TYPE, VAL)) + +/* Private data for an simple_object_read. */ + +struct simple_object_elf_read +{ + /* Type functions. */ + const struct elf_type_functions* type_functions; + /* Elf data. */ + unsigned char ei_data; + /* Elf class. */ + unsigned char ei_class; + /* ELF OS ABI. */ + unsigned char ei_osabi; + /* Elf machine number. */ + unsigned short machine; + /* Processor specific flags. */ + unsigned int flags; + /* File offset of section headers. */ + ulong_type shoff; + /* Number of sections. */ + unsigned int shnum; + /* Index of string table section header. */ + unsigned int shstrndx; +}; + +/* Private data for an simple_object_attributes. */ + +struct simple_object_elf_attributes +{ + /* Type functions. */ + const struct elf_type_functions* type_functions; + /* Elf data. */ + unsigned char ei_data; + /* Elf class. */ + unsigned char ei_class; + /* ELF OS ABI. */ + unsigned char ei_osabi; + /* Elf machine number. */ + unsigned short machine; + /* Processor specific flags. */ + unsigned int flags; +}; + +/* See if we have an ELF file. */ + +static void * +simple_object_elf_match (unsigned char header[SIMPLE_OBJECT_MATCH_HEADER_LEN], + int descriptor, off_t offset, + const char *segment_name ATTRIBUTE_UNUSED, + const char **errmsg, int *err) +{ + unsigned char ei_data; + unsigned char ei_class; + const struct elf_type_functions *type_functions; + unsigned char ehdr[sizeof (Elf64_External_Ehdr)]; + struct simple_object_elf_read *eor; + + if (header[EI_MAG0] != ELFMAG0 + || header[EI_MAG1] != ELFMAG1 + || header[EI_MAG2] != ELFMAG2 + || header[EI_MAG3] != ELFMAG3 + || header[EI_VERSION] != EV_CURRENT) + { + *errmsg = NULL; + *err = 0; + return NULL; + } + + ei_data = header[EI_DATA]; + if (ei_data != ELFDATA2LSB && ei_data != ELFDATA2MSB) + { + *errmsg = "unknown ELF endianness"; + *err = 0; + return NULL; + } + + ei_class = header[EI_CLASS]; + switch (ei_class) + { + case ELFCLASS32: + type_functions = (ei_data == ELFDATA2LSB + ? &elf_little_32_functions + : &elf_big_32_functions); + break; + + case ELFCLASS64: +#ifndef UNSIGNED_64BIT_TYPE + *errmsg = "64-bit ELF objects not supported"; + *err = 0; + return NULL; +#else + type_functions = (ei_data == ELFDATA2LSB + ? &elf_little_64_functions + : &elf_big_64_functions); + break; +#endif + + default: + *errmsg = "unrecognized ELF size"; + *err = 0; + return NULL; + } + + if (!simple_object_internal_read (descriptor, offset, ehdr, sizeof ehdr, + errmsg, err)) + return NULL; + + eor = XNEW (struct simple_object_elf_read); + eor->type_functions = type_functions; + eor->ei_data = ei_data; + eor->ei_class = ei_class; + eor->ei_osabi = header[EI_OSABI]; + eor->machine = ELF_FETCH_FIELD (type_functions, ei_class, Ehdr, ehdr, + e_machine, Elf_Half); + eor->flags = ELF_FETCH_FIELD (type_functions, ei_class, Ehdr, ehdr, + e_flags, Elf_Word); + eor->shoff = ELF_FETCH_FIELD (type_functions, ei_class, Ehdr, ehdr, + e_shoff, Elf_Addr); + eor->shnum = ELF_FETCH_FIELD (type_functions, ei_class, Ehdr, ehdr, + e_shnum, Elf_Half); + eor->shstrndx = ELF_FETCH_FIELD (type_functions, ei_class, Ehdr, ehdr, + e_shstrndx, Elf_Half); + + if ((eor->shnum == 0 || eor->shstrndx == SHN_XINDEX) + && eor->shoff != 0) + { + unsigned char shdr[sizeof (Elf64_External_Shdr)]; + + /* Object file has more than 0xffff sections. */ + + if (!simple_object_internal_read (descriptor, offset + eor->shoff, shdr, + (ei_class == ELFCLASS32 + ? sizeof (Elf32_External_Shdr) + : sizeof (Elf64_External_Shdr)), + errmsg, err)) + { + XDELETE (eor); + return NULL; + } + + if (eor->shnum == 0) + eor->shnum = ELF_FETCH_FIELD (type_functions, ei_class, Shdr, + shdr, sh_size, Elf_Addr); + + if (eor->shstrndx == SHN_XINDEX) + { + eor->shstrndx = ELF_FETCH_FIELD (type_functions, ei_class, Shdr, + shdr, sh_link, Elf_Word); + + /* Versions of the GNU binutils between 2.12 and 2.18 did + not handle objects with more than SHN_LORESERVE sections + correctly. All large section indexes were offset by + 0x100. There is more information at + http://sourceware.org/bugzilla/show_bug.cgi?id-5900 . + Fortunately these object files are easy to detect, as the + GNU binutils always put the section header string table + near the end of the list of sections. Thus if the + section header string table index is larger than the + number of sections, then we know we have to subtract + 0x100 to get the real section index. */ + if (eor->shstrndx >= eor->shnum + && eor->shstrndx >= SHN_LORESERVE + 0x100) + eor->shstrndx -= 0x100; + } + } + + if (eor->shstrndx >= eor->shnum) + { + *errmsg = "invalid ELF shstrndx >= shnum"; + *err = 0; + XDELETE (eor); + return NULL; + } + + return (void *) eor; +} + +/* Find all sections in an ELF file. */ + +static const char * +simple_object_elf_find_sections (simple_object_read *sobj, + int (*pfn) (void *, const char *, + off_t offset, off_t length), + void *data, + int *err) +{ + struct simple_object_elf_read *eor = + (struct simple_object_elf_read *) sobj->data; + const struct elf_type_functions *type_functions = eor->type_functions; + unsigned char ei_class = eor->ei_class; + size_t shdr_size; + unsigned int shnum; + unsigned char *shdrs; + const char *errmsg; + unsigned char *shstrhdr; + size_t name_size; + off_t shstroff; + unsigned char *names; + unsigned int i; + + shdr_size = (ei_class == ELFCLASS32 + ? sizeof (Elf32_External_Shdr) + : sizeof (Elf64_External_Shdr)); + + /* Read the section headers. We skip section 0, which is not a + useful section. */ + + shnum = eor->shnum; + shdrs = XNEWVEC (unsigned char, shdr_size * (shnum - 1)); + + if (!simple_object_internal_read (sobj->descriptor, + sobj->offset + eor->shoff + shdr_size, + shdrs, + shdr_size * (shnum - 1), + &errmsg, err)) + { + XDELETEVEC (shdrs); + return errmsg; + } + + /* Read the section names. */ + + shstrhdr = shdrs + (eor->shstrndx - 1) * shdr_size; + name_size = ELF_FETCH_FIELD (type_functions, ei_class, Shdr, + shstrhdr, sh_size, Elf_Addr); + shstroff = ELF_FETCH_FIELD (type_functions, ei_class, Shdr, + shstrhdr, sh_offset, Elf_Addr); + names = XNEWVEC (unsigned char, name_size); + if (!simple_object_internal_read (sobj->descriptor, + sobj->offset + shstroff, + names, name_size, &errmsg, err)) + { + XDELETEVEC (names); + XDELETEVEC (shdrs); + return errmsg; + } + + for (i = 1; i < shnum; ++i) + { + unsigned char *shdr; + unsigned int sh_name; + const char *name; + off_t offset; + off_t length; + + shdr = shdrs + (i - 1) * shdr_size; + sh_name = ELF_FETCH_FIELD (type_functions, ei_class, Shdr, + shdr, sh_name, Elf_Word); + if (sh_name >= name_size) + { + *err = 0; + XDELETEVEC (names); + XDELETEVEC (shdrs); + return "ELF section name out of range"; + } + + name = (const char *) names + sh_name; + offset = ELF_FETCH_FIELD (type_functions, ei_class, Shdr, + shdr, sh_offset, Elf_Addr); + length = ELF_FETCH_FIELD (type_functions, ei_class, Shdr, + shdr, sh_size, Elf_Addr); + + if (!(*pfn) (data, name, offset, length)) + break; + } + + XDELETEVEC (names); + XDELETEVEC (shdrs); + + return NULL; +} + +/* Fetch the attributes for an simple_object_read. */ + +static void * +simple_object_elf_fetch_attributes (simple_object_read *sobj, + const char **errmsg ATTRIBUTE_UNUSED, + int *err ATTRIBUTE_UNUSED) +{ + struct simple_object_elf_read *eor = + (struct simple_object_elf_read *) sobj->data; + struct simple_object_elf_attributes *ret; + + ret = XNEW (struct simple_object_elf_attributes); + ret->type_functions = eor->type_functions; + ret->ei_data = eor->ei_data; + ret->ei_class = eor->ei_class; + ret->ei_osabi = eor->ei_osabi; + ret->machine = eor->machine; + ret->flags = eor->flags; + return ret; +} + +/* Release the privata data for an simple_object_read. */ + +static void +simple_object_elf_release_read (void *data) +{ + XDELETE (data); +} + +/* Compare two attributes structures. */ + +static const char * +simple_object_elf_attributes_compare (void *data1, void *data2, int *err) +{ + struct simple_object_elf_attributes *attrs1 = + (struct simple_object_elf_attributes *) data1; + struct simple_object_elf_attributes *attrs2 = + (struct simple_object_elf_attributes *) data2; + + if (attrs1->ei_data != attrs2->ei_data + || attrs1->ei_class != attrs2->ei_class + || attrs1->machine != attrs2->machine) + { + *err = 0; + return "ELF object format mismatch"; + } + return NULL; +} + +/* Release the private data for an attributes structure. */ + +static void +simple_object_elf_release_attributes (void *data) +{ + XDELETE (data); +} + +/* Prepare to write out a file. */ + +static void * +simple_object_elf_start_write (void *attributes_data, + const char **errmsg ATTRIBUTE_UNUSED, + int *err ATTRIBUTE_UNUSED) +{ + struct simple_object_elf_attributes *attrs = + (struct simple_object_elf_attributes *) attributes_data; + struct simple_object_elf_attributes *ret; + + /* We're just going to record the attributes, but we need to make a + copy because the user may delete them. */ + ret = XNEW (struct simple_object_elf_attributes); + *ret = *attrs; + return ret; +} + +/* Write out an ELF ehdr. */ + +static int +simple_object_elf_write_ehdr (simple_object_write *sobj, int descriptor, + const char **errmsg, int *err) +{ + struct simple_object_elf_attributes *attrs = + (struct simple_object_elf_attributes *) sobj->data; + const struct elf_type_functions* fns; + unsigned char cl; + size_t ehdr_size; + unsigned char buf[sizeof (Elf64_External_Ehdr)]; + simple_object_write_section *section; + unsigned int shnum; + + fns = attrs->type_functions; + cl = attrs->ei_class; + + shnum = 0; + for (section = sobj->sections; section != NULL; section = section->next) + ++shnum; + if (shnum > 0) + { + /* Add a section header for the dummy section and one for + .shstrtab. */ + shnum += 2; + } + + ehdr_size = (cl == ELFCLASS32 + ? sizeof (Elf32_External_Ehdr) + : sizeof (Elf64_External_Ehdr)); + memset (buf, 0, sizeof (Elf64_External_Ehdr)); + + buf[EI_MAG0] = ELFMAG0; + buf[EI_MAG1] = ELFMAG1; + buf[EI_MAG2] = ELFMAG2; + buf[EI_MAG3] = ELFMAG3; + buf[EI_CLASS] = cl; + buf[EI_DATA] = attrs->ei_data; + buf[EI_VERSION] = EV_CURRENT; + buf[EI_OSABI] = attrs->ei_osabi; + + ELF_SET_FIELD (fns, cl, Ehdr, buf, e_type, Elf_Half, ET_REL); + ELF_SET_FIELD (fns, cl, Ehdr, buf, e_machine, Elf_Half, attrs->machine); + ELF_SET_FIELD (fns, cl, Ehdr, buf, e_version, Elf_Word, EV_CURRENT); + /* e_entry left as zero. */ + /* e_phoff left as zero. */ + ELF_SET_FIELD (fns, cl, Ehdr, buf, e_shoff, Elf_Addr, ehdr_size); + ELF_SET_FIELD (fns, cl, Ehdr, buf, e_flags, Elf_Word, attrs->flags); + ELF_SET_FIELD (fns, cl, Ehdr, buf, e_ehsize, Elf_Half, ehdr_size); + ELF_SET_FIELD (fns, cl, Ehdr, buf, e_phentsize, Elf_Half, + (cl == ELFCLASS32 + ? sizeof (Elf32_External_Phdr) + : sizeof (Elf64_External_Phdr))); + /* e_phnum left as zero. */ + ELF_SET_FIELD (fns, cl, Ehdr, buf, e_shentsize, Elf_Half, + (cl == ELFCLASS32 + ? sizeof (Elf32_External_Shdr) + : sizeof (Elf64_External_Shdr))); + ELF_SET_FIELD (fns, cl, Ehdr, buf, e_shnum, Elf_Half, shnum); + ELF_SET_FIELD (fns, cl, Ehdr, buf, e_shstrndx, Elf_Half, + shnum == 0 ? 0 : shnum - 1); + + return simple_object_internal_write (descriptor, 0, buf, ehdr_size, + errmsg, err); +} + +/* Write out an ELF shdr. */ + +static int +simple_object_elf_write_shdr (simple_object_write *sobj, int descriptor, + off_t offset, unsigned int sh_name, + unsigned int sh_type, unsigned int sh_flags, + unsigned int sh_offset, unsigned int sh_size, + unsigned int sh_addralign, const char **errmsg, + int *err) +{ + struct simple_object_elf_attributes *attrs = + (struct simple_object_elf_attributes *) sobj->data; + const struct elf_type_functions* fns; + unsigned char cl; + size_t shdr_size; + unsigned char buf[sizeof (Elf64_External_Shdr)]; + + fns = attrs->type_functions; + cl = attrs->ei_class; + + shdr_size = (cl == ELFCLASS32 + ? sizeof (Elf32_External_Shdr) + : sizeof (Elf64_External_Shdr)); + memset (buf, 0, sizeof (Elf64_External_Shdr)); + + ELF_SET_FIELD (fns, cl, Shdr, buf, sh_name, Elf_Word, sh_name); + ELF_SET_FIELD (fns, cl, Shdr, buf, sh_type, Elf_Word, sh_type); + ELF_SET_FIELD (fns, cl, Shdr, buf, sh_flags, Elf_Addr, sh_flags); + ELF_SET_FIELD (fns, cl, Shdr, buf, sh_offset, Elf_Addr, sh_offset); + ELF_SET_FIELD (fns, cl, Shdr, buf, sh_size, Elf_Addr, sh_size); + /* sh_link left as zero. */ + /* sh_info left as zero. */ + ELF_SET_FIELD (fns, cl, Shdr, buf, sh_addralign, Elf_Addr, sh_addralign); + /* sh_entsize left as zero. */ + + return simple_object_internal_write (descriptor, offset, buf, shdr_size, + errmsg, err); +} + +/* Write out a complete ELF file. + Ehdr + initial dummy Shdr + user-created Shdrs + .shstrtab Shdr + user-created section data + .shstrtab data */ + +static const char * +simple_object_elf_write_to_file (simple_object_write *sobj, int descriptor, + int *err) +{ + struct simple_object_elf_attributes *attrs = + (struct simple_object_elf_attributes *) sobj->data; + unsigned char cl; + size_t ehdr_size; + size_t shdr_size; + const char *errmsg; + simple_object_write_section *section; + unsigned int shnum; + size_t shdr_offset; + size_t sh_offset; + size_t sh_name; + unsigned char zero; + + if (!simple_object_elf_write_ehdr (sobj, descriptor, &errmsg, err)) + return errmsg; + + cl = attrs->ei_class; + if (cl == ELFCLASS32) + { + ehdr_size = sizeof (Elf32_External_Ehdr); + shdr_size = sizeof (Elf32_External_Shdr); + } + else + { + ehdr_size = sizeof (Elf64_External_Ehdr); + shdr_size = sizeof (Elf64_External_Shdr); + } + + shnum = 0; + for (section = sobj->sections; section != NULL; section = section->next) + ++shnum; + if (shnum == 0) + return NULL; + + /* Add initial dummy Shdr and .shstrtab. */ + shnum += 2; + + shdr_offset = ehdr_size; + sh_offset = shdr_offset + shnum * shdr_size; + + if (!simple_object_elf_write_shdr (sobj, descriptor, shdr_offset, + 0, 0, 0, 0, 0, 0, &errmsg, err)) + return errmsg; + + shdr_offset += shdr_size; + + sh_name = 1; + for (section = sobj->sections; section != NULL; section = section->next) + { + size_t mask; + size_t new_sh_offset; + size_t sh_size; + struct simple_object_write_section_buffer *buffer; + + mask = (1U << section->align) - 1; + new_sh_offset = sh_offset + mask; + new_sh_offset &= ~ mask; + while (new_sh_offset > sh_offset) + { + unsigned char zeroes[16]; + size_t write; + + memset (zeroes, 0, sizeof zeroes); + write = new_sh_offset - sh_offset; + if (write > sizeof zeroes) + write = sizeof zeroes; + if (!simple_object_internal_write (descriptor, sh_offset, zeroes, + write, &errmsg, err)) + return errmsg; + sh_offset += write; + } + + sh_size = 0; + for (buffer = section->buffers; buffer != NULL; buffer = buffer->next) + { + if (!simple_object_internal_write (descriptor, sh_offset + sh_size, + ((const unsigned char *) + buffer->buffer), + buffer->size, &errmsg, err)) + return errmsg; + sh_size += buffer->size; + } + + if (!simple_object_elf_write_shdr (sobj, descriptor, shdr_offset, + sh_name, SHT_PROGBITS, 0, sh_offset, + sh_size, 1U << section->align, + &errmsg, err)) + return errmsg; + + shdr_offset += shdr_size; + sh_name += strlen (section->name) + 1; + sh_offset += sh_size; + } + + if (!simple_object_elf_write_shdr (sobj, descriptor, shdr_offset, + sh_name, SHT_STRTAB, 0, sh_offset, + sh_name + strlen (".shstrtab") + 1, + 1, &errmsg, err)) + return errmsg; + + /* .shstrtab has a leading zero byte. */ + zero = 0; + if (!simple_object_internal_write (descriptor, sh_offset, &zero, 1, + &errmsg, err)) + return errmsg; + ++sh_offset; + + for (section = sobj->sections; section != NULL; section = section->next) + { + size_t len; + + len = strlen (section->name) + 1; + if (!simple_object_internal_write (descriptor, sh_offset, + (const unsigned char *) section->name, + len, &errmsg, err)) + return errmsg; + sh_offset += len; + } + + if (!simple_object_internal_write (descriptor, sh_offset, + (const unsigned char *) ".shstrtab", + strlen (".shstrtab") + 1, &errmsg, err)) + return errmsg; + + return NULL; +} + +/* Release the private data for an simple_object_write structure. */ + +static void +simple_object_elf_release_write (void *data) +{ + XDELETE (data); +} + +/* The ELF functions. */ + +const struct simple_object_functions simple_object_elf_functions = +{ + simple_object_elf_match, + simple_object_elf_find_sections, + simple_object_elf_fetch_attributes, + simple_object_elf_release_read, + simple_object_elf_attributes_compare, + simple_object_elf_release_attributes, + simple_object_elf_start_write, + simple_object_elf_write_to_file, + simple_object_elf_release_write +}; diff --git a/libiberty/simple-object-mach-o.c b/libiberty/simple-object-mach-o.c new file mode 100644 index 0000000..4067b16 --- /dev/null +++ b/libiberty/simple-object-mach-o.c @@ -0,0 +1,1022 @@ +/* simple-object-mach-o.c -- routines to manipulate Mach-O object files. + Copyright 2010 Free Software Foundation, Inc. + Written by Ian Lance Taylor, Google. + +This program 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 2, 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, write to the Free Software +Foundation, 51 Franklin Street - Fifth Floor, +Boston, MA 02110-1301, USA. */ + +#include "config.h" +#include "libiberty.h" +#include "simple-object.h" + +#include <stddef.h> + +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif + +#ifdef HAVE_STDINT_H +#include <stdint.h> +#endif + +#ifdef HAVE_STRING_H +#include <string.h> +#endif + +#ifdef HAVE_INTTYPES_H +#include <inttypes.h> +#endif + +#include "simple-object-common.h" + +/* Mach-O structures and constants. */ + +/* Mach-O header (32-bit version). */ + +struct mach_o_header_32 +{ + unsigned char magic[4]; /* Magic number. */ + unsigned char cputype[4]; /* CPU that this object is for. */ + unsigned char cpusubtype[4]; /* CPU subtype. */ + unsigned char filetype[4]; /* Type of file. */ + unsigned char ncmds[4]; /* Number of load commands. */ + unsigned char sizeofcmds[4]; /* Total size of load commands. */ + unsigned char flags[4]; /* Flags for special featues. */ +}; + +/* Mach-O header (64-bit version). */ + +struct mach_o_header_64 +{ + unsigned char magic[4]; /* Magic number. */ + unsigned char cputype[4]; /* CPU that this object is for. */ + unsigned char cpusubtype[4]; /* CPU subtype. */ + unsigned char filetype[4]; /* Type of file. */ + unsigned char ncmds[4]; /* Number of load commands. */ + unsigned char sizeofcmds[4]; /* Total size of load commands. */ + unsigned char flags[4]; /* Flags for special featues. */ + unsigned char reserved[4]; /* Reserved. Duh. */ +}; + +/* For magic field in header. */ + +#define MACH_O_MH_MAGIC 0xfeedface +#define MACH_O_MH_MAGIC_64 0xfeedfacf + +/* For filetype field in header. */ + +#define MACH_O_MH_OBJECT 0x01 + +/* A Mach-O file is a list of load commands. This is the header of a + load command. */ + +struct mach_o_load_command +{ + unsigned char cmd[4]; /* The type of load command. */ + unsigned char cmdsize[4]; /* Size in bytes of entire command. */ +}; + +/* For cmd field in load command. */ + +#define MACH_O_LC_SEGMENT 0x01 +#define MACH_O_LC_SEGMENT_64 0x19 + +/* LC_SEGMENT load command. */ + +struct mach_o_segment_command_32 +{ + unsigned char cmd[4]; /* The type of load command (LC_SEGMENT). */ + unsigned char cmdsize[4]; /* Size in bytes of entire command. */ + unsigned char segname[16]; /* Name of this segment. */ + unsigned char vmaddr[4]; /* Virtual memory address of this segment. */ + unsigned char vmsize[4]; /* Size there, in bytes. */ + unsigned char fileoff[4]; /* Offset in bytes of the data to be mapped. */ + unsigned char filesize[4]; /* Size in bytes on disk. */ + unsigned char maxprot[4]; /* Maximum permitted vmem protection. */ + unsigned char initprot[4]; /* Initial vmem protection. */ + unsigned char nsects[4]; /* Number of sections in this segment. */ + unsigned char flags[4]; /* Flags that affect the loading. */ +}; + +/* LC_SEGMENT_64 load command. */ + +struct mach_o_segment_command_64 +{ + unsigned char cmd[4]; /* The type of load command (LC_SEGMENT_64). */ + unsigned char cmdsize[4]; /* Size in bytes of entire command. */ + unsigned char segname[16]; /* Name of this segment. */ + unsigned char vmaddr[8]; /* Virtual memory address of this segment. */ + unsigned char vmsize[8]; /* Size there, in bytes. */ + unsigned char fileoff[8]; /* Offset in bytes of the data to be mapped. */ + unsigned char filesize[8]; /* Size in bytes on disk. */ + unsigned char maxprot[4]; /* Maximum permitted vmem protection. */ + unsigned char initprot[4]; /* Initial vmem protection. */ + unsigned char nsects[4]; /* Number of sections in this segment. */ + unsigned char flags[4]; /* Flags that affect the loading. */ +}; + +/* 32-bit section header. */ + +struct mach_o_section_32 +{ + unsigned char sectname[16]; /* Section name. */ + unsigned char segname[16]; /* Segment that the section belongs to. */ + unsigned char addr[4]; /* Address of this section in memory. */ + unsigned char size[4]; /* Size in bytes of this section. */ + unsigned char offset[4]; /* File offset of this section. */ + unsigned char align[4]; /* log2 of this section's alignment. */ + unsigned char reloff[4]; /* File offset of this section's relocs. */ + unsigned char nreloc[4]; /* Number of relocs for this section. */ + unsigned char flags[4]; /* Section flags/attributes. */ + unsigned char reserved1[4]; + unsigned char reserved2[4]; +}; + +/* 64-bit section header. */ + +struct mach_o_section_64 +{ + unsigned char sectname[16]; /* Section name. */ + unsigned char segname[16]; /* Segment that the section belongs to. */ + unsigned char addr[8]; /* Address of this section in memory. */ + unsigned char size[8]; /* Size in bytes of this section. */ + unsigned char offset[4]; /* File offset of this section. */ + unsigned char align[4]; /* log2 of this section's alignment. */ + unsigned char reloff[4]; /* File offset of this section's relocs. */ + unsigned char nreloc[4]; /* Number of relocs for this section. */ + unsigned char flags[4]; /* Section flags/attributes. */ + unsigned char reserved1[4]; + unsigned char reserved2[4]; + unsigned char reserved3[4]; +}; + +/* Flags for Mach-O sections. */ + +#define MACH_O_S_ATTR_DEBUG 0x02000000 + +/* The length of a segment or section name. */ + +#define MACH_O_NAME_LEN (16) + +/* A GNU specific extension for long section names. */ + +#define GNU_SECTION_NAMES "__section_names" + +/* Private data for an simple_object_read. */ + +struct simple_object_mach_o_read +{ + /* User specified segment name. */ + char *segment_name; + /* Magic number. */ + unsigned int magic; + /* Whether this file is big-endian. */ + int is_big_endian; + /* CPU type from header. */ + unsigned int cputype; + /* CPU subtype from header. */ + unsigned int cpusubtype; + /* Number of commands, from header. */ + unsigned int ncmds; + /* Flags from header. */ + unsigned int flags; + /* Reserved field from header, only used on 64-bit. */ + unsigned int reserved; +}; + +/* Private data for an simple_object_attributes. */ + +struct simple_object_mach_o_attributes +{ + /* Magic number. */ + unsigned int magic; + /* Whether this file is big-endian. */ + int is_big_endian; + /* CPU type from header. */ + unsigned int cputype; + /* CPU subtype from header. */ + unsigned int cpusubtype; + /* Flags from header. */ + unsigned int flags; + /* Reserved field from header, only used on 64-bit. */ + unsigned int reserved; +}; + +/* See if we have a Mach-O file. */ + +static void * +simple_object_mach_o_match ( + unsigned char header[SIMPLE_OBJECT_MATCH_HEADER_LEN], + int descriptor, + off_t offset, + const char *segment_name, + const char **errmsg, + int *err) +{ + unsigned int magic; + int is_big_endian; + unsigned int (*fetch_32) (const unsigned char *); + unsigned int filetype; + struct simple_object_mach_o_read *omr; + unsigned char buf[sizeof (struct mach_o_header_64)]; + unsigned char *b; + + magic = simple_object_fetch_big_32 (header); + if (magic == MACH_O_MH_MAGIC || magic == MACH_O_MH_MAGIC_64) + is_big_endian = 1; + else + { + magic = simple_object_fetch_little_32 (header); + if (magic == MACH_O_MH_MAGIC || magic == MACH_O_MH_MAGIC_64) + is_big_endian = 0; + else + { + *errmsg = NULL; + *err = 0; + return NULL; + } + } + +#ifndef UNSIGNED_64BIT_TYPE + if (magic == MACH_O_MH_MAGIC_64) + { + *errmsg = "64-bit Mach-O objects not supported"; + *err = 0; + return NULL; + } +#endif + + /* We require the user to provide a segment name. This is + unfortunate but I don't see any good choices here. */ + + if (segment_name == NULL) + { + *errmsg = "Mach-O file found but no segment name specified"; + *err = 0; + return NULL; + } + + if (strlen (segment_name) > MACH_O_NAME_LEN) + { + *errmsg = "Mach-O segment name too long"; + *err = 0; + return NULL; + } + + /* The 32-bit and 64-bit headers are similar enough that we can use + the same code. */ + + fetch_32 = (is_big_endian + ? simple_object_fetch_big_32 + : simple_object_fetch_little_32); + + if (!simple_object_internal_read (descriptor, offset, buf, + (magic == MACH_O_MH_MAGIC + ? sizeof (struct mach_o_header_32) + : sizeof (struct mach_o_header_64)), + errmsg, err)) + return NULL; + + b = &buf[0]; + + filetype = (*fetch_32) (b + offsetof (struct mach_o_header_32, filetype)); + if (filetype != MACH_O_MH_OBJECT) + { + *errmsg = "Mach-O file is not object file"; + *err = 0; + return NULL; + } + + omr = XNEW (struct simple_object_mach_o_read); + omr->segment_name = xstrdup (segment_name); + omr->magic = magic; + omr->is_big_endian = is_big_endian; + omr->cputype = (*fetch_32) (b + offsetof (struct mach_o_header_32, cputype)); + omr->cpusubtype = (*fetch_32) (b + + offsetof (struct mach_o_header_32, + cpusubtype)); + omr->ncmds = (*fetch_32) (b + offsetof (struct mach_o_header_32, ncmds)); + omr->flags = (*fetch_32) (b + offsetof (struct mach_o_header_32, flags)); + if (magic == MACH_O_MH_MAGIC) + omr->reserved = 0; + else + omr->reserved = (*fetch_32) (b + + offsetof (struct mach_o_header_64, + reserved)); + + return (void *) omr; +} + +/* Get the file offset and size from a section header. */ + +static void +simple_object_mach_o_section_info (int is_big_endian, int is_32, + const unsigned char *sechdr, off_t *offset, + size_t *size) +{ + unsigned int (*fetch_32) (const unsigned char *); + ulong_type (*fetch_64) (const unsigned char *); + + fetch_32 = (is_big_endian + ? simple_object_fetch_big_32 + : simple_object_fetch_little_32); + + fetch_64 = NULL; +#ifdef UNSIGNED_64BIT_TYPE + fetch_64 = (is_big_endian + ? simple_object_fetch_big_64 + : simple_object_fetch_little_64); +#endif + + if (is_32) + { + *offset = fetch_32 (sechdr + + offsetof (struct mach_o_section_32, offset)); + *size = fetch_32 (sechdr + + offsetof (struct mach_o_section_32, size)); + } + else + { + *offset = fetch_32 (sechdr + + offsetof (struct mach_o_section_64, offset)); + *size = fetch_64 (sechdr + + offsetof (struct mach_o_section_64, size)); + } +} + +/* Handle a segment in a Mach-O file. Return 1 if we should continue, + 0 if the caller should return. */ + +static int +simple_object_mach_o_segment (simple_object_read *sobj, off_t offset, + const unsigned char *segbuf, + int (*pfn) (void *, const char *, off_t offset, + off_t length), + void *data, + const char **errmsg, int *err) +{ + struct simple_object_mach_o_read *omr = + (struct simple_object_mach_o_read *) sobj->data; + unsigned int (*fetch_32) (const unsigned char *); + int is_32; + size_t seghdrsize; + size_t sechdrsize; + size_t segname_offset; + size_t sectname_offset; + unsigned int nsects; + unsigned char *secdata; + unsigned int i; + unsigned int strtab_index; + char *strtab; + size_t strtab_size; + + fetch_32 = (omr->is_big_endian + ? simple_object_fetch_big_32 + : simple_object_fetch_little_32); + + is_32 = omr->magic == MACH_O_MH_MAGIC; + + if (is_32) + { + seghdrsize = sizeof (struct mach_o_segment_command_32); + sechdrsize = sizeof (struct mach_o_section_32); + segname_offset = offsetof (struct mach_o_section_32, segname); + sectname_offset = offsetof (struct mach_o_section_32, sectname); + nsects = (*fetch_32) (segbuf + + offsetof (struct mach_o_segment_command_32, + nsects)); + } + else + { + seghdrsize = sizeof (struct mach_o_segment_command_64); + sechdrsize = sizeof (struct mach_o_section_64); + segname_offset = offsetof (struct mach_o_section_64, segname); + sectname_offset = offsetof (struct mach_o_section_64, sectname); + nsects = (*fetch_32) (segbuf + + offsetof (struct mach_o_segment_command_64, + nsects)); + } + + secdata = XNEWVEC (unsigned char, nsects * sechdrsize); + if (!simple_object_internal_read (sobj->descriptor, offset + seghdrsize, + secdata, nsects * sechdrsize, errmsg, err)) + { + XDELETEVEC (secdata); + return 0; + } + + /* Scan for a __section_names section. This is in effect a GNU + extension that permits section names longer than 16 chars. */ + + for (i = 0; i < nsects; ++i) + { + size_t nameoff; + + nameoff = i * sechdrsize + segname_offset; + if (strcmp ((char *) secdata + nameoff, omr->segment_name) != 0) + continue; + nameoff = i * sechdrsize + sectname_offset; + if (strcmp ((char *) secdata + nameoff, GNU_SECTION_NAMES) == 0) + break; + } + + strtab_index = i; + if (strtab_index >= nsects) + { + strtab = NULL; + strtab_size = 0; + } + else + { + off_t strtab_offset; + + simple_object_mach_o_section_info (omr->is_big_endian, is_32, + secdata + strtab_index * sechdrsize, + &strtab_offset, &strtab_size); + strtab = XNEWVEC (char, strtab_size); + if (!simple_object_internal_read (sobj->descriptor, + sobj->offset + strtab_offset, + (unsigned char *) strtab, strtab_size, + errmsg, err)) + { + XDELETEVEC (strtab); + XDELETEVEC (secdata); + return 0; + } + } + + /* Process the sections. */ + + for (i = 0; i < nsects; ++i) + { + const unsigned char *sechdr; + char namebuf[MACH_O_NAME_LEN + 1]; + char *name; + off_t secoffset; + size_t secsize; + + if (i == strtab_index) + continue; + + sechdr = secdata + i * sechdrsize; + + if (strcmp ((char *) sechdr + segname_offset, omr->segment_name) != 0) + continue; + + memcpy (namebuf, sechdr + sectname_offset, MACH_O_NAME_LEN); + namebuf[MACH_O_NAME_LEN] = '\0'; + + name = &namebuf[0]; + if (strtab != NULL && name[0] == '_' && name[1] == '_') + { + unsigned long stringoffset; + + if (sscanf (name + 2, "%08lX", &stringoffset) == 1) + { + if (stringoffset >= strtab_size) + { + *errmsg = "section name offset out of range"; + *err = 0; + XDELETEVEC (strtab); + XDELETEVEC (secdata); + return 0; + } + + name = strtab + stringoffset; + } + } + + simple_object_mach_o_section_info (omr->is_big_endian, is_32, sechdr, + &secoffset, &secsize); + + if (!(*pfn) (data, name, secoffset, secsize)) + { + *errmsg = NULL; + *err = 0; + XDELETEVEC (strtab); + XDELETEVEC (secdata); + return 0; + } + } + + XDELETEVEC (strtab); + XDELETEVEC (secdata); + + return 1; +} + +/* Find all sections in a Mach-O file. */ + +static const char * +simple_object_mach_o_find_sections (simple_object_read *sobj, + int (*pfn) (void *, const char *, + off_t offset, off_t length), + void *data, + int *err) +{ + struct simple_object_mach_o_read *omr = + (struct simple_object_mach_o_read *) sobj->data; + off_t offset; + size_t seghdrsize; + unsigned int (*fetch_32) (const unsigned char *); + const char *errmsg; + unsigned int i; + + if (omr->magic == MACH_O_MH_MAGIC) + { + offset = sizeof (struct mach_o_header_32); + seghdrsize = sizeof (struct mach_o_segment_command_32); + } + else + { + offset = sizeof (struct mach_o_header_64); + seghdrsize = sizeof (struct mach_o_segment_command_64); + } + + fetch_32 = (omr->is_big_endian + ? simple_object_fetch_big_32 + : simple_object_fetch_little_32); + + for (i = 0; i < omr->ncmds; ++i) + { + unsigned char loadbuf[sizeof (struct mach_o_load_command)]; + unsigned int cmd; + unsigned int cmdsize; + + if (!simple_object_internal_read (sobj->descriptor, + sobj->offset + offset, + loadbuf, + sizeof (struct mach_o_load_command), + &errmsg, err)) + return errmsg; + + cmd = (*fetch_32) (loadbuf + offsetof (struct mach_o_load_command, cmd)); + cmdsize = (*fetch_32) (loadbuf + + offsetof (struct mach_o_load_command, cmdsize)); + + if (cmd == MACH_O_LC_SEGMENT || cmd == MACH_O_LC_SEGMENT_64) + { + unsigned char segbuf[sizeof (struct mach_o_segment_command_64)]; + int r; + + if (!simple_object_internal_read (sobj->descriptor, + sobj->offset + offset, + segbuf, seghdrsize, &errmsg, err)) + return errmsg; + + r = simple_object_mach_o_segment (sobj, offset, segbuf, pfn, + data, &errmsg, err); + if (!r) + return errmsg; + } + + offset += cmdsize; + } + + return NULL; +} + +/* Fetch the attributes for an simple_object_read. */ + +static void * +simple_object_mach_o_fetch_attributes (simple_object_read *sobj, + const char **errmsg ATTRIBUTE_UNUSED, + int *err ATTRIBUTE_UNUSED) +{ + struct simple_object_mach_o_read *omr = + (struct simple_object_mach_o_read *) sobj->data; + struct simple_object_mach_o_attributes *ret; + + ret = XNEW (struct simple_object_mach_o_attributes); + ret->magic = omr->magic; + ret->is_big_endian = omr->is_big_endian; + ret->cputype = omr->cputype; + ret->cpusubtype = omr->cpusubtype; + ret->flags = omr->flags; + ret->reserved = omr->reserved; + return ret; +} + +/* Release the private data for an simple_object_read. */ + +static void +simple_object_mach_o_release_read (void *data) +{ + struct simple_object_mach_o_read *omr = + (struct simple_object_mach_o_read *) data; + + free (omr->segment_name); + XDELETE (omr); +} + +/* Compare two attributes structures. */ + +static const char * +simple_object_mach_o_attributes_compare (void *data1, void *data2, int *err) +{ + struct simple_object_mach_o_attributes *attrs1 = + (struct simple_object_mach_o_attributes *) data1; + struct simple_object_mach_o_attributes *attrs2 = + (struct simple_object_mach_o_attributes *) data2; + + if (attrs1->magic != attrs2->magic + || attrs1->is_big_endian != attrs2->is_big_endian + || attrs1->cputype != attrs2->cputype) + { + *err = 0; + return "Mach-O object format mismatch"; + } + return NULL; +} + +/* Release the private data for an attributes structure. */ + +static void +simple_object_mach_o_release_attributes (void *data) +{ + XDELETE (data); +} + +/* Prepare to write out a file. */ + +static void * +simple_object_mach_o_start_write (void *attributes_data, + const char **errmsg ATTRIBUTE_UNUSED, + int *err ATTRIBUTE_UNUSED) +{ + struct simple_object_mach_o_attributes *attrs = + (struct simple_object_mach_o_attributes *) attributes_data; + struct simple_object_mach_o_attributes *ret; + + /* We're just going to record the attributes, but we need to make a + copy because the user may delete them. */ + ret = XNEW (struct simple_object_mach_o_attributes); + *ret = *attrs; + return ret; +} + +/* Write out the header of a Mach-O file. */ + +static int +simple_object_mach_o_write_header (simple_object_write *sobj, int descriptor, + size_t nsects, const char **errmsg, + int *err) +{ + struct simple_object_mach_o_attributes *attrs = + (struct simple_object_mach_o_attributes *) sobj->data; + void (*set_32) (unsigned char *, unsigned int); + unsigned char hdrbuf[sizeof (struct mach_o_header_64)]; + unsigned char *hdr; + size_t wrsize; + + set_32 = (attrs->is_big_endian + ? simple_object_set_big_32 + : simple_object_set_little_32); + + memset (hdrbuf, 0, sizeof hdrbuf); + + /* The 32-bit and 64-bit headers start out the same. */ + + hdr = &hdrbuf[0]; + set_32 (hdr + offsetof (struct mach_o_header_32, magic), attrs->magic); + set_32 (hdr + offsetof (struct mach_o_header_32, cputype), attrs->cputype); + set_32 (hdr + offsetof (struct mach_o_header_32, cpusubtype), + attrs->cpusubtype); + set_32 (hdr + offsetof (struct mach_o_header_32, filetype), MACH_O_MH_OBJECT); + set_32 (hdr + offsetof (struct mach_o_header_32, ncmds), 1); + set_32 (hdr + offsetof (struct mach_o_header_32, flags), attrs->flags); + if (attrs->magic == MACH_O_MH_MAGIC) + { + wrsize = sizeof (struct mach_o_header_32); + set_32 (hdr + offsetof (struct mach_o_header_32, sizeofcmds), + (sizeof (struct mach_o_segment_command_32) + + nsects * sizeof (struct mach_o_section_32))); + } + else + { + set_32 (hdr + offsetof (struct mach_o_header_64, sizeofcmds), + (sizeof (struct mach_o_segment_command_64) + + nsects * sizeof (struct mach_o_section_64))); + set_32 (hdr + offsetof (struct mach_o_header_64, reserved), + attrs->reserved); + wrsize = sizeof (struct mach_o_header_64); + } + + return simple_object_internal_write (descriptor, 0, hdrbuf, wrsize, + errmsg, err); +} + +/* Write a Mach-O section header. */ + +static int +simple_object_mach_o_write_section_header (simple_object_write *sobj, + int descriptor, + size_t sechdr_offset, + const char *name, size_t secaddr, + size_t secsize, size_t offset, + unsigned int align, + const char **errmsg, int *err) +{ + struct simple_object_mach_o_attributes *attrs = + (struct simple_object_mach_o_attributes *) sobj->data; + void (*set_32) (unsigned char *, unsigned int); + unsigned char hdrbuf[sizeof (struct mach_o_section_64)]; + unsigned char *hdr; + size_t sechdrsize; + + set_32 = (attrs->is_big_endian + ? simple_object_set_big_32 + : simple_object_set_little_32); + + memset (hdrbuf, 0, sizeof hdrbuf); + + hdr = &hdrbuf[0]; + if (attrs->magic == MACH_O_MH_MAGIC) + { + strncpy ((char *) hdr + offsetof (struct mach_o_section_32, sectname), + name, MACH_O_NAME_LEN); + strncpy ((char *) hdr + offsetof (struct mach_o_section_32, segname), + sobj->segment_name, MACH_O_NAME_LEN); + set_32 (hdr + offsetof (struct mach_o_section_32, addr), secaddr); + set_32 (hdr + offsetof (struct mach_o_section_32, size), secsize); + set_32 (hdr + offsetof (struct mach_o_section_32, offset), offset); + set_32 (hdr + offsetof (struct mach_o_section_32, align), align); + /* reloff left as zero. */ + /* nreloc left as zero. */ + set_32 (hdr + offsetof (struct mach_o_section_32, flags), + MACH_O_S_ATTR_DEBUG); + /* reserved1 left as zero. */ + /* reserved2 left as zero. */ + sechdrsize = sizeof (struct mach_o_section_32); + } + else + { +#ifdef UNSIGNED_64BIT_TYPE + void (*set_64) (unsigned char *, ulong_type); + + set_64 = (attrs->is_big_endian + ? simple_object_set_big_64 + : simple_object_set_little_64); + + strncpy ((char *) hdr + offsetof (struct mach_o_section_64, sectname), + name, MACH_O_NAME_LEN); + strncpy ((char *) hdr + offsetof (struct mach_o_section_64, segname), + sobj->segment_name, MACH_O_NAME_LEN); + set_64 (hdr + offsetof (struct mach_o_section_64, addr), secaddr); + set_64 (hdr + offsetof (struct mach_o_section_64, size), secsize); + set_32 (hdr + offsetof (struct mach_o_section_64, offset), offset); + set_32 (hdr + offsetof (struct mach_o_section_64, align), align); + /* reloff left as zero. */ + /* nreloc left as zero. */ + set_32 (hdr + offsetof (struct mach_o_section_64, flags), + MACH_O_S_ATTR_DEBUG); + /* reserved1 left as zero. */ + /* reserved2 left as zero. */ + /* reserved3 left as zero. */ +#endif + sechdrsize = sizeof (struct mach_o_section_64); + } + + return simple_object_internal_write (descriptor, sechdr_offset, hdr, + sechdrsize, errmsg, err); +} + +/* Write out the single segment and the sections of a Mach-O file. */ + +static int +simple_object_mach_o_write_segment (simple_object_write *sobj, int descriptor, + size_t nsects, const char **errmsg, + int *err) +{ + struct simple_object_mach_o_attributes *attrs = + (struct simple_object_mach_o_attributes *) sobj->data; + void (*set_32) (unsigned char *, unsigned int); + size_t hdrsize; + size_t seghdrsize; + size_t sechdrsize; + size_t cmdsize; + size_t offset; + size_t sechdr_offset; + size_t secaddr; + unsigned int name_offset; + simple_object_write_section *section; + unsigned char hdrbuf[sizeof (struct mach_o_segment_command_64)]; + unsigned char *hdr; + + set_32 = (attrs->is_big_endian + ? simple_object_set_big_32 + : simple_object_set_little_32); + + /* Write out the sections first. */ + + if (attrs->magic == MACH_O_MH_MAGIC) + { + hdrsize = sizeof (struct mach_o_header_32); + seghdrsize = sizeof (struct mach_o_segment_command_32); + sechdrsize = sizeof (struct mach_o_section_32); + } + else + { + hdrsize = sizeof (struct mach_o_header_64); + seghdrsize = sizeof (struct mach_o_segment_command_64); + sechdrsize = sizeof (struct mach_o_section_64); + } + + sechdr_offset = hdrsize + seghdrsize; + cmdsize = seghdrsize + nsects * sechdrsize; + offset = hdrsize + cmdsize; + name_offset = 0; + secaddr = 0; + + for (section = sobj->sections; section != NULL; section = section->next) + { + size_t mask; + size_t new_offset; + size_t secsize; + struct simple_object_write_section_buffer *buffer; + char namebuf[MACH_O_NAME_LEN + 1]; + + mask = (1U << section->align) - 1; + new_offset = offset + mask; + new_offset &= ~ mask; + while (new_offset > offset) + { + unsigned char zeroes[16]; + size_t write; + + memset (zeroes, 0, sizeof zeroes); + write = new_offset - offset; + if (write > sizeof zeroes) + write = sizeof zeroes; + if (!simple_object_internal_write (descriptor, offset, zeroes, write, + errmsg, err)) + return 0; + offset += write; + } + + secsize = 0; + for (buffer = section->buffers; buffer != NULL; buffer = buffer->next) + { + if (!simple_object_internal_write (descriptor, offset + secsize, + ((const unsigned char *) + buffer->buffer), + buffer->size, errmsg, err)) + return 0; + secsize += buffer->size; + } + + snprintf (namebuf, sizeof namebuf, "__%08X", name_offset); + if (!simple_object_mach_o_write_section_header (sobj, descriptor, + sechdr_offset, namebuf, + secaddr, secsize, offset, + section->align, + errmsg, err)) + return 0; + + sechdr_offset += sechdrsize; + offset += secsize; + name_offset += strlen (section->name) + 1; + secaddr += secsize; + } + + /* Write out the section names. */ + + if (!simple_object_mach_o_write_section_header (sobj, descriptor, + sechdr_offset, + GNU_SECTION_NAMES, secaddr, + name_offset, offset, 0, + errmsg, err)) + return 0; + + for (section = sobj->sections; section != NULL; section = section->next) + { + size_t namelen; + + namelen = strlen (section->name) + 1; + if (!simple_object_internal_write (descriptor, offset, + (const unsigned char *) section->name, + namelen, errmsg, err)) + return 0; + offset += namelen; + } + + /* Write out the segment header. */ + + memset (hdrbuf, 0, sizeof hdrbuf); + + hdr = &hdrbuf[0]; + if (attrs->magic == MACH_O_MH_MAGIC) + { + set_32 (hdr + offsetof (struct mach_o_segment_command_32, cmd), + MACH_O_LC_SEGMENT); + set_32 (hdr + offsetof (struct mach_o_segment_command_32, cmdsize), + cmdsize); + strncpy (((char *) hdr + + offsetof (struct mach_o_segment_command_32, segname)), + sobj->segment_name, MACH_O_NAME_LEN); + /* vmaddr left as zero. */ + /* vmsize left as zero. */ + set_32 (hdr + offsetof (struct mach_o_segment_command_32, fileoff), + hdrsize + cmdsize); + set_32 (hdr + offsetof (struct mach_o_segment_command_32, filesize), + offset - (hdrsize + cmdsize)); + /* maxprot left as zero. */ + /* initprot left as zero. */ + set_32 (hdr + offsetof (struct mach_o_segment_command_32, nsects), + nsects); + /* flags left as zero. */ + } + else + { +#ifdef UNSIGNED_64BIT_TYPE + void (*set_64) (unsigned char *, ulong_type); + + set_64 = (attrs->is_big_endian + ? simple_object_set_big_64 + : simple_object_set_little_64); + + set_32 (hdr + offsetof (struct mach_o_segment_command_64, cmd), + MACH_O_LC_SEGMENT); + set_32 (hdr + offsetof (struct mach_o_segment_command_64, cmdsize), + cmdsize); + strncpy (((char *) hdr + + offsetof (struct mach_o_segment_command_64, segname)), + sobj->segment_name, MACH_O_NAME_LEN); + /* vmaddr left as zero. */ + /* vmsize left as zero. */ + set_64 (hdr + offsetof (struct mach_o_segment_command_64, fileoff), + hdrsize + cmdsize); + set_64 (hdr + offsetof (struct mach_o_segment_command_64, filesize), + offset - (hdrsize + cmdsize)); + /* maxprot left as zero. */ + /* initprot left as zero. */ + set_32 (hdr + offsetof (struct mach_o_segment_command_64, nsects), + nsects); + /* flags left as zero. */ +#endif + } + + return simple_object_internal_write (descriptor, hdrsize, hdr, seghdrsize, + errmsg, err); +} + +/* Write out a complete Mach-O file. */ + +static const char * +simple_object_mach_o_write_to_file (simple_object_write *sobj, int descriptor, + int *err) +{ + size_t nsects; + simple_object_write_section *section; + const char *errmsg; + + /* Start at 1 for symbol_names section. */ + nsects = 1; + for (section = sobj->sections; section != NULL; section = section->next) + ++nsects; + + if (!simple_object_mach_o_write_header (sobj, descriptor, nsects, + &errmsg, err)) + return errmsg; + + if (!simple_object_mach_o_write_segment (sobj, descriptor, nsects, + &errmsg, err)) + return errmsg; + + return NULL; +} + +/* Release the private data for an simple_object_write structure. */ + +static void +simple_object_mach_o_release_write (void *data) +{ + XDELETE (data); +} + +/* The Mach-O functions. */ + +const struct simple_object_functions simple_object_mach_o_functions = +{ + simple_object_mach_o_match, + simple_object_mach_o_find_sections, + simple_object_mach_o_fetch_attributes, + simple_object_mach_o_release_read, + simple_object_mach_o_attributes_compare, + simple_object_mach_o_release_attributes, + simple_object_mach_o_start_write, + simple_object_mach_o_write_to_file, + simple_object_mach_o_release_write +}; diff --git a/libiberty/simple-object.c b/libiberty/simple-object.c new file mode 100644 index 0000000..c9bd82f --- /dev/null +++ b/libiberty/simple-object.c @@ -0,0 +1,423 @@ +/* simple-object.c -- simple routines to read and write object files. + Copyright 2010 Free Software Foundation, Inc. + Written by Ian Lance Taylor, Google. + +This program 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 2, 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, write to the Free Software +Foundation, 51 Franklin Street - Fifth Floor, +Boston, MA 02110-1301, USA. */ + +#include "config.h" +#include "libiberty.h" +#include "simple-object.h" + +#include <errno.h> + +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif + +#ifdef HAVE_STDINT_H +#include <stdint.h> +#endif + +#ifdef HAVE_STRING_H +#include <string.h> +#endif + +#ifdef HAVE_INTTYPES_H +#include <inttypes.h> +#endif + +#ifndef SEEK_SET +#define SEEK_SET 0 +#endif + +#include "simple-object-common.h" + +/* The known object file formats. */ + +static const struct simple_object_functions * const format_functions[] = +{ + &simple_object_elf_functions, + &simple_object_mach_o_functions, + &simple_object_coff_functions +}; + +/* Read data from a file using the simple_object error reporting + conventions. */ + +int +simple_object_internal_read (int descriptor, off_t offset, + unsigned char *buffer, size_t size, + const char **errmsg, int *err) +{ + ssize_t got; + + if (lseek (descriptor, offset, SEEK_SET) < 0) + { + *errmsg = "lseek"; + *err = errno; + return 0; + } + + got = read (descriptor, buffer, size); + if (got < 0) + { + *errmsg = "read"; + *err = errno; + return 0; + } + + if ((size_t) got < size) + { + *errmsg = "file too short"; + *err = 0; + return 0; + } + + return 1; +} + +/* Write data to a file using the simple_object error reporting + conventions. */ + +int +simple_object_internal_write (int descriptor, off_t offset, + const unsigned char *buffer, size_t size, + const char **errmsg, int *err) +{ + ssize_t wrote; + + if (lseek (descriptor, offset, SEEK_SET) < 0) + { + *errmsg = "lseek"; + *err = errno; + return 0; + } + + wrote = write (descriptor, buffer, size); + if (wrote < 0) + { + *errmsg = "write"; + *err = errno; + return 0; + } + + if ((size_t) wrote < size) + { + *errmsg = "short write"; + *err = 0; + return 0; + } + + return 1; +} + +/* Open for read. */ + +simple_object_read * +simple_object_start_read (int descriptor, off_t offset, + const char *segment_name, const char **errmsg, + int *err) +{ + unsigned char header[SIMPLE_OBJECT_MATCH_HEADER_LEN]; + size_t len, i; + + if (!simple_object_internal_read (descriptor, offset, header, + SIMPLE_OBJECT_MATCH_HEADER_LEN, + errmsg, err)) + return NULL; + + len = sizeof (format_functions) / sizeof (format_functions[0]); + for (i = 0; i < len; ++i) + { + void *data; + + data = format_functions[i]->match (header, descriptor, offset, + segment_name, errmsg, err); + if (data != NULL) + { + simple_object_read *ret; + + ret = XNEW (simple_object_read); + ret->descriptor = descriptor; + ret->offset = offset; + ret->functions = format_functions[i]; + ret->data = data; + return ret; + } + } + + *errmsg = "file not recognized"; + *err = 0; + return NULL; +} + +/* Find all sections. */ + +const char * +simple_object_find_sections (simple_object_read *sobj, + int (*pfn) (void *, const char *, off_t, off_t), + void *data, + int *err) +{ + return sobj->functions->find_sections (sobj, pfn, data, err); +} + +/* Internal data passed to find_one_section. */ + +struct find_one_section_data +{ + /* The section we are looking for. */ + const char *name; + /* Where to store the section offset. */ + off_t *offset; + /* Where to store the section length. */ + off_t *length; + /* Set if the name is found. */ + int found; +}; + +/* Internal function passed to find_sections. */ + +static int +find_one_section (void *data, const char *name, off_t offset, off_t length) +{ + struct find_one_section_data *fosd = (struct find_one_section_data *) data; + + if (strcmp (name, fosd->name) != 0) + return 1; + + *fosd->offset = offset; + *fosd->length = length; + fosd->found = 1; + + /* Stop iteration. */ + return 0; +} + +/* Find a section. */ + +int +simple_object_find_section (simple_object_read *sobj, const char *name, + off_t *offset, off_t *length, + const char **errmsg, int *err) +{ + struct find_one_section_data fosd; + + fosd.name = name; + fosd.offset = offset; + fosd.length = length; + fosd.found = 0; + + *errmsg = simple_object_find_sections (sobj, find_one_section, + (void *) &fosd, err); + if (*errmsg != NULL) + return 0; + if (!fosd.found) + return 0; + return 1; +} + +/* Fetch attributes. */ + +simple_object_attributes * +simple_object_fetch_attributes (simple_object_read *sobj, const char **errmsg, + int *err) +{ + void *data; + simple_object_attributes *ret; + + data = sobj->functions->fetch_attributes (sobj, errmsg, err); + if (data == NULL) + return NULL; + ret = XNEW (simple_object_attributes); + ret->functions = sobj->functions; + ret->data = data; + return ret; +} + +/* Release an simple_object_read. */ + +void +simple_object_release_read (simple_object_read *sobj) +{ + sobj->functions->release_read (sobj->data); + XDELETE (sobj); +} + +/* Compare attributes. */ + +const char * +simple_object_attributes_compare (simple_object_attributes *attrs1, + simple_object_attributes *attrs2, + int *err) +{ + if (attrs1->functions != attrs2->functions) + { + *err = 0; + return "different object file format"; + } + return attrs1->functions->attributes_compare (attrs1->data, attrs2->data, + err); +} + +/* Release an attributes structure. */ + +void +simple_object_release_attributes (simple_object_attributes *attrs) +{ + attrs->functions->release_attributes (attrs->data); + XDELETE (attrs); +} + +/* Start creating an object file. */ + +simple_object_write * +simple_object_start_write (simple_object_attributes *attrs, + const char *segment_name, const char **errmsg, + int *err) +{ + void *data; + simple_object_write *ret; + + data = attrs->functions->start_write (attrs->data, errmsg, err); + if (data == NULL) + return NULL; + ret = XNEW (simple_object_write); + ret->functions = attrs->functions; + ret->segment_name = xstrdup (segment_name); + ret->sections = NULL; + ret->last_section = NULL; + ret->data = data; + return ret; +} + +/* Start creating a section. */ + +simple_object_write_section * +simple_object_write_create_section (simple_object_write *sobj, const char *name, + unsigned int align, + const char **errmsg ATTRIBUTE_UNUSED, + int *err ATTRIBUTE_UNUSED) +{ + simple_object_write_section *ret; + + ret = XNEW (simple_object_write_section); + ret->next = NULL; + ret->name = xstrdup (name); + ret->align = align; + ret->buffers = NULL; + ret->last_buffer = NULL; + + if (sobj->last_section == NULL) + { + sobj->sections = ret; + sobj->last_section = ret; + } + else + { + sobj->last_section->next = ret; + sobj->last_section = ret; + } + + return ret; +} + +/* Add data to a section. */ + +const char * +simple_object_write_add_data (simple_object_write *sobj ATTRIBUTE_UNUSED, + simple_object_write_section *section, + const void *buffer, + size_t size, int copy, + int *err ATTRIBUTE_UNUSED) +{ + struct simple_object_write_section_buffer *wsb; + + wsb = XNEW (struct simple_object_write_section_buffer); + wsb->next = NULL; + wsb->size = size; + + if (!copy) + { + wsb->buffer = buffer; + wsb->free_buffer = NULL; + } + else + { + wsb->free_buffer = (void *) XNEWVEC (char, size); + memcpy (wsb->free_buffer, buffer, size); + wsb->buffer = wsb->free_buffer; + } + + if (section->last_buffer == NULL) + { + section->buffers = wsb; + section->last_buffer = wsb; + } + else + { + section->last_buffer->next = wsb; + section->last_buffer = wsb; + } + + return NULL; +} + +/* Write the complete object file. */ + +const char * +simple_object_write_to_file (simple_object_write *sobj, int descriptor, + int *err) +{ + return sobj->functions->write_to_file (sobj, descriptor, err); +} + +/* Release an simple_object_write. */ + +void +simple_object_release_write (simple_object_write *sobj) +{ + simple_object_write_section *section; + + free (sobj->segment_name); + + section = sobj->sections; + while (section != NULL) + { + struct simple_object_write_section_buffer *buffer; + simple_object_write_section *next_section; + + buffer = section->buffers; + while (buffer != NULL) + { + struct simple_object_write_section_buffer *next_buffer; + + if (buffer->free_buffer != NULL) + XDELETEVEC (buffer->free_buffer); + next_buffer = buffer->next; + XDELETE (buffer); + buffer = next_buffer; + } + + next_section = section->next; + free (section->name); + XDELETE (section); + section = next_section; + } + + sobj->functions->release_write (sobj->data); + XDELETE (sobj); +} diff --git a/libiberty/simple-object.txh b/libiberty/simple-object.txh new file mode 100644 index 0000000..907233a --- /dev/null +++ b/libiberty/simple-object.txh @@ -0,0 +1,168 @@ +@c -*- mode: texinfo -*- +@deftypefn Extension {simple_object_read *} simple_object_open_read (int @var{descriptor}, off_t @var{offset}, const char *{segment_name}, const char **@var{errmsg}, int *@var{err}) + +Opens an object file for reading. Creates and returns an +@code{simple_object_read} pointer which may be passed to other +functions to extract data from the object file. + +@var{descriptor} holds a file descriptor which permits reading. + +@var{offset} is the offset into the file; this will be @code{0} in the +normal case, but may be a different value when reading an object file +in an archive file. + +@var{segment_name} is only used with the Mach-O file format used on +Darwin aka Mac OS X. It is required on that platform, and means to +only look at sections within the segment with that name. The +parameter is ignored on other systems. + +If an error occurs, this functions returns @code{NULL} and sets +@code{*@var{errmsg}} to an error string and sets @code{*@var{err}} to +an errno value or @code{0} if there is no relevant errno. + +@end deftypefn + +@deftypefn Extension {const char *} simple_object_find_sections (simple_object_read *@var{simple_object}, int (*@var{pfn}) (void *@var{data}, const char *@var{name}, off_t @var{offset}, off_t @var{length}), void *@var{data}, int *@var{err}) + +This function calls @var{pfn} for each section in @var{simple_object}. +It calls @var{pfn} with the section name, the offset within the file +of the section contents, and the length of the section contents. The +offset within the file is relative to the offset passed to +@code{simple_object_open_read}. The @var{data} argument to this +function is passed along to @var{pfn}. + +If @var{pfn} returns @code{0}, the loop over the sections stops and +@code{simple_object_find_sections} returns. If @var{pfn} returns some +other value, the loop continues. + +On success @code{simple_object_find_sections} returns. On error it +returns an error string, and sets @code{*@var{err}} to an errno value +or @code{0} if there is no relevant errno. + +@end deftypefn + +@deftypefn Extension {int} simple_object_find_section (simple_object_read *@var{simple_object} off_t *@var{offset}, off_t *@var{length}, const char **@var{errmsg}, int *@var{err}) + +Look for the section @var{name} in @var{simple_object}. This returns +information for the first section with that name. + +If found, return 1 and set @code{*@var{offset}} to the offset in the +file of the section contents and set @code{*@var{length}} to the +length of the section contents. The value in @code{*@var{offset}} +will be relative to the offset passed to +@code{simple_object_open_read}. + +If the section is not found, and no error occurs, +@code{simple_object_find_section} returns @code{0} and set +@code{*@var{errmsg}} to @code{NULL}. + +If an error occurs, @code{simple_object_find_section} returns +@code{0}, sets @code{*@var{errmsg}} to an error message, and sets +@code{*@var{err}} to an errno value or @code{0} if there is no +relevant errno. + +@end deftypefn + +@deftypefn Extension {void} simple_object_release_read (simple_object_read *@var{simple_object}) + +Release all resources associated with @var{simple_object}. This does +not close the file descriptor. + +@end deftypefn + +@deftypefn Extension {simple_object_attributes *} simple_object_fetch_attributes (simple_object_read *@var{simple_object}, const char **@var{errmsg}, int *@var{err}) + +Fetch the attributes of @var{simple_object}. The attributes are +internal information such as the format of the object file, or the +architecture it was compiled for. This information will persist until +@code{simple_object_attributes_release} is called, even if +@var{simple_object} itself is released. + +On error this returns @code{NULL}, sets @code{*@var{errmsg}} to an +error message, and sets @code{*@var{err}} to an errno value or +@code{0} if there is no relevant errno. + +@end deftypefn + +@deftypefn Extension {const char *} simple_object_attributes_compare (simple_object_attributes *@var{attrs1}, simple_object_attributes *@var{attrs2}, int *@var{err}) + +Compare @var{attrs1} and @var{attrs2}. If they could be linked +together without error, return @code{NULL}. Otherwise, return an +error message and set @code{*@var{err}} to an errno value or @code{0} +if there is no relevant errno. + +@end deftypefn + +@deftypefn Extension {void} simple_object_release_attributes (simple_object_attributes *@var{attrs}) + +Release all resources associated with @var{attrs}. + +@end deftypefn + +@deftypefn Extension {simple_object_write *} simple_object_start_write (simple_object_attributes @var{attrs}, const char *@var{segment_name}, const char **@var{errmsg}, int *@var{err}) + +Start creating a new object file using the object file format +described in @var{attrs}. You must fetch attribute information from +an existing object file before you can create a new one. There is +currently no support for creating an object file de novo. + +@var{segment_name} is only used with Mach-O as found on Darwin aka Mac +OS X. The parameter is required on that target. It means that all +sections are created within the named segment. It is ignored for +other object file formats. + +On error @code{simple_object_start_write} returns @code{NULL}, sets +@code{*@var{ERRMSG}} to an error message, and sets @code{*@var{err}} +to an errno value or @code{0} if there is no relevant errno. + +@end deftypefn + +@deftypefn Extension {simple_object_write_section *} simple_object_write_create_section (simple_object_write *@var{simple_object}, const char *@var{name}, unsigned int @var{align}, const char **@var{errmsg}, int *@var{err}) + +Add a section to @var{simple_object}. @var{name} is the name of the +new section. @var{align} is the required alignment expressed as the +number of required low-order 0 bits (e.g., 2 for alignment to a 32-bit +boundary). + +The section is created as containing data, readable, not writable, not +executable, not loaded at runtime. The section is not written to the +file until @code{simple_object_write_to_file} is called. + +On error this returns @code{NULL}, sets @code{*@var{errmsg}} to an +error message, and sets @code{*@var{err}} to an errno value or +@code{0} if there is no relevant errno. + +@end deftypefn + +@deftypefn Extension {const char *} simple_object_write_add_data (simple_object_write *@var{simple_object}, simple_object_write_section *@var{section}, const void *@var{buffer}, size_t @var{size}, int @var{copy}, int *@var{err}) + +Add data @var{buffer}/@var{size} to @var{section} in +@var{simple_object}. If @var{copy} is non-zero, the data will be +copied into memory if necessary. If @var{copy} is zero, @var{buffer} +must persist until @code{simple_object_write_to_file} is called. is +released. + +On success this returns @code{NULL}. On error this returns an error +message, and sets @code{*@var{err}} to an errno value or 0 if there is +no relevant erro. + +@end deftypefn + +@deftypefn Extension {const char *} simple_object_write_to_file (simple_object_write *@var{simple_object}, int @var{descriptor}, int *@var{err}) + +Write the complete object file to @var{descriptor}, an open file +descriptor. This writes out all the data accumulated by calls to +@code{simple_object_write_create_section} and +@var{simple_object_write_add_data}. + +This returns @code{NULL} on success. On error this returns an error +message and sets @code{*@var{err}} to an errno value or @code{0} if +there is no relevant errno. + +@end deftypefn + +@deftypefn Extension {void} simple_object_release_write (simple_object_write *@var{simple_object}) + +Release all resources associated with @var{simple_object}. + +@end deftypefn |