5 changed files with 1845 additions and 1 deletions
@ -0,0 +1 @@ |
|||||
|
0.0 |
||||
@ -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.. |
||||
@ -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; |
||||
|
} |
||||
@ -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 new issue