Back from Intel with Steve
This commit is contained in:
parent
4a81b56152
commit
6fd9467324
5 changed files with 1845 additions and 1 deletions
1
bfd/VERSION
Normal file
1
bfd/VERSION
Normal file
|
@ -0,0 +1 @@
|
|||
0.0
|
|
@ -20,7 +20,10 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
|
|||
|
||||
/* $Id$
|
||||
* $Log$
|
||||
* Revision 1.1 1991/03/21 21:11:23 gumby
|
||||
* Revision 1.1.1.1 1991/03/21 21:11:23 gumby
|
||||
* Back from Intel with Steve
|
||||
*
|
||||
* Revision 1.1 1991/03/21 21:11:23 gumby
|
||||
* Initial revision
|
||||
*
|
||||
* Revision 1.2 1991/03/15 18:16:52 rich
|
||||
|
|
705
bfd/bfd.doc
Executable file
705
bfd/bfd.doc
Executable file
|
@ -0,0 +1,705 @@
|
|||
This file contains -*- Text -*-.
|
||||
|
||||
BFD is a set of routines for reading and writing binary files.
|
||||
|
||||
The user should call only the interface routines at the end of bfd.h.
|
||||
The one I'm working out of is /4/gumby/bfd/bfd.h
|
||||
|
||||
Sample "strip" program using BFD:
|
||||
|
||||
#include "bfd.h"
|
||||
|
||||
doit ()
|
||||
{
|
||||
ibfd = bfd_openr(...)
|
||||
obfd = bfd_openw(...)
|
||||
bfd_check_format (ibfd, object);
|
||||
bfd_set_format (obfd, object);
|
||||
|
||||
bfd_set_arch_mach (obfd, ...)
|
||||
bfd_set_start_address (obfd, ...)
|
||||
etc...
|
||||
|
||||
[optionally:
|
||||
asymbol * foo = malloc (get_symtab_upper_bound (ibfd));
|
||||
bfd_canonicalize_symtab (ibfd, foo);
|
||||
<sort foo, frob foo, etc, using asymbol def from bfd.h>
|
||||
bfd_set_symtab (obfd, foo, updated_symbol_count);
|
||||
]
|
||||
|
||||
bfd_map_over_sections (abfd, setup, NULL);
|
||||
bfd_map_over_sections (abfd, cleaner, NULL);
|
||||
|
||||
bfd_close (obfd);
|
||||
bfd_close (ibfd);
|
||||
}
|
||||
|
||||
setup (ibfd, sect)
|
||||
{
|
||||
osect = make_section (obfd, bfd_section_name (ibfd, sect));
|
||||
bfd_set_section_size (obfd, osect, bfd_section_size (ibfd, sect));
|
||||
...
|
||||
}
|
||||
|
||||
cleaner (ibfd, sect)
|
||||
{
|
||||
osect = bfd_get_section_by_name (obfd,
|
||||
bfd_section_name (ibfd, sect));
|
||||
bfd_copy_section (ibfd, sect, obfd, osect);
|
||||
[perhaps: bfd_set_reloc (osect, NULL, 0); ]
|
||||
}
|
||||
|
||||
|
||||
|
||||
BFD is a package for manipulating binary files required for developing
|
||||
programs. It implements a group of structured operations designed to
|
||||
shield the programmer from the underlying representation of these
|
||||
binary files. It understands object (compiled) files, archive
|
||||
libraries, and core files. It is designed to work in a variety of
|
||||
target environments.
|
||||
|
||||
To use the library, include bfd.h and link with libbfd.a.
|
||||
|
||||
A bfd iteself is a representation for a particular file. It is opened
|
||||
in a manner similar to a file; code then manipulates it rather than
|
||||
the raw files.
|
||||
|
||||
BFD makes a distinction between TARGETS (families of file formats) and
|
||||
FORMATS (individual file formats). For instance, the "sun4os4" target
|
||||
can handle core, object and archive formats of files. The exact
|
||||
layout of the different formats depends on the target environment.
|
||||
|
||||
The target "default" means the first one known (usually used for
|
||||
environments that only support one format, or where the common format
|
||||
is known at compile or link time). The target NULL means the one
|
||||
specified at runtime in the environment variable GNUTARGET; if that is
|
||||
null or not defined then the first entry in the target list is chosen
|
||||
(on output), or all targets are searched (on input) to find a matching
|
||||
one..
|
||||
|
||||
Most programs should use the target NULL.
|
||||
|
||||
There is a way to get a list of the names of all the targets:
|
||||
char** bfd_target_list ()
|
||||
This function returns a freshly-malloced list of all the
|
||||
defined targets (or NULL if it could not malloc). The names
|
||||
are read-only. You could use this to prompt the user, or
|
||||
perhaps to error-check.
|
||||
|
||||
char * bfd_format_string (bfd_format format)
|
||||
This function will give you a printable, single-word description
|
||||
(like "core" or "archive") for a bfd format.
|
||||
|
||||
Error handling
|
||||
|
||||
General rules:
|
||||
funtions which are boolean return true on success and false on failure
|
||||
(unless they're a predicate). Functions which return pointers to
|
||||
objects return NULL on error. The specifics are documented with each
|
||||
function.
|
||||
|
||||
If a function fails, you should check the variable bfd_error. If the
|
||||
value is no_error, then check the C variable errno just as you would
|
||||
with any other program. The other values bfd_error may take on are
|
||||
documented in bfd.h.
|
||||
|
||||
If you would prefer a comprehensible string for the error message, use
|
||||
the function bfd_errmsg:
|
||||
char * bfd_errmsg (error_tag)
|
||||
This function returns a read-only string which documents the error
|
||||
code. If the error code is no_error then it will return a string
|
||||
depending on the value of errno.
|
||||
|
||||
bfd_perror() is like the perror() function except it understands
|
||||
bfd_error.
|
||||
|
||||
Operations on bfds themselves
|
||||
|
||||
bfd * bfd_openr (char *filename, char *target);
|
||||
bfd * bfd_fdopenr (int fd, char *target, char *filename);
|
||||
|
||||
Open a binary file for reading. TARGET is the type of the file,
|
||||
a char string like "sun4os4" or "elf". (Note this is not the
|
||||
"function" of the file, e.g. an object versus a core file
|
||||
versus an archive, but instead describes how all these files
|
||||
are encoded.) Returns a new bfd or NULL upon failure.
|
||||
|
||||
bfd * bfd_openw (char *filename, char *target);
|
||||
|
||||
Open a file named `filename' for writing. If an existing
|
||||
file has the same name, then it will be overwritten by a
|
||||
successful bfd_close on the returned bfd. Will return either
|
||||
a new bfd or NULL upon failure.
|
||||
|
||||
boolean bfd_close (bfd *abfd);
|
||||
|
||||
Close a BFD opened for either reading or writing. May involve
|
||||
several filesystem operations, depending on the data format;
|
||||
some things may not be known to the system until file-closing
|
||||
time. Returns true if it successfully wrote the file, false
|
||||
if not. A false return will not leave a partially-written
|
||||
file behind with the name supplied to bfd_openw.
|
||||
|
||||
On a bfd open for reading will generally successfully
|
||||
complete.
|
||||
|
||||
It is an error to call this on a file opened from inside an
|
||||
archive.
|
||||
|
||||
FIXME -- show which error codes may be recoverable and
|
||||
followed by another call to bfd_close!
|
||||
|
||||
|
||||
The defined formats are specified by the enumeration bfd_format.
|
||||
|
||||
boolean bfd_check_format (bfd *abfd, bfd_format format);
|
||||
|
||||
This routine must be called after a bfd_openr. It sets up
|
||||
internal data structures based on the contents of the file.
|
||||
It returns FALSE if the file is not really in the specified
|
||||
format.
|
||||
|
||||
boolean bfd_set_format (bfd *abfd, bfd_format format);
|
||||
|
||||
This routine must be called after a bfd_openw. It sets up
|
||||
internal data structures for the proper format of file.
|
||||
It returns FALSE if that format is not supported for output
|
||||
(e.g. core files).
|
||||
|
||||
The following macros may be used to obtain information about a bfd:
|
||||
|
||||
bfd_get_filename -- returns a pointer to a null-terminated string
|
||||
which names the bfd's file, or NULL if that is not known.
|
||||
Don't side-effect this string!
|
||||
bfd_get_format -- returns the format code for the bfd.
|
||||
bfd_get_target -- returns the string which names the bfd's target.
|
||||
bfd_get_mtime -- returns an time_t indicating the modification time of an
|
||||
input bfd, if that could be determined, or 0 of not.
|
||||
|
||||
Object files have certain properties. For input bfds, these
|
||||
properties may be read at any time. For output bfds you should set
|
||||
them before you begin building any sections.
|
||||
|
||||
bfd_vma bfd_get_start_address (bfd *abfd);
|
||||
|
||||
Returns the address in an object file where execution will begin.
|
||||
|
||||
boolean bfd_set_start_address (bfd *abfd, int vma);
|
||||
|
||||
Set the address where execution will start in an object file.
|
||||
|
||||
If the address you select is incorrect for your architecture
|
||||
(for instance, if it's required to be on a page_boundary and
|
||||
your supplied starting address is not, then you may get the
|
||||
invalid_operation error. It is not always possible to
|
||||
generate an error in this case.
|
||||
|
||||
An object file has an architecture, which is the general instruction
|
||||
set of the instructions that it contains. Architectures are defined in
|
||||
enum bfd_architecture in bfd.h. New architectures can be added by
|
||||
putting them in the enum, updating architectures.c, and adding code to
|
||||
handle them for the object files that know that architecture. The
|
||||
bfd_architecture values are not stored in files, but are only used
|
||||
within the BFD library and its callers.
|
||||
|
||||
An object file also has a machine type, which is the specific machine
|
||||
within the architecture. For example, if the architecture is bfd_arch_m68k,
|
||||
the Motorola 68000 series, then the machine type might be 68010, the mc68010
|
||||
chip. For architectures such as the SPARC where specific versions of
|
||||
the architecture exist, the version number should probably be used.
|
||||
|
||||
Particular object file formats may or may not store the machine architecture
|
||||
and type. When copying an object file, you should copy these fields.
|
||||
Most callers of BFD will not need to know the particular values that
|
||||
these fields contain, but will instead propagate them from file to file,
|
||||
or compare the architectures from two files.
|
||||
|
||||
enum bfd_architecture bfd_get_architecture (bfd *abfd);
|
||||
unsigned long bfd_get_machine (bfd *abfd);
|
||||
|
||||
Get the machine type and architecture.
|
||||
|
||||
boolean bfd_set_arch_mach (bfd *abfd, enum bfd_architecture arch,
|
||||
unsigned long machine);
|
||||
|
||||
Set the architecture and machine type. The result is true
|
||||
if the object file can exactly represent the specified type.
|
||||
The result is false otherwise.
|
||||
|
||||
boolean bfd_arch_compatible (bfd *abfd, bfd *bbfd,
|
||||
enum bfd_architecture *res_arch,
|
||||
unsigned long *res_machine);
|
||||
|
||||
Decides whether two BFD's contain compatible architectures and
|
||||
machine types. If the result is TRUE and the res_arch and
|
||||
res_machine pointers are non-NULL, the resulting "merged"
|
||||
architecture and machine type are returned through the pointers.
|
||||
A linker could call this to decide whether two object files
|
||||
can be linked, and to deterine the arch and machine type of
|
||||
the resulting file.
|
||||
|
||||
char * bfd_printable_arch_mach (enum bfd_architecture arch,
|
||||
unsigned long machine);
|
||||
|
||||
Returns a printable string that represents the particular
|
||||
combination of architecture and machine type.
|
||||
|
||||
boolean bfd_scan_arch_mach (char *string, enum bfd_architecture *archp,
|
||||
unsigned long *machinep);
|
||||
|
||||
Examines a printable string and tries to extract an
|
||||
architecture and machine type from it. The intended use is for
|
||||
parsing specifications from the user, e.g. command line
|
||||
arguments. The result is true if a known architecture was
|
||||
found, and the resulting architecture and machine type are
|
||||
stored through the argument pointers. Note that an
|
||||
architecture scannable by this function might not be
|
||||
representable by the particular object file format in use.
|
||||
(i.e. bfd_set_arch_mach might return false).
|
||||
|
||||
|
||||
There are also a number of boolean flags which apply to object bfds.
|
||||
|
||||
flagword bfd_get_file_flags (bfd *abfd);
|
||||
|
||||
returns a flagword containing the bfd's flags.
|
||||
|
||||
boolean bfd_set_file_flags (bfd *abfd, flagword flags,
|
||||
boolean on_or_off);
|
||||
|
||||
sets (on_or_off == true) or clears (on_or_off == false) the flags
|
||||
specified by flagword. All other flags are unaffected.
|
||||
Some flag combinations don't make sense; It is not always
|
||||
possible to detect them (since they may depend on other information).
|
||||
Returns true if the flags could be modified as requested,
|
||||
false if not. Upon a false return, no flags will have been
|
||||
altered.
|
||||
|
||||
|
||||
flagword bfd_applicable_file_flags (bfd *abfd);
|
||||
|
||||
returns a flagword with bits set for all the flags which are
|
||||
meaningful for the bfd.
|
||||
|
||||
The flags are:
|
||||
HAS_RELOC -- file contains unresolved relocation information.
|
||||
EXEC_P -- file can be executed. These two may both be on in the
|
||||
case of some dynamically-linked binaries.
|
||||
HAS_LINENO -- has line number information.
|
||||
HAS_DEBUG -- has debugging information.
|
||||
HAS_SYMS -- has any symbols.
|
||||
HAS_LOCALS -- has local symbols.
|
||||
DYNAMIC -- binary is dynamically linked.
|
||||
WP_TEXT -- text is write-protected
|
||||
D_PAGED -- binary should be demand-paged
|
||||
|
||||
These flags are one bit wide and may be OR-ed together with |.
|
||||
|
||||
If you are building a large application with bfd there may be data
|
||||
specific to your program that you may wish to associate with a bfd.
|
||||
Rather than require you to build a parallel table structure, bfd
|
||||
provides a void* pointer in each bfd for arbitrary user data. The
|
||||
macro bfd_usrdata (bfd *abfd) extracts these data; you may set them
|
||||
with = (ie bfd_usrdata (my_bfd) = frob_it (my_bfd, moon_phase);).
|
||||
|
||||
Object and core files have sections.
|
||||
|
||||
File sections are represented by opaque pointers. You may map over
|
||||
the sections of a file or you may ask for one by name. Note that not
|
||||
all files may have all the possible sections.
|
||||
|
||||
Section pointers are valid from the time you get them until the bfd
|
||||
to which they refer is closed.
|
||||
|
||||
When doing output, you must set up all the file's sections before
|
||||
outputting to any. All that means is that all the file's sections
|
||||
must have already been created and their size set before output
|
||||
commences.
|
||||
|
||||
Each section contains some small information, plus three chunks of
|
||||
data in the object file: contents, relocation, and line numbers.
|
||||
In some file formats (e.g. a.out), the line number part is always
|
||||
empty, and line number information (if any) is instead recorded in
|
||||
the symbol table.
|
||||
|
||||
sec_ptr bfd_get_section_by_name (bfd *abfd, char *name);
|
||||
Returns a section named NAME, or NULL if none by that name
|
||||
exists. Works on input and output bfds.
|
||||
|
||||
sec_ptr bfd_make_section (bfd *abfd, char *name);
|
||||
Creates a section named name in the output bfd abfd.
|
||||
returns NULL if it cannot create the section (if, for instance,
|
||||
the output format does not permit such a section). If a
|
||||
section with that name already exists, it is returned; a new
|
||||
one with the same name is NOT created.
|
||||
|
||||
unsigned int bfd_count_sections (bfd *abfd)
|
||||
|
||||
This function returns the number of sections in the bfd abfd.
|
||||
|
||||
void bfd_map_over_sections (bfd *abfd, void (*operation)(),
|
||||
void *user_storage);
|
||||
|
||||
This is how you operate on all sections of an input file.
|
||||
Pass in a function pointer. The function will be called for each
|
||||
section of the file, in random order. It will be passed
|
||||
three arguments: the bfd, the sec_ptr for the section, and
|
||||
whatever was passed in as user_storage.
|
||||
|
||||
char * bfd_section_name (bfd *abfd, sec_ptr ptr);
|
||||
|
||||
Produces the name of a section, e.g. ".text" or ".data".
|
||||
This will produce arbitrary names for files with extensible
|
||||
section names (e.g. COFF, ELF) so don't assume that you will
|
||||
only see a few values here.
|
||||
|
||||
long bfd_section_size (bfd *abfd, sec_ptr ptr);
|
||||
|
||||
The size of a section in bytes. Result == -1 for error.
|
||||
|
||||
boolean bfd_set_section_size (bfd *abfd, sec_ptr section unsigned long size);
|
||||
|
||||
Set the size of a section. This must be done before any data
|
||||
transfer is done for the section.
|
||||
|
||||
bfd_vma bfd_section_vma (bfd *abfd, sec_ptr ptr);
|
||||
|
||||
Virtual memory address where a section "belongs".
|
||||
|
||||
boolean bfd_set_section_vma (bfd *abfd, bfd_vma vma);
|
||||
|
||||
Set the virtual memory address of a section.
|
||||
|
||||
int bfd_get_section_alignment (bfd *abfd, sec_ptr ptr);
|
||||
|
||||
returns the alignment of a section. If alignment is not
|
||||
possible, return value is undefined.
|
||||
|
||||
boolean bfd_set_section_alignment (bfd *abfd, sec_ptr ptr, int alignment)
|
||||
|
||||
returns true if it can set the section to the requested value.
|
||||
Alignment is an integer; it refers to the power of two
|
||||
specifying the byte boundary we want (ie 0 is byte-aligned; 4
|
||||
is word aligned). If the requested alignment is not available
|
||||
any existing value is unchanged.
|
||||
|
||||
Sections have properties just as object files may:
|
||||
|
||||
flagword bfd_get_section_flags (bfd *abfd, sec_ptr section);
|
||||
|
||||
returns a flagword containing the section's flags.
|
||||
|
||||
boolean bfd_set_section_flags (bfd *abfd, sec_ptr section,
|
||||
flagword flags, boolean on_or_off);
|
||||
|
||||
sets (on_or_off == true) or clears (on_or_off == false) the flags
|
||||
specified by flagword. All other flags are unaffected.
|
||||
Some flag combinations don't make sense; It is not always
|
||||
possible to detect them (since they may depend on other information).
|
||||
Returns true if the flags could me modified as requested,
|
||||
false if not. Unpon a false return, no flags will have been
|
||||
altered.
|
||||
|
||||
flagword bfd_applicable_section_flags (bfd *abfd);
|
||||
|
||||
returns a flagword with bits set for all the flags which are
|
||||
meaningful for a section.
|
||||
|
||||
The flags are:
|
||||
|
||||
SEC_BALIGN -- segment can be byte-aligned.
|
||||
SEC_RELOC -- segment should be relocated.
|
||||
SEC_ALLOC -- when converted into a memory image with the intent of
|
||||
constructing a runable process, memory space will be
|
||||
allocated for this section.
|
||||
SEC_LOAD -- when converted into a memory image with the intent of
|
||||
constructing a runable process, section contents will be
|
||||
copied from the object file into memory. When this flag
|
||||
is set, SEC_ALLOC is guaranteed to also be set.
|
||||
SEC_HAS_CONTENTS -- The contents of this section exist in the
|
||||
object file. Sections whose contents do not exist in the
|
||||
object file may still have their contents read. On read,
|
||||
a segment filled with zeroes will be invented to satisfy
|
||||
the read request. It is an error to attempt to set the
|
||||
contents of a section that has no contents.
|
||||
|
||||
These last three probably need some explanation. In a traditional,
|
||||
native unix object format, there are three real sections, text, data,
|
||||
and bss. The text section will be allocated memory on exec, and will
|
||||
be loaded from file into memory on exec. So the flags for a
|
||||
traditional unix text section would typically be at least (SEC_ALLOC |
|
||||
SEC_LOAD | SEC_HAS_CONTENTS). The data section has basically these
|
||||
same traits. The bss section, however is a little different. It is
|
||||
not relocated, and it is not loaded from file on exec, but it is
|
||||
allocated memory on exec. Thus, its flags would be more like
|
||||
(SEC_ALLOC). It is possible to have a section which is the converse
|
||||
of the bss section. That is, (SEC_HAS_CONTENTS & ~SEC_ALLOC). This
|
||||
could be anything from profiling information or notes from one pass of
|
||||
a toolchain to another to time and version stamp information.
|
||||
|
||||
Note that the section flags currently lack information on position
|
||||
dependance.
|
||||
|
||||
boolean bfd_get_section_contents (bfd *abfd, sec_ptr section,
|
||||
unsigned char *location,
|
||||
int offset, int count);
|
||||
|
||||
Stores count bytes from the section's contents starting at
|
||||
offset from within those contents. The values are stored into
|
||||
location. Returns true if it could do so. Supplying invalid
|
||||
values for offset and count will produce unpredictable results.
|
||||
|
||||
boolean bfd_set_section_contents (bfd *abfd, sec_ptr section,
|
||||
unsigned char *location,
|
||||
int offset, int count);
|
||||
Stores count bytes from location into offset within the
|
||||
section contents. You need not write all the contents contiguously
|
||||
(that is, you may write words 5-7 followed by 0-4 if you
|
||||
wish). However once you start writing into a section, any
|
||||
other sections into which you have previously written are
|
||||
considered finished, and you may not write in them any more.
|
||||
|
||||
*** Line numbers ***
|
||||
|
||||
bfd_get_section_lineno_size (bfd *abfd, sec_ptr section);
|
||||
Returns how many bytes of line numbers are associated with this
|
||||
section.
|
||||
|
||||
bfd_set_section_lineno_size (bfd *abfd, sec_ptr section, unsigned long val);
|
||||
Sets the number of bytes of line numbers that this section should
|
||||
contain.
|
||||
|
||||
boolean bfd_get_section_linenos (bfd *abfd, sec_ptr section,
|
||||
unsigned char *location,
|
||||
int offset, int count);
|
||||
Same as get_section_contents, except that it works on the linenos
|
||||
for this section.
|
||||
|
||||
boolean bfd_set_section_linenos (bfd *abfd, sec_ptr section,
|
||||
unsigned char *location,
|
||||
int offset, int count);
|
||||
Same as set_section_contents, except that it works on the linenos
|
||||
for this section.
|
||||
|
||||
As with files, you may associate arbitrary program-specific data with
|
||||
a section of a bfd. The following two functions are provided for
|
||||
manipulating these data:
|
||||
|
||||
void * bfd_get_section_userdata (bfd *abfd, sec_ptr section)
|
||||
Returns whatever was stored in section's user data, or NULL if nothing.
|
||||
|
||||
boolean bfd_set_section_userdata (bfd *abfd, sec_ptr section, void *contents)
|
||||
Set the section contents. Returns true if it can, false if not.
|
||||
|
||||
Core files
|
||||
|
||||
Core files are currently only supported for reading.
|
||||
|
||||
Apart from opening them, looking at the various sections (generally
|
||||
the .data, .stack, and .regs sections; maybe a .user_struct section
|
||||
eventually), you can make some queries about the status of the core
|
||||
file, detailed below. The ".regs" section contains the general and
|
||||
floating point registers of the process that died, in some machine-
|
||||
specific order and format "intended to be unsurprising to someone who
|
||||
knows the machine".
|
||||
|
||||
char * bfd_core_file_failing_command (bfd *abfd);
|
||||
|
||||
The command name of the program that failed, creating the core file.
|
||||
The result is NULL if BFD can't figure out what the failing command was.
|
||||
|
||||
int bfd_core_file_failing_signal (bfd *abfd);
|
||||
|
||||
The signal number which caused the program to die, causing the
|
||||
core file to be created. It will be positive if valid.
|
||||
|
||||
boolean core_file_matches_executable_p (bfd *core_bfd, bfd *exec_bfd);
|
||||
|
||||
For debuggers, checks whether a core file "matches" (is likely to
|
||||
have come from) an executable file. This will not be perfect on
|
||||
most systems, but will just provide a way to reject gross mismatches.
|
||||
|
||||
Archives.
|
||||
|
||||
An archive is a special file which can contain other files.
|
||||
Originally it was intended to be a general way to group files, the way
|
||||
tar is today. But now it is used almost exclusively to hold object
|
||||
files.
|
||||
|
||||
An archive may be opened for reading or writing just like any other
|
||||
bfd. Once it is open for reading you may obtain bfds for each of the
|
||||
files contained within it with the following function:
|
||||
|
||||
bfd * bfd_openr_next_archived_file (bfd *arch_bfd, bfd *last_file);
|
||||
|
||||
If called with NULL as the second argument, returns the first
|
||||
file contained in the archive arch_bfd. If called with a file
|
||||
contained within arch_bfd, returns the one which follows that
|
||||
one, or NULL if it was the last. Returns NULL also if the
|
||||
bfd supplied as last_file did not come from the archive arch_bfd.
|
||||
|
||||
Any bfd open for read may be placed in an output archive. When the
|
||||
output archive is closed, the contents will be placed into the
|
||||
archive.
|
||||
|
||||
You control the order of files in an archive. You set the first one
|
||||
with the following function:
|
||||
|
||||
boolean bfd_set_archive_head (bfd *output_archive, bfd *new_head)
|
||||
|
||||
This function sets the first file in the archive
|
||||
output_archive to be the bfd new_head.
|
||||
|
||||
bfd's contain a pointer called next, which is bfd *. It is used by
|
||||
bfd_close when an archive is closed to decide which file should next
|
||||
go into the archive. So to place a group of files into an archive,
|
||||
open bfds for each of them, chain them together using the next pointer
|
||||
in the order you desire (be sure to store NULL into the final one's
|
||||
next pointer), then do bfd_set_archive_head with the head of the
|
||||
chain. The next pointer may be freely smashed at any time; it is only
|
||||
looked at when closing an output archive.
|
||||
|
||||
bfds for files contained within archives are normal bfds; you can do
|
||||
any input operations on them that you can do with a normal bfd.
|
||||
|
||||
bfd_my_archive is a macro which takes an input bfd and returns NULL if
|
||||
it lives in the filesystem and a bfd if it is contained in an archive.
|
||||
In the latter case, the returned bfd is the archive itself.
|
||||
|
||||
Archives containing only object files may have a "map" -- a table in
|
||||
the front which maps external symbols to the files which contain them.
|
||||
|
||||
Archive maps will refer only to object files; if an archive contains a
|
||||
file which is not an archive that file will of course not appear in
|
||||
the map.
|
||||
|
||||
boolean bfd_has_map (bfd *archive_bfd)
|
||||
|
||||
This macro takes a bfd of an archive and returns true or
|
||||
false depending on whether the bfd has a map. For output
|
||||
bfds this may be set to true or false, depending on whether
|
||||
you want the map to be maintained or not. For some targets,
|
||||
setting this to false will cause no map to be generated; for
|
||||
others it will merely cause an empty map to be created, since
|
||||
a map is required by that target.
|
||||
|
||||
For archives with maps you may use the following function:
|
||||
|
||||
int bfd_get_next_mapent (bfd *abfd, int prev, char **name)
|
||||
|
||||
You may use this to step through all the entries in the archive
|
||||
map. Supply BFD_NO_MORE_SYMBOLS as the 'prev' entry to get the
|
||||
first entry; then use successive returned values from this
|
||||
function to get the succeeding ones. The name of the next entry
|
||||
will be stored through the pointer name.
|
||||
|
||||
This function returns BFD_NO_MORE_SYMBOLS when there are no more
|
||||
entries or on error.
|
||||
|
||||
bfd * bfd_get_elt_at_index (abfd, int index)
|
||||
|
||||
This function takes an index as returned by bfd_get_next_mapent
|
||||
and returns the bfd which corresponds to that entry. Returns NULL
|
||||
on error.
|
||||
|
||||
Symbol and relocation information.
|
||||
|
||||
Symbol-table information is the area of greatest incompatibility.
|
||||
bfd has a canonical symbol representation; all formats are parsed into
|
||||
and out of it.
|
||||
|
||||
Note that canonicalize_symtab takes a pointer to an array of pointers
|
||||
to canonical symbols. This is necessary so that the end of the array
|
||||
can be marked with NULL. You may shuffle the pointers and you may
|
||||
clobber the symbol contents. But don't move the symbols themselves.
|
||||
|
||||
unsigned int bfd_get_symtab_upper_bound (bfd *abfd);
|
||||
|
||||
Returns the maximum number of bytes that would be taken by
|
||||
the output of canonicalize_symtab. Returns 0 on error.
|
||||
|
||||
unsigned int bfd_canonicalize_symtab (bfd *abfd, asymbol **location);
|
||||
|
||||
Produces a symbol table in canonical format at LOCATION, which
|
||||
must be of size specified by get_symtab_upper_bound bytes.
|
||||
Not all those bytes may be used. Returns the number of
|
||||
symbol pointers written. Returns 0 upon error.
|
||||
|
||||
boolean bfd_set_symtab (bfd *outbfd, asymbol **location,
|
||||
unsigned int symcount);
|
||||
|
||||
Takes a generic symbol table and an output bfd. Used to set
|
||||
the symbol table for an output bfd. Do not change the table
|
||||
after using this function (although the storage may be
|
||||
reclaimed once the bfd has been closed).
|
||||
|
||||
If you're done with the symol table you can tell bfd about it by
|
||||
calling bfd_reclaim_symbol_table, which takes a bfd. Calling this
|
||||
function will also reclaim any relocation entries you may have
|
||||
requested. If you don't use this function bfd will keep around all
|
||||
symbol information until the bfd is closed.
|
||||
|
||||
Similarly, relocations have a canonical format. See the file bfd.h for
|
||||
the exact definition. It is similar to the sun-4 relocation format.
|
||||
Please note that:
|
||||
o - Each relocation has a pointer to a generic symbol.
|
||||
o - Not all values of reloc_type are supported for all targets. There
|
||||
is a bitvector which explains which are; you can index into it by
|
||||
relocation type. The macro which extracts it is bfd_valid_reloc_types.
|
||||
|
||||
Since relocation information is saved on a per-section basis, the
|
||||
interface is slightly different from that of the symbol table:
|
||||
|
||||
unsigned int get_reloc_upper_bound (bfd *abfd, sec_ptr asect);
|
||||
|
||||
Returns the maximum number of bytes that would be taken by
|
||||
the output of canonicalize_reloc. Returns 0 on error.
|
||||
|
||||
unsigned int canonicalize_reloc (bfd *abfd, sec_ptr asect, arelent *location);
|
||||
|
||||
Produces a relocation table in canonical format at LOCATION,
|
||||
which must be of size specified by get_reloc_upper_bound
|
||||
bytes. Not all those bytes may be used. Returns the number
|
||||
of entries written. Returns 0 upon error.
|
||||
|
||||
boolean bfd_set_reloc (bfd *outbfd, sec_ptr asect, arelent *location,
|
||||
unsigned int count);
|
||||
|
||||
Takes a generic reloc table and an output bfd. Used to set
|
||||
the reloc table for an output bfd. Do not change the table
|
||||
after using this function (although the storage may be
|
||||
reclaimed once the bfd has been closed).
|
||||
|
||||
Byte-swapping
|
||||
|
||||
Unfortunately, not all machines have the same byte order. Worse,
|
||||
storage layout is in general highly machine-dependent. Although bfd
|
||||
can hide that from you in most cases, it cannot do so with the section
|
||||
contents, since they are totally uninterpreted. Hence you must
|
||||
byte-swap those data yourself. This is not usually much of an issue
|
||||
since you should just generate your data in the correct byte order.
|
||||
|
||||
[THIS IS WRONG AND ALSO DOES NOT REFLECT THE CODE WHICH IS CORRECT]
|
||||
|
||||
Fortunately, bfd can tell if byte-swapping or realignment is required
|
||||
at all! The macro bfd_bit_twiddle_required takes a pointer to a bfd
|
||||
and returns true if byte-swapping is required, false if not.
|
||||
|
||||
However if you don't wish to check this you may just use the following
|
||||
functions which will do the conversions required:
|
||||
|
||||
|
||||
long bfd_getlong (bfd *abfd, unsigned char *ptr);
|
||||
bfd_putlong (bfd *abfd, unsigned char *ptr, long time);
|
||||
|
||||
short bfd_getshort (bfd *abfd, unsigned char *ptr);
|
||||
bfd_putshort (bfd *abfd, unsigned char *ptr, short stop);
|
||||
|
||||
These functions take a pointer that points to data which is,
|
||||
or will be, part of a section contents. They extract numbers
|
||||
from the data, or insert numbers into the data. The argument
|
||||
or result is in the host's number format; the data stored at
|
||||
the pointer or retrieved from it is in the target's number format.
|
||||
Typically this transfer is either a no-op or is a byte-swap;
|
||||
sometimes it involves an access to a "misaligned" location from
|
||||
the host's point of view..
|
942
bfd/cplus-dem.c
Executable file
942
bfd/cplus-dem.c
Executable file
|
@ -0,0 +1,942 @@
|
|||
/* Demangler for GNU C++
|
||||
Copyright (C) 1989 Free Software Foundation, Inc.
|
||||
written by James Clark (jjc@jclark.uucp)
|
||||
|
||||
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 1, 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, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
|
||||
|
||||
/* This is for g++ 1.36.1 (November 6 version). It will probably
|
||||
require changes for any other version.
|
||||
|
||||
Modified for g++ 1.36.2 (November 18 version). */
|
||||
|
||||
/* This file exports one function
|
||||
|
||||
char *cplus_demangle (const char *name)
|
||||
|
||||
If `name' is a mangled function name produced by g++, then
|
||||
a pointer to a malloced string giving a C++ representation
|
||||
of the name will be returned; otherwise NULL will be returned.
|
||||
It is the caller's responsibility to free the string which
|
||||
is returned.
|
||||
|
||||
For example,
|
||||
|
||||
cplus_demangle ("_foo__1Ai")
|
||||
|
||||
returns
|
||||
|
||||
"A::foo(int)"
|
||||
|
||||
This file imports xmalloc and xrealloc, which are like malloc and
|
||||
realloc except that they generate a fatal error if there is no
|
||||
available memory. */
|
||||
|
||||
/* #define nounderscore 1 /* define this is names don't start with _ */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
/* #include "misc.h" */
|
||||
|
||||
#ifdef USG
|
||||
#include <memory.h>
|
||||
#else
|
||||
#define memcpy(s1, s2, n) strncpy(s1, s2, n)
|
||||
#define memcmp(s1, s2, n) strncmp(s1, s2, n)
|
||||
#define strchr(s, c) index(s, c)
|
||||
#endif
|
||||
|
||||
#ifndef __STDC__
|
||||
#define const
|
||||
#endif
|
||||
|
||||
#ifdef __STDC__
|
||||
extern char *cplus_demangle (const char *type);
|
||||
#else
|
||||
extern char *cplus_demangle ();
|
||||
#endif
|
||||
|
||||
static char **typevec = 0;
|
||||
static int ntypes = 0;
|
||||
static int typevec_size = 0;
|
||||
|
||||
static struct {
|
||||
const char *in;
|
||||
const char *out;
|
||||
} optable[] = {
|
||||
"new", " new",
|
||||
"delete", " delete",
|
||||
"ne", "!=",
|
||||
"eq", "==",
|
||||
"ge", ">=",
|
||||
"gt", ">",
|
||||
"le", "<=",
|
||||
"lt", "<",
|
||||
"plus", "+",
|
||||
"minus", "-",
|
||||
"mult", "*",
|
||||
"convert", "+", /* unary + */
|
||||
"negate", "-", /* unary - */
|
||||
"trunc_mod", "%",
|
||||
"trunc_div", "/",
|
||||
"truth_andif", "&&",
|
||||
"truth_orif", "||",
|
||||
"truth_not", "!",
|
||||
"postincrement", "++",
|
||||
"postdecrement", "--",
|
||||
"bit_ior", "|",
|
||||
"bit_xor", "^",
|
||||
"bit_and", "&",
|
||||
"bit_not", "~",
|
||||
"call", "()",
|
||||
"cond", "?:",
|
||||
"alshift", "<<",
|
||||
"arshift", ">>",
|
||||
"component", "->",
|
||||
"indirect", "*",
|
||||
"method_call", "->()",
|
||||
"addr", "&", /* unary & */
|
||||
"array", "[]",
|
||||
"nop", "", /* for operator= */
|
||||
};
|
||||
|
||||
/* Beware: these aren't '\0' terminated. */
|
||||
|
||||
typedef struct {
|
||||
char *b; /* pointer to start of string */
|
||||
char *p; /* pointer after last character */
|
||||
char *e; /* pointer after end of allocated space */
|
||||
} string;
|
||||
|
||||
#ifdef __STDC__
|
||||
static void string_need (string *s, int n);
|
||||
static void string_delete (string *s);
|
||||
static void string_init (string *s);
|
||||
static void string_clear (string *s);
|
||||
static int string_empty (string *s);
|
||||
static void string_append (string *p, const char *s);
|
||||
static void string_appends (string *p, string *s);
|
||||
static void string_appendn (string *p, const char *s, int n);
|
||||
static void string_prepend (string *p, const char *s);
|
||||
#if 0
|
||||
static void string_prepends (string *p, string *s);
|
||||
#endif
|
||||
static void string_prependn (string *p, const char *s, int n);
|
||||
static int get_count (const char **type, int *count);
|
||||
static int do_args (const char **type, string *decl);
|
||||
static int do_type (const char **type, string *result);
|
||||
static int do_arg (const char **type, string *result);
|
||||
static int do_args (const char **type, string *decl);
|
||||
static void munge_function_name (string *name);
|
||||
#else
|
||||
static void string_need ();
|
||||
static void string_delete ();
|
||||
static void string_init ();
|
||||
static void string_clear ();
|
||||
static int string_empty ();
|
||||
static void string_append ();
|
||||
static void string_appends ();
|
||||
static void string_appendn ();
|
||||
static void string_prepend ();
|
||||
static void string_prepends ();
|
||||
static void string_prependn ();
|
||||
static int get_count ();
|
||||
static int do_args ();
|
||||
static int do_type ();
|
||||
static int do_arg ();
|
||||
static int do_args ();
|
||||
static void munge_function_name ();
|
||||
#endif
|
||||
|
||||
char *
|
||||
cplus_demangle (type)
|
||||
const char *type;
|
||||
{
|
||||
string decl;
|
||||
int n;
|
||||
int success = 0;
|
||||
int constructor = 0;
|
||||
int const_flag = 0;
|
||||
int i;
|
||||
const char *p, *premangle;
|
||||
|
||||
if (type == NULL || *type == '\0')
|
||||
return NULL;
|
||||
#ifndef nounderscore
|
||||
if (*type++ != '_')
|
||||
return NULL;
|
||||
#endif
|
||||
p = type;
|
||||
while (*p != '\0' && !(*p == '_' && p[1] == '_'))
|
||||
p++;
|
||||
if (*p == '\0')
|
||||
{
|
||||
/* destructor */
|
||||
if (type[0] == '_' && type[1] == '$' && type[2] == '_')
|
||||
{
|
||||
unsigned int l = (strlen (type) - 3)*2 + 3 + 2 + 1;
|
||||
char *tem = (char *) zalloc (l);
|
||||
strcpy (tem, type + 3);
|
||||
strcat (tem, "::~");
|
||||
strcat (tem, type + 3);
|
||||
strcat (tem, "()");
|
||||
return tem;
|
||||
}
|
||||
/* static data member */
|
||||
if (*type != '_' && (p = (char *) strchr (type, '$')) != NULL)
|
||||
{
|
||||
int n = strlen (type) + 2;
|
||||
char *tem = (char *) xmalloc (n);
|
||||
memcpy (tem, type, p - type);
|
||||
strcpy (tem + (p - type), "::");
|
||||
strcpy (tem + (p - type) + 2, p + 1);
|
||||
return tem;
|
||||
}
|
||||
/* virtual table */
|
||||
if (type[0] == '_' && type[1] == 'v' && type[2] == 't' && type[3] == '$')
|
||||
{
|
||||
int n = strlen (type + 4) + 14 + 1;
|
||||
char *tem = (char *) xmalloc (n);
|
||||
strcpy (tem, type + 4);
|
||||
strcat (tem, " virtual table");
|
||||
return tem;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
string_init (&decl);
|
||||
|
||||
if (p == type)
|
||||
{
|
||||
if (!isdigit (p[2]))
|
||||
{
|
||||
string_delete (&decl);
|
||||
return NULL;
|
||||
}
|
||||
constructor = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
string_appendn (&decl, type, p - type);
|
||||
munge_function_name (&decl);
|
||||
}
|
||||
p += 2;
|
||||
|
||||
premangle = p;
|
||||
switch (*p)
|
||||
{
|
||||
case 'C':
|
||||
/* a const member function */
|
||||
if (!isdigit (p[1]))
|
||||
{
|
||||
string_delete (&decl);
|
||||
return NULL;
|
||||
}
|
||||
p += 1;
|
||||
const_flag = 1;
|
||||
/* fall through */
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9':
|
||||
n = 0;
|
||||
do
|
||||
{
|
||||
n *= 10;
|
||||
n += *p - '0';
|
||||
p += 1;
|
||||
}
|
||||
while (isdigit (*p));
|
||||
if (strlen (p) < n)
|
||||
{
|
||||
string_delete (&decl);
|
||||
return NULL;
|
||||
}
|
||||
if (constructor)
|
||||
{
|
||||
string_appendn (&decl, p, n);
|
||||
string_append (&decl, "::");
|
||||
string_appendn (&decl, p, n);
|
||||
}
|
||||
else
|
||||
{
|
||||
string_prepend (&decl, "::");
|
||||
string_prependn (&decl, p, n);
|
||||
}
|
||||
#ifndef LONGERNAMES
|
||||
p = premangle;
|
||||
#else
|
||||
p += n;
|
||||
#endif
|
||||
success = do_args (&p, &decl);
|
||||
if (const_flag)
|
||||
string_append (&decl, " const");
|
||||
break;
|
||||
case 'F':
|
||||
p += 1;
|
||||
success = do_args (&p, &decl);
|
||||
break;
|
||||
}
|
||||
|
||||
for (i = 0; i < ntypes; i++)
|
||||
if (typevec[i] != NULL)
|
||||
free (typevec[i]);
|
||||
ntypes = 0;
|
||||
if (typevec != NULL)
|
||||
{
|
||||
free ((char *)typevec);
|
||||
typevec = NULL;
|
||||
typevec_size = 0;
|
||||
}
|
||||
|
||||
if (success)
|
||||
{
|
||||
string_appendn (&decl, "", 1);
|
||||
return decl.b;
|
||||
}
|
||||
else
|
||||
{
|
||||
string_delete (&decl);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
get_count (type, count)
|
||||
const char **type;
|
||||
int *count;
|
||||
{
|
||||
if (!isdigit (**type))
|
||||
return 0;
|
||||
*count = **type - '0';
|
||||
*type += 1;
|
||||
/* see flush_repeats in cplus-method.c */
|
||||
if (isdigit (**type))
|
||||
{
|
||||
const char *p = *type;
|
||||
int n = *count;
|
||||
do
|
||||
{
|
||||
n *= 10;
|
||||
n += *p - '0';
|
||||
p += 1;
|
||||
}
|
||||
while (isdigit (*p));
|
||||
if (*p == '_')
|
||||
{
|
||||
*type = p + 1;
|
||||
*count = n;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* result will be initialised here; it will be freed on failure */
|
||||
|
||||
static int
|
||||
do_type (type, result)
|
||||
const char **type;
|
||||
string *result;
|
||||
{
|
||||
int n;
|
||||
int done;
|
||||
int non_empty = 0;
|
||||
int success;
|
||||
string decl;
|
||||
const char *remembered_type;
|
||||
|
||||
string_init (&decl);
|
||||
string_init (result);
|
||||
|
||||
done = 0;
|
||||
success = 1;
|
||||
while (success && !done)
|
||||
{
|
||||
int member;
|
||||
switch (**type)
|
||||
{
|
||||
case 'P':
|
||||
*type += 1;
|
||||
string_prepend (&decl, "*");
|
||||
break;
|
||||
|
||||
case 'R':
|
||||
*type += 1;
|
||||
string_prepend (&decl, "&");
|
||||
break;
|
||||
|
||||
case 'T':
|
||||
*type += 1;
|
||||
if (!get_count (type, &n) || n >= ntypes)
|
||||
success = 0;
|
||||
else
|
||||
{
|
||||
remembered_type = typevec[n];
|
||||
type = &remembered_type;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'F':
|
||||
*type += 1;
|
||||
if (!string_empty (&decl) && decl.b[0] == '*')
|
||||
{
|
||||
string_prepend (&decl, "(");
|
||||
string_append (&decl, ")");
|
||||
}
|
||||
if (!do_args (type, &decl) || **type != '_')
|
||||
success = 0;
|
||||
else
|
||||
*type += 1;
|
||||
break;
|
||||
|
||||
case 'M':
|
||||
case 'O':
|
||||
{
|
||||
int constp = 0;
|
||||
int volatilep = 0;
|
||||
|
||||
member = **type == 'M';
|
||||
*type += 1;
|
||||
if (!isdigit (**type))
|
||||
{
|
||||
success = 0;
|
||||
break;
|
||||
}
|
||||
n = 0;
|
||||
do
|
||||
{
|
||||
n *= 10;
|
||||
n += **type - '0';
|
||||
*type += 1;
|
||||
}
|
||||
while (isdigit (**type));
|
||||
if (strlen (*type) < n)
|
||||
{
|
||||
success = 0;
|
||||
break;
|
||||
}
|
||||
string_append (&decl, ")");
|
||||
string_prepend (&decl, "::");
|
||||
string_prependn (&decl, *type, n);
|
||||
string_prepend (&decl, "(");
|
||||
*type += n;
|
||||
if (member)
|
||||
{
|
||||
if (**type == 'C')
|
||||
{
|
||||
*type += 1;
|
||||
constp = 1;
|
||||
}
|
||||
if (**type == 'V')
|
||||
{
|
||||
*type += 1;
|
||||
volatilep = 1;
|
||||
}
|
||||
if (*(*type)++ != 'F')
|
||||
{
|
||||
success = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ((member && !do_args (type, &decl)) || **type != '_')
|
||||
{
|
||||
success = 0;
|
||||
break;
|
||||
}
|
||||
*type += 1;
|
||||
if (constp)
|
||||
{
|
||||
if (non_empty)
|
||||
string_append (&decl, " ");
|
||||
else
|
||||
non_empty = 1;
|
||||
string_append (&decl, "const");
|
||||
}
|
||||
if (volatilep)
|
||||
{
|
||||
if (non_empty)
|
||||
string_append (&decl, " ");
|
||||
else
|
||||
non_empty = 1;
|
||||
string_append (&decl, "volatilep");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 'C':
|
||||
if ((*type)[1] == 'P')
|
||||
{
|
||||
*type += 1;
|
||||
if (!string_empty (&decl))
|
||||
string_prepend (&decl, " ");
|
||||
string_prepend (&decl, "const");
|
||||
break;
|
||||
}
|
||||
|
||||
/* fall through */
|
||||
default:
|
||||
done = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
done = 0;
|
||||
non_empty = 0;
|
||||
while (success && !done)
|
||||
{
|
||||
switch (**type)
|
||||
{
|
||||
case 'C':
|
||||
*type += 1;
|
||||
if (non_empty)
|
||||
string_append (result, " ");
|
||||
else
|
||||
non_empty = 1;
|
||||
string_append (result, "const");
|
||||
break;
|
||||
case 'U':
|
||||
*type += 1;
|
||||
if (non_empty)
|
||||
string_append (result, " ");
|
||||
else
|
||||
non_empty = 1;
|
||||
string_append (result, "unsigned");
|
||||
break;
|
||||
case 'V':
|
||||
*type += 1;
|
||||
if (non_empty)
|
||||
string_append (result, " ");
|
||||
else
|
||||
non_empty = 1;
|
||||
string_append (result, "volatile");
|
||||
break;
|
||||
default:
|
||||
done = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (success)
|
||||
switch (**type)
|
||||
{
|
||||
case '\0':
|
||||
case '_':
|
||||
break;
|
||||
case 'v':
|
||||
*type += 1;
|
||||
if (non_empty)
|
||||
string_append (result, " ");
|
||||
string_append (result, "void");
|
||||
break;
|
||||
case 'l':
|
||||
*type += 1;
|
||||
if (non_empty)
|
||||
string_append (result, " ");
|
||||
string_append (result, "long");
|
||||
break;
|
||||
case 'i':
|
||||
*type += 1;
|
||||
if (non_empty)
|
||||
string_append (result, " ");
|
||||
string_append (result, "int");
|
||||
break;
|
||||
case 's':
|
||||
*type += 1;
|
||||
if (non_empty)
|
||||
string_append (result, " ");
|
||||
string_append (result, "short");
|
||||
break;
|
||||
case 'c':
|
||||
*type += 1;
|
||||
if (non_empty)
|
||||
string_append (result, " ");
|
||||
string_append (result, "char");
|
||||
break;
|
||||
case 'r':
|
||||
*type += 1;
|
||||
if (non_empty)
|
||||
string_append (result, " ");
|
||||
string_append (result, "long double");
|
||||
break;
|
||||
case 'd':
|
||||
*type += 1;
|
||||
if (non_empty)
|
||||
string_append (result, " ");
|
||||
string_append (result, "double");
|
||||
break;
|
||||
case 'f':
|
||||
*type += 1;
|
||||
if (non_empty)
|
||||
string_append (result, " ");
|
||||
string_append (result, "float");
|
||||
break;
|
||||
case 'G':
|
||||
*type += 1;
|
||||
if (!isdigit (**type))
|
||||
{
|
||||
success = 0;
|
||||
break;
|
||||
}
|
||||
/* fall through */
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9':
|
||||
n = 0;
|
||||
do
|
||||
{
|
||||
n *= 10;
|
||||
n += **type - '0';
|
||||
*type += 1;
|
||||
}
|
||||
while (isdigit (**type));
|
||||
if (strlen (*type) < n)
|
||||
{
|
||||
success = 0;
|
||||
break;
|
||||
}
|
||||
if (non_empty)
|
||||
string_append (result, " ");
|
||||
string_appendn (result, *type, n);
|
||||
*type += n;
|
||||
break;
|
||||
default:
|
||||
success = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (success)
|
||||
{
|
||||
if (!string_empty (&decl))
|
||||
{
|
||||
string_append (result, " ");
|
||||
string_appends (result, &decl);
|
||||
}
|
||||
string_delete (&decl);
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
string_delete (&decl);
|
||||
string_delete (result);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* `result' will be initialised in do_type; it will be freed on failure */
|
||||
|
||||
static int
|
||||
do_arg (type, result)
|
||||
const char **type;
|
||||
string *result;
|
||||
{
|
||||
char *tem;
|
||||
int len;
|
||||
const char *start;
|
||||
const char *end;
|
||||
|
||||
start = *type;
|
||||
if (!do_type (type, result))
|
||||
return 0;
|
||||
end = *type;
|
||||
if (ntypes >= typevec_size)
|
||||
{
|
||||
if (typevec_size == 0)
|
||||
{
|
||||
typevec_size = 3;
|
||||
typevec = (char **) xmalloc (sizeof (char*)*typevec_size);
|
||||
}
|
||||
else
|
||||
{
|
||||
typevec_size *= 2;
|
||||
typevec = (char **) realloc ((char *)typevec, sizeof (char*)*typevec_size);
|
||||
}
|
||||
}
|
||||
len = end - start;
|
||||
tem = (char *) xmalloc (len + 1);
|
||||
memcpy (tem, start, len);
|
||||
tem[len] = '\0';
|
||||
typevec[ntypes++] = tem;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* `decl' must be already initialised, usually non-empty;
|
||||
it won't be freed on failure */
|
||||
|
||||
static int
|
||||
do_args (type, decl)
|
||||
const char **type;
|
||||
string *decl;
|
||||
{
|
||||
string arg;
|
||||
int need_comma = 0;
|
||||
int dont_want_first;
|
||||
|
||||
#ifndef LONGERNAMES
|
||||
dont_want_first = 1;
|
||||
#else
|
||||
dont_want_first = 0;
|
||||
#endif
|
||||
|
||||
string_append (decl, "(");
|
||||
|
||||
while (**type != '_' && **type != '\0' && **type != 'e' && **type != 'v')
|
||||
{
|
||||
if (**type == 'N')
|
||||
{
|
||||
int r;
|
||||
int t;
|
||||
*type += 1;
|
||||
if (!get_count (type, &r) || !get_count (type, &t) || t >= ntypes)
|
||||
return 0;
|
||||
while (--r >= 0)
|
||||
{
|
||||
const char *tem = typevec[t];
|
||||
if (need_comma)
|
||||
string_append (decl, ", ");
|
||||
if (!do_arg (&tem, &arg))
|
||||
return 0;
|
||||
string_appends (decl, &arg);
|
||||
string_delete (&arg);
|
||||
need_comma = 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (need_comma)
|
||||
string_append (decl, ", ");
|
||||
if (!do_arg (type, &arg))
|
||||
return 0;
|
||||
if (dont_want_first)
|
||||
dont_want_first = 0;
|
||||
else
|
||||
{
|
||||
string_appends (decl, &arg);
|
||||
need_comma = 1;
|
||||
}
|
||||
string_delete (&arg);
|
||||
}
|
||||
}
|
||||
|
||||
if (**type == 'v')
|
||||
*type += 1;
|
||||
else if (**type == 'e')
|
||||
{
|
||||
*type += 1;
|
||||
if (need_comma)
|
||||
string_append (decl, ",");
|
||||
string_append (decl, "...");
|
||||
}
|
||||
|
||||
string_append (decl, ")");
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void
|
||||
munge_function_name (name)
|
||||
string *name;
|
||||
{
|
||||
if (!string_empty (name) && name->p - name->b >= 3
|
||||
&& name->b[0] == 'o' && name->b[1] == 'p' && name->b[2] == '$')
|
||||
{
|
||||
int i;
|
||||
/* see if it's an assignment expression */
|
||||
if (name->p - name->b >= 10 /* op$assign_ */
|
||||
&& memcmp (name->b + 3, "assign_", 7) == 0)
|
||||
{
|
||||
for (i = 0; i < sizeof (optable)/sizeof (optable[0]); i++)
|
||||
{
|
||||
int len = name->p - name->b - 10;
|
||||
if (strlen (optable[i].in) == len
|
||||
&& memcmp (optable[i].in, name->b + 10, len) == 0)
|
||||
{
|
||||
string_clear (name);
|
||||
string_append (name, "operator");
|
||||
string_append (name, optable[i].out);
|
||||
string_append (name, "=");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = 0; i < sizeof (optable)/sizeof (optable[0]); i++)
|
||||
{
|
||||
int len = name->p - name->b - 3;
|
||||
if (strlen (optable[i].in) == len
|
||||
&& memcmp (optable[i].in, name->b + 3, len) == 0)
|
||||
{
|
||||
string_clear (name);
|
||||
string_append (name, "operator");
|
||||
string_append (name, optable[i].out);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
else if (!string_empty (name) && name->p - name->b >= 5
|
||||
&& memcmp (name->b, "type$", 5) == 0)
|
||||
{
|
||||
/* type conversion operator */
|
||||
string type;
|
||||
const char *tem = name->b + 5;
|
||||
if (do_type (&tem, &type))
|
||||
{
|
||||
string_clear (name);
|
||||
string_append (name, "operator ");
|
||||
string_appends (name, &type);
|
||||
string_delete (&type);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* a mini string-handling package */
|
||||
|
||||
static void
|
||||
string_need (s, n)
|
||||
string *s;
|
||||
int n;
|
||||
{
|
||||
if (s->b == NULL)
|
||||
{
|
||||
if (n < 32)
|
||||
n = 32;
|
||||
s->p = s->b = (char *) xmalloc (n);
|
||||
s->e = s->b + n;
|
||||
}
|
||||
else if (s->e - s->p < n)
|
||||
{
|
||||
int tem = s->p - s->b;
|
||||
n += tem;
|
||||
n *= 2;
|
||||
s->b = (char *) realloc (s->b, n);
|
||||
s->p = s->b + tem;
|
||||
s->e = s->b + n;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
string_delete (s)
|
||||
string *s;
|
||||
{
|
||||
if (s->b != NULL)
|
||||
{
|
||||
free (s->b);
|
||||
s->b = s->e = s->p = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
string_init (s)
|
||||
string *s;
|
||||
{
|
||||
s->b = s->p = s->e = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
string_clear (s)
|
||||
string *s;
|
||||
{
|
||||
s->p = s->b;
|
||||
}
|
||||
|
||||
static int
|
||||
string_empty (s)
|
||||
string *s;
|
||||
{
|
||||
return s->b == s->p;
|
||||
}
|
||||
|
||||
static void
|
||||
string_append (p, s)
|
||||
string *p;
|
||||
const char *s;
|
||||
{
|
||||
int n;
|
||||
if (s == NULL || *s == '\0')
|
||||
return;
|
||||
n = strlen (s);
|
||||
string_need (p, n);
|
||||
memcpy (p->p, s, n);
|
||||
p->p += n;
|
||||
}
|
||||
|
||||
static void
|
||||
string_appends (p, s)
|
||||
string *p, *s;
|
||||
{
|
||||
int n;
|
||||
if (s->b == s->p)
|
||||
return;
|
||||
n = s->p - s->b;
|
||||
string_need (p, n);
|
||||
memcpy (p->p, s->b, n);
|
||||
p->p += n;
|
||||
}
|
||||
|
||||
static void
|
||||
string_appendn (p, s, n)
|
||||
string *p;
|
||||
const char *s;
|
||||
int n;
|
||||
{
|
||||
if (n == 0)
|
||||
return;
|
||||
string_need (p, n);
|
||||
memcpy (p->p, s, n);
|
||||
p->p += n;
|
||||
}
|
||||
|
||||
static void
|
||||
string_prepend (p, s)
|
||||
string *p;
|
||||
const char *s;
|
||||
{
|
||||
if (s == NULL || *s == '\0')
|
||||
return;
|
||||
string_prependn (p, s, strlen (s));
|
||||
}
|
||||
|
||||
static void
|
||||
string_prependn (p, s, n)
|
||||
string *p;
|
||||
const char *s;
|
||||
int n;
|
||||
{
|
||||
char *q;
|
||||
|
||||
if (n == 0)
|
||||
return;
|
||||
string_need (p, n);
|
||||
for (q = p->p - 1; q >= p->b; q--)
|
||||
q[n] = q[0];
|
||||
memcpy (p->b, s, n);
|
||||
p->p += n;
|
||||
}
|
193
bfd/filemode.c
Normal file
193
bfd/filemode.c
Normal file
|
@ -0,0 +1,193 @@
|
|||
/* filemode.c -- make a string describing file modes
|
||||
Copyright (C) 1985, 1990 Free Software Foundation, Inc.
|
||||
|
||||
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 1, 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, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
void mode_string ();
|
||||
static char ftypelet ();
|
||||
static void rwx ();
|
||||
static void setst ();
|
||||
|
||||
/* filemodestring - fill in string STR with an ls-style ASCII
|
||||
representation of the st_mode field of file stats block STATP.
|
||||
10 characters are stored in STR; no terminating null is added.
|
||||
The characters stored in STR are:
|
||||
|
||||
0 File type. 'd' for directory, 'c' for character
|
||||
special, 'b' for block special, 'm' for multiplex,
|
||||
'l' for symbolic link, 's' for socket, 'p' for fifo,
|
||||
'-' for any other file type
|
||||
|
||||
1 'r' if the owner may read, '-' otherwise.
|
||||
|
||||
2 'w' if the owner may write, '-' otherwise.
|
||||
|
||||
3 'x' if the owner may execute, 's' if the file is
|
||||
set-user-id, '-' otherwise.
|
||||
'S' if the file is set-user-id, but the execute
|
||||
bit isn't set.
|
||||
|
||||
4 'r' if group members may read, '-' otherwise.
|
||||
|
||||
5 'w' if group members may write, '-' otherwise.
|
||||
|
||||
6 'x' if group members may execute, 's' if the file is
|
||||
set-group-id, '-' otherwise.
|
||||
'S' if it is set-group-id but not executable.
|
||||
|
||||
7 'r' if any user may read, '-' otherwise.
|
||||
|
||||
8 'w' if any user may write, '-' otherwise.
|
||||
|
||||
9 'x' if any user may execute, 't' if the file is "sticky"
|
||||
(will be retained in swap space after execution), '-'
|
||||
otherwise.
|
||||
'T' if the file is sticky but not executable. */
|
||||
|
||||
void
|
||||
filemodestring (statp, str)
|
||||
struct stat *statp;
|
||||
char *str;
|
||||
{
|
||||
mode_string (statp->st_mode, str);
|
||||
}
|
||||
|
||||
/* Like filemodestring, but only the relevant part of the `struct stat'
|
||||
is given as an argument. */
|
||||
|
||||
void
|
||||
mode_string (mode, str)
|
||||
unsigned short mode;
|
||||
char *str;
|
||||
{
|
||||
str[0] = ftypelet (mode);
|
||||
rwx ((mode & 0700) << 0, &str[1]);
|
||||
rwx ((mode & 0070) << 3, &str[4]);
|
||||
rwx ((mode & 0007) << 6, &str[7]);
|
||||
setst (mode, str);
|
||||
}
|
||||
|
||||
/* Return a character indicating the type of file described by
|
||||
file mode BITS:
|
||||
'd' for directories
|
||||
'b' for block special files
|
||||
'c' for character special files
|
||||
'm' for multiplexor files
|
||||
'l' for symbolic links
|
||||
's' for sockets
|
||||
'p' for fifos
|
||||
'-' for any other file type. */
|
||||
|
||||
static char
|
||||
ftypelet (bits)
|
||||
unsigned short bits;
|
||||
{
|
||||
switch (bits & S_IFMT)
|
||||
{
|
||||
default:
|
||||
return '-';
|
||||
case S_IFDIR:
|
||||
return 'd';
|
||||
#ifdef S_IFLNK
|
||||
case S_IFLNK:
|
||||
return 'l';
|
||||
#endif
|
||||
#ifdef S_IFCHR
|
||||
case S_IFCHR:
|
||||
return 'c';
|
||||
#endif
|
||||
#ifdef S_IFBLK
|
||||
case S_IFBLK:
|
||||
return 'b';
|
||||
#endif
|
||||
#ifdef S_IFMPC
|
||||
case S_IFMPC:
|
||||
case S_IFMPB:
|
||||
return 'm';
|
||||
#endif
|
||||
#ifdef S_IFSOCK
|
||||
case S_IFSOCK:
|
||||
return 's';
|
||||
#endif
|
||||
#ifdef S_IFIFO
|
||||
#if S_IFIFO != S_IFSOCK
|
||||
case S_IFIFO:
|
||||
return 'p';
|
||||
#endif
|
||||
#endif
|
||||
#ifdef S_IFNWK /* HP-UX */
|
||||
case S_IFNWK:
|
||||
return 'n';
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/* Look at read, write, and execute bits in BITS and set
|
||||
flags in CHARS accordingly. */
|
||||
|
||||
static void
|
||||
rwx (bits, chars)
|
||||
unsigned short bits;
|
||||
char *chars;
|
||||
{
|
||||
chars[0] = (bits & S_IREAD) ? 'r' : '-';
|
||||
chars[1] = (bits & S_IWRITE) ? 'w' : '-';
|
||||
chars[2] = (bits & S_IEXEC) ? 'x' : '-';
|
||||
}
|
||||
|
||||
/* Set the 's' and 't' flags in file attributes string CHARS,
|
||||
according to the file mode BITS. */
|
||||
|
||||
static void
|
||||
setst (bits, chars)
|
||||
unsigned short bits;
|
||||
char *chars;
|
||||
{
|
||||
#ifdef S_ISUID
|
||||
if (bits & S_ISUID)
|
||||
{
|
||||
if (chars[3] != 'x')
|
||||
/* Set-uid, but not executable by owner. */
|
||||
chars[3] = 'S';
|
||||
else
|
||||
chars[3] = 's';
|
||||
}
|
||||
#endif
|
||||
#ifdef S_ISGID
|
||||
if (bits & S_ISGID)
|
||||
{
|
||||
if (chars[6] != 'x')
|
||||
/* Set-gid, but not executable by group. */
|
||||
chars[6] = 'S';
|
||||
else
|
||||
chars[6] = 's';
|
||||
}
|
||||
#endif
|
||||
#ifdef S_ISVTX
|
||||
if (bits & S_ISVTX)
|
||||
{
|
||||
if (chars[9] != 'x')
|
||||
/* Sticky, but not executable by others. */
|
||||
chars[9] = 'T';
|
||||
else
|
||||
chars[9] = 't';
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
Loading…
Reference in a new issue