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