diff options
| author | Andrew Lee <alee14498@protonmail.com> | 2021-08-15 00:16:45 -0400 |
|---|---|---|
| committer | Andrew Lee <alee14498@protonmail.com> | 2021-08-15 00:16:45 -0400 |
| commit | 723428bebe3105ad3c3406e416402d1831b482c4 (patch) | |
| tree | ff990e306163515973746ddfb261f29ba8765441 | |
| download | linux-0.01-distro-723428bebe3105ad3c3406e416402d1831b482c4.tar.gz linux-0.01-distro-723428bebe3105ad3c3406e416402d1831b482c4.tar.bz2 linux-0.01-distro-723428bebe3105ad3c3406e416402d1831b482c4.zip | |
Inital commit
132 files changed, 34566 insertions, 0 deletions
diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/README.md diff --git a/binutils-1.9.tar.bz2 b/binutils-1.9.tar.bz2 Binary files differnew file mode 100644 index 0000000..ebc20db --- /dev/null +++ b/binutils-1.9.tar.bz2 diff --git a/binutils-1.9/COPYING b/binutils-1.9/COPYING new file mode 100644 index 0000000..9a17037 --- /dev/null +++ b/binutils-1.9/COPYING @@ -0,0 +1,249 @@ + + GNU GENERAL PUBLIC LICENSE + Version 1, February 1989 + + Copyright (C) 1989 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The license agreements of most software companies try to keep users +at the mercy of those companies. By contrast, our General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. The +General Public License applies to the Free Software Foundation's +software and to any other program whose authors commit to using it. +You can use it for your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Specifically, the General Public License is designed to make +sure that you have the freedom to give away or sell copies of free +software, that you receive source code or can get it if you want it, +that you can change the software or use pieces of it in new free +programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of a such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must tell them their rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any program or other work which +contains a notice placed by the copyright holder saying it may be +distributed under the terms of this General Public License. The +"Program", below, refers to any such program or work, and a "work based +on the Program" means either the Program or any work containing the +Program or a portion of it, either verbatim or with modifications. Each +licensee is addressed as "you". + + 1. You may copy and distribute verbatim copies of the Program's source +code as you receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice and +disclaimer of warranty; keep intact all the notices that refer to this +General Public License and to the absence of any warranty; and give any +other recipients of the Program a copy of this General Public License +along with the Program. You may charge a fee for the physical act of +transferring a copy. + + 2. You may modify your copy or copies of the Program or any portion of +it, and copy and distribute such modifications under the terms of Paragraph +1 above, provided that you also do the following: + + a) cause the modified files to carry prominent notices stating that + you changed the files and the date of any change; and + + b) cause the whole of any work that you distribute or publish, that + in whole or in part contains the Program or any part thereof, either + with or without modifications, to be licensed at no charge to all + third parties under the terms of this General Public License (except + that you may choose to grant warranty protection to some or all + third parties, at your option). + + c) If the modified program normally reads commands interactively when + run, you must cause it, when started running for such interactive use + in the simplest and most usual way, to print or display an + announcement including an appropriate copyright notice and a notice + that there is no warranty (or else, saying that you provide a + warranty) and that users may redistribute the program under these + conditions, and telling the user how to view a copy of this General + Public License. + + d) You may charge a fee for the physical act of transferring a + copy, and you may at your option offer warranty protection in + exchange for a fee. + +Mere aggregation of another independent work with the Program (or its +derivative) on a volume of a storage or distribution medium does not bring +the other work under the scope of these terms. + + 3. You may copy and distribute the Program (or a portion or derivative of +it, under Paragraph 2) in object code or executable form under the terms of +Paragraphs 1 and 2 above provided that you also do one of the following: + + a) accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of + Paragraphs 1 and 2 above; or, + + b) accompany it with a written offer, valid for at least three + years, to give any third party free (except for a nominal charge + for the cost of distribution) a complete machine-readable copy of the + corresponding source code, to be distributed under the terms of + Paragraphs 1 and 2 above; or, + + c) accompany it with the information you received as to where the + corresponding source code may be obtained. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form alone.) + +Source code for a work means the preferred form of the work for making +modifications to it. For an executable file, complete source code means +all the source code for all modules it contains; but, as a special +exception, it need not include source code for modules which are standard +libraries that accompany the operating system on which the executable +file runs, or for standard header files or definitions files that +accompany that operating system. + + 4. You may not copy, modify, sublicense, distribute or transfer the +Program except as expressly provided under this General Public License. +Any attempt otherwise to copy, modify, sublicense, distribute or transfer +the Program is void, and will automatically terminate your rights to use +the Program under this License. However, parties who have received +copies, or rights to use copies, from you under this General Public +License will not have their licenses terminated so long as such parties +remain in full compliance. + + 5. By copying, distributing or modifying the Program (or any work based +on the Program) you indicate your acceptance of this license to do so, +and all its terms and conditions. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the original +licensor to copy, distribute or modify the Program subject to these +terms and conditions. You may not impose any further restrictions on the +recipients' exercise of the rights granted herein. + + 7. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of the license which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +the license, you may choose any version ever published by the Free Software +Foundation. + + 8. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 9. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 10. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to humanity, the best way to achieve this is to make it +free software which everyone can redistribute and change under these +terms. + + To do so, attach the following notices to the program. It is safest to +attach them to the start of each source file to most effectively convey +the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) 19yy <name of author> + + 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. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19xx name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the +appropriate parts of the General Public License. Of course, the +commands you use may be called something other than `show w' and `show +c'; they could even be mouse-clicks or menu items--whatever suits your +program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + program `Gnomovision' (a program to direct compilers to make passes + at assemblers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/binutils-1.9/ChangeLog b/binutils-1.9/ChangeLog new file mode 100644 index 0000000..e2ea724 --- /dev/null +++ b/binutils-1.9/ChangeLog @@ -0,0 +1,1647 @@ +Wed Apr 17 15:33:33 1991 Per Bothner (bothner at pogo.gnu.ai.mit.edu) + + * Make version number be 1.9. + +Wed Apr 10 18:19:15 1991 Per Bothner (bothner at pogo.gnu.ai.mit.edu) + + * strip.c (xmalloc): Handle allocation request of 0 (make it 1). + +Tue Apr 9 13:40:31 1991 Per Bothner (bothner at pogo.gnu.ai.mit.edu) + + * strip.c (rewrite_file_symbols): Instead of tell(fd), + use the more portable lseek(fd, 0L, 1). + +Mon Apr 8 17:31:17 1991 Per Bothner (bothner at pogo.gnu.ai.mit.edu) + + * Makefile: Replaced mis-spelled and obsolete -DPIGNAL_MISSING + by -DSYS_SIGLIST_MISSING in sample CFLAGS for COFF systems. + +Sun Mar 31 23:06:42 1991 Per Bothner (bothner at pogo.gnu.ai.mit.edu) + + * Increment version number to 1.8. + +Fri Mar 29 15:29:55 1991 Per Bothner (bothner at pogo.gnu.ai.mit.edu) + + * ld.c (do_relocation_warnings): Add some paranoia checking + in case of a corrupted binary. + +Thu Mar 28 17:21:24 1991 Per Bothner (bothner at pogo.gnu.ai.mit.edu) + + * ld.c (subfile_wanted_p): Not quite as minimalist: + If a subfile defines a common that is needed (i.e. currently + undefined), don't load the subfile, just define the undefined + symbol as a common. + This brings back some of the logic from Roland's change. + It is needed to handle a dubious libc.a in SunOS. + +Sat Mar 23 13:25:07 1991 Per Bothner (bothner at pogo.gnu.ai.mit.edu) + + * ld.c (subfile_wanted_p): Try a minimalist approach: + Treat an existing common definition as a true definition + wrt the decision as to whether a subfile is needed - + i.e. don't pull in the subfile in that case. + +Tue Mar 19 01:01:34 1991 Per Bothner (bothner at mole.ai.mit.edu) + + * ld.c (subfile_wanted_p): A compromise solution, suggested by Mike: + Restore the old #if 1 behavior, with the following changes: + + allow a library member to grow a common symbol only if some other + library member is already known to reference that symbol + + allow a library member to replace a common symbol with some other + definition if some other library member is already known to + reference that symbol. + +Sun Mar 17 00:47:44 1991 Per Bothner (bothner at pogo.ai.mit.edu) + + * Makefile: Add $(LIBS) to dependencies for most programs, + to make sure signame.o and alloca.o get built when needed. + * ar.c: Don't include fcntl.h again if USG. + * ar.c: Initialize program_name variable. + * ar.c: Changes from Arnold Robbins <arnold@audiofax.com>: + "gnu ar didn't properly use just the trailing component + of filenames. If creating an archive, it would put members in + with names like ./foo. If updating an archive with a member + foo with /some/path/foo, the new file would get appended to + the archive instead of replacing the old one." + So add basename routine to skip directory prefix, and use it + to name archive elements with just the basename. + Also some System V stuff: + "System V machines (of course) use a slightly different version + of the archive format header, and allow a leading '-' on the + flags. Also, the temp file name didn't allow for 14 character + file names." + I (Per) incorporated these changes. It seemed reasonable to always + allow an initial '-' to the flags. + * ar.c: Improved argc/argv/usage() checking. + * ar.c: Unlink if we fail to open after "touch" for create. + * gprof.c: Reformat, adding white space between = and &. + Without it, pcc thinks it's an old-style &=. + * ld.c: Code for ns32000 from Jyrki Kuoppala <jkp@sauna.hut.fi>, + some of it by Ian Dall. + * ld.c (subfile_wanted_p): For now, undo the change made for 1.7. + (It causes problems, and anyway I'm unconvinced by the justification.) + * ranlib.c (main): Removed emulation of psignal when PSIGNAL_MISSING + is defined, since there is a version in signame.c. + +Thu Mar 7 17:29:09 1991 Mike Haertel (mike at ducky) + + * a.out.gnu.h [sequent && i386]: Changes to support the + sequent symmetry. + * ar.c (update_symdefs): Loop to original_num_symdefs rather + than nsymdefs, since the latter changes inside the loop. + * gprof.c (main): Divide by (nhist - 1) to get the correct + histogram increment. + * hp-bin/hpxt.c (process_archive_entry): Deal correctly with + names containing '/'. + * ld.c [tek4300, LARMAG]: Add UTek support. + [hp300]: Mark the filename symbol with N_FN|N_EXT not N_TEXT. + (deduce_file_type): Remove forward reference to coffheader. + (read_a_out_header): Likewise. + (read_a_out_header) [NMAGIC]: Adjust orig_data_address when + reading NMAGIC files. + (enter_global_ref): Update symbol's last_library_ref for + use by subfile_wanted_p(). + (subfile_wanted_p): Never use a library member to define + a common symbol unless some other library member already known + to be needed references that symbol. + (do_file_warnings): Warn for multiply defined N_ABS symbols. + (do_warnings: Warn if -e entry symbol is never defined. + (perform_relocation): Make sure we never carry out of the + relocation masked bits. + (getsym): Clear last_library_ref to NULL when initializing + a new symbol, for subfile_wanted_p(). + ranlib.c [PSIGNAL_MISSING]: Don't use psignal(). + strip.c (file_close): Use -1 for invalid descriptor flag value. + (read_file_symbols): Close the file if an error occurs. + (rewrite_file_symbols): Use ~ prefixed temp file to avoid + problems with 14 character limits on file names. Use open() + instead of creat() since the file will need to be read later. + Be sure to close all input files. Cast lseek() arguments + correctly. + +Wed Nov 28 23:42:48 1990 Jim Kingdon and Roland McGrath (roland@ai.mit.edu) + + * ld.c (subfile_wanted_p): If the subfile has a common def of + a symbol already defined as common, look at the subfile's def + to find the largest-sized common def. But do not include the + subfile if the symbol is already defined at all. + +Mon Oct 15 13:07:24 1990 Mike Haertel (mike at albert.ai.mit.edu) + + * ld.c (digest_symbols): Align the end of the data segment + to sizeof (double) after allocating set vectors, not before. + +Wed Sep 26 12:51:05 EDT 1990 Jay Fenlason (hack@ai.mit.edu) + + * gprof.c When closing /dev/kmem, use fclose instead of ck_fclose + because some systems erroniously return NOT_OWNER on close. . . + +Mon Sep 3 18:14:35 1990 Mike Haertel (mike at wookumz.ai.mit.edu) + + * ar.c (insert_in_map): Always insert __.SYMDEF at the front + of the archive. + +Mon Aug 13 19:01:27 1990 Chris Hanson (cph at kleph) + + * ranlib.c (main): Remove emulation of `psignal' from last + change. There is already an emulation that was accidentally + omitted from the last release. + +Thu Aug 9 13:57:53 1990 Chris Hanson (cph at kleph) + + * hp-bin/ioutil.c (iou_open): Remove external declaration of + `open', which conflicts with that supplied in hp-ux 7.0. + + * hp-bin/hpxt.c: Add compile-time test that signals an error + if this file is compiled on the wrong kind of machine. + + * nm.c (getpagesize): Add emulation for USG machines. This is + needed for GNU malloc, and perhaps should be placed there + instead. + + * strip.c, ar.c: Make conditionalization of BSD features be + more selective. Previously these features were not used if + USG was defined -- but HP-UX supports them. + + * ranlib.c (main): Add emulation of `psignal' for USG + machines. + +Tue Jul 10 02:07:08 1990 David J. MacKenzie (djm at apple-gunkies) + + * ar.c: Remove unused variables. + (copy_out_member): Declare `outname' as char *. + Use `program_name' instead of hardcoded "ar" in messages. + + * robotussin.c: Use `program_name' instead of hardcoded + "robotussin" in messages. + + * objdump.c: Indent. Declare a lot of functions void. + Improve comments. Rename option variables to reflect their + meanings rather than the short option names. + Use error instead of fprintf and perror. Include filename in messages. + (main): Exit with 0 status normally. Change +reloc to +relocation. + (dump_file): Renamed from doit. + (dump_reloc1): Do nothing if sun and sparc (structure isn't defined). + (getpagesize): Add emulations for USG and sparc, copied from ld.c. + + * size.c: Declare some functions void. Use `program_name' + instead of hardcoded "size" in messages. + + * gprof.c, robotussin.c: Use VPRINTF_MISSING, to be consistent + with error.c. + + * nm.c: Declare some functions void. Remove unused variables. + + * strip.c: Declare some functions void. + Make `strip_symbols' and `discard_locals' enums instead of ints. + Remove unused variables. + (fatal, error_with_file, usage): Use `program_name' instead of + hardcoded "strip". + (main): Set `program_name'. + (error, error_with_file, perror_file, perror_name, fatal, + print_file_name, prline_file_name): Replace with a single + `error' function, and change callers. + +Fri Jul 6 01:22:26 1990 David J. MacKenzie (djm at apple-gunkies) + + * ranlib.c (main): Print usage message if no files given. + (usage): New function. + Global: Use `program_name' instead of hardcoded "ranlib" in + messages. + +Thu Jul 5 11:31:12 1990 David J. MacKenzie (djm at apple-gunkies) + + * Makefile (dist): Put the list of files to distribute into + the Makefile instead of ARCHLIST. Include signame.c and not TAGS. + (ranlib): Link with signame.o. + +Thu Jun 28 17:03:51 1990 Mike Haertel (mike at ducky) + + * ld.c: (compute_mach_o_section_offsets): Bug fix for -r. + (relocate_file_addresses): Replace ~N_EXT with N_TYPE, + undoing earlier change which broke -g. + +Wed Jun 20 13:34:11 1990 Mike Haertel (mike at thor.acc.stolaf.edu) + + * gprof.c: Make qsort compare functions take const void * + arguments for compatibility with the standard. + +Tue Jun 19 19:23:41 1990 Mike Haertel (mike at ducky) + + * ld.c: (do_file_warnings, do_relocation_warnings): + Always print the object file name in error messages, + even if we also know a source file name. Use + print_file_name() to properly print the names of + files that came from libraries. + (getpagesize): Fake getpagesize() for sun 4's. + +Wed Jun 13 13:11:21 1990 Mike Haertel (mike at ducky) + + * Makefile: (ld): Link the demangler. + * cplus-dem.c: Don't include <memory.h> on NeXT. + * ld.c: Include <sys/resource.h> unless USG. + (demangler): New variable; unconditionally set + to cplus_demangle(). This is different from how + ld++ did it, but conditionalizing on whether -lg++ + is specified seems too much of a kludge. The + demangler will only demangle names that look like + they came from g++ anyway. + (main): Attempt to raise the stack limit so we + don't barf on large files. + (decode_command): Add new option `-nostdlib', with + new flag `no_standard_dirs'. + (file_open): Eliminate unused variable `p'. + Deal with the case of no standard library dirs. + (enter_file_symbols): Eliminate unused `lowest_set_vector'. + (enter_global_ref): Logically and nlist.n_type with + ~N_EXT rather than N_TYPE; otherwise, e.g., <stab.h>'s + N_FUN would get turned into N_TEXT, which we don't want. + (subfile_wanted_p): Eliminate Kingdon's "if the user + declares 'int pipe;' we don't want to get 'pipe()' + from the library" bugfix, because (first of all) + it breaks g++, and secondly, it was generally wrong. + If the user declares "int errno;" and the library + declares "int errno = 0;" we want to get the library's + version. The proper fix will require some thought. + (digest_symbols): Eliminated unused variable `erred'. + (relocate_file_addresses): Use ~N_EXT instead of N_TYPE. + (address_to_line): Likewise. + (do_relocation_warnings): Eliminate unused variables + `next' and `source'. Use the demangler to print names. + (do_file_warnings): Use the demangler when possible. + (do_warnings): Eliminate unused variable `i'. + (initialize_a_out_data_start): If no entry symbol + was given, set it to "start", if sequent. + (perform_relocation): Eliminate variable `data_input_address' + performing copy propogation by hand to the one place it + was used. Get rid of misleading comments. + (coptxtrel): Use ~N_EXT instead of N_TYPE. + (copdatrel): Likewise. + (write_syms): Likewise. + +Tue Jun 12 11:16:26 1990 Mike Haertel (mike at ducky) + + * Makefile: Use $(CFLAGS) when linking. + * ar.c: (struct member_desc): date is long. + (scan): Fully initialize member_desc. + (delete_from_map): Clobber info.name instead + of unlinking from list. + (update_symdefs): Only install new symdefs for + members that *have* them. + Changes for MACH_O: + (read_header_info): New function. + (make_new_symdefs): Use it. + * gprof.c: fread() returns size_t for ANSI. + Declare qsort() properly for ANSI. + Move #include <assert.h> to the top. + size_t is an unsigned int. + Changes for MACH_O: + (read_header_info): New function. + (main): Use it. + (badsym): Allow N_SECT symbols. + (fatal): Print a space after the colon. + * ld.c: Changes for MACH_O: + (struct file_entry): New fields containing header + info but no explicit struct exec, as well as file type info. + Also section ordinals for Mach-O files. + (output_file_type): New global variable. + (output_style): New global variable, also supersedes + the flag relocatable_output. + Removed a.out specific stuff from global variables. + (output_*_offset): New global variables initialized + according to the output file type. + (decode_command): Remove a.out specific stuff. + (deduce_file_type): New function. + (read_a_out_header): New function. + (read_mach_o_header): New function. + (read_header): Use the above functions. + (read_entry_symbols): Use new file_entry fields; + call translate_mach_o_symbols(). + (read_entry_strings): Use new file_entry fields; + no longer deduce symseg presence. + (read_file_symbols): Use deduce_file_type(). + (enter_file_symbols): Use new file_entry fields. + Display N_INDR refs differently. Move default: in + display switch out of #ifdef sequent. + (contains_symbol): Use new file_entry fields. + (symdef_library): Use xmalloc(). Remember to + free subentry->strings, but only if we allocated them. + (process_subentry): Use new file_entry fields. + (subfile_wanted_p): Likewise. + (digest_symbols): Use new functions initialize_text_start() + and initialize_data_start(). Remove a.out specific stuff. + (consider_file_section_lengths): Use new file_entry fields. + (relocate_file_addresses): Use new file_entry fields. + (describe_file_sections): Likewise. + (list_file_locals): Likewise. + (next_debug_entry): Likewise. + (init_debug_scan): Likewise. + (do_relocation_warnings): Likewise. + (do_file_warnings): Likewise. + (do_warnings): Use new global output_style. + (initialize_a_out_text_start): New function. + (initialize_a_out_data_start): New function. + (compute_a_out_section_offsets): New function. + (compute_more_a_out_section_offsets): New function. + (write_a_out_header): New function. + (translate_mach_o_symbols): New function. + (translate_mach_o_relocation): New function. + (initialize_mach_o_text_start): New function. + (initialize_mach_o_data_start): New function. + (compute_mach_o_section_offsets): New function. + (compute_more_mach_o_section_offsets): New function. + (write_mach_o_header): New function. + (generate_mach_o_symbols): New function. + (generate_mach_o_relocations): New function. + (initialize_text_start): New function switch on + ouput_file_type. + (initialize_data_start): Likewise. + (compute_section_offsets): Likewise. + (compute_more_section_offsets): Likewise. + (write_header): Switch on output_file_type. + (write_output): Use the above functions. Bug fix + for umask(). + (write_text): Use output_text_offset. + (text_offset): No longer used. + (read_file_relocation): Use new file_entry fields. + Call translate_mach_o_relocation() for Mach-O input files. + (copy_text): Likewise. + (write_data): Use output_data_offset. + (copy_data): Use new file_entry fields. Call + translate_mach_o_relocation() for Mach-O input files. + (perform_relocation): Use new file_entry fields. + (write_rel): Use output_*rel_offset. + (coptxtrel): Use new file_entry fields. Call + generate_mach_o_relocations() for Mach-O output files. + (copdatrel): Likewise. + (write_string_table): Use output_strs_{offset,size}. + (write_syms):Use output_{syms,strs}_{offset,size}. + Use n_sect field if N_SECT is defined. Call + generate_mach_o_symbols() for Mach-O output files. + (write_file_syms): Likewise. + (write_symsegs): Use output_symseg_offset. + (write_file_symseg): Use new file_entry fields. + Changes for NeXT: + (N_TXTADDR): Provide version for NeXT. + (N_DATADDR): Provide version for NeXT. + (enter_global_ref): Deal with NeXT N_INDR weirdness. + (compute_a_out_section_offsets): Likewise. + (CPU_TYPE, et al): Mach-O info for the NeXT. + (compute_mach_o_section_offsets): Deal with N_INDR + strangeness. + (write_rel): Likewise. + (coptxtrel): Likewise. + (copdatrel): Likewise. + (write_syms): Likewise. + (symtab_init): Deal with NeXT shared library strangeness. + * nm.c: Changes for MACH_O: + (do_one_file): Remove a.out specific stuff. + (read_header_info): New function. + (do_one_rel_file): Use it. + (read_header): Removed. + (print_one_symbol): Deal with Mach-O section ordinals. + * size.c: Changes for MACH_O: + (do_one_file): Remove a.out specific stuff. + (read_header_info): New function. + (do_one_rel_file): Use it. + (read_header): Removed. + * strip.c: Changes for MACH_O: + (struct file_entry): Remove struct exec; add generic + fields to contain necessary information. + (main): Use new file_entry fields. + (file_open): Remove a.out specific stuff. + (read_header): Use new file_entry fields. Handle + Mach-O files. + (read_entry_symbols): Use new file_entry fields. + (count_file_symbols): Likewise. + (rewrite_file_symbols): Likewise. + (write_file_syms): Likewise. + (modify_relocation): Likewise. + +Mon May 28 16:25:59 1990 Mike Haertel (mike at apple-gunkies) + + * Copied the binutils home to work on. So please anyone + else don't make any changes!!!! + + * Removed gprof.texinfo from ARCHLIST because the file + it was symbolically linked to mysteriously disappeared. + +Mon May 21 18:39:33 1990 Jim Kingdon (kingdon at mole.ai.mit.edu) + + * Makefile (clean): Also remove $(archpfx)*.o. + + * nm.c (print_one_symbol): Cast n_other and n_desc to unsigned + before passing to printf. + +Tue May 15 15:19:49 1990 David J. MacKenzie (djm at albert.ai.mit.edu) + + * ARCHLIST: Add signame.h. + +Sun May 6 23:41:35 1990 David J. MacKenzie (djm at albert.ai.mit.edu) + + * Makefile (dist): Don't make an uncompressed tar file. + +Sat Apr 7 22:58:27 1990 Jim Kingdon (kingdon at pogo.ai.mit.edu) + + * Makefile (MALLOC): Add. + ARCHLIST: Add gmalloc.c. + gmalloc.c: New file linked from ../lib/malloc. + +Fri Apr 6 00:02:22 1990 Jim Kingdon (kingdon at pogo.ai.mit.edu) + + * Makefile: Make objdump depend on a.out.gnu.h. + + * gprof.c: Remove never-used declaration of getopt. + + * gprof.c: Add REMOVE_TIME_IF_THERE to not complain on gcc-compiled + programs. + +Tue Mar 20 15:41:22 1990 Richard Stallman (rms at sugar-bombs.ai.mit.edu) + + * hp-bin/mkhplib: Create a dummy libg.a. + +Thu Mar 1 14:22:02 1990 David J. MacKenzie (djm at albert.ai.mit.edu) + + * ld.c (classify_arg, decode_option): Functions removed, with + some of the code moved to decode_command. + (decode_command): Use getopt_long_only instead of custom arg parser. + (usage, fatal, fatal_with_file): Use `progname' instead + of hardcoded name. + (usage): If STRING is null, don't print it. + + * Makefile: Link ld with GNU_GETOPT_LONG. + +Wed Feb 28 14:32:06 1990 Jim Kingdon (kingdon at pogo.ai.mit.edu) + + * ranlib.c (main): Call psignal() with arguments in the correct order. + signame.c, signame.h: New files (linked from ../lib). + ranlib.c: Include signame.h. + + * Makefile: Make sure LIBS is after every program which + uses GNU_GETOPT_LONG (for alloca()). + +Thu Feb 15 23:35:07 1990 Jim Kingdon (kingdon at mole.ai.mit.edu) + + * ar.c (mywrite): New function which checks for errors. + All over: Use it instead of write. + (perror_with_name): If errno >= sys_nerr, print "unknown error" + not "can't open". + (extract_member): Use ferror to check for error. + +Tue Feb 13 14:29:24 EST 1990 Jay Fenlason (hack@wookumz.ai.mit.edu) + + * strip.c (rewrite_file_symbols) Seek to the right place in + COFF files. A three line patch from Eliot Dresselhaus + (eliot@mgm.mit.edu). + +Wed Jan 31 22:15:11 1990 Jim Kingdon (kingdon at pogo.ai.mit.edu) + + * hp-include: Remove sys/fcntl.h + + * nm.c [USG], ar.c [LOCKS]: Include <fcntl.h> not <sys/fcntl.h>. + +Fri Jan 26 20:13:12 1990 Mike Haertel (mike at rice-chex) + + * Makefile: Removed references to GNU CC in copyright notice. + +Thu Jan 11 03:32:52 1990 David J. MacKenzie (djm at hobbes.ai.mit.edu) + + * ARCHLIST: Include ARCHLIST in distribution. + + * ranlib.c: If X_OK is not defined, define it (for USG). + (main, touch_symdefs): Include program name in error messages. + + * strip.c (xmalloc): Don't check for size != 0. + (usage): New function. + (main): Call usage instead of fprintf or fatal. + +Wed Jan 10 15:06:00 1990 Jim Kingdon (kingdon at pogo) + + * ld.c (subfile_wanted_p): Never use a library to satisfy a + program's common symbol. + + * ld.c (symtab_init) [sun]: Use symbol_define for __DYNAMIC. + [sequent]: Same for _i387_flt. + +Wed Jan 10 01:44:58 1990 David J. MacKenzie (djm at hobbes.ai.mit.edu) + + * size.c (main): Exit with 0 status normally, instead of garbage. + * strip.c (main): Make -g a recognized option. Document -g in + usage message. Exit with 0 status normally. + * nm.c (main): Exit with 0 status normally. + * gprof.c (main): Ditto. + +Mon Jan 8 00:06:55 1990 Mike Haertel (mike at rice-chex) + + * strip.c (main): Made -g a synonym for -S (strip debugging + symbols) for greater mnemonic value. + +Thu Dec 28 02:41:37 1989 David J. MacKenzie (djm at hobbes.ai.mit.edu) + + * objdump.c, ld.c, nm.c, size.c (xmalloc, xrealloc): Take the + change to return 0 if size is 0 back out. bfox convinced me + that, assuming that programs do not allocate 0 bytes + intentionally, printing an error message if they try makes + it easier to track down the bug. + +Sat Dec 23 00:49:43 1989 David J. MacKenzie (djm at hobbes.ai.mit.edu) + + * ld.c (usage): Mention some options that were missing from the + usage message. + + * ranlib.c (main): stbf wasn't defined. Use access instead + of stat, since it tests for execute permission. + +Fri Dec 22 23:38:15 1989 David J. MacKenzie (djm at rice-chex) + + * ar.c, ld.c, nm.c: Put various alloca declaration stuff in + one place, and declare it as char * if not GNU C or sparc. + + * ld.c, nm.c, size.c (xmalloc, xrealloc): Return char *, not int. + Ok to return 0 if 0 bytes requested (ANSI C). + Fix declarations for [x][re]alloc. + + * strip.c (main): Combine fprintf calls for usage. + +Fri Dec 22 11:23:26 1989 Richard Stallman (rms at sugar-bombs.ai.mit.edu) + + * ranlib.c (main): Look for ar in /usr/local/gnubin first. + Rename `jak' to `junk'. + Move `#define vfork' to start of file. Don't declare it extern. + +Thu Dec 21 17:02:54 1989 David J. MacKenzie (djm at hobbes.ai.mit.edu) + + * gprof.c (main): Combine fprintf calls in usage message. + + * objdump.c (usage): Combine fprintf calls. + + * robotussin.c (main): Add dashes to usage message to reduce + confusion of three 'f's in a row . . . . + + * nm.c (main): Combine several fprintf calls to make usage + message easier to edit. + + * ld.c (usage): New function to print error message and usage message. + (decode_command, decode_option): Call usage instead of fatal. + + * ar.c (usage): New function to print error message and usage message. + (main): Call usage instead of fatal. + +Fri Dec 15 16:05:38 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * ld.c (perform_relocation) [CROSS_LINKER]: New code. + +Tue Dec 12 00:17:41 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * ld.c (symtab_init): Use _edata not __edata. + +Mon Dec 11 23:34:51 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * ld.c: Always call each_full_file with 2 args. + +Mon Dec 4 16:02:43 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * Makefile: Define bindir. + (install): Add semicolon to end of 'cp' and 'mv' lines. + (install): Double last '$' in 'mv' line. + +Sat Dec 2 13:54:22 1989 David J. MacKenzie (djm at hobbes.ai.mit.edu) + + * objdump.c (usage): Remove stray newline in message. + +Thu Nov 30 21:38:02 1989 David J. MacKenzie (djm at rice-chex) + + * objdump.c (usage): Update to reflect Jim's change. + +Thu Nov 30 19:05:17 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * objdump.c (main): Rename +sym-links to +symbols. + + * nm.c (do_one_rel_file): Check for validity of string table + offsets to avoid core dumps on bad input files. + + * ld.c (process_subentry): Remove variable prev and assign + directly to *prev_addr. + +Thu Nov 30 15:33:59 1989 Jay Fenlason (hack at gnu.ai.mit.edu) + + * Fixed gprof.c so it would compile and run. Jim forgot + a } and said FUN1 when he meant EXT1 when he added + the demangle stuff. + +Mon Nov 27 18:12:30 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * objdump.c: Add page_size. + (main): Set it. + +Mon Nov 27 16:35:50 1989 David J. MacKenzie (djm at hobbes.ai.mit.edu) + + * Makefile (dist): Give up on weird linking schemes for making + the distribution archive, and simply read the list of files to + put in the archive from a file. + + * gprof.c (main): Rename long options: + suppress-local to no-static, suppress-blurbs to brief, + suppress-arg-prof to no-prof, suppress-arg-time to no-time, + arg-prof-only to only-prof, arg-time-only to only-time. + +Sun Nov 26 00:46:10 1989 David J. MacKenzie (djm at hobbes.ai.mit.edu) + + * gprof.c (main): Make long_options static. Mention long + options in usage message. Use common code for handling + equivalent long and short named options. + +Fri Nov 24 03:44:04 1989 David J. MacKenzie (djm at hobbes.ai.mit.edu) + + * Makefile (dist): Don't distribute backup files in the hp-bin + and hp-include directories. + + * strip.c (main): Add null terminating element to + long_options. Print a usage message if an invalid option is + given. + + * ranlib.c (main): Add `val' element to long_options elements. + Print a usage message if an invalid option is given. + + * gprof.c (main): Add null terminating element to + long_options. Mention long options in usage message and exit + if an invalid option is given. + + * nm.c (main): Rename +debug-syms to +all and +reverse-sort to + +reverse. Add null terminating element to long_options. + Print a usage message if given an invalid option. + + * objdump.c: Add program_name variable. + (main): Rename +syms to +sym-links. Add null + terminating element to long_options. Set program_name. + (usage): Mention long options and use program_name. + (xmalloc): New function to allocate memory with error check. + Global: use xmalloc instead of malloc. + Global: Use program_name in error messages. + +Mon Nov 20 16:58:25 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * objdump.c [!COFF_ENCAPSULATE]: Define N_MAGIC. + + * ld.c (main) [COFF_ENCAPSULATE]: Don't write coff header + for output of ld -A. + (read_file_symbols, read_header): Seek past coff header for + input with just_syms_flag set. + +Fri Nov 17 02:45:43 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * objdump.c (dump_header) [__GNU_EXEC_MACROS]: Don't access a_info + field of struct exec. + + * objdump.c: Include <a.out.h> not "a.out.gnu.h" + + * objdump.c (main): Make long_options static. + +Thu Nov 16 22:55:38 1989 Richard Stallman (rms at sugar-bombs.ai.mit.edu) + + * Makefile (ranlib): Depend on getopt. + +Thu Nov 16 11:48:27 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * ld.c (decode_option, case 'r'): Set default_magic instead + of magic. + (decode_command): Use default_magic. + + * Makefile (CPLUS_DEM): Use this for nm and gprof. + + * nm.c (print_one_symbol): Use cplus_demangle. + + * gprof.c (many): Use cplus_demangle when printing names. + + * ld.c (copdatrel): Use N_DATADDR(outheader) for correct + behavior under 'ld -r -n'. + + * ld.c (relocate_file_addresses): Use DATA_ADDR_DOT_O + instead of entry->header.a_text. + +Wed Nov 15 13:08:49 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * ld.c (digest_symbols): When padding data to 8 byte boundary, + set pad_data so that write_data() can write the padding. + Change old computation of pad_data to add to pad_data instead + of assigning into it. + + * ld.c (perform_relocation): Add variable data_input_address + to deal with NMAGIC input files. + + * ld.c (symbol_define): New function. + (symtab_init): Use it. + + * robotussin [sun386]: Merge sun386 changes. + +Tue Nov 14 12:52:34 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * Makefile (ld): Change ld to $(archpfx)ld. + + * Makefile (objdump): Add GNU_GETOPT_LONG to dependencies. + + * ld.c [pyr, hp300]: Define INITIALIZE_HEADER and N_{TXT,DATA}ADDR. + +Sat Nov 11 12:08:21 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * ld.c (symtab_init): Don't mess with user-defined "etext". + (digest_symbols): Check for e{text,data}_symbol null. + + * ld.c (main): Call symtab_init after load_symbols. + + * ld.c (write_output): Add call to unlink. + +Wed Nov 8 11:19:08 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * strip.c (relocation_info_ptr, RELOCATION_INFO_SYMBOL_NUM): + Added. + (modify_relocation): Use them. + + * Makefile (strip): Use GNU_GETOPT_LONG, not GNU_GETOPT. + + * ld.c [sun]. Set machtype based on machtype of object files. + (read_header): Call READ_HEADER_HOOK if defined. + +Fri Nov 3 15:18:15 EST 1989 Jay Fenlason (hack@ai.mit.edu) + + * nm.c (main) make long_options static so it can be compiled with cc. + * strip.c (main) ditto. + +Thu Oct 26 12:28:33 1989 Richard Stallman (rms at sugar-bombs.ai.mit.edu) + + * Makefile (install): Supply missing `done'. + +Tue Oct 17 12:50:46 1989 Mike Haertel (mike at wheat-chex) + + * ld.c (enter_file_symbols): ignore symbols of type + N_SETV | N_EXT. These shouldn't be here at all should + they? + +Mon Oct 16 16:53:03 1989 Joseph Arceneaux (jla at apple-gunkies.ai.mit.edu) + + * ld.c (process_subentry): New function called from + linear_library. + +Fri Oct 6 10:46:40 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * gprof.c [!HAVE_VPRINTF]: Put in v{f,s}printf emulations. + +Mon Oct 2 17:20:42 1989 Richard Stallman (rms at sugar-bombs.ai.mit.edu) + + * ld.c [sequent] (N_TXTADDR, N_DATADDR): Define these macros. + [sequent] (INITIALIZE_HEADER): Define this same as for i386. + (RELOC_MEMORY_SUB_P): New customization macro. + Define as 0 on sun 4. Define as 0 for defaults. + [sequent] (RELOC_* macros): Definitions for sequent. + (decode_command, digest_symbols): Handle case where NMAGIC is undefined. + (enter_global_ref) [sequent]: Handle special N_SHUNDF code. + (digest_symbols, write_symbols) [sequent]: Adjust outheader.a_text. + (perform_relocation): Handle RELOC_MEMORY_SUB_P like ..._ADD_P. + (symtab_init) [sequent]: Define symbol `_387_flt' w/ value 0. + +Fri Sep 22 11:06:25 EDT 1989 hack@ai.mit.edu + + * gprof.c (main) Installed Joy's code for using the long-option names. + +Thu Sep 21 03:24:36 1989 Richard Stallman (rms at sugar-bombs.ai.mit.edu) + + * ld.c (symtab_init) [sun]: Define __DYNAMIC as 0. + (perform_relocation) [RELOC_ADD_EXTRA]: + Special handling for relocatable_output case. + (read_file_relocation): Fix error message typos. + +Mon Sep 18 14:40:40 1989 Richard Stallman (rms at sugar-bombs.ai.mit.edu) + + * ld.c (digest_symbols): Pad data and bss to 8 byte boundary. + +Fri Sep 15 16:42:33 1989 Roland McGrath (mcgrath at paris.Berkeley.EDU) + + * a.out.gnu.h [sony]: Defined SEGMENT_SIZE as 0x2000. + + * Makefile (ranlib): Include getopt in the link. + (objdump): New rule. + (install): New rule. + +Fri Sep 1 01:32:01 1989 Roland McGrath (mcgrath at saffron.Berkeley.EDU) + + * ranlib.c: Added copyright notice and license info. + +Wed Aug 30 20:53:21 1989 Roland McGrath (mcgrath at saffron.Berkeley.EDU) + + * Makefile (LIBS): Also get getopt1.o. + (dist): Include getopt1.c. + + * ranlib.c (main): Move long option array into main, and fixed it so it + will compile. + + * Makefile (dist): Link rather than copy files into the subdir. + Include system.h and getopt.h. + +Thu Aug 24 14:33:48 1989 Randy Smith (randy at hobbes.ai.mit.edu) + + * ld.c (do_file_warnings): Don't print out multiple definition + warnings at references. + +Mon Aug 21 20:19:08 1989 Roland McGrath (roland at hobbes.ai.mit.edu) + + * ar.c (make_new_symdefs): Abort if MAPELT->info.name is nil. + (update_symdefs): In the loop that calls make_new_symdefs, reject + deleted members (i.e., TAIL->info.name == 0). + + * ar.c (make_new_symdefs): Return if we fail reading the header or the + magic number is bad. + + * ar.c (make_new_symdefs): Check for ridiculous string table offsets. + + * ranlib.c (main): If the child ar exits with a signal, print the + signal that killed it. + + * ar.c (make_new_symdefs): Check for a ridiculous string table size. + + * ar.c (write_archive): When fixing up symdefs, use each member's + new_offset rather than data_offset. + + * ar.c (update_symdefs): When the old archive's string table is too + small, die, don't just bitch. + + * ar.c (update_symdefs): Don't decrement num_old_symdefs when deleting + symdefs of deleted members. After compactifying old symdefs, decrease + num_old_symdefs as necessary. + + * ar.c (update_symdefs): When removing symdefs of deleted members, + compare the symdef offsets to the mapelt's data offset, not its header + offset. + +Sat Aug 19 08:07:26 1989 Roland McGrath (roland at hobbes.ai.mit.edu) + + * ar.c (header_from_map): Print correct message when truncating. + + * ar.c (find_mapelt_noerror, header_from_map): Truncate to 15 chars, + not 16. + + * ranlib.c (main): Don't do the first ar run an infinite number of + times. One will suffice. + + * ranlib.c (main): Under -v, echo the ar command before running it. + + * ar.c (ignore_symdef): New variable. If nonzero, only do symdef + processing if the `s' option is given. + (main): If one of the members given on the command line is __.SYMDEF, + set ignore_symdef. + (scan): If ignore_symdef is nonzero, don't set symdef_exists. + + * ar.c (find_mapelt_noerror): A mapelt matches NAME if the names are + the same for 14 characters and either both end in ".o", or they are the + same for as many more characters as they both have (i.e., the longer + name is truncated to the length of the shorter name for the + comparison). + + * ar.c (update_symdefs): When correcting a bad string table size, + correct the size of the __.SYMDEF member as well. + + * ar.c (update_symdefs): Keep track of the space in the string table + accounted for by deleted members. If the old archive's string table + size was too big, correct it; if it was too small, die. + +Tue Aug 15 00:54:37 1989 Roland McGrath (roland at apple-gunkies.ai.mit.edu) + + * ranlib.c (main): Use getopt, and accept new `-v' option, which means + to pass `v' to ar. Use vfork instead of fork. Look at the status of + the children, and exit if it's nonzero. Also added error checking on + several calls. + + * ar.c (write_archive): Fully initialize the new mapelt for __.SYMDEF. + +Sun Aug 13 00:01:27 1989 Joy Kendall (jak at hobbes.ai.mit.edu) + + * strip.c, nm.c, objdump.c: Installed versions with long + named options available, i.e., getopt substituted with + getopt_long. + + * Makefile: changed GNU_GETOPT to GNU_GETOPT_LONG (need to + make sure I did this right) + +Wed Aug 9 00:24:34 1989 Roland McGrath (roland at hobbes.ai.mit.edu) + + * ar.c (write_archive): When rewriting the symdef member, seek to its + header offset, not its data offset. + + * ar.c (header_from_map): New function to convert a `struct mapelt' to + an ar header. + + * ar.c: Miscellaneous cosmetic clean-ups. + + * ar.c (copy_out_member): If truncating the member name, and the + desired name (which is too long) ends in ".o", make the written member + name end in ".o" as well. + +Tue Aug 8 16:26:49 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * ld.c (classify_arg): Added 'V' to the list of possible + argument having thingys. + + * ld.c (do_relocation_warnings, do_file_warnings): Changed + messages slightly and removed hack to allow N_WARNING name to + be a printf string taking stuff as arguments. + +Mon Aug 7 16:27:04 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * ld.c: Created set_element_prefixes (list of symbol name + prefixes to force symbols to be treated as set elements). + (main): Initialized to 0. + (decode_option): -V name recognized as defining a prefix. + (set_element_prefixed_p): New function. + (enter_file_symbols): Check every symbol to see if it has a + prefix; if so, change the type. + (subfile_wanted_p): A subfile isn't wanted if the symbol + definition indicated is a set element by prefix. + + * ld.c [N_WARNING]: Changed commenting. + + * ld.c (do_file_warnings): On the second pass for actual + warning symbols, warn only if it isn't a definition and it + isn't the reference used by the warning symbol itself. + +Fri Aug 4 13:04:57 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * ld.c: Remove the warning field from struct file entry. + (enter_file_symbols): Deal with N_WARNING symbols on a + per symbol basis instead of a per file basis. + (mark_flagged_symbols): Deleted. + (do_warnings): Don't call mark_flagged_symbols anymore. + + * ld.c: Improved commenting on new GNU symbols. + + * ld.c (subfile_wanted_p): Don't load in files for a set + element symbol definition. + +Thu Aug 3 12:54:30 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * ld.c (write_output): Checked return code on last chmod and + output error if there is one. + +Wed Aug 2 13:26:16 1989 Randy Smith (randy at hobbes.ai.mit.edu) + + * ld.c (enter_global_ref): Separate out cases of undefined + reference and defining it versus from stuff done whenever you + define a symbol. Clean up assignment to sp->defined. + +Tue Aug 1 23:12:07 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * ld.c: Created count common_defined_global_count. + (main): Initialized to 0. + (symdef_library): Symbol is needed if it isn't defined OR it's + defined common. Don't stop looking till we don't have any + more symbols defined as common. + (linear_library): Don't stop looking until we have no more + symbols defined as common. + (subfile_wanted_p): If a symbol is defined common, we want to + check for a real definition. If it used to be undefined and + we've defined it by common, increment CDGC. + (enter_global_ref): If a symbol is defined common and it used + to be undefined, increment CDGC. If it is defined for real + and it used to be defined common, decrement CDGC and zero + max_common_size (so we can tell that it isn't defined common + any more). Rewrote logic to be cleaner. + +Mon Jul 31 14:52:25 1989 Randy Smith (randy at hobbes.ai.mit.edu) + + * ld.c (digest_symbols): Reverse the order of the set element + vector so that the elements will be in the order of the input + files to ld. + +Mon Jul 31 00:19:05 1989 Roland McGrath (roland at apple-gunkies.ai.mit.edu) + + * ld.c [sun && sparc]: Don't include alloca.h if !defined(__GNUC__). + +Mon Jul 24 19:29:40 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * ld.c (enter_global_ref): Produce a warning message and + rewrite not to be so dangerous if someone is clueless enough + to indirect a symbol to itself. + +Fri Jul 14 04:04:19 1989 Roland McGrath (roland at hobbes.ai.mit.edu) + + * ar.c (write_archive): Don't zero out the count of old symdefs if we + just read some. + (update_symdefs): Don't copy symdefs onto themselves when compacting. + +Wed Jul 12 16:16:36 1989 Roland McGrath (roland at hobbes.ai.mit.edu) + + * ar.c (write_archive): Use read_old_symdefs if the member is there. + (make_new_symdefs): Return if passed a deleted member. + + * ar.c (write_archive): Zero out the info of the new __.SYMDEF member + properly. + +Mon Jul 10 22:16:08 1989 Randy Smith (roland at hobbes.ai.mit.edu) + + * ld.c (enter_global_ref): Put the type of the nlist entry with the + strongest type into the "defined" field of the symbol. Used this to + determine the first definition of a set element. + +Wed Jul 5 17:29:27 1989 Randy Smith (randy at apple-gunkies.ai.mit.edu) + + * ld.c (do_file_warnings): Find undefined references on second + passes. + +Fri Jun 30 03:32:50 1989 Roland McGrath (roland at apple-gunkies.ai.mit.edu) + + * ar.c [__GNUC__]: Use __builtin_alloca. + [sparc]: Include <alloca.h>. + + * ar.c (copy_out_member): After writing the header, set the entry's + `data_offset' field to the current file position. + (write_archive): After copying out members, check for any whose data + offsets weren't known when the symdef member was written, fix up the + offsets, and rewrite the symdef member. + +Thu Jun 29 21:29:35 1989 Roland McGrath (roland at apple-gunkies.ai.mit.edu) + + * ar.c (main): Call exit (0), instead of running off the end. + +Tue Jun 27 02:11:22 1989 Roland McGrath (roland at hobbes.ai.mit.edu) + + * ar.c (make_new_symdefs): Rewritten to correctly convert the namelist + to symdefs. + + * ar.c (replace_members): Write the archive if symdef_flag is true and + there is no symdef member. + + * Makefile: Added $(archpfx) where appropriate. + + * ld.c (symdef_library): When decode_library_subfile returns nil + (meaning it hit the end of the library archive), barf. + +Mon Jun 26 17:41:38 1989 Randy Smith (randy at hobbes.ai.mit.edu) + + * ld.c (do_warnings): Take out the blank line printed at the + end of the warning routine. + + * ld.c (do_file_warnings): When looking for multiple + definitions, don't print out references (things that are just + N_EXT). + +Sun Jun 25 15:17:42 1989 Roland McGrath (roland at hobbes.ai.mit.edu) + + * ar.c (requestedp): Removed unused function. + (find_mapelt): Use find_mapelt_noerror. + (find_mapelt_noerror): Compare only the first 16 chars of names. + (error): Accept more args. + (copy_out_member): Write a message if the member name is truncated. + + * GNUmakefile: Just include Makefile, rather than doing the silly + recursion bit. + + * ar.c (write_archive): Split unlocking and closing into new function + close_archive. + (replace_members): If nothing changed, don't write the archive! + + * ar.c: Only write old Unix style informational messages + (x - foo, a - bar, etc.). 1003.2 requires it (sigh). + +Wed Jun 21 21:45:57 1989 Roland McGrath (roland at hobbes.ai.mit.edu) + + * ar.c (insert_in_map): Only write one message under -v. + (print_descr): Use puts instead of printf where applicable. + + * ar.c (copy_out_member): Don't say "copying ... to new archive" under + -v. This amounts to listing the entire archive with each insertion. + + * Makefile (ranlib.o): Use $(bindir)/ar rather than `pwd`/ar. + +Tue Jun 20 18:46:42 1989 Roland McGrath (roland at hobbes.ai.mit.edu) + + * ar.c (main): The `u' flag implies the `r' operation. + +Mon Jun 12 19:12:37 1989 Jay Fenlason (hack at apple-gunkies.ai.mit.edu) + + * nm.c Fixed #ifndef N_WARNING to actually compile correctly. + + * ld.c (N_TXTADDR, N_DATADDR): Changed #ifdef vax to + #if defined(vax) || defined(sony_news) + so that it'll compile on the Sony's. + +Tue Jun 6 15:27:10 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * ld.c (print_files_defining_symbol): Eliminated. + (main): Zero the first cmdline references after it is allocated. + (add_cmdline_ref): Make sure that we don't overwrite the end of the + array. + +Sun May 28 15:15:42 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * nm.c (,print_one_symbol): Added code to handle N_WARNING. + +Thu May 11 20:26:23 1989 Mike Haertel (mike at apple-gunkies.ai.mit.edu) + + * ld.c (read_file_symbols): Use a struct exec instead of an int + for magic number checking. + * nm.c (do_one_file): Similar change. + * size.c (do_one_file): Similar change. + * strip.c (file_open): Similar change. + +Thu May 11 16:33:51 1989 Richard Stallman (rms at sugar-bombs.ai.mit.edu) + + * ar.c (update_symdefs): Detect null pointer in info.name. + +Thu May 4 01:53:32 1989 Richard Stallman (rms at sugar-bombs.ai.mit.edu) + + * Makefile: Provide $(LIBS) when linking strip. + +Mon May 1 16:40:27 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * ld.c (main): Change return from main to be exit from main + (supported on more systems). + +Mon Apr 24 14:47:53 1989 Richard Stallman (rms at sugar-bombs.ai.mit.edu) + + * ld.c (decode_option, classify_arg): Accept -Bstatic as no-op. + +Mon Apr 24 13:08:48 1989 Jay Fenlason (hack at apple-gunkies.ai.mit.edu) + + * gprof.c: Removed #ifdef __STDC__ stuff from all the + function headers, etc. + +Sun Apr 23 00:16:09 1989 Richard Stallman (rms at sugar-bombs.ai.mit.edu) + + * ld.c: Define INITIALIZE_HEADER for ALTOS. + + * robotussin.c (reloc_segment): Handle R_RELLONG like R_DIR32. + (INPUT_MAGIC): Set it for 386 or 68k according to predef symbols. + + * ranlib.c: Handle USG. Define bzero, gettimeofday. + New macro `seconds' defined for USG or BSD. + Include sys/types.h and fcntl.h. + +Thu Apr 20 14:47:37 1989 Richard Stallman (rms at sugar-bombs.ai.mit.edu) + + * robotussin.c: discard symbols in section -1. + + * libconvert: change *.o to * in for statement. + +Mon Apr 10 16:54:43 1989 Pace Willisson (pace at apple-gunkies.ai.mit.edu) + + * strip.c (rewrite_file_symbols): Make strip work for + COFF_ENCAPSULATE (it used to write the new exec header at + file offset 0, instead of using HEADER_SEEK_FD) + +Fri Mar 10 15:50:45 1989 Randall Smith (randy at sugar-bombs.ai.mit.edu) + + * ranlib.c (touch): Created to simply touch an archive (update the + date on the symdef member). Done here since adding an option to + ar.c would be a hassle and using the routines in ar.c would + require doing almost all of the work of an "ar rs x.a" anyway. + Same result in both cases; differing amounts of time. + +Mon Mar 6 15:27:56 1989 Jay Fenlason (hack at apple-gunkies.ai.mit.edu) + + * gprof.c (ck_fclose) only fflush() streams opened for writing. + +Sun Mar 5 17:13:37 1989 Randall Smith (randy at gluteus.ai.mit.edu) + + * ar.c (write_archive): Modified test to write symdef header; + wasn't being done if the symdef map entry didn't need to be newly + created. + +Fri Mar 3 10:56:55 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * *.*, Makefile: Changed to use new wording as directed by the new + GNU General Public License. + * COPYING: Created as a link to /gp/rms/COPYING. + +Wed Feb 22 04:42:54 1989 Richard Stallman (rms at sugar-bombs.ai.mit.edu) + + * Makefile: Use GNU getopt by preference. + + * gprof.c [USG]: Define bcopy as macro. + +Tue Feb 21 04:46:44 1989 Richard Stallman (rms at sugar-bombs.ai.mit.edu) + + * ld.c (INITIALIZE_HEADER) [HPUX]: Use N_SET_MACHTYPE. + +Sat Feb 18 12:47:36 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * Makefile: Added note for using GNU getopt for app routines. + Linked (symlink) it into this directory. + + * ld.c: Added #ifndef sony_news around inclusion of fcntl.h. This + file duplicates code in sys/file.h on the sony. + + * nm.c: Added include of alloca.h on a sun4 and use of + __builtin_alloca if compiling with GCC. + +Sat Feb 18 09:43:51 1989 Richard Stallman (rms at sugar-bombs.ai.mit.edu) + + * ar.c (write_archive): Don't die if info.name is 0 (member deleted). + (scan): Reverse fread args, so value is right. + +Fri Feb 17 05:19:50 1989 Richard Stallman (rms at sugar-bombs.ai.mit.edu) + + * ar.c (extract_members, scan): Open just a FILE *, and pass that. + (extract_member, print_contents): Expect a FILE *; no need to fdopen. + +Thu Feb 16 07:36:03 1989 Richard Stallman (rms at sugar-bombs.ai.mit.edu) + + * gprof.c: Reformatted. + +Fri Feb 3 14:28:24 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * ld.c (do_warnings): Changed calls to each_full_file to each_file + (which is what we want, since we are concerned with symbol + definitions). + + * ld.c: Added a parameter MAX_ALIGNMENT, defined in #ifdef sparc + and by default, to set the maximum necessary alignment for data + objects on this machine. This is necessary for allocation in the + bss area. + (digest_symbols): Made sure that everything allocated had the + minimum of it's alignment (lowest bit set in the size) or the + MAX_ALIGNMENT. + +Thu Jan 26 13:31:52 1989 Pace Willison (pace at apple-gunkies.ai.mit.edu) + + * Makefile: Delete -Dnounderscore, add gprof to USG PROG list + + * ar.c (rename for USG): ignore error from first unlink + + * gprof.c: COFF_ENCAPSULATE and USG changes. + + * ld.c strip.c: Deal with internal labels starting with '.' on + nounderscore machines and 'L' on normal ones (LPREFIX). + + * ld.c(next_debug_entry): Mask n_type field in switch statement + since it is a char, and some of its values (N_SOL) are > 128, which + get sign extended on some machines. + + * objdump.c: Deal with symbols that have no name; mask fields + of nlist before printing. + +Mon Jan 23 14:08:43 1989 Randall Smith (randy at plantaris.ai.mit.edu) + + * ld.c (coptxtrel, copdatrel): Made sure that, when relocation is + being copied, that N_INDR symbols were properly followed. Also + made sure that the symbol indicies were correct even in the + presence of indirection information. + + * ld.c (write_rel): When specifying symbol numbers, make sure to + leave room for the extra undefined ref that will be written for + the sake of N_INDR entries. + + * ld.c (write_syms): Fixed typo; a duplicate of the N_INDR entry + was being written instead of the undefined ref required. + +Tue Jan 17 16:23:56 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * strip.c (rename): Arranged to ignore return code from "unlink"; + file may not be there. + +Thu Jan 12 15:24:23 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * Makefile: Modified "dist" target so as to get hp-* + subdirectories. + + * strip.c (rename): Added function to mimic BSD system call for + system V. + +Tue Jan 10 19:44:33 1989 Pace Willison (pace at prep.ai.mit.edu) + + * Makefile: put -Dnounderscore back for COFF_ENCAPSULATE. + +Tue Jan 10 17:58:50 1989 Randall Smith (randy at cream-of-wheat.ai.mit.edu) + + * ld.c (linear_library, symdef_library, list_file_locals, + write_file_syms): Took care to make sure that when the buffer + pointed to by entry->strings became invalid, entry->strings was + set to zero (either on a free or a function return if space had + been allocated through alloca). + + * ld.c (getpagesize): Deleted define in hpux dependent section; + taken care of when compiling on hpux because of define of USG in + Makefile. + +Mon Jan 9 22:49:42 1989 Richard Stallman (rms at sugar-bombs.ai.mit.edu) + + * strip.c (strip_file): Effectively defer certain signals + while rewriting the file. + +Mon Jan 9 09:45:41 1989 Pace Willisson (pace at prep.ai.mit.edu) + + * These changes improve support for COFF_ENCAPSULATE, and + for the future development of the GNU exec header. The + main change is to rename the exec header field a_magic + so that it can contain additional information. Now, the + magic number must be accessed with N_MAGIC(exec), and set + with N_SET_MAGIC(exec,val). Programs that only need to use + N_BADMAG will not have to change. Also COFF_ENCAPSULATE will + no longer use "nounderscore", so that it will be more like + normal bsd systems. This means gcc must be updated before using + these new tools. (It is safe to put -Dnounderscore back in + if you want to use an old gcc for a while.) + + * README-ENCAP: new documentation for how to set up to use + COFF_ENCAPSULATE. + + * Makefile: Add target for gnulib. + Don't define 'nounderscore' (must tell gcc about that too.) + + * ar.c: Don't automatically define COFF_ENCAPSULATE. + + * nm.c: No need to initialize the header before reading it. + + * size.c: Include, sys/types.h, and sys/fcntl.h if USG + * strip.c: Likewise--types.h before file.h. + + * ld.c, objdump.c, robotussin.c: + Changed to use new exec macros for a_magic. + + * libconvert: now takes arguments + + * robotussin.c: don't automatically define nounderscore - leave + that to the makefile, if desired + +Fri Jan 6 13:06:37 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * ld.c (digest_symbols, write_text, write_data): Changed N_SETV + from being in the text area to being in the data area. + * nm.c, a.out.gnu.h: Changed comments to conform with above. + + * ld.c (do_file_warnings): Added loop to go through each external + nlist entry and check for multiple definitions, as well as + catching any references which weren't caught by the relocation + pass. + + * ld.c (address_to_line): Changed so that it will work (albeit + slowly) with an address less than the current address. + (do_file_warnings, do_relocation_warnings): Broke out scan through + relocation entries into a separate function. Added creation of a + bitvector with each bit refereing to an nlist entry so that + do_relocation_warnings could mark which symbol entries it had + output for undefined references. + +Thu Jan 5 20:36:44 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * ld.c: Cosmetic changes: Moved helper functions for + do_file_warnings to before it and modified comments to + next_debug_symbol and address_to_line. + +Wed Jan 4 15:37:52 1989 Randall Smith (randy at gluteus.ai.mit.edu) + + * ld.c: Added #define getpagesize() EXEC_PAGESIZE for hpux. + + * ld.c (do_warnings): Will now print out all undefined external + symbols which were not referenced from the text or data sections + separately. + (do_file_warnings): Decreases undefined_global_sym_count for each + symbol printed. + +Tue Jan 3 23:43:41 1989 Richard Stallman (rms at sugar-bombs.ai.mit.edu) + + * Makefile (nm): Link with $(LIBS). + + * ld.c (getpagesize): HPUX definition deleted; not needed. + +Mon Jan 2 23:04:35 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * ld.c (digest_symbols): Modified the creation of set vectors + to include a zero word after the end of the vector. + (write_text): Made the same modification of the set vector section + size. + +Sun Jan 1 12:01:22 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * ld.c: Fixed typo in description of relocation values, and + changed note on RELOC_ADD_EXTRA to force is to be an lvalue if + it's defined. + (coptxtrel): If a specific relocation entry has just changed from + external to internal, and we aren't supposed to add in memory on + the new relocation value, make sure that the value of the symbol + get's added to the ADD_EXTRA in the relocation value. Otherwise, + all the work we do in a partial linking will be wasted (will be in + memory, but will be ignored on the next pass of the linker). + +Sat Dec 31 13:13:01 1988 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * ld.c: Removed "|| TARGET == SUN2" from line 210; I believe that + TARGET and SUN2 were both showing up defined as 0 on the sparc, + which resulted in a redefine of the INITIALIZE_HEADER macro, back + to sun2 style. + +Thu Dec 29 01:48:03 1988 Richard Stallman (rms at sugar-bombs.ai.mit.edu) + + * ld.c, ar.c: Don't define COFF_ENCAPSULATE automatically. + The recommended Makefile change defines it. + + * ld.c (alloca): If compiling with GCC, use __builtin_alloca. + + * robotussin.c: New reformatted version with all variables renamed. + + * ranlib.c: New file, just runs `ar rs' on each specified file. + * Makefile: Special hack to tell ranlib where to find GNU ar. + (LIBS): Recommend -lPW on USG; ld needs it for alloca (if not GCC). + +Sat Dec 24 13:59:09 1988 Richard Stallman (rms at sugar-bombs.ai.mit.edu) + + * ld.c (error): Start with name of program running. + (main): Set `progname' to that name. + (digest_symbols): Fix punctuation and spelling in calls to `error'. + +Tue Dec 20 21:49:46 1988 Richard Stallman (rms at sugar-bombs.ai.mit.edu) + + * robotussin.c (INPUT_MAGIC): New macro, has the magic number + to expect in input files. + (nounderscore): New macro; as in ld, define it to inhibit + adding underscore to symbols. + + * Makefile: Don't compile objdump on BSD; N_DATADDR causes trouble. + +Tue Dec 20 14:57:38 1988 Pace Willisson (pace at prep.at.mit.edu) + + * objdump.c: New program like the system 5 'dump' program. + Documentation will follow... + + * Makefile: Set up CFLAGS for USG systems. Added target + libc.a to do robotussin conversion. Added objdump. + + * libconvert: Wrote shell script to do robotussin conversion. + + * ar.c, ld.c: Don't define COFF_ENCAPSULATE if it is already defined. + + * ld.c: If i386, set a_machtype to M_386. Use a_flags instead + of a_encap. Don't compute coff header if it isn't going to + get written out. + + * robotussin.c: Define COFF_ENCAPSULATE. Include a.out.encap.h + instead of a.out.h. Check magic number of input + file. Skip over optional header, if present. Don't ignore + symbols with aux entries (they could be function definitions), + instead, ignore symbols beginning with '.' (.text, etc). + Don't prepend underscore to externals, since gcc doesn't do + it now. Don't run past the end of symbols that are exactly + eight characters long. Always write the string table size, + even if it is empty. Change relocation types handled from + R_PCRBYTE, etc, to R_DIR32 and R_PCRLONG (these are the + only two emitted by the system 5 assembler.) + + * size.c: Include <sys/types.h> so including sys/file.h will + not get an error on USG systems. Include fcntl.h on usg systems. + + * strip.c: Move inclusion of file.h to after types.h. Include + fcntl.h. Add defintion of rename. + +Fri Dec 16 13:55:11 1988 Richard Stallman (rms at sugar-bombs.ai.mit.edu) + + * size.c: Delete all ROBOTUSSIN conditionals and contents. + Change SYSV conditionals to USG. + COFF_ENCAPSULATE conditionals for headers. + (do_one_file, read_header): Skip encapsulation headers if any. + + * strip.c: Delete all ROBOTUSSIN conditionals and contents. + Change SYSV conditionals to USG. + COFF_ENCAPSULATE conditionals for headers. + (file_open, read_header): Skip encapsulation headers if any. + + * strip.c: Change most fatal errors to nonfatal. + (file_open, read_header, read_{file,entry}_symbols): + Now return 0 for success, -1 for failure. + Failure means do no more for the current file. + (modify_relocation): Now just warn if strip a symbol needed + for relocation, and warn only once per file. + (error_with_file): New function, replaces most fatal_with_file. + Print filename first, as in most programs. + (fatal_with_file): Deleted. + (rewrite_file_symbols): Use perror_file when system call fails. + +Tue Dec 13 17:16:39 1988 Jay Fenlason (hack at apple-gunkies.ai.mit.edu) + + * ar.c: Changed pad character after odd-length archive member + from \0 to \n so archives can be cmp'd with the output from /bin/ar + Added fix for when ranlib is using ar to insert an __.SYMDEF member + +Tue Dec 13 09:09:27 1988 Richard Stallman (rms at sugar-bombs.ai.mit.edu) + + * ar.c: conditional #includes for USG. + * COFF_ENCAPSULATE conditionals for headers. + (extract_member): Don't do fchmod if USG. + Alternate USG code to set modtimes of extracted members. + (write_archive): Don't do fsync if USG. + (make_new_symdefs): Skip encapsulation headers if any. + [USG] (bcopy, bzero, bcmp): New fns. + + * nm.c: Delete all ROBOTUSSIN conditionals and contents. + Include types.h. + Change SYSV conditionals to USG. + * COFF_ENCAPSULATE conditionals for headers. + (do_one_file): Skip encapsulation headers if any. + (read_header): Likewise. + + * ld.c: Delete all ROBOTUSSIN conditionals and contents. + Change SYSV conditionals to USG. + Change HEADER_TYPE back to `struct exec'. + (L_SET): Define it if headers don't. + * COFF_ENCAPSULATE conditionals for headers. + (main): Update text_size differently if encapsulating. + (write_header): Write the encapsulation headers if nec. + Don't end with padding if encapsulation being done. + [USG] (bzero, bcopy, getpagesize): New fns. + +Tue Dec 6 13:26:56 1988 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * ld.c (do_file_warnings): Ignored text relocation entries that + went through local symbols; any problems with lack of definitions + etc. with them would have been caught by the compiler. + +Mon Dec 5 16:13:22 1988 Jay Fenlason (hack at sugar-bombs.ai.mit.edu) + + * ar.c (make_new_symdefs): On error, close the input files. + +Thu Nov 10 18:15:07 1988 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * ld.c: Put declaration of alloca inside an #ifdef so that it + wouldn't mess up on the sparc. + + * ld.c: Added #define CORE_ADDR for include of symseg.h from gdb + and took out TARGET == SUN2 for sun2 INITIALIZE_HEADER. + +Wed Nov 2 18:43:09 1988 Randall Smith (randy at gluteus.ai.mit.edu) + + * ld.c: Merged in isi68k port. This included a kludge for symbols + starting with _$ (#ifdef DOLLAR_KLUDGE) and addition of the + STANDARD_SEARCH_DIRS macro to override the default if it's + defined. + + * ld.c: Added code for the N_WARNING symbol type. If a reference + is found to a symbol in an input .o file which contains an + N_WARNING symbol, a warning message (the name of the N_WARNING + symbol) is printed. This name is treated as a printf format + string; the name of the symbol referenced (which caused the + warning) is supplied as a single argument to the print which + interpets this string. + +Tue Nov 1 16:57:00 1988 Randall Smith (randy at gluteus.ai.mit.edu) + + * ld.c: Added code for Sun 2. + + * ld.c: Modified access to the relocation information to be *much* + more general; added in sparc support. This change is a minor + performance hit; the perform_relocation routine uses about 0.1 + seconds more time on linking gdb than did the original ld. + (perform_relocation is about 5% of the total time the loader + spends). The price of generality. + +Thu Aug 4 13:20:50 1988 Randy Smith (randy at rice-chex.ai.mit.edu) + + * Modified ld.c to print only the first 10 unresolved references + for each symbol, followed by a message indicating that there are + more unresolved references that have not been printed (if indeed + there are). Made default behaivior upon errors *not* writing any + output file at all. Also added the -noinhibit-exec flag to force + writing of an executable when that was desirable. + +Tue Aug 2 12:04:01 1988 Randy Smith (randy at rice-chex.ai.mit.edu) + + * Modified ld.c to give line numbers wherever possible on + unreferenced symbols. Added a new symbol (N_DSLNE) to allow for + the same mapping of data location to line number as is done for + text segments by N_SLINE. Added code to sort the relocation + entries when it is necessary to output these line numbers. The + assumption was made that both N_SLINE and N_DSLNE symbols would + always be in order by address. + +Wed Jul 27 15:13:08 1988 Randy Smith (randy at rice-chex.ai.mit.edu) + + * Modified ld.c to include a facility for equivalencing two + symbols (translating one to another). Modified lib/a.out.h to + include a definition of this new symbol. Modified nm.c to + recognize this symbol and all of the set element and vector + symbols I had added before. + +Thu Jul 21 17:06:10 1988 Randy Smith (randy at rice-chex.ai.mit.edu) + + * Modified ld.c to printout source file and line numbers for + unresolved references whenever possible (ie. whenever the input + file has debugger symbols and the reference is from the text area). + +Wed Jul 13 17:21:33 1988 Randy Smith (randy at frosted-flakes.ai.mit.edu) + + * Modified ld.c and a.out.h to handle new types of symbols; the + loader can now create "sets" of symbols from entries in its input + files. See a.out.h for more info. Also fixed a bug in ld in + which references to common areas that we not defined in one pass + of the loader caused errors on the next. + +Sat Jul 2 00:05:44 1988 Richard Stallman (rms at sugar-bombs.ai.mit.edu) + + * ld.c (symdef_library): Error check was off by one. + +Mon May 9 12:53:08 1988 Chris Hanson (cph at kleph) + + * ar.c (replace_members): After updating map, write out + `change_map->next' rather than `map', since the latter may be + null. + +Local Variables: +mode: indented-text +left-margin: 8 +version-control: never +End: diff --git a/binutils-1.9/Makefile b/binutils-1.9/Makefile new file mode 100644 index 0000000..8a4beda --- /dev/null +++ b/binutils-1.9/Makefile @@ -0,0 +1,167 @@ +# Makefile for GNU binary-file utilities +# Select a set of CFLAGS and PROGS, below, depending on the system type. +# Copyright (C) 1989, 1990 Free Software Foundation, Inc. +# +# This file is part of the GNU binutils. +# +# The GNU binutils are free software; you can redistribute them and/or modify +# them 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. +# +# The GNU binutils are distributed in the hope that they 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 the GNU binutils; see the file COPYING. If not, write to +# the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + +# gprof.c contains `void *' and very long string constants which many +# C compilers can't handle, so you might need gcc to compile it. +GNUCC = gcc -O +# But you might need to use your old C compiler to compile the other +# programs (ld at least), if you can't run gcc without them +# (particularly on COFF systems). +#CC = cc + +bindir=/usr/local/gnubin + +# For BSD: +# For SunOS 4.x, take out -DVPRINTF_MISSING. +CFLAGS = -g -DVPRINTF_MISSING +# Don't add robotussin; it won't compile on BSD or GNU systems. +# objdump is not here because it (at least used to) not compile +# on most systems (trouble with N_DATADDR). Some of those problems +# have been fixed, though. +PROGS = $(archpfx)gprof $(archpfx)ld $(archpfx)size \ + $(archpfx)nm $(archpfx)strip $(archpfx)ar $(archpfx)ranlib + +# For USG systems using COFF_ENCAPSULATE: +# Also, you will want to make the target libc.a (but it takes a long time). +# Note that you should leave a copy of `ar' in this directory +# after you install it, since `ranlib' will try to run it from here. +#CFLAGS=-g -DUSG -DCOFF_ENCAPSULATE -DPORTAR -DNON_NATIVE -DSYS_SIGLIST_MISSING +#PROGS = ld size nm strip ar robotussin objdump ranlib gprof +#SIGNAME = $(archpfx)signame.o +# On ALTOS systems, add -DALTOS to CFLAGS. + +#It's better to move a copy of alloca into your libc than to risk getting some +#incompatible functions from -lPW (like index()), but if you +#want to be lazy, uncomment this line. +#ALLOCALIBS = -lPW + +# For HP-UX: +# Don't add `robotussin'; use `hpxt' instead. +# Note that you should leave a copy of `ar' in this directory +# after you install it, since `ranlib' will try to run it from here. +#CFLAGS = -g -Ihp-include -DUSG -DNON_NATIVE -DVPRINTF_MISSING +#PROGS = ld size nm strip ar ranlib +#ALLOCALIBS = alloca.o + +# For the NeXT: +# Set GNUCC = cc -O. +# Put -DMACH_O in CFLAGS, take out -DVPRINTF_MISSING. +# Comment out MALLOC below to use the system's malloc(). + +# For Sun386: +# Compile with -DPORTAR -DCOFF_ENCAPSULATE. +# Depending on how you configure gcc, you might also want -Dnounderscore, +# though I did not wind up using it. + +# If you run out of stack space while running GNU ar or GNU ld (this +# manifests itself as a segment violation), you should link in alloca.c +# from the gcc sources, and get rid of the "#define alloca" in ar.c and +# ld.c; or you could try to get Sun to fix this annoying "feature". +#CFLAGS = -g -DCOFF_ENCAPSULATE -DPORTAR +#PROGS = ld size nm strip ar robotussin objdump ranlib gprof + +# nm tries to malloc enough space for the string table. The old GNU malloc +# rounds this up to a power of two (e.g. 5M becomes 8M), and so it might +# fail unnecessarily. I've also seen some Unix malloc's fail, even when +# there is enough memory. So use the new GNU malloc. +MALLOC = $(archpfx)gmalloc.o + +GNU_GETOPT = $(archpfx)getopt.o +GNU_GETOPT_LONG = $(archpfx)getopt.o $(archpfx)getopt1.o + +# C++ demangler +CPLUS_DEM = $(archpfx)cplus-dem.o + +LIBS=$(ALLOCALIBS) $(SIGNAME) + +all: $(PROGS) + +$(archpfx)ld: $(archpfx)ld.o $(GNU_GETOPT_LONG) $(CPLUS_DEM) $(LIBS) +# LIBS is used here since ld needs to use alloca. +# Alternatively, compile it with GNU C--then the compiler handles alloca. + $(CC) $(CFLAGS) -o $(archpfx)ld $(archpfx)ld.o $(GNU_GETOPT_LONG) $(CPLUS_DEM) $(LIBS) + +$(archpfx)size: $(archpfx)size.o $(LIBS) + $(CC) $(CFLAGS) -o $(archpfx)size $(archpfx)size.o + +$(archpfx)nm: $(archpfx)nm.o $(GNU_GETOPT_LONG) $(CPLUS_DEM) $(MALLOC) $(LIBS) + $(CC) $(CFLAGS) -o $(archpfx)nm $(archpfx)nm.o \ + $(GNU_GETOPT_LONG) $(CPLUS_DEM) $(LIBS) $(MALLOC) + +$(archpfx)strip: $(archpfx)strip.o $(archpfx)error.o $(GNU_GETOPT_LONG) $(LIBS) + $(CC) $(CFLAGS) -o $(archpfx)strip $(archpfx)strip.o $(archpfx)error.o $(GNU_GETOPT_LONG) $(LIBS) + +$(archpfx)ar: $(archpfx)ar.o $(LIBS) + $(CC) $(CFLAGS) -o $(archpfx)ar $(archpfx)ar.o $(LIBS) + +$(archpfx)gprof: $(archpfx)gprof.o $(GNU_GETOPT_LONG) $(CPLUS_DEM) $(LIBS) + $(GNUCC) $(CFLAGS) -o $(archpfx)gprof $(archpfx)gprof.o \ + $(GNU_GETOPT_LONG) $(CPLUS_DEM) $(LIBS) +$(archpfx)gprof.o: gprof.c gmon.h + $(GNUCC) $(CFLAGS) -c gprof.c $(OUTPUT_OPTION) + +$(archpfx)ranlib: $(archpfx)ranlib.o $(GNU_GETOPT_LONG) $(LIBS) + $(CC) $(CFLAGS) -o $(archpfx)ranlib $(archpfx)ranlib.o $(GNU_GETOPT_LONG) $(LIBS) +$(archpfx)ranlib.o: ranlib.c + $(CC) -c $(CFLAGS) -DAR_PROG=\"$(bindir)/ar\" ranlib.c $(OUTPUT_OPTION) + +$(archpfx)objdump: $(archpfx)objdump.o $(archpfx)error.o $(GNU_GETOPT_LONG) a.out.gnu.h $(LIBS) + $(CC) $(CFLAGS) -o $(archpfx)objdump $(archpfx)objdump.o $(archpfx)error.o \ + $(GNU_GETOPT_LONG) $(LIBS) + +# Robotussin is NOT part of `all'. +$(archpfx)robotussin: $(archpfx)robotussin.o $(LIBS) + $(CC) $(CFLAGS) -o $(archpfx)robotussin $(archpfx)robotussin.o + +libc.a: $(archpfx)robotussin libconvert $(archpfx)ar + libconvert /lib/libc.a libc.a + +# usg-gnulib is the file gcc makes using the usg compiler +gnulib: $(archpfx)robotussin libconvert usg-gnulib $(archpfx)ar + libconvert usg-gnulib gnulib + +clean: + -rm -f *.o core + -rm -f $(archpfx)*.o + -rm -f $(PROGS) objdump + +DISTNAME = binutils-1.9 +dist: + rm -rf $(DISTNAME) + mkdir $(DISTNAME) $(DISTNAME)/hp-bin $(DISTNAME)/hp-include + ln COPYING ChangeLog Makefile README README-ENCAP \ + a.out.encap.h a.out.gnu.h ar.c cplus-dem.c \ + error.c getopt.c getopt.h getopt1.c gmalloc.c \ + gmon.h gprof.c gprof.texinfo ld.c libconvert nm.c objdump.c \ + ranlib.c ranlib.h robotussin.c signame.c signame.h size.c \ + stab.def stab.h strip.c symseg.h $(DISTNAME) + cd hp-bin; ln Makefile chatr.c hpxt.c ioutil.c ioutil.h \ + mkhplib ../$(DISTNAME)/hp-bin + cd hp-include; ln a.out.h stab.def stab.h ../$(DISTNAME)/hp-include + tar chZf $(DISTNAME).tar.Z $(DISTNAME) + rm -rf $(DISTNAME) +.PHONY: dist + +.PHONY: install +install: $(PROGS) + for file in $(PROGS); do \ + cp $$file $(bindir)/$${file}.new; \ + mv $(bindir)/$${file}.new $(bindir)/$$file; \ + done diff --git a/binutils-1.9/README b/binutils-1.9/README new file mode 100644 index 0000000..e27720c --- /dev/null +++ b/binutils-1.9/README @@ -0,0 +1,38 @@ +These files are some GNU utilities for operating on binary files. +GNU make, which used to be included here, is now distributed +in a separate tar file. + +Report bugs in these programs to bug-gnu-utils@prep.ai.mit.edu. +Please note in the bug report which version of the binutils you are +using. Prior to version 1.7, the versions were identified by the top +line of the ChangeLog. + +Define USG with `-DUSG' when compiling these programs to run on system V. + +COFF is not supported, but we do support a way of encapsulating GNU +executable files with COFF headers. Use -DCOFF_ENCAPSULATE when you +compile, to enable this feature. + +In order to use encapsulation, you must use entirely GNU tools, +including these plus GAS, GCC and GDB. You will need to convert the +system libraries to BSD object file format. Use the shell script +libconvert (which uses robotussin) for that. + +The GNU version of ld has some interesting features: + +1. Undefined and multiply-defined global symbol errors +are now associated with specific source files and line numbers, +and printed in a format M-x next-error can parse. + +2. Normally no output is written if there are serious errors. +Use the option `-noinhibit-exec' if you want an output file anyway. + +3. Global symbols can be defined by indirection to other symbols. +See comments at definition of N_INDR in ld.c + +4. LD can accumulate sets of related values from all the object files +that are being linked together, and put them into a vector that can +be accessed at run time. Thus, you can arrange for each file to have +initializations to be run when your `main' function sees fit, without +having to know the names of all the files that are linked together. +See comments at definition of N_SETA, etc., in ld.c. diff --git a/binutils-1.9/README-ENCAP b/binutils-1.9/README-ENCAP new file mode 100644 index 0000000..0b171c1 --- /dev/null +++ b/binutils-1.9/README-ENCAP @@ -0,0 +1,55 @@ +1/3/89 Pace Willisson + +Here is what to do to bring up gcc & tools on an 80386 running system 5.3 +(if you are adventurous and want to use the latest and greatest tools. +If you want it to be easy, check back in a couple of months.) + +All of these instructions assume you are using the usg compiler. After +you get to the end, you could start over using the gnu compiler, but +that may not work yet ... + +First make gcc, cpp, and gnulib: + + cd .../gcc + config.gcc i386-sysv-gas + make install + +(The i386gnu type was added a little after gcc-1.32) +(the install part doesn't really work - you will have to look at +what it tries to do, and do it by hand.) + +Now, make the gnu assembler: + + cd .../gas + make a386 + cp a386 /usr/local/lib/gcc-as + +Now, make the other utilities: + + cd .../binutils + +edit Makefile to turn on the CFLAGS for USG using COFF_ENCAPSULATE + + make + cp ld /usr/local/lib/gcc-ld + +Put the other programs (size nm strip ar objdump and ranlib) somewhere +handy, but don't clobber your usg programs. I put them all in +/usr/gnu/gnucomp, and I have this shell script in my path under the name "gnu": + + exec /usr/gnu/gnucomp/$* + +That way, I can say "gnu nm a.out", etc. + +Convert the libraries to the encapsulated format: + + make libc.a + cp libc.a /usr/local/lib/gnu/libc.a + + cp .../gcc/gnulib usg-gnulib + make gnulib + cp gnulib /usr/local/lib/gcc-gnulib + + robotussin /lib/crt0.o /usr/local/lib/gcc-crt0.o + +Now, you should be able to use 'gcc' to compile programs. diff --git a/binutils-1.9/a.out.encap.h b/binutils-1.9/a.out.encap.h new file mode 100644 index 0000000..1a6449c --- /dev/null +++ b/binutils-1.9/a.out.encap.h @@ -0,0 +1,134 @@ +/* Another try at encapsulating bsd object files in coff. + Copyright (C) 1988, 1989, Free Software Foundation, Inc. + Written by Pace Willisson 12/9/88 + + This file 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 file 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 file; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* + * This time, we will only use the coff headers to tell the kernel + * how to exec the file. Therefore, the only fields that need to + * be filled in are the scnptr and vaddr for the text and data + * sections, and the vaddr for the bss. As far as coff is concerned, + * there is no symbol table, relocation, or line numbers. + * + * A normal bsd header (struct exec) is placed after the coff headers, + * and before the real text. I defined a the new fields 'a_machtype' + * and a_flags. If a_machtype is M_386, and a_flags & A_ENCAP is + * true, then the bsd header is preceeded by a coff header. Macros + * like N_TXTOFF and N_TXTADDR use this field to find the bsd header. + * + * The only problem is to track down the bsd exec header. The + * macros HEADER_OFFSET, etc do this. Look at nm.c, dis.c, etc + * for examples. + */ + +#if !defined (A_OUT_ENCAP_H) +#define A_OUT_ENCAP_H 1 + +#include "a.out.gnu.h" + +#define N_FLAGS_COFF_ENCAPSULATE 0x20 /* coff header precedes bsd header */ + +/* Describe the COFF header used for encapsulation. */ + +struct coffheader +{ + /* filehdr */ + unsigned short f_magic; + unsigned short f_nscns; + long f_timdat; + long f_symptr; + long f_nsyms; + unsigned short f_opthdr; + unsigned short f_flags; + /* aouthdr */ + short magic; + short vstamp; + long tsize; + long dsize; + long bsize; + long entry; + long text_start; + long data_start; + struct coffscn + { + char s_name[8]; + long s_paddr; + long s_vaddr; + long s_size; + long s_scnptr; + long s_relptr; + long s_lnnoptr; + unsigned short s_nreloc; + unsigned short s_nlnno; + long s_flags; + } scns[3]; +}; + +/* Describe some of the parameters of the encapsulation, + including how to find the encapsulated BSD header. */ + +#ifdef i386 +#define COFF_MAGIC 0514 /* I386MAGIC */ +#endif +#ifdef m68k +#define COFF_MAGIC 0520 /* MC68MAGIC */ +#endif + +#ifdef COFF_MAGIC +short __header_offset_temp; +#define HEADER_OFFSET(f) \ + (__header_offset_temp = 0, \ + fread ((char *)&__header_offset_temp, sizeof (short), 1, (f)), \ + fseek ((f), -sizeof (short), 1), \ + __header_offset_temp==COFF_MAGIC ? sizeof(struct coffheader) : 0) + +#define HEADER_OFFSET_FD(fd) \ + (__header_offset_temp = 0, \ + read ((fd), (char *)&__header_offset_temp, sizeof (short)), \ + lseek ((fd), -sizeof (short), 1), \ + __header_offset_temp==COFF_MAGIC ? sizeof(struct coffheader) : 0) + + +#else +#define HEADER_OFFSET(f) 0 +#define HEADER_OFFSET_FD(fd) 0 +#endif + +#define HEADER_SEEK(f) (fseek ((f), HEADER_OFFSET((f)), 1)) +#define HEADER_SEEK_FD(fd) (lseek ((fd), HEADER_OFFSET_FD((fd)), 1)) + + +/* Describe the characteristics of the BSD header + that appears inside the encapsulation. */ + +#undef _N_HDROFF +#undef N_TXTADDR +#undef N_DATADDR + +#define _N_HDROFF(x) ((N_FLAGS(x) & N_FLAGS_COFF_ENCAPSULATE) ? \ + sizeof (struct coffheader) : 0) + +/* Address of text segment in memory after it is loaded. */ +#define N_TXTADDR(x) \ + ((N_FLAGS(x) & N_FLAGS_COFF_ENCAPSULATE) ? \ + sizeof (struct coffheader) + sizeof (struct exec) : 0) +#define SEGMENT_SIZE 0x400000 + +#define N_DATADDR(x) \ + ((N_FLAGS(x) & N_FLAGS_COFF_ENCAPSULATE) ? \ + (SEGMENT_SIZE + ((N_TXTADDR(x)+(x).a_text-1) & ~(SEGMENT_SIZE-1))) : \ + (N_TXTADDR(x)+(x).a_text)) +#endif /* a.out.encap.h not already included. */ diff --git a/binutils-1.9/a.out.gnu.h b/binutils-1.9/a.out.gnu.h new file mode 100644 index 0000000..2e7cd75 --- /dev/null +++ b/binutils-1.9/a.out.gnu.h @@ -0,0 +1,272 @@ +#ifndef __A_OUT_GNU_H__ +#define __A_OUT_GNU_H__ + +#if defined(sequent) && defined(i386) +#define a_magic a_info +#include <a.out.h> +#undef a_magic +#define __STRUCT_EXEC_OVERRIDE__ +#define N_NLIST_DECLARED +#define N_RELOCATION_INFO_DECLARED +#endif + +#define __GNU_EXEC_MACROS__ + +#ifndef __STRUCT_EXEC_OVERRIDE__ + +struct exec +{ + unsigned long a_info; /* Use macros N_MAGIC, etc for access */ + unsigned a_text; /* length of text, in bytes */ + unsigned a_data; /* length of data, in bytes */ + unsigned a_bss; /* length of uninitialized data area for file, in bytes */ + unsigned a_syms; /* length of symbol table data in file, in bytes */ + unsigned a_entry; /* start address */ + unsigned a_trsize; /* length of relocation info for text, in bytes */ + unsigned a_drsize; /* length of relocation info for data, in bytes */ +}; + +#endif /* __STRUCT_EXEC_OVERRIDE__ */ + +/* these go in the N_MACHTYPE field */ +enum machine_type { +#if defined (M_OLDSUN2) + M__OLDSUN2 = M_OLDSUN2, +#else + M_OLDSUN2 = 0, +#endif +#if defined (M_68010) + M__68010 = M_68010, +#else + M_68010 = 1, +#endif +#if defined (M_68020) + M__68020 = M_68020, +#else + M_68020 = 2, +#endif +#if defined (M_SPARC) + M__SPARC = M_SPARC, +#else + M_SPARC = 3, +#endif + /* skip a bunch so we don't run into any of sun's numbers */ + M_386 = 100, +}; + +#if !defined (N_MAGIC) +#define N_MAGIC(exec) ((exec).a_info & 0xffff) +#endif +#define N_MACHTYPE(exec) ((enum machine_type)(((exec).a_info >> 16) & 0xff)) +#define N_FLAGS(exec) (((exec).a_info >> 24) & 0xff) +#define N_SET_INFO(exec, magic, type, flags) \ + ((exec).a_info = ((magic) & 0xffff) \ + | (((int)(type) & 0xff) << 16) \ + | (((flags) & 0xff) << 24)) +#define N_SET_MAGIC(exec, magic) \ + ((exec).a_info = (((exec).a_info & 0xffff0000) | ((magic) & 0xffff))) + +#define N_SET_MACHTYPE(exec, machtype) \ + ((exec).a_info = \ + ((exec).a_info&0xff00ffff) | ((((int)(machtype))&0xff) << 16)) + +#define N_SET_FLAGS(exec, flags) \ + ((exec).a_info = \ + ((exec).a_info&0x00ffffff) | (((flags) & 0xff) << 24)) + +#ifndef OMAGIC +/* Code indicating object file or impure executable. */ +#define OMAGIC 0407 +/* Code indicating pure executable. */ +#define NMAGIC 0410 +/* Code indicating demand-paged executable. */ +#define ZMAGIC 0413 +#endif /* not OMAGIC */ + +#if !defined (N_BADMAG) +#define N_BADMAG(x) \ + (N_MAGIC(x) != OMAGIC && N_MAGIC(x) != NMAGIC \ + && N_MAGIC(x) != ZMAGIC) +#endif + +#define _N_BADMAG(x) \ + (N_MAGIC(x) != OMAGIC && N_MAGIC(x) != NMAGIC \ + && N_MAGIC(x) != ZMAGIC) + +#define _N_HDROFF(x) (SEGMENT_SIZE - sizeof (struct exec)) + +#if !defined (N_TXTOFF) +#define N_TXTOFF(x) \ + (N_MAGIC(x) == ZMAGIC ? _N_HDROFF((x)) + sizeof (struct exec) : sizeof (struct exec)) +#endif + +#if !defined (N_DATOFF) +#define N_DATOFF(x) (N_TXTOFF(x) + (x).a_text) +#endif + +#if !defined (N_TRELOFF) +#define N_TRELOFF(x) (N_DATOFF(x) + (x).a_data) +#endif + +#if !defined (N_DRELOFF) +#define N_DRELOFF(x) (N_TRELOFF(x) + (x).a_trsize) +#endif + +#if !defined (N_SYMOFF) +#define N_SYMOFF(x) (N_DRELOFF(x) + (x).a_drsize) +#endif + +#if !defined (N_STROFF) +#define N_STROFF(x) (N_SYMOFF(x) + (x).a_syms) +#endif + +/* Address of text segment in memory after it is loaded. */ +#if !defined (N_TXTADDR) +#define N_TXTADDR(x) 0 +#endif + +/* Address of data segment in memory after it is loaded. + Note that it is up to you to define SEGMENT_SIZE + on machines not listed here. */ +#if defined(vax) || defined(hp300) || defined(pyr) +#define SEGMENT_SIZE PAGE_SIZE +#endif +#ifdef hp300 +#define PAGE_SIZE 4096 +#endif +#ifdef sony +#define SEGMENT_SIZE 0x2000 +#endif /* Sony. */ +#ifdef is68k +#define SEGMENT_SIZE 0x20000 +#endif +#if defined(m68k) && defined(PORTAR) +#define PAGE_SIZE 0x400 +#define SEGMENT_SIZE PAGE_SIZE +#endif + +#define _N_SEGMENT_ROUND(x) (((x) + SEGMENT_SIZE - 1) & ~(SEGMENT_SIZE - 1)) + +#define _N_TXTENDADDR(x) (N_TXTADDR(x)+(x).a_text) + +#ifndef N_DATADDR +#define N_DATADDR(x) \ + (N_MAGIC(x)==OMAGIC? (_N_TXTENDADDR(x)) \ + : (_N_SEGMENT_ROUND (_N_TXTENDADDR(x)))) +#endif + +/* Address of bss segment in memory after it is loaded. */ +#if !defined (N_BSSADDR) +#define N_BSSADDR(x) (N_DATADDR(x) + (x).a_data) +#endif + +#if !defined (N_NLIST_DECLARED) +struct nlist { + union { + char *n_name; + struct nlist *n_next; + long n_strx; + } n_un; + unsigned char n_type; + char n_other; + short n_desc; + unsigned long n_value; +}; +#endif /* no N_NLIST_DECLARED. */ + +#if !defined (N_UNDF) +#define N_UNDF 0 +#endif +#if !defined (N_ABS) +#define N_ABS 2 +#endif +#if !defined (N_TEXT) +#define N_TEXT 4 +#endif +#if !defined (N_DATA) +#define N_DATA 6 +#endif +#if !defined (N_BSS) +#define N_BSS 8 +#endif +#if !defined (N_COMM) +#define N_COMM 18 +#endif +#if !defined (N_FN) +#define N_FN 15 +#endif + +#if !defined (N_EXT) +#define N_EXT 1 +#endif +#if !defined (N_TYPE) +#define N_TYPE 036 +#endif +#if !defined (N_STAB) +#define N_STAB 0340 +#endif + +/* The following type indicates the definition of a symbol as being + an indirect reference to another symbol. The other symbol + appears as an undefined reference, immediately following this symbol. + + Indirection is asymmetrical. The other symbol's value will be used + to satisfy requests for the indirect symbol, but not vice versa. + If the other symbol does not have a definition, libraries will + be searched to find a definition. */ +#define N_INDR 0xa + +/* The following symbols refer to set elements. + All the N_SET[ATDB] symbols with the same name form one set. + Space is allocated for the set in the text section, and each set + element's value is stored into one word of the space. + The first word of the space is the length of the set (number of elements). + + The address of the set is made into an N_SETV symbol + whose name is the same as the name of the set. + This symbol acts like a N_DATA global symbol + in that it can satisfy undefined external references. */ + +/* These appear as input to LD, in a .o file. */ +#define N_SETA 0x14 /* Absolute set element symbol */ +#define N_SETT 0x16 /* Text set element symbol */ +#define N_SETD 0x18 /* Data set element symbol */ +#define N_SETB 0x1A /* Bss set element symbol */ + +/* This is output from LD. */ +#define N_SETV 0x1C /* Pointer to set vector in data area. */ + +#if !defined (N_RELOCATION_INFO_DECLARED) +/* This structure describes a single relocation to be performed. + The text-relocation section of the file is a vector of these structures, + all of which apply to the text section. + Likewise, the data-relocation section applies to the data section. */ + +struct relocation_info +{ + /* Address (within segment) to be relocated. */ + int r_address; + /* The meaning of r_symbolnum depends on r_extern. */ + unsigned int r_symbolnum:24; + /* Nonzero means value is a pc-relative offset + and it should be relocated for changes in its own address + as well as for changes in the symbol or section specified. */ + unsigned int r_pcrel:1; + /* Length (as exponent of 2) of the field to be relocated. + Thus, a value of 2 indicates 1<<2 bytes. */ + unsigned int r_length:2; + /* 1 => relocate with value of symbol. + r_symbolnum is the index of the symbol + in file's the symbol table. + 0 => relocate with the address of a segment. + r_symbolnum is N_TEXT, N_DATA, N_BSS or N_ABS + (the N_EXT bit may be set also, but signifies nothing). */ + unsigned int r_extern:1; + /* Four bits that aren't used, but when writing an object file + it is desirable to clear them. */ + unsigned int r_pad:4; +}; +#endif /* no N_RELOCATION_INFO_DECLARED. */ + + +#endif /* __A_OUT_GNU_H__ */ diff --git a/binutils-1.9/ar.c b/binutils-1.9/ar.c new file mode 100644 index 0000000..82aac69 --- /dev/null +++ b/binutils-1.9/ar.c @@ -0,0 +1,2070 @@ +/* ar.c - Archive modify and extract. + Copyright (C) 1988, 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 <stdio.h> +#include <ar.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> + +#if !defined(A_OUT) && !defined(MACH_O) +#define A_OUT +#endif + +#ifdef A_OUT +#ifdef COFF_ENCAPSULATE +#include "a.out.encap.h" +#else +#include <a.out.h> +#endif +#endif + +#ifdef MACH_O +#ifndef A_OUT +#include <nlist.h> +#endif +#include <sys/loader.h> +#endif + +#if !defined(USG) || defined(hpux) +#define HAVE_FCHMOD +#define HAVE_FSYNC +#define HAVE_RENAME +#endif /* !USG || hpux */ + +#ifdef USG +#include <time.h> +#include <fcntl.h> +#else +#include <sys/file.h> +#include <sys/time.h> +#endif + +#ifdef __GNUC__ +#define alloca __builtin_alloca +#else +# ifdef sparc +# include <alloca.h> +# else +char *alloca (); +# endif +#endif + +#ifdef USG +#define bcopy(source, dest, size) memcpy((dest), (source), (size)) +#define bcmp(a, b, size) memcmp((a), (b), (size)) +#define bzero(s, size) memset((s), 0, (size)) +#endif + +/* Locking is normally disabled because fcntl hangs on the Sun + and it isn't supported properly across NFS anyway. */ +#ifdef LOCKS +/* You might need to compile with -I/usr/include/sys if your fcntl.h + isn't in /usr/include (which is where it should be according to POSIX). */ +#ifndef USG /* already included for USG */ +#include <fcntl.h> +#endif /* USG */ +#endif + +/* This structure is used internally to represent the info + on a member of an archive. This is to make it easier to change format. */ + +struct member_desc + { + /* Name of member. */ + char *name; + + /* The following fields are stored in the member header as decimal or octal + numerals, but in this structure they are stored as machine numbers. */ + int mode; /* Protection mode from member header. */ + long int date; /* Last modify date as stored in member header. */ + unsigned int size; /* Bytes of member's data, from member header. */ + int uid, gid; /* UID and GID fields copied from member header. */ + unsigned int offset;/* Offset in archive of the header of this member. */ + unsigned int data_offset;/* Offset of first data byte of the member. */ + + /* The next field does not describe where the member was in the + old archive, but rather where it will be in the modified archive. + It is set up by write_archive. */ + unsigned int new_offset; /* Offset of this member in new archive */ + + /* Symdef data for member. Used only for files being inserted. */ + struct symdef *symdefs; + unsigned int nsymdefs; /* Number of entries of symdef data. */ + unsigned int string_size; /* Size of strings needed by symdef data. */ + }; + +/* Each symbol is recorded by something like this. */ + +struct symdef + { + union + { + unsigned long int stringoffset; + char *name; + } s; + unsigned long int offset; + }; + +/* Nonzero means it's the name of an existing member; + position new or moved files with respect to this one. */ + +char *posname; + +/* How to use `posname': + POS_BEFORE means position before that member. + POS_AFTER means position after that member. + POS_DEFAULT if position by default; then `posname' should also be zero. */ + +enum { POS_DEFAULT, POS_BEFORE, POS_AFTER } postype; + +/* Nonzero means describe each action performed. */ + +int verbose; + +/* Nonzero means don't warn about creating the archive file if necessary. */ + +int silent_create; + +/* Nonzero means don't replace existing members whose + dates are more recent than the corresponding files. */ + +int newer_only; + +/* Nonzero means preserve dates of members when extracting them. */ + +int preserve_dates; + +/* Operation to be performed. */ + +#define DELETE 1 +#define REPLACE 2 +#define PRINT_TABLE 3 +#define PRINT_FILES 4 +#define EXTRACT 5 +#define MOVE 6 +#define QUICK_APPEND 7 + +int operation; + +/* Name of archive file. */ + +char *archive; + +/* Descriptor on which we have locked the original archive file, + or -1 if this has not been done. */ + +int lock_indesc; + +/* Pointer to tail of `argv', at first subfile name argument, + or zero if no such were specified. */ + +char **files; + +/* Nonzero means write a __.SYMDEF member into the modified archive. */ + +int symdef_flag; + +/* Nonzero means __.SYMDEF member exists in old archive. */ + +int symdef_exists; + +/* Nonzero means don't update __.SYMDEF unless the flag was given. */ + +int ignore_symdef; + +/* Total number of symdef entries we will have. */ + +unsigned long int nsymdefs; + +/* Symdef data from old archive (set up only if we need it) */ + +struct symdef *old_symdefs; + +/* Number of symdefs in remaining in old_symdefs. */ + +unsigned int num_old_symdefs; + +/* Number of symdefs old_symdefs had when it was read in. */ + +unsigned long int original_num_symdefs; + +/* String table from old __.SYMDEF member. */ + +char *old_strings; + +/* Size of old_strings */ + +unsigned long int old_strings_size; + +/* String table to be written into __.SYMDEF member. */ + +char *new_strings; + +/* Size of new_strings */ + +unsigned long int new_strings_size; + +/* An archive map is a chain of these structures. + Each structure describes one member of the archive. + The chain is in the same order as the members are. */ + +struct mapelt +{ + struct member_desc info; + struct mapelt *next; +}; + +struct mapelt *maplast; + +/* If nonzero, this is the map-element for the __.SYMDEF member + and we should update the time of that member just before finishing. */ + +struct mapelt *symdef_mapelt; + +/* Header that we wrote for the __.SYMDEF member. */ + +struct ar_hdr symdef_header; + +/* Name this program was run with. */ +char *program_name; + +char *xmalloc (), *xrealloc (); +void free (); + +void add_to_map (), delete_from_map (); +int insert_in_map (); +void print_descr (); +char *concat (); +void scan (); +void extract_members (); +void extract_member (); +void print_contents (); +void write_symdef_member (); +void read_old_symdefs (); +void two_operations (); +void usage (), fatal (), error (), error_with_file (); +void perror_with_name (), pfatal_with_name (); +void write_archive (); +void touch_symdef_member (); +void update_symdefs (); +void delete_members (), move_members (), replace_members (); +void quick_append (); +char *basename (); + +/* Output BYTES of data at BUF to the descriptor DESC. + FILE is the name of the file (for error messages). */ + +void +mywrite (desc, buf, bytes, file) + int desc; + char *buf; + int bytes; + char *file; +{ + register int val; + + while (bytes > 0) + { + val = write (desc, buf, bytes); + if (val <= 0) + perror_with_name (file); + buf += val; + bytes -= val; + } +} + +void +main (argc, argv) + int argc; + char **argv; +{ + int i; + + operation = 0; + verbose = 0; + newer_only = 0; + silent_create = 0; + posname = 0; + postype = POS_DEFAULT; + preserve_dates = 0; + symdef_flag = 0; + symdef_exists = 0; + ignore_symdef = 0; + symdef_mapelt = 0; + files = 0; + lock_indesc = -1; + + program_name = argv[0]; + + if (argc < 2) + usage ("too few command arguments", 0); + + { + char *key = argv[1]; + char *p = key; + char c; + + if (*p == '-') + p++; + while (c = *p++) + { + switch (c) + { + case 'a': + postype = POS_AFTER; + break; + + case 'b': + postype = POS_BEFORE; + break; + + case 'c': + silent_create = 1; + break; + + case 'd': + if (operation) + two_operations (); + + operation = DELETE; + break; + + case 'i': + postype = POS_BEFORE; + break; + + case 'l': + break; + + case 'm': + if (operation) + two_operations (); + operation = MOVE; + break; + + case 'o': + preserve_dates = 1; + break; + + case 'p': + if (operation) + two_operations (); + operation = PRINT_FILES; + break; + + case 'q': + if (operation) + two_operations (); + operation = QUICK_APPEND; + break; + + case 'r': + if (operation) + two_operations (); + operation = REPLACE; + break; + + case 's': + symdef_flag = 1; + break; + + case 't': + if (operation) + two_operations (); + operation = PRINT_TABLE; + break; + + case 'u': + operation = REPLACE; + newer_only = 1; + break; + + case 'v': + verbose = 1; + break; + + case 'x': + if (operation) + two_operations (); + operation = EXTRACT; + break; + } + } + + } + + if (operation == 0 && symdef_flag) + operation = REPLACE; + + if (operation == 0) + usage ("no operation specified", 0); + + i = 2; + + if (postype != POS_DEFAULT) + if (i < argc) + posname = argv[i++]; + else + usage ("no position operand", 0); + + if (i >= argc) + usage ("no archive specified", 0); + archive = argv[i++]; + + if (i < argc) + { + files = &argv[i]; + while (i < argc) + if (!strcmp (argv[i++], "__.SYMDEF")) + { + ignore_symdef = 1; + break; + } + } + + switch (operation) + { + case EXTRACT: + extract_members (extract_member); + break; + + case PRINT_TABLE: + extract_members (print_descr); + break; + + case PRINT_FILES: + extract_members (print_contents); + break; + + case DELETE: + if (files != 0) + delete_members (); + break; + + case MOVE: + if (files != 0) + move_members (); + break; + + case REPLACE: + if (files != 0 || symdef_flag) + replace_members (); + break; + + case QUICK_APPEND: + if (files != 0) + quick_append (); + break; + + default: + usage ("invalid operation %d", operation); + } + + exit (0); +} + +void +two_operations () +{ + usage ("two different operation switches specified", 0); +} + +void +scan (function, crflag) + void (*function) (); + int crflag; +{ + FILE *arcstream = fopen (archive, "r"); + + if (arcstream == 0 && crflag) + /* Creation-warning, if desired, will happen later. */ + return; + + if (arcstream == 0) + { + perror_with_name (archive); + exit (1); + } + { + char buf[SARMAG]; + int nread = fread (buf, 1, SARMAG, arcstream); + if (nread != SARMAG || bcmp (buf, ARMAG, SARMAG)) + fatal ("file %s not a valid archive", archive); + } + + /* Now find the members one by one. */ + { + int member_offset = SARMAG; + while (1) + { + int nread; + struct ar_hdr member_header; + struct member_desc member_desc; + char name [1 + sizeof member_header.ar_name]; + + if (fseek (arcstream, member_offset, 0) < 0) + perror_with_name (archive); + + nread = fread (&member_header, 1, sizeof (struct ar_hdr), arcstream); + if (nread == 0) + /* No data left means end of file; that is OK. */ + break; + + if (nread != sizeof (member_header) + || bcmp (member_header.ar_fmag, ARFMAG, 2)) + fatal ("file %s not a valid archive", archive); + bcopy (member_header.ar_name, name, sizeof member_header.ar_name); + { + char *p = name + sizeof member_header.ar_name; + *p = '\0'; + while (p > name && *--p == ' ') + *p = '\0'; +#ifdef USG + if (*p == '/') /* Since V.2, names are terminated with '/' */ + *p = '\0'; +#endif + } + member_desc.name = name; + sscanf (member_header.ar_mode, "%o", &member_desc.mode); + member_desc.date = atoi (member_header.ar_date); + member_desc.size = atoi (member_header.ar_size); + member_desc.uid = atoi (member_header.ar_uid); + member_desc.gid = atoi (member_header.ar_gid); + member_desc.offset = member_offset; + member_desc.data_offset = member_offset + sizeof (member_header); + + member_desc.new_offset = 0; + member_desc.symdefs = 0; + member_desc.nsymdefs = 0; + member_desc.string_size = 0; + + if (!ignore_symdef && !strcmp (name, "__.SYMDEF")) + symdef_exists = 1; + + function (member_desc, arcstream); + + member_offset += sizeof (member_header) + member_desc.size; + if (member_offset & 1) + ++member_offset; + } + } + + + fclose (arcstream); +} + +void print_modes (); + +void +print_descr (member) + struct member_desc member; +{ + char *timestring; + if (!verbose) + { + puts (member.name); + return; + } + print_modes (member.mode); + timestring = ctime (&member.date); + printf (" %2d/%2d %6d %12.12s %4.4s %s\n", + member.uid, member.gid, + member.size, timestring + 4, timestring + 20, + member.name); +} + +void +print_modes (modes) + int modes; +{ + putchar (modes & 0400 ? 'r' : '-'); + putchar (modes & 0200 ? 'w' : '-'); + putchar (modes & 0100 ? 'x' : '-'); + putchar (modes & 040 ? 'r' : '-'); + putchar (modes & 020 ? 'w' : '-'); + putchar (modes & 010 ? 'x' : '-'); + putchar (modes & 04 ? 'r' : '-'); + putchar (modes & 02 ? 'w' : '-'); + putchar (modes & 01 ? 'x' : '-'); +} + +#define BUFSIZE 1024 + +void +extract_member (member, istream) + struct member_desc member; + FILE *istream; +{ + int ncopied = 0; + FILE *ostream; + + fseek (istream, member.data_offset, 0); + ostream = fopen (member.name, "w"); + if (!ostream) + { + perror_with_name (member.name); + return; + } + + if (verbose) + printf ("x - %s\n", member.name); + + while (ncopied < member.size) + { + char buf [BUFSIZE]; + int tocopy = member.size - ncopied; + int nread; + if (tocopy > BUFSIZE) tocopy = BUFSIZE; + nread = fread (buf, 1, tocopy, istream); + if (nread != tocopy) + fatal ("error reading archive %s", archive); + fwrite (buf, 1, nread, ostream); + ncopied += tocopy; + } + +#ifdef HAVE_FCHMOD + fchmod (fileno (ostream), member.mode); +#else + chmod (member.name, member.mode); +#endif + if (ferror (ostream) || fclose (ostream) != 0) + error ("%s: I/O error", member.name); + + if (preserve_dates) + { +#ifdef USG + long tv[2]; + tv[0] = member.date; + tv[1] = member.date; + utime (member.name, tv); +#else + struct timeval tv[2]; + tv[0].tv_sec = member.date; + tv[0].tv_usec = 0; + tv[1].tv_sec = member.date; + tv[1].tv_usec = 0; + utimes (member.name, tv); +#endif + } +} + +void +print_contents (member, istream) + struct member_desc member; + FILE *istream; +{ + int ncopied = 0; + + fseek (istream, member.data_offset, 0); + + if (verbose) + printf ("\n<member %s>\n\n", member.name); + + while (ncopied < member.size) + { + char buf [BUFSIZE]; + int tocopy = member.size - ncopied; + int nread; + if (tocopy > BUFSIZE) tocopy = BUFSIZE; + nread = fread (buf, 1, tocopy, istream); + if (nread != tocopy) + fatal ("file %s not a valid archive", archive); + fwrite (buf, 1, nread, stdout); + ncopied += tocopy; + } +} + +/* Make a map of the existing members of the archive: their names, + positions and sizes. */ + +/* If `nonexistent_ok' is nonzero, + just return 0 for an archive that does not exist. + This will cause the ordinary supersede procedure to + create a new archive. */ + +struct mapelt * +make_map (nonexistent_ok) + int nonexistent_ok; +{ + struct mapelt mapstart; + mapstart.next = 0; + maplast = &mapstart; + scan (add_to_map, nonexistent_ok); + return mapstart.next; +} + +void +add_to_map (member) + struct member_desc member; +{ + struct mapelt *mapelt = (struct mapelt *) xmalloc (sizeof (struct mapelt)); + mapelt->info = member; + mapelt->info.name = concat (mapelt->info.name, "", ""); + maplast->next = mapelt; + mapelt->next = 0; + maplast = mapelt; +} + +/* Return the last element of the specified map. */ + +struct mapelt * +last_mapelt (map) + struct mapelt *map; +{ + struct mapelt *tail = map; + while (tail->next) tail = tail->next; + return tail; +} + +/* Return the element of the specified map which precedes elt. */ + +struct mapelt * +prev_mapelt (map, elt) + struct mapelt *map, *elt; +{ + struct mapelt *tail = map; + while (tail->next && tail->next != elt) + tail = tail->next; + if (tail->next) return tail; + return 0; +} + +/* Return the element of the specified map which has the specified name. */ + +struct mapelt * +find_mapelt_noerror (map, name) + struct mapelt *map; + register char *name; +{ + register struct mapelt *tail; + unsigned int len; + int dot_o; + + name = basename (name); + len = strlen (name); + dot_o = name[len - 2] == '.' && name[len - 1] == 'o'; + + for (tail = map; tail != 0; tail = tail->next) + { + if (tail->info.name == 0) + continue; + if (!strncmp (tail->info.name, name, 13)) + { + unsigned int eltlen = strlen (tail->info.name); + if (len <= 13 || eltlen <= 13) + return tail; + else + { + char *p = tail->info.name + 13; + if (dot_o && p[0] == '.' && p[1] == 'o' && p[2] == '\0') + return tail; + else if (!strncmp (p, name + 13, + (len > eltlen ? len : eltlen) - 13)) + return tail; + } + } + } + + return 0; +} + +struct mapelt * +find_mapelt (map, name) + struct mapelt *map; + char *name; +{ + register struct mapelt *found = find_mapelt_noerror (map, name); + if (found == 0) + error ("no member named `%s'", name); + return found; +} + +/* Before looking at the archive, if we are going to update it + based on looking at its current contents, make an exclusive lock on it. + The lock is released when `write_archive' is called. */ + +void +lock_for_update () +{ + /* Open the existing archive file; if that fails, create an empty one. */ + + lock_indesc = open (archive, O_RDWR, 0); + + if (lock_indesc < 0) + { + int outdesc; + + if (!silent_create) + printf ("Creating archive file `%s'\n", archive); + outdesc = open (archive, O_WRONLY | O_APPEND | O_CREAT, 0666); + if (outdesc < 0) + pfatal_with_name (archive); + mywrite (outdesc, ARMAG, SARMAG, archive); + close (outdesc); + + /* Now we had better be able to open for update! */ + + lock_indesc = open (archive, O_RDWR, 0); + if (lock_indesc < 0) + { + unlink (archive); + pfatal_with_name (archive); + } + } + +#ifdef LOCKS + /* Lock the old file so that it won't be updated by two programs at once. + This uses the fcntl locking facility found on Sun systems + which is also in POSIX. (Perhaps it comes from sysV.) + + Note that merely reading an archive does not require a lock, + because we use `rename' to update the whole file atomically. */ + + { + struct flock lock; + + lock.l_type = F_WRLCK; + lock.l_whence = 0; + lock.l_start = 0; + lock.l_len = 0; + + while (1) + { + int value = fcntl (lock_indesc, F_SETLKW, &lock); + if (value >= 0) + break; + else if (errno == EINTR) + continue; + else + pfatal_with_name ("locking archive"); + } + } +#endif +} + +/* Unlock archive and close the file descriptor. */ + +void +close_archive () +{ +#ifdef LOCKS + { + struct flock lock; + + /* Unlock the old archive. */ + + lock.l_type = F_UNLCK; + lock.l_whence = 0; + lock.l_start = 0; + lock.l_len = 0; + + fcntl (lock_indesc, F_SETLK, &lock); + } +#endif + + /* Close the archive. If we renamed a new one, the old one disappears. */ + close (lock_indesc); +} + +/* Write a new archive file from a given map. */ +/* When a map is used as the pattern for a new archive, + each element represents one member to put in it, and + the order of elements controls the order of writing. + + Ordinarily, the element describes a member of the old + archive, to be copied into the new one. + + If the `offset' field of the element's info is 0, + then the element describes a file to be copied into the + new archive. The `name' field is the file's name. + + If the `name' field of an element is 0, the element is ignored. + This makes it easy to specify deletion of archive members. + + Every operation that will eventually call `write_archive' + should call `lock_for_update' before beginning + to do any I/O on the archive file. +*/ + +char *make_tempname (); +void copy_out_member (); + +void +write_archive (map, appendflag) + struct mapelt *map; + int appendflag; +{ + char *tempname = make_tempname (archive); + int indesc = lock_indesc; + int outdesc; + char *outname; + struct mapelt *tail; + + /* Now open the output. */ + + if (!appendflag) + { + /* Updating an existing archive normally. + Write output as TEMPNAME and rename at the end. + There can never be two invocations trying to do this at once, + because of the lock made on the old archive file. */ + + outdesc = open (tempname, O_WRONLY | O_CREAT, 0666); + if (outdesc < 0) + pfatal_with_name (tempname); + outname = tempname; + mywrite (outdesc, ARMAG, SARMAG, outname); + } + else + { + /* Fast-append to existing archive. */ + + outdesc = open (archive, O_WRONLY | O_APPEND, 0); + if (outdesc < 0) + pfatal_with_name (archive); + outname = archive; + } + + /* If archive has or should have a __.SYMDEF member, + compute the contents for it. */ + + if (symdef_flag || symdef_exists) + { + if (symdef_exists) + read_old_symdefs (map, indesc); + else + { + struct mapelt *this = (struct mapelt *) + xmalloc (sizeof (struct mapelt)); + this->info.name = "__.SYMDEF"; + this->info.offset = SARMAG; + this->info.data_offset = SARMAG + sizeof (struct ar_hdr); + this->info.new_offset = 0; + this->info.date = 0; + this->info.size = 0; + this->info.uid = 0; + this->info.gid = 0; + this->info.mode = 0666; + this->info.symdefs = 0; + this->info.nsymdefs = 0; + this->info.string_size = 0; + this->next = map; + map = this; + original_num_symdefs = 0; + old_strings_size = 0; + } + + update_symdefs (map, indesc); + } + + /* Copy the members into the output, either from the old archive + or from specified files. */ + + for (tail = map; tail != 0; tail = tail->next) + { + if ((symdef_flag || symdef_exists) && tail->info.name + && !strcmp (tail->info.name, "__.SYMDEF") +#if 0 + && tail->info.date==0 +#endif + ) + write_symdef_member (tail, map, outdesc, outname); + else + copy_out_member (tail, indesc, outdesc, outname); + } + + if (symdef_mapelt != 0) + { + /* Check for members whose data offsets weren't + known when the symdef member was first written. */ + int doneany = 0; + for (tail = map; tail != 0; tail = tail->next) + if (tail->info.offset == 0) + { + /* Fix up the symdefs. */ + register unsigned int i; + for (i = 0; i < tail->info.nsymdefs; ++i) + tail->info.symdefs[i].offset = tail->info.new_offset; + doneany = 1; + } + if (doneany) + { + /* Some files had bad symdefs; rewrite the symdef member. */ + lseek (outdesc, symdef_mapelt->info.offset, 0); + write_symdef_member (symdef_mapelt, map, outdesc, outname); + } + } + + /* Mark the __.SYMDEF member as up to date. */ + + if (symdef_mapelt != 0) + touch_symdef_member (outdesc, outname); + + /* Install the new output under the intended name. */ + +#ifdef HAVE_FSYNC + fsync (outdesc); +#endif + close (outdesc); + + if (!appendflag) + if (rename (tempname, archive)) + pfatal_with_name (tempname); + + close_archive (); +} + +void +header_from_map (header, mapelt) + struct ar_hdr *header; + struct mapelt *mapelt; +{ + unsigned int namelen; + char *p; + + /* Zero the header, then store in the data as text. */ + bzero ((char *) header, sizeof (*header)); + + p = basename (mapelt->info.name); + strncpy (header->ar_name, p, sizeof (header->ar_name)); + namelen = strlen (p); + if (namelen >= sizeof (header->ar_name)) + { + if (mapelt->info.name[namelen - 2] == '.' && + mapelt->info.name[namelen - 1] == 'o') + { + header->ar_name[sizeof (header->ar_name) - 3] = '.'; + header->ar_name[sizeof (header->ar_name) - 2] = 'o'; + } + header->ar_name[sizeof (header->ar_name) - 1] = '\0'; + error ("member name `%s' truncated to `%s'", + mapelt->info.name, header->ar_name); + } +#ifdef USG + { + /* System V tacks a trailing '/' onto the end of the name */ + char *p = header->ar_name; + char *end = p + sizeof (header->ar_name); + + while (p < end && *p) + p++; + *p = '/'; + } +#endif + + sprintf (header->ar_date, "%ld", mapelt->info.date); + sprintf (header->ar_size, "%d", mapelt->info.size); + sprintf (header->ar_uid, "%d", mapelt->info.uid); + sprintf (header->ar_gid, "%d", mapelt->info.gid); + sprintf (header->ar_mode, "%o", mapelt->info.mode); + strncpy (header->ar_fmag, ARFMAG, sizeof (header->ar_fmag)); + + /* Change all remaining nulls in the header into spaces. */ + { + char *end = (char *) &header[1]; + register char *p; + for (p = (char *) header; p < end; ++p) + if (*p == '\0') + *p = ' '; + } +} + +/* gets just the file part of name */ + +char * +basename (path) + char *path; +{ + char *save, *start; + for (start = save = path; *path; path++) + if (*path == '/') + save = path + 1; + + if (save != start) + return save; + else + return start; +} + +/* writes to file open on OUTDESC with name OUTNAME. */ +void +copy_out_member (mapelt, archive_indesc, outdesc, outname) + struct mapelt *mapelt; + int archive_indesc; + int outdesc; + char *outname; +{ + struct ar_hdr header; + int indesc; + + if (mapelt->info.name == 0) + /* This element was cancelled. */ + return; + + header_from_map (&header, mapelt); + + if (mapelt->info.offset != 0) + { + indesc = archive_indesc; + lseek (indesc, mapelt->info.data_offset, 0); + } + else + { + indesc = open (mapelt->info.name, 0, 0); + if (indesc < 0) + { + perror_with_name (mapelt->info.name); + return; + } + } + + mywrite (outdesc, &header, sizeof (header), outname); + + if (mapelt->info.data_offset == 0) + mapelt->info.data_offset = lseek (outdesc, 0L, 1); + + { + char buf[BUFSIZE]; + int tocopy = mapelt->info.size; + while (tocopy > 0) + { + int thistime = tocopy; + if (thistime > BUFSIZE) thistime = BUFSIZE; + read (indesc, buf, thistime); + mywrite (outdesc, buf, thistime, outname); + tocopy -= thistime; + } + } + + if (indesc != archive_indesc) + close (indesc); + + if (mapelt->info.size & 1) + mywrite (outdesc, "\n", 1, outname); +} + +/* Update the time of the __.SYMDEF member; done when we updated + that member, just before we close the new archive file. + It is open on OUTDESC and its name is OUTNAME. */ + +void +touch_symdef_member (outdesc, outname) + int outdesc; + char *outname; +{ + struct stat statbuf; + int i; + + /* See what mtime the archive file has as a result of our writing it. */ + fstat (outdesc, &statbuf); + + /* Advance member's time to that time. */ + bzero (symdef_header.ar_date, sizeof symdef_header.ar_date); + sprintf (symdef_header.ar_date, "%ld", statbuf.st_mtime); + for (i = 0; i < sizeof symdef_header.ar_date; i++) + if (symdef_header.ar_date[i] == 0) + symdef_header.ar_date[i] = ' '; + + /* Write back this member's header with the new time. */ + if (lseek (outdesc, symdef_mapelt->info.new_offset, 0) >= 0) + mywrite (outdesc, &symdef_header, sizeof symdef_header, outname); +} + +char * +make_tempname (name) + char *name; +{ +#ifdef USG + /* sigh. 14 character filenames are *wonderful*, just *wonderful*. */ + char *p = basename (name); + char *q, *r; + + if (p != name) + { + q = concat (name, "", ""); /* get a fresh copy */ + r = basename (q); /* r points just after last '/' */ + *--r = '\0'; + return concat (q, "/t_", p); + } + else if (strlen (name) >= 14) + return concat ("t_", name, ""); + else +#endif + return concat (name, "", "_supersede"); +} + +void +delete_members () +{ + struct mapelt *map = make_map (0); + struct mapelt mapstart; + char **p; + + mapstart.info.name = 0; + mapstart.next = map; + map = &mapstart; + + lock_for_update (); + + if (files) + for (p = files; *p; p++) + { + /* If user says to delete the __.SYMDEF member, + don't make a new one to replace it. */ + if (!strcmp (*p, "__.SYMDEF")) + symdef_exists = 0; + delete_from_map (*p, map); + } + + write_archive (map->next, 0); +} + +void +delete_from_map (name, map) + char *name; + struct mapelt *map; +{ + struct mapelt *this = find_mapelt (map, name); + + if (!this) return; + this->info.name = 0; + if (verbose) + printf ("d - %s\n", name); +} + +void +move_members () +{ + struct mapelt *map = make_map (0); + char **p; + struct mapelt *after_mapelt; + struct mapelt mapstart; + struct mapelt *change_map; + + mapstart.info.name = 0; + mapstart.next = map; + change_map = &mapstart; + + lock_for_update (); + + switch (postype) + { + case POS_DEFAULT: + after_mapelt = last_mapelt (change_map); + break; + + case POS_AFTER: + after_mapelt = find_mapelt (map, posname); + break; + + case POS_BEFORE: + after_mapelt = prev_mapelt (change_map, find_mapelt (map, posname)); + } + + /* Failure to find specified "before" or "after" member + is a fatal error; message has already been printed. */ + + if (!after_mapelt) exit (1); + + if (files) + for (p = files; *p; p++) + { + if (move_in_map (*p, change_map, after_mapelt)) + after_mapelt = after_mapelt->next; + } + + write_archive (map, 0); +} + +int +move_in_map (name, map, after) + char *name; + struct mapelt *map, *after; +{ + struct mapelt *this = find_mapelt (map, name); + struct mapelt *prev; + + if (!this) return 0; + prev = prev_mapelt (map, this); + prev->next = this->next; + this->next = after->next; + after->next = this; + return 1; +} + +/* Insert files into the archive. */ + +void +replace_members () +{ + struct mapelt *map = make_map (1); + struct mapelt mapstart; + struct mapelt *after_mapelt; + struct mapelt *change_map; + char **p; + int changed; + + mapstart.info.name = 0; + mapstart.next = map; + change_map = &mapstart; + + lock_for_update (); + + switch (postype) + { + case POS_DEFAULT: + after_mapelt = last_mapelt (change_map); + break; + + case POS_AFTER: + after_mapelt = find_mapelt (map, posname); + break; + + case POS_BEFORE: + after_mapelt = prev_mapelt (change_map, find_mapelt (map, posname)); + } + + /* Failure to find specified "before" or "after" member + is a fatal error; the message has already been printed. */ + if (after_mapelt == 0) + exit (1); + + changed = 0; + if (files != 0) + for (p = files; *p != 0; ++p) + if (insert_in_map (*p, change_map, after_mapelt)) + { + after_mapelt = after_mapelt->next; + changed = 1; + } + + change_map = change_map->next; + if (!changed && (!symdef_flag || symdef_exists)) + /* Nothing changed. */ + close_archive (change_map); + else + write_archive (change_map, 0); +} + +/* Handle the "quick insert" operation. */ + +void +quick_append () +{ + struct mapelt *map; + struct mapelt *after; + struct mapelt mapstart; + char **p; + + mapstart.info.name = 0; + mapstart.next = 0; + map = &mapstart; + after = map; + + lock_for_update (); + + /* Insert the specified files into the "map", + but is a map of the inserted files only, + and starts out empty. */ + if (files) + for (p = files; *p; p++) + { + if (insert_in_map (*p, map, after)) + after = after->next; + } + + /* Append these files to the end of the existing archive file. */ + + write_archive (map->next, 1); +} + +/* Insert an entry for name NAME into the map MAP after the map entry AFTER. + Delete an old entry for NAME. + MAP is assumed to start with a dummy entry, which facilitates + insertion at the beginning of the list. + Return 1 if successful, 0 if did nothing because file NAME doesn't + exist or (optionally) is older. */ + +int +insert_in_map (name, map, after) + char *name; + struct mapelt *map, *after; +{ + struct mapelt *old = find_mapelt_noerror (map, name); + struct mapelt *this; + struct stat status; + + if (stat (name, &status)) + { + perror_with_name (name); + return 0; + } + if (old && newer_only && status.st_mtime <= old->info.date) + return 0; + if (old) + /* Delete the old one. */ + old->info.name = 0; + this = (struct mapelt *) xmalloc (sizeof (struct mapelt)); + this->info.name = name; + this->info.offset = 0; + this->info.data_offset = 0; + this->info.date = status.st_mtime; + this->info.size = status.st_size; + this->info.uid = status.st_uid; + this->info.gid = status.st_gid; + this->info.mode = status.st_mode; + /* Always place a __.SYMDEF member first in the archive, regardless + of any position specifications. */ + if (! strcmp (name, "__.SYMDEF")) + this->next = map->next, map->next = this; + else + this->next = after->next, after->next = this; + + if (verbose) + printf ("%c - %s\n", old == 0 ? 'a' : 'r', this->info.name); + + return 1; +} + +/* Apply a function to each of the specified members. +*/ + +void +extract_members (function) + void (*function) (); +{ + struct mapelt *map; + FILE *arcstream; + char **p; + + if (!files) + { + /* Handle case where we want to operate on every member. + No need to make a map and search it for this. */ + scan (function, 0); + return; + } + + arcstream = fopen (archive, "r"); + if (!arcstream) + fatal ("failure opening archive %s for the second time", archive); + map = make_map (0); + + for (p = files; *p; p++) + { + struct mapelt *this = find_mapelt (map, *p); + if (!this) continue; + function (this->info, arcstream); + } + + fclose (arcstream); +} + +/* Write the __.SYMDEF member from data in core. OUTDESC and OUTNAME + are descriptor and name of file to write to. */ + +void +write_symdef_member (mapelt, map, outdesc, outname) + struct mapelt *mapelt; + struct mapelt *map; + int outdesc; + char *outname; +{ + struct ar_hdr header; + struct mapelt *mapptr; + unsigned long int symdefs_size; + + if (mapelt->info.name == 0) + /* This element was cancelled. */ + return; + + header_from_map (&header, mapelt); + + bcopy (&header, &symdef_header, sizeof header); + + mywrite (outdesc, &header, sizeof (header), outname); + + /* Write the number of symdefs. */ + symdefs_size = nsymdefs * sizeof (struct symdef); + mywrite (outdesc, &symdefs_size, sizeof symdefs_size, outname); + + /* Write symdefs surviving from old archive. */ + mywrite (outdesc, old_symdefs, num_old_symdefs * sizeof (struct symdef), + outname); + + /* Write symdefs for new members. */ + for (mapptr = map; mapptr != 0; mapptr = mapptr->next) + if (mapptr->info.nsymdefs != 0) + write (outdesc, mapptr->info.symdefs, + mapptr->info.nsymdefs * sizeof (struct symdef)); + + /* Write the string table size. */ + mywrite (outdesc, &new_strings_size, sizeof new_strings_size, outname); + + /* Write the string table. */ + mywrite (outdesc, new_strings, new_strings_size, outname); + + if (mapelt->info.size & 1) + mywrite (outdesc, "", 1, outname); +} + +void +read_old_symdefs (map, archive_indesc) + struct mapelt *map; + int archive_indesc; +{ + struct mapelt *mapelt; + char *data; + int val; + int symdefs_size; + + mapelt = find_mapelt_noerror (map, "__.SYMDEF"); + if (!mapelt) + abort (); /* Only call here if an old one exists */ + + data = (char *) xmalloc (mapelt->info.size); + lseek (archive_indesc, mapelt->info.data_offset, 0); + val = read (archive_indesc, data, mapelt->info.size); + + symdefs_size = *(unsigned long int *) data; + original_num_symdefs = symdefs_size / sizeof (struct symdef); + old_symdefs = (struct symdef *) (data + sizeof (symdefs_size)); + old_strings = ((char *) (old_symdefs + original_num_symdefs) + + sizeof (symdefs_size)); + old_strings_size + = *(unsigned long int *) (old_symdefs + original_num_symdefs); +} + +/* Read various information from the header of an object file. + Return 0 for failure or 1 for success. */ + +int +read_header_info (mapelt, desc, offset, syms_offset, syms_size, strs_offset, strs_size) + struct mapelt *mapelt; + int desc; + long int offset; + long int *syms_offset; + unsigned int *syms_size; + long int *strs_offset; + unsigned int *strs_size; +{ +#ifdef A_OUT + { + struct exec hdr; + + lseek (desc, offset, 0); +#ifdef HEADER_SEEK_FD + HEADER_SEEK_FD (desc); +#endif + + if (read (desc, (char *) &hdr, sizeof hdr) == sizeof hdr && !N_BADMAG(hdr)) + { + *syms_offset = N_SYMOFF (hdr); + *syms_size = hdr.a_syms; + *strs_offset = N_STROFF (hdr); + lseek (desc, N_STROFF (hdr) + offset, 0); + if (read (desc, (char *) strs_size, sizeof *strs_size) != sizeof *strs_size) + { + error_with_file ("failure reading string table size in ", mapelt); + return 0; + } + return 1; + } + } +#endif + +#ifdef MACH_O + { + struct mach_header mach_header; + struct load_command *load_command; + struct symtab_command *symtab_command; + char *hdrbuf; + int cmd, symtab_seen; + + lseek (desc, offset, 0); + if (read (desc, (char *) &mach_header, sizeof mach_header) == sizeof mach_header + && mach_header.magic == MH_MAGIC) + { + hdrbuf = xmalloc (mach_header.sizeofcmds); + if (read (desc, hdrbuf, mach_header.sizeofcmds) != mach_header.sizeofcmds) + { + error_with_file ("failure reading load commands of ", mapelt); + return 0; + } + load_command = (struct load_command *) hdrbuf; + symtab_seen = 0; + for (cmd = 0; cmd < mach_header.ncmds; ++cmd) + { + if (load_command->cmd == LC_SYMTAB) + { + symtab_seen = 1; + symtab_command = (struct symtab_command *) load_command; + *syms_offset = symtab_command->symoff; + *syms_size = symtab_command->nsyms * sizeof (struct nlist); + *strs_offset = symtab_command->stroff; + *strs_size = symtab_command->strsize; + } + load_command = (struct load_command *) ((char *) load_command + load_command->cmdsize); + } + free (hdrbuf); + if (!symtab_seen) + *syms_offset = *syms_size = *strs_offset = *strs_size = 0; + return 1; + } + } +#endif + + error_with_file ("bad format (not an object file) in ", mapelt); + return 0; +} + +/* Create the info.symdefs for a new member + by reading the file it is coming from. */ + +void +make_new_symdefs (mapelt, archive_indesc) + struct mapelt *mapelt; + int archive_indesc; +{ + int indesc; + char *name = mapelt->info.name; + long int syms_offset, strs_offset; + unsigned int syms_size, strs_size; + struct nlist *symbols; + int symcount; + char *strings; + register unsigned int i; + unsigned long int offset; + + if (name == 0) + /* Deleted member. */ + abort (); + + if (mapelt->info.offset != 0) + { + indesc = archive_indesc; + lseek (indesc, mapelt->info.data_offset, 0); + offset = mapelt->info.data_offset; + } + else + { + indesc = open (mapelt->info.name, 0, 0); + if (indesc < 0) + { + perror_with_name (mapelt->info.name); + return; + } + offset = 0; + } + + if (!read_header_info (mapelt, indesc, offset, &syms_offset, &syms_size, &strs_offset, &strs_size)) + { + if (mapelt->info.offset == 0) + close (indesc); + return; + } + + /* Number of symbol entries in the file. */ + symcount = syms_size / sizeof (struct nlist); + /* Allocate temporary space for the symbol entries. */ + symbols = (struct nlist *) alloca (syms_size); + /* Read in the symbols. */ + lseek (indesc, syms_offset + offset, 0); + if (read (indesc, (char *) symbols, syms_size) != syms_size) + { + error_with_file ("premature end of file in symbols of ", mapelt); + if (mapelt->info.offset == 0) + (void) close (indesc); + return; + } + + /* The string table size includes the size word. */ + if (strs_size < sizeof (strs_size)) + { + error_with_file ("bad string table size in ", mapelt); + if (mapelt->info.offset == 0) + (void) close (indesc); + return; + } + strs_size -= sizeof (strs_size); + + /* Allocate permanent space for the string table. */ + strings = (char *) xmalloc (strs_size); + + /* Read in the strings. */ + lseek (indesc, offset + strs_offset + sizeof strs_size, 0); + if (read (indesc, strings, strs_size) != strs_size) + { + error_with_file ("premature end of file in strings of ", mapelt); + if (mapelt->info.offset == 0) + (void) close (indesc); + return; + } + + if (indesc != archive_indesc) + (void) close (indesc); + + /* Discard the symbols we don't want to mention; compact the rest down. */ + symcount = filter_symbols (symbols, symcount); + + mapelt->info.symdefs = (struct symdef *) + xmalloc (symcount * sizeof (struct symdef)); + mapelt->info.nsymdefs = symcount; + mapelt->info.string_size = 0; + + for (i = 0; i < symcount; ++i) + { + unsigned long int stroff = symbols[i].n_un.n_strx - sizeof (strs_size); + char *symname = strings + stroff; + if (stroff > strs_size) + { + char buf[100]; + sprintf (buf, "ridiculous string offset %lu in symbol %u of ", + stroff + sizeof (strs_size), i); + error_with_file (buf, mapelt); + return; + } + mapelt->info.symdefs[i].s.name = symname; + mapelt->info.string_size += strlen (symname) + 1; + } +} + +/* Choose which symbol entries to mention in __.SYMDEF; + compact them downward to get rid of the rest. + Return the number of symbols left. */ + +int +filter_symbols (syms, symcount) + struct nlist *syms; + unsigned int symcount; +{ + struct nlist *from, *to; + struct nlist *end = syms + symcount; + + for (to = from = syms; from < end; ++from) + if ((from->n_type & N_EXT) + && (from->n_type != N_EXT || from->n_value != 0)) + *to++ = *from; + + return to - syms; +} + + +/* Update the __.SYMDEF data before writing a new archive. */ + +void +update_symdefs (map, archive_indesc) + struct mapelt *map; + int archive_indesc; +{ + struct mapelt *tail; + int pos; + register unsigned int i; + unsigned int len; + struct symdef *s; + unsigned long int deleted_strings_size = 0; + + nsymdefs = original_num_symdefs; + num_old_symdefs = original_num_symdefs; + new_strings_size = old_strings_size; + + if (nsymdefs != 0) + { + /* We already had a __.SYMDEF member, so just update it. */ + + /* Mark as canceled any old symdefs for members being deleted. */ + + for (tail = map; tail != 0; tail = tail->next) + { + if (tail->info.name == 0) + { + /* Old member being deleted. Delete its symdef entries too. */ + for (i = 0; i < original_num_symdefs; i++) + if (old_symdefs[i].offset == tail->info.offset) + { + old_symdefs[i].offset = 0; + --nsymdefs; + deleted_strings_size + += strlen (old_strings + + old_symdefs[i].s.stringoffset) + 1; + } + } + } + + /* Compactify old symdefs. */ + { + register unsigned int j = 0; + for (i = 0; i < num_old_symdefs; ++i) + { + if (j != i) + old_symdefs[j] = old_symdefs[i]; + if (old_symdefs[i].offset != 0) + ++j; + } + num_old_symdefs -= i - j; + } + + /* Create symdef data for any new members. */ + for (tail = map; tail != 0; tail = tail->next) + { + if (tail->info.offset != 0 + || tail->info.name == 0 + || !strcmp (tail->info.name, "__.SYMDEF")) + continue; + make_new_symdefs (tail, archive_indesc); + nsymdefs += tail->info.nsymdefs; + new_strings_size += tail->info.string_size; + } + } + else + { + /* Create symdef data for all existing members. */ + + for (tail = map; tail != 0; tail = tail->next) + { + if (tail->info.name == 0 + || !strcmp (tail->info.name, "__.SYMDEF")) + continue; + make_new_symdefs (tail, archive_indesc); + nsymdefs += tail->info.nsymdefs; + new_strings_size += tail->info.string_size; + } + } + + new_strings_size -= deleted_strings_size; + old_strings_size -= deleted_strings_size; + + /* Now we know the size of __.SYMDEF, + so assign the positions of all the members. */ + + tail = find_mapelt_noerror (map, "__.SYMDEF"); + tail->info.size = (sizeof (nsymdefs) + (nsymdefs * sizeof (struct symdef)) + + sizeof (new_strings_size) + new_strings_size); + symdef_mapelt = tail; + + pos = SARMAG; + for (tail = map; tail != 0; tail = tail->next) + { + if (tail->info.name == 0) + /* Ignore deleted members. */ + continue; + tail->info.new_offset = pos; + pos += sizeof (struct ar_hdr) + tail->info.size; + if (tail->info.size & 1) + ++pos; + } + + /* Now update the offsets in the symdef data + to be the new offsets rather than the old ones. */ + + for (tail = map; tail != 0; tail = tail->next) + { + if (tail->info.name == 0) + continue; + if (tail->info.symdefs == 0) + /* Member without new symdef data. + Check the old symdef data; it may be included there. */ + for (i = 0; i < num_old_symdefs; i++) + { + if (old_symdefs[i].offset == tail->info.offset) + old_symdefs[i].offset = tail->info.new_offset; + } + else + for (i = 0; i < tail->info.nsymdefs; i++) + tail->info.symdefs[i].offset = tail->info.new_offset; + } + + /* Generate new, combined string table and put each string's offset into the + symdef that refers to it. Note that old symdefs ref their strings by + offsets into old_strings but new symdefs contain addresses of strings. */ + + new_strings = (char *) xmalloc (new_strings_size); + pos = 0; + + /* Write the strings of the old symdefs and update the structures + to contain indices into the string table instead of strings. */ + for (i = 0; i < num_old_symdefs; i++) + { + strcpy (new_strings + pos, old_strings + old_symdefs[i].s.stringoffset); + old_symdefs[i].s.stringoffset = pos; + pos += strlen (new_strings + pos) + 1; + } + if (pos < old_strings_size) + { + unsigned int d = old_strings_size - pos; + /* Correct the string table size. */ + new_strings_size -= d; + /* Correct the size of the `__.SYMDEF' member, + since it contains the string table. */ + symdef_mapelt->info.size -= d; + } + else if (pos > old_strings_size) + fatal ("Old archive's string size was %u too small.", + pos - old_strings_size); + + for (tail = map; tail != 0; tail = tail->next) + if (tail->info.symdefs) + { + len = tail->info.nsymdefs; + s = tail->info.symdefs; + + for (i = 0; i < len; i++) + { + strcpy (new_strings + pos, s[i].s.name); + s[i].s.stringoffset = pos; + pos += strlen (new_strings + pos) + 1; + } + } + if (pos != new_strings_size) + fatal ("internal error: inconsistency in new_strings_size", 0); +} + +/* Print error message and usage message, and exit. */ + +void +usage (s1, s2) + char *s1, *s2; +{ + error (s1, s2); + fprintf (stderr, "\ +Usage: %s [d|m|p|q|r|t|x [[abi [position-name] [cilouv]] archive file...\n", + program_name); + exit (1); +} + +/* Print error message and exit. */ + +void +fatal (s1, s2) + char *s1, *s2; +{ + error (s1, s2); + exit (1); +} + +/* Print error message. `s1' is printf control string, the rest are args. */ + +void +error (s1, s2, s3, s4, s5) + char *s1, *s2, *s3, *s4, *s5; +{ + fprintf (stderr, "%s: ", program_name); + fprintf (stderr, s1, s2, s3, s4, s5); + fprintf (stderr, "\n"); +} + +void +error_with_file (string, mapelt) + char *string; + struct mapelt *mapelt; +{ + fprintf (stderr, "%s: ", program_name); + fprintf (stderr, string); + if (mapelt->info.offset != 0) + fprintf (stderr, "%s(%s)", archive, mapelt->info.name); + else + fprintf (stderr, "%s", mapelt->info.name); + fprintf (stderr, "\n"); +} + +void +perror_with_name (name) + char *name; +{ + extern int errno, sys_nerr; + extern char *sys_errlist[]; + char *s; + + if (errno < sys_nerr) + s = concat ("", sys_errlist[errno], " for %s"); + else + s = "unknown error for %s"; + error (s, name); +} + +void +pfatal_with_name (name) + char *name; +{ + extern int errno, sys_nerr; + extern char *sys_errlist[]; + char *s; + + if (errno < sys_nerr) + s = concat ("", sys_errlist[errno], " for %s"); + else + s = "cannot open %s"; + fatal (s, name); +} + +/* Return a newly-allocated string whose contents + concatenate those of S1, S2, and S3. */ + +char * +concat (s1, s2, s3) + char *s1, *s2, *s3; +{ + int len1 = strlen (s1), len2 = strlen (s2), len3 = strlen (s3); + char *result = (char *) xmalloc (len1 + len2 + len3 + 1); + + strcpy (result, s1); + strcpy (result + len1, s2); + strcpy (result + len1 + len2, s3); + *(result + len1 + len2 + len3) = 0; + + return result; +} + +/* Like malloc but get fatal error if memory is exhausted. */ + +char * +xmalloc (size) + unsigned int size; +{ + extern char *malloc (); + char *result = malloc (size); + if (result == 0) + fatal ("virtual memory exhausted", 0); + return result; +} + +char * +xrealloc (ptr, size) + char *ptr; + unsigned int size; +{ + extern char *realloc (); + char *result = realloc (ptr, size); + if (result == 0) + fatal ("virtual memory exhausted"); + return result; +} + +#ifndef HAVE_RENAME +int +rename (from, to) + char *from, *to; +{ + (void)unlink (to); + if (link (from, to) < 0 + || unlink (from) < 0) + return -1; + else + return 0; +} +#endif diff --git a/binutils-1.9/cplus-dem.c b/binutils-1.9/cplus-dem.c new file mode 100644 index 0000000..0ea6186 --- /dev/null +++ b/binutils-1.9/cplus-dem.c @@ -0,0 +1,925 @@ +/* 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. */ + +/* 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> + +#if !defined(sequent) && !defined(NeXT) +#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 + +#ifdef __STDC__ +extern char *xmalloc (int); +extern char *xrealloc (char *, int); +#else +extern char *xmalloc (); +extern char *xrealloc (); +#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", "*", + "negate", "-", + "trunc_mod", "%", + "trunc_div", "/", + "truth_andif", "&&", + "truth_orif", "||", + "postincrement", "++", + "postdecrement", "--", + "bit_ior", "|", + "bit_xor", "^", + "bit_and", "&", + "bit_not", "~", + "call", "()", + "cond", "?:", + "alshift", "<<", + "arshift", ">>", + "component", "->", + "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; + + 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] == '_') + { + int n = (strlen (type) - 3)*2 + 3 + 2 + 1; + char *tem = (char *) xmalloc (n); + strcpy (tem, type + 3); + strcat (tem, "::~"); + strcat (tem, type + 3); + strcat (tem, "()"); + return tem; + } + /* static data member */ + if (*type != '_' && (p = strchr (type, '$')) != '\0') + { + 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; + } + 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; + + 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); + } + p += n; + 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; + 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 **) xrealloc ((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; + + 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; + string_appends (decl, &arg); + string_delete (&arg); + need_comma = 1; + } + } + + 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 *) xrealloc (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)); +} + +#if 0 +static void +string_prepends (p, s) + string *p, *s; +{ + if (s->b == s->p) + return; + string_prependn (p, s->b, s->p - s->b); +} +#endif + +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; +} diff --git a/binutils-1.9/error.c b/binutils-1.9/error.c new file mode 100644 index 0000000..9f989e2 --- /dev/null +++ b/binutils-1.9/error.c @@ -0,0 +1,104 @@ +/* error.c -- error handler for noninteractive utilities + Copyright (C) 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. */ + +/* David MacKenzie */ + +#include <stdio.h> + +#ifndef VPRINTF_MISSING + +#ifdef __STDC__ +#include <stdarg.h> +#else +#include <varargs.h> +#endif + +#else + +#ifndef DOPRNT_MISSING +#define va_alist args +#define va_dcl int args; +#else +#define va_alist a1, a2, a3, a4, a5, a6, a7, a8 +#define va_dcl char *a1, *a2, *a3, *a4, *a5, *a6, *a7, *a8; +#endif + +#endif + +#ifdef STDC_HEADERS +#include <stdlib.h> +#include <string.h> +#else +void exit (); + +static char * +strerror (errnum) + int errnum; +{ + extern char *sys_errlist[]; + extern int sys_nerr; + + if (errnum > 0 && errnum < sys_nerr) + return sys_errlist[errnum]; + return "Unknown system error"; +} +#endif + +/* Print the program name and error message MESSAGE, which is a printf-style + format string with optional args. + If ERRNUM is nonzero, print its corresponding system error message. + Exit with status STATUS if it is nonzero. */ +/* VARARGS */ +void +#if !defined (VPRINTF_MISSING) && defined (__STDC__) +error (int status, int errnum, char *message, ...) +#else +error (status, errnum, message, va_alist) + int status; + int errnum; + char *message; + va_dcl +#endif +{ + extern char *program_name; +#ifndef VPRINTF_MISSING + va_list args; +#endif + + fprintf (stderr, "%s: ", program_name); +#ifndef VPRINTF_MISSING +#ifdef __STDC__ + va_start (args, message); +#else + va_start (args); +#endif + vfprintf (stderr, message, args); + va_end (args); +#else +#ifndef DOPRNT_MISSING + _doprnt (message, &args, stderr); +#else + fprintf (stderr, message, a1, a2, a3, a4, a5, a6, a7, a8); +#endif +#endif + if (errnum) + fprintf (stderr, ": %s", strerror (errnum)); + putc ('\n', stderr); + fflush (stderr); + if (status) + exit (status); +} diff --git a/binutils-1.9/getopt.c b/binutils-1.9/getopt.c new file mode 100644 index 0000000..c3c9765 --- /dev/null +++ b/binutils-1.9/getopt.c @@ -0,0 +1,596 @@ +/* Getopt for GNU. + Copyright (C) 1987, 1989, 1990, 1991 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. */ + +#ifdef __STDC__ +#define CONST const +#else +#define CONST +#endif + +/* This version of `getopt' appears to the caller like standard Unix `getopt' + but it behaves differently for the user, since it allows the user + to intersperse the options with the other arguments. + + As `getopt' works, it permutes the elements of `argv' so that, + when it is done, all the options precede everything else. Thus + all application programs are extended to handle flexible argument order. + + Setting the environment variable _POSIX_OPTION_ORDER disables permutation. + Then the behavior is completely standard. + + GNU application programs can use a third alternative mode in which + they can distinguish the relative order of options and other arguments. */ + +#include <stdio.h> + +/* If compiled with GNU C, use the built-in alloca */ +#ifdef __GNUC__ +#define alloca __builtin_alloca +#else /* not __GNUC__ */ +#ifdef sparc +#include <alloca.h> +#else +char *alloca (); +#endif /* sparc */ +#endif /* not __GNUC__ */ + +#if defined(STDC_HEADERS) || defined(__GNU_LIBRARY__) +#include <stdlib.h> +#include <string.h> +#define bcopy(s, d, n) memcpy ((d), (s), (n)) +#define index strchr +#else /* STDC_HEADERS or __GNU_LIBRARY__ */ + +#ifdef USG +#include <string.h> +#define bcopy(s, d, n) memcpy ((d), (s), (n)) +#define index strchr +#else /* USG */ +#ifdef VMS +#include <string.h> +#else +#include <strings.h> +#endif /* VMS */ +/* Declaring bcopy causes errors on systems whose declarations are different. + If the declaration is omitted, everything works fine. rms. */ +#endif /* USG */ + +char *getenv (); +char *malloc (); +#endif /* STDC_HEADERS or __GNU_LIBRARY__ */ + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +char *optarg = 0; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns EOF, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +int optind = 0; + +/* The next char to be scanned in the option-element + in which the last option character we returned was found. + This allows us to pick up the scan where we left off. + + If this is zero, or a null string, it means resume the scan + by advancing to the next ARGV-element. */ + +static char *nextchar; + +/* Callers store zero here to inhibit the error message + for unrecognized options. */ + +int opterr = 1; + +/* Describe how to deal with options that follow non-option ARGV-elements. + + If the caller did not specify anything, + the default is REQUIRE_ORDER if the environment variable + _POSIX_OPTION_ORDER is defined, PERMUTE otherwise. + + REQUIRE_ORDER means don't recognize them as options; + stop option processing when the first non-option is seen. + This is what Unix does. + This mode of operation is selected by either setting the environment + variable _POSIX_OPTION_ORDER, or using `+' as the first character + of the list of option characters. + + PERMUTE is the default. We permute the contents of ARGV as we scan, + so that eventually all the non-options are at the end. This allows options + to be given in any order, even with programs that were not written to + expect this. + + RETURN_IN_ORDER is an option available to programs that were written + to expect options and other ARGV-elements in any order and that care about + the ordering of the two. We describe each non-option ARGV-element + as if it were the argument of an option with character code 1. + Using `-' as the first character of the list of option characters + selects this mode of operation. + + The special argument `--' forces an end of option-scanning regardless + of the value of `ordering'. In the case of RETURN_IN_ORDER, only + `--' can cause `getopt' to return EOF with `optind' != ARGC. */ + +static enum +{ + REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER +} ordering; + +/* Describe the long-named options requested by the application. + _GETOPT_LONG_OPTIONS is a vector of `struct option' terminated by an + element containing a name which is zero. + The field `has_arg' is 1 if the option takes an argument, + 2 if it takes an optional argument. */ + +struct option +{ + char *name; + int has_arg; + int *flag; + int val; +}; + +CONST struct option *_getopt_long_options; + +int _getopt_long_only = 0; + +/* Index in _GETOPT_LONG_OPTIONS of the long-named option actually found. + Only valid when a long-named option was found. */ + +int option_index; + +/* Handle permutation of arguments. */ + +/* Describe the part of ARGV that contains non-options that have + been skipped. `first_nonopt' is the index in ARGV of the first of them; + `last_nonopt' is the index after the last of them. */ + +static int first_nonopt; +static int last_nonopt; + +/* Exchange two adjacent subsequences of ARGV. + One subsequence is elements [first_nonopt,last_nonopt) + which contains all the non-options that have been skipped so far. + The other is elements [last_nonopt,optind), which contains all + the options processed since those non-options were skipped. + + `first_nonopt' and `last_nonopt' are relocated so that they describe + the new indices of the non-options in ARGV after they are moved. */ + +static void +exchange (argv) + char **argv; +{ + int nonopts_size = (last_nonopt - first_nonopt) * sizeof (char *); + char **temp = (char **) alloca (nonopts_size); + + /* Interchange the two blocks of data in ARGV. */ + + bcopy (&argv[first_nonopt], temp, nonopts_size); + bcopy (&argv[last_nonopt], &argv[first_nonopt], + (optind - last_nonopt) * sizeof (char *)); + bcopy (temp, &argv[first_nonopt + optind - last_nonopt], nonopts_size); + + /* Update records for the slots the non-options now occupy. */ + + first_nonopt += (optind - last_nonopt); + last_nonopt = optind; +} + +/* Scan elements of ARGV (whose length is ARGC) for option characters + given in OPTSTRING. + + If an element of ARGV starts with '-', and is not exactly "-" or "--", + then it is an option element. The characters of this element + (aside from the initial '-') are option characters. If `getopt' + is called repeatedly, it returns successively each of the option characters + from each of the option elements. + + If `getopt' finds another option character, it returns that character, + updating `optind' and `nextchar' so that the next call to `getopt' can + resume the scan with the following option character or ARGV-element. + + If there are no more option characters, `getopt' returns `EOF'. + Then `optind' is the index in ARGV of the first ARGV-element + that is not an option. (The ARGV-elements have been permuted + so that those that are not options now come last.) + + OPTSTRING is a string containing the legitimate option characters. + If an option character is seen that is not listed in OPTSTRING, + return '?' after printing an error message. If you set `opterr' to + zero, the error message is suppressed but we still return '?'. + + If a char in OPTSTRING is followed by a colon, that means it wants an arg, + so the following text in the same ARGV-element, or the text of the following + ARGV-element, is returned in `optarg'. Two colons mean an option that + wants an optional arg; if there is text in the current ARGV-element, + it is returned in `optarg', otherwise `optarg' is set to zero. + + If OPTSTRING starts with `-' or `+', it requests different methods of + handling the non-option ARGV-elements. + See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. + + Long-named options begin with `+' instead of `-'. + Their names may be abbreviated as long as the abbreviation is unique + or is an exact match for some defined option. If they have an + argument, it follows the option name in the same ARGV-element, separated + from the option name by a `=', or else the in next ARGV-element. + When `getopt' finds a long-named option, it returns 0 if that option's + `flag' field is nonzero, the value of the option's `val' field + otherwise. */ + +int +getopt (argc, argv, optstring) + int argc; + char **argv; + CONST char *optstring; +{ + optarg = 0; + + /* Initialize the internal data when the first call is made. + Start processing options with ARGV-element 1 (since ARGV-element 0 + is the program name); the sequence of previously skipped + non-option ARGV-elements is empty. */ + + if (optind == 0) + { + first_nonopt = last_nonopt = optind = 1; + + nextchar = 0; + + /* Determine how to handle the ordering of options and nonoptions. */ + + if (optstring[0] == '-') + { + ordering = RETURN_IN_ORDER; + ++optstring; + } + else if (optstring[0] == '+') + { + ordering = REQUIRE_ORDER; + ++optstring; + } + else if (getenv ("_POSIX_OPTION_ORDER") != 0) + ordering = REQUIRE_ORDER; + else + ordering = PERMUTE; + } + + if (nextchar == 0 || *nextchar == 0) + { + if (ordering == PERMUTE) + { + /* If we have just processed some options following some non-options, + exchange them so that the options come first. */ + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange (argv); + else if (last_nonopt != optind) + first_nonopt = optind; + + /* Now skip any additional non-options + and extend the range of non-options previously skipped. */ + + while (optind < argc + && (argv[optind][0] != '-' + || argv[optind][1] == 0) + && (_getopt_long_options == 0 + || argv[optind][0] != '+' + || argv[optind][1] == 0)) + optind++; + last_nonopt = optind; + } + + /* Special ARGV-element `--' means premature end of options. + Skip it like a null option, + then exchange with previous non-options as if it were an option, + then skip everything else like a non-option. */ + + if (optind != argc && !strcmp (argv[optind], "--")) + { + optind++; + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange (argv); + else if (first_nonopt == last_nonopt) + first_nonopt = optind; + last_nonopt = argc; + + optind = argc; + } + + /* If we have done all the ARGV-elements, stop the scan + and back over any non-options that we skipped and permuted. */ + + if (optind == argc) + { + /* Set the next-arg-index to point at the non-options + that we previously skipped, so the caller will digest them. */ + if (first_nonopt != last_nonopt) + optind = first_nonopt; + return EOF; + } + + /* If we have come to a non-option and did not permute it, + either stop the scan or describe it to the caller and pass it by. */ + + if ((argv[optind][0] != '-' || argv[optind][1] == 0) + && (_getopt_long_options == 0 + || argv[optind][0] != '+' || argv[optind][1] == 0)) + { + if (ordering == REQUIRE_ORDER) + return EOF; + optarg = argv[optind++]; + return 1; + } + + /* We have found another option-ARGV-element. + Start decoding its characters. */ + + nextchar = argv[optind] + 1; + } + + if (_getopt_long_options != 0 + && (argv[optind][0] == '+' + || (_getopt_long_only && argv[optind][0] == '-')) + ) + { + CONST struct option *p; + char *s = nextchar; + int exact = 0; + int ambig = 0; + CONST struct option *pfound = 0; + int indfound; + + while (*s && *s != '=') + s++; + + /* Test all options for either exact match or abbreviated matches. */ + for (p = _getopt_long_options, option_index = 0; p->name; + p++, option_index++) + if (!strncmp (p->name, nextchar, s - nextchar)) + { + if (s - nextchar == strlen (p->name)) + { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == 0) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else + /* Second nonexact match found. */ + ambig = 1; + } + + if (ambig && !exact) + { + fprintf (stderr, "%s: option `%s' is ambiguous\n", + argv[0], argv[optind]); + nextchar += strlen (nextchar); + optind++; + return '?'; + } + + if (pfound != 0) + { + option_index = indfound; + optind++; + if (*s) + { + if (pfound->has_arg > 0) + optarg = s + 1; + else + { + fprintf (stderr, + "%s: option `%c%s' doesn't allow an argument\n", + argv[0], argv[optind - 1][0], pfound->name); + nextchar += strlen (nextchar); + return '?'; + } + } + else if (pfound->has_arg == 1) + { + if (optind < argc) + optarg = argv[optind++]; + else + { + fprintf (stderr, "%s: option `%s' requires an argument\n", + argv[0], argv[optind - 1]); + nextchar += strlen (nextchar); + return '?'; + } + } + nextchar += strlen (nextchar); + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + /* Can't find it as a long option. If this is getopt_long_only, + and the option starts with '-' and is a valid short + option, then interpret it as a short option. Otherwise it's + an error. */ + if (_getopt_long_only == 0 || argv[optind][0] == '+' || + index (optstring, *nextchar) == 0) + { + if (opterr != 0) + fprintf (stderr, "%s: unrecognized option `%c%s'\n", + argv[0], argv[optind][0], nextchar); + nextchar += strlen (nextchar); + optind++; + return '?'; + } + } + + /* Look at and handle the next option-character. */ + + { + char c = *nextchar++; + char *temp = index (optstring, c); + + /* Increment `optind' when we start to process its last character. */ + if (*nextchar == 0) + optind++; + + if (temp == 0 || c == ':') + { + if (opterr != 0) + { + if (c < 040 || c >= 0177) + fprintf (stderr, "%s: unrecognized option, character code 0%o\n", + argv[0], c); + else + fprintf (stderr, "%s: unrecognized option `-%c'\n", + argv[0], c); + } + return '?'; + } + if (temp[1] == ':') + { + if (temp[2] == ':') + { + /* This is an option that accepts an argument optionally. */ + if (*nextchar != 0) + { + optarg = nextchar; + optind++; + } + else + optarg = 0; + nextchar = 0; + } + else + { + /* This is an option that requires an argument. */ + if (*nextchar != 0) + { + optarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + optind++; + } + else if (optind == argc) + { + if (opterr != 0) + fprintf (stderr, "%s: option `-%c' requires an argument\n", + argv[0], c); + c = '?'; + } + else + /* We already incremented `optind' once; + increment it again when taking next ARGV-elt as argument. */ + optarg = argv[optind++]; + nextchar = 0; + } + } + return c; + } +} + +#ifdef TEST + +/* Compile with -DTEST to make an executable for use in testing + the above definition of `getopt'. */ + +int +main (argc, argv) + int argc; + char **argv; +{ + int c; + int digit_optind = 0; + + while (1) + { + int this_option_optind = optind ? optind : 1; + + c = getopt (argc, argv, "abc:d:0123456789"); + if (c == EOF) + break; + + switch (c) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (digit_optind != 0 && digit_optind != this_option_optind) + printf ("digits occur in two different argv-elements.\n"); + digit_optind = this_option_optind; + printf ("option %c\n", c); + break; + + case 'a': + printf ("option a\n"); + break; + + case 'b': + printf ("option b\n"); + break; + + case 'c': + printf ("option c with value `%s'\n", optarg); + break; + + case '?': + break; + + default: + printf ("?? getopt returned character code 0%o ??\n", c); + } + } + + if (optind < argc) + { + printf ("non-option ARGV-elements: "); + while (optind < argc) + printf ("%s ", argv[optind++]); + printf ("\n"); + } + + exit (0); +} + +#endif /* TEST */ diff --git a/binutils-1.9/getopt.h b/binutils-1.9/getopt.h new file mode 100644 index 0000000..6583ab9 --- /dev/null +++ b/binutils-1.9/getopt.h @@ -0,0 +1,102 @@ +/* declarations for getopt + Copyright (C) 1989, 1990, 1991 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. */ + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +extern char *optarg; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns EOF, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +extern int optind; + +/* Callers store zero here to inhibit the error message `getopt' prints + for unrecognized options. */ + +extern int opterr; + +/* Describe the long-named options requested by the application. + _GETOPT_LONG_OPTIONS is a vector of `struct option' terminated by an + element containing a name which is zero. + + The field `has_arg' is: + 0 if the option does not take an argument, + 1 if the option requires an argument, + 2 if the option takes an optional argument. + + If the field `flag' is nonzero, it points to a variable that is set + to the value given in the field `val' when the option is found, but + left unchanged if the option is not found. + + To have a long-named option do something other than set an `int' to + a compiled-in constant, such as set a value from `optarg', set the + option's `flag' field to zero and its `val' field to a nonzero + value (the equivalent single-letter option character, if there is + one). For long options that have a zero `flag' field, `getopt' + returns the contents of the `val' field. */ + +struct option +{ + char *name; + int has_arg; + int *flag; + int val; +}; + +#ifdef __STDC__ +extern const struct option *_getopt_long_options; +#else +extern struct option *_getopt_long_options; +#endif + +/* If nonzero, '-' can introduce long-named options. + Set by getopt_long_only. */ + +extern int _getopt_long_only; + +/* The index in GETOPT_LONG_OPTIONS of the long-named option found. + Only valid when a long-named option has been found by the most + recent call to `getopt'. */ + +extern int option_index; + +#ifdef __STDC__ +int getopt (int argc, char **argv, const char *shortopts); +int getopt_long (int argc, char **argv, const char *shortopts, + const struct option *longopts, int *longind); +int getopt_long_only (int argc, char **argv, const char *shortopts, + const struct option *longopts, int *longind); +void envopt(int *pargc, char ***pargv, char *optstr); +#else +int getopt (); +int getopt_long (); +int getopt_long_only (); +void envopt(); +#endif diff --git a/binutils-1.9/getopt1.c b/binutils-1.9/getopt1.c new file mode 100644 index 0000000..d0d783d --- /dev/null +++ b/binutils-1.9/getopt1.c @@ -0,0 +1,160 @@ +/* Getopt for GNU. + Copyright (C) 1987, 1989, 1991 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 "getopt.h" + +#ifdef __STDC__ +#define CONST const +#else +#define CONST +#endif + +#if !defined (NULL) +#define NULL 0 +#endif + +int +getopt_long (argc, argv, options, long_options, opt_index) + int argc; + char **argv; + CONST char *options; + CONST struct option *long_options; + int *opt_index; +{ + int val; + + _getopt_long_options = long_options; + val = getopt (argc, argv, options); + if (val == 0 && opt_index != NULL) + *opt_index = option_index; + return val; +} + +/* Like getopt_long, but '-' as well as '+' can indicate a long option. + If an option that starts with '-' doesn't match a long option, + but does match a short option, it is parsed as a short option + instead. */ + +int +getopt_long_only (argc, argv, options, long_options, opt_index) + int argc; + char **argv; + CONST char *options; + CONST struct option *long_options; + int *opt_index; +{ + int val; + + _getopt_long_options = long_options; + _getopt_long_only = 1; + val = getopt (argc, argv, options); + if (val == 0 && opt_index != NULL) + *opt_index = option_index; + return val; +} + + +#ifdef TEST + +#include <stdio.h> + +int +main (argc, argv) + int argc; + char **argv; +{ + int c; + int digit_optind = 0; + + while (1) + { + int this_option_optind = optind ? optind : 1; + char *name = '\0'; + int option_index = 0; + static struct option long_options[] = + { + {"add", 1, 0, 0}, + {"append", 0, 0, 0}, + {"delete", 1, 0, 0}, + {"verbose", 0, 0, 0}, + {"create", 0, 0, 0}, + {"file", 1, 0, 0}, + {0, 0, 0, 0} + }; + + c = getopt_long (argc, argv, "abc:d:0123456789", + long_options, &option_index); + if (c == EOF) + break; + + switch (c) + { + case 0: + printf ("option %s", (long_options[option_index]).name); + if (optarg) + printf (" with arg %s", optarg); + printf ("\n"); + break; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (digit_optind != 0 && digit_optind != this_option_optind) + printf ("digits occur in two different argv-elements.\n"); + digit_optind = this_option_optind; + printf ("option %c\n", c); + break; + + case 'a': + printf ("option a\n"); + break; + + case 'b': + printf ("option b\n"); + break; + + case 'c': + printf ("option c with value `%s'\n", optarg); + break; + + case '?': + break; + + default: + printf ("?? getopt returned character code 0%o ??\n", c); + } + } + + if (optind < argc) + { + printf ("non-option ARGV-elements: "); + while (optind < argc) + printf ("%s ", argv[optind++]); + printf ("\n"); + } + + exit (0); +} + +#endif /* TEST */ diff --git a/binutils-1.9/gmalloc.c b/binutils-1.9/gmalloc.c new file mode 100644 index 0000000..0468551 --- /dev/null +++ b/binutils-1.9/gmalloc.c @@ -0,0 +1,1116 @@ + +/* gmalloc.c - THIS FILE IS AUTOMAGICALLY GENERATED SO DON'T EDIT IT. */ + +/* Single-file skeleton for GNU malloc. + Copyright 1989 Free Software Foundation + Written May 1989 by Mike Haertel. + + 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. + + The author may be reached (Email) at the address mike@ai.mit.edu, + or (US mail) as Mike Haertel c/o Free Software Foundation. */ + +#define __ONEFILE + +/* DO NOT DELETE THIS LINE -- ansidecl.h INSERTED HERE. */ +/* Copyright (C) 1989 Free Software Foundation, Inc. +This file is part of the GNU C Library. + +The GNU C Library 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. + +The GNU C Library 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 the GNU C Library; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* ANSI and traditional C compatibility macros + + ANSI C is assumed if __STDC__ is #defined. + + Macros + PTR - Generic pointer type + LONG_DOUBLE - `long double' type + CONST - `const' keyword + VOLATILE - `volatile' keyword + SIGNED - `signed' keyword + PTRCONST - Generic const pointer (void *const) + + EXFUN(name, prototype) - declare external function NAME + with prototype PROTOTYPE + DEFUN(name, arglist, args) - define function NAME with + args ARGLIST of types in ARGS + DEFUN_VOID(name) - define function NAME with no args + AND - argument separator for ARGS + NOARGS - null arglist + DOTS - `...' in args + + For example: + extern int EXFUN(printf, (CONST char *format DOTS)); + int DEFUN(fprintf, (stream, format), + FILE *stream AND CONST char *format DOTS) { ... } + void DEFUN_VOID(abort) { ... } +*/ + +#ifndef _ANSIDECL_H + +#define _ANSIDECL_H 1 + + +/* Every source file includes this file, + so they will all get the switch for lint. */ +/* LINTLIBRARY */ + + +#ifdef __STDC__ + +#define PTR void * +#define PTRCONST void *CONST +#define LONG_DOUBLE long double + +#define AND , +#define NOARGS void +#define CONST const +#define VOLATILE volatile +#define SIGNED signed +#define DOTS , ... + +#define EXFUN(name, proto) name proto +#define DEFUN(name, arglist, args) name(args) +#define DEFUN_VOID(name) name(NOARGS) + +#else /* Not ANSI C. */ + +#define PTR char * +#define PTRCONST PTR +#define LONG_DOUBLE double + +#define AND ; +#define NOARGS +#define CONST +#define VOLATILE +#define SIGNED +#define DOTS + +#define EXFUN(name, proto) name() +#define DEFUN(name, arglist, args) name arglist args; +#define DEFUN_VOID(name) name() + +#endif /* ANSI C. */ + + +#endif /* ansidecl.h */ + +#ifdef __STDC__ +#include <limits.h> +#else +/* DO NOT DELETE THIS LINE -- limits.h INSERTED HERE. */ +/* Number of bits in a `char'. */ +#define CHAR_BIT 8 + +/* No multibyte characters supported yet. */ +#define MB_LEN_MAX 1 + +/* Minimum and maximum values a `signed char' can hold. */ +#define SCHAR_MIN -128 +#define SCHAR_MAX 127 + +/* Maximum value an `unsigned char' can hold. (Minimum is 0). */ +#define UCHAR_MAX 255U + +/* Minimum and maximum values a `char' can hold. */ +#ifdef __CHAR_UNSIGNED__ +#define CHAR_MIN 0 +#define CHAR_MAX 255U +#else +#define CHAR_MIN -128 +#define CHAR_MAX 127 +#endif + +/* Minimum and maximum values a `signed short int' can hold. */ +#define SHRT_MIN -32768 +#define SHRT_MAX 32767 + +/* Maximum value an `unsigned short int' can hold. (Minimum is 0). */ +#define USHRT_MAX 65535U + +/* Minimum and maximum values a `signed int' can hold. */ +#define INT_MIN -2147483648 +#define INT_MAX 2147483647 + +/* Maximum value an `unsigned int' can hold. (Minimum is 0). */ +#define UINT_MAX 4294967295U + +/* Minimum and maximum values a `signed long int' can hold. + (Same as `int'). */ +#define LONG_MIN (-LONG_MAX-1) +#define LONG_MAX 2147483647 + +/* Maximum value an `unsigned long int' can hold. (Minimum is 0). */ +#define ULONG_MAX 4294967295U +#endif + +#ifdef __STDC__ +#include <stddef.h> +#else +/* DO NOT DELETE THIS LINE -- stddef.h INSERTED HERE. */ +#ifndef _STDDEF_H +#define _STDDEF_H + +/* Signed type of difference of two pointers. */ + +typedef long ptrdiff_t; + +/* Unsigned type of `sizeof' something. */ + +#ifndef _SIZE_T /* in case <sys/types.h> has defined it. */ +#define _SIZE_T +typedef unsigned long size_t; +#endif /* _SIZE_T */ + +/* A null pointer constant. */ + +#undef NULL /* in case <stdio.h> has defined it. */ +#define NULL 0 + +/* Offset of member MEMBER in a struct of type TYPE. */ + +#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) + +#endif /* _STDDEF_H */ +#endif + +/* DO NOT DELETE THIS LINE -- stdlib.h INSERTED HERE. */ +/* Fake stdlib.h supplying the stuff needed by malloc. */ + +#ifndef __ONEFILE +#include <stddef.h> +#endif + +extern void EXFUN(abort, (void)); +extern void EXFUN(free, (PTR)); +extern PTR EXFUN(malloc, (size_t)); +extern PTR EXFUN(realloc, (PTR, size_t)); + +/* DO NOT DELETE THIS LINE -- string.h INSERTED HERE. */ +/* Fake string.h supplying stuff used by malloc. */ +#ifndef __ONEFILE +#include <stddef.h> +#endif + +extern PTR EXFUN(memcpy, (PTR, PTR, size_t)); +extern PTR EXFUN(memset, (PTR, int, size_t)); +#define memmove memcpy + +#define _MALLOC_INTERNAL +/* DO NOT DELETE THIS LINE -- malloc.h INSERTED HERE. */ +/* Declarations for `malloc' and friends. + Copyright 1990 Free Software Foundation + Written May 1989 by Mike Haertel. + + 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. + + The author may be reached (Email) at the address mike@@ai.mit.edu, + or (US mail) as Mike Haertel c/o Free Software Foundation. */ + +#ifndef _MALLOC_H + +#define _MALLOC_H 1 + +#ifndef __ONEFILE +#define __need_NULL +#define __need_size_t +#define __need_ptrdiff_t +#include <stddef.h> +#endif + +#ifdef _MALLOC_INTERNAL + +#ifndef __ONEFILE +#include <limits.h> +#endif + +/* The allocator divides the heap into blocks of fixed size; large + requests receive one or more whole blocks, and small requests + receive a fragment of a block. Fragment sizes are powers of two, + and all fragments of a block are the same size. When all the + fragments in a block have been freed, the block itself is freed. */ +#define INT_BIT (CHAR_BIT * sizeof(int)) +#define BLOCKLOG (INT_BIT > 16 ? 12 : 9) +#define BLOCKSIZE (1 << BLOCKLOG) +#define BLOCKIFY(SIZE) (((SIZE) + BLOCKSIZE - 1) / BLOCKSIZE) + +/* Determine the amount of memory spanned by the initial heap table + (not an absolute limit). */ +#define HEAP (INT_BIT > 16 ? 4194304 : 65536) + +/* Number of contiguous free blocks allowed to build up at the end of + memory before they will be returned to the system. */ +#define FINAL_FREE_BLOCKS 8 + +/* Where to start searching the free list when looking for new memory. + The two possible values are 0 and _heapindex. Starting at 0 seems + to reduce total memory usage, while starting at _heapindex seems to + run faster. */ +#define MALLOC_SEARCH_START _heapindex + +/* Data structure giving per-block information. */ +typedef union + { + /* Heap information for a busy block. */ + struct + { + /* Zero for a large block, or positive giving the + logarithm to the base two of the fragment size. */ + int type; + union + { + struct + { + size_t nfree; /* Free fragments in a fragmented block. */ + size_t first; /* First free fragment of the block. */ + } frag; + /* Size (in blocks) of a large cluster. */ + size_t size; + } info; + } busy; + /* Heap information for a free block (that may be the first of + a free cluster). */ + struct + { + size_t size; /* Size (in blocks) of a free cluster. */ + size_t next; /* Index of next free cluster. */ + size_t prev; /* Index of previous free cluster. */ + } free; + } malloc_info; + +/* Pointer to first block of the heap. */ +extern char *_heapbase; + +/* Table indexed by block number giving per-block information. */ +extern malloc_info *_heapinfo; + +/* Address to block number and vice versa. */ +#define BLOCK(A) (((char *) (A) - _heapbase) / BLOCKSIZE + 1) +#define ADDRESS(B) ((PTR) (((B) - 1) * BLOCKSIZE + _heapbase)) + +/* Current search index for the heap table. */ +extern size_t _heapindex; + +/* Limit of valid info table indices. */ +extern size_t _heaplimit; + +/* Doubly linked lists of free fragments. */ +struct list + { + struct list *next; + struct list *prev; + }; + +/* Free list headers for each fragment size. */ +extern struct list _fraghead[]; + +/* Instrumentation. */ +extern size_t _chunks_used; +extern size_t _bytes_used; +extern size_t _chunks_free; +extern size_t _bytes_free; + +/* Internal version of free() used in morecore(). */ +extern void EXFUN(__free, (PTR __ptr)); + +#endif /* _MALLOC_INTERNAL. */ + +/* Underlying allocation function; successive calls should + return contiguous pieces of memory. */ +extern PTR EXFUN((*__morecore), (ptrdiff_t __size)); + +/* Default value of previous. */ +extern PTR EXFUN(__default_morecore, (ptrdiff_t __size)); + +/* Flag whether malloc has been called. */ +extern int __malloc_initialized; + +/* Hooks for debugging versions. */ +extern void EXFUN((*__free_hook), (PTR __ptr)); +extern PTR EXFUN((*__malloc_hook), (size_t __size)); +extern PTR EXFUN((*__realloc_hook), (PTR __ptr, size_t __size)); + +/* Activate a standard collection of debugging hooks. */ +extern void EXFUN(mcheck, (void EXFUN((*func), (void)))); + +/* Statistics available to the user. */ +struct mstats + { + size_t bytes_total; /* Total size of the heap. */ + size_t chunks_used; /* Chunks allocated by the user. */ + size_t bytes_used; /* Byte total of user-allocated chunks. */ + size_t chunks_free; /* Chunks in the free list. */ + size_t bytes_free; /* Byte total of chunks in the free list. */ + }; + +/* Pick up the current statistics. */ +extern struct mstats EXFUN(mstats, (NOARGS)); + +#endif /* malloc.h */ + +/* DO NOT DELETE THIS LINE -- free.c INSERTED HERE. */ +/* Free a block of memory allocated by `malloc'. + Copyright 1990 Free Software Foundation + Written May 1989 by Mike Haertel. + + 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. + + The author may be reached (Email) at the address mike@ai.mit.edu, + or (US mail) as Mike Haertel c/o Free Software Foundation. */ + +#ifndef __ONEFILE +#include "ansidecl.h" +#include <stddef.h> +#include <stdlib.h> + +#define _MALLOC_INTERNAL +#include "malloc.h" +#endif /* __ONEFILE */ + +/* Debugging hook for free. */ +void EXFUN((*__free_hook), (PTR __ptr)); + +/* Return memory to the heap. Like free() but don't call a __free_hook + if there is one. */ +void +DEFUN(__free, (ptr), PTR ptr) +{ + int type; + size_t block, blocks; + register size_t i; + struct list *prev, *next; + + block = BLOCK(ptr); + + type = _heapinfo[block].busy.type; + switch (type) + { + case 0: + /* Get as many statistics as early as we can. */ + --_chunks_used; + _bytes_used -= _heapinfo[block].busy.info.size * BLOCKSIZE; + _bytes_free += _heapinfo[block].busy.info.size * BLOCKSIZE; + + /* Find the free cluster previous to this one in the free list. + Start searching at the last block referenced; this may benefit + programs with locality of allocation. */ + i = _heapindex; + if (i > block) + while (i > block) + i = _heapinfo[i].free.prev; + else + { + do + i = _heapinfo[i].free.next; + while (i > 0 && i < block); + i = _heapinfo[i].free.prev; + } + + /* Determine how to link this block into the free list. */ + if (block == i + _heapinfo[i].free.size) + { + /* Coalesce this block with its predecessor. */ + _heapinfo[i].free.size += _heapinfo[block].busy.info.size; + block = i; + } + else + { + /* Really link this block back into the free list. */ + _heapinfo[block].free.size = _heapinfo[block].busy.info.size; + _heapinfo[block].free.next = _heapinfo[i].free.next; + _heapinfo[block].free.prev = i; + _heapinfo[i].free.next = block; + _heapinfo[_heapinfo[block].free.next].free.prev = block; + ++_chunks_free; + } + + /* Now that the block is linked in, see if we can coalesce it + with its successor (by deleting its successor from the list + and adding in its size). */ + if (block + _heapinfo[block].free.size == _heapinfo[block].free.next) + { + _heapinfo[block].free.size + += _heapinfo[_heapinfo[block].free.next].free.size; + _heapinfo[block].free.next + = _heapinfo[_heapinfo[block].free.next].free.next; + _heapinfo[_heapinfo[block].free.next].free.prev = block; + --_chunks_free; + } + + /* Now see if we can return stuff to the system. */ + blocks = _heapinfo[block].free.size; + if (blocks >= FINAL_FREE_BLOCKS && block + blocks == _heaplimit + && (*__morecore)(0) == ADDRESS(block + blocks)) + { + register size_t bytes = blocks * BLOCKSIZE; + _heaplimit -= blocks; + (*__morecore)(- bytes); + _heapinfo[_heapinfo[block].free.prev].free.next + = _heapinfo[block].free.next; + _heapinfo[_heapinfo[block].free.next].free.prev + = _heapinfo[block].free.prev; + block = _heapinfo[block].free.prev; + --_chunks_free; + _bytes_free -= bytes; + } + + /* Set the next search to begin at this block. */ + _heapindex = block; + break; + + default: + /* Do some of the statistics. */ + --_chunks_used; + _bytes_used -= 1 << type; + ++_chunks_free; + _bytes_free += 1 << type; + + /* Get the address of the first free fragment in this block. */ + prev = (struct list *) ((char *) ADDRESS(block) + + (_heapinfo[block].busy.info.frag.first << type)); + + if (_heapinfo[block].busy.info.frag.nfree == (BLOCKSIZE >> type) - 1) + { + /* If all fragments of this block are free, remove them + from the fragment list and free the whole block. */ + next = prev; + for (i = 1; i < BLOCKSIZE >> type; ++i) + next = next->next; + prev->prev->next = next; + if (next != NULL) + next->prev = prev->prev; + _heapinfo[block].busy.type = 0; + _heapinfo[block].busy.info.size = 1; + + /* Keep the statistics accurate. */ + ++_chunks_used; + _bytes_used += BLOCKSIZE; + _chunks_free -= BLOCKSIZE >> type; + _bytes_free -= BLOCKSIZE; + + free(ADDRESS(block)); + } + else if (_heapinfo[block].busy.info.frag.nfree != 0) + { + /* If some fragments of this block are free, link this + fragment into the fragment list after the first free + fragment of this block. */ + next = (struct list *) ptr; + next->next = prev->next; + next->prev = prev; + prev->next = next; + if (next->next != NULL) + next->next->prev = next; + ++_heapinfo[block].busy.info.frag.nfree; + } + else + { + /* No fragments of this block are free, so link this + fragment into the fragment list and announce that + it is the first free fragment of this block. */ + prev = (struct list *) ptr; + _heapinfo[block].busy.info.frag.nfree = 1; + _heapinfo[block].busy.info.frag.first = (unsigned int) + (((char *) ptr - (char *) NULL) % BLOCKSIZE >> type); + prev->next = _fraghead[type].next; + prev->prev = &_fraghead[type]; + prev->prev->next = prev; + if (prev->next != NULL) + prev->next->prev = prev; + } + break; + } +} + +/* Return memory to the heap. */ +void +DEFUN(free, (ptr), PTR ptr) +{ + if (ptr == NULL) + return; + + if (__free_hook != NULL) + (*__free_hook)(ptr); + else + __free (ptr); +} + +/* DO NOT DELETE THIS LINE -- malloc.c INSERTED HERE. */ +/* Memory allocator `malloc'. + Copyright 1990 Free Software Foundation + Written May 1989 by Mike Haertel. + + 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. + + The author may be reached (Email) at the address mike@ai.mit.edu, + or (US mail) as Mike Haertel c/o Free Software Foundation. */ + +#ifndef __ONEFILE +#include "ansidecl.h" +#include <stddef.h> +#include <stdlib.h> +#include <string.h> + +#define _MALLOC_INTERNAL +#include "malloc.h" +#endif /* __ONEFILE */ + +/* How to really get more memory. */ +PTR EXFUN((*__morecore), (ptrdiff_t __size)) = __default_morecore; + +/* Debugging hook for `malloc'. */ +PTR EXFUN((*__malloc_hook), (size_t __size)); + +/* Pointer to the base of the first block. */ +char *_heapbase; + +/* Block information table. Allocated with align/__free (not malloc/free). */ +malloc_info *_heapinfo; + +/* Number of info entries. */ +static size_t heapsize; + +/* Search index in the info table. */ +size_t _heapindex; + +/* Limit of valid info table indices. */ +size_t _heaplimit; + +/* Free lists for each fragment size. */ +struct list _fraghead[BLOCKLOG]; + +/* Instrumentation. */ +size_t _chunks_used; +size_t _bytes_used; +size_t _chunks_free; +size_t _bytes_free; + +/* Are you experienced? */ +int __malloc_initialized; + +/* Aligned allocation. */ +static PTR +DEFUN(align, (size), size_t size) +{ + PTR result; + unsigned int adj; + + result = (*__morecore)(size); + adj = (unsigned int) ((char *) result - (char *) NULL) % BLOCKSIZE; + if (adj != 0) + { + adj = BLOCKSIZE - adj; + (void) (*__morecore)(adj); + result = (char *) result + adj; + } + return result; +} + +/* Set everything up and remember that we have. */ +static int +DEFUN_VOID(initialize) +{ + heapsize = HEAP / BLOCKSIZE; + _heapinfo = (malloc_info *) align(heapsize * sizeof(malloc_info)); + if (_heapinfo == NULL) + return 0; + memset(_heapinfo, 0, heapsize * sizeof(malloc_info)); + _heapinfo[0].free.size = 0; + _heapinfo[0].free.next = _heapinfo[0].free.prev = 0; + _heapindex = 0; + _heapbase = (char *) _heapinfo; + __malloc_initialized = 1; + return 1; +} + +/* Get neatly aligned memory, initializing or + growing the heap info table as necessary. */ +static PTR +DEFUN(morecore, (size), size_t size) +{ + PTR result; + malloc_info *newinfo, *oldinfo; + size_t newsize; + + result = align(size); + if (result == NULL) + return NULL; + + /* Check if we need to grow the info table. */ + if (BLOCK((char *) result + size) > heapsize) + { + newsize = heapsize; + while (BLOCK((char *) result + size) > newsize) + newsize *= 2; + newinfo = (malloc_info *) align(newsize * sizeof(malloc_info)); + if (newinfo == NULL) + { + (*__morecore)(- size); + return NULL; + } + memset(newinfo, 0, newsize * sizeof(malloc_info)); + memcpy(newinfo, _heapinfo, heapsize * sizeof(malloc_info)); + oldinfo = _heapinfo; + newinfo[BLOCK(oldinfo)].busy.type = 0; + newinfo[BLOCK(oldinfo)].busy.info.size + = BLOCKIFY(heapsize * sizeof(malloc_info)); + _heapinfo = newinfo; + __free(oldinfo); + heapsize = newsize; + } + + _heaplimit = BLOCK((char *) result + size); + return result; +} + +/* Allocate memory from the heap. */ +PTR +DEFUN(malloc, (size), size_t size) +{ + PTR result; + size_t block, blocks, lastblocks, start; + register size_t i; + struct list *next; + + if (size == 0) + return NULL; + + if (__malloc_hook != NULL) + return (*__malloc_hook)(size); + + if (!__malloc_initialized) + if (!initialize()) + return NULL; + + if (size < sizeof(struct list)) + size = sizeof(struct list); + + /* Determine the allocation policy based on the request size. */ + if (size <= BLOCKSIZE / 2) + { + /* Small allocation to receive a fragment of a block. + Determine the logarithm to base two of the fragment size. */ + register size_t log = 1; + --size; + while ((size /= 2) != 0) + ++log; + + /* Look in the fragment lists for a + free fragment of the desired size. */ + next = _fraghead[log].next; + if (next != NULL) + { + /* There are free fragments of this size. + Pop a fragment out of the fragment list and return it. + Update the block's nfree and first counters. */ + result = (PTR) next; + next->prev->next = next->next; + if (next->next != NULL) + next->next->prev = next->prev; + block = BLOCK(result); + if (--_heapinfo[block].busy.info.frag.nfree != 0) + _heapinfo[block].busy.info.frag.first = (unsigned int) + (((char *) next->next - (char *) NULL) % BLOCKSIZE) >> log; + + /* Update the statistics. */ + ++_chunks_used; + _bytes_used += 1 << log; + --_chunks_free; + _bytes_free -= 1 << log; + } + else + { + /* No free fragments of the desired size, so get a new block + and break it into fragments, returning the first. */ + result = malloc(BLOCKSIZE); + if (result == NULL) + return NULL; + + /* Link all fragments but the first into the free list. */ + for (i = 1; i < BLOCKSIZE >> log; ++i) + { + next = (struct list *) ((char *) result + (i << log)); + next->next = _fraghead[log].next; + next->prev = &_fraghead[log]; + next->prev->next = next; + if (next->next != NULL) + next->next->prev = next; + } + + /* Initialize the nfree and first counters for this block. */ + block = BLOCK(result); + _heapinfo[block].busy.type = log; + _heapinfo[block].busy.info.frag.nfree = i - 1; + _heapinfo[block].busy.info.frag.first = i - 1; + + _chunks_free += (BLOCKSIZE >> log) - 1; + _bytes_free += BLOCKSIZE - (1 << log); + } + } + else + { + /* Large allocation to receive one or more blocks. + Search the free list in a circle starting at the last place visited. + If we loop completely around without finding a large enough + space we will have to get more memory from the system. */ + blocks = BLOCKIFY(size); + start = block = MALLOC_SEARCH_START; + while (_heapinfo[block].free.size < blocks) + { + block = _heapinfo[block].free.next; + if (block == start) + { + /* Need to get more from the system. Check to see if + the new core will be contiguous with the final free + block; if so we don't need to get as much. */ + block = _heapinfo[0].free.prev; + lastblocks = _heapinfo[block].free.size; + if (_heaplimit != 0 && block + lastblocks == _heaplimit && + (*__morecore)(0) == ADDRESS(block + lastblocks) && + (morecore((blocks - lastblocks) * BLOCKSIZE)) != NULL) + { + _heapinfo[block].free.size = blocks; + _bytes_free += (blocks - lastblocks) * BLOCKSIZE; + continue; + } + result = morecore(blocks * BLOCKSIZE); + if (result == NULL) + return NULL; + block = BLOCK(result); + _heapinfo[block].busy.type = 0; + _heapinfo[block].busy.info.size = blocks; + ++_chunks_used; + _bytes_used += blocks * BLOCKSIZE; + return result; + } + } + + /* At this point we have found a suitable free list entry. + Figure out how to remove what we need from the list. */ + result = ADDRESS(block); + if (_heapinfo[block].free.size > blocks) + { + /* The block we found has a bit left over, + so relink the tail end back into the free list. */ + _heapinfo[block + blocks].free.size + = _heapinfo[block].free.size - blocks; + _heapinfo[block + blocks].free.next + = _heapinfo[block].free.next; + _heapinfo[block + blocks].free.prev + = _heapinfo[block].free.prev; + _heapinfo[_heapinfo[block].free.prev].free.next + = _heapinfo[_heapinfo[block].free.next].free.prev + = _heapindex = block + blocks; + } + else + { + /* The block exactly matches our requirements, + so just remove it from the list. */ + _heapinfo[_heapinfo[block].free.next].free.prev + = _heapinfo[block].free.prev; + _heapinfo[_heapinfo[block].free.prev].free.next + = _heapindex = _heapinfo[block].free.next; + --_chunks_free; + } + + _heapinfo[block].busy.type = 0; + _heapinfo[block].busy.info.size = blocks; + ++_chunks_used; + _bytes_used += blocks * BLOCKSIZE; + _bytes_free -= blocks * BLOCKSIZE; + } + + return result; +} + +/* DO NOT DELETE THIS LINE -- realloc.c INSERTED HERE. */ +/* Change the size of a block allocated by `malloc'. + Copyright 1990 Free Software Foundation + Written May 1989 by Mike Haertel. + + 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. + + The author may be reached (Email) at the address mike@ai.mit.edu, + or (US mail) as Mike Haertel c/o Free Software Foundation. */ + +#ifndef __ONEFILE +#include "ansidecl.h" +#include <stdlib.h> +#include <string.h> + +#define _MALLOC_INTERNAL +#include "malloc.h" +#endif /* __ONEFILE */ + +#define MIN(A, B) ((A) < (B) ? (A) : (B)) + +/* Debugging hook for realloc. */ +PTR EXFUN((*__realloc_hook), (PTR __ptr, size_t __size)); + +/* Resize the given region to the new size, returning a pointer + to the (possibly moved) region. This is optimized for speed; + some benchmarks seem to indicate that greater compactness is + achieved by unconditionally allocating and copying to a + new region. This module has incestuous knowledge of the + internals of both free and malloc. */ +PTR +DEFUN(realloc, (ptr, size), PTR ptr AND size_t size) +{ + PTR result; + int type; + size_t block, blocks, oldlimit; + + if (size == 0) + { + free(ptr); + return NULL; + } + else if (ptr == NULL) + return malloc(size); + + if (__realloc_hook != NULL) + return (*__realloc_hook)(ptr, size); + + block = BLOCK(ptr); + + type = _heapinfo[block].busy.type; + switch (type) + { + case 0: + /* Maybe reallocate a large block to a small fragment. */ + if (size <= BLOCKSIZE / 2) + { + result = malloc(size); + if (result != NULL) + { + memcpy(result, ptr, size); + free(ptr); + return result; + } + } + + /* The new size is a large allocation as well; + see if we can hold it in place. */ + blocks = BLOCKIFY(size); + if (blocks < _heapinfo[block].busy.info.size) + { + /* The new size is smaller; return + excess memory to the free list. */ + _heapinfo[block + blocks].busy.type = 0; + _heapinfo[block + blocks].busy.info.size + = _heapinfo[block].busy.info.size - blocks; + _heapinfo[block].busy.info.size = blocks; + free(ADDRESS(block + blocks)); + result = ptr; + } + else if (blocks == _heapinfo[block].busy.info.size) + /* No size change necessary. */ + result = ptr; + else + { + /* Won't fit, so allocate a new region that will. + Free the old region first in case there is sufficient + adjacent free space to grow without moving. */ + blocks = _heapinfo[block].busy.info.size; + /* Prevent free from actually returning memory to the system. */ + oldlimit = _heaplimit; + _heaplimit = 0; + free(ptr); + _heaplimit = oldlimit; + result = malloc(size); + if (result == NULL) + { + (void) malloc(blocks * BLOCKSIZE); + return NULL; + } + if (ptr != result) + memmove(result, ptr, blocks * BLOCKSIZE); + } + break; + + default: + /* Old size is a fragment; type is logarithm + to base two of the fragment size. */ + if (size > 1 << (type - 1) && size <= 1 << type) + /* The new size is the same kind of fragment. */ + result = ptr; + else + { + /* The new size is different; allocate a new space, + and copy the lesser of the new size and the old. */ + result = malloc(size); + if (result == NULL) + return NULL; + memcpy(result, ptr, MIN(size, 1 << type)); + free(ptr); + } + break; + } + + return result; +} + +/* DO NOT DELETE THIS LINE -- unix.c INSERTED HERE. */ +/* unix.c - get more memory with a UNIX system call. + Copyright 1990 Free Software Foundation + Written May 1989 by Mike Haertel. + + 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. + + The author may be reached (Email) at the address mike@ai.mit.edu, + or (US mail) as Mike Haertel c/o Free Software Foundation. */ + +#ifndef __ONEFILE +#include "ansidecl.h" +#include <stddef.h> + +#define _MALLOC_INTERNAL +#include "malloc.h" +#endif /* __ONEFILE */ + +extern PTR EXFUN(sbrk, (ptrdiff_t size)); + +PTR +DEFUN(__default_morecore, (size), ptrdiff_t size) +{ + PTR result; + + result = sbrk(size); + if (result == (PTR) -1) + return NULL; + return result; +} + +#define __getpagesize getpagesize +/* DO NOT DELETE THIS LINE -- valloc.c INSERTED HERE. */ +/* Allocate memory on a page boundary. + Copyright 1990 Free Software Foundation + Written May 1989 by Mike Haertel. + + 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. + + The author may be reached (Email) at the address mike@ai.mit.edu, + or (US mail) as Mike Haertel c/o Free Software Foundation. */ + +#ifndef __ONEFILE +#include "ansidecl.h" +#include <stdlib.h> +#endif /* __ONEFILE */ + +extern size_t EXFUN(__getpagesize, (NOARGS)); + +static size_t pagesize; + +PTR +DEFUN(valloc, (size), size_t size) +{ + PTR result; + unsigned int adj; + + if (pagesize == 0) + pagesize = __getpagesize(); + + result = malloc(size + pagesize); + if (result == NULL) + return NULL; + adj = (unsigned int) ((char *) result - (char *) NULL) % pagesize; + if (adj != 0) + result = (char *) result + pagesize - adj; + return result; +} diff --git a/binutils-1.9/gmon.h b/binutils-1.9/gmon.h new file mode 100644 index 0000000..02f12fb --- /dev/null +++ b/binutils-1.9/gmon.h @@ -0,0 +1,48 @@ +/* Format of gmon.out file. */ + +/* This header appears at the beginning of the gmon.out file. + LOW and HIGH are low and high water marks for the program counter + during the creation of the gmon.out file. + LOW is also the offset where the histogram table starts in the + text (code) segment. + NBYTES is the number of bytes in this header plus the histogram itself, + which immediately follows the header in the file. + + Therefore, the number of histogram entries is + (NBYTES - sizeof (struct gm_header)) / (sizeof (CHUNK)). + + Each entry applies to a range of PC values. + The first entry applies to PC values starting at LOW. + The last entry applies to PC values ending at HIGH. + Therefore, the span of each entry's range is + (HIGH - LOW) / number-of-entries + Usually this value is 4. +*/ + +struct gm_header { + unsigned long low; + unsigned long high; + long nbytes; +}; + +/* Data type of an entry in the PC histogram. */ +#define CHUNK short + +/* After the histogram cone the function call count entries. + They fill all the rest of the file. + Each count entry records the number of calls to one function + from one pc value. + + FROM describes the caller pc, as an offset into the text segment. + TO is the address of the called function. + NCALLS is the number of calls counted from FROM to TO. + + Note that if a function A is called from several places in B, + there are separate call count entries for each call, with different FROM. + All of them together count the number of calls from B to A. */ + +struct gm_call { + unsigned long from; + unsigned long to; + unsigned long ncalls; +}; diff --git a/binutils-1.9/gprof.c b/binutils-1.9/gprof.c new file mode 100644 index 0000000..e3a7a73 --- /dev/null +++ b/binutils-1.9/gprof.c @@ -0,0 +1,2737 @@ +/* `gprof', analyze gmon.out and print a profile. + Copyright (C) 1988 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. */ + +/* GNU gprof was written mainly by Jay Fenlason. */ + +#include <assert.h> +#include "getopt.h" +#ifdef __STDC__ +#include "stddef.h" +#include "stdarg.h" +#ifdef __GNUC__ +#define alloca __builtin_alloca +#endif +#else /* no __STDC__ */ +#ifdef sparc +#include "alloca.h" +#endif /* sparc */ +#include "varargs.h" +#define const +typedef unsigned int size_t; +#endif /* no __STDC__ */ + +#undef NULL +#include <stdio.h> +#undef NULL +#define NULL 0 + +#if !defined(A_OUT) && !defined(MACH_O) +#define A_OUT +#endif + +#ifdef A_OUT +#ifdef COFF_ENCAPSULATE +#include "a.out.encap.h" +#else +#include <a.out.h> +#endif +#endif + +#ifdef MACH_O +#ifndef A_OUT +#include <nlist.h> +#ifndef N_TEXT +#define N_TEXT 0x04 +#endif +#endif +#include <sys/loader.h> +#endif + +#include "gmon.h" +/* #include <nlist.h> */ + +#ifdef USG +#define index strchr +#define bzero(s, n) (memset((s), 0, (n))) +#define bcopy(from, to, n) (memcpy ((to), (from), (n))) +#endif + +/* Special macros designed to remove some __STDC__ ugliness from my source + files. Instead, I use these (which may be just as ugly). Instead of using +extern foo(); + or +extern foo(int,double); + I use +extern foo FUN2(int, double); + which is expanded to the right thing depending on whether you're using an + ANSI cc or not. + + Also, instead of saying +type +foo(x,y) +int x; +double y; + or +type +foo(int x, double y) + I use +type +foo FUN2(int, x, double, y) +Which is also expanded into the right thing. . . + */ + +#ifdef __STDC__ +#define var_start(x,y) va_start(x,y) + +/* These macros expand into ANSI prototypes */ +#define FUN0() (void) +#define EXT0() (void) + +#define FUN1(t1,a1) (t1 a1) +#define EXT1(t1) (t1) +#define FUN1N(t1,a1) (t1 a1, ...) +#define EXT1N(t1) (t1, ...) + +#define FUN2(t1,a1,t2,a2) (t1 a1,t2 a2) +#define EXT2(t1, t2) (t1, t2) +#define FUN2N(t1,a1,t2,a2) (t1 a1,t2 a2, ...) +#define EXT2N(t1, t2) (t1, t2, ...) + +#define FUN3(t1,a1,t2,a2,t3,a3) (t1 a1, t2 a2, t3 a3) +#define EXT3(t1, t2, t3) (t1, t2, t3) +#define FUN3N(t1,a1,t2,a2,t3,a3)(t1 a1, t2 a2, t3 a3, ...) +#define EXT3N(t1, t2, t3) (t1, t2, t3, ...) + +#define FUN4(t1,a1,t2,a2,t3,a3,t4,a4) (t1 a1, t2 a2, t3 a3, t4 a4) +#define EXT4(t1, t2, t3, t4) (t1, t2, t3, t4) + +#define FUN5(t1,a1,t2,a2,t3,a3,t4,a4,t5,a5) (t1 a1, t2 a2, t3 a3, t4 a4, t5 a5) +#define EXT5(t1, t2, t3, t4, t5) (t1, t2, t3, t4, t5) + +#define FUN6(t1,a1,t2,a2,t3,a3,t4,a4,t5,a5,t6,a6) (t1 a1, t2 a2, t3 a3, t4 a4, t5 a5, t6 a6) +#define EXT6(t1, t2, t3, t4, t5, t6) (t1, t2, t3, t4, t5, t6) + +#define FUN7(t1,a1,t2,a2,t3,a3,t4,a4,t5,a5,t6,a6,t7,a7) (t1 a1, t2 a2, t3 a3, t4 a4, t5 a5, t6 a6, t7 a7) +#define EXT7(t1, t2, t3, t4, t5, t6, t7) (t1, t2, t3, t4, t5, t6, t7) + +#define FUN8(t1,a1,t2,a2,t3,a3,t4,a4,t5,a5,t6,a6,t7,a7,t8,a8) (t1 a1, t2 a2, t3 a3, t4 a4, t5 a5, t6 a6, t7 a7, t8 a8) +#define EXT8(t1, t2, t3, t4, t5, t6, t7, t8) (t1, t2, t3, t4, t5, t6, t7, t8) + +#define FUN9(t1,a1,t2,a2,t3,a3,t4,a4,t5,a5,t6,a6,t7,a7,t8,a8,t9,a9) (t1 a1, t2 a2, t3 a3, t4 a4, t5 a5, t6 a6, t7 a7, t8 a8, t9 a9) +#define EXT9(t1, t2, t3, t4, t5, t6, t7, t8, t9) (t1, t2, t3, t4, t5, t6, t7, t8, t9) + +#define FUN10(t1,a1,t2,a2,t3,a3,t4,a4,t5,a5,t6,a6,t7,a7,t8,a8,t9,a9,t10,a10) (t1 a1, t2 a2, t3 a3, t4 a4, t5 a5, t6 a6, t7 a7, t8 a8, t9 a9, t10 a10) +#define EXT10(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10) (t1, t2, t3, t4, t5, t6, t7, t8, t9, t10) + +#define FUN11(t1,a1,t2,a2,t3,a3,t4,a4,t5,a5,t6,a6,t7,a7,t8,a8,t9,a9,t10,a10,t11,a11) (t1 a1, t2 a2, t3 a3, t4 a4, t5 a5, t6 a6, t7 a7, t8 a8, t9 a9, t10 a10, t11 a11) +#define EXT11(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11) (t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11) + +#define FUN12(t1,a1,t2,a2,t3,a3,t4,a4,t5,a5,t6,a6,t7,a7,t8,a8,t9,a9,t10,a10,t11,a11,t12,a12) (t1 a1, t2 a2, t3 a3, t4 a4, t5 a5, t6 a6, t7 a7, t8 a8, t9 a9, t10 a10, t11 a11, t12 a12) +#define EXT12(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12) (t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12) + +#define FUN13(t1,a1,t2,a2,t3,a3,t4,a4,t5,a5,t6,a6,t7,a7,t8,a8,t9,a9,t10,a10,t11,a11,t12,a12,t13,a13) (t1 a1, t2 a2, t3 a3, t4 a4, t5 a5, t6 a6, t7 a7, t8 a8, t9 a9, t10 a10, t11 a11, t12 a12, t13 a13) +#define EXT13(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13) (t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13) + + +#else +/* Non-ANSI */ +#define var_start(x,y) va_start(x) + +/* These macros expand into old-style function definitions */ + +#define FUN0() () +#define EXT0() () + +#define FUN1(t1,a1) (a1) t1 a1; +#define EXT1(t1) () +#define FUN1N(t1,a1) (a1,va_alist) t1 a1; va_dcl +#define EXT1N(t1) () + +#define FUN2(t1,a1,t2,a2) (a1, a2) t1 a1; t2 a2; +#define EXT2(t1, t2) () +#define FUN2N(t1,a1,t2,a2,va_alist) (a1, a2) t1 a1; t2 a2; va_dcl +#define EXT2N(t1, t2) () + +#define FUN3(t1,a1,t2,a2,t3,a3) (a1, a2, a3) t1 a1; t2 a2; t3 a3; +#define EXT3(t1, t2, t3) () +#define FUN3N(t1,a1,t2,a2,t3,a3) (a1, a2, a3, va_alist) t1 a1; t2 a2; t3 a3; va_dcl +#define EXT3N(t1, t2, t3) () + +#define FUN4(t1,a1,t2,a2,t3,a3,t4,a4) (a1, a2, a3, a4) t1 a1; t2 a2; t3 a3; t4 a4; +#define EXT4(t1, t2, t3, t4) () + +#define FUN5(t1,a1,t2,a2,t3,a3,t4,a4,t5,a5) (a1, a2, a3, a4, a5) t1 a1; t2 a2; t3 a3; t4 a4; t5 a5; +#define EXT5(t1, t2, t3, t4, t5) () + +#define FUN6(t1,a1,t2,a2,t3,a3,t4,a4,t5,a5,t6,a6) (a1, a2, a3, a4, a5, a6) t1 a1; t2 a2; t3 a3; t4 a4; t5 a5; t6 a6; +#define EXT6(t1, t2, t3, t4, t5, t6) () + +#define FUN7(t1,a1,t2,a2,t3,a3,t4,a4,t5,a5,t6,a6,t7,a7) (a1, a2, a3, a4, a5, a6, a7) t1 a1; t2 a2; t3 a3; t4 a4; t5 a5; t6 a6; t7 a7; +#define EXT7(t1, t2, t3, t4, t5, t6, t7) () + +#define FUN8(t1,a1,t2,a2,t3,a3,t4,a4,t5,a5,t6,a6,t7,a7,t8,a8) (a1, a2, a3, a4, a5, a6, a7, a8) t1 a1; t2 a2; t3 a3; t4 a4; t5 a5; t6 a6; t7 a7; t8 a8; +#define EXT8(t1, t2, t3, t4, t5, t6, t7, t8) () + +#define FUN9(t1,a1,t2,a2,t3,a3,t4,a4,t5,a5,t6,a6,t7,a7,t8,a8,t9,a9) (a1, a2, a3, a4, a5, a6, a7, a8, a9) t1 a1; t2 a2; t3 a3; t4 a4; t5 a5; t6 a6; t7 a7; t8 a8; t9 a9; +#define EXT9(t1, t2, t3, t4, t5, t6, t7, t8, t9) () + +#define FUN10(t1,a1,t2,a2,t3,a3,t4,a4,t5,a5,t6,a6,t7,a7,t8,a8,t9,a9,t10,a10) (a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) t1 a1; t2 a2; t3 a3; t4 a4; t5 a5; t6 a6; t7 a7; t8 a8; t9 a9; t10 a10; +#define EXT10(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10) () + +#define FUN11(t1,a1,t2,a2,t3,a3,t4,a4,t5,a5,t6,a6,t7,a7,t8,a8,t9,a9,t10,a10,t11,a11) (a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11) t1 a1; t2 a2; t3 a3; t4 a4; t5 a5; t6 a6; t7 a7; t8 a8; t9 a9; t10 a10; t11 a11; +#define EXT11(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11) () + +#define FUN12(t1,a1,t2,a2,t3,a3,t4,a4,t5,a5,t6,a6,t7,a7,t8,a8,t9,a9,t10,a10,t11,a11,t12,a12) (a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12) t1 a1; t2 a2; t3 a3; t4 a4; t5 a5; t6 a6; t7 a7; t8 a8; t9 a9; t10 a10; t11 a11; t12 a12; +#define EXT12(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12) () + +#define FUN13(t1,a1,t2,a2,t3,a3,t4,a4,t5,a5,t6,a6,t7,a7,t8,a8,t9,a9,t10,a10,t11,a11,t12,a12,t13,a13) (a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13) t1 a1; t2 a2; t3 a3; t4 a4; t5 a5; t6 a6; t7 a7; t8 a8; t9 a9; t10 a10; t11 a11; t12 a12; t13 a13; +#define EXT13(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13) () + + +#endif + +#ifdef VPRINTF_MISSING +/* The following will work for some systems. */ +#define vfprintf(stream, format, ap) _doprnt (format, ap, stream) +int +vsprintf FUN3(char *, into, char *, s, va_list, ap) +{ + int ret; + FILE f; + + f._cnt = 32767; + /* taking address and dereferencing deals with the fact that + f._ptr can be either a char * or an unsigned char *. */ + *(char **)&f._ptr = into; + f._flag = _IOWRT+_IOSTRG; + ret = _doprnt(s, ap, &f); + *f._ptr = 0; + return (ret); +} +#endif + +/* Names or default names for various files. */ +#define OBJ_NAM "a.out" +#define MON_NAM "gmon.out" +#define SUM_NAM "gmon.sum" + +/* Debugging stuff */ + +#define DEBUG + +/* A mask of flags defined below. */ +long debug=0; + +/* Print obnoxious msgs about the a.out file, and the amusing values therein */ +#define DB_AFILE (1<<0) /* a.out file */ + +/* Describe in detail the rending and tearing of the gmon.out file */ +#define DB_GFILE (1<<1) /* gmon.out file */ + +/* Describe in detail the writing out of the gmon.sum file */ +#define DB_SUM (1<<2) /* gmon.sum file */ + +/* Print neeto messages as we deal with -e -E -f and -F options */ +#define DB_OPT (1<<3) /* Options */ + +/* Print msgs whenever the ring buffer is used */ +/* This probably doesn't work since the ring buffer code was + moved into the utilities file */ +#define DB_RB (1<<4) /* Ring buffer */ + +/* Print msgs about cycles */ +#define DB_CYC (1<<5) + +/* Print msgs about assigning the histogram entries to functions */ +#define DB_HISTO (1<<6) + +/* Print obnoxious msgs here and there as we do our stuff */ +#define DB_MISC (1<<31) /* misc stuff */ + +#ifdef DEBUG +#define PRINT_OBNOXIOUS_DEBUG_MESSAGE(x, msg) if (debug&x) dbgprintf msg +#else +#define PRINT_OBNOXIOUS_DEBUG_MESSAGE(x, msg) +#endif + +/* LessThan EQual GreaterThan */ +/* These get returned by the various comparison functions */ +#define LT (-1) +#define EQ (0) +#define GT (1) + +/* Note since *ALL* non-zero values are true, do *NOT* say + if (x==TRUE) + These values are here only for saying + return TRUE; + and things like that. */ + +#define TRUE (1) +#define FALSE (0) + +/* The floating point datatype to use for storing + propagated info. Float should be big enough */ +#define FLOAT double + +/* Used when creating a gmon.sum file */ +/* #define FUDGE_FACTOR (sizeof (CHUNK)) */ +#define FUDGE_FACTOR 0 + +/* Description of one symbol in gprof's internal symbol table. + There is one of these for each function and one for each cycle. + + NAME is the name of the function or cycle. + VALUE is the address of the start of the function. + + CALLS and CALLED are chains of edges for calls into and out of + this function. These constitute the call graph. + + NCALLS is the total number + of times this function called other functions, + NCALLED is the total number of times this function was called. + Note that if NCALLED is zero, but NCALLS is nozero, this function + must have been started magically. It happens. (Signals, etc.) + + RECURSIVE is the total number of times this function was called + recursively. + + HISTO is the total number of histogram counts for this function. + + SUB_HISTO is the total number of histogram counts for this function + plus time propagated from the children. + + CYCNUM is the number of the cycle this function is in, + or the number of this cycle is this entry is for a cycle. + -1 means the function isn't in any cycles. 0 means the + cycle-ness of the function hasn't been determined yet. + + NUMINDEX is the index number assigned to this function + for the call graph. + + If FLAG is non-zero, this function is in the process of being + saved from the oblivion caused by the -f or -F options. */ + +struct mesym { + char *name; + unsigned long value; + + struct symlist *calls; + unsigned ncalls; + struct symlist *called; + unsigned ncalled; + + unsigned recursive; + + unsigned histo; + FLOAT sub_histo; + int cycnum; + int numindex; + int flag; +}; + +/* Vector of `struct mesym' for all functions, + sorted in ascending order by VALUE field for binary search. */ +struct mesym *syms; +int nsym=0; /* Length of vector */ + +/* Vector of `struct mesym' for all cycles so far identified. */ +struct mesym **cycles; +int number_of_cycles = 0; /* Length of vector */ + +/* Structure for an edges of the call graph. + + Each edge--each pair of functions A and B such that A called B-- + is represented by one of these structures. It appears in + A's `calls' chain and in B's `called' chain. + + Meanwhile, the structure describes its meaning with the + SYM_FROM and SYM_TO fields, which point to the symbol entries + for A and B. + + Basically, if you got here from a mesym's calls + pointer, SYM_FROM points back to that symbol, NEXT_FROM is irrevelant, + SYM_TO points to the symbol it called, and NEXT_TO points to the + next one in the list. + + If you got here from a mesym's called pointer, SYM_FROM points to + the symbol that called it, NEXT_FROM points to the next one in the + list, SYM_TO points back to the symbol, and NEXT_TO is irrelevant. + + NCALLS is the number of times SYM_FROM called SYM_TO, + PROP_TIME is the amount of time in SYM_TO itself propagated to SYM_FROM, + CHILD_TIME is the amount of time + propagated from SYM_TO's children to SYM_FROM. */ + +struct symlist { + struct mesym *sym_from; + struct symlist *next_from; + struct mesym *sym_to; + struct symlist *next_to; + + unsigned ncalls; + FLOAT prop_time; + FLOAT child_time; +}; + +/* argv[0], here for the sake of error messages. */ +char *myname = 0; + +/* Name of the executable file. */ +char *exec_file_name; + +/* The string table of the executable file. + Each symbol entry in the file contains an index in the string table. + When the symbols are in core, we relocate them to point to their names, + which remain inside the string table. */ +char *strs; + +/* Header of the first gmon.out file we read. This is used to (try to) + make sure all the rest of the gmon.out files agree with the first one + (and the executable.) */ +struct gm_header hdr; + +/* Number of clock ticks per second. Read from the kernel's memory, + this number tells us how long an interval a single stab in the + histogram represents. */ +long ticks; + +/* This is an array of pointers to symbols. These are the functions that + we'll have to print out in the flat graph. */ +struct mesym **flat_profile_functions; +int number_of_flat_profile_functions = 0; /* Size of vector */ + +/* This is an array of pointers to symbols. These are the functions that + we should mention in the call tree. */ +struct mesym **functions_in_call_tree; +int number_of_functions_in_call_tree= 0; +struct mesym **f_end; + +/* Number of slots in the histogram. */ +int nhist; + +/* Total number of counts in the histogram. */ +unsigned long tothist = 0; + +/* Pointer to the histogram itself. */ +unsigned CHUNK *histo; /* Stabs */ + +/* Now that I've re-written the ring_buffer code, we need this */ +void *ring_buffer; + +/* Record the specified options. */ + +/* If the -a option is given, static (private) functions are not read into + the symbol table. This means that time spent in them, calls to them, etc, + are instead added to whatever function was loaded next to it in the a.out + file. This is compatable with UN*X gprof. (Bleh.) The right thing to do + is keep track of time spent in static functions, and forget about it, + instead of adding it to another hapless function. Rms disagrees with me. + I think its evil to add histogram time to a function simply because it + happend to be loaded in memory just before a function we don't want to print. */ +int no_locals = 0; /* -a flag */ + +/* The -b option tells gprof to not print out the obnoxious blurbs telling + what all the fields of the output mean. This is useful if you've seen + the blurbs a gzillion times and you only want to look at your numbers. */ +int no_blurbs = 0; /* -b flag */ + +/* If the -s option is given, gprof will write out a 'sum file' gmon.sum + which is a gmon.out file that contains the profile info from all the + gmon.out files that gprof read in. */ +int write_sum_file = 0; /* -s option */ + +/* The -z option tells gprof to include functions with zero usage (never called, + and used no time) in the output. Usually, such functions are considered + boring, and aren't printed. */ +int print_zeros = 0; /* -z option */ + +/* The -e option takes a function name, and suppress the printing of that + function and its descendents from the call graph profile. (If its + descendents are called from elswhere, well. . .) (Currently, they're + printed, and the -e'd function is shown under the list of parents so + you can see where the child's time is disappearing to. . .) + + The -E option takes a function name, and not only -e's it, but + removes the time spent in the function from the total time. (Its as if + that function never existed (unless it calls something that is also + called from somewhere else, in which case. . . (Currently it works + as described for -e above.)) + + -f prints only the call tree for its argument. + + -F is like -f, but it uses only the time in the function and its + descendents for computing total time. + */ + +enum option_type { SMALL_E, BIG_E, SMALL_F, BIG_F, + /* Like BIG_E except don't print a warning if the + function doesn't exist. */ + REMOVE_TIME_IF_THERE }; + +struct filter { + char *name; + enum option_type type; +}; + +/* Vector with an element for each -e, -E, -f or -F option specified. */ +struct filter *filters; +/* Length of the vector. */ +int nfilters; + +/* These values are stored in the flag field of a struct mesym so we can know + what we're doing to that function */ + +/* This one is being saved by a -f or -F flag */ +#define SAVE_ME 01 +/* This one is being killed by a -e or -E flag */ +#define KILL_ME 02 + +/* Blurbs that are copied verbatim into the output file + to explain the data in the tables. + + If your compiler can't stand this, split this up into a vector + of strings, and print them one after another. */ + +char *first_blurb = "\n\ + The above table shows how much time was spent directly in each\n\ + function. The table was sorted by the amount of time the computer\n\ + spent in each function.\n\n\ + % time This is the percentage of the total execution time\n\ + the program spent in this function. These should all add\n\ + up to 100%.\n\n\ + seconds This is the total number of seconds the computer spent\n\ + executing this function.\n\n\ + cumsec This is the cumulative total number of seconds the\n\ + computer spent executing this functions, plus the time spent\n\ + in all the functions above this one in this table.\n\n\ + calls This is the total number of times the function was called.\n\ + If there isn't a number here, the function wasn't compiled with\n\ + the profiler enabled, and further information about this function\n\ + is limited. In particular, all information about where this \n\ + function was called from has been lost.\n\n\ + function This is the name of the function.\n\ +\f"; + +char *second_blurb = "\n\ + This table describes the call tree of the program, and was sorted by\n\ + the total amount of time spent in each function and its children.\n\n\ + Each entry in this table consists of several lines. The line with the\n\ + index number at the left hand margin lists the current function.\n\ + The lines above it list the functions that called this function,\n\ + and the lines below it list the functions this one called.\n\ + This line lists:\n\ + index A unique number given to each element of the table.\n\ + Index numbers are sorted numerically.\n\ + The index number is printed next to every function name so\n\ + it is easier to look up where the function in the table.\n\n\ + % time This is the percentage of the `total' time that was spent\n\ + in this function and its children. Note that due to\n\ + different viewpoints, functions excluded by options, etc,\n\ + these numbers will NOT add up to 100%.\n\n\ + self This is the total amount of time spent in this function.\n\n\ + children This is the total amount of time propagated into this\n\ + function by its children.\n\n\ + called This is the number of times the function was called.\n\ + If the function called itself recursively, the number\n\ + only includes non-recursive calls, and is followed by\n\ + a `+' and the number of recursive calls.\n\n\ + name The name of the current function. The index number is\n\ + printed after it. If the function is a member of a\n\ + cycle, the cycle number is printed between the\n\ + function's name and the index number.\n\n\n\ + For the function's parents, the fields have the following meanings:\n\n\ + self This is the amount of time that was propagated directly\n\ + from the function into this parent.\n\n\ + children This is the amount of time that was propagated from\n\ + the function's children into this parent.\n\n\ + called This is the number of times this parent called the\n\ + function `/' the total number of times the function\n\ + was called. Recursive calls to the function are not\n\ + included in the number after the `/'.\n\n\ + name This is the name of the parent. The parent's index\n\ + number is printed after it. If the parent is a\n\ + member of a cycle, the cycle number is printed between\n\ + the name and the index number.\n\n\ + If the parents of the function cannot be determined, the word\n\ + `<spontaneous>' is printed in the `name' field, and all the other\n\ + fields are blank.\n\n\ + For the function's children, the fields have the following meanings:\n\n\ + self This is the amount of time that was propagated directly\n\ + from the child into the function.\n\n\ + children This is the amount of time that was propagated from the\n\ + child's children to the function.\n\n\ + called This is the number of times the function called\n\ + this child `/' the total number of times the child\n\ + was called. Recursive calls by the child are not\n\ + listed in the number after the `/'.\n\n\ + name This is the name of the child. The child's index\n\ + number is printed after it. If the child is a\n\ + member of a cycle, the cycle number is printed\n\ + between the name and the index number.\n\n\ + If there are any cycles (circles) in the call graph, there is an\n\ + entry for the cycle-as-a-whole. This entry shows who called the\n\ + cycle (as parents) and the members of the cycle (as children.)\n\ + The `+' recursive calls entry shows the number of function calls that\n\ + were internal to the cycle, and the calls entry for each member shows,\n\ + for that member, how many times it was called from other members of\n\ + the cycle.\n\n"; + + +/* Prototypes for all the functions. */ + +int main EXT2(int, char **); +int read_header_info EXT6 (char *, FILE *, long int *, unsigned int *, + long int *, unsigned int *); +void print_flat_profile EXT0(); +void print_call_graph EXT0(); +void write_summary EXT0(); +void filter_graph EXT0(); +FLOAT convert_and_round EXT1(FLOAT); +void add_to_lists EXT3(struct mesym *, struct mesym *, unsigned); +void delete_from_lists EXT2(struct mesym*, struct mesym *); +void flushfuns EXT0(); +void findcycles EXT0(); +void kill_children EXT2(struct mesym *, int); +void save_the_children EXT2(struct mesym *, int); +void remove_from_call_tree EXT1(struct mesym **); +struct mesym **find_funp_from_name EXT1(char *); +struct mesym **find_funp_from_pointer EXT1(struct mesym *); +void read_syms EXT2(FILE *, int); +struct mesym *val_to_sym EXT1(unsigned long); +int badsym EXT1(struct nlist *); +int symcmp EXT2(const void *, const void *); +int timecmp EXT2(const void *, const void *); +int callcmp EXT2(const void *, const void *); +int treetimecmp EXT2(const void *, const void *); +int listcmp EXT2(const void *, const void *); +void readgm EXT1(char *); +void print_blurb EXT1(char *blurb); +long get_ticks EXT0(); +void add_filter EXT2(char *name, int flag); +void print_sorted_list EXT3(int, int, struct symlist *); + +void fatal EXT1N(char *); +void fatal_io EXT2(char *, char *); + +FILE *ck_fopen EXT2(char *, char *); +void ck_fseek EXT3(FILE *, long, int); +void ck_fread EXT4(void *, size_t, size_t, FILE *); +void ck_fwrite EXT4(void *, size_t, size_t, FILE *); +void ck_fclose EXT1(FILE *); + +void *ck_malloc EXT1(size_t); +void *ck_calloc EXT2(size_t, size_t); +void *ck_realloc EXT2(void *, size_t); + +char *mk_sprintf EXT1N(char *); + +void *init_ring_buffer EXT0(); +void push_ring_buffer EXT2(void *, void *); +void *pop_ring_buffer EXT1(void *); +int ring_buffer_isnt_empty EXT1(void *); +void flush_ring_buffer EXT1(void *); + +/* C++ demangler stuff. */ +char *cplus_demangle EXT1(char *); +void fprint_name EXT2(FILE *, char *); + +void fatal EXT1N(char *); + +void *malloc EXT1(size_t); +void *realloc EXT2(void *,size_t); +void free EXT1(void *); + + +/* Since we don't have prototypes for the system funs, we add them + ourselves. . . */ +int atoi EXT1(const char *); +long atol EXT1(const char *); +double atof EXT1(const char *); + +#ifndef NeXT /* NeXT has a bug in their include files. */ +void qsort EXT4(void *, size_t, size_t, int (*)(const void *, const void *)); +#endif + +void exit EXT1(int); + +int strcmp EXT2(const char *, const char *); +char *index EXT2(const char *, int); +int printf EXT1N(const char *); +int fprintf EXT2N(FILE *, const char *); +/* char *sprintf EXT2N(const char *, const char *); */ + +int puts EXT1(const char *); +int fputs EXT2(const char *, FILE *); + +int fputc EXT2(int, FILE *); +size_t fread EXT4(void *, size_t, size_t, FILE *); + +int nlist EXT2(const char *, struct nlist *); + +#ifndef VPRINTF_MISSING +int vfprintf EXT3(FILE *, const char *, va_list); +#endif + +void dbgprintf EXT1N(char *); +void dumpsyms EXT0(); +void dumpfuns EXT0(); + + +/* And now, the program. */ + +int +main FUN2(int, ac, char **,av) +{ + FILE *fp; + int argc; + char **argv; + int c; + long int syms_offset, strs_offset; + unsigned int syms_size, strs_size; + + extern char *optarg; + extern int optind, opterr; + + static struct option long_options[] = + { + {"no-static", 0, &no_locals, 1}, + {"brief", 0, &no_blurbs, 1}, + {"no-prof", 1, 0, 'e'}, + {"no-time", 1, 0, 'E'}, + {"only-prof", 1, 0, 'f'}, + {"only-time", 1, 0, 'F'}, + {"sum", 0, &write_sum_file, 1}, + {"zeros", 0, &print_zeros, 1}, + {NULL, 0, NULL, 0} + }; + + char *name = '\0'; + int ind; + + argc=ac; + argv=av; + + myname= argv[0]; + + /* Omit profiling internal functions from call graph. */ +#ifdef sparc + +#else + add_filter ("mcount", BIG_E); +#endif + /* add_filter ("mcleanup", BIG_E); Seems to have dissappeared? */ + + /* GCC output doesn't have this function. */ + add_filter ("moncontrol", REMOVE_TIME_IF_THERE); + + ring_buffer=init_ring_buffer (); + + while ((c = getopt_long (argc, argv, "abcdD:e:E:f:F:svz", long_options, + &ind)) !=EOF) { + if (c == 0 && long_options[ind].flag == 0) + c = long_options[ind].val; + switch (c) { + case 0 : + break; + case 'a': + no_locals= TRUE; + break; + case 'b': + no_blurbs = TRUE; + break; + case 'c': + fatal ("The -c option is not supported"); + case 'd': + debug= -1; + break; + case 'D': + debug=atoi (optarg); + break; + case 's': + write_sum_file= TRUE; + break; + case 'z': + print_zeros = TRUE; + break; + case 'e': + add_filter (optarg, SMALL_E); + break; + case 'E': + add_filter (optarg, BIG_E); + break; + case 'f': + add_filter (optarg, SMALL_F); + break; + case 'F': + add_filter (optarg, BIG_F); + break; + default: + fprintf (stderr, "\ +Usage: %s [-absz] [-e func] [-E func] [-f func] [-F func]\n\ + [+no-static] [+brief] [+no-prof func]\n\ + [+no-time func] [+only-prof func]\n\ + [+only-time func] [+sum] [+zeros] executable gmon.out...\n", + myname); + exit (1); + } + } + if (optind<argc) { + exec_file_name=argv[optind]; + optind++; + } + if (!exec_file_name) + exec_file_name=OBJ_NAM; + + /* Open the a.out file, and read in selected portions */ + fp=ck_fopen (exec_file_name, "r"); + + /* Make sure its really an a.out file. If it isn't yell and scream + and stamp our feet. */ + if (!read_header_info(exec_file_name, fp, &syms_offset, &syms_size, &strs_offset, &strs_size)) + fatal ("`%s' is not an executable file", exec_file_name); + + /* Read in the string table. */ + ck_fseek (fp, strs_offset + sizeof strs_size, 0); + strs=(char *)ck_malloc (strs_size); + ck_fread ((void *)(strs+sizeof (long)), sizeof (char), strs_size-sizeof (long), fp); + + /* Read the symbols, and put the interesting ones (sorted) in SYMS. */ + ck_fseek (fp, syms_offset, 0); + read_syms (fp, syms_size / sizeof (struct nlist)); + + ck_fclose (fp); + /* We are done with the a.out file */ + + /* Read in the gmon.out files; accumulate all histogram data in HISTO + and put all number-of-calls figures into the call graph. */ + if (optind>=argc) readgm (MON_NAM); + else { + while (optind<argc) { + readgm (argv[optind]); + optind++; + } + } + + /* If a summary output file is wanted, + we can do it straightaway since we have merged the data. */ + + if (write_sum_file) { + write_summary (); + exit (0); + } + + /* Find out how many clock ticks/second our machine puts out */ + ticks=get_ticks (); + + /* Assign the ticks in the histogram to the functions they represent */ + { + int n; + unsigned long pos; + struct mesym *sym; + int incr = (hdr.high - hdr.low) / (nhist - 1); + + if ((hdr.high - hdr.low) != 4 * (nhist - 1)) + abort (); + + for (n=0, pos=hdr.low, sym= &syms[0]; n<nhist; n++, pos+=incr) { + if (histo[n]) { + + /* We've found the right symbol when *sym<=pos && *(sym+1)>pos */ + /* This means POS lies between sym and the one after it */ + + while ((sym+1)->value<=pos) + sym++; + if ((sym+1)->value<(pos+incr)) + { + /* Wow! On the edge. Split the time between the symbols */ + sym->histo+=(histo[n]+1)/2; + (sym+1)->histo+=(histo[n])/2; + PRINT_OBNOXIOUS_DEBUG_MESSAGE (DB_HISTO, ("%05lx %02d-> %s (%d) %s (%d)\n", pos, histo[n], sym->name, sym->histo, (sym+1)->name, (sym+1)->histo)); + } else { + sym->histo+=histo[n]; + PRINT_OBNOXIOUS_DEBUG_MESSAGE (DB_HISTO, ("%05lx %02d-> %s (%d)\n", pos, histo[n], sym->name, sym->histo)); + } + } + } + } + + /* Avoid division by zero if there wasn't any time collected! */ + if (tothist==0) + tothist=1; + + print_flat_profile (); + + print_call_graph (); + + if (debug&DB_AFILE) + dumpsyms (); + + if (debug&DB_AFILE) + dumpfuns (); + exit (0); +} + +/* Read various information from the header of an object file. + Return 0 for failure or 1 for success. */ + +int +read_header_info (name, fp, syms_offset, syms_size, strs_offset, strs_size) + char *name; + FILE *fp; + long int *syms_offset; + unsigned int *syms_size; + long int *strs_offset; + unsigned int *strs_size; +{ +#ifdef A_OUT + { + struct exec hdr; + + ck_fseek (fp, 0L, 0); +#ifdef HEADER_SEEK + HEADER_SEEK (fp); +#endif + + if (fread ((char *) &hdr, sizeof hdr, 1, fp) == 1 && !N_BADMAG(hdr)) + { + *syms_offset = N_SYMOFF (hdr); + *syms_size = hdr.a_syms; + *strs_offset = N_STROFF (hdr); + ck_fseek (fp, N_STROFF (hdr), 0); + if (fread ((char *) strs_size, sizeof *strs_size, 1, fp) != 1) + fatal ("error reading string table of `%s'", name); + return 1; + } + } +#endif + +#ifdef MACH_O + { + struct mach_header mach_header; + struct load_command *load_command; + struct symtab_command *symtab_command; + char *hdrbuf; + int cmd, symtab_seen; + + ck_fseek (fp, 0L, 0); + if (fread ((char *) &mach_header, sizeof mach_header, 1, fp) == 1 + && mach_header.magic == MH_MAGIC) + { + hdrbuf = ck_malloc (mach_header.sizeofcmds); + if (fread (hdrbuf, mach_header.sizeofcmds, 1, fp) != 1) + fatal ("failure reading load commands of file `%s'", name); + load_command = (struct load_command *) hdrbuf; + symtab_seen = 0; + for (cmd = 0; cmd < mach_header.ncmds; ++cmd) + { + if (load_command->cmd == LC_SYMTAB) + { + symtab_seen = 1; + symtab_command = (struct symtab_command *) load_command; + *syms_offset = symtab_command->symoff; + *syms_size = symtab_command->nsyms * sizeof (struct nlist); + *strs_offset = symtab_command->stroff; + *strs_size = symtab_command->strsize; + } + load_command = (struct load_command *) ((char *) load_command + load_command->cmdsize); + } + free (hdrbuf); + if (!symtab_seen) + *syms_offset = *syms_size = *strs_offset = *strs_size = 0; + return 1; + } + } +#endif + + return 0; +} + +/* Output a summary gmon file containing all our accumulated + histogram and call-graph data. */ + +void +write_summary FUN0() +{ + struct mesym *p; + struct symlist *t; + struct gm_call call_tmp; + FILE *fp; + + fp=ck_fopen (SUM_NAM, "w"); + hdr.nbytes=sizeof (struct gm_header)+nhist*sizeof (CHUNK); + ck_fwrite ((void *)&hdr, sizeof (hdr), 1, fp); + ck_fwrite ((void *)histo, sizeof (unsigned CHUNK), nhist, fp); + /* for (p= &syms[nsym]; --p>=&syms[0];) { */ + if (nsym) { + p= &syms[nsym-1]; + do { + for (t=p->calls; t; t=t->next_to) { + /* Since we've forgotten exactly where in FROM we + were called from, we fake it. Since this is only + gonna be fed back into gprof, it doesn't matter */ + call_tmp.from=(p->value+FUDGE_FACTOR)-hdr.low; + call_tmp.to=(t->sym_to->value+FUDGE_FACTOR)-hdr.low; + call_tmp.ncalls=t->ncalls; + ck_fwrite ((void *)&call_tmp, sizeof (call_tmp), 1, fp); + } + } while (p-->&syms[0]); + } + ck_fclose (fp); +} + +/* Print the flat profile from the symbol table information. */ + +void +print_flat_profile FUN0() +{ + int n; + struct mesym **funp; + + /* Scan the symbol table and discover how many functions either had time + spent in them, or had a non-zero call count */ + for (n=0; n<nsym; n++) { + if (syms[n].histo || syms[n].ncalled || print_zeros) + number_of_flat_profile_functions++; + } + /* Collect all the interesting functions */ + flat_profile_functions + =(struct mesym **)ck_calloc (number_of_flat_profile_functions, sizeof (struct mesym *)); + for (n=0, funp=flat_profile_functions; n<nsym; n++) { + if (syms[n].histo || syms[n].ncalled || print_zeros) { + *funp= &syms[n]; + funp++; + } + } + + /* Sort them */ + qsort (flat_profile_functions, number_of_flat_profile_functions, + sizeof (struct mesym *), timecmp); + + /* And print them out */ + + if (no_blurbs) + printf ("\t\tFlat profile\n\n"); + else + printf ("\tFlat profile (explanation follows)\n\n"); + + if (no_blurbs) + printf ("\t\tCall graph is on the following page.\n\n"); + else + printf ("\tCall graph is on the following page.\n\n"); + + printf ("Each sample counts as %g seconds.\n\n", 1.0/ticks); + + puts ("% time seconds cumsec calls function"); + + for (n=0; n<number_of_flat_profile_functions; n++) { + unsigned histo; + static cumhist = 0; + + histo=flat_profile_functions[n]->histo; + cumhist+=histo; + printf ("%6.2f ", (FLOAT)(100.0*histo)/(FLOAT)tothist); + printf ("%8.2f ", ((FLOAT)histo)/(FLOAT)ticks); + printf ("%8.2f ", ((FLOAT)cumhist)/(FLOAT)ticks); + if (flat_profile_functions[n]->ncalled) + printf (" %5d ", flat_profile_functions[n]->ncalled); + else fputs (" ", stdout); + fprint_name (stdout, flat_profile_functions[n]->name); + fputs ("\n", stdout); + } + + /* RMS says we should print the blurb last, which makes sense to me */ + print_blurb (first_blurb); +} + +/* Compute the call graph and print it. */ + +void +print_call_graph FUN0() +{ + int n; + int index; + struct mesym **funp; + struct mesym **f; + + /* Count the functions that appear in the call tree. */ + for (n=0; n<nsym; n++) { + /* If a function calls something else, or is called by + something else, its in the call tree. . . */ + if (syms[n].ncalls || syms[n].ncalled) + number_of_functions_in_call_tree++; + } + + if (number_of_functions_in_call_tree == 0) { + printf ("\t\tNo call graph information available.\n"); + return; + } + + /* Allocate a vector of these functions. */ + functions_in_call_tree + =(struct mesym **)ck_calloc (number_of_functions_in_call_tree, sizeof (struct mesym *)); + for (n=0, funp=functions_in_call_tree; n<nsym; n++) { + if (syms[n].ncalls || syms[n].ncalled) { + *funp= &syms[n]; + funp++; + } + } + + /* Put all the leaf nodes at the end of the call tree */ + qsort (functions_in_call_tree, number_of_functions_in_call_tree, sizeof (struct mesym *), callcmp); + + /* Root nodes are now all at the head, and can be easily found + 'cuz they have call-counts of zero (Never been called, but + calls something else; that's spontaneous in my book.) */ + /* Ordinary nodes are in the middle, (were called, and + called others. Leaf nodes are at the end. */ + + /* Our mission, should we choose to accept it, is to detect + circles in the call graph. */ + /* We do this by keeping a pointer into the call tree called f_end. This + points to the end of the functions that we don't know if they are leaf + nodes or not. When we know something is a leaf node, we move it down + past f_end */ + + f= &functions_in_call_tree[number_of_functions_in_call_tree-1]; + do { + if ((*f)->ncalls!=0) + break; + (*f)->cycnum= -1; + + /* Note the neeto side effect here! Is there a better way to do this? */ + } while (f-- > &functions_in_call_tree[0]); + + /* Note that functions that only call themselves (and nobody else) don't + get marked above. Doesn't matter; they get marked soon. */ + if (f== &functions_in_call_tree[0]) { + (*f)->cycnum= -1; + } else { + int found; + + f_end=f; + + /* Eliminate recursive calls */ + do { + struct symlist *t, *u; + + for (t= (*f)->calls; t; t=t->next_to) { + if (t->sym_to!= (*f)) + continue; + (*f)->recursive+=t->ncalls; + + (*f)->ncalls-=t->ncalls; + (*f)->ncalled-=t->ncalls; + + /* Delete from linked list */ + if (t==(*f)->calls) + (*f)->calls=t->next_to; + else { + for (u=(*f)->calls; u->next_to!=t; u=u->next_to) + ; + u->next_to=t->next_to; + } + + /* Find and delete from called list too */ + if (t==(*f)->called) + (*f)->called=t->next_from; + else { + for (u=(*f)->called; u->next_from!=t; u=u->next_from) + ; + u->next_from=t->next_from; + } + free (t); + break; + } + } while (f--!=&functions_in_call_tree[0]); + + number_of_cycles = 0; + + /* Either there weren't any cycles, or all the cycles live + between f_end and functions_in_call_tree */ + + /* Mark all functions that are not in any cycle. */ + flushfuns (); + + + /* If we haven't got them all, find the cycle (s). */ + if (f_end != &functions_in_call_tree[0]) + findcycles (); + } + + /* Add entries for the cycles to the vector of all call-graph nodes. */ + + functions_in_call_tree + =ck_realloc (functions_in_call_tree, + (number_of_cycles+number_of_functions_in_call_tree)*sizeof (struct mesym *)); + for (n=0; n<number_of_cycles; n++) + functions_in_call_tree[number_of_functions_in_call_tree++]= cycles[n]; + + /* Now discard the functions that the filters say should not appear. */ + + filter_graph (); + + /* So by now, the only functions left are the ones we want to print */ + qsort (functions_in_call_tree, number_of_functions_in_call_tree, sizeof (struct mesym *), treetimecmp); + + /* Assign each function its index number. */ + for (n=0; n<number_of_functions_in_call_tree; n++) + functions_in_call_tree[n]->numindex=n+1; + + if (no_blurbs) + printf ("\t\t\tCall graph\n\n"); + else + printf ("\t\t Call graph (explanation follows)\n\n"); + + puts ("index % time self children called name"); + + /* Loop over entries. */ + + for (index = 0; index <number_of_functions_in_call_tree; index++) { + struct mesym *current = functions_in_call_tree[index]; + char tmpstr[40]; /* Can an int have more than 40 digits? I hope not */ + + /* Print out all the things that called this */ + if (current->ncalled==0 && current->called==0) + printf (" <spontaneous>\n"); + else { + print_sorted_list (current->ncalled, current->cycnum, current->called); + } + + + sprintf (tmpstr, "[%d]", current->numindex); + printf ("%-6s %6.2f %7.2f %7.2f ", + tmpstr, + (FLOAT)(100.0*(current->sub_histo+current->histo))/(FLOAT)tothist, + convert_and_round ((FLOAT)current->histo), + convert_and_round ((FLOAT)current->sub_histo)); + + if (current->recursive) { + printf ("%4d+%-4d ", + current->ncalled, + current->recursive); + fprint_name (stdout, current->name); + } else { + printf ("%4d ", current->ncalled); + fprint_name (stdout, current->name); + } + + if (current->cycnum>0 && current->name[0]!='<') + printf (" <cycle %d>", current->cycnum); + + printf (" [%d]\n", current->numindex); + + + /* Now print out the children */ + + if (current->name[0]=='<') + print_sorted_list (-2, current->cycnum, current->calls); + else + print_sorted_list (-1, current->cycnum, current->calls); + + + printf ("----------------------------------------\n"); + } + + print_blurb (second_blurb); +} + +/* Scan the call tree for virtual leaf nodes */ +/* If we find any, propagate time into them from their children, + mark them as being leaf nodes. Then repeat the process. When + we drop out of here, either we've flushed the entire tree, or + we've found a cycle. */ + +void +flushfuns FUN0() +{ + int found; + struct mesym **f; + + do { + found=0; + f=f_end; + do { + struct symlist *t; + + assert (f>=functions_in_call_tree); + + for (t=(*f)->calls; t; t=t->next_to) + if (t->sym_to->cycnum==0) + break; + + /* We've found an virtual leaf node. We shold + propagate time into the node, and move it + past f_end, since we aren't interested in + it anymore */ + if (!t) { + found++; + + /* If its a member of a cycle, cycnum is positive, and + time propagation has already been dealt with. */ + if ((*f)->cycnum==0) { + for (t=(*f)->calls; t; t=t->next_to) { + struct mesym *symP; + + if (t->sym_to->name[0]=='<') + continue; + if (t->sym_to->cycnum==-1) + symP= t->sym_to; + else + symP= cycles[t->sym_to->cycnum-1]; + + t->prop_time= (FLOAT)t->ncalls*(FLOAT)symP->histo/(FLOAT)symP->ncalled; + t->child_time=(FLOAT)t->ncalls* symP->sub_histo /(FLOAT)symP->ncalled; + (*f)->sub_histo+=t->prop_time+t->child_time; + } + (*f)->cycnum= -1; + } + + /* move this node past f_end, since we don't care + about it anymore */ + if (f_end!=functions_in_call_tree) { + /* move this function to the end */ + if (f!=f_end) { + struct mesym *tmp; + + tmp= *f; + *f= *f_end; + *f_end=tmp; + } + --f_end; + } + assert (f_end>=functions_in_call_tree); + } + } while (f-->&functions_in_call_tree[0]); + } while (found && f_end>&functions_in_call_tree[0]); +} + +void +findcycles FUN0() +{ + struct mesym **f; + struct mesym *ptr; + struct symlist *tmp; + struct cy { + struct mesym *memb1; + struct mesym *memb2; + struct mesym **others; + int width; + int deepest; + int shallowest; + }; + struct cy *cy; + int ncy = 0; + int n; + + int bigdepth = 0; + int curdepth; + struct mesym *current_cycle_pointer; + struct mesym *cursym; + + int tree_depth; + + for (f= &functions_in_call_tree[0]; f<=f_end; f++) { + if ((*f)->ncalled==0) { + push_ring_buffer (ring_buffer, *f); + (*f)->flag=0; + } + } + push_ring_buffer (ring_buffer, (void *)0); + + tree_depth = 1; + for (;;) { + ptr=pop_ring_buffer (ring_buffer); + if (!ptr) { + if (ring_buffer_isnt_empty (ring_buffer)) { + push_ring_buffer (ring_buffer, (void *)0); + tree_depth ++; + continue; + } else { + break; + } + } else if (ptr->flag==1) { + fprintf (stderr, "Ignoring call to spont function "); + fprint_name (stderr, ptr->name); + fprintf (stderr, "\n"); + } else if (ptr->flag!=0) { + /* Save upward edge */ + /* printf ("Upward edge detected in function %s\n", ptr->name); */ + if (!ncy) { + cy=ck_malloc (sizeof (struct cy)); + ncy=1; + } else { + ncy++; + cy=ck_realloc (cy, ncy*sizeof (struct cy)); + } + cy[ncy-1].memb1=ptr; + cy[ncy-1].memb2=0; + } else { + ptr->flag=tree_depth; + for (tmp=ptr->calls; tmp; tmp=tmp->next_to) + if (tmp->sym_to->cycnum!=-1) + push_ring_buffer (ring_buffer, tmp->sym_to); + } + } + if (!ncy) + return; + + for (n=0; n<ncy; n++) { + struct symlist *s; + struct symlist *u; + + + cursym=cy[n].memb1; + if (cursym->cycnum) + continue; + + number_of_cycles++; + /* for (s=cursym->called; s; s=s->next_from) { + if (s->sym_from->flag>=cursym->flag) + break; + } + if (!s) + abort (); */ + + if (!cycles) + cycles=(struct mesym **)ck_malloc (sizeof (struct mesym *)); + else + cycles=(struct mesym **)ck_realloc ((void *)cycles, + number_of_cycles*sizeof (struct mesym *)); + + current_cycle_pointer = (struct mesym *)ck_malloc (sizeof (struct mesym)); + cycles[number_of_cycles-1] = current_cycle_pointer; + bzero (current_cycle_pointer, sizeof *current_cycle_pointer); + current_cycle_pointer->name=mk_sprintf ("<cycle %d as a whole>", number_of_cycles); + current_cycle_pointer->value= (unsigned long)-1; + current_cycle_pointer->cycnum=number_of_cycles; + + cursym=cy[n].memb1; + + push_ring_buffer (ring_buffer, cursym); + + while (ring_buffer_isnt_empty (ring_buffer)) { + cursym=pop_ring_buffer (ring_buffer); + if (cursym->cycnum==number_of_cycles) + continue; + if (cursym->cycnum!=0) + abort (); + PRINT_OBNOXIOUS_DEBUG_MESSAGE (DB_CYC, ("adding %s (%d) to cycle", cursym->name, cursym->histo)); + current_cycle_pointer->histo+=cursym->histo; + cursym->cycnum=number_of_cycles; + add_to_lists (current_cycle_pointer, cursym, 0); + + /* Now queue the subroutines of this function to be scanned + eventually. */ + + for (u=cursym->calls; u; u=u->next_to) + if (u->sym_to->cycnum==0) { + push_ring_buffer (ring_buffer, (void *)(u->sym_to)); + } else if (u->sym_to->name[0] == '<') { + PRINT_OBNOXIOUS_DEBUG_MESSAGE (DB_CYC, ("Cycle calls %s (%d)", u->sym_to->name, u->ncalls)); + add_to_lists (current_cycle_pointer, u->sym_to, u->ncalls); + } + } + + /* The cycle's list of children now contains all the members of the cycle. + Occasionally a function creeps in that doesn't belong in the cycle. + Find and remove them. */ + + { + struct symlist *v; + int found; + + do { + found = 0; + for (u=current_cycle_pointer->calls; u; u=u->next_to) { + for (v=u->sym_to->called; v; v=v->next_from) + if (v->sym_from->name[0]!='<' && v->sym_from->cycnum==number_of_cycles) + break; + if (!v) { + PRINT_OBNOXIOUS_DEBUG_MESSAGE (DB_CYC, ("%s not really in cycle", u->sym_to->name)); + /* This 'cycle member' wasn't called by any member of the cycle. + thus, it isn't a cycle member. */ + u->sym_to->cycnum=0; + delete_from_lists (current_cycle_pointer, u->sym_to); + found++; + } + } + } while (found); + } + /* The cycle's lists of callers and children are now correct. + Propagate the time through the cycle. */ + + + /* Now we propagate time INTO the + cycle from its children. We could do this in flushfuns, + but its easier to do here, since doing it here guarentees + it only happens once per cycle */ + + for (u=current_cycle_pointer->calls; u; u=u->next_to) { + struct mesym *symP; + struct symlist *v; + + /* We have to remember to not propagate time that's already here. . . */ + if (u->sym_to->cycnum!=number_of_cycles) { + current_cycle_pointer->ncalls+=u->ncalls; + + if (u->sym_to->cycnum==-1) + symP= u->sym_to; + else /* Propagate time FROM another cycle here */ + symP= cycles[u->sym_to->cycnum-1]; + u->prop_time= (FLOAT)u->ncalls*(FLOAT)symP->histo/(FLOAT)symP->ncalled; + u->child_time=(FLOAT)u->ncalls* symP->sub_histo /(FLOAT)symP->ncalled; + current_cycle_pointer->sub_histo+=u->prop_time+u->child_time; + for (v=u->sym_to->called; v; v=v->next_from) { + if (v->sym_from->cycnum==number_of_cycles) { + v->prop_time= (FLOAT)v->ncalls*(FLOAT)symP->histo/(FLOAT)symP->ncalled; + v->child_time=(FLOAT)v->ncalls* symP->sub_histo /(FLOAT)symP->ncalled; + } + } + } else { + u->prop_time= u->sym_to->histo; + u->child_time=u->sym_to->sub_histo; + + for (v=u->sym_to->calls; v; v=v->next_to) { + if (v->sym_to->cycnum==number_of_cycles) { + add_to_lists (current_cycle_pointer, v->sym_to, v->ncalls); + current_cycle_pointer->recursive+=v->ncalls; + } else { + symP = v->sym_to; + v->prop_time= (FLOAT)v->ncalls*(FLOAT)symP->histo/(FLOAT)symP->ncalled; + v->child_time=(FLOAT)v->ncalls* symP->sub_histo /(FLOAT)symP->ncalled; + } + } + + for (v=u->sym_to->called; v; v=v->next_from) { + assert (v->sym_from->cycnum==0 || v->sym_from->cycnum==number_of_cycles); + if (v->sym_from->cycnum==0) { + PRINT_OBNOXIOUS_DEBUG_MESSAGE (DB_CYC, ("Cycle called by %s (%d)", v->sym_from->name, v->ncalls)); + add_to_lists (v->sym_from, current_cycle_pointer, v->ncalls); + } + } + } + } + + + /* Sum all calls into the cycle, from each caller not in the cycle, + to get the number of calls into the cycle. */ + for (u=current_cycle_pointer->called; u; u=u->next_from) { + if (u->sym_from->cycnum!=number_of_cycles) + current_cycle_pointer->ncalled+=u->ncalls; + } + + /* Loop over functions in this cycle. */ + for (u=current_cycle_pointer->calls; u; u=u->next_to) { + struct symlist *v; + + if (u->sym_to->cycnum!=number_of_cycles) + continue; + + /* U->sym_to is a function in this cycle. */ + + /* Find all calls to this function from functions outside the cycle + and add remove them from the count of calls "from the cycle" to this function. */ + + for (v=u->sym_to->called; v; v=v->next_from) { + if (v->sym_from != current_cycle_pointer + && v->sym_from->cycnum==number_of_cycles) + u->sym_to->ncalled-=v->ncalls; + } + } + + /* Compute amounts of time to propagate out of the cycle + to the callers-in. */ + + for (u=current_cycle_pointer->called; u; u=u->next_from) + if (u->sym_from->cycnum != number_of_cycles) { + struct symlist *v; + + u->prop_time= ((FLOAT)u->ncalls*(FLOAT)current_cycle_pointer->histo /(FLOAT)current_cycle_pointer->ncalled); + u->child_time=((FLOAT)u->ncalls* current_cycle_pointer->sub_histo /(FLOAT)current_cycle_pointer->ncalled); + for (v=u->sym_from->calls; v; v=v->next_to) { + if (v->sym_to->cycnum==number_of_cycles) { + v->prop_time= ((FLOAT)v->ncalls*(FLOAT)current_cycle_pointer->histo /(FLOAT)current_cycle_pointer->ncalled); + v->child_time=((FLOAT)v->ncalls* current_cycle_pointer->sub_histo /(FLOAT)current_cycle_pointer->ncalled); + } + } + } + + flushfuns (); + } +} + + +/* Remove from the call tree all nodes that are rejected by + the -e, -E, -f and -F filters that were specified. + Their nodes are removed from functions_in_call_tree + and their edges are deleted from the lists they are in. */ + +void +filter_graph FUN0() +{ + int n; + int the_bomb_is_falling = 0; + + for (n=0; n<nfilters; n++) { + struct mesym **call_tree_pointer; + + call_tree_pointer=find_funp_from_name (filters[n].name); + /* Couldn't find it? Skip it! */ + if (!call_tree_pointer) { + /* It may have taken time, although it + isn't in the call tree. Seek and + destroy! */ + if (filters[n].type==BIG_E + || filters[n].type == REMOVE_TIME_IF_THERE) { + struct mesym *p; + + for (p=syms; p< &syms[nsym]; p++) { + if (!strcmp (p->name, filters[n].name)) { + tothist-=p->histo; + break; + } + } + if (p==&syms[nsym] && filters[n].type != REMOVE_TIME_IF_THERE) { + fprintf (stderr, "Warning: couldn't find function "); + fprint_name (stderr, filters[n].name); + fprintf (stderr, "\n"); + } + } else { + fprintf (stderr, "Warning: "); + fprint_name (stderr, filters[n].name); + fprintf (stderr, " is not in the call tree.\n"); + } + continue; + } + PRINT_OBNOXIOUS_DEBUG_MESSAGE (DB_OPT, ("Option %d on %s", filters[n].type, (*call_tree_pointer)->name)); + + switch (filters[n].type) { + case SMALL_E: + kill_children (*call_tree_pointer, FALSE); + break; + case BIG_E: + case REMOVE_TIME_IF_THERE: + kill_children (*call_tree_pointer, TRUE); + break; + case SMALL_F: + if (!the_bomb_is_falling) + the_bomb_is_falling = 1; + save_the_children (*call_tree_pointer, FALSE); + break; + case BIG_F: + if (!the_bomb_is_falling) { + the_bomb_is_falling = 1; + tothist=0; + } + save_the_children (*call_tree_pointer, TRUE); + break; + default: + abort (); + } + } + + /* If we had -e or -E filters, now delete everything that + was not marked to be saved. */ + + if (the_bomb_is_falling) { + struct mesym **call_tree_pointer; + + PRINT_OBNOXIOUS_DEBUG_MESSAGE (DB_OPT, ("And now we drop the bomb.")); + call_tree_pointer = + &functions_in_call_tree[number_of_functions_in_call_tree-1]; + do { + if (((*call_tree_pointer)->flag&SAVE_ME)==0) + remove_from_call_tree (call_tree_pointer); + } while (call_tree_pointer-->&functions_in_call_tree[0]); + } +} + +/* Collect a list of parents/children, sort them, and print them */ +/* If CALLED > 0, we print parents, + and the value of CALLED is the total number of times this fn was called. + If CALLED == -2 we print children. + This is used for printing the children of an entire cycle. + If CALLED == -1, we print children and if their cycle number is the + same as CYCLE, we print abbreviated information. + This is used for printing the children of an ordinary function. */ +/* LIST is the list of parents/children we want to print */ + +void +print_sorted_list FUN3(int, called, int, cycle, struct symlist *, list) +{ + static struct symlist **sbuf; + static nsbuf, sizsbuf; + struct symlist **sbp, *t; + struct mesym *symP; + + if (!sizsbuf) { + sbuf=(struct symlist **)ck_calloc (30, sizeof (struct symlist *)); + nsbuf=0; + sizsbuf=30; + } + + /* Extract all the symbols in LIST as a vector, + but ignore any which represent entire cycles. */ + + t=list; + for (sbp= &sbuf[0]; t;) { + symP = (called>=0) ? t->sym_from : t->sym_to; + if (symP && symP->name[0] != '<') { + *sbp=t; + sbp++; + nsbuf++; + if (nsbuf==sizsbuf) { + sizsbuf*=2; + sbuf=(struct symlist **)ck_realloc ((void *)sbuf, sizsbuf*sizeof (struct symlist *)); + sbp= &sbuf[nsbuf]; + } + } else { + symP++; + + } + + if (called>=0) t=t->next_from; + else t=t->next_to; + } + + /* Sort the vector. */ + qsort (sbuf, nsbuf, sizeof (struct symlist *), listcmp); + + /* Print the elements of the vector. */ + for (sbp= &sbuf[0]; nsbuf>0; nsbuf--, sbp++) { + t= *sbp; + if (called>=0) symP=t->sym_from; + else symP=t->sym_to; + + if (cycle>0 && cycle==symP->cycnum) { + if (called==-2) { + printf (" %7.2f %7.2f %4d ", + convert_and_round ((FLOAT)(symP->histo)), + convert_and_round ((FLOAT)(symP->sub_histo)), + t->ncalls); + fprint_name (stdout, symP->name); + } else { + /* For things in the same cycle, we only want to print + the number of calls */ + printf ("%30s %4d ", "", t->ncalls); + fprint_name (stdout, symP->name); + } + } else { + printf (" %7.2f %7.2f %4d/%-4d ", + convert_and_round (t->prop_time), + convert_and_round (t->child_time), + t->ncalls, + (called>=0) ? called : symP->ncalled); + fprint_name (stdout, symP->name); + } + + if (symP->cycnum>0 && symP->name[0]!='<') + printf (" <cycle %d>", symP->cycnum); + + if (symP->numindex) + printf (" [%d]\n", symP->numindex); + else + printf (" [not printed]\n"); + } +} + +/* Compare two symbols for which should come first among + the callers or subroutines in a single call-graph entry. */ + +int +listcmp FUN2(const void *, a, const void *, b) +{ + struct symlist *aa, *bb; + FLOAT n; + + aa= *(struct symlist **)a; + bb= *(struct symlist **)b; + + n=(bb->prop_time + bb->child_time - aa->prop_time - aa->child_time); + + if (n<0) return -1; + if (n>0) return 1; + return 0; +} + +/* Convert a number (IN) from the histogram into seconds, rounding to the + nearest 100th of a second. */ +FLOAT +convert_and_round FUN1(FLOAT, in) +{ + long int inter; + + inter=((in/(FLOAT)(ticks))+.005)*100.0; + return ((FLOAT)(inter)/100.0); +} + +/* Given ncalls from fromP to toP, add a symlist element telling about it */ +void +add_to_lists FUN3(struct mesym *, fromP, struct mesym *, toP, unsigned, ncalls) +{ + struct symlist *tmp; + + for (tmp=fromP->calls; tmp; tmp=tmp->next_to) + if (tmp->sym_to==toP) { + tmp->ncalls+=ncalls; + break; + } + if (!tmp) { + tmp=(struct symlist *)ck_malloc (sizeof (struct symlist)); + tmp->sym_from=fromP; + tmp->next_from=toP->called; + tmp->sym_to=toP; + tmp->next_to=fromP->calls; + tmp->ncalls=ncalls; + tmp->prop_time = -1; + tmp->child_time = -1; + + fromP->calls=tmp; + toP->called=tmp; + } +} + + +/* The reverse of add_to_lists. Forget that fromP ever called toP */ +void +delete_from_lists FUN2(struct mesym *, fromP, struct mesym *, toP) +{ + struct symlist *die, *tmp, *old; + + if (fromP->calls->sym_to==toP) { + die=fromP->calls; + fromP->calls=fromP->calls->next_to; + } else { + old=0; + for (tmp=fromP->calls; tmp; tmp=tmp->next_to) { + if (tmp->sym_to==toP) { + die=tmp; + old->next_to=tmp->next_to; + } + old=tmp; + } + } + + if (toP->called->sym_from==fromP) { + die=toP->called; + toP->called=toP->called->next_from; + } else { + for (tmp=toP->called; tmp; tmp=tmp->next_from) { + old=0; + if (tmp->sym_from==fromP) { + die=tmp; + old->next_from=tmp->next_from; + } + old=tmp; + } + } + free (die); +} + + +/* Implement the -e or -E option by deleting a certain function + and all its descendents from the call graph. + If FLUSHFLAG is set, remove its histogram time from the total, too. */ + +void +kill_children FUN2(struct mesym *, p, int, flushflag) +{ + struct symlist *t; + + push_ring_buffer (ring_buffer, p); + while (ring_buffer_isnt_empty (ring_buffer)) { + p=pop_ring_buffer (ring_buffer); + if (flushflag) + tothist-=p->histo; + PRINT_OBNOXIOUS_DEBUG_MESSAGE (DB_OPT, ("Flushing %s (%d)", p->name, flushflag ? p->histo : 0)); + p->flag|=KILL_ME; + remove_from_call_tree (find_funp_from_pointer (p)); + for (t=p->calls; t; t=t->next_to) { + if (t->sym_to->ncalled==t->ncalls + || (p->cycnum>0 && t->sym_to->cycnum==p->cycnum)) { + push_ring_buffer (ring_buffer, t->sym_to); + } else if (t->sym_to->cycnum>0 && t->sym_to->cycnum!=p->cycnum) { + if (cycles[t->sym_to->cycnum-1]->ncalled==t->ncalls) { + push_ring_buffer (ring_buffer, cycles[t->sym_to->cycnum-1]); + } + } else { + struct symlist *ztmp; + + for (ztmp=t->sym_to->called; ztmp; ztmp=ztmp->next_from) { + if ((ztmp->sym_from->flag&KILL_ME)==0) + break; + } + if (!ztmp) + push_ring_buffer (ring_buffer, t->sym_to); + else { + PRINT_OBNOXIOUS_DEBUG_MESSAGE (DB_OPT, ("Not flushing %s. . . More parents", t->sym_to->name)); + /* Do we want to deal with removing FRACTIONS of time from + functions who were called from different places? If so, + code goes here. (F'rinstance, if half of the calls to + function X were made from here, cut its stored time in half */ + } + } + } + } +} + +/* This is the opposite of kill_children (). + -f or -F has been specified, so all call-graph nodes EXCEPT + the descendents of specified functions will be killed. + Find all these descendents and mark them to be saved + by setting the `flag' fields nonzero. + + If TIMEFLAG is set, the histogram-total has + already been nuked, and we should add our histogram time to + it in an attempt at reconstruction. . . */ + +void +save_the_children FUN2(struct mesym *, p, int, timeflag) +{ + struct symlist *t; + + push_ring_buffer (ring_buffer, p); + while (ring_buffer_isnt_empty (ring_buffer)) { + p=pop_ring_buffer (ring_buffer); + PRINT_OBNOXIOUS_DEBUG_MESSAGE (DB_OPT, ("Saving %s (%d)", p->name, p->histo)); + p->flag|=SAVE_ME; + if (p->cycnum>0) + cycles[p->cycnum-1]->flag|=SAVE_ME; + if (timeflag) + tothist+=p->histo; + for (t=p->calls; t; t=t->next_to) { + if ((t->sym_to->flag&SAVE_ME)==0) + push_ring_buffer (ring_buffer, t->sym_to); + } + } +} + +/* The bomb is falling, and PT has just been hit by severe doses of + radiation. Remove it from the call-tree vector */ +void +remove_from_call_tree FUN1(struct mesym **, pt) +{ + if (!pt) { + /* Just quietly returning allows us to remove cycles from + the call tree easily. */ + /* panic ("Internal Error: trying to remove null from call tree"); */ + return; + } + PRINT_OBNOXIOUS_DEBUG_MESSAGE (DB_OPT, ("Removing %ss from the tree", (*pt)->name)); + *pt=functions_in_call_tree[number_of_functions_in_call_tree-1]; + number_of_functions_in_call_tree--; +} + +/* We want to play with a member of the call tree, but we + only know its name. Try to find the funp. This is slow, + natch, since it uses linear search, but it doesn't get called much */ + +/* Should be some way to scan rest of symbols so we could delete + mcount/mcleanup histogram ticks. Should be way to disable warning? */ +struct mesym ** +find_funp_from_name FUN1(char *, name) +{ + struct mesym **ret; + + ret= &functions_in_call_tree[number_of_functions_in_call_tree-1]; + do { + if (strcmp ((*ret)->name, name)==0) + return ret; + } while (ret-->&functions_in_call_tree[0]); + return 0; +} + +/* We have the mesym, but we need to know where it lives in the call-tree + This is almost as slow as find_funp_from_name (). */ +struct mesym ** +find_funp_from_pointer FUN1(struct mesym *, p) +{ + struct mesym **ret; + + ret= &functions_in_call_tree[number_of_functions_in_call_tree-1]; + do { + if (*ret==p) + return ret; + } while (ret-->&functions_in_call_tree[0]); + /* fprintf (stderr, "Warning: Can't find %s in the call tree.\n", p->name); */ + return 0; +} + +/* Read symbols from a.out file open on FP. There are N of them. Allocate a + vector to store the interesting ones in, and sort it numerically. */ + +void +read_syms FUN2(FILE *, fp, int, n) +{ + struct nlist *tmpsyms; + struct nlist *sym; + int i; +#ifdef __STDC__ + char buf[n]; +#else + char *buf; + char *alloca (); + + buf=alloca (n); +#endif + /* Read the entire symbol table. */ + tmpsyms=(struct nlist *)ck_malloc (n*sizeof (struct nlist)); + ck_fread (tmpsyms, sizeof (struct nlist), n, fp); + + bzero (buf, n); + + /* Count the useful symbols. + Also relocate their name-fields to be C string pointers. */ + for (sym= &tmpsyms[0], i=0; sym<&tmpsyms[n]; sym++) { + sym->n_un.n_name= strs+sym->n_un.n_strx; + if (!badsym (sym)) + buf[sym-tmpsyms] = 1, i++; + } + + /* Allocate permanent space and copy useful symbols into it. */ + nsym=i; + syms=(struct mesym *)ck_calloc (nsym, sizeof (struct mesym)); + + for (sym= &tmpsyms[0], i=0; sym<&tmpsyms[n]; sym++) { + if (!badsym (sym)) { + syms[i].name=sym->n_un.n_name; + +#ifndef nounderscore + /* Remove the initial _ from the symbol name */ + if (*(syms[i].name)=='_') + syms[i].name++; +#endif + syms[i].value=sym->n_value; + i++; + } + else if (buf[sym-tmpsyms]) + abort (); + } + + if (i != nsym) + abort (); + + /* Put symbols in numeric order. */ + qsort (syms, nsym, sizeof (struct mesym), symcmp); + free ((void *)tmpsyms); +} + +/* Return the symbol which has the largest value less than VAL. + Since the symbol vector is sorted by value, this is done + with a binary search. */ + +struct mesym * +val_to_sym FUN1(unsigned long, val) +{ + struct mesym *m; + int gap=nsym/4; + + m= &syms[nsym/2]; + for (;;) { + if (m->value>val) { + m-=gap; + gap/=2; + } else if ((m+1)->value<val) { + m+=gap; + gap/=2; + } else + break; + if (m<&syms[0] || m>=&syms[nsym]) + abort (); + if (gap<1) + gap=1; + } + return m; +} + +/* Return TRUE if the nlist-entry SYM describes a symbol + that gprof should pay attention to. */ + +int +badsym FUN1(struct nlist *, sym) +{ +#ifndef N_SECT + if ((sym->n_type & ~N_EXT) != N_TEXT) + return TRUE; +#else + if ((sym->n_type & ~N_EXT) != N_TEXT && (sym->n_type & ~N_EXT) != N_SECT) + return TRUE; +#endif + if (no_locals && !(sym->n_type&N_EXT)) + return TRUE; + /* Filenames or pascal labels should be ignored */ + if (index (sym->n_un.n_name, '.') || index (sym->n_un.n_name, '$')) + return TRUE; + return FALSE; +} + +/* This is used to qsort () the symbol table into numerical order. */ + +int +symcmp FUN2(const void *, a, const void *, b) +{ + struct mesym *aa, *bb; + + aa=(struct mesym *)a; + bb=(struct mesym *)b; + return aa->value - bb->value; +} + +/* This is used to sort the vector for the flat profile. + if they used different amounts of time, the one with + the most time goes first. If they used the same amount, + the one that was called most goes first. If they were + called the same #, they are sorted alphabetically */ + +int +timecmp FUN2(const void *, a, const void *, b) +{ + struct mesym *aa, *bb; + int n; + + aa= *(struct mesym **)a; + bb= *(struct mesym **)b; + n = bb->histo - aa->histo; + if (n==0) n=bb->ncalled - aa->ncalled; + if (n==0) n=strcmp (aa->name, bb->name); + return n; +} + +/* This is used to sort the functions_in_call_tree[] vector + so the leaf nodes are at the end, and the root nodes + are at the beginning. This bit about useless nodes could + probably be taken out now, since they shouldn't make it + into the vector in the first place */ + +int +callcmp FUN2(const void *, a, const void *, b) +{ + struct mesym *aa, *bb; + + aa= *(struct mesym **)a; + bb= *(struct mesym **)b; + + /* Send useless symbols to the end */ + if (aa->ncalls==0 && aa->ncalled==0) { + if (bb->ncalls==0 && bb->ncalled==0) + return EQ; + return GT; + } + if (bb->ncalled==0 && bb->ncalls==0) + return LT; + + /* send root nodes to the front; */ + /* Functions that were called 0 times + are spontaneous */ + + if (aa->ncalled==0) { + if (bb->ncalled==0) + return EQ; + return LT; + } + if (bb->ncalled==0) + return GT; + + /* Send leaf nodes to the end */ + if (aa->ncalls==0) { + if (bb->ncalls==0) + return EQ; + return GT; + } + if (bb->ncalls==0) + return LT; + + /* And keep the rest the same */ + return EQ; +} + +/* This is used to sort functions_in_call_tree[] before printing. + To print, we want + A: the ones with the most time first + a: (If one was never called, put it first, 'cuz it + was invoked by GOD, else the one called the most + # of times first) + 1: the one with the lower name (strcmp ()) first + */ + +int +treetimecmp FUN2(const void *, a, const void *, b) +{ + struct mesym *aa, *bb; + int n; + + aa= *(struct mesym **)a; + bb= *(struct mesym **)b; + n = (bb->histo+bb->sub_histo) - (aa->histo+aa->sub_histo); + if (n==0) { + + /* Just to be bizarre: If one was never called, + put it first, else put the one who was called + the most first. Confused yet? */ + if (aa->ncalled==0) + return LT; + if (bb->ncalled==0) + return GT; + n=bb->ncalled - aa->ncalled; + if (n==0) + n=strcmp (aa->name, bb->name); + } + return n; +} + +/* Read in a gmon.out file named NAME and deal with the stuff inside it. */ + +void +readgm FUN1(char *, name) +{ + FILE *fp; + struct gm_header tmp; + unsigned CHUNK *p; + int n; + struct gm_call calltmp; + + PRINT_OBNOXIOUS_DEBUG_MESSAGE (DB_GFILE, ("gmon from `%s'", name)); + + fp=ck_fopen (name, "r"); + + /* Read in the gmon file header and check that histogram is + compatible with the other gmon files already read. */ + + ck_fread ((void *)&tmp, sizeof (tmp), 1, fp); + if (hdr.low) { + if (hdr.low!=tmp.low || hdr.high!=tmp.high) + fatal ("file `%s' is incompatable with previous gmon.out file", name); + } else + hdr=tmp; + + PRINT_OBNOXIOUS_DEBUG_MESSAGE (DB_GFILE, ("lowpc=%ld hipc=%ld nbytes= %ld", hdr.low, hdr.high, hdr.nbytes)); + + /* Allocate the total histogram if we haven't yet done so. */ + + if (!histo) { + nhist=(hdr.nbytes-sizeof (struct gm_header))/sizeof (CHUNK); + histo=(unsigned CHUNK *)ck_calloc (nhist, sizeof (unsigned CHUNK)); + } + + /* Read this file's histogram and merge it into the total one. */ + + p=(unsigned CHUNK *)ck_malloc (nhist*sizeof (unsigned CHUNK)); + ck_fread ((void *)p, sizeof (unsigned CHUNK), nhist, fp); + for (n=0; n<nhist; n++) { + tothist+=p[n]; + histo[n]+=p[n]; + if (debug&DB_GFILE) { + static ncol; + + if (n==0) ncol=0; + if (histo[n]) { + fprintf (stderr, "s[%05d]=%-3d", n, histo[n]); + if (ncol++%10==9) fputc ('\n', stderr); + } + } + } + free ((void *)p); + PRINT_OBNOXIOUS_DEBUG_MESSAGE (DB_GFILE, ("")); + + /* We've read in the histogram, now read in the function call data. + Loop, reading one call-graph edge from the file + and recording the edge in the graph. */ + + while (fread ((void *)&calltmp, sizeof (calltmp), 1, fp)==1) { + struct symlist *tmp; + struct mesym *s_fm, *s_to; + + /* Find the calling and called functions's symbol entries. */ + + s_fm=val_to_sym (calltmp.from); + s_to=val_to_sym (calltmp.to); + + if (!s_fm || !s_to) { + PRINT_OBNOXIOUS_DEBUG_MESSAGE (DB_GFILE, ("unknown call %#lx to %#lx %d times.", calltmp.from, calltmp.to, calltmp.ncalls)); + continue; + } + + /* Updated total numbers of calls from this caller and to this callee. */ + + s_to->ncalled+=calltmp.ncalls; + s_fm->ncalls+=calltmp.ncalls; + + /* Add these calls to the edge between them. */ + + add_to_lists (s_fm, s_to, calltmp.ncalls); + + PRINT_OBNOXIOUS_DEBUG_MESSAGE + (DB_GFILE, ("call %s (%#lx) to %s (%#lx) %ld times", s_fm->name, + calltmp.from, s_to->name, calltmp.to, calltmp.ncalls)); + } + + ck_fclose (fp); +} + +/* Print one of those obnoxious and vaguely informative blurbs that we know + and love so well. Used to be, we'd print them out of a file, but nowaday + the blurb is encoded direcly into the program. Makes printing them + much faster. Also means bozo syswiz can't accidentally delete/move/etc the + blurb file on us. */ + +void +print_blurb FUN1(char *, blurb) +{ + if (! no_blurbs) + fputs (blurb, stdout); +} + + +/* Find the number of clock ticks/second by reading the kernel's memory. + This means that if /dev/kmem isn't readable, this program will have to run + set[ug]id to someone who can read the kernel. setgid is probably best. */ + +long +get_ticks FUN0() +{ + static struct nlist nl[2]; + long ret; + FILE *fp; + +#ifdef USG +#include <sys/types.h> +#include <sys/param.h> + ret = HZ; +#else + nl[0].n_un.n_name="_hz"; + nlist ("/vmunix", &nl[0]); + if (nl[0].n_type==0) + fatal ("Can't find `%s' in namelist of /vmunix", nl[0].n_un.n_name); + fp=ck_fopen ("/dev/kmem", "r"); + ck_fseek (fp, (long)nl[0].n_value, 0); + ck_fread ((void *)&ret, sizeof (ret), 1, fp); + fclose (fp); /* This really should be ck_fclose, but on some systems, + closing devices returns -1, errno=NOT_OWNER, which + really screws things up. . . */ +#endif + PRINT_OBNOXIOUS_DEBUG_MESSAGE (DB_MISC, ("get_ticks ()=%ld", ret)); + return ret; +} + + +/* Record one -e, -E, -f or -F option in `filters', and check for conflicts. + All these options are recorded there for processing later + once the call-graph has been constructed. */ + +void +add_filter FUN2(char *, name, int, type) +{ + if (!filters) { + filters=(struct filter *)ck_malloc (sizeof (struct filter)); + nfilters=1; + } else { + int n; + + for (n=0; n<nfilters; n++) { + if (filters[n].name==name || !strcmp (filters[n].name, name)) + fatal ("Conflicting options for function `%s'", name); + } + nfilters++; + filters=(struct filter *)ck_realloc ((void *)filters, sizeof (struct filter)*nfilters); + } + filters[nfilters-1].name=name; + filters[nfilters-1].type=type; +} + +/* Data base associating stdio streams with the file names that are open. */ + +struct file_to_name { + FILE *stream; /* A stdio stream */ + char *name; /* The file name (malloc'd specially for this list) */ + struct file_to_name *next; +}; + +struct file_to_name *files_to_names; + +/* Given a stdio stream, look it up in the data base and return + the file name. */ + +char * +stream_name FUN1(FILE *, stream) +{ + struct file_to_name *tail; + + for (tail = files_to_names; tail; tail = tail->next) + if (tail->stream == stream) + return tail->name; + + return "unknown file"; +} + +/* Open a file like `fopen', but report a fatal error if it fails; + if it succeeds, record the stream and filename in the data base of such. */ + +FILE * +ck_fopen FUN2(char *, name, char *, mode) +{ + FILE *stream; + int n; + struct file_to_name *new; + + stream=fopen (name, mode); + if (stream==(FILE *)0) + fatal_io ("Couldn't open", name); + + new = ck_malloc (sizeof (struct file_to_name)); + new->name = ck_malloc (strlen (name) + 1); + strcpy (new->name, name); + new->stream = stream; + new->next = files_to_names; + files_to_names = new; + return stream; +} + +/* Interfaces to various functions of stdio + which use the stream/filename database to print an error message + if the function gets an error. */ + +void +ck_fseek FUN3(FILE *, stream, long, i, int, w) +{ + if (fseek (stream, i, w)==-1) + fatal_io ("Couldn't lseek", stream_name (stream)); +} + +void +ck_fread FUN4(void *, ptr, size_t, size, size_t, nmemb, FILE *, stream) +{ + if (fread (ptr, size, nmemb, stream)!=nmemb) + fatal_io ("Couldn't read", stream_name (stream)); +} + +void +ck_fwrite FUN4(void *, ptr, size_t, size, size_t, nmemb, FILE *, stream) +{ + if (fwrite (ptr, size, nmemb, stream)!=nmemb) + fatal_io ("Couldn't write", stream_name (stream)); +} + +void +ck_fclose FUN1(FILE *, stream) +{ + struct file_to_name *tail; + + if(stream->_flag & _IOWRT) + fflush (stream); + if (ferror (stream)) + fatal ("I/O error on `%s'", stream_name (stream)); + if (fclose (stream)==EOF) + fatal_io ("Couldn't close", stream_name (stream)); + + if (files_to_names && files_to_names->stream == stream) + files_to_names = files_to_names->next; + else + for (tail = files_to_names; tail->next; tail = tail->next) + if (tail->next->stream == stream) + { + struct file_to_name *loser = tail->next; + tail->next = tail->next->next; + free (loser->name); + free (loser); + return; + } +} + +/* Report a fatal error doing I/O, and exit. + PROBLEM is "Couldn't whatever" and NAME is the file name. */ + +void +fatal_io FUN2(char *, problem, char *, name) +{ + fprintf (stderr, "%s: %s %s:", myname, problem, name); + perror (0); + exit (1); +} + +/* Report a fatal error and exit. Arguments like `printf'. */ + +void +fatal FUN1N(char *, s) +{ + va_list iggy; + + var_start (iggy, s); + fprintf (stderr, "%s: ", myname); + vfprintf (stderr, s, iggy); + /* _doprnt (s, iggy, stderr); */ + putc ('\n', stderr); + va_end (iggy); + + exit (1); +} + +/* Memory allocation functions. */ + +/* This function is called just like `printf' + except that the output is put into a new string + allocated with `malloc'. + Returns the address of the string. The caller must free the string. */ + +char * +mk_sprintf FUN1N(char *, str) +{ + char tmpbuf[2048]; + char *ret; + va_list iggy; + + var_start (iggy, str); + vsprintf (tmpbuf, str, iggy); + va_end (iggy); + + ret=ck_malloc (strlen (tmpbuf)+1); + strcpy (ret, tmpbuf); + return ret; +} + +/* Encapsulations of `malloc', `calloc' and `realloc' + that cause fatal errors if there is not enough memory. */ + +void * +ck_malloc FUN1(size_t, size) +{ + void *ret; + void *malloc (); + + ret=malloc (size); + if (ret==(void *)0) + fatal ("Virtual memory exhausted"); + return ret; +} + +void * +ck_calloc FUN2(size_t, nmemb, size_t, size) +{ + void *ret; + void *calloc (); + + ret=calloc (nmemb, size); + if (ret==(void *)0) + fatal ("Virtual memory exhausted"); + return ret; +} + +void * +ck_realloc FUN2(void *, ptr, size_t, size) +{ + void *ret; + void *realloc (); + + ret=realloc (ptr, size); + if (ret==(void *)0) + fatal ("Virtual memory exhausted"); + return ret; +} + +/* Implement an expandable fifo buffer of pointers + (we don't care what they point to) + which is used for conducting breadth-first tree walks in the call graph. + + The fifo is implemented as a ring buffer. */ + +struct ring_buf +{ + void **buf; + int size; + void **push_to_here; + void **pop_frm_here; +}; + +void * +init_ring_buffer FUN0() +{ + struct ring_buf *ret; + + ret=ck_malloc (sizeof (struct ring_buf)); + ret->size=40; + ret->buf=(void **)ck_calloc (ret->size, sizeof (void **)); + ret->push_to_here=ret->buf; + ret->pop_frm_here=ret->buf; + return ret; +} + +void +push_ring_buffer FUN2(void *, b, void *, n) +{ + struct ring_buf *buf; + + buf=(struct ring_buf *)b; + if (buf->push_to_here+1==buf->pop_frm_here + || (buf->pop_frm_here==buf->buf + && buf->push_to_here==buf->buf+(buf->size-1))) { + int f, t, from_num; + + f=buf->pop_frm_here-buf->buf; + t=buf->push_to_here-buf->buf; + from_num=buf->size-f; + + buf->size*=2; + buf->buf=ck_realloc ((void *)buf->buf, buf->size*sizeof (void **)); + if (t==0) { + buf->push_to_here=buf->buf+f+from_num; + buf->pop_frm_here=buf->buf+f; + } else if (t>f) { + buf->push_to_here=buf->buf+t; + buf->pop_frm_here=buf->buf+f; + } else { + buf->push_to_here=buf->buf+t; + buf->pop_frm_here=buf->buf+(buf->size-from_num); + if (from_num) + bcopy (buf->buf+f, + buf->pop_frm_here, + from_num*sizeof (void **)); + } + } + *(buf->push_to_here)=n; + buf->push_to_here++; + if (buf->push_to_here==buf->buf+buf->size) + buf->push_to_here=buf->buf; +} + +void * +pop_ring_buffer FUN1(void *, b) +{ + struct ring_buf *buf; + void *ret; + + buf=(struct ring_buf *)b; + ret= *(buf->pop_frm_here); + buf->pop_frm_here++; + if (buf->pop_frm_here==buf->buf+buf->size) + buf->pop_frm_here=buf->buf; + return ret; +} + +int +ring_buffer_isnt_empty FUN1(void *, b) +{ + struct ring_buf *buf; + + buf=(struct ring_buf *)b; + return buf->pop_frm_here!=buf->push_to_here; +} + +void +flush_ring_buffer FUN1(void *, b) +{ + struct ring_buf *buf; + + buf=(struct ring_buf *)b; + free (buf->buf); + free (buf); +} + + +/* Functions to print debugging messages. */ + +/* Like vprintf but print an extra newline at the end. */ + +void +dbgprintf FUN1N(char *, s) +{ + va_list iggy; + + var_start (iggy, s); + vfprintf (stderr, s, iggy); + putc ('\n', stderr); + va_end (iggy); +} + +/* Print out the entire symbol table */ + +void +dumpsyms FUN0() +{ + struct mesym *np, *endp; + struct symlist *sy; + int n; + char buf[80]; + + n=0; + for (np=syms, endp= &syms[nsym]; np<endp; np++) { + char *demangled = cplus_demangle (np->name); + char *name = demangled == NULL ? np->name : demangled; + + sprintf (buf, "%-15s %#6lx %d %2d.%2d[%d]", + name, np->value, np->histo, np->ncalled, np->ncalls, np->cycnum); + if (demangled != NULL) + free (demangled); + + if (n==0) { + printf ("%-35s", buf); + n++; + } else { + printf ("%s\n", buf); + n=0; + } + } + putchar ('\n'); + if (n==0) putchar ('\n'); +} + +/* Print out the call tree vector */ +void +dumpfuns FUN0() +{ + struct mesym *np; + struct symlist *sy; + int n; + + for (n=0; n<number_of_functions_in_call_tree; n++) { + np=functions_in_call_tree[n]; + fprint_name (stdout, np->name); + printf ("{%d}[%d] ", np->cycnum, np->ncalls); + /* for (sy=np->called; sy; sy=sy->next) + printf ("%s{%d}(%d) ", sy->sym->name, sy->sym->cycnum, sy->ncalls); + putchar ('\n'); */ + } +} + +/* JF someone put this stuff in the file before all the prototyes et all. + I moved them here where they belong */ + +/* Like malloc but abort if out of memory. */ +void * +xmalloc FUN1(size_t, size) +{ + return ck_malloc(size); +} + +/* Like realloc but abort if out of memory. */ +void * +xrealloc FUN2(void *, p, size_t, size) +{ + return ck_realloc(p,size); +} + +/* Print NAME on STREAM, demangling if necessary. */ +void +fprint_name FUN2(FILE *, stream, char *, name) +{ + char *demangled = cplus_demangle (name); + if (demangled == NULL) + fputs (name, stream); + else + { + fputs (demangled, stream); + free (demangled); + } +} diff --git a/binutils-1.9/gprof.texinfo b/binutils-1.9/gprof.texinfo new file mode 100644 index 0000000..a4161ed --- /dev/null +++ b/binutils-1.9/gprof.texinfo @@ -0,0 +1,943 @@ +\input texinfo @c -*-texinfo-*- + +@setfilename gprof.info +@settitle gprof +@setchapternewpage odd +@ifinfo +This file documents the gprof profiler of the GNU system. + +Copyright (C) 1988 Free Software Foundation, Inc. + +Permission is granted to make and distribute verbatim copies of +this manual provided the copyright notice and this permission notice +are preserved on all copies. + +@ignore +Permission is granted to process this file through Tex and print the +results, provided the printed document carries copying permission +notice identical to this one except for the removal of this paragraph +(this paragraph not being relevant to the printed manual). + +@end ignore +Permission is granted to copy and distribute modified versions of this +manual under the conditions for verbatim copying, provided that the entire +resulting derived work is distributed under the terms of a permission +notice identical to this one. + +Permission is granted to copy and distribute translations of this manual +into another language, under the above conditions for modified versions. +@end ifinfo + +@iftex +@finalout +@end iftex + +@titlepage +@sp 11 +@center @titlefont{gprof} +@sp 1 +@center The GNU Profiler +@sp 2 +@center Jay Fenlason and Richard Stallman +@sp 1 +@center @today +@sp 3 +This manual describes the GNU profiler, @code{gprof}, and how you can use +it to determine which parts of a program are taking most of the execution +time. We assume that you know how to write, compile, and execute programs. +GNU @code{gprof} was written by Jay Fenlason. +@page +@vskip 0pt plus 1filll +Copyright @copyright{} 1988 Free Software Foundation, Inc. + +Permission is granted to make and distribute verbatim copies of +this manual provided the copyright notice and this permission notice +are preserved on all copies. + +@ignore +Permission is granted to process this file through Tex and print the +results, provided the printed document carries copying permission +notice identical to this one except for the removal of this paragraph +(this paragraph not being relevant to the printed manual). + +@end ignore +Permission is granted to copy and distribute modified versions of this +manual under the conditions for verbatim copying, provided that the entire +resulting derived work is distributed under the terms of a permission +notice identical to this one. + +Permission is granted to copy and distribute translations of this manual +into another language, under the same conditions as for modified versions. + +@end titlepage + +@ifinfo +@node Top, Why, Top, (dir) +@ichapter Profiling a Program: Where Does It Spend Its Time? + +This manual describes the GNU profiler @code{gprof}, and how you can use it +to determine which parts of a program are taking most of the execution +time. We assume that you know how to write, compile, and execute programs. +GNU @code{gprof} was written by Jay Fenlason. + +@menu +* Why:: What profiling means, and why it is useful. +* Compiling:: How to compile your program for profiling. +* Executing:: How to execute your program to generate the + profile data file @file{gmon.out}. +* Analyzing:: How to run @code{gprof}, and how to specify + options for it. + +* Flat Profile:: The flat profile shows how much time was spent + executing directly in each function. +* Call Graph:: The call graph shows which functions called which + others, and how much time each function used + when its subroutine calls are included. + +* Implementation:: How the profile data is recorded and written. +* Sampling Error:: Statistical margins of error. + How to accumulate data from several runs + to make it more accurate. + +* Assumptions:: Some of @code{gprof}'s measurements are based + on assumptions about your program + that could be very wrong. + +* Incompatibilities:: (between GNU @code{gprof} and Unix @code{gprof}.) +@end menu +@end ifinfo + +@node Why, Compiling, Top, Top +@chapter Why Profile + +Profiling allows you to learn where your program spent its time and which +functions called which other functions while it was executing. This +information can show you which pieces of your program are slower than you +expected, and might be candidates for rewriting to make your program +execute faster. It can also tell you which functions are being called more +or less often than you expected. This may help you spot bugs that had +otherwise been unnoticed. + +Since the profiler uses information collected during the actual execution +of your program, it can be used on programs that are too large or too +complex to analyze by reading the source. However, how your program is run +will affect the information that shows up in the profile data. If you +don't use some feature of your program while it is being profiled, no +profile information will be generated for that feature. + +Profiling has several steps: + +@itemize @bullet +@item +You must compile and link your program with profiling enabled. +@xref{Compiling}. + +@item +You must execute your program to generate a profile data file. +@xref{Executing}. + +@item +You must run @code{gprof} to analyze the profile data. +@xref{Analyzing}. +@end itemize + +The next three chapters explain these steps in greater detail. + +The result of the analysis is a file containing two tables, the +@dfn{flat profile} and the @dfn{call graph} (plus blurbs which briefly +explain the contents of these tables). + +The flat profile shows how much time your program spent in each function, +and how many times that function was called. If you simply want to know +which functions burn most of the cycles, it is stated concisely here. +@xref{Flat Profile}. + +The call graph shows, for each function, which functions called it, which +other functions it called, and how many times. There is also an estimate +of how much time was spent in the subroutines of each function. This can +suggest places where you might try to eliminate function calls that use a +lot of time. @xref{Call Graph}. + +@node Compiling, Executing, Why, Top +@chapter Compiling a Program for Profiling + +The first step in generating profile information for your program is +to compile and link it with profiling enabled. + +To compile a source file for profiling, specify the @samp{-pg} option when +you run the compiler. (This is in addition to the options you normally +use.) + +To link the program for profiling, if you use a compiler such as @code{cc} +to do the linking, simply specify @samp{-pg} in addition to your usual +options. The same option, @samp{-pg}, alters either compilation or linking +to do what is necessary for profiling. Here are examples: + +@example +cc -g -c myprog.c utils.c -pg +cc -o myprog myprog.o utils.o -pg +@end example + +The @samp{-pg} option also works with a command that both compiles and links: + +@example +cc -o myprog myprog.c utils.c -g -pg +@end example + +If you run the linker @code{ld} directly instead of through a compiler such +as @code{cc}, you must specify the profiling startup file +@file{/lib/gcrt0.o} as the first input file instead of the usual startup +file @file{/lib/crt0.o}. In addition, you would probably want to specify +the profiling C library, @file{/usr/lib/libc_p.a}, by writing @samp{-lc_p} +instead of the usual @samp{-lc}. This is not absolutely necessary, but doing +this gives you number-of-calls information for standard library functions such +as @code{read} and @code{open}. For example: + +@example +ld -o myprog /lib/gcrt0.o myprog.o utils.o -lc_p +@end example + +If you compile only some of the modules of the program with @samp{-pg}, you +can still profile the program, but you won't get complete information about +the modules that were compiled without @samp{-pg}. The only information +you get for the functions in those modules is the total time spent in them; +there is no record of how many times they were called, or from where. This +will not affect the flat profile (except that the @code{calls} field for +the functions will be blank), but will greatly reduce the usefulness of the +call graph. + +So far GNU @code{gprof} has been tested only with C programs, but it ought +to work with any language in which programs are compiled and linked to form +executable files. If it does not, please let us know. + +@node Executing, Analyzing, Compiling, Top +@chapter Executing the Program to Generate Profile Data + +Once the program is compiled for profiling, you must run it in order to +generate the information that @code{gprof} needs. Simply run the program +as usual, using the normal arguments, file names, etc. The program should +run normally, producing the same output as usual. It will, however, run +somewhat slower than normal because of the time spent collecting and the +writing the profile data. + +The way you run the program---the arguments and input that you give +it---may have a dramatic effect on what the profile information shows. The +profile data will describe the parts of the program that were activated for +the particular input you use. For example, if the first command you give +to your program is to quit, the profile data will show the time used in +initialization and in cleanup, but not much else. + +You program will write the profile data into a file called @file{gmon.out} +just before exiting. If there is already a file called @file{gmon.out}, +its contents are overwritten. There is currently no way to tell the +program to write the profile data under a different name, but you can rename +the file afterward if you are concerned that it may be overwritten. + +In order to write the @file{gmon.out} file properly, your program must exit +normally: by returning from @code{main} or by calling @code{exit}. Calling +the low-level function @code{_exit} does not write the profile data, and +neither does abnormal termination due to an unhandled signal. + +The @file{gmon.out} file is written in the program's @emph{current working +directory} at the time it exits. This means that if your program calls +@code{chdir}, the @file{gmon.out} file will be left in the last directory +your program @code{chdir}'d to. If you don't have permission to write in +this directory, the file is not written. You may get a confusing error +message if this happens. (We have not yet replaced the part of Unix +responsible for this; when we do, we will make the error message +comprehensible.) + +@node Analyzing, Flat Profile, Executing, Top +@chapter Analyzing the Profile Data: @code{gprof} Command Summary + +After you have a profile data file @file{gmon.out}, you can run @code{gprof} +to interpret the information in it. The @code{gprof} program prints a +flat profile and a call graph on standard output. Typically you would +redirect the output of @code{gprof} into a file with @samp{>}. + +You run @code{gprof} like this: + +@example +@code{gprof} @var{options} [@var{executable-file} [@var{profile-data-files}@dots{}]] [> @var{outfile}] +@end example + +@noindent +Here square-brackets indicate optional arguments. + +If you omit the executable file name, the file @file{a.out} is used. If +you give no profile data file name, the file @file{gmon.out} is used. If +any file is not in the proper format, or if the profile data file does not +appear to belong to the executable file, an error message is printed. + +You can give more than one profile data file by entering all their names +after the executable file name; then the statistics in all the data files +are summed together. + +The following options may be used to selectively include or exclude +functions in the output: + +@table @code +@item -a +The @code{-a} option causes @code{gprof} to ignore static (private) +functions. (These are functions whose names are not listed as global, +and which are not visible outside the file/function/block where they +were defined.) Time spent in these functions, calls to/from them, +etc, will all be attributed to the function that was loaded directly +before it in the executable file. This is compatible with Unix +@code{gprof}, but a bad idea. This option affects both the flat +profile and the call graph. + +@item -e @var{function_name} +The @code{-e @var{function}} option tells @code{gprof} to not print +information about the function (and its children@dots{}) in the call +graph. The function will still be listed as a child of any functions +that call it, but its index number will be shown as @samp{[not +printed]}. + +@item -E @var{function_name} +The @code{-E @var{function}} option works like the @code{-e} option, +but time spent in the function (and children who were not called from +anywhere else), will not be used to compute the percentages-of-time +for the call graph. + +@item -f @var{function_name} +The @code{-f @var{function}} option causes @code{gprof} to limit the +call graph to the function and its children (and their +children@dots{}). + +@item -F @var{function_name} +The @code{-F @var{function}} option works like the @code{-f} option, +but only time spent in the function and its children (and their +children@dots{}) will be used to determine total-time and +percentages-of-time for the call graph. + +@item -z +If you give the @code{-z} option, @code{gprof} will mention all +functions in the flat profile, even those that were never called, and +that had no time spent in them. +@end table + +The order of these options does not matter. + +Note that only one function can be specified with each @code{-e}, +@code{-E}, @code{-f} or @code{-F} option. To specify more than one +function, use multiple options. For example, this command: + +@example +gprof -e boring -f foo -f bar myprogram > gprof.output +@end example + +@noindent +lists in the call graph all functions that were reached from either +@code{foo} or @code{bar} and were not reachable from @code{boring}. + +There are two other useful @code{gprof} options: + +@table @code +@item -b +If the @code{-b} option is given, @code{gprof} doesn't print the +verbose blurbs that try to explain the meaning of all of the fields in +the tables. This is useful if you intend to print out the output, or +are tired of seeing the blurbs. + +@item -s +The @code{-s} option causes @code{gprof} to summarize the information +in the profile data files it read in, and write out a profile data +file called @file{gmon.sum}, which contains all the information from +the profile data files that @code{gprof} read in. The file @file{gmon.sum} +may be one of the specified input files; the effect of this is to +merge the data in the other input files into @file{gmon.sum}. +@xref{Sampling Error}. + +Eventually you can run @code{gprof} again without @samp{-s} to analyze the +cumulative data in the file @file{gmon.sum}. +@end table + +@node Flat Profile, Call Graph, Analyzing, Top +@chapter How to Understand the Flat Profile +@cindex flat profile + +The @dfn{flat profile} shows the total amount of time your program +spent executing each function. Unless the @samp{-z} option is given, +functions with no apparent time spent in them, and no apparent calls +to them, are not mentioned. Note that if a function was not compiled +for profiling, and didn't run long enough to show up on the program +counter histogram, it will be indistinguishable from a function that +was never called. +@c ??? + +Here is a sample flat profile for a small program: + +@example +Each sample counts as 0.01 seconds. + +% time seconds cumsec calls function + 79.17 0.19 0.19 6 a + 16.67 0.04 0.23 1 main + 4.17 0.01 0.24 mcount + 0.00 0 0.24 1 profil +@end example + +@noindent +The functions are sorted by decreasing run-time spent in them. The +functions @code{mcount} and @code{profil} are part of the profiling +aparatus and appear in every flat profile; their time gives a measure of +the amount of overhead due to profiling. (These internal functions are +omitted from the call graph.) + +The sampling period estimates the margin of error in each of the time +figures. A time figure that is not much larger than this is not reliable. +In this example, the @code{seconds} field for @code{mcount} might well be 0 +or 0.02 in another run. @xref{Sampling Error}, for a complete discussion. + +Here is what the fields in each line mean: + +@table @code +@item % time +This is the percentage of the total execution time your program spent +in this function. These should all add up to 100%. + +@item seconds +This is the total number of seconds the computer spent executing the +user code of this function. + +@item cumsec +This is the cumulative total number of seconds the computer spent +executing this functions, plus the time spent in all the functions +above this one in this table. + +@item calls +This is the total number of times the function was called. If the +function was never called, or the number of times it was called cannot +be determined (probably because the function was not compiled with +profiling enabled), the @dfn{calls} field is blank. + +@item function +This is the name of the function. +@end table + +@node Call Graph, Implementation, Flat Profile, Top +@chapter How to Read the Call Graph + +@cindex call graph +The @dfn{call graph} shows how much time was spent in each function +and its children. From this information, you can find functions that, +while they themselves may not have used much time, called other +functions that did use unusual amounts of time. + +Here is a sample call from a small program. This call came from the +same @code{gprof} run as the flat profile example in the previous +chapter. + +@example +index % time self children called name + <spontaneous> +[1] 100.00 0 0.23 0 start [1] + 0.04 0.19 1/1 main [2] +---------------------------------------- + 0.04 0.19 1/1 start [1] +[2] 100.00 0.04 0.19 1 main [2] + 0.19 0 1/1 a [3] +---------------------------------------- + 0.19 0 1/1 main [2] +[3] 82.61 0.19 0 1+5 a [3] +---------------------------------------- +@end example + +The lines full of dashes divide this table into @dfn{entries}, one for each +function. Each entry has one or more lines. + +In each entry, the primary line is the one that starts with an index number +in square brackets. The end of this line says which function the entry is +for. The preceding lines in the entry describe the callers of this +function and the following lines describe its subroutines (also called +@dfn{children} when we speak of the call graph). + +The entries are sorted by time spent in the function and its subroutines. + +The internal profiling functions @code{mcount} and @code{profil} +(@pxref{Flat Profile}) are never mentioned in the call graph. + +@menu +* Primary:: Details of the primary line's contents. +* Callers:: Details of caller-lines' contents. +* Subroutines:: Details of subroutine-lines' contents. +* Cycles:: When there are cycles of recursion, + such as @code{a} calls @code{b} calls @code{a}@dots{} +@end menu + +@node Primary, Callers, Call Graph, Call Graph +@section The Primary Line + +The @dfn{primary line} in a call graph entry is the line that +describes the function which the entry is about and gives the overall +statistics for this function. + +For reference, we repeat the primary line from the entry for function +@code{a} in our main example, together with the heading line that shows the +names of the fields: + +@example +index % time self children called name +@dots{} +[3] 82.61 0.19 0 1+5 a [3] +@end example + +Here is what the fields in the primary line mean: + +@table @code +@item index +Entries are numbered with consecutive integers. Each function +therefore has an index number, which appears at the beginning of its +primary line. + +Each cross-reference to a function, as a caller or subroutine of +another, gives its index number as well as its name. The index number +guides you if you wish to look for the entry for that function. + +@item % time +This is the percentage of the total time that was spent in this +function, including time spent in subroutines called from this +function. + +The time spent in this function is counted again for the callers of +this function. Therefore, adding up these percentages is meaningless. + +@item self +This is the total amount of time spent in this function. This +should be identical to the number printed in the @code{seconds} field +for this function in the flat profile. + +@item children +This is the total amount of time spent in the subroutine calls made by +this function. This should be equal to the sum of all the @code{self} +and @code{children} entries of the children listed directly below this +function. + +@item called +This is the number of times the function was called. + +If the function called itself recursively, there are two numbers, +separated by a @samp{+}. The first number counts non-recursive calls, +and the second counts recursive calls. + +In the example above, the function @code{a} called itself five times, +and was called once from @code{main}. + +@item name +This is the name of the current function. The index number is +repeated after it. + +If the function is part of a cycle of recursion, the cycle number is +printed between the function's name and the index number +(@pxref{Cycles}). For example, if function @code{gnurr} is part of +cycle number one, and has index number twelve, its primary line would +be end like this: + +@example +gnurr <cycle 1> [12] +@end example +@end table + +@node Callers, Subroutines, Primary, Call Graph +@section Lines for a Function's Callers + +A function's entry has a line for each function it was called by. +These lines' fields correspond to the fields of the primary line, but +their meanings are different because of the difference in context. + +For reference, we repeat two lines from the entry for the function +@code{a}, the primary line and one caller-line preceding it, together +with the heading line that shows the names of the fields: + +@example +index % time self children called name +@dots{} + 0.19 0 1/1 main [2] +[3] 82.61 0.19 0 1+5 a [3] +@end example + +Here are the meanings of the fields in the caller-line for @code{a} +called from @code{main}: + +@table @code +@item self +An estimate of the amount of time spent in @code{a} itself when it was +called from @code{main}. + +@item children +An estimate of the amount of time spent in @code{a}'s subroutines when +@code{a} was called from @code{main}. + +The sum of the @code{self} and @code{children} fields is an estimate +of the amount of time spent within calls to @code{a} from @code{main}. + +@item called +Two numbers: the number of times @code{a} was called from @code{main}, +followed by the total number of nonrecursive calls to @code{a} from +all its callers. + +@item name and index number +The name of the caller of @code{a} to which this line applies, +followed by the caller's index number. + +Not all functions have entries in the call graph; some +options to @code{gprof} request the omission of certain functions. +When a caller has no entry of its own, it still has caller-lines +in the entries of the functions it calls. Since this caller +has no index number, the string @samp{[not printed]} is used +instead of one. + +If the caller is part of a recursion cycle, the cycle number is +printed between the name and the index number. +@end table + +If the identity of the callers of a function cannot be determined, a +dummy caller-line is printed which has @samp{<spontaneous>} as the +``caller's name'' and all other fields blank. This can happen for +signal handlers. +@c What if some calls have determinable callers' names but not all? + +@node Subroutines, Cycles, Callers, Call Graph +@section Lines for a Function's Subroutines + +A function's entry has a line for each of its subroutines---in other +words, a line for each other function that it called. These lines' +fields correspond to the fields of the primary line, but their meanings +are different because of the difference in context. + +For reference, we repeat two lines from the entry for the function +@code{main}, the primary line and a line for a subroutine, together +with the heading line that shows the names of the fields: + +@example +index % time self children called name +@dots{} +[2] 100.00 0.04 0.19 1 main [2] + 0.19 0 1/1 a [3] +@end example + +Here are the meanings of the fields in the subroutine-line for @code{main} +calling @code{a}: + +@table @code +@item self +An estimate of the amount of time spent directly within @code{a} +when @code{a} was called from @code{main}. + +@item children +An estimate of the amount of time spent in subroutines of @code{a} +when @code{a} was called from @code{main}. + +The sum of the @code{self} and @code{children} fields is an estimate +of the total time spent in calls to @code{a} from @code{main}. + +@item called +Two numbers, the number of calls to @code{a} from @code{main} +followed by the total number of nonrecursive calls to @code{a}. + +@item name +The name of the subroutine of @code{a} to which this line applies, +followed by the subroutine's index number. If the subroutine is +a function omitted from the call graph, it has no index number, +so @samp{[not printed]} appears instead. + +If the caller is part of a recursion cycle, the cycle number is +printed between the name and the index number. +@end table + +@node Cycles,, Subroutines, Call Graph +@section How Mutually Recursive Functions Are Described +@cindex cycle +@cindex recursion cycle + +The graph may be complicated by the presence of @dfn{cycles of +recursion} in the call graph. A cycle exists if a function calls +another function that (directly or indirectly) calls (or appears to +call) the original function. For example: if @code{a} calls @code{b}, +and @code{b} calls @code{a}, then @code{a} and @code{b} form a cycle. + +Whenever there are call-paths both ways between a pair of functions, they +belong to the same cycle. If @code{a} and @code{b} call each other and +@code{b} and @code{c} call each other, all three make one cycle. Note that +even if @code{b} only calls @code{a} if it was not called from @code{a}, +@code{gprof} cannot determine this, so @code{a} and @code{b} are still +considered a cycle. + +The cycles are numbered with consecutive integers. When a function +belongs to a cycle, each time the function name appears in the call graph +it is followed by @samp{<cycle @var{number}>}. + +The reason cycles matter is that they make the time values in the call +graph paradoxical. The ``time spent in children'' of @code{a} should +include the time spent in its subroutine @code{b} and in @code{b}'s +subroutines---but one of @code{b}'s subroutines is @code{a}! How much of +@code{a}'s time should be included in the children of @code{a}, when +@code{a} is indirectly recursive? + +The way @code{gprof} resolves this paradox is by creating a single entry +for the cycle as a whole. The primary line of this entry describes the +total time spent directly in the functions of the cycle. The +``subroutines'' of the cycle are the individual functions of the cycle, and +all other functions that were called directly by them. The ``callers'' of +the cycle are the functions, outside the cycle, that called functions in +the cycle. + +Here is a portion of the call graph which shows a cycle containing +functions @code{a} and @code{b}. The cycle was entered by a call to +@code{a} from @code{main}; both @code{a} and @code{b} called @code{c}.@refill + +@example +index % time self children called name +---------------------------------------- + 1.77 0 1/1 main [2] +[3] 91.71 1.77 0 1+5 <cycle 1 as a whole> [3] + 1.02 0 3 b <cycle 1> [4] + 0.75 0 2 a <cycle 1> [5] +---------------------------------------- + 3 a <cycle 1> [5] +[4] 52.85 1.02 0 0 b <cycle 1> [4] + 2 a <cycle 1> [5] + 0 0 3/6 c [6] +---------------------------------------- + 1.77 0 1/1 main [2] + 2 b <cycle 1> [4] +[5] 38.86 0.75 0 1 a <cycle 1> [5] + 3 b <cycle 1> [4] + 0 0 3/6 c [6] +---------------------------------------- +@end example + +@noindent +(The entire call graph for this program contains in addition an entry for +@code{main}, which calls @code{a}, and an entry for @code{c}, with callers +@code{a} and @code{b}.) + +@example +index % time self children called name + <spontaneous> +[1] 100.00 0 1.93 0 start [1] + 0.16 1.77 1/1 main [2] +---------------------------------------- + 0.16 1.77 1/1 start [1] +[2] 100.00 0.16 1.77 1 main [2] + 1.77 0 1/1 a <cycle 1> [5] +---------------------------------------- + 1.77 0 1/1 main [2] +[3] 91.71 1.77 0 1+5 <cycle 1 as a whole> [3] + 1.02 0 3 b <cycle 1> [4] + 0.75 0 2 a <cycle 1> [5] + 0 0 6/6 c [6] +---------------------------------------- + 3 a <cycle 1> [5] +[4] 52.85 1.02 0 0 b <cycle 1> [4] + 2 a <cycle 1> [5] + 0 0 3/6 c [6] +---------------------------------------- + 1.77 0 1/1 main [2] + 2 b <cycle 1> [4] +[5] 38.86 0.75 0 1 a <cycle 1> [5] + 3 b <cycle 1> [4] + 0 0 3/6 c [6] +---------------------------------------- + 0 0 3/6 b <cycle 1> [4] + 0 0 3/6 a <cycle 1> [5] +[6] 0.00 0 0 6 c [6] +---------------------------------------- +@end example + +The @code{self} field of the cycle's primary line is the total time +spent in all the functions of the cycle. It equals the sum of the +@code{self} fields for the individual functions in the cycle, found +in the entry in the subroutine lines for these functions. + +The @code{children} fields of the cycle's primary line and subroutine lines +count only subroutines outside the cycle. Even though @code{a} calls +@code{b}, the time spent in those calls to @code{b} is not counted in +@code{a}'s @code{children} time. Thus, we do not encounter the problem of +what to do when the time in those calls to @code{b} includes indirect +recursive calls back to @code{a}. + +The @code{children} field of a caller-line in the cycle's entry estimates +the amount of time spent @emph{in the whole cycle}, and its other +subroutines, on the times when that caller called a function in the cycle. + +The @code{calls} field in the primary line for the cycle has two numbers: +first, the number of times functions in the cycle were called by functions +outside the cycle; second, the number of times they were called by +functions in the cycle (including times when a function in the cycle calls +itself). This is a generalization of the usual split into nonrecursive and +recursive calls. + +The @code{calls} field of a subroutine-line for a cycle member in the +cycle's entry says how many time that function was called from functions in +the cycle. The total of all these is the second number in the primary line's +@code{calls} field. + +In the individual entry for a function in a cycle, the other functions in +the same cycle can appear as subroutines and as callers. These lines show +how many times each function in the cycle called or was called from each other +function in the cycle. The @code{self} and @code{children} fields in these +lines are blank because of the difficulty of defining meanings for them +when recursion is going on. + +@node Implementation, Sampling Error, Call Graph, Top +@chapter Implementation of Profiling + +Profiling works by changing how every function in your program is compiled +so that when it is called, it will stash away some information about where +it was called from. From this, the profiler can figure out what function +called it, and can count how many times it was called. This change is made +by the compiler when your program is compiled with the @samp{-pg} option. + +Profiling also involves watching your program as it runs, and keeping a +histogram of where the program counter happens to be every now and then. +Typically the program counter is looked at around 100 times per second of +run time, but the exact frequency may vary from system to system. + +A special startup routine allocates memory for the histogram and sets up a +clock signal handler to make entries in it. Use of this special startup +routine is one of the effects of using @samp{cc -pg} to link. The startup +file also includes an @code{exit} function which is responsible for writing +the file @file{gmon.out}. + +Number-of-calls information for library routines is collected by using a +special version of the C library. The programs in it are the same as in +the usual C library, but they were compiled with @samp{-pg}. If you link +your program with @samp{cc -pg}, it automatically uses the profiling +version of the library. + +The output from @code{gprof} gives no indication of parts of your program that +are limited by I/O or swapping bandwidth. This is because samples of the +program counter are taken at fixed intervals of run time. Therefore, the +time measurements in @code{gprof} output say nothing about time that your +program was not running. For example, a part of the program that creates +so much data that it cannot all fit in physical memory at once may run very +slowly due to thrashing, but @code{gprof} will say it uses little time. On +the other hand, sampling by run time has the advantage that the amount of +load due to other users won't directly affect the output you get. + +@node Sampling Error, Assumptions, Implementation, Top +@chapter Statistical Inaccuracy of @code{gprof} Output + +The run-time figures that @code{gprof} gives you are based on a sampling +process, so they are subject to statistical inaccuracy. If a function runs +only a small amount of time, so that on the average the sampling process +ought to catch that function in the act only once, there is a pretty good +chance it will actually find that function zero times, or twice. + +By contrast, the number-of-calls figures are derived by counting, not +sampling. They are completely accurate and will not vary from run to run +if your program is deterministic. + +The @dfn{sampling period} that is printed at the beginning of the flat +profile says how often samples are taken. The rule of thumb is that a +run-time figure is accurate if it is considerably bigger than the sampling +period. + +The actual amount of error is usually more than one sampling period. In +fact, if a value is @var{n} times the sampling period, the @emph{expected} +error in it is the square-root of @var{n} sampling periods. If the +sampling period is 0.01 seconds and @code{foo}'s run-time is 1 second, the +expected error in @code{foo}'s run-time is 0.1 seconds. It is likely to +vary this much @emph{on the average} from one profiling run to the next. +(@emph{Sometimes} it will vary more.) + +This does not mean that a small run-time figure is devoid of information. +If the program's @emph{total} run-time is large, a small run-time for one +function does tell you that that function used an insignificant fraction of +the whole program's time. Usually this means it is not worth optimizing. + +One way to get more accuracy is to give your program more (but similar) +input data so it will take longer. Another way is to combine the data from +several runs, using the @samp{-s} option of @code{gprof}. Here is how: + +@enumerate +@item +Run your program once. + +@item +Issue the command @samp{mv gmon.out gmon.sum}. + +@item +Run your program again, the same as before. + +@item +Merge the new data in @file{gmon.out} into @file{gmon.sum} with this command: + +@example +gprof -s @var{executable-file} gmon.out gmon.sum +@end example + +@item +Repeat the last two steps as often as you wish. + +@item +Analyze the cumulative data using this command: + +@example +gprof @var{executable-file} gmon.sum > @var{output-file} +@end example +@end enumerate + +@node Assumptions, Incompatibilities, Sampling Error, Top +@chapter Estimating @code{children} Times Uses an Assumption + +Some of the figures in the call graph are estimates---for example, the +@code{children} time values and all the the time figures in caller and +subroutine lines. + +There is no direct information about these measurements in the profile +data itself. Instead, @code{gprof} estimates them by making an assumption +about your program that might or might not be true. + +The assumption made is that the average time spent in each call to any +function @code{foo} is not correlated with who called @code{foo}. If +@code{foo} used 5 seconds in all, and 2/5 of the calls to @code{foo} came +from @code{a}, then @code{foo} contributes 2 seconds to @code{a}'s +@code{children} time, by assumption. + +This assumption is usually true enough, but for some programs it is far +from true. Suppose that @code{foo} returns very quickly when its argument +is zero; suppose that @code{a} always passes zero as an argument, while +other callers of @code{foo} pass other arguments. In this program, all the +time spent in @code{foo} is in the calls from callers other than @code{a}. +But @code{gprof} has no way of knowing this; it will blindly and +incorrectly charge 2 seconds of time in @code{foo} to the children of +@code{a}. + +We hope some day to put more complete data into @file{gmon.out}, so that +this assumption is no longer needed, if we can figure out how. For the +nonce, the estimated figures are usually more useful than misleading. + +@node Incompatibilities, , Assumptions, Top +@chapter Incompatibilities with Unix @code{gprof} + +GNU @code{gprof} and Berkeley Unix @code{gprof} use the same data file +@file{gmon.out}, and provide essentially the same information. But there a +few differences.@refill + +GNU @code{gprof} does not support the @samp{-c} option which prints a +static call graph based on reading the machine language of your +program. We think that program cross-references ought to be based on +the source files, which can be analyzed in a machine-independent +fashion.@refill + +For a recursive function, Unix @code{gprof} lists the function as a parent +and as a child, with a @code{calls} field that lists the number of +recursive calls. GNU @code{gprof} omits these lines and puts the number of +recursive calls in the primary line. + +When a function is suppressed from the call graph with @samp{-e}, GNU +@code{gprof} still lists it as a subroutine of functions that call it. + +The function names printed in GNU @code{gprof} output do not include +the leading underscores that are added internally to the front of all +C identifiers on many operating systems. + +The blurbs, field widths, and output formats are different. GNU +@code{gprof} prints blurbs after the tables, so that you can see the +tables without skipping the blurbs. + +@contents +@bye diff --git a/binutils-1.9/hp-bin/Makefile b/binutils-1.9/hp-bin/Makefile new file mode 100644 index 0000000..8ca44f1 --- /dev/null +++ b/binutils-1.9/hp-bin/Makefile @@ -0,0 +1,15 @@ +CFLAGS = -O -I../hp-include +EXECUTABLES = hpxt chatr + +all : $(EXECUTABLES) + +clean : + rm -f *.o $(EXECUTABLES) + +hpxt : hpxt.o ioutil.o + $(CC) $(CFLAGS) -o hpxt hpxt.o ioutil.o + +chatr : chatr.o ioutil.o + $(CC) $(CFLAGS) -o chatr chatr.o ioutil.o + +hpxt.o chatr.o : ioutil.h diff --git a/binutils-1.9/hp-bin/chatr.c b/binutils-1.9/hp-bin/chatr.c new file mode 100644 index 0000000..1d7cbad --- /dev/null +++ b/binutils-1.9/hp-bin/chatr.c @@ -0,0 +1,214 @@ +/* Change Attributes program for GNU. + Copyright (C) 1988 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 <a.out.h> +#include <stdio.h> +#include <fcntl.h> +#include <sys/file.h> +#include "ioutil.h" + +#define forward extern + +enum boolean { false, true }; +typedef enum boolean boolean; + +boolean change_to_shared_p; +boolean change_to_demand_loaded_p; +boolean silent_p; +int failures; + +char *input_filename; +char output_filename [20]; +int output_descriptor; + +void +main (argc, argv) + int argc; + char *argv[]; +{ + register int argi; + register char **argp; + forward void usage (); + forward void process_file (); + forward void file_abort_handler (); + extern char *mktemp (); + + iou_set_program_name (argv); + argi = 1; + argp = (& (argv [1])); + + change_to_shared_p = false; + change_to_demand_loaded_p = false; + silent_p = false; + + while ((argi < argc) && (((*argp) [0]) == '-')) + { + switch ((*argp) [1]) + { + case 'n': + change_to_shared_p = true; + break; + case 'q': + change_to_demand_loaded_p = true; + break; + case 's': + silent_p = true; + break; + default: + usage (); + } + if (((*argp) [2]) != '\0') + usage (); + argi += 1; + argp += 1; + } + + if (change_to_shared_p && change_to_demand_loaded_p) + iou_error ("conflicting options: -n and -q"); + + if ((! change_to_shared_p) && (! change_to_demand_loaded_p) && silent_p) + exit (0); + + if (argi == argc) + exit (255); + + strcpy (output_filename, "/tmp/chatrXXXXXX"); + if (output_filename != (mktemp (output_filename))) + iou_error ("mktemp failure"); + + failures = 0; + for (; (argi < argc); argi += 1) + { + input_filename = (*argp++); + output_descriptor = + (iou_open (output_filename, (O_RDWR | O_CREAT | O_TRUNC), 0666)); + iou_abort_handler_bind (process_file, file_abort_handler); + iou_close (output_descriptor); + } + iou_unlink (output_filename); + exit (failures); +} + +void +usage () +{ + fprintf (stderr, "usage: %s [-n] [-q] [-s] file ...\n", iou_program_name); + iou_error (); +} + +void +file_abort_handler () +{ + failures += 1; + return; +} + +void +file_error (message) + char *message; +{ + char buffer [256]; + + sprintf (buffer, "%s: \"%s\"", message, input_filename); + iou_error (buffer); +} + +void +file_copy (input_descriptor, output_descriptor) + int input_descriptor; + int output_descriptor; +{ + char buffer [8192]; + register int bytes_read; + + while (1) + { + bytes_read = (iou_read (input_descriptor, buffer, 8192)); + if (bytes_read == 0) break; + iou_write (output_descriptor, buffer, bytes_read); + } + return; +} + +void +process_file () +{ + int input_descriptor; + struct exec input_exec; + struct exec output_exec; + + input_descriptor = (iou_open (input_filename, O_RDONLY)); + + if ((iou_read (input_descriptor, (& input_exec), (sizeof (input_exec)))) != + (sizeof (input_exec))) + file_error ("unable to read file header"); + + switch (N_MAGIC (input_exec)) + { + case NMAGIC: + if (change_to_shared_p) + file_error ("file not demand load executable"); + break; + + case ZMAGIC: + if (change_to_demand_loaded_p) + file_error ("file not shared executable"); + break; + + default: + file_error ("file not executable format"); + } + + if (! silent_p) + { + printf ("%s:\n", input_filename); + if (change_to_shared_p || change_to_demand_loaded_p) + printf (" current values:\n"); + printf + (" %s executable\n", + (((N_MAGIC (input_exec)) == NMAGIC) ? "shared" : "demand loaded")); + fflush (stdout); + } + + if ((! change_to_shared_p) && (! change_to_demand_loaded_p)) + return; + + output_exec = input_exec; + N_SET_MAGIC (output_exec, (change_to_shared_p ? NMAGIC : ZMAGIC)); + + iou_write (output_descriptor, (& output_exec), (sizeof (output_exec))); + iou_lseek (input_descriptor, (N_TXTOFF (input_exec)), 0); + iou_lseek (output_descriptor, (N_TXTOFF (output_exec)), 0); + file_copy (input_descriptor, output_descriptor); + + /* Now copy the temporary output file back into the input file. */ + iou_close (input_descriptor); + iou_lseek (output_descriptor, 0, 0); + input_descriptor = (iou_open (input_filename, (O_WRONLY | O_TRUNC), 0777)); + file_copy (output_descriptor, input_descriptor); + iou_close (input_descriptor); + + if (! silent_p) + { + printf (" new values:\n"); + printf + (" %s executable\n", + (((N_MAGIC (output_exec)) == NMAGIC) ? "shared" : "demand loaded")); + fflush (stdout); + } + return; +} diff --git a/binutils-1.9/hp-bin/hpxt.c b/binutils-1.9/hp-bin/hpxt.c new file mode 100644 index 0000000..a07188a --- /dev/null +++ b/binutils-1.9/hp-bin/hpxt.c @@ -0,0 +1,1077 @@ +/* Program to translate binary format files from HP-UX to GNU. + Input formats for HP 9000 series 300/400 only. + Copyright (C) 1988 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. */ + +#if !defined(hp9000s300) && !defined(__hp9000s300) +#include "error: hpxt is for 300- or 400-series machines only" +#endif + +#include <stdio.h> +#include <varargs.h> +#include <fcntl.h> +#include <sys/file.h> +#include <string.h> +#define index(s, c) strchr (s, c) +#define bzero(p, n) memset (p, 0, n) +#include "ioutil.h" + +#define forward extern + +/* IO primitives */ + +void +xread (input_file, buffer, length) + int input_file; + char *buffer; + int length; +{ + if ((iou_read (input_file, buffer, length)) != length) + iou_error ("read failed to complete"); + return; +} + +/* HP-UX data structure descriptions */ + +struct hp_magic + { + unsigned short system_id; + unsigned short file_type; + }; + +#define HP_SYSID_98x6 0x020A +#define HP_SYSID_9000S200 0x020C + +#define HP_SYSID_OK(sysid) \ + (((sysid) == HP_SYSID_9000S200) \ + || ((sysid) == HP_SYSID_98x6)) + +#define HP_FILETYPE_RELOC 0x0106 /* relocatable only */ +#define HP_FILETYPE_EXEC 0x0107 /* normal executable */ +#define HP_FILETYPE_SHARE 0x0108 /* shared executable */ +#define HP_FILETYPE_DEMAND 0x010B /* demand-load executable */ + +#define HP_FILETYPE_OK(filetype) \ + (((filetype) == HP_FILETYPE_EXEC) \ + || ((filetype) == HP_FILETYPE_SHARE) \ + || ((filetype) == HP_FILETYPE_DEMAND) \ + || ((filetype) == HP_FILETYPE_RELOC)) + +#define HP_MAGIC_OK(magic) \ + ((HP_SYSID_OK ((magic) . system_id)) \ + && (HP_FILETYPE_OK ((magic) . file_type))) + +struct hp_exec + { + struct hp_magic a_magic; + short a_stamp; /* version id */ + short a_unused; + long a_sparehp; + long a_text; /* size of text segment */ + long a_data; /* size of data segment */ + long a_bss; /* size of bss segment */ + long a_trsize; /* text relocation size */ + long a_drsize; /* data relocation size */ + long a_pasint; /* pascal interface size */ + long a_lesyms; /* symbol table size */ + long a_dnttsize; /* debug name table size */ + long a_entry; /* entry point */ + long a_sltsize; /* source line table size */ + long a_vtsize; /* value table size */ + long a_spare3; + long a_spare4; + }; + +#define HP_EXEC_MAGIC(header) (((header) . a_magic) . file_type) + +#define HP_PAGE_SIZE 0x1000 + +#define HP_PAGE_SEEK(descriptor, header, offset) \ +{ \ + if ((HP_EXEC_MAGIC (header)) == HP_FILETYPE_DEMAND) \ + { \ + int HP_PAGE_SEEK_remainder; \ + \ + HP_PAGE_SEEK_remainder = ((offset) % HP_PAGE_SIZE); \ + if (HP_PAGE_SEEK_remainder != 0) \ + iou_lseek \ + ((descriptor), (HP_PAGE_SIZE - HP_PAGE_SEEK_remainder), 1); \ + } \ +} + +struct hp_nlist + { + long n_value; + unsigned char n_type; + unsigned char n_length; /* length of ascii symbol name */ + short n_almod; /* alignment mod */ + short n_unused; + }; + +#define HP_SYMTYPE_UNDEFINED 0x00 +#define HP_SYMTYPE_ABSOLUTE 0x01 +#define HP_SYMTYPE_TEXT 0x02 +#define HP_SYMTYPE_DATA 0x03 +#define HP_SYMTYPE_BSS 0x04 +#define HP_SYMTYPE_COMMON 0x05 +#define HP_SYMTYPE_FILENAME 0x1F + +#define HP_SYMTYPE_ALIGN 0x10 +#define HP_SYMTYPE_EXTERNAL 0x20 + +#define HP_SYMTYPE_TYPE 0x0F + +struct hp_relocation_info + { + long r_address; /* position of relocation in segment */ + short r_symbolnum; /* id of the symbol of external relocations */ + char r_segment; + char r_length; + }; + +#define HP_RSEGMENT_TEXT 0x00 +#define HP_RSEGMENT_DATA 0x01 +#define HP_RSEGMENT_BSS 0x02 +#define HP_RSEGMENT_EXTERNAL 0x03 +#define HP_RSEGMENT_NOOP 0x3F + +#define HP_RLENGTH_BYTE 0x00 +#define HP_RLENGTH_WORD 0x01 +#define HP_RLENGTH_LONG 0x02 +#define HP_RLENGTH_ALIGN 0x03 + +#define HP_ARMAG "!<arch>\n" +#define HP_SARMAG 8 +#define HP_ARFMAG "`\n" + +struct hp_ar_hdr + { + char ar_name [16]; /* file member name - `/' terminated */ + char ar_date [12]; /* file member date - decimal */ + char ar_uid [6]; /* file member user id - decimal */ + char ar_gid [6]; /* file member group id - decimal */ + char ar_mode [8]; /* file member mode - octal */ + char ar_size [10]; /* file member size - decimal */ + char ar_fmag [2]; /* ARFMAG - string to end header */ + }; + +struct hp_ar_symtab_hdr + { + unsigned short n_entries; + long string_table_size; + }; + +struct hp_ar_symtab_entry + { + long string_index; + long file_index; + }; + +/* GNU data structure descriptions */ + +#ifdef hpux + +/* The `exec' structure and overall layout must be close to HP's when + we are running on an HP system, otherwise we will not be able to + execute the resulting file. */ + +#define GNU_OMAGIC HP_FILETYPE_EXEC +#define GNU_NMAGIC HP_FILETYPE_SHARE +#define GNU_ZMAGIC HP_FILETYPE_SHARE + +struct gnu_exec + { + struct hp_magic a_magic; + unsigned long a_spare1; + unsigned long a_spare2; + unsigned long a_text; /* size of text segment */ + unsigned long a_data; /* size of data segment */ + unsigned long a_bss; /* size of bss segment */ + unsigned long a_trsize; /* text relocation size */ + unsigned long a_drsize; /* data relocation size */ + unsigned long a_spare3; /* HP = pascal interface size */ + unsigned long a_spare4; /* HP = symbol table size */ + unsigned long a_spare5; /* HP = debug name table size */ + unsigned long a_entry; /* entry point */ + unsigned long a_spare6; /* HP = source line table size */ + unsigned long a_spare7; /* HP = value table size */ + unsigned long a_syms; /* symbol table size */ + unsigned long a_spare8; + }; + +#define GNU_EXEC_MAGIC(header) (((header) . a_magic) . file_type) + +#define GNU_EXEC_SET_MAGIC(header, magic) \ +{ \ + (((header) . a_magic) . system_id) = HP_SYSID_9000S200; \ + (((header) . a_magic) . file_type) = (magic); \ +} + +#define GNU_PAGE_SEEK_HEADER HP_PAGE_SEEK +#define GNU_PAGE_SEEK_TEXT HP_PAGE_SEEK + +#else /* not hpux */ + +#define GNU_OMAGIC 0407 /* old impure format */ +#define GNU_NMAGIC 0410 /* read-only text */ +#define GNU_ZMAGIC 0413 /* demand load format */ + +struct gnu_exec + { + unsigned long a_magic; /* magic number */ + unsigned long a_text; /* size of text segment */ + unsigned long a_data; /* size of initialized data */ + unsigned long a_bss; /* size of uninitialized data */ + unsigned long a_syms; /* size of symbol table */ + unsigned long a_entry; /* entry point */ + unsigned long a_trsize; /* size of text relocation */ + unsigned long a_drsize; /* size of data relocation */ + }; + +#define GNU_EXEC_MAGIC(header) ((header) . a_magic) +#define GNU_EXEC_SET_MAGIC(header, magic) ((header) . a_magic) = (magic) + +#define GNU_PAGE_SIZE 0x400 + +#define GNU_PAGE_SEEK_HEADER(descriptor, header, offset) \ +{ \ + if ((GNU_EXEC_MAGIC (header)) == GNU_ZMAGIC) \ + { \ + int GNU_PAGE_SEEK_remainder; \ + \ + GNU_PAGE_SEEK_remainder = ((offset) % GNU_PAGE_SIZE); \ + if (GNU_PAGE_SEEK_remainder != 0) \ + iou_lseek \ + ((descriptor), (GNU_PAGE_SIZE - GNU_PAGE_SEEK_remainder), 1); \ + } \ +} + +#define GNU_PAGE_SEEK_TEXT(descriptor, header, offset) + +#endif /* hpux */ + +struct gnu_nlist + { + union + { + char *n_name; /* for use when in-core */ + long n_strx; /* index into file string table */ + } n_un; + unsigned char n_type; /* type flag (N_TEXT,..) */ + char n_other; /* unused */ + short n_desc; /* see <stab.h> */ + unsigned long n_value; /* value of symbol (or sdb offset) */ + }; + +#define GNU_SYMTYPE_UNDEFINED 0x0 +#define GNU_SYMTYPE_ABSOLUTE 0x2 +#define GNU_SYMTYPE_TEXT 0x4 +#define GNU_SYMTYPE_DATA 0x6 +#define GNU_SYMTYPE_BSS 0x8 +#define GNU_SYMTYPE_COMMON 0x12 +#define GNU_SYMTYPE_FILENAME 0x1F +#define GNU_SYMTYPE_EXTERNAL 0x01 + +struct gnu_relocation_info + { + long r_address; /* address which is relocated */ + unsigned int + r_symbolnum : 24, /* local symbol ordinal */ + r_pcrel : 1, /* was relocated pc relative already */ + r_length : 2, /* BYTE, WORD, or LONG */ + r_extern : 1, /* does not include value of sym referenced */ + : 4; /* nothing, yet */ + }; + +#define GNU_RLENGTH_BYTE 0x00 +#define GNU_RLENGTH_WORD 0x01 +#define GNU_RLENGTH_LONG 0x02 + +#define GNU_ARMAG "!<arch>\n" +#define GNU_SARMAG 8 +#define GNU_ARFMAG "`\n" + +struct gnu_ar_hdr + { + char ar_name [16]; + char ar_date [12]; + char ar_uid [6]; + char ar_gid [6]; + char ar_mode [8]; + char ar_size [10]; + char ar_fmag [2]; + }; + +struct gnu_symdef + { + int symbol_name_string_index; + int library_member_offset; + }; + +/* List abstraction */ + +struct pair + { + char *car; + char *cdr; + }; + +struct pair * +pair_cons (car, cdr) + char *car; + char *cdr; +{ + register struct pair *result; + + result = ((struct pair *) (iou_malloc (sizeof (struct pair)))); + (result -> car) = car; + (result -> cdr) = cdr; + return (result); +} + +#define LIST_CDR(list) ((struct pair *) ((list) -> cdr)) +#define LIST_SET_CDR(list, newcdr) (list -> cdr) = ((char *) newcdr) + +struct pair * +list_reverse (list) + register struct pair *list; +{ + register struct pair *previous; + register struct pair *next; + + previous = NULL; + while (list != NULL) + { + next = (LIST_CDR (list)); + LIST_SET_CDR (list, previous); + previous = list; + list = next; + } + return (previous); +} + +struct pair * +read_list (input_file, n_bytes, field_name, read_item, length) + int input_file; + int n_bytes; + char * field_name; + char * (* read_item) (); + register int * length; /* return value */ +{ + register struct pair *result; + + result = NULL; + (* length) = 0; + while (n_bytes > 0) + { + result = (pair_cons (((* read_item) (input_file, (& n_bytes))), result)); + (* length) += 1; + } + if (n_bytes != 0) + iou_error ("%s field size incorrect", field_name); + return (list_reverse (result)); +} + +/* Symbol Table */ + +struct gnu_nlist * +read_symbol (input_file, n_bytes) + int input_file; + int *n_bytes; /* return value */ +{ + struct hp_nlist input_nlist; + register struct gnu_nlist *output_nlist; + register char *name; + + { + register int name_length; + + xread (input_file, (& input_nlist), (sizeof (input_nlist))); + name_length = (input_nlist . n_length); + name = (iou_malloc (name_length + 1)); + xread (input_file, name, name_length); + (name [name_length]) = '\0'; + (* n_bytes) -= ((sizeof (input_nlist)) + name_length); + } + + output_nlist = + ((struct gnu_nlist *) (iou_malloc (sizeof (struct gnu_nlist)))); + ((output_nlist -> n_un) . n_name) = name; + (output_nlist -> n_other) = 0; + (output_nlist -> n_desc) = 0; + (output_nlist -> n_value) = (input_nlist . n_value); + + { + register int name_type; + + name_type = (input_nlist . n_type); + if ((name_type & HP_SYMTYPE_ALIGN) != 0) + iou_error ("aligned symbol encountered: %s", name); + if (name_type == HP_SYMTYPE_FILENAME) + (output_nlist -> n_type) = GNU_SYMTYPE_FILENAME; + else + { + switch (name_type & HP_SYMTYPE_TYPE) + { + case HP_SYMTYPE_UNDEFINED: + (output_nlist -> n_type) = GNU_SYMTYPE_UNDEFINED; + break; + + case HP_SYMTYPE_ABSOLUTE: + (output_nlist -> n_type) = GNU_SYMTYPE_ABSOLUTE; + break; + + case HP_SYMTYPE_TEXT: + (output_nlist -> n_type) = GNU_SYMTYPE_TEXT; + break; + + case HP_SYMTYPE_DATA: + (output_nlist -> n_type) = GNU_SYMTYPE_DATA; + break; + + case HP_SYMTYPE_BSS: + (output_nlist -> n_type) = GNU_SYMTYPE_BSS; + break; + + case HP_SYMTYPE_COMMON: + (output_nlist -> n_type) = GNU_SYMTYPE_COMMON; + break; + + default: + iou_error ("unknown symbol type encountered: %x", name_type); + } + if ((name_type & HP_SYMTYPE_EXTERNAL) != 0) + (output_nlist -> n_type) |= GNU_SYMTYPE_EXTERNAL; + } + } + + return (output_nlist); +} + +struct pair * +read_symbol_table (input_file, n_bytes, length) + int input_file; + int n_bytes; + int * length; /* return value */ +{ + return + (read_list (input_file, n_bytes, "symbol table", read_symbol, length)); +} + +void +write_symbol_table (output_file, symbol_table, length) + int output_file; + struct pair *symbol_table; + int length; +{ + register struct pair *scan; + register char *name; + struct gnu_nlist output_nlist; + int string_table_index; + + (output_nlist . n_other) = 0; + (output_nlist . n_desc) = 0; + + string_table_index = (sizeof (string_table_index)); + for (scan = symbol_table; (scan != NULL); scan = (LIST_CDR (scan))) + { + register struct gnu_nlist *symbol; + + symbol = ((struct gnu_nlist *) (scan -> car)); + name = ((symbol -> n_un) . n_name); + + ((output_nlist . n_un) . n_strx) = string_table_index; + (output_nlist . n_type) = (symbol -> n_type); + (output_nlist . n_value) = (symbol -> n_value); + iou_write (output_file, (& output_nlist), (sizeof (output_nlist))); + + string_table_index += ((strlen (name)) + 1); + (scan -> car) = name; + iou_free (symbol); + } + iou_write + (output_file, (& string_table_index), (sizeof (string_table_index))); + + scan = symbol_table; + while (scan != NULL) + { + register struct pair *cdr; + + name = (scan -> car); + iou_write (output_file, name, ((strlen (name)) + 1)); + iou_free (name); + cdr = (LIST_CDR (scan)); + iou_free (scan); + scan = cdr; + } + return; +} + +/* Relocation Info */ + +struct gnu_relocation_info * +read_relocation_info_1 (input_file, n_bytes) + int input_file; + register int * n_bytes; /* return value */ +{ + struct hp_relocation_info input_info; + register struct gnu_relocation_info *output_info; + + xread (input_file, (& input_info), (sizeof (input_info))); + (* n_bytes) -= (sizeof (input_info)); + + output_info = + ((struct gnu_relocation_info *) + (iou_malloc (sizeof (struct gnu_relocation_info)))); + (output_info -> r_pcrel) = 0; + (output_info -> r_address) = (input_info . r_address); + + switch (input_info . r_segment) + { + case HP_RSEGMENT_TEXT: + (output_info -> r_symbolnum) = GNU_SYMTYPE_TEXT; + (output_info -> r_extern) = 0; + break; + + case HP_RSEGMENT_DATA: + (output_info -> r_symbolnum) = GNU_SYMTYPE_DATA; + (output_info -> r_extern) = 0; + break; + + case HP_RSEGMENT_BSS: + (output_info -> r_symbolnum) = GNU_SYMTYPE_BSS; + (output_info -> r_extern) = 0; + break; + + case HP_RSEGMENT_EXTERNAL: + (output_info -> r_symbolnum) = (input_info . r_symbolnum); + (output_info -> r_extern) = 1; + break; + + default: + iou_error + ("illegal relocation segment type: %x", (input_info . r_segment)); + } + + switch (input_info . r_length) + { + case HP_RLENGTH_BYTE: + (output_info -> r_length) = GNU_RLENGTH_BYTE; + break; + + case HP_RLENGTH_WORD: + (output_info -> r_length) = GNU_RLENGTH_WORD; + break; + + case HP_RLENGTH_LONG: + (output_info -> r_length) = GNU_RLENGTH_LONG; + break; + + default: + iou_error ("illegal relocation length: %x", (input_info . r_length)); + } + + return (output_info); +} + +struct pair * +read_relocation_info (input_file, n_bytes, length) + int input_file; + int n_bytes; + int * length; +{ + return + (read_list + (input_file, n_bytes, "relocation info", read_relocation_info_1, length)); +} + +void +write_relocation_info (output_file, relocation_info, length) + int output_file; + struct pair *relocation_info; + int length; +{ + register struct pair *this; + register struct gnu_relocation_info *car; + register struct pair *cdr; + + this = relocation_info; + while (this != NULL) + { + car = ((struct gnu_relocation_info *) (this -> car)); + iou_write (output_file, car, (sizeof (struct gnu_relocation_info))); + iou_free (car); + cdr = (LIST_CDR (this)); + iou_free (this); + this = cdr; + } + return; +} + +/* Convert an executable (or relocatable) file. */ + +void +process_executable_file (input_file, output_file) + int input_file; + int output_file; +{ + struct hp_exec input_exec; + struct gnu_exec output_exec; + char *text_segment; + char *data_segment; + struct pair *symbol_table; + int symbol_table_length; + struct pair *text_relocation_info; + int text_relocation_info_length; + struct pair *data_relocation_info; + int data_relocation_info_length; + + xread (input_file, (& input_exec), (sizeof (input_exec))); + HP_PAGE_SEEK (input_file, input_exec, (sizeof (input_exec))); + + if ((input_exec . a_text) != 0) + { + text_segment = (iou_malloc (input_exec . a_text)); + xread (input_file, text_segment, (input_exec . a_text)); + HP_PAGE_SEEK (input_file, input_exec, (input_exec . a_text)); + } + else + text_segment = NULL; + + if ((input_exec . a_data) != 0) + { + data_segment = (iou_malloc (input_exec . a_data)); + xread (input_file, data_segment, (input_exec . a_data)); + HP_PAGE_SEEK (input_file, input_exec, (input_exec . a_data)); + } + else + data_segment = NULL; + + /* Skip the pascal interface text */ + iou_lseek (input_file, (input_exec . a_pasint), 1); + + symbol_table = + (read_symbol_table + (input_file, (input_exec . a_lesyms), (& symbol_table_length))); + + /* Skip the debugging tables */ + iou_lseek + (input_file, + ((input_exec . a_dnttsize) + + (input_exec . a_sltsize) + + (input_exec . a_vtsize)), + 1); + + text_relocation_info = + (read_relocation_info + (input_file, (input_exec . a_trsize), (& text_relocation_info_length))); + data_relocation_info = + (read_relocation_info + (input_file, (input_exec . a_drsize), (& data_relocation_info_length))); + + bzero ((& output_exec), (sizeof (output_exec))); + switch (HP_EXEC_MAGIC (input_exec)) + { + case HP_FILETYPE_RELOC: + case HP_FILETYPE_EXEC: + GNU_EXEC_SET_MAGIC (output_exec, GNU_OMAGIC); + break; + + case HP_FILETYPE_SHARE: + GNU_EXEC_SET_MAGIC (output_exec, GNU_NMAGIC); + break; + + case HP_FILETYPE_DEMAND: + GNU_EXEC_SET_MAGIC (output_exec, GNU_ZMAGIC); + break; + + default: + iou_error ("unknown magic type: %x", (HP_EXEC_MAGIC (input_exec))); + } + (output_exec . a_text) = (input_exec . a_text); + (output_exec . a_data) = (input_exec . a_data); + (output_exec . a_bss) = (input_exec . a_bss); + (output_exec . a_syms) = (symbol_table_length * (sizeof (struct gnu_nlist))); + (output_exec . a_entry) = (input_exec . a_entry); + (output_exec . a_trsize) = (input_exec . a_trsize); + (output_exec . a_drsize) = (input_exec . a_drsize); + + iou_write (output_file, (& output_exec), (sizeof (output_exec))); + GNU_PAGE_SEEK_HEADER (output_file, output_exec, (sizeof (output_exec))); + if (text_segment != NULL) + { + iou_write (output_file, text_segment, (output_exec . a_text)); + iou_free (text_segment); + GNU_PAGE_SEEK_TEXT (output_file, output_exec, (output_exec . a_text)); + } + if (data_segment != NULL) + { + iou_write (output_file, data_segment, (output_exec . a_data)); + iou_free (data_segment); + GNU_PAGE_SEEK_TEXT (output_file, output_exec, (output_exec . a_data)); + } + write_relocation_info + (output_file, text_relocation_info, text_relocation_info_length); + write_relocation_info + (output_file, data_relocation_info, data_relocation_info_length); + write_symbol_table (output_file, symbol_table, symbol_table_length); + + return; +} + +/* Convert an archive file. */ + +/* These procedures take advantage of the fact that the HP and GNU + formats for struct ar_hdr are virtually identical. */ + +long symtab_start_position; +char *symtab_member_data; +struct hp_ar_symtab_entry *hp_symtab_entries; +struct gnu_symdef *gnu_symtab_entries; +long symtab_entries_length; +long symtab_names_length; + +void +process_archive_file (input_file, output_file) + int input_file; + int output_file; +{ + int start_position; + int length; + struct hp_ar_hdr input_header; + int member_length; + forward void process_archive_symtab (); + forward void update_symtab_entries (); + forward void output_symtab_entries (); + forward void process_archive_entry (); + + gnu_symtab_entries = NULL; + while (1) + { + start_position = (iou_lseek (input_file, 0, 1)); + length = (read (input_file, (& input_header), (sizeof (input_header)))); + if (length == 0) + break; + if (length != (sizeof (input_header))) + iou_error ("input archive ends prematurely"); + if (((strncmp + ((input_header . ar_fmag), + HP_ARFMAG, + (sizeof (input_header . ar_fmag)))) + != 0) + || + ((sscanf ((input_header . ar_size), "%d", (& member_length))) + != 1)) + iou_error ("malformatted header of archive member"); + + if (((input_header . ar_name) [0]) == '/') + process_archive_symtab + (input_file, output_file, (& input_header), member_length); + else + { + if (gnu_symtab_entries != NULL) + update_symtab_entries + (start_position, (iou_lseek (output_file, 0, 1))); + process_archive_entry (input_file, output_file, (& input_header), + member_length); + } + } + if (gnu_symtab_entries != NULL) + output_symtab_entries (output_file, (& input_header)); + return; +} + +#ifdef DEBUG_SYMTAB + +void +dump_symtab_data () +{ + register struct hp_ar_symtab_entry *symtab_entries; + register char *symtab_names; + register int i; + + symtab_names = (symtab_member_data + (sizeof (struct hp_ar_symtab_hdr))); + symtab_entries = + ((struct hp_ar_symtab_entry *) (symtab_names + symtab_names_length)); + for (i = 0; (i < symtab_entries_length); i += 1) + { + fprintf + (stdout, + "Entry %d: string_index = %d; file_index = %d; name = %s\n", + i, + ((symtab_entries [i]) . string_index), + ((symtab_entries [i]) . file_index), + (symtab_names + ((symtab_entries [i]) . string_index))); + } + fflush (stdout); + return; +} + +#endif /* DEBUG_SYMTAB */ + +void +process_archive_symtab (input_file, output_file, input_header, member_length) + int input_file; + int output_file; + struct hp_ar_hdr *input_header; + int member_length; +{ + long symtab_entries_bytes; + void initialize_symtab_entries (); + + symtab_member_data = (iou_malloc (member_length)); + xread (input_file, symtab_member_data, member_length); + + symtab_entries_length = + (((struct hp_ar_symtab_hdr *) symtab_member_data) -> n_entries); + symtab_entries_bytes = + (symtab_entries_length * (sizeof (struct hp_ar_symtab_entry))); + symtab_names_length = + (((struct hp_ar_symtab_hdr *) symtab_member_data) -> string_table_size); + +#ifdef DEBUG_SYMTAB + dump_symtab_data (); +#endif + + { + char string_buffer [((sizeof (input_header -> ar_name)) + 1)]; + + sprintf + (string_buffer, "%-*s", (sizeof (input_header -> ar_name)), "__.SYMDEF"); + strncpy + ((input_header -> ar_name), + string_buffer, + (sizeof (input_header -> ar_name))); + } + { + char string_buffer [((sizeof (input_header -> ar_size)) + 1)]; + + sprintf + (string_buffer, + "%-*d", + (sizeof (input_header -> ar_size)), + (symtab_entries_bytes + symtab_names_length + (2 * (sizeof (long))))); + strncpy + ((input_header -> ar_size), + string_buffer, + (sizeof (input_header -> ar_size))); + } + iou_write (output_file, input_header, (sizeof (* input_header))); + iou_write (output_file, (& symtab_entries_bytes), (sizeof (long))); + + /* Leave slot for entries, which we fill in later. */ + symtab_start_position = (iou_lseek (output_file, 0, 1)); + initialize_symtab_entries (); + iou_lseek (output_file, symtab_entries_bytes, 1); + + iou_write (output_file, (& symtab_names_length), (sizeof (long))); + iou_write + (output_file, + (symtab_member_data + (sizeof (struct hp_ar_symtab_hdr))), + symtab_names_length); + + return; +} + +void +initialize_symtab_entries () +{ + register struct hp_ar_symtab_entry *scan_hp; + register struct hp_ar_symtab_entry *end; + register struct gnu_symdef *scan_gnu; + + hp_symtab_entries = + ((struct hp_ar_symtab_entry *) + (symtab_member_data + + (sizeof (struct hp_ar_symtab_hdr)) + + symtab_names_length)); + gnu_symtab_entries = + ((struct gnu_symdef *) + (iou_malloc (symtab_entries_length * (sizeof (struct gnu_symdef))))); + + scan_hp = hp_symtab_entries; + end = (scan_hp + symtab_entries_length); + scan_gnu = gnu_symtab_entries; + while (scan_hp < end) + { + (scan_gnu -> symbol_name_string_index) = (scan_hp -> string_index); + (scan_gnu -> library_member_offset) = (-1); + scan_hp += 1; + scan_gnu += 1; + } + + return; +} + +void +update_symtab_entries (old_offset, new_offset) + register long old_offset; + long new_offset; +{ + register struct hp_ar_symtab_entry *scan_hp; + register struct hp_ar_symtab_entry *end; + register struct gnu_symdef *scan_gnu; + +#ifdef DEBUG_SYMTAB + fprintf (stdout, "Update: old = %d, new = %d\n", old_offset, new_offset); +#endif + + scan_hp = hp_symtab_entries; + end = (scan_hp + symtab_entries_length); + scan_gnu = gnu_symtab_entries; + while (scan_hp < end) + { + if ((scan_hp -> file_index) == old_offset) + { +#ifdef DEBUG_SYMTAB + fprintf + (stdout, + "\t%s\n", + (symtab_member_data + + (sizeof (struct hp_ar_symtab_hdr)) + + (scan -> string_index))); +#endif + (scan_gnu -> library_member_offset) = new_offset; + } + scan_hp += 1; + scan_gnu += 1; + } + return; +} + +void +check_symtab_entries () +{ + register struct gnu_symdef *scan; + register struct gnu_symdef *end; + + scan = gnu_symtab_entries; + end = (scan + symtab_entries_length); + while (scan < end) + if (((scan++) -> library_member_offset) == (-1)) + iou_error ("Uninitialized __.SYMTAB entry"); + + return; +} + +void +output_symtab_entries (output_file, input_header) + int output_file; + struct hp_ar_hdr *input_header; +{ + check_symtab_entries (); + iou_lseek (output_file, symtab_start_position, 0); + iou_write + (output_file, + gnu_symtab_entries, + (symtab_entries_length * (sizeof (struct gnu_symdef)))); + iou_free (symtab_member_data); + iou_free (gnu_symtab_entries); + return; +} + +void +process_archive_entry (input_file, output_file, input_header, member_length) + int input_file; + int output_file; + struct hp_ar_hdr *input_header; + int member_length; +{ + long header_position; + long output_start; + long output_end; + long input_end; + char *slashptr; + + /* Strategy: leave a hole for the header, then process the file. + Afterwards, back up and write the header in place. */ + header_position = (iou_lseek (output_file, 0, 1)); + output_start = (iou_lseek (output_file, (sizeof (* input_header)), 1)); + input_end = ((iou_lseek (input_file, 0, 1)) + member_length); + + process_executable_file (input_file, output_file); + if ((iou_lseek (input_file, 0, 1)) != input_end) + iou_error ("process_executable_file used wrong amount of input"); + + output_end = (iou_lseek (output_file, 0, 1)); + iou_lseek (output_file, header_position, 0); + if (slashptr = index(input_header->ar_name, '/')) + *slashptr = ' '; + { + char string_buffer [((sizeof (input_header -> ar_size)) + 1)]; + + sprintf + (string_buffer, + "%-*d", + (sizeof (input_header -> ar_size)), + (output_end - output_start)); + strncpy + ((input_header -> ar_size), + string_buffer, + (sizeof (input_header -> ar_size))); + } + iou_write (output_file, input_header, (sizeof (* input_header))); + iou_lseek (output_file, output_end, 0); + + /* Pad to even byte boundary if needed */ + if ((input_end & 1) != 0) + iou_lseek (input_file, 1, 1); + if ((output_end & 1) != 0) + iou_write (output_file, "\0", 1); + + return; +} + +void +main (argc, argv) + int argc; + char *argv[]; +{ + int input_file; + int output_file; + register int length; + struct hp_magic input_magic; + + iou_set_program_name (argv); + if (argc != 3) + { + fprintf (stderr, "usage: %s input-file output-file\n", iou_program_name); + iou_error (); + } + + input_file = (iou_open ((argv [1]), O_RDONLY)); + output_file = (iou_open ((argv [2]), (O_WRONLY | O_CREAT | O_TRUNC), 0666)); + + xread (input_file, (& input_magic), (sizeof (input_magic))); + if (HP_MAGIC_OK (input_magic)) + { + iou_lseek (input_file, 0, 0); + process_executable_file (input_file, output_file); + } + else + { + char armag [HP_SARMAG]; + + iou_lseek (input_file, 0, 0); + xread (input_file, armag, HP_SARMAG); + if ((strncmp (armag, HP_ARMAG, HP_SARMAG)) != 0) + iou_error ("input file not executable or archive"); + iou_write (output_file, GNU_ARMAG, GNU_SARMAG); + process_archive_file (input_file, output_file); + } + exit (0); + /*NOTREACHED*/ +} diff --git a/binutils-1.9/hp-bin/ioutil.c b/binutils-1.9/hp-bin/ioutil.c new file mode 100644 index 0000000..e488c87 --- /dev/null +++ b/binutils-1.9/hp-bin/ioutil.c @@ -0,0 +1,332 @@ +/* I/O Utilities + Copyright (C) 1988 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 <stdio.h> +#include <varargs.h> +#include <sys/file.h> +#include <string.h> +#define index(s, c) strchr (s, c) +#include <errno.h> +#include <setjmp.h> +#include "ioutil.h" + +char *iou_program_name; + +void +iou_set_program_name (argv) + char *argv []; +{ + register char *start; + register char *slash; + + start = (argv [0]); + while (1) + { + slash = (index (start, '/')); + if (slash == NULL) break; + start = (slash + 1); + } + iou_program_name = start; + return; +} + +struct abort_list + { + struct abort_list *next; + void (* handler) (); + jmp_buf catch_point; + }; +typedef struct abort_list *abort_list; + +static abort_list abort_handlers = NULL; + +void +iou_abort () +{ + register abort_list handlers; + + handlers = abort_handlers; + if (handlers == NULL) abort (); + longjmp ((handlers -> catch_point), 1); /*NOTREACHED*/ +} + +void +iou_abort_handler_bind (thunk, handler) + void (* thunk) (); + void (* handler) (); +{ + register abort_list handlers; + register int setjmp_result; + + handlers = ((abort_list) (iou_malloc (sizeof (struct abort_list)))); + (handlers -> next) = abort_handlers; + (handlers -> handler) = handler; + abort_handlers = handlers; + setjmp_result = (setjmp (handlers -> catch_point)); + switch (setjmp_result) + { + case 0: + (* thunk) (); + if (handlers != abort_handlers) + iou_fatal_error ("iou_abort_handler_bind: sync error"); + abort_handlers = (handlers -> next); + iou_free (handlers); + return; + + case 1: + { + register abort_list handlers; + register void (* handler) (); + + handlers = abort_handlers; + handler = (handlers -> handler); + abort_handlers = (handlers -> next); + iou_free (handlers); + (* handler) (); + return; + } + + default: + iou_fatal_error ("Illegal setjmp value: %d", setjmp_result); + /*NOTREACHED*/ + } +} + +#ifndef hpux +#ifdef bsd + +static void +vfprintf (stream, format, args) + FILE *stream; + char *format; + char *args; +{ + _doprnt (format, args, stderr); + return; +} + +#else +#include "error: no definition for `vfprintf' procedure" +#endif +#endif + +#define WARN() \ +{ \ + va_list args; \ + char *format; \ + \ + fprintf (stderr, "%s: ", iou_program_name); \ + va_start (args); \ + format = (va_arg (args, char *)); \ + vfprintf (stderr, format, args); \ + va_end (args); \ + putc ('\n', stderr); \ +} + +/*VARARGS0*/ void +iou_warning (va_alist) + va_dcl +{ + WARN(); + return; +} + +/*VARARGS0*/ void +iou_error (va_alist) + va_dcl +{ + WARN(); + iou_abort (); /*NOTREACHED*/ +} + +/*VARARGS0*/ void +iou_fatal_error (va_alist) + va_dcl +{ + WARN(); + abort (); /*NOTREACHED*/ +} + +void +iou_call_warning (name) + char *name; +{ + extern int sys_nerr; + extern char *sys_errlist []; + + if (errno < sys_nerr) + iou_warning ("%s during system call: %s", (sys_errlist [errno]), name); + else + iou_warning ("error code %d during system call: %s", errno, name); + return; +} + +void +iou_call_error (name) + char *name; +{ + iou_call_warning (name); + iou_abort (); /*NOTREACHED*/ +} + +void +iou_stat (path, buf) + char *path; + struct stat *buf; +{ + extern int stat (); + register int result; + + while (1) + { + result = (stat (path, buf)); + if (result == 0) break; + if (errno != EINTR) iou_call_error ("stat"); + } + return; +} + +void +iou_unlink (path) + char *path; +{ + extern int unlink (); + + if ((unlink (path)) != 0) + iou_call_error ("unlink"); + return; +} + +int +iou_open (path, oflag, mode) + char *path; + int oflag; + int mode; +{ + register int result; + + while (1) + { + result = (open (path, oflag, mode)); + if (result >= 0) break; + if (errno != EINTR) iou_call_error ("open"); + } + return (result); +} + +void +iou_close (descriptor) + int descriptor; +{ + if ((close (descriptor)) != 0) + iou_call_error ("close"); + return; +} + +long +iou_lseek (descriptor, offset, whence) + int descriptor; + long offset; + int whence; +{ + extern long lseek (); + register long result; + + while (1) + { + result = (lseek (descriptor, offset, whence)); + if (result >= 0) break; + if (errno != EINTR) iou_call_error ("lseek"); + } + return (result); +} + +int +iou_read (input_file, buffer, length) + int input_file; + register char *buffer; + int length; +{ + extern int read (); + register int bytes_remaining; + register int result; + + bytes_remaining = length; + while (bytes_remaining > 0) + { + result = (read (input_file, buffer, bytes_remaining)); + if (result == 0) return (length - bytes_remaining); + if (result > 0) + { + buffer += result; + bytes_remaining -= result; + continue; + } + if (errno != EINTR) iou_call_error ("read"); + } + return (length); +} + +void +iou_write (output_file, buffer, length) + int output_file; + register char *buffer; + register int length; +{ + extern int write (); + register int bytes_remaining; + register int result; + + bytes_remaining = length; + while (bytes_remaining > 0) + { + result = (write (output_file, buffer, bytes_remaining)); + if (result >= 0) + { + buffer += result; + bytes_remaining -= result; + continue; + } + if (errno != EINTR) iou_call_error ("write"); + } + return; +} + +char * +iou_malloc (length) + unsigned length; +{ + extern char *malloc (); + register char *result; + + result = (malloc (length)); + if (result == NULL) iou_error ("virtual memory exhausted"); + return (result); +} + +char * +iou_realloc (pointer, length) + char *pointer; + unsigned length; +{ + extern char *realloc (); + register char *result; + + result = (realloc (pointer, length)); + if (result == NULL) iou_error ("virtual memory exhausted"); + return (result); +} diff --git a/binutils-1.9/hp-bin/ioutil.h b/binutils-1.9/hp-bin/ioutil.h new file mode 100644 index 0000000..27bde15 --- /dev/null +++ b/binutils-1.9/hp-bin/ioutil.h @@ -0,0 +1,20 @@ +extern char *iou_program_name; +extern void iou_set_program_name (); +extern void iou_abort (); +extern void iou_abort_handler_bind (); +extern void iou_warning (); +extern void iou_error (); +extern void iou_fatal_error (); +extern void iou_call_warning (); +extern void iou_call_error (); +extern void iou_stat (); +extern void iou_unlink (); +extern int iou_open (); +extern void iou_close (); +extern long iou_lseek (); +extern int iou_read (); +extern void iou_write (); +extern char *iou_malloc (); +extern char *iou_realloc (); +#define iou_free free +extern void iou_free (); diff --git a/binutils-1.9/hp-bin/mkhplib b/binutils-1.9/hp-bin/mkhplib new file mode 100755 index 0000000..2a86289 --- /dev/null +++ b/binutils-1.9/hp-bin/mkhplib @@ -0,0 +1,30 @@ +#!/bin/csh -f + +if (! -d /usr/local/lib/gnu) then + mkdir /usr/local/lib/gnu +endif + +# Make a dummy "libg.a". +/bin/ar q /tmp/libg$$.a +hpxt /tmp/libg$$.a /usr/local/lib/gnu/libg.a +rm -f /tmp/libg$$.a + +# Note: ordering of directories must be reverse of the normal search +# order! This is because we are compressing all of the directories +# into a single directory. If two directories each contain a library +# with the same name, the one we convert second will be the one we +# end up with. + +foreach dir (/usr/local/lib /usr/lib /lib) + cd $dir + foreach i (lib*.a) + echo "$dir/$i" + hpxt $i /usr/local/lib/gnu/$i + end +end + +cd /lib +foreach i (*crt0.o) + echo "/lib/$i" + hpxt $i /usr/local/lib/gnu/$i +end diff --git a/binutils-1.9/hp-include/a.out.h b/binutils-1.9/hp-include/a.out.h new file mode 100644 index 0000000..87e7219 --- /dev/null +++ b/binutils-1.9/hp-include/a.out.h @@ -0,0 +1,79 @@ +/* Special version of <a.out.h> for use under hp-ux. + Copyright (C) 1988 Free Software Foundation, Inc. + + This file 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 file 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 file; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* The `exec' structure and overall layout must be close to HP's when + we are running on an HP system, otherwise we will not be able to + execute the resulting file. */ + +/* Allow this file to be included twice. */ +#ifndef __GNU_EXEC_MACROS__ + +struct exec +{ + unsigned short a_machtype; /* machine type */ + unsigned short a_magic; /* magic number */ + unsigned long a_spare1; + unsigned long a_spare2; + unsigned long a_text; /* length of text, in bytes */ + unsigned long a_data; /* length of data, in bytes */ + unsigned long a_bss; /* length of uninitialized data area for file, in bytes */ + unsigned long a_trsize; /* length of relocation info for text, in bytes */ + unsigned long a_drsize; /* length of relocation info for data, in bytes */ + unsigned long a_spare3; /* HP = pascal interface size */ + unsigned long a_spare4; /* HP = symbol table size */ + unsigned long a_spare5; /* HP = debug name table size */ + unsigned long a_entry; /* start address */ + unsigned long a_spare6; /* HP = source line table size */ + unsigned long a_spare7; /* HP = value table size */ + unsigned long a_syms; /* length of symbol table data in file, in bytes */ + unsigned long a_spare8; +}; + +/* Tell a.out.gnu.h not to define `struct exec'. */ +#define __STRUCT_EXEC_OVERRIDE__ + +#include "../a.out.gnu.h" + +#undef N_MAGIC +#undef N_MACHTYPE +#undef N_FLAGS +#undef N_SET_INFO +#undef N_SET_MAGIC +#undef N_SET_MACHTYPE +#undef N_SET_FLAGS + +#define N_MAGIC(exec) ((exec) . a_magic) +#define N_MACHTYPE(exec) ((exec) . a_machtype) +#define N_SET_MAGIC(exec, magic) (((exec) . a_magic) = (magic)) +#define N_SET_MACHTYPE(exec, machtype) (((exec) . a_machtype) = (machtype)) + +#undef N_BADMAG +#define N_BADMAG(x) ((_N_BADMAG (x)) || (_N_BADMACH (x))) + +#define _N_BADMACH(x) \ +(((N_MACHTYPE (x)) != HP9000S200_ID) && \ + ((N_MACHTYPE (x)) != HP98x6_ID)) + +#define HP98x6_ID 0x20A +#define HP9000S200_ID 0x20C + +#undef _N_HDROFF +#define _N_HDROFF(x) (SEGMENT_SIZE - (sizeof (struct exec))) + +#define SEGMENT_SIZE 0x1000 + +#endif /* __GNU_EXEC_MACROS__ */ diff --git a/binutils-1.9/hp-include/stab.def b/binutils-1.9/hp-include/stab.def new file mode 100644 index 0000000..b81cda4 --- /dev/null +++ b/binutils-1.9/hp-include/stab.def @@ -0,0 +1,115 @@ +/* Table of DBX symbol codes for the GNU system. + Copyright (C) 1988 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. */ + +/* Global variable. Only the name is significant. + To find the address, look in the corresponding external symbol. */ +__define_stab (N_GSYM, 0x20, "GSYM") + +/* Function name for BSD Fortran. Only the name is significant. + To find the address, look in the corresponding external symbol. */ +__define_stab (N_FNAME, 0x22, "FNAME") + +/* Function name or text-segment variable for C. Value is its address. + Desc is supposedly starting line number, but GCC doesn't set it + and DBX seems not to miss it. */ +__define_stab (N_FUN, 0x24, "FUN") + +/* Data-segment variable with internal linkage. Value is its address. */ +__define_stab (N_STSYM, 0x26, "STSYM") + +/* BSS-segment variable with internal linkage. Value is its address. */ +__define_stab (N_LCSYM, 0x28, "LCSYM") + +/* Name of main routine. Only the name is significant. + This is not used in C. */ +__define_stab (N_MAIN, 0x2a, "MAIN") + +/* Register variable. Value is number of register. */ +__define_stab (N_RSYM, 0x40, "RSYM") + +/* Structure or union element. Value is offset in the structure. */ +__define_stab (N_SSYM, 0x60, "SSYM") + +/* Parameter variable. Value is offset from argument pointer. + (On most machines the argument pointer is the same as the frame pointer. */ +__define_stab (N_PSYM, 0xa0, "PSYM") + +/* Automatic variable in the stack. Value is offset from frame pointer. + Also used for type descriptions. */ +__define_stab (N_LSYM, 0x80, "LSYM") + +/* Alternate entry point. Value is its address. */ +__define_stab (N_ENTRY, 0xa4, "ENTRY") + +/* Name of main source file. + Value is starting text address of the compilation. */ +__define_stab (N_SO, 0x64, "SO") + +/* Name of sub-source file. + Value is starting text address of the compilation. */ +__define_stab (N_SOL, 0x84, "SOL") + +/* Line number in text segment. Desc is the line number; + value is corresponding address. */ +__define_stab (N_SLINE, 0x44, "SLINE") +/* Similar, for data segment. */ +__define_stab (N_DSLINE, 0x46, "DSLINE") +/* Similar, for bss segment. */ +__define_stab (N_BSLINE, 0x48, "BSLINE") + +/* Beginning of an include file. Only Sun uses this. + In an object file, only the name is significant. + The Sun linker puts data into some of the other fields. */ +__define_stab (N_BINCL, 0x82, "BINCL") +/* End of an include file. No name. + These two act as brackets around the file's output. + In an object file, there is no significant data in this entry. + The Sun linker puts data into some of the fields. */ +__define_stab (N_EINCL, 0xa2, "EINCL") +/* Place holder for deleted include file. + This appears only in output from the Sun linker. */ +__define_stab (N_EXCL, 0xc2, "EXCL") + +/* Beginning of lexical block. + The desc is the nesting level in lexical blocks. + The value is the address of the start of the text for the block. + The variables declared inside the block *precede* the N_LBRAC symbol. */ +__define_stab (N_LBRAC, 0xc0, "LBRAC") +/* End of a lexical block. Desc matches the N_LBRAC's desc. + The value is the address of the end of the text for the block. */ +__define_stab (N_RBRAC, 0xe0, "RBRAC") + +/* Begin named common block. Only the name is significant. */ +__define_stab (N_BCOMM, 0xe2, "BCOMM") +/* Begin named common block. Only the name is significant + (and it should match the N_BCOMM). */ +__define_stab (N_ECOMM, 0xe4, "ECOMM") +/* End common (local name): value is address. + I'm not sure how this is used. */ +__define_stab (N_ECOML, 0xe8, "ECOML") +/* Second symbol entry containing a length-value for the preceding entry. + The value is the length. */ +__define_stab (N_LENG, 0xfe, "LENG") + +/* Global symbol in Pascal. + Supposedly the value is its line number; I'm skeptical. */ +__define_stab (N_PC, 0x30, "PC") + +/* Modula-2 compilation unit. Can someone say what info it contains? */ +__define_stab (N_M2C, 0x42, "M2C") +/* Modula-2 scope information. Can someone say what info it contains? */ +__define_stab (N_SCOPE, 0xc4, "SCOPE") diff --git a/binutils-1.9/hp-include/stab.h b/binutils-1.9/hp-include/stab.h new file mode 100644 index 0000000..80bd594 --- /dev/null +++ b/binutils-1.9/hp-include/stab.h @@ -0,0 +1,17 @@ +#ifndef __GNU_STAB__ + +/* Indicate the GNU stab.h is in use. */ + +#define __GNU_STAB__ + +#define __define_stab(NAME, CODE, STRING) NAME=CODE, + +enum __stab_debug_code +{ +#include "stab.def" +LAST_UNUSED_STAB_CODE +}; + +#undef __define_stab + +#endif /* __GNU_STAB_ */ diff --git a/binutils-1.9/ld.c b/binutils-1.9/ld.c new file mode 100644 index 0000000..d7bc0ce --- /dev/null +++ b/binutils-1.9/ld.c @@ -0,0 +1,6312 @@ +/* Linker `ld' for GNU + Copyright (C) 1988 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. */ + +/* Written by Richard Stallman with some help from Eric Albert. + Set, indirect, and warning symbol features added by Randy Smith. */ + +#include <ar.h> +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/file.h> +#ifndef USG +#include <sys/time.h> +#include <sys/resource.h> +#endif +#ifndef sony_news +#include <fcntl.h> +#endif +#ifdef ns32000 +struct relocation_info +{ /* Look for the default declaration in a.out.gnu.h for documentation. */ + int r_address; + unsigned int r_symbolnum:24; + unsigned int r_pcrel:1; + unsigned int r_length:2; + unsigned int r_extern:1; + unsigned int r_bsr:1; + unsigned int r_disp:2; + unsigned int r_pad:1; +}; +#define N_RELOCATION_INFO_DECLARED +#endif /*ns32000*/ + +#if !defined(A_OUT) && !defined(MACH_O) +#define A_OUT +#endif + +#ifdef A_OUT +#ifdef COFF_ENCAPSULATE +#include "a.out.encap.h" +#else +#include <a.out.h> +#endif +#endif + +#ifdef MACH_O +#ifndef A_OUT +#include <nlist.h> +#include <reloc.h> +#endif +#ifndef N_TEXT +#define N_TEXT 0x04 +#define N_DATA 0x06 +#define N_BSS 0x08 +#endif +#include <sys/loader.h> +#endif + +#ifndef N_SET_MAGIC +#define N_SET_MAGIC(exec, val) ((exec).a_magic = val) +#endif + +/* If compiled with GNU C, use the built-in alloca */ +#ifdef __GNUC__ +# define alloca __builtin_alloca +#else +# if defined(sun) && defined(sparc) +# include "alloca.h" +# else +char *alloca (); +# endif +#endif + +#include "getopt.h" + +/* Always use the GNU version of debugging symbol type codes, if possible. */ + +#include "stab.h" +#define CORE_ADDR unsigned long /* For symseg.h */ +#include "symseg.h" + +#ifdef USG +#include <string.h> +#else +#include <strings.h> +#endif + +/* Determine whether we should attempt to handle (minimally) + N_BINCL and N_EINCL. */ + +#if defined (__GNU_STAB__) || defined (N_BINCL) +#define HAVE_SUN_STABS +#endif + +#define min(a,b) ((a) < (b) ? (a) : (b)) + +/* Macro to control the number of undefined references printed */ +#define MAX_UREFS_PRINTED 10 + +/* Size of a page; obtained from the operating system. */ + +int page_size; + +/* Name this program was invoked by. */ + +char *progname; + +/* System dependencies */ + +/* Define this if names etext, edata and end should not start with `_'. */ +/* #define nounderscore 1 */ + +/* Define NON_NATIVE if using BSD or pseudo-BSD file format on a system + whose native format is different. */ +/* #define NON_NATIVE */ + +/* Define this to specify the default executable format. */ + +#ifdef hpux +#define DEFAULT_OUTPUT_STYLE OUTPUT_READONLY_TEXT +#endif + +/* Ordinary 4.3bsd lacks these macros in a.out.h. */ + +#ifndef N_TXTADDR +#if defined(vax) || defined(sony_news) || defined(hp300) || defined(pyr) +#define N_TXTADDR(X) 0 +#endif +#ifdef is68k +#define N_TXTADDR(x) (sizeof (struct exec)) +#endif +#ifdef sequent +#define N_TXTADDR(x) (N_ADDRADJ(x)) +#endif +#ifdef tek4300 +#define N_TXTADDR(x) ((x).a_magic == ZMAGIC ? page_size : 0) +#endif +#ifdef NeXT +#define N_TXTADDR(X) ((X).a_magic == ZMAGIC ? page_size : 0) +#endif +#endif + +#ifndef N_DATADDR +#if defined(vax) || defined(sony_news) || defined(hp300) || defined(pyr) +#define N_DATADDR(x) \ + (((x).a_magic==OMAGIC)? (N_TXTADDR(x)+(x).a_text) \ + : (page_size+((N_TXTADDR(x)+(x).a_text-1) & ~(page_size-1)))) +#endif +#ifdef is68k +#define SEGMENT_SIZE 0x20000 +#define N_DATADDR(x) \ + (((x).a_magic==Omagic)? (N_TXTADDR(x)+(x).a_text) \ + : (SEGMENT_SIZE + ((N_TXTADDR(x)+(x).a_text-1) & ~(SEGMENT_SIZE-1)))) +#endif +#ifdef sequent +#define N_DATADDR(x) \ + (((x).a_magic==OMAGIC)? (N_TXTADDR(x)+(x).a_text) \ + : (page_size+(((x).a_text-1) & ~(page_size-1)))) +#endif +#ifdef tek4300 +#define N_DATADDR(x) \ + (((x).a_magic==OMAGIC)? (N_TXTADDR(x)+(x).a_text) \ + : (page_size+((N_TXTADDR(x)+(x).a_text-1) & ~(page_size-1)))) +#endif +#ifdef NeXT +#define N_DATADDR(X) \ + (((X).a_magic==ZMAGIC)?(N_TXTADDR(X)+(X).a_text+0xFFFF)&~0xFFFF \ + :N_TXTADDR(X)+(X).a_text) +#endif +#endif + +/* The "address" of the data segment in a relocatable file. + The text address of a relocatable file is always + considered to be zero (instead of the value of N_TXTADDR, which + is what the address is in an executable), so we need to subtract + N_TXTADDR from N_DATADDR to get the "address" for the input file. */ +#define DATA_ADDR_DOT_O(hdr) (N_DATADDR(hdr) - N_TXTADDR(hdr)) + +/* Define how to initialize system-dependent header fields. */ +#ifdef sun +#ifdef sparc +#define INITIALIZE_HEADER \ + {outheader.a_machtype = M_SPARC; outheader.a_toolversion = 1;} +#endif /* sparc. */ +#if defined(mc68000) +/* Set the machine type according to the machine type of the .o files. + If they are all sun2 (68010), then the type of the executable is sun2. + If any is sun3 (68020), then the type of the executable is sun3. + This is consistent with the Sun loader and more useful than having + it depend on which machine you are on when you run ld. */ +static int sun_machtype = M_68010; +#define INITIALIZE_HEADER outheader.a_machtype = sun_machtype +#define READ_HEADER_HOOK(machtype) \ + if (machtype == M_68020) \ + { \ + sun_machtype = M_68020; \ + } +#endif /* mc68000. */ +#endif /* Sun. */ + +#ifdef ALTOS +#define INITIALIZE_HEADER N_SET_MACHTYPE (outheader, M_68020) +#endif +#ifdef is68k +#ifdef M_68020 +/* ISI rel 4.0D doesn't use it, and rel 3.05 doesn't have an + a_machtype field and so won't recognize the magic number. To keep + binary compatibility for now, just ignore it */ +#define INITIALIZE_HEADER outheader.a_machtype = 0; +#endif +#endif +#ifdef hpux +#define INITIALIZE_HEADER N_SET_MACHTYPE (outheader, HP9000S200_ID) +#endif +#if defined(i386) && !defined(sequent) +#define INITIALIZE_HEADER N_SET_MACHTYPE (outheader, M_386) +#endif /* Sequent symmetry. */ +#if defined(hp300) +#define INITIALIZE_HEADER outheader.a_mid = MID_HP300 +/* MORE/bsd tags the filename symbol with N_FN|N_EXT not N_TEXT */ +#define OFILE_FN_FLAGGED +#endif /* hp300. */ +#ifdef pyr +#define INITIALIZE_HEADER outheader.a_machid = PYR90X +#endif /* Pyramid. */ + +#ifdef tek4300 +#undef INITIALIZE_HEADER +#define INITIALIZE_HEADER (outheader).a_archclass = ARCHCLASS_MC68020, (outheader).a_textoff = ZMOFF +/* The alternative to the following patch is to change field "header" + in struct file_entry, from struct exec to struct zexec; this affects + the -A option */ +#undef N_DATOFF +#define N_DATOFF(x) ( (x).a_magic==ZMAGIC \ + ? (ZMOFF + (x).a_text) \ + : (sizeof (struct exec) + (x).a_text) ) +#endif + +#ifdef is68k +/* This enables code to take care of an ugly hack in the ISI OS. + If a symbol beings with _$, then the object file is included only + if the rest of the symbol name has been referenced. */ +#define DOLLAR_KLUDGE +#endif + +/* Values for 3rd argument to lseek(). */ +#ifndef L_SET +#define L_SET 0 +#endif +/* This is called L_INCR in BSD, but SEEK_CUR in POSIX. */ +#ifndef SEEK_CUR +#define SEEK_CUR 1 +#endif + +/* + * Ok. Following are the relocation information macros. If your + * system cannot use the default set (below), you must define all of these: + + * relocation_info: This must be typedef'd (or #define'd) to the type + * of structure that is stored in the relocation info section of your + * a.out files. Often this is defined in the a.out.h for your system. + * + * RELOC_ADDRESS (rval): Offset into the current section of the + * <whatever> to be relocated. *Must be an lvalue*. + * + * RELOC_EXTERN_P (rval): Is this relocation entry based on an + * external symbol (1), or was it fully resolved upon entering the + * loader (0) in which case some combination of the value in memory + * (if RELOC_MEMORY_ADD_P) and the extra (if RELOC_ADD_EXTRA) contains + * what the value of the relocation actually was. *Must be an lvalue*. + * + * RELOC_TYPE (rval): For a non-external relocation, this is the + * segment to relocate for. *Must be an lvalue.* + * + * RELOC_SYMBOL (rval): For an external relocation, this is the + * index of its symbol in the symbol table. *Must be an lvalue*. + * + * RELOC_MEMORY_ADD_P (rval): This should be 1 if the final + * relocation value output here should be added to memory; 0, if the + * section of memory described should simply be set to the relocation + * value. + * + * RELOC_MEMORY_ADD_P (rval): If this is nonzero, the value previously + * present in the memory location to be relocated is *added* + * to the relocation value, to produce the final result. + * Otherwise, the relocation value is stored in the memory location + * and the value previously found there is ignored. + * By default, this is always 1. + * + * RELOC_MEMORY_SUB_P (rval): If this is nonzero, the value previously + * present in the memory location to be relocated is *subtracted* + * from the relocation value, to produce the final result. + * By default, this is always 0. + * + * RELOC_ADD_EXTRA (rval): (Optional) This macro, if defined, gives + * an extra value to be added to the relocation value based on the + * individual relocation entry. *Must be an lvalue if defined*. + * + * RELOC_PCREL_P (rval): True if the relocation value described is + * pc relative. + * + * RELOC_VALUE_RIGHTSHIFT (rval): Number of bits right to shift the + * final relocation value before putting it where it belongs. + * + * RELOC_TARGET_SIZE (rval): log to the base 2 of the number of + * bytes of size this relocation entry describes; 1 byte == 0; 2 bytes + * == 1; 4 bytes == 2, and etc. This is somewhat redundant (we could + * do everything in terms of the bit operators below), but having this + * macro could end up producing better code on machines without fancy + * bit twiddling. Also, it's easier to understand/code big/little + * endian distinctions with this macro. + * + * RELOC_TARGET_BITPOS (rval): The starting bit position within the + * object described in RELOC_TARGET_SIZE in which the relocation value + * will go. + * + * RELOC_TARGET_BITSIZE (rval): How many bits are to be replaced + * with the bits of the relocation value. It may be assumed by the + * code that the relocation value will fit into this many bits. This + * may be larger than RELOC_TARGET_SIZE if such be useful. + * + * + * Things I haven't implemented + * ---------------------------- + * + * Values for RELOC_TARGET_SIZE other than 0, 1, or 2. + * + * Pc relative relocation for External references. + * + * + */ + +#if defined(sun) && defined(sparc) +/* Sparc (Sun 4) macros */ +#undef relocation_info +#define relocation_info reloc_info_sparc +#define RELOC_ADDRESS(r) ((r)->r_address) +#define RELOC_EXTERN_P(r) ((r)->r_extern) +#define RELOC_TYPE(r) ((r)->r_index) +#define RELOC_SYMBOL(r) ((r)->r_index) +#define RELOC_MEMORY_SUB_P(r) 0 +#define RELOC_MEMORY_ADD_P(r) 0 +#define RELOC_ADD_EXTRA(r) ((r)->r_addend) +#define RELOC_PCREL_P(r) \ + ((r)->r_type >= RELOC_DISP8 && (r)->r_type <= RELOC_WDISP22) +#define RELOC_VALUE_RIGHTSHIFT(r) (reloc_target_rightshift[(r)->r_type]) +#define RELOC_TARGET_SIZE(r) (reloc_target_size[(r)->r_type]) +#define RELOC_TARGET_BITPOS(r) 0 +#define RELOC_TARGET_BITSIZE(r) (reloc_target_bitsize[(r)->r_type]) + +/* Note that these are very dependent on the order of the enums in + enum reloc_type (in a.out.h); if they change the following must be + changed */ +/* Also note that the last few may be incorrect; I have no information */ +static int reloc_target_rightshift[] = { + 0, 0, 0, 0, 0, 0, 2, 2, 10, 0, 0, 0, 0, 0, 0, +}; +static int reloc_target_size[] = { + 0, 1, 2, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, +}; +static int reloc_target_bitsize[] = { + 8, 16, 32, 8, 16, 32, 30, 22, 22, 22, 13, 10, 32, 32, 16, +}; + +#define MAX_ALIGNMENT (sizeof (double)) +#endif + +#if defined(sequent) || defined(ns32000) +#define RELOC_ADDRESS(r) ((r)->r_address) +#define RELOC_EXTERN_P(r) ((r)->r_extern) +#define RELOC_TYPE(r) ((r)->r_symbolnum) +#define RELOC_SYMBOL(r) ((r)->r_symbolnum) +#ifdef sequent /* I think r_bsr is needed only for compat. + with sequent object files, or that's + what Ian has done. But might be that + this is needed for all 32k machines.*/ +#define RELOC_MEMORY_SUB_P(r) ((r)->r_bsr) +#else +#define RELOC_MEMORY_SUB_P(r) 0 +#endif +#define RELOC_MEMORY_ADD_P(r) 1 +#undef RELOC_ADD_EXTRA +#ifdef sequent /* might need to be on all 32k machines */ +#define RELOC_PCREL_P(r) ((r)->r_pcrel || (r)->r_bsr) +#else +#define RELOC_PCREL_P(r) ((r)->r_pcrel) +#endif +#define RELOC_VALUE_RIGHTSHIFT(r) 0 +#define RELOC_TARGET_SIZE(r) ((r)->r_length) +#define RELOC_TARGET_BITPOS(r) 0 +#define RELOC_TARGET_BITSIZE(r) 32 +#endif + +/* Default macros */ +#ifndef RELOC_ADDRESS +#define RELOC_ADDRESS(r) ((r)->r_address) +#define RELOC_EXTERN_P(r) ((r)->r_extern) +#define RELOC_TYPE(r) ((r)->r_symbolnum) +#define RELOC_SYMBOL(r) ((r)->r_symbolnum) +#define RELOC_MEMORY_SUB_P(r) 0 +#define RELOC_MEMORY_ADD_P(r) 1 +#undef RELOC_ADD_EXTRA +#define RELOC_PCREL_P(r) ((r)->r_pcrel) +#define RELOC_VALUE_RIGHTSHIFT(r) 0 +#define RELOC_TARGET_SIZE(r) ((r)->r_length) +#define RELOC_TARGET_BITPOS(r) 0 +#define RELOC_TARGET_BITSIZE(r) 32 +#endif + +#ifndef MAX_ALIGNMENT +#define MAX_ALIGNMENT (sizeof (int)) +#endif + +#ifdef nounderscore +#define LPREFIX '.' +#else +#define LPREFIX 'L' +#endif + + +/* Special global symbol types understood by GNU LD. */ + +/* The following type indicates the definition of a symbol as being + an indirect reference to another symbol. The other symbol + appears as an undefined reference, immediately following this symbol. + + Indirection is asymmetrical. The other symbol's value will be used + to satisfy requests for the indirect symbol, but not vice versa. + If the other symbol does not have a definition, libraries will + be searched to find a definition. + + So, for example, the following two lines placed in an assembler + input file would result in an object file which would direct gnu ld + to resolve all references to symbol "foo" as references to symbol + "bar". + + .stabs "_foo",11,0,0,0 + .stabs "_bar",1,0,0,0 + + Note that (11 == (N_INDR | N_EXT)) and (1 == (N_UNDF | N_EXT)). */ + +#ifndef N_INDR +#define N_INDR 0xa +#endif + +/* The following symbols refer to set elements. These are expected + only in input to the loader; they should not appear in loader + output (unless relocatable output is requested). To be recognized + by the loader, the input symbols must have their N_EXT bit set. + All the N_SET[ATDB] symbols with the same name form one set. The + loader collects all of these elements at load time and outputs a + vector for each name. + Space (an array of 32 bit words) is allocated for the set in the + data section, and the n_value field of each set element value is + stored into one word of the array. + The first word of the array is the length of the set (number of + elements). The last word of the vector is set to zero for possible + use by incremental loaders. The array is ordered by the linkage + order; the first symbols which the linker encounters will be first + in the array. + + In C syntax this looks like: + + struct set_vector { + unsigned int length; + unsigned int vector[length]; + unsigned int always_zero; + }; + + Before being placed into the array, each element is relocated + according to its type. This allows the loader to create an array + of pointers to objects automatically. N_SETA type symbols will not + be relocated. + + The address of the set is made into an N_SETV symbol + whose name is the same as the name of the set. + This symbol acts like a N_DATA global symbol + in that it can satisfy undefined external references. + + For the purposes of determining whether or not to load in a library + file, set element definitions are not considered "real + definitions"; they will not cause the loading of a library + member. + + If relocatable output is requested, none of this processing is + done. The symbols are simply relocated and passed through to the + output file. + + So, for example, the following three lines of assembler code + (whether in one file or scattered between several different ones) + will produce a three element vector (total length is five words; + see above), referenced by the symbol "_xyzzy", which will have the + addresses of the routines _init1, _init2, and _init3. + + *NOTE*: If symbolic addresses are used in the n_value field of the + defining .stabs, those symbols must be defined in the same file as + that containing the .stabs. + + .stabs "_xyzzy",23,0,0,_init1 + .stabs "_xyzzy",23,0,0,_init2 + .stabs "_xyzzy",23,0,0,_init3 + + Note that (23 == (N_SETT | N_EXT)). */ + +#ifndef N_SETA +#define N_SETA 0x14 /* Absolute set element symbol */ +#endif /* This is input to LD, in a .o file. */ + +#ifndef N_SETT +#define N_SETT 0x16 /* Text set element symbol */ +#endif /* This is input to LD, in a .o file. */ + +#ifndef N_SETD +#define N_SETD 0x18 /* Data set element symbol */ +#endif /* This is input to LD, in a .o file. */ + +#ifndef N_SETB +#define N_SETB 0x1A /* Bss set element symbol */ +#endif /* This is input to LD, in a .o file. */ + +/* Macros dealing with the set element symbols defined in a.out.h */ +#define SET_ELEMENT_P(x) ((x)>=N_SETA&&(x)<=(N_SETB|N_EXT)) +#define TYPE_OF_SET_ELEMENT(x) ((x)-N_SETA+N_ABS) + +#ifndef N_SETV +#define N_SETV 0x1C /* Pointer to set vector in data area. */ +#endif /* This is output from LD. */ + +/* If a this type of symbol is encountered, its name is a warning + message to print each time the symbol referenced by the next symbol + table entry is referenced. + + This feature may be used to allow backwards compatibility with + certain functions (eg. gets) but to discourage programmers from + their use. + + So if, for example, you wanted to have ld print a warning whenever + the function "gets" was used in their C program, you would add the + following to the assembler file in which gets is defined: + + .stabs "Obsolete function \"gets\" referenced",30,0,0,0 + .stabs "_gets",1,0,0,0 + + These .stabs do not necessarily have to be in the same file as the + gets function, they simply must exist somewhere in the compilation. */ + +#ifndef N_WARNING +#define N_WARNING 0x1E /* Warning message to print if symbol + included */ +#endif /* This is input to ld */ + +#ifndef __GNU_STAB__ + +/* Line number for the data section. This is to be used to describe + the source location of a variable declaration. */ +#ifndef N_DSLINE +#define N_DSLINE (N_SLINE+N_DATA-N_TEXT) +#endif + +/* Line number for the bss section. This is to be used to describe + the source location of a variable declaration. */ +#ifndef N_BSLINE +#define N_BSLINE (N_SLINE+N_BSS-N_TEXT) +#endif + +#endif /* not __GNU_STAB__ */ + +/* Symbol table */ + +/* Global symbol data is recorded in these structures, + one for each global symbol. + They are found via hashing in 'symtab', which points to a vector of buckets. + Each bucket is a chain of these structures through the link field. */ + +typedef + struct glosym + { + /* Pointer to next symbol in this symbol's hash bucket. */ + struct glosym *link; + /* Name of this symbol. */ + char *name; + /* Value of this symbol as a global symbol. */ + long value; + /* Chain of external 'nlist's in files for this symbol, both defs + and refs. */ + struct nlist *refs; + /* Any warning message that might be associated with this symbol + from an N_WARNING symbol encountered. */ + char *warning; + /* Nonzero means definitions of this symbol as common have been seen, + and the value here is the largest size specified by any of them. */ + int max_common_size; + /* For OUTPUT_RELOCATABLE, records the index of this global sym in the + symbol table to be written, with the first global sym given index 0.*/ + int def_count; + /* Nonzero means a definition of this global symbol is known to exist. + Library members should not be loaded on its account. */ + char defined; + /* Nonzero means a reference to this global symbol has been seen + in a file that is surely being loaded. + A value higher than 1 is the n_type code for the symbol's + definition. */ + char referenced; + /* A count of the number of undefined references printed for a + specific symbol. If a symbol is unresolved at the end of + digest_symbols (and the loading run is supposed to produce + relocatable output) do_file_warnings keeps track of how many + unresolved reference error messages have been printed for + each symbol here. When the number hits MAX_UREFS_PRINTED, + messages stop. */ + unsigned char undef_refs; + /* 1 means that this symbol has multiple definitions. 2 means + that it has multiple definitions, and some of them are set + elements, one of which has been printed out already. */ + unsigned char multiply_defined; + /* Pointer to the file_entry for the last library that contained a + subfile referencing this symbol. */ + struct file_entry *last_library_ref; + /* Nonzero means print a message at all refs or defs of this symbol */ + char trace; + } + symbol; + +/* Demangler for C++. */ +extern char *cplus_demangle (); + +/* Demangler function to use. We unconditionally enable the C++ demangler + because we assume any name it successfully demangles was probably produced + by the C++ compiler. Enabling it only if -lg++ was specified seems too + much of a kludge. */ +char *(*demangler)() = cplus_demangle; + +/* Number of buckets in symbol hash table */ +#define TABSIZE 1009 + +/* The symbol hash table: a vector of TABSIZE pointers to struct glosym. */ +symbol *symtab[TABSIZE]; + +/* Number of symbols in symbol hash table. */ +int num_hash_tab_syms = 0; + +/* Count the number of nlist entries that are for local symbols. + This count and the three following counts + are incremented as as symbols are entered in the symbol table. */ +int local_sym_count; + +/* Count number of nlist entries that are for local symbols + whose names don't start with L. */ +int non_L_local_sym_count; + +/* Count the number of nlist entries for debugger info. */ +int debugger_sym_count; + +/* Count the number of global symbols referenced and not defined. */ +int undefined_global_sym_count; + +/* Count the number of global symbols multiply defined. */ +int multiple_def_count; + +/* Count the number of defined global symbols. + Each symbol is counted only once + regardless of how many different nlist entries refer to it, + since the output file will need only one nlist entry for it. + This count is computed by `digest_symbols'; + it is undefined while symbols are being loaded. */ +int defined_global_sym_count; + +/* Count the number of symbols defined through common declarations. + This count is kept in symdef_library, linear_library, and + enter_global_ref. It is incremented when the defined flag is set + in a symbol because of a common definition, and decremented when + the symbol is defined "for real" (ie. by something besides a common + definition). */ +int common_defined_global_count; + +/* Count the number of set element type symbols and the number of + separate vectors which these symbols will fit into. See the + GNU a.out.h for more info. + This count is computed by 'enter_file_symbols' */ +int set_symbol_count; +int set_vector_count; + +/* Define a linked list of strings which define symbols which should + be treated as set elements even though they aren't. Any symbol + with a prefix matching one of these should be treated as a set + element. + + This is to make up for deficiencies in many assemblers which aren't + willing to pass any stabs through to the loader which they don't + understand. */ +struct string_list_element { + char *str; + struct string_list_element *next; +}; + +struct string_list_element *set_element_prefixes; + +/* Count the number of definitions done indirectly (ie. done relative + to the value of some other symbol. */ +int global_indirect_count; + +/* Count the number of warning symbols encountered. */ +int warning_count; + +/* Total number of symbols to be written in the output file. + Computed by digest_symbols from the variables above. */ +int nsyms; + + +/* Nonzero means ptr to symbol entry for symbol to use as start addr. + -e sets this. */ +symbol *entry_symbol; + +/* These can be NULL if we don't actually have such a symbol. */ +symbol *edata_symbol; /* the symbol _edata */ +symbol *etext_symbol; /* the symbol _etext */ +symbol *end_symbol; /* the symbol _end */ +/* We also need __{edata,etext,end} so that they can safely + be used from an ANSI library. */ +symbol *edata_symbol_alt; +symbol *etext_symbol_alt; +symbol *end_symbol_alt; + +/* Kinds of files potentially understood by the linker. */ + +enum file_type { IS_UNKNOWN, IS_ARCHIVE, IS_A_OUT, IS_MACH_O }; + +/* Each input file, and each library member ("subfile") being loaded, + has a `file_entry' structure for it. + + For files specified by command args, these are contained in the vector + which `file_table' points to. + + For library members, they are dynamically allocated, + and chained through the `chain' field. + The chain is found in the `subfiles' field of the `file_entry'. + The `file_entry' objects for the members have `superfile' fields pointing + to the one for the library. */ + +struct file_entry { + /* Name of this file. */ + char *filename; + + /* What kind of file this is. */ + enum file_type file_type; + + /* Name to use for the symbol giving address of text start */ + /* Usually the same as filename, but for a file spec'd with -l + this is the -l switch itself rather than the filename. */ + char *local_sym_name; + + /* Describe the layout of the contents of the file. */ + + /* The text section. */ + unsigned long int orig_text_address; + unsigned long int text_size; + long int text_offset; + + /* Text relocation. */ + unsigned long int text_reloc_size; + long int text_reloc_offset; + + /* The data section. */ + unsigned long int orig_data_address; + unsigned long int data_size; + long int data_offset; + + /* Data relocation. */ + unsigned long int data_reloc_size; + long int data_reloc_offset; + + /* The bss section. */ + unsigned long int orig_bss_address; + unsigned long int bss_size; + + /* The symbol and string tables. */ + unsigned long int syms_size; + long int syms_offset; + unsigned long int strs_size; + long int strs_offset; + + /* The GDB symbol segment, if any. */ + unsigned long int symseg_size; + long int symseg_offset; + +#ifdef MACH_O + /* Section ordinals from the Mach-O load commands. These + are compared with the n_sect fields of symbols. */ + int text_ordinal; + int data_ordinal; + int bss_ordinal; +#endif + + /* Describe data from the file loaded into core */ + + /* Symbol table of the file. */ + struct nlist *symbols; + + /* Pointer to the string table. + The string table is not kept in core all the time, + but when it is in core, its address is here. */ + char *strings; + + /* Next two used only if OUTPUT_RELOCATABLE or if needed for */ + /* output of undefined reference line numbers. */ + + /* Text reloc info saved by `write_text' for `coptxtrel'. */ + struct relocation_info *textrel; + /* Data reloc info saved by `write_data' for `copdatrel'. */ + struct relocation_info *datarel; + + /* Relation of this file's segments to the output file */ + + /* Start of this file's text seg in the output file core image. */ + int text_start_address; + /* Start of this file's data seg in the output file core image. */ + int data_start_address; + /* Start of this file's bss seg in the output file core image. */ + int bss_start_address; + /* Offset in bytes in the output file symbol table + of the first local symbol for this file. Set by `write_file_symbols'. */ + int local_syms_offset; + + /* For library members only */ + + /* For a library, points to chain of entries for the library members. */ + struct file_entry *subfiles; + /* For a library member, offset of the member within the archive. + Zero for files that are not library members. */ + int starting_offset; + /* Size of contents of this file, if library member. */ + int total_size; + /* For library member, points to the library's own entry. */ + struct file_entry *superfile; + /* For library member, points to next entry for next member. */ + struct file_entry *chain; + + /* 1 if file is a library. */ + char library_flag; + + /* 1 if file's header has been read into this structure. */ + char header_read_flag; + + /* 1 means search a set of directories for this file. */ + char search_dirs_flag; + + /* 1 means this is base file of incremental load. + Do not load this file's text or data. + Also default text_start to after this file's bss. */ + char just_syms_flag; +}; + +/* Vector of entries for input files specified by arguments. + These are all the input files except for members of specified libraries. */ +struct file_entry *file_table; + +/* Length of that vector. */ +int number_of_files; + +/* When loading the text and data, we can avoid doing a close + and another open between members of the same library. + + These two variables remember the file that is currently open. + Both are zero if no file is open. + + See `each_file' and `file_close'. */ + +struct file_entry *input_file; +int input_desc; + +/* The name of the file to write; "a.out" by default. */ + +char *output_filename; + +/* What kind of output file to write. */ + +enum file_type output_file_type; + +#ifndef DEFAULT_OUTPUT_FILE_TYPE +#ifdef MACH_O +#define DEFAULT_OUTPUT_FILE_TYPE IS_MACH_O +#else +#define DEFAULT_OUTPUT_FILE_TYPE IS_A_OUT +#endif +#endif + +/* What `style' of output file to write. For BSD a.out files + this specifies OMAGIC, NMAGIC, or ZMAGIC. For Mach-O files + this switches between MH_OBJECT and two flavors of MH_EXECUTE. */ + +enum output_style + { + OUTPUT_UNSPECIFIED, + OUTPUT_RELOCATABLE, /* -r */ + OUTPUT_WRITABLE_TEXT, /* -N */ + OUTPUT_READONLY_TEXT, /* -n */ + OUTPUT_DEMAND_PAGED /* -Z (default) */ + }; + +enum output_style output_style; + +#ifndef DEFAULT_OUTPUT_STYLE +#define DEFAULT_OUTPUT_STYLE OUTPUT_DEMAND_PAGED +#endif + +/* Descriptor for writing that file with `mywrite'. */ + +int outdesc; + +/* The following are computed by `digest_symbols'. */ + +int text_size; /* total size of text of all input files. */ +int text_header_size; /* size of the file header if included in the + text size. */ +int data_size; /* total size of data of all input files. */ +int bss_size; /* total size of bss of all input files. */ +int text_reloc_size; /* total size of text relocation of all input files. */ +int data_reloc_size; /* total size of data relocation of all input + files. */ + +/* The following are computed by write_header(). */ +long int output_text_offset; /* file offset of the text section. */ +long int output_data_offset; /* file offset of the data section. */ +long int output_trel_offset; /* file offset of the text relocation info. */ +long int output_drel_offset; /* file offset of the data relocation info. */ +long int output_syms_offset; /* file offset of the symbol table. */ +long int output_strs_offset; /* file offset of the string table. */ + +/* The following are incrementally computed by write_syms(); we keep + them here so we can examine their values afterwards. */ +unsigned int output_syms_size; /* total bytes of symbol table output. */ +unsigned int output_strs_size; /* total bytes of string table output. */ + +/* This can only be computed after the size of the string table is known. */ +long int output_symseg_offset; /* file offset of the symbol segment (if any). */ + +/* Incrementally computed by write_file_symseg(). */ +unsigned int output_symseg_size; + +/* Specifications of start and length of the area reserved at the end + of the text segment for the set vectors. Computed in 'digest_symbols' */ +int set_sect_start; +int set_sect_size; + +/* Pointer for in core storage for the above vectors, before they are + written. */ +unsigned long *set_vectors; + +/* Amount of cleared space to leave at the end of the text segment. */ + +int text_pad; + +/* Amount of padding at end of data segment. This has two parts: + That which is before the bss segment, and that which overlaps + with the bss segment. */ +int data_pad; + +/* Format of __.SYMDEF: + First, a longword containing the size of the 'symdef' data that follows. + Second, zero or more 'symdef' structures. + Third, a longword containing the length of symbol name strings. + Fourth, zero or more symbol name strings (each followed by a null). */ + +struct symdef { + int symbol_name_string_index; + int library_member_offset; +}; + +/* Record most of the command options. */ + +/* Address we assume the text section will be loaded at. + We relocate symbols and text and data for this, but we do not + write any padding in the output file for it. */ +int text_start; + +/* Address we decide the data section will be loaded at. */ +int data_start; + +/* Nonzero if -T was specified in the command line. + This prevents text_start from being set later to default values. */ +int T_flag_specified; + +/* Nonzero if -Tdata was specified in the command line. + This prevents data_start from being set later to default values. */ +int Tdata_flag_specified; + +/* Size to pad data section up to. + We simply increase the size of the data section, padding with zeros, + and reduce the size of the bss section to match. */ +int specified_data_size; + +/* Nonzero means print names of input files as processed. */ +int trace_files; + +/* Which symbols should be stripped (omitted from the output): + none, all, or debugger symbols. */ +enum { STRIP_NONE, STRIP_ALL, STRIP_DEBUGGER } strip_symbols; + +/* Which local symbols should be omitted: + none, all, or those starting with L. + This is irrelevant if STRIP_NONE. */ +enum { DISCARD_NONE, DISCARD_ALL, DISCARD_L } discard_locals; + +/* 1 => write load map. */ +int write_map; + +/* 1 => assign space to common symbols even if OUTPUT_RELOCATABLE. */ +int force_common_definition; + +/* Standard directories to search for files specified by -l. */ +char *standard_search_dirs[] = +#ifdef STANDARD_SEARCH_DIRS + {STANDARD_SEARCH_DIRS}; +#else +#ifdef NON_NATIVE + {"/usr/local/lib/gnu"}; +#else + {"/lib", "/usr/lib", "/usr/local/lib"}; +#endif +#endif + +/* If set STANDARD_SEARCH_DIRS is not searched. */ +int no_standard_dirs; + +/* Actual vector of directories to search; + this contains those specified with -L plus the standard ones. */ +char **search_dirs; + +/* Length of the vector `search_dirs'. */ +int n_search_dirs; + +/* Non zero means to create the output executable. + Cleared by nonfatal errors. */ +int make_executable; + +/* Force the executable to be output, even if there are non-fatal + errors */ +int force_executable; + +/* Keep a list of any symbols referenced from the command line (so + that error messages for these guys can be generated). This list is + zero terminated. */ +struct glosym **cmdline_references; +int cl_refs_allocated; + +#ifndef bcopy +void bcopy (), bzero (); +#endif +char *malloc (), *realloc (); +void free (); + +char *xmalloc (); +char *xrealloc (); +void usage (); +void fatal (); +void fatal_with_file (); +void perror_name (); +void perror_file (); +void error (); + +int parse (); +void initialize_text_start (); +void initialize_data_start (); +void digest_symbols (); +void print_symbols (); +void load_symbols (); +void decode_command (); +void list_undefined_symbols (); +void list_unresolved_references (); +void write_output (); +void write_header (); +void write_text (); +void read_file_relocation (); +void write_data (); +void write_rel (); +void write_syms (); +void write_symsegs (); +void mywrite (); +void symtab_init (); +void padfile (); +char *concat (); +char *get_file_name (); +symbol *getsym (), *getsym_soft (); + +int +main (argc, argv) + char **argv; + int argc; +{ + page_size = getpagesize (); + progname = argv[0]; + +#ifdef RLIMIT_STACK + /* Avoid dumping core on large .o files. */ + { + struct rlimit rl; + + getrlimit (RLIMIT_STACK, &rl); + rl.rlim_cur = rl.rlim_max; + setrlimit (RLIMIT_STACK, &rl); + } +#endif + + /* Clear the cumulative info on the output file. */ + + text_size = 0; + data_size = 0; + bss_size = 0; + text_reloc_size = 0; + data_reloc_size = 0; + + data_pad = 0; + text_pad = 0; + + /* Initialize the data about options. */ + + specified_data_size = 0; + strip_symbols = STRIP_NONE; + trace_files = 0; + discard_locals = DISCARD_NONE; + entry_symbol = 0; + write_map = 0; + force_common_definition = 0; + T_flag_specified = 0; + Tdata_flag_specified = 0; + make_executable = 1; + force_executable = 0; + set_element_prefixes = 0; + + /* Initialize the cumulative counts of symbols. */ + + local_sym_count = 0; + non_L_local_sym_count = 0; + debugger_sym_count = 0; + undefined_global_sym_count = 0; + set_symbol_count = 0; + set_vector_count = 0; + global_indirect_count = 0; + warning_count = 0; + multiple_def_count = 0; + common_defined_global_count = 0; + + /* Keep a list of symbols referenced from the command line */ + + cl_refs_allocated = 10; + cmdline_references + = (struct glosym **) xmalloc (cl_refs_allocated + * sizeof(struct glosym *)); + *cmdline_references = 0; + + /* Completely decode ARGV. */ + + decode_command (argc, argv); + + /* Load symbols of all input files. + Also search all libraries and decide which library members to load. */ + + load_symbols (); + + /* Create various built-in symbols. This must occur after + all input files are loaded so that a user program can have a + symbol named etext (for example). */ + + if (output_style != OUTPUT_RELOCATABLE) + symtab_init (); + + /* Compute where each file's sections go, and relocate symbols. */ + + digest_symbols (); + + /* Print error messages for any missing symbols, for any warning + symbols, and possibly multiple definitions */ + + do_warnings (stderr); + + /* Print a map, if requested. */ + + if (write_map) print_symbols (stdout); + + /* Write the output file. */ + + if (make_executable || force_executable) + write_output (); + + exit (!make_executable); +} + +void add_cmdline_ref (); + +static struct option longopts[] = +{ + {"d", 0, 0, 'd'}, + {"dc", 0, 0, 'd'}, /* For Sun compatibility. */ + {"dp", 0, 0, 'd'}, /* For Sun compatibility. */ + {"e", 1, 0, 'e'}, + {"n", 0, 0, 'n'}, + {"noinhibit-exec", 0, 0, 130}, + {"nostdlib", 0, 0, 133}, + {"o", 1, 0, 'o'}, + {"r", 0, 0, 'r'}, + {"s", 0, 0, 's'}, + {"t", 0, 0, 't'}, + {"u", 1, 0, 'u'}, + {"x", 0, 0, 'x'}, + {"z", 0, 0, 'z'}, + {"A", 1, 0, 'A'}, + {"Bstatic", 0, 0, 129}, /* For Sun compatibility. */ + {"D", 1, 0, 'D'}, + {"M", 0, 0, 'M'}, + {"N", 0, 0, 'N'}, + {"S", 0, 0, 'S'}, + {"T", 1, 0, 'T'}, + {"Ttext", 1, 0, 'T'}, + {"Tdata", 1, 0, 132}, + {"V", 1, 0, 'V'}, + {"X", 0, 0, 'X'}, + {0, 0, 0, 0} +}; + +/* Since the Unix ld accepts -lfoo, -Lfoo, and -yfoo, we must also. + This effectively prevents any long options from starting with + one of these letters. */ +#define SHORTOPTS "-l:y:L:" + +/* Process the command arguments, + setting up file_table with an entry for each input file, + and setting variables according to the options. */ + +void +decode_command (argc, argv) + char **argv; + int argc; +{ + int optc, longind; + register struct file_entry *p; + + number_of_files = 0; + output_filename = "a.out"; + + n_search_dirs = 0; + search_dirs = (char **) xmalloc (sizeof (char *)); + + /* First compute number_of_files so we know how long to make file_table. + Also process most options completely. */ + + while ((optc = getopt_long_only (argc, argv, SHORTOPTS, longopts, &longind)) + != EOF) + { + if (optc == 0) + optc = longopts[longind].val; + + switch (optc) + { + case '?': + usage (0, 0); + break; + + case 1: + /* Non-option argument. */ + number_of_files++; + break; + + case 'd': + force_common_definition = 1; + break; + + case 'e': + entry_symbol = getsym (optarg); + if (!entry_symbol->defined && !entry_symbol->referenced) + undefined_global_sym_count++; + entry_symbol->referenced = 1; + add_cmdline_ref (entry_symbol); + break; + + case 'l': + number_of_files++; + break; + + case 'n': + if (output_style && output_style != OUTPUT_READONLY_TEXT) + fatal ("illegal combination of -n with -N, -r, or -z", (char *) 0); + output_style = OUTPUT_READONLY_TEXT; + break; + + case 130: /* -noinhibit-exec */ + force_executable = 1; + break; + + case 133: /* -nostdlib */ + no_standard_dirs = 1; + break; + + case 'o': + output_filename = optarg; + break; + + case 'r': + if (output_style && output_style != OUTPUT_RELOCATABLE) + fatal ("illegal combination of -r with -N, -n, or -z", (char *) 0); + output_style = OUTPUT_RELOCATABLE; + text_start = 0; + break; + + case 's': + strip_symbols = STRIP_ALL; + break; + + case 't': + trace_files = 1; + break; + + case 'u': + { + register symbol *sp = getsym (optarg); + + if (!sp->defined && !sp->referenced) + undefined_global_sym_count++; + sp->referenced = 1; + add_cmdline_ref (sp); + } + break; + + case 'x': + discard_locals = DISCARD_ALL; + break; + + case 'y': + { + register symbol *sp = getsym (optarg); + + sp->trace = 1; + } + break; + + case 'z': + if (output_style && output_style != OUTPUT_DEMAND_PAGED) + fatal ("illegal combination of -z with -N, -n, or -r", (char *) 0); + output_style = OUTPUT_DEMAND_PAGED; + break; + + case 'A': + number_of_files++; + break; + + case 129: /* -Bstatic. */ + /* Ignore. */ + break; + + case 'D': + specified_data_size = parse (optarg, "%x", "invalid argument to -D"); + break; + + case 'L': + n_search_dirs++; + search_dirs = (char **) + xrealloc (search_dirs, n_search_dirs * sizeof (char *)); + search_dirs[n_search_dirs - 1] = optarg; + break; + + case 'M': + write_map = 1; + break; + + case 'N': + if (output_style && output_style != OUTPUT_WRITABLE_TEXT) + fatal ("illegal combination of -N with -n, -r, or -z", (char *) 0); + output_style = OUTPUT_WRITABLE_TEXT; + break; + + case 'S': + strip_symbols = STRIP_DEBUGGER; + break; + + case 'T': + text_start = parse (optarg, "%x", "invalid argument to -Ttext"); + T_flag_specified = 1; + break; + + case 132: /* -Tdata addr */ + data_start = parse (optarg, "%x", "invalid argument to -Tdata"); + Tdata_flag_specified = 1; + break; + + case 'V': + { + struct string_list_element *new + = (struct string_list_element *) + xmalloc (sizeof (struct string_list_element)); + + new->str = optarg; + new->next = set_element_prefixes; + set_element_prefixes = new; + } + break; + + case 'X': + discard_locals = DISCARD_L; + break; + } + } + + if (!number_of_files) + usage ("no input files", 0); + + p = file_table + = (struct file_entry *) xmalloc (number_of_files * sizeof (struct file_entry)); + bzero (p, number_of_files * sizeof (struct file_entry)); + + /* Now scan again and fill in file_table. + All options except -A and -l are ignored here. */ + + optind = 0; /* Reset getopt. */ + while ((optc = getopt_long_only (argc, argv, SHORTOPTS, longopts, &longind)) + != EOF) + { + if (optc == 0) + optc = longopts[longind].val; + + switch (optc) + { + case 1: + /* Non-option argument. */ + p->filename = optarg; + p->local_sym_name = optarg; + p++; + break; + + case 'A': + if (p != file_table) + usage ("-A specified before an input file other than the first"); + p->filename = optarg; + p->local_sym_name = optarg; + p->just_syms_flag = 1; + p++; + break; + + case 'l': + p->filename = concat ("lib", optarg, ".a"); + p->local_sym_name = concat ("-l", optarg, ""); + p->search_dirs_flag = 1; + p++; + break; + } + } + + if (!output_file_type) + output_file_type = DEFAULT_OUTPUT_FILE_TYPE; + + if (!output_style) + output_style = DEFAULT_OUTPUT_STYLE; + +#if 0 + /* THIS CONSISTENCY CHECK BELONGS SOMEWHERE ELSE. */ + /* Now check some option settings for consistency. */ + + if ((output_style == OUTPUT_READONLY_TEXT || output_style == OUTPUT_DEMAND_PAGED) + && (text_start - text_start_alignment) & (page_size - 1)) + usage ("-T argument not multiple of page size, with sharable output", 0); +#endif + + /* Append the standard search directories to the user-specified ones. */ + if (!no_standard_dirs) + { + int n = sizeof standard_search_dirs / sizeof standard_search_dirs[0]; + n_search_dirs += n; + search_dirs + = (char **) xrealloc (search_dirs, n_search_dirs * sizeof (char *)); + bcopy (standard_search_dirs, &search_dirs[n_search_dirs - n], + n * sizeof (char *)); + } +} + + +void +add_cmdline_ref (sp) + struct glosym *sp; +{ + struct glosym **ptr; + + for (ptr = cmdline_references; + ptr < cmdline_references + cl_refs_allocated && *ptr; + ptr++) + ; + + if (ptr >= cmdline_references + cl_refs_allocated - 1) + { + int diff = ptr - cmdline_references; + + cl_refs_allocated *= 2; + cmdline_references = (struct glosym **) + xrealloc (cmdline_references, + cl_refs_allocated * sizeof (struct glosym *)); + ptr = cmdline_references + diff; + } + + *ptr++ = sp; + *ptr = (struct glosym *) 0; +} + +int +set_element_prefixed_p (name) + char *name; +{ + struct string_list_element *p; + int i; + + for (p = set_element_prefixes; p; p = p->next) + { + for (i = 0; p->str[i] != '\0' && (p->str[i] == name[i]); i++) + ; + + if (p->str[i] == '\0') + return 1; + } + return 0; +} + +/* Convenient functions for operating on one or all files being + loaded. */ +void print_file_name (); + +/* Call FUNCTION on each input file entry. + Do not call for entries for libraries; + instead, call once for each library member that is being loaded. + + FUNCTION receives two arguments: the entry, and ARG. */ + +void +each_file (function, arg) + register void (*function)(); + register int arg; +{ + register int i; + + for (i = 0; i < number_of_files; i++) + { + register struct file_entry *entry = &file_table[i]; + if (entry->library_flag) + { + register struct file_entry *subentry = entry->subfiles; + for (; subentry; subentry = subentry->chain) + (*function) (subentry, arg); + } + else + (*function) (entry, arg); + } +} + +/* Call FUNCTION on each input file entry until it returns a non-zero + value. Return this value. + Do not call for entries for libraries; + instead, call once for each library member that is being loaded. + + FUNCTION receives two arguments: the entry, and ARG. It must be a + function returning unsigned long (though this can probably be fudged). */ + +unsigned long +check_each_file (function, arg) + register unsigned long (*function)(); + register int arg; +{ + register int i; + register unsigned long return_val; + + for (i = 0; i < number_of_files; i++) + { + register struct file_entry *entry = &file_table[i]; + if (entry->library_flag) + { + register struct file_entry *subentry = entry->subfiles; + for (; subentry; subentry = subentry->chain) + if (return_val = (*function) (subentry, arg)) + return return_val; + } + else + if (return_val = (*function) (entry, arg)) + return return_val; + } + return 0; +} + +/* Like `each_file' but ignore files that were just for symbol definitions. */ + +void +each_full_file (function, arg) + register void (*function)(); + register int arg; +{ + register int i; + + for (i = 0; i < number_of_files; i++) + { + register struct file_entry *entry = &file_table[i]; + if (entry->just_syms_flag) + continue; + if (entry->library_flag) + { + register struct file_entry *subentry = entry->subfiles; + for (; subentry; subentry = subentry->chain) + (*function) (subentry, arg); + } + else + (*function) (entry, arg); + } +} + +/* Close the input file that is now open. */ + +void +file_close () +{ + close (input_desc); + input_desc = 0; + input_file = 0; +} + +/* Open the input file specified by 'entry', and return a descriptor. + The open file is remembered; if the same file is opened twice in a row, + a new open is not actually done. */ + +int +file_open (entry) + register struct file_entry *entry; +{ + register int desc; + + if (entry->superfile) + return file_open (entry->superfile); + + if (entry == input_file) + return input_desc; + + if (input_file) file_close (); + + if (entry->search_dirs_flag && n_search_dirs) + { + int i; + + for (i = 0; i < n_search_dirs; i++) + { + register char *string + = concat (search_dirs[i], "/", entry->filename); + desc = open (string, O_RDONLY, 0); + if (desc > 0) + { + entry->filename = string; + entry->search_dirs_flag = 0; + break; + } + free (string); + } + } + else + desc = open (entry->filename, O_RDONLY, 0); + + if (desc > 0) + { + input_file = entry; + input_desc = desc; + return desc; + } + + perror_file (entry); + /* NOTREACHED */ +} + +/* Print the filename of ENTRY on OUTFILE (a stdio stream), + and then a newline. */ + +void +prline_file_name (entry, outfile) + struct file_entry *entry; + FILE *outfile; +{ + print_file_name (entry, outfile); + fprintf (outfile, "\n"); +} + +/* Print the filename of ENTRY on OUTFILE (a stdio stream). */ + +void +print_file_name (entry, outfile) + struct file_entry *entry; + FILE *outfile; +{ + if (entry->superfile) + { + print_file_name (entry->superfile, outfile); + fprintf (outfile, "(%s)", entry->filename); + } + else + fprintf (outfile, "%s", entry->filename); +} + +/* Return the filename of entry as a string (malloc'd for the purpose) */ + +char * +get_file_name (entry) + struct file_entry *entry; +{ + char *result, *supfile; + if (entry->superfile) + { + supfile = get_file_name (entry->superfile); + result = (char *) xmalloc (strlen (supfile) + + strlen (entry->filename) + 3); + sprintf (result, "%s(%s)", supfile, entry->filename); + free (supfile); + } + else + { + result = (char *) xmalloc (strlen (entry->filename) + 1); + strcpy (result, entry->filename); + } + return result; +} + +/* Medium-level input routines for rel files. */ + +/* Determine whether the given ENTRY is an archive, a BSD a.out file, + a Mach-O file, or whatever. DESC is the descriptor on which the + file is open. */ +void +deduce_file_type(desc, entry) + int desc; + struct file_entry *entry; +{ + int len; + + { + char magic[SARMAG]; + + lseek (desc, entry->starting_offset, 0); + len = read (desc, magic, SARMAG); + if (len == SARMAG && !strncmp(magic, ARMAG, SARMAG)) + { + entry->file_type = IS_ARCHIVE; + return; + } +#ifdef LARMAG + /* Odd hack for Tektronix. */ + if (len == SARMAG && !strncmp(magic, LARMAG, SARMAG)) + { + entry->file_type = IS_ARCHIVE; + return; + } +#endif + } + +#ifdef A_OUT + { + struct exec hdr; + + lseek (desc, entry->starting_offset, 0); +#ifdef COFF_ENCAPSULATE + if (entry->just_syms_flag) + /* Since a file given with -A will have a coff header, unlike normal + input files, we need to skip over it. */ + lseek (desc, sizeof (struct coffheader), SEEK_CUR); +#endif + len = read (desc, (char *) &hdr, sizeof (struct exec)); + if (len == sizeof (struct exec) && !N_BADMAG (hdr)) + { + entry->file_type = IS_A_OUT; + return; + } + } +#endif + +#ifdef MACH_O + { + struct mach_header hdr; + + lseek (desc, entry->starting_offset, 0); + len = read (desc, (char *) &hdr, sizeof (struct mach_header)); + if (len == sizeof (struct mach_header) && hdr.magic == MH_MAGIC) + { + entry->file_type = IS_MACH_O; + return; + } + } +#endif + + fatal_with_file ("malformed input file (not rel or archive) ", entry); +} + +#ifdef A_OUT +/* Read an a.out file's header and set up the fields of + the ENTRY accordingly. DESC is the descriptor on which + the file is open. */ +void +read_a_out_header (desc, entry) + int desc; + struct file_entry *entry; +{ + register int len; + struct exec hdr; + struct stat st; + + lseek (desc, entry->starting_offset, 0); + +#ifdef COFF_ENCAPSULATE + if (entry->just_syms_flag) + /* Since a file given with -A will have a coff header, unlike normal + input files, we need to skip over it. */ + lseek (desc, sizeof (struct coffheader), SEEK_CUR); +#endif + + read (desc, (char *) &hdr, sizeof (struct exec)); + +#ifdef READ_HEADER_HOOK + READ_HEADER_HOOK(hdr.a_machtype); +#endif + + if (entry->just_syms_flag) + entry->orig_text_address = N_TXTADDR(hdr); + else + entry->orig_text_address = 0; + entry->text_size = hdr.a_text; + entry->text_offset = N_TXTOFF(hdr); + + entry->text_reloc_size = hdr.a_trsize; +#ifdef N_TRELOFF + entry->text_reloc_offset = N_TRELOFF(hdr); +#else +#ifdef N_DATOFF + entry->text_reloc_offset = N_DATOFF(hdr) + hdr.a_data; +#else + entry->text_reloc_offset = N_TXTOFF(hdr) + hdr.a_text + hdr.a_data; +#endif +#endif + + if (entry->just_syms_flag) + entry->orig_data_address = N_DATADDR(hdr); + else + entry->orig_data_address = entry->text_size; + entry->data_size = hdr.a_data; +#ifdef N_DATOFF + entry->data_offset = N_DATOFF(hdr); +#else + entry->data_offset = N_TXTOFF(hdr) + hdr.a_text; +#endif + +#ifdef NMAGIC + /* If this is an NMAGIC file, then even though the text and data + are contiguous in the input file, the data has _already_ been + relocated to the next page boundary. + Change orig_data_address to reflect this. */ +#ifdef N_MAGIC + if (N_MAGIC(hdr) == NMAGIC) +#else + if (hdr.a_magic == NMAGIC) +#endif + entry->orig_data_address += (((hdr.a_text + page_size - 1) + & ~(page_size - 1)) - hdr.a_text); +#endif + + entry->data_reloc_size = hdr.a_drsize; +#ifdef N_DRELOFF + entry->data_reloc_offset = N_DRELOFF(hdr); +#else + entry->data_reloc_offset = entry->text_reloc_offset + entry->text_reloc_size; +#endif + +#ifdef N_BSSADDR + if (entry->just_syms_flag) + entry->orig_bss_address = N_BSSADDR(hdr); + else +#endif + entry->orig_bss_address = entry->orig_data_address + entry->data_size; + entry->bss_size = hdr.a_bss; + + entry->syms_size = hdr.a_syms; + entry->syms_offset = N_SYMOFF(hdr); + entry->strs_offset = N_STROFF(hdr); + lseek(desc, entry->starting_offset + entry->strs_offset, 0); + if (read(desc, (char *) &entry->strs_size, sizeof (unsigned long int)) + != sizeof (unsigned long int)) + fatal_with_file ("failure reading string table size of ", entry); + + if (!entry->superfile) + { + fstat(desc, &st); + if (st.st_size > entry->strs_offset + entry->strs_size) + { + entry->symseg_size = st.st_size - (entry->strs_offset + entry->strs_size); + entry->symseg_offset = entry->strs_offset + entry->strs_size; + } + } + else + if (entry->total_size > entry->strs_offset + entry->strs_size) + { + entry->symseg_size = entry->total_size - (entry->strs_offset + entry->strs_size); + entry->symseg_offset = entry->strs_offset + entry->strs_size; + } +} +#endif + +#ifdef MACH_O +/* Read a Mach-O file's header. DESC is the descriptor on which the + file is open, and ENTRY is the file's entry. */ +void +read_mach_o_header (desc, entry) + int desc; + struct file_entry *entry; +{ + struct mach_header mach_header; + char *hdrbuf; + struct load_command *load_command; + struct segment_command *segment_command; + struct section *section; + struct symtab_command *symtab_command; +#ifdef LC_SYMSEG + struct symseg_command *symseg_command; +#endif; + int ordinal; + int symtab_seen, symseg_seen; + int len, cmd, seg; + + entry->text_ordinal = entry->data_ordinal = entry->bss_ordinal = 0; + symtab_seen = symseg_seen = 0; + ordinal = 1; + + lseek (desc, entry->starting_offset, 0); + len = read (desc, (char *) &mach_header, sizeof (struct mach_header)); + if (len != sizeof (struct mach_header)) + fatal_with_file ("failure reading Mach-O header of ", entry); + if (mach_header.filetype != MH_OBJECT && mach_header.filetype != MH_EXECUTE) + fatal_with_file ("unsupported Mach-O file type (not MH_OBJECT or MH_EXECUTE) in ", entry); + hdrbuf = xmalloc (mach_header.sizeofcmds); + len = read (desc, hdrbuf, mach_header.sizeofcmds); + if (len != mach_header.sizeofcmds) + fatal_with_file ("failure reading Mach-O load commands of ", entry); + load_command = (struct load_command *) hdrbuf; + for (cmd = 0; cmd < mach_header.ncmds; ++cmd) + { + switch (load_command->cmd) + { + case LC_SEGMENT: + segment_command = (struct segment_command *) load_command; + section = (struct section *) ((char *) (segment_command + 1)); + for (seg = 0; seg < segment_command->nsects; ++seg, ++section, ++ordinal) + { + if (!strncmp(SECT_TEXT, section->sectname, sizeof section->sectname)) + if (entry->text_ordinal) + fatal_with_file ("more than one __text section in ", entry); + else + { + entry->text_ordinal = ordinal; + entry->orig_text_address = section->addr; + entry->text_size = section->size; + entry->text_offset = section->offset; + entry->text_reloc_size = section->nreloc * sizeof (struct relocation_info); + entry->text_reloc_offset = section->reloff; + } + else if (!strncmp(SECT_DATA, section->sectname, sizeof section->sectname)) + if (entry->data_ordinal) + fatal_with_file ("more than one __data section in ", entry); + else + { + entry->data_ordinal = ordinal; + entry->orig_data_address = section->addr; + entry->data_size = section->size; + entry->data_offset = section->offset; + entry->data_reloc_size = section->nreloc * sizeof (struct relocation_info); + entry->data_reloc_offset = section->reloff; + } + else if (!strncmp(SECT_BSS, section->sectname, sizeof section->sectname)) + if (entry->bss_ordinal) + fatal_with_file ("more than one __bss section in ", entry); + else + { + entry->bss_ordinal = ordinal; + entry->orig_bss_address = section->addr; + entry->bss_size = section->size; + } + else + if (section->size != 0) + fprintf (stderr, "%s: warning: unknown section `%.*s' in %s\n", + progname, sizeof section->sectname, section->sectname, + entry->filename); + } + break; + case LC_SYMTAB: + if (symtab_seen) + fatal_with_file ("more than one LC_SYMTAB in ", entry); + else + { + symtab_seen = 1; + symtab_command = (struct symtab_command *) load_command; + entry->syms_size = symtab_command->nsyms * sizeof (struct nlist); + entry->syms_offset = symtab_command->symoff; + entry->strs_size = symtab_command->strsize; + entry->strs_offset = symtab_command->stroff; + } + break; +#ifdef LC_SYMSEG + case LC_SYMSEG: + if (symseg_seen) + fatal_with_file ("more than one LC_SYMSEG in ", entry); + else + { + symseg_seen = 1; + symseg_command = (struct symseg_command *) load_command; + entry->symseg_size = symseg_command->size; + entry->symseg_offset = symseg_command->offset; + } + break; +#endif + } + load_command = (struct load_command *) + ((char *) load_command + load_command->cmdsize); + } + + free (hdrbuf); + + if (!symtab_seen) + fprintf (stderr, "%s: no symbol table in %s\n", progname, entry->filename); +} +#endif + +/* Read a file's header info into the proper place in the file_entry. + DESC is the descriptor on which the file is open. + ENTRY is the file's entry. + Switch in the file_type to determine the appropriate actual + header reading routine to call. */ + +void +read_header (desc, entry) + int desc; + register struct file_entry *entry; +{ + if (!entry->file_type) + deduce_file_type (desc, entry); + + switch (entry->file_type) + { + case IS_ARCHIVE: + default: + /* Should never happen. */ + abort (); + +#ifdef A_OUT + case IS_A_OUT: + read_a_out_header (desc, entry); + break; +#endif + +#ifdef MACH_O + case IS_MACH_O: + read_mach_o_header (desc, entry); + break; +#endif + } + + entry->header_read_flag = 1; +} + +#ifdef MACH_O +void translate_mach_o_symbols (); +#endif + +/* Read the symbols of file ENTRY into core. + Assume it is already open, on descriptor DESC. */ + +void +read_entry_symbols (desc, entry) + struct file_entry *entry; + int desc; +{ + int str_size; + + if (!entry->header_read_flag) + read_header (desc, entry); + + entry->symbols = (struct nlist *) xmalloc (entry->syms_size); + + lseek (desc, entry->syms_offset + entry->starting_offset, 0); + if (entry->syms_size != read (desc, entry->symbols, entry->syms_size)) + fatal_with_file ("premature end of file in symbols of ", entry); + +#ifdef MACH_O + if (entry->file_type == IS_MACH_O) + translate_mach_o_symbols (entry); +#endif +} + +/* Read the string table of file ENTRY into core. + Assume it is already open, on descriptor DESC. */ + +void +read_entry_strings (desc, entry) + struct file_entry *entry; + int desc; +{ + int buffer; + + if (!entry->header_read_flag) + read_header (desc, entry); + + lseek (desc, entry->strs_offset + entry->starting_offset, 0); + if (entry->strs_size != read (desc, entry->strings, entry->strs_size)) + fatal_with_file ("premature end of file in strings of ", entry); +} + +/* Read in the symbols of all input files. */ + +void read_file_symbols (), read_entry_symbols (), read_entry_strings (); +void enter_file_symbols (), enter_global_ref (), search_library (); + +void +load_symbols () +{ + register int i; + + if (trace_files) fprintf (stderr, "Loading symbols:\n\n"); + + for (i = 0; i < number_of_files; i++) + { + register struct file_entry *entry = &file_table[i]; + read_file_symbols (entry); + } + + if (trace_files) fprintf (stderr, "\n"); +} + +/* If ENTRY is a rel file, read its symbol and string sections into core. + If it is a library, search it and load the appropriate members + (which means calling this function recursively on those members). */ + +void +read_file_symbols (entry) + register struct file_entry *entry; +{ + register int desc; + + desc = file_open (entry); + + if (!entry->file_type) + deduce_file_type (desc, entry); + + if (entry->file_type == IS_ARCHIVE) + { + entry->library_flag = 1; + search_library (desc, entry); + } + else + { + read_entry_symbols (desc, entry); + entry->strings = (char *) alloca (entry->strs_size); + read_entry_strings (desc, entry); + enter_file_symbols (entry); + entry->strings = 0; + } + + file_close (); +} + +/* Enter the external symbol defs and refs of ENTRY in the hash table. */ + +void +enter_file_symbols (entry) + struct file_entry *entry; +{ + register struct nlist + *p, + *end = entry->symbols + entry->syms_size / sizeof (struct nlist); + + if (trace_files) prline_file_name (entry, stderr); + + for (p = entry->symbols; p < end; p++) + { + if (p->n_type == (N_SETV | N_EXT)) continue; + if (set_element_prefixes + && set_element_prefixed_p (p->n_un.n_strx + entry->strings)) + p->n_type += (N_SETA - N_ABS); + + if (SET_ELEMENT_P (p->n_type)) + { + set_symbol_count++; + if (output_style != OUTPUT_RELOCATABLE) + enter_global_ref (p, p->n_un.n_strx + entry->strings, entry); + } + else if (p->n_type == N_WARNING) + { + char *name = p->n_un.n_strx + entry->strings; + + /* Grab the next entry. */ + p++; + if (p->n_type != (N_UNDF | N_EXT)) + { + fprintf (stderr, "%s: Warning symbol found in %s without external reference following.\n", + progname, entry->filename); + make_executable = 0; + p--; /* Process normally. */ + } + else + { + symbol *sp; + char *sname = p->n_un.n_strx + entry->strings; + /* Deal with the warning symbol. */ + enter_global_ref (p, p->n_un.n_strx + entry->strings, entry); + sp = getsym (sname); + sp->warning = (char *) xmalloc (strlen(name) + 1); + strcpy (sp->warning, name); + warning_count++; + } + } + else if (p->n_type & N_EXT) + enter_global_ref (p, p->n_un.n_strx + entry->strings, entry); + else if (p->n_un.n_strx && !(p->n_type & (N_STAB | N_EXT))) + { + if ((p->n_un.n_strx + entry->strings)[0] != LPREFIX) + non_L_local_sym_count++; + local_sym_count++; + } + else debugger_sym_count++; + } + + /* Count one for the local symbol that we generate, + whose name is the file's name (usually) and whose address + is the start of the file's text. */ + + local_sym_count++; + non_L_local_sym_count++; +} + +/* Enter one global symbol in the hash table. + NLIST_P points to the `struct nlist' read from the file + that describes the global symbol. NAME is the symbol's name. + ENTRY is the file entry for the file the symbol comes from. + + The `struct nlist' is modified by placing it on a chain of + all such structs that refer to the same global symbol. + This chain starts in the `refs' field of the symbol table entry + and is chained through the `n_name'. */ + +void +enter_global_ref (nlist_p, name, entry) + register struct nlist *nlist_p; + char *name; + struct file_entry *entry; +{ + register symbol *sp = getsym (name); + register int type = nlist_p->n_type; + int oldref = sp->referenced; + int olddef = sp->defined; + + nlist_p->n_un.n_name = (char *) sp->refs; + sp->refs = nlist_p; + + sp->referenced = 1; + sp->last_library_ref = entry->superfile; + + if (type != (N_UNDF | N_EXT) || nlist_p->n_value) + { + if (!sp->defined || sp->defined == (N_UNDF | N_EXT)) + sp->defined = type; + + if (oldref && !olddef) + /* It used to be undefined and we're defining it. */ + undefined_global_sym_count--; + + if (!olddef && type == (N_UNDF | N_EXT) && nlist_p->n_value) + { + /* First definition and it's common. */ + common_defined_global_count++; + sp->max_common_size = nlist_p->n_value; + } + else if (olddef && sp->max_common_size && type != (N_UNDF | N_EXT)) + { + /* It used to be common and we're defining it as + something else. */ + common_defined_global_count--; + sp->max_common_size = 0; + } + else if (olddef && sp->max_common_size && type == (N_UNDF | N_EXT) + && sp->max_common_size < nlist_p->n_value) + /* It used to be common and this is a new common entry to + which we need to pay attention. */ + sp->max_common_size = nlist_p->n_value; + + /* Are we defining it as a set element? */ + if (SET_ELEMENT_P (type) + && (!olddef || (olddef && sp->max_common_size))) + set_vector_count++; + /* As an indirection? */ + else if (type == (N_INDR | N_EXT)) + { + /* Indirect symbols value should be modified to point + a symbol being equivalenced to. */ + nlist_p->n_value +#ifndef NeXT + = (unsigned int) getsym ((nlist_p + 1)->n_un.n_strx + + entry->strings); +#else + /* NeXT also has indirection but they do it weirdly. */ + = (unsigned int) getsym (nlist_p->n_value + entry->strings); +#endif + if ((symbol *) nlist_p->n_value == sp) + { + /* Somebody redefined a symbol to be itself. */ + fprintf (stderr, "%s: Symbol %s indirected to itself.\n", + entry->filename, name); + /* Rewrite this symbol as being a global text symbol + with value 0. */ + nlist_p->n_type = sp->defined = N_TEXT | N_EXT; + nlist_p->n_value = 0; + /* Don't make the output executable. */ + make_executable = 0; + } + else + global_indirect_count++; + } + } + else + if (!oldref) +#ifndef DOLLAR_KLUDGE + undefined_global_sym_count++; +#else + { + if (entry->superfile && type == (N_UNDF | N_EXT) && name[1] == '$') + { + /* This is an (ISI?) $-conditional; skip it */ + sp->referenced = 0; + if (sp->trace) + { + fprintf (stderr, "symbol %s is a $-conditional ignored in ", sp->name); + print_file_name (entry, stderr); + fprintf (stderr, "\n"); + } + return; + } + else + undefined_global_sym_count++; + } +#endif + + if (sp == end_symbol && entry->just_syms_flag && !T_flag_specified) + text_start = nlist_p->n_value; + + if (sp->trace) + { + register char *reftype; + switch (type & ~N_EXT) + { + case N_UNDF: + if (nlist_p->n_value) + reftype = "defined as common"; + else reftype = "referenced"; + break; + + case N_ABS: + reftype = "defined as absolute"; + break; + + case N_TEXT: + reftype = "defined in text section"; + break; + + case N_DATA: + reftype = "defined in data section"; + break; + + case N_BSS: + reftype = "defined in BSS section"; + break; + + case N_SETT: + reftype = "is a text set element"; + break; + + case N_SETD: + reftype = "is a data set element"; + break; + + case N_SETB: + reftype = "is a BSS set element"; + break; + + case N_SETA: + reftype = "is an absolute set element"; + break; + + case N_SETV: + reftype = "defined in data section as vector"; + break; + + case N_INDR: + reftype = (char *) alloca (23 + strlen (((symbol *) nlist_p->n_value)->name)); + sprintf (reftype, "defined equivalent to %s", + ((symbol *) nlist_p->n_value)->name); + break; + +#ifdef sequent + case N_SHUNDF: + reftype = "shared undf"; + break; + +/* These conflict with cases above. + case N_SHDATA: + reftype = "shared data"; + break; + + case N_SHBSS: + reftype = "shared BSS"; + break; +*/ +#endif + + default: + reftype = "I don't know this type"; + break; + } + + fprintf (stderr, "symbol %s %s in ", sp->name, reftype); + print_file_name (entry, stderr); + fprintf (stderr, "\n"); + } +} + +/* This return 0 if the given file entry's symbol table does *not* + contain the nlist point entry, and it returns the files entry + pointer (cast to unsigned long) if it does. */ + +unsigned long +contains_symbol (entry, n_ptr) + struct file_entry *entry; + register struct nlist *n_ptr; +{ + if (n_ptr >= entry->symbols && + n_ptr < (entry->symbols + + (entry->syms_size / sizeof (struct nlist)))) + return (unsigned long) entry; + return 0; +} + + +/* Searching libraries */ + +struct file_entry *decode_library_subfile (); +void linear_library (), symdef_library (); + +/* Search the library ENTRY, already open on descriptor DESC. + This means deciding which library members to load, + making a chain of `struct file_entry' for those members, + and entering their global symbols in the hash table. */ + +void +search_library (desc, entry) + int desc; + struct file_entry *entry; +{ + int member_length; + register char *name; + register struct file_entry *subentry; + + if (!undefined_global_sym_count) return; + + /* Examine its first member, which starts SARMAG bytes in. */ + subentry = decode_library_subfile (desc, entry, SARMAG, &member_length); + if (!subentry) return; + + name = subentry->filename; + free (subentry); + + /* Search via __.SYMDEF if that exists, else linearly. */ + + if (!strcmp (name, "__.SYMDEF")) + symdef_library (desc, entry, member_length); + else + linear_library (desc, entry); +} + +/* Construct and return a file_entry for a library member. + The library's file_entry is library_entry, and the library is open on DESC. + SUBFILE_OFFSET is the byte index in the library of this member's header. + We store the length of the member into *LENGTH_LOC. */ + +struct file_entry * +decode_library_subfile (desc, library_entry, subfile_offset, length_loc) + int desc; + struct file_entry *library_entry; + int subfile_offset; + int *length_loc; +{ + int bytes_read; + register int namelen; + int member_length; + register char *name; + struct ar_hdr hdr1; + register struct file_entry *subentry; + + lseek (desc, subfile_offset, 0); + +#ifdef LARMAG + bytes_read = getarhdr (desc, &hdr1); +#else + bytes_read = read (desc, &hdr1, sizeof hdr1); +#endif + + if (!bytes_read) + return 0; /* end of archive */ + +#ifdef LARMAG + if (bytes_read < 0) +#else + if (sizeof hdr1 != bytes_read) +#endif + fatal_with_file ("malformed library archive ", library_entry); + + if (sscanf (hdr1.ar_size, "%d", &member_length) != 1) + fatal_with_file ("malformatted header of archive member in ", library_entry); + + subentry = (struct file_entry *) xmalloc (sizeof (struct file_entry)); + bzero (subentry, sizeof (struct file_entry)); + +#ifdef LARMAG + namelen = strlen (hdr1.ar_name); +#else + for (namelen = 0; + namelen < sizeof hdr1.ar_name + && hdr1.ar_name[namelen] != 0 && hdr1.ar_name[namelen] != ' ' + && hdr1.ar_name[namelen] != '/'; + namelen++); +#endif + + name = (char *) xmalloc (namelen+1); + strncpy (name, hdr1.ar_name, namelen); + name[namelen] = 0; + + subentry->filename = name; + subentry->local_sym_name = name; + subentry->symbols = 0; + subentry->strings = 0; + subentry->subfiles = 0; +#ifdef LARMAG + /* The struct ar_hdr is packed in the file */ + /* The return value of getarhdr accounts for just part of it */ + bytes_read += sizeof hdr1 - sizeof hdr1.ar_name; + subentry->starting_offset = subfile_offset + bytes_read; +#else + subentry->starting_offset = subfile_offset + sizeof hdr1; +#endif + subentry->superfile = library_entry; + subentry->library_flag = 0; + subentry->header_read_flag = 0; + subentry->just_syms_flag = 0; + subentry->chain = 0; + subentry->total_size = member_length; + + (*length_loc) = member_length; + + return subentry; +} + +int subfile_wanted_p (); + +/* Search a library that has a __.SYMDEF member. + DESC is a descriptor on which the library is open. + The file pointer is assumed to point at the __.SYMDEF data. + ENTRY is the library's file_entry. + MEMBER_LENGTH is the length of the __.SYMDEF data. */ + +void +symdef_library (desc, entry, member_length) + int desc; + struct file_entry *entry; + int member_length; +{ + int *symdef_data = (int *) xmalloc (member_length); + register struct symdef *symdef_base; + char *sym_name_base; + int number_of_symdefs; + int length_of_strings; + int not_finished; + int bytes_read; + register int i; + struct file_entry *prev = 0; + int prev_offset = 0; + + bytes_read = read (desc, symdef_data, member_length); + if (bytes_read != member_length) + fatal_with_file ("malformatted __.SYMDEF in ", entry); + + number_of_symdefs = *symdef_data / sizeof (struct symdef); + if (number_of_symdefs < 0 || + number_of_symdefs * sizeof (struct symdef) + 2 * sizeof (int) > member_length) + fatal_with_file ("malformatted __.SYMDEF in ", entry); + + symdef_base = (struct symdef *) (symdef_data + 1); + length_of_strings = *(int *) (symdef_base + number_of_symdefs); + + if (length_of_strings < 0 + || number_of_symdefs * sizeof (struct symdef) + length_of_strings + + 2 * sizeof (int) != member_length) + fatal_with_file ("malformatted __.SYMDEF in ", entry); + + sym_name_base = sizeof (int) + (char *) (symdef_base + number_of_symdefs); + + /* Check all the string indexes for validity. */ + + for (i = 0; i < number_of_symdefs; i++) + { + register int index = symdef_base[i].symbol_name_string_index; + if (index < 0 || index >= length_of_strings + || (index && *(sym_name_base + index - 1))) + fatal_with_file ("malformatted __.SYMDEF in ", entry); + } + + /* Search the symdef data for members to load. + Do this until one whole pass finds nothing to load. */ + + not_finished = 1; + while (not_finished) + { + not_finished = 0; + + /* Scan all the symbols mentioned in the symdef for ones that we need. + Load the library members that contain such symbols. */ + + for (i = 0; + (i < number_of_symdefs + && (undefined_global_sym_count || common_defined_global_count)); + i++) + if (symdef_base[i].symbol_name_string_index >= 0) + { + register symbol *sp; + + sp = getsym_soft (sym_name_base + + symdef_base[i].symbol_name_string_index); + + /* If we find a symbol that appears to be needed, think carefully + about the archive member that the symbol is in. */ + + if (sp && ((sp->referenced && !sp->defined) + || (sp->defined && sp->max_common_size))) + { + int junk; + register int j; + register int offset = symdef_base[i].library_member_offset; + struct file_entry *subentry; + + /* Don't think carefully about any archive member + more than once in a given pass. */ + + if (prev_offset == offset) + continue; + prev_offset = offset; + + /* Read the symbol table of the archive member. */ + + subentry = decode_library_subfile (desc, entry, offset, &junk); + if (subentry == 0) + fatal ("invalid offset for %s in symbol table of %s", + sym_name_base + + symdef_base[i].symbol_name_string_index, + entry->filename); + read_entry_symbols (desc, subentry); + subentry->strings = xmalloc (subentry->strs_size); + read_entry_strings (desc, subentry); + + /* Now scan the symbol table and decide whether to load. */ + + if (!subfile_wanted_p (subentry)) + { + free (subentry->symbols); + free (subentry->strings); + free (subentry); + } + else + { + /* This member is needed; load it. + Since we are loading something on this pass, + we must make another pass through the symdef data. */ + + not_finished = 1; + + enter_file_symbols (subentry); + + if (prev) + prev->chain = subentry; + else entry->subfiles = subentry; + prev = subentry; + + /* Clear out this member's symbols from the symdef data + so that following passes won't waste time on them. */ + + for (j = 0; j < number_of_symdefs; j++) + { + if (symdef_base[j].library_member_offset == offset) + symdef_base[j].symbol_name_string_index = -1; + } + + /* We'll read the strings again if we need them again. */ + free (subentry->strings); + subentry->strings = 0; + } + } + } + } + + free (symdef_data); +} + + +/* Handle a subentry for a file with no __.SYMDEF. */ + +process_subentry (desc, subentry, entry, prev_addr) + int desc; + register struct file_entry *subentry; + struct file_entry **prev_addr, *entry; +{ + read_entry_symbols (desc, subentry); + subentry->strings = (char *) alloca (subentry->strs_size); + read_entry_strings (desc, subentry); + + if (!subfile_wanted_p (subentry)) + { + free (subentry->symbols); + free (subentry); + } + else + { + enter_file_symbols (subentry); + + if (*prev_addr) + (*prev_addr)->chain = subentry; + else + entry->subfiles = subentry; + *prev_addr = subentry; + subentry->strings = 0; /* Since space will dissapear on return */ + } +} + +/* Search a library that has no __.SYMDEF. + ENTRY is the library's file_entry. + DESC is the descriptor it is open on. */ + +void +linear_library (desc, entry) + int desc; + struct file_entry *entry; +{ + struct file_entry *prev = 0; + register int this_subfile_offset = SARMAG; + + while (undefined_global_sym_count || common_defined_global_count) + { + int member_length; + register struct file_entry *subentry; + + subentry = decode_library_subfile (desc, entry, this_subfile_offset, + &member_length); + if (!subentry) return; + + process_subentry (desc, subentry, entry, &prev); +#ifdef LARMAG + this_subfile_offset = member_length + subentry->starting_offset; +#else + this_subfile_offset += member_length + sizeof (struct ar_hdr); +#endif + if (this_subfile_offset & 1) this_subfile_offset++; + } +} + +/* ENTRY is an entry for a library member. + Its symbols have been read into core, but not entered. + Return nonzero if we ought to load this member. */ + +int +subfile_wanted_p (entry) + struct file_entry *entry; +{ + register struct nlist *p; + register struct nlist *end + = entry->symbols + entry->syms_size / sizeof (struct nlist); +#ifdef DOLLAR_KLUDGE + register int dollar_cond = 0; +#endif + + for (p = entry->symbols; p < end; p++) + { + register int type = p->n_type; + register char *name = p->n_un.n_strx + entry->strings; + + /* If the symbol has an interesting definition, we could + potentially want it. */ + if (type & N_EXT + && (type != (N_UNDF | N_EXT) || p->n_value + +#ifdef DOLLAR_KLUDGE + || name[1] == '$' +#endif + ) + && !SET_ELEMENT_P (type) + && !set_element_prefixed_p (name)) + { + register symbol *sp = getsym_soft (name); + +#ifdef DOLLAR_KLUDGE + if (name[1] == '$') + { + sp = getsym_soft (&name[2]); + dollar_cond = 1; + if (!sp) continue; + if (sp->referenced) + { + if (write_map) + { + print_file_name (entry, stdout); + fprintf (stdout, " needed due to $-conditional %s\n", name); + } + return 1; + } + continue; + } +#endif + + /* If this symbol has not been hashed, we can't be looking for it. */ + + if (!sp) continue; + + /* Note: There has been a lot of discussion about what to + when a common definition was previously seen (i.e. when + sp->max_common_size > 0). + The latest solution is to treat a previous common definition + (wrt to subfile_wanted_p) no differently from a real definition. + This has the advantage of simplicity and consistency: a common + definition is just like a common definition (consistent + with strict ANSI C) except that we allow duplicate definitions. + Possible disadvantage: May not be the best choice for Fortran, + though it is consistent with the standard. + + An earlier solution: + We wanted to see a common definition in the subfile, + and note its size, but ignore any other definition + if the symbol was already defined (even as a common). + This meant that if there were multiple common definitions, + the final definition would use the largest size of any of them, + as it should. But if there was a common definition and another + definition, like "int pipe;" in a program and "int pipe() {}" + in the library, only the common would be used. + Disadvantage: a poorly justified kludge. + + Another previous solution: + If the symbol already had a definition as a common symbol, + we would want this subfile if some other subfile of the + same library that we already need anyway also used the symbol. + This seemed like an even more ad hoc decision. + It would also cause subfiles to be pulled in that would + then conflict with previous entries. I.e. you couldn't + have: ld ... start.o libc.a ... if libc.a contained start.o. + + Other hybrid solutions were also considered. + */ + if ((sp->referenced && !sp->defined)) + { +#ifdef DOLLAR_KLUDGE + if (dollar_cond) continue; +#endif + if (type == (N_UNDF | N_EXT)) + { + /* Symbol being defined as common. + Remember this, but don't load subfile just for this. */ + + common_defined_global_count++; + sp->max_common_size = p->n_value; + undefined_global_sym_count--; + sp->defined = 1; + continue; + } + if (write_map) + { + print_file_name (entry, stdout); + fprintf (stdout, " needed due to %s\n", sp->name); + } + return 1; + } + } + } + + return 0; +} + +void consider_file_section_lengths (), relocate_file_addresses (); + +/* Having entered all the global symbols and found the sizes of sections + of all files to be linked, make all appropriate deductions from this data. + + We propagate global symbol values from definitions to references. + We compute the layout of the output file and where each input file's + contents fit into it. */ + +void +digest_symbols () +{ + register int i; + int setv_fill_count; + + if (trace_files) + fprintf (stderr, "Digesting symbol information:\n\n"); + + /* Initialize the text_start address; this depends on the output file formats. */ + + initialize_text_start (); + + text_size = text_header_size; + + /* Compute total size of sections */ + + each_file (consider_file_section_lengths, 0); + + /* If necessary, pad text section to full page in the file. + Include the padding in the text segment size. */ + + if (output_style == OUTPUT_READONLY_TEXT || output_style == OUTPUT_DEMAND_PAGED) + { +#ifdef tek4300 + int page_size = ZMALIGN; /* a lie */ +#endif +#ifdef is68k + text_pad = ((text_size + SEGMENT_SIZE - 1) & (- SEGMENT_SIZE) - text_size; +#else + text_pad = ((text_size + page_size - 1) & (- page_size)) - text_size; +#endif + text_size += text_pad; + } + + /* Now that the text_size is known, initialize the data start address; + this depends on text_size as well as the output file format. */ + + initialize_data_start (); + + /* Set up the set element vector */ + + if (output_style != OUTPUT_RELOCATABLE) + { + /* The set sector size is the number of set elements + a word + for each symbol for the length word at the beginning of the + vector, plus a word for each symbol for a zero at the end of + the vector (for incremental linking). */ + set_sect_size + = (2 * set_symbol_count + set_vector_count) * sizeof (unsigned long); + set_sect_start = data_start + data_size; + data_size += set_sect_size; + set_vectors = (unsigned long *) xmalloc (set_sect_size); + setv_fill_count = 0; + } + + /* Make sure bss starts out aligned as much as anyone can want. */ + { + int new_data_size = (data_size + sizeof(double) - 1) & ~(sizeof(double)-1); + + data_pad += new_data_size - data_size; + data_size = new_data_size; + } + + /* Compute start addresses of each file's sections and symbols. */ + + each_full_file (relocate_file_addresses, 0); + + /* Now, for each symbol, verify that it is defined globally at most once. + Put the global value into the symbol entry. + Common symbols are allocated here, in the BSS section. + Each defined symbol is given a '->defined' field + which is the correct N_ code for its definition, + except in the case of common symbols with -r. + Then make all the references point at the symbol entry + instead of being chained together. */ + + defined_global_sym_count = 0; + + for (i = 0; i < TABSIZE; i++) + { + register symbol *sp; + for (sp = symtab[i]; sp; sp = sp->link) + { + /* For each symbol */ + register struct nlist *p, *next; + int defs = 0, com = sp->max_common_size; + struct nlist *first_definition; + for (p = sp->refs; p; p = next) + { + register int type = p->n_type; + + if (SET_ELEMENT_P (type)) + { + if (output_style == OUTPUT_RELOCATABLE) + fatal ("internal: global ref to set element with -r"); + if (!defs++) + { + sp->value = set_sect_start + + setv_fill_count++ * sizeof (unsigned long); + sp->defined = N_SETV | N_EXT; + first_definition = p; + } + else if ((sp->defined & ~N_EXT) != N_SETV) + { + sp->multiply_defined = 1; + multiple_def_count++; + } + set_vectors[setv_fill_count++] = p->n_value; + } + else if ((type & N_EXT) && type != (N_UNDF | N_EXT)) + { + /* non-common definition */ + if (defs++ && sp->value != p->n_value) + { + sp->multiply_defined = 1; + multiple_def_count++; + } + sp->value = p->n_value; + sp->defined = type; + first_definition = p; + } + next = (struct nlist *) p->n_un.n_name; + p->n_un.n_name = (char *) sp; + } + /* Allocate as common if defined as common and not defined for real */ + if (com && !defs) + { + if (output_style != OUTPUT_RELOCATABLE || force_common_definition) + { + int align = sizeof (int); + + /* Round up to nearest sizeof (int). I don't know + whether this is necessary or not (given that + alignment is taken care of later), but it's + traditional, so I'll leave it in. Note that if + this size alignment is ever removed, ALIGN above + will have to be initialized to 1 instead of + sizeof (int). */ + + com = (com + sizeof (int) - 1) & (- sizeof (int)); + + while (!(com & align)) + align <<= 1; + + align = align > MAX_ALIGNMENT ? MAX_ALIGNMENT : align; + + bss_size = ((((bss_size + data_size + data_start) + + (align - 1)) & (- align)) + - data_size - data_start); + + sp->value = data_start + data_size + bss_size; + sp->defined = N_BSS | N_EXT; + bss_size += com; + if (write_map) + printf ("Allocating common %s: %x at %x\n", + sp->name, com, sp->value); + } + else + { + sp->defined = 0; + undefined_global_sym_count++; + } + } + /* Set length word at front of vector and zero byte at end. + Reverse the vector itself to put it in file order. */ + if ((sp->defined & ~N_EXT) == N_SETV) + { + unsigned long length_word_index + = (sp->value - set_sect_start) / sizeof (unsigned long); + unsigned long i, tmp; + + set_vectors[length_word_index] + = setv_fill_count - 1 - length_word_index; + + /* Reverse the vector. */ + for (i = 1; + i < (setv_fill_count - length_word_index - 1) / 2 + 1; + i++) + { + tmp = set_vectors[length_word_index + i]; + set_vectors[length_word_index + i] + = set_vectors[setv_fill_count - i]; + set_vectors[setv_fill_count - i] = tmp; + } + + set_vectors[setv_fill_count++] = 0; + } + if (sp->defined) + defined_global_sym_count++; + } + } + + /* Make sure end of bss is aligned as much as anyone can want. */ + + bss_size = (bss_size + sizeof(double) - 1) & ~(sizeof(double)-1); + + /* Give values to _end and friends. */ + { + int end_value = data_start + data_size + bss_size; + if (end_symbol) + end_symbol->value = end_value; + if (end_symbol_alt) + end_symbol_alt->value = end_value; + } + + { + int etext_value = text_size + text_start; + if (etext_symbol) + etext_symbol->value = etext_value; + if (etext_symbol_alt) + etext_symbol_alt->value = etext_value; + } + + { + int edata_value = data_start + data_size; + if (edata_symbol) + edata_symbol->value = edata_value; + if (edata_symbol_alt) + edata_symbol_alt->value = edata_value; + } + + /* Figure the data_pad now, so that it overlaps with the bss addresses. */ + + { + /* The amount of data_pad that we are computing now. This is the + part which overlaps with bss. What was computed previously + goes before bss. */ + int data_pad_additional = 0; + + if (specified_data_size && specified_data_size > data_size) + data_pad_additional = specified_data_size - data_size; + + if (output_style == OUTPUT_DEMAND_PAGED) + data_pad_additional = + ((data_pad_additional + data_size + page_size - 1) & (- page_size)) - data_size; + + bss_size -= data_pad_additional; + if (bss_size < 0) bss_size = 0; + + data_size += data_pad_additional; + + data_pad += data_pad_additional; + } +} + +/* Accumulate the section sizes of input file ENTRY + into the section sizes of the output file. */ + +void +consider_file_section_lengths (entry) + register struct file_entry *entry; +{ + if (entry->just_syms_flag) + return; + + entry->text_start_address = text_size; + /* If there were any vectors, we need to chop them off */ + text_size += entry->text_size; + entry->data_start_address = data_size; + data_size += entry->data_size; + entry->bss_start_address = bss_size; + bss_size += entry->bss_size; + + text_reloc_size += entry->text_reloc_size; + data_reloc_size += entry->data_reloc_size; +} + +/* Determine where the sections of ENTRY go into the output file, + whose total section sizes are already known. + Also relocate the addresses of the file's local and debugger symbols. */ + +void +relocate_file_addresses (entry) + register struct file_entry *entry; +{ + entry->text_start_address += text_start; + + /* Note that `data_start' and `data_size' have not yet been adjusted + for the portion of data_pad which overlaps with bss. If they had + been, we would get the wrong results here. */ + entry->data_start_address += data_start; + entry->bss_start_address += data_start + data_size; + + { + register struct nlist *p; + register struct nlist *end + = entry->symbols + entry->syms_size / sizeof (struct nlist); + + for (p = entry->symbols; p < end; p++) + { + /* If this belongs to a section, update it by the section's start address */ + register int type = p->n_type & N_TYPE; + + switch (type) + { + case N_TEXT: + case N_SETT: + p->n_value += entry->text_start_address - entry->orig_text_address; + break; + case N_DATA: + case N_SETV: + case N_SETD: + /* Data segment symbol. Subtract the address of the + data segment in the input file, and add the address + of this input file's data segment in the output file. */ + p->n_value += + entry->data_start_address - entry->orig_data_address; + break; + case N_BSS: + case N_SETB: + /* likewise for symbols with value in BSS. */ + p->n_value += entry->bss_start_address - entry->orig_bss_address; + break; + } + } + } +} + +void describe_file_sections (), list_file_locals (); + +/* Print a complete or partial map of the output file. */ + +void +print_symbols (outfile) + FILE *outfile; +{ + register int i; + + fprintf (outfile, "\nFiles:\n\n"); + + each_file (describe_file_sections, outfile); + + fprintf (outfile, "\nGlobal symbols:\n\n"); + + for (i = 0; i < TABSIZE; i++) + { + register symbol *sp; + for (sp = symtab[i]; sp; sp = sp->link) + { + if (sp->defined == 1) + fprintf (outfile, " %s: common, length 0x%x\n", sp->name, sp->max_common_size); + if (sp->defined) + fprintf (outfile, " %s: 0x%x\n", sp->name, sp->value); + else if (sp->referenced) + fprintf (outfile, " %s: undefined\n", sp->name); + } + } + + each_file (list_file_locals, outfile); +} + +void +describe_file_sections (entry, outfile) + struct file_entry *entry; + FILE *outfile; +{ + fprintf (outfile, " "); + print_file_name (entry, outfile); + if (entry->just_syms_flag) + fprintf (outfile, " symbols only\n", 0); + else + fprintf (outfile, " text %x(%x), data %x(%x), bss %x(%x) hex\n", + entry->text_start_address, entry->text_size, + entry->data_start_address, entry->data_size, + entry->bss_start_address, entry->bss_size); +} + +void +list_file_locals (entry, outfile) + struct file_entry *entry; + FILE *outfile; +{ + register struct nlist + *p, + *end = entry->symbols + entry->syms_size / sizeof (struct nlist); + + entry->strings = (char *) alloca (entry->strs_size); + read_entry_strings (file_open (entry), entry); + + fprintf (outfile, "\nLocal symbols of "); + print_file_name (entry, outfile); + fprintf (outfile, ":\n\n"); + + for (p = entry->symbols; p < end; p++) + /* If this is a definition, + update it if necessary by this file's start address. */ + if (!(p->n_type & (N_STAB | N_EXT))) + fprintf (outfile, " %s: 0x%x\n", + entry->strings + p->n_un.n_strx, p->n_value); + + entry->strings = 0; /* All done with them. */ +} + + +/* Static vars for do_warnings and subroutines of it */ +int list_unresolved_refs; /* List unresolved refs */ +int list_warning_symbols; /* List warning syms */ +int list_multiple_defs; /* List multiple definitions */ + +/* + * Structure for communication between do_file_warnings and it's + * helper routines. Will in practice be an array of three of these: + * 0) Current line, 1) Next line, 2) Source file info. + */ +struct line_debug_entry +{ + int line; + char *filename; + struct nlist *sym; +}; + +void qsort (); +/* + * Helper routines for do_file_warnings. + */ + +/* Return an integer less than, equal to, or greater than 0 as per the + relation between the two relocation entries. Used by qsort. */ + +int +relocation_entries_relation (rel1, rel2) + struct relocation_info *rel1, *rel2; +{ + return RELOC_ADDRESS(rel1) - RELOC_ADDRESS(rel2); +} + +/* Moves to the next debugging symbol in the file. USE_DATA_SYMBOLS + determines the type of the debugging symbol to look for (DSLINE or + SLINE). STATE_POINTER keeps track of the old and new locatiosn in + the file. It assumes that state_pointer[1] is valid; ie + that it.sym points into some entry in the symbol table. If + state_pointer[1].sym == 0, this routine should not be called. */ + +int +next_debug_entry (use_data_symbols, state_pointer) + register int use_data_symbols; + /* Next must be passed by reference! */ + struct line_debug_entry state_pointer[3]; +{ + register struct line_debug_entry + *current = state_pointer, + *next = state_pointer + 1, + /* Used to store source file */ + *source = state_pointer + 2; + struct file_entry *entry = (struct file_entry *) source->sym; + + current->sym = next->sym; + current->line = next->line; + current->filename = next->filename; + + while (++(next->sym) < (entry->symbols + + entry->syms_size/sizeof (struct nlist))) + { + /* n_type is a char, and N_SOL, N_EINCL and N_BINCL are > 0x80, so + * may look negative...therefore, must mask to low bits + */ + switch (next->sym->n_type & 0xff) + { + case N_SLINE: + if (use_data_symbols) continue; + next->line = next->sym->n_desc; + return 1; + case N_DSLINE: + if (!use_data_symbols) continue; + next->line = next->sym->n_desc; + return 1; +#ifdef HAVE_SUN_STABS + case N_EINCL: + next->filename = source->filename; + continue; +#endif + case N_SO: + source->filename = next->sym->n_un.n_strx + entry->strings; + source->line++; +#ifdef HAVE_SUN_STABS + case N_BINCL: +#endif + case N_SOL: + next->filename + = next->sym->n_un.n_strx + entry->strings; + default: + continue; + } + } + next->sym = (struct nlist *) 0; + return 0; +} + +/* Create a structure to save the state of a scan through the debug + symbols. USE_DATA_SYMBOLS is set if we should be scanning for + DSLINE's instead of SLINE's. entry is the file entry which points + at the symbols to use. */ + +struct line_debug_entry * +init_debug_scan (use_data_symbols, entry) + int use_data_symbols; + struct file_entry *entry; +{ + struct line_debug_entry + *state_pointer + = (struct line_debug_entry *) + xmalloc (3 * sizeof (struct line_debug_entry)); + register struct line_debug_entry + *current = state_pointer, + *next = state_pointer + 1, + *source = state_pointer + 2; /* Used to store source file */ + + struct nlist *tmp; + + for (tmp = entry->symbols; + tmp < (entry->symbols + + entry->syms_size/sizeof (struct nlist)); + tmp++) + if (tmp->n_type == (int) N_SO) + break; + + if (tmp >= (entry->symbols + + entry->syms_size/sizeof (struct nlist))) + { + /* I believe this translates to "We lose" */ + current->filename = next->filename = entry->filename; + current->line = next->line = -1; + current->sym = next->sym = (struct nlist *) 0; + return state_pointer; + } + + next->line = source->line = 0; + next->filename = source->filename + = (tmp->n_un.n_strx + entry->strings); + source->sym = (struct nlist *) entry; + next->sym = tmp; + + next_debug_entry (use_data_symbols, state_pointer); /* To setup next */ + + if (!next->sym) /* No line numbers for this section; */ + /* setup output results as appropriate */ + { + if (source->line) + { + current->filename = source->filename = entry->filename; + current->line = -1; /* Don't print lineno */ + } + else + { + current->filename = source->filename; + current->line = 0; + } + return state_pointer; + } + + + next_debug_entry (use_data_symbols, state_pointer); /* To setup current */ + + return state_pointer; +} + +/* Takes an ADDRESS (in either text or data space) and a STATE_POINTER + which describes the current location in the implied scan through + the debug symbols within the file which ADDRESS is within, and + returns the source line number which corresponds to ADDRESS. */ + +int +address_to_line (address, state_pointer) + unsigned long address; + /* Next must be passed by reference! */ + struct line_debug_entry state_pointer[3]; +{ + struct line_debug_entry + *current = state_pointer, + *next = state_pointer + 1; + struct line_debug_entry *tmp_pointer; + + int use_data_symbols; + + if (next->sym) + use_data_symbols = (next->sym->n_type & ~N_EXT) == N_DATA; + else + return current->line; + + /* Go back to the beginning if we've already passed it. */ + if (current->sym->n_value > address) + { + tmp_pointer = init_debug_scan (use_data_symbols, + (struct file_entry *) + ((state_pointer + 2)->sym)); + state_pointer[0] = tmp_pointer[0]; + state_pointer[1] = tmp_pointer[1]; + state_pointer[2] = tmp_pointer[2]; + free (tmp_pointer); + } + + /* If we're still in a bad way, return -1, meaning invalid line. */ + if (current->sym->n_value > address) + return -1; + + while (next->sym + && next->sym->n_value <= address + && next_debug_entry (use_data_symbols, state_pointer)) + ; + return current->line; +} + + +/* Macros for manipulating bitvectors. */ +#define BIT_SET_P(bv, index) ((bv)[(index) >> 3] & 1 << ((index) & 0x7)) +#define SET_BIT(bv, index) ((bv)[(index) >> 3] |= 1 << ((index) & 0x7)) + +/* This routine will scan through the relocation data of file ENTRY, + printing out references to undefined symbols and references to + symbols defined in files with N_WARNING symbols. If DATA_SEGMENT + is non-zero, it will scan the data relocation segment (and use + N_DSLINE symbols to track line number); otherwise it will scan the + text relocation segment. Warnings will be printed on the output + stream OUTFILE. Eventually, every nlist symbol mapped through will + be marked in the NLIST_BITVECTOR, so we don't repeat ourselves when + we scan the nlists themselves. */ + +do_relocation_warnings (entry, data_segment, outfile, nlist_bitvector) + struct file_entry *entry; + int data_segment; + FILE *outfile; + unsigned char *nlist_bitvector; +{ + struct relocation_info + *reloc_start = data_segment ? entry->datarel : entry->textrel, + *reloc; + long syms_size = entry->syms_size / sizeof (struct nlist); + int reloc_size + = ((data_segment ? entry->data_reloc_size : entry->text_reloc_size) + / sizeof (struct relocation_info)); + int start_of_segment + = (data_segment ? entry->data_start_address : entry->text_start_address); + struct nlist *start_of_syms = entry->symbols; + struct line_debug_entry *state_pointer + = init_debug_scan (data_segment != 0, entry); + register struct line_debug_entry *current = state_pointer; + /* Assigned to generally static values; should not be written into. */ + char *errfmt; + /* Assigned to alloca'd values cand copied into; should be freed + when done. */ + char *errmsg; + int invalidate_line_number; + + /* We need to sort the relocation info here. Sheesh, so much effort + for one lousy error optimization. */ + + qsort (reloc_start, reloc_size, sizeof (struct relocation_info), + relocation_entries_relation); + + for (reloc = reloc_start; + reloc < (reloc_start + reloc_size); + reloc++) + { + register struct nlist *s; + register symbol *g; + int s_index; + + /* If the relocation isn't resolved through a symbol, continue */ + if (!RELOC_EXTERN_P(reloc)) + continue; + + s_index = RELOC_SYMBOL(reloc); + if (s_index < 0 || s_index >= syms_size) + fatal_with_file ("bad symbol in relocation table of ", entry); + s = &entry->symbols[s_index]; + + /* Local symbols shouldn't ever be used by relocation info, so + the next should be safe. + This is, of course, wrong. References to local BSS symbols can be + the targets of relocation info, and they can (must) be + resolved through symbols. However, these must be defined properly, + (the assembler would have caught it otherwise), so we can + ignore these cases. */ + if (!(s->n_type & N_EXT)) + continue; + + g = (symbol *) s->n_un.n_name; + errmsg = 0; + + if (!g->defined && list_unresolved_refs) /* Reference */ + { + /* Mark as being noted by relocation warning pass. */ + SET_BIT (nlist_bitvector, s - start_of_syms); + + if (g->undef_refs >= MAX_UREFS_PRINTED) /* Listed too many */ + continue; + + /* Undefined symbol which we should mention */ + + if (++(g->undef_refs) == MAX_UREFS_PRINTED) + { + errfmt = "More undefined symbol %s refs follow"; + invalidate_line_number = 1; + } + else + { + errfmt = "Undefined symbol %s referenced from %s segment"; + invalidate_line_number = 0; + } + } + else /* Defined */ + { + /* Potential symbol warning here */ + if (!g->warning) continue; + + /* Mark as being noted by relocation warning pass. */ + SET_BIT (nlist_bitvector, s - start_of_syms); + + errfmt = 0; + errmsg = g->warning; + invalidate_line_number = 0; + } + + + /* If errfmt == 0, errmsg has already been defined. */ + if (errfmt != 0) + { + char *nm; + + if (!demangler || !(nm = (*demangler)(g->name))) + nm = g->name; + errmsg = xmalloc (strlen (errfmt) + strlen (nm) + 1); + sprintf (errmsg, errfmt, nm, data_segment ? "data" : "text"); + if (nm != g->name) + free (nm); + } + + address_to_line (RELOC_ADDRESS (reloc) + start_of_segment, + state_pointer); + + if (current->line >=0) + { + fprintf (outfile, "%s:%d (", current->filename, + invalidate_line_number ? 0 : current->line); + print_file_name (entry, outfile); + fprintf (outfile, "): %s\n", errmsg); + } + else + { + print_file_name(entry, outfile); + fprintf(outfile, ": %s\n", errmsg); + } + + if (errfmt != 0) + free (errmsg); + } + + free (state_pointer); +} + +/* Print on OUTFILE a list of all warnings generated by references + and/or definitions in the file ENTRY. List source file and line + number if possible, just the .o file if not. */ + +void +do_file_warnings (entry, outfile) + struct file_entry *entry; + FILE *outfile; +{ + int number_of_syms = entry->syms_size / sizeof (struct nlist); + unsigned char *nlist_bitvector + = (unsigned char *) alloca ((number_of_syms >> 3) + 1); + struct line_debug_entry *text_scan, *data_scan; + int i; + char *errfmt, *file_name; + int line_number; + int dont_allow_symbol_name; + + bzero (nlist_bitvector, (number_of_syms >> 3) + 1); + + /* Read in the files strings if they aren't available */ + if (!entry->strings) + { + int desc; + + entry->strings = (char *) alloca (entry->strs_size); + desc = file_open (entry); + read_entry_strings (desc, entry); + } + + read_file_relocation (entry); + + /* Do text warnings based on a scan through the relocation info. */ + do_relocation_warnings (entry, 0, outfile, nlist_bitvector); + + /* Do data warnings based on a scan through the relocation info. */ + do_relocation_warnings (entry, 1, outfile, nlist_bitvector); + + /* Scan through all of the nlist entries in this file and pick up + anything that the scan through the relocation stuff didn't. */ + + text_scan = init_debug_scan (0, entry); + data_scan = init_debug_scan (1, entry); + + for (i = 0; i < number_of_syms; i++) + { + struct nlist *s; + struct glosym *g; + + s = entry->symbols + i; + + if (!(s->n_type & N_EXT)) + continue; + + g = (symbol *) s->n_un.n_name; + dont_allow_symbol_name = 0; + + if (list_multiple_defs && g->multiply_defined) + { + errfmt = "Definition of symbol %s (multiply defined)"; + switch (s->n_type) + { + case N_ABS | N_EXT: + line_number = -1; + break; + case N_TEXT | N_EXT: + line_number = address_to_line (s->n_value, text_scan); + file_name = text_scan[0].filename; + break; + case N_DATA | N_EXT: + line_number = address_to_line (s->n_value, data_scan); + file_name = data_scan[0].filename; + break; + case N_SETA | N_EXT: + case N_SETT | N_EXT: + case N_SETD | N_EXT: + case N_SETB | N_EXT: + if (g->multiply_defined == 2) + continue; + errfmt = "First set element definition of symbol %s (multiply defined)"; + break; + default: + continue; /* Don't print out multiple defs + at references. */ + } + } + else if (BIT_SET_P (nlist_bitvector, i)) + continue; + else if (list_unresolved_refs && !g->defined) + { + if (g->undef_refs >= MAX_UREFS_PRINTED) + continue; + + if (++(g->undef_refs) == MAX_UREFS_PRINTED) + errfmt = "More undefined \"%s\" refs follow"; + else + errfmt = "Undefined symbol \"%s\" referenced"; + line_number = -1; + } + else if (g->warning) + { + /* There are two cases in which we don't want to + do this. The first is if this is a definition instead of + a reference. The second is if it's the reference used by + the warning stabs itself. */ + if (s->n_type != (N_EXT | N_UNDF) + || (i && (s-1)->n_type == N_WARNING)) + continue; + + errfmt = g->warning; + line_number = -1; + dont_allow_symbol_name = 1; + } + else + continue; + + if (line_number == -1) + { + print_file_name (entry, outfile); + fprintf (outfile, ": "); + } + else + { + fprintf (outfile, "%s:%d (", file_name, line_number); + print_file_name (entry, outfile); + fprintf (outfile, "): "); + } + + if (dont_allow_symbol_name) + fprintf (outfile, "%s", errfmt); + else + { + char *nm; + + if (!demangler || !(nm = (*demangler)(g->name))) + fprintf (outfile, errfmt, g->name); + else + { + fprintf (outfile, errfmt, nm); + free (nm); + } + } + + fputc ('\n', outfile); + } + free (text_scan); + free (data_scan); + entry->strings = 0; /* Since it will dissapear anyway. */ +} + +do_warnings (outfile) + FILE *outfile; +{ + list_unresolved_refs = output_style != OUTPUT_RELOCATABLE && undefined_global_sym_count; + list_warning_symbols = warning_count; + list_multiple_defs = multiple_def_count != 0; + + if (!(list_unresolved_refs || + list_warning_symbols || + list_multiple_defs )) + /* No need to run this routine */ + return; + + each_file (do_file_warnings, outfile); + + if (entry_symbol && !entry_symbol->defined) + fprintf(stderr, "%s: error: Entry symbol `%s' never defined.\n", + progname, entry_symbol->name); + + if (list_unresolved_refs || list_multiple_defs) + make_executable = 0; +} + +#ifdef A_OUT + +/* Stuff pertaining to creating a.out files. */ + +/* The a.out header. */ + +#ifdef tek4300 +struct zexec outheader; +#else +struct exec outheader; +#endif + +#ifdef COFF_ENCAPSULATE +int need_coff_header; +struct coffheader coffheader; +#endif + +/* Compute text_start and text_header_size for an a.out file. */ + +void +initialize_a_out_text_start () +{ + int magic; + + switch (output_style) + { + case OUTPUT_RELOCATABLE: + case OUTPUT_WRITABLE_TEXT: + magic = OMAGIC; + break; + case OUTPUT_READONLY_TEXT: +#ifdef NMAGIC + magic = NMAGIC; + break; +#endif + case OUTPUT_DEMAND_PAGED: + magic = ZMAGIC; + break; + default: + fatal ("unknown output style found (bug in ld)", (char *) 0); + break; + } + + /* Determine whether to count the header as part of + the text size, and initialize the text size accordingly. + This depends on the kind of system and on the output format selected. */ + N_SET_MAGIC (outheader, magic); +#ifdef INITIALIZE_HEADER + INITIALIZE_HEADER; +#endif + + text_header_size = sizeof (struct exec); +#ifdef COFF_ENCAPSULATE + /* Don't write the coff header for the output of ld -A (since + it is not executable by the kernel anyway). */ + if (output_style != OUTPUT_RELOCATABLE && !file_table[0].just_syms_flag) + { + need_coff_header = 1; + /* set this flag now, since it will change the values of N_TXTOFF, etc */ + N_SET_FLAGS (outheader, N_FLAGS_COFF_ENCAPSULATE); + text_header_size += sizeof (struct coffheader); + } +#endif + if (text_header_size <= N_TXTOFF (outheader)) + text_header_size = 0; + else + text_header_size -= N_TXTOFF (outheader); + +#ifdef _N_BASEADDR + /* SunOS 4.1 N_TXTADDR depends on the value of outheader.a_entry. */ + outheader.a_entry = N_PAGSIZ(outheader); +#endif + + if (!T_flag_specified && output_style != OUTPUT_RELOCATABLE) + text_start = N_TXTADDR (outheader); +} + +/* Compute data_start once text_size is known. */ + +void +initialize_a_out_data_start () +{ + outheader.a_text = text_size; +#ifdef sequent + outheader.a_text += N_ADDRADJ (outheader); + if (entry_symbol == 0) + entry_symbol = getsym ("start"); +#endif + if (! Tdata_flag_specified) + data_start = N_DATADDR (outheader) + text_start - N_TXTADDR (outheader); +} + +/* Compute offsets of various pieces of the a.out output file. */ + +void +compute_a_out_section_offsets () +{ + outheader.a_data = data_size; + outheader.a_bss = bss_size; + outheader.a_entry = (entry_symbol ? entry_symbol->value + : text_start + text_header_size); + +#ifdef COFF_ENCAPSULATE + if (need_coff_header) + { + /* We are encapsulating BSD format within COFF format. */ + struct coffscn *tp, *dp, *bp; + + tp = &coffheader.scns[0]; + dp = &coffheader.scns[1]; + bp = &coffheader.scns[2]; + + strcpy (tp->s_name, ".text"); + tp->s_paddr = text_start; + tp->s_vaddr = text_start; + tp->s_size = text_size; + tp->s_scnptr = sizeof (struct coffheader) + sizeof (struct exec); + tp->s_relptr = 0; + tp->s_lnnoptr = 0; + tp->s_nreloc = 0; + tp->s_nlnno = 0; + tp->s_flags = 0x20; + strcpy (dp->s_name, ".data"); + dp->s_paddr = data_start; + dp->s_vaddr = data_start; + dp->s_size = data_size; + dp->s_scnptr = tp->s_scnptr + tp->s_size; + dp->s_relptr = 0; + dp->s_lnnoptr = 0; + dp->s_nreloc = 0; + dp->s_nlnno = 0; + dp->s_flags = 0x40; + strcpy (bp->s_name, ".bss"); + bp->s_paddr = dp->s_vaddr + dp->s_size; + bp->s_vaddr = bp->s_paddr; + bp->s_size = bss_size; + bp->s_scnptr = 0; + bp->s_relptr = 0; + bp->s_lnnoptr = 0; + bp->s_nreloc = 0; + bp->s_nlnno = 0; + bp->s_flags = 0x80; + + coffheader.f_magic = COFF_MAGIC; + coffheader.f_nscns = 3; + /* store an unlikely time so programs can + * tell that there is a bsd header + */ + coffheader.f_timdat = 1; + coffheader.f_symptr = 0; + coffheader.f_nsyms = 0; + coffheader.f_opthdr = 28; + coffheader.f_flags = 0x103; + /* aouthdr */ + coffheader.magic = ZMAGIC; + coffheader.vstamp = 0; + coffheader.tsize = tp->s_size; + coffheader.dsize = dp->s_size; + coffheader.bsize = bp->s_size; + coffheader.entry = outheader.a_entry; + coffheader.text_start = tp->s_vaddr; + coffheader.data_start = dp->s_vaddr; + } +#endif + + if (strip_symbols == STRIP_ALL) + nsyms = 0; + else + { + nsyms = (defined_global_sym_count + + undefined_global_sym_count); + if (discard_locals == DISCARD_L) + nsyms += non_L_local_sym_count; + else if (discard_locals == DISCARD_NONE) + nsyms += local_sym_count; + /* One extra for following reference on indirects */ + if (output_style == OUTPUT_RELOCATABLE) +#ifndef NeXT + nsyms += set_symbol_count + global_indirect_count; +#else + nsyms += set_symbol_count; +#endif + } + + if (strip_symbols == STRIP_NONE) + nsyms += debugger_sym_count; + + outheader.a_syms = nsyms * sizeof (struct nlist); + + if (output_style == OUTPUT_RELOCATABLE) + { + outheader.a_trsize = text_reloc_size; + outheader.a_drsize = data_reloc_size; + } + else + { + outheader.a_trsize = 0; + outheader.a_drsize = 0; +#ifdef tek4300 + /* UTek has been known to panic without the following ... */ + outheader.a_textoff = ZMOFF; + outheader.a_dataoff = ZMOFF + text_size; + outheader.a_textaddr = text_start; + outheader.a_dataaddr = data_start; + outheader.a_bssaddr = data_start + data_size; +#endif + } + + /* Initialize the various file offsets. */ + + output_text_offset = N_TXTOFF (outheader); +#ifdef N_DATOFF + output_data_offset = N_DATOFF (outheader); +#else + output_data_offset = output_text_offset + text_size; +#endif +#ifdef N_TRELOFF + output_trel_offset = N_TRELOFF (outheader); +#else + output_trel_offset = output_data_offset + data_size; +#endif +#ifdef N_DRELOFF + output_drel_offset = N_DRELOFF (outheader); +#else + output_drel_offset = output_trel_offset + text_reloc_size; +#endif + output_syms_offset = N_SYMOFF (outheader); + output_strs_offset = N_STROFF (outheader); +} + +/* Compute more section offsets once the size of the string table is known. */ + +void +compute_more_a_out_section_offsets () +{ + output_symseg_offset = output_strs_offset + output_strs_size; +} + +/* Write the a.out header once everything else is known. */ + +void +write_a_out_header () +{ + lseek (outdesc, 0L, 0); + +#ifdef COFF_ENCAPSULATE + if (need_coff_header) + mywrite (&coffheader, sizeof coffheader, 1, outdesc); +#endif + +#ifdef tek4300 + if (outheader.a_magic == ZMAGIC) + mywrite (&outheader, sizeof outheader, 1, outdesc); + else +#endif + mywrite (&outheader, sizeof (struct exec), 1, outdesc); + + /* Output whatever padding is required in the executable file + between the header and the start of the text. */ + +#ifndef COFF_ENCAPSULATE + padfile (N_TXTOFF (outheader) - sizeof outheader, outdesc); +#endif +} + +#endif + +#ifdef MACH_O + +/* Stuff pertaining to creating Mach-O files. */ + +/* Convert the Mach-O style n_sect references into something the rest + of the loader can understand. */ + +void +translate_mach_o_symbols (entry) + struct file_entry *entry; +{ + int i, n, g; + struct nlist *sym; + + n = entry->syms_size / sizeof (struct nlist); + for (i = 0; i < n; ++i) + if (((sym = &entry->symbols[i])->n_type & ~N_EXT) == N_SECT) + { + if (sym->n_sect == entry->text_ordinal) + sym->n_type = (sym->n_type & N_EXT) | N_TEXT; + else if (sym->n_sect == entry->data_ordinal) + sym->n_type = (sym->n_type & N_EXT) | N_DATA; + else if (sym->n_sect == entry->bss_ordinal) + sym->n_type = (sym->n_type & N_EXT) | N_BSS; + else + fatal_with_file ("unknown section referenced in symbols of ", entry); + sym->n_sect = 0; + } + else if ((sym = &entry->symbols[i])->n_type == N_SLINE) + { + if (sym->n_sect == entry->text_ordinal) + sym->n_type = N_SLINE; + else if (sym->n_sect == entry->data_ordinal) + sym->n_type = N_DSLINE; + else if (sym->n_sect == entry->bss_ordinal) + sym->n_type = N_BSLINE; + else + fatal_with_file ("unknown section referenced in debugging symbols of ", entry); + } +} + +/* Convert Mach-O style relocation info into a.out style relocation + info internally. */ +void +translate_mach_o_relocation (entry, reloc, count) + struct file_entry *entry; + struct relocation_info *reloc; + int count; +{ + int i; + + for (i = 0; i < count; ++i) + if (!RELOC_EXTERN_P(&reloc[i])) + if (RELOC_TYPE(&reloc[i]) == R_ABS) + RELOC_TYPE(&reloc[i]) = N_ABS; + else if (RELOC_TYPE(&reloc[i]) == entry->text_ordinal) + RELOC_TYPE(&reloc[i]) = N_TEXT; + else if (RELOC_TYPE(&reloc[i]) == entry->data_ordinal) + RELOC_TYPE(&reloc[i]) = N_DATA; + else if (RELOC_TYPE(&reloc[i]) == entry->bss_ordinal) + RELOC_TYPE(&reloc[i]) = N_BSS; + else + fatal_with_file ("unknown section ordinal in relocation info of ", entry); +} + +/* Header structure for OUTPUT_RELOCATABLE. */ + +struct +{ + struct mach_header header; + struct segment_command segment; + struct section text; + struct section data; + struct section bss; + struct symtab_command symtab; +#ifdef LC_SYMSEG + struct symseg_command symseg; +#endif +} m_object; + +#ifdef NeXT +#define CPU_TYPE CPU_TYPE_MC68030 +#define CPU_SUBTYPE CPU_SUBTYPE_NeXT +#define THREAD_FLAVOR NeXT_THREAD_STATE_REGS +#define THREAD_COUNT NeXT_THREAD_STATE_REGS_COUNT +typedef struct NeXT_thread_state_regs thread_state; +#define thread_state_entry_field pc +#endif + +/* Header structure for all executable output forms. */ + +struct +{ + struct mach_header header; + struct segment_command pagezero; + struct segment_command text_segment; + struct section text; + struct segment_command data_segment; + struct section data; + struct section bss; + struct thread_command unixthread; + unsigned long int flavor; + unsigned long int count; + thread_state state; + struct symtab_command symtab; +#ifdef LC_SYMSEG + struct symseg_command symseg; +#endif +} m_exec; + +/* Compute text_start and text_header_size for an a.out file. */ + +void +initialize_mach_o_text_start () +{ + if (output_style != OUTPUT_RELOCATABLE) + { + text_header_size = sizeof m_exec; + if (!T_flag_specified && output_style != OUTPUT_RELOCATABLE) + /* We reserve the first page of an executable to trap NULL dereferences. */ + text_start = page_size; + } +} + +/* Compute data_start once text_size is known. */ + +void +initialize_mach_o_data_start () +{ + if (! Tdata_flag_specified) + data_start = text_start + text_size; +} + +/* Compute offsets of various pieces of the Mach-O output file. */ +void +compute_mach_o_section_offsets () +{ + int header_size, trsize, drsize; + + switch (output_style) + { + case OUTPUT_RELOCATABLE: + header_size = sizeof m_object; + break; + default: + header_size = sizeof m_exec; + break; + } + + if (strip_symbols == STRIP_ALL) + nsyms = 0; + else + { + nsyms = (defined_global_sym_count + + undefined_global_sym_count); + if (discard_locals == DISCARD_L) + nsyms += non_L_local_sym_count; + else if (discard_locals == DISCARD_NONE) + nsyms += local_sym_count; + /* One extra for following reference on indirects */ + if (output_style == OUTPUT_RELOCATABLE) +#ifndef NeXT + nsyms += set_symbol_count + global_indirect_count; +#else + nsyms += set_symbol_count; +#endif + } + + if (strip_symbols == STRIP_NONE) + nsyms += debugger_sym_count; + + output_text_offset = header_size; + output_data_offset = output_text_offset + text_size; + output_trel_offset = output_data_offset + data_size; + if (output_style == OUTPUT_RELOCATABLE) + trsize = text_reloc_size, drsize = data_reloc_size; + else + trsize = drsize = 0; + output_drel_offset = output_trel_offset + trsize; + output_syms_offset = output_drel_offset + drsize; + output_strs_offset = output_syms_offset + nsyms * sizeof (struct nlist); +} + +/* Compute more section offsets once the size of the string table is known. */ +void +compute_more_mach_o_section_offsets () +{ + output_symseg_offset = output_strs_offset + output_strs_size; +} + +/* Write the Mach-O header once everything else is known. */ + +void +write_mach_o_header () +{ + struct mach_header header; + struct section text, data, bss; + struct symtab_command symtab; +#ifdef LC_SYMSEG + struct symseg_command symseg; +#endif + thread_state state; + + lseek (outdesc, 0L, 0); + + + header.magic = MH_MAGIC; + header.cputype = CPU_TYPE; + header.cpusubtype = CPU_SUBTYPE; + header.filetype = output_style == OUTPUT_RELOCATABLE ? MH_OBJECT : MH_EXECUTE; +#ifdef LC_SYMSEG + switch (output_style) + { + case OUTPUT_RELOCATABLE: + header.ncmds = 3; + header.sizeofcmds = sizeof m_object - sizeof header; + break; + default: + header.ncmds = 6; + header.sizeofcmds = sizeof m_exec - sizeof header; + break; + } +#else + switch (output_style) + { + case OUTPUT_RELOCATABLE: + header.ncmds = 2; + header.sizeofcmds = sizeof m_object - sizeof header; + break; + default: + header.ncmds = 5; + header.sizeofcmds = sizeof m_exec - sizeof header; + break; + } +#endif + header.flags = undefined_global_sym_count ? 0 : MH_NOUNDEFS; + + bzero((char *) &text, sizeof text); + strncpy(text.sectname, SECT_TEXT, sizeof text.sectname); + strncpy(text.segname, SEG_TEXT, sizeof text.segname); + text.addr = text_start; + text.size = text_size; + text.offset = output_text_offset; + text.align = text.addr % sizeof (double) ? sizeof (int) : sizeof (double); + text.reloff = output_trel_offset; + text.nreloc = output_style == OUTPUT_RELOCATABLE + ? text_reloc_size / sizeof (struct relocation_info) : 0; + text.flags = 0; + + bzero((char *) &data, sizeof data); + strncpy(data.sectname, SECT_DATA, sizeof data.sectname); + strncpy(data.segname, output_style == OUTPUT_WRITABLE_TEXT ? SEG_TEXT : SEG_DATA, + sizeof data.segname); + data.addr = data_start; + data.size = data_size; + data.offset = output_data_offset; + data.align = data.addr % sizeof (double) ? sizeof (int) : sizeof (double); + data.reloff = output_drel_offset; + data.nreloc = output_style == OUTPUT_RELOCATABLE + ? data_reloc_size / sizeof (struct relocation_info) : 0; + data.flags = 0; + + bzero((char *) &bss, sizeof bss); + strncpy(bss.sectname, SECT_BSS, sizeof data.sectname); + strncpy(bss.segname, output_style == OUTPUT_WRITABLE_TEXT ? SEG_TEXT : SEG_DATA, + sizeof bss.segname); + bss.addr = data_start + data_size; + bss.size = bss_size; + bss.align = bss.addr % sizeof (double) ? sizeof (int) : sizeof (double); + bss.reloff = 0; + bss.nreloc = 0; + bss.flags = S_ZEROFILL; + + symtab.cmd = LC_SYMTAB; + symtab.cmdsize = sizeof symtab; + symtab.symoff = output_syms_offset; + symtab.nsyms = output_syms_size / sizeof (struct nlist); + symtab.stroff = output_strs_offset; + symtab.strsize = output_strs_size; + +#ifdef LC_SYMSEG + symseg.cmd = LC_SYMSEG; + symseg.cmdsize = sizeof symseg; + symseg.offset = output_symseg_offset; + symseg.size = output_symseg_size; +#endif + + switch (output_style) + { + case OUTPUT_RELOCATABLE: + m_object.header = header; + m_object.segment.cmd = LC_SEGMENT; + m_object.segment.cmdsize = sizeof (struct segment_command) + 3 * sizeof (struct section); + strncpy(m_object.segment.segname, SEG_TEXT, sizeof m_object.segment.segname); + m_object.segment.vmaddr = 0; + m_object.segment.vmsize = text.size + data.size + bss.size; + m_object.segment.fileoff = text.offset; + m_object.segment.filesize = text.size + data.size; + m_object.segment.maxprot = VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE; + m_object.segment.initprot = VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE; + m_object.segment.nsects = 3; + m_object.segment.flags = 0; + m_object.text = text; + m_object.data = data; + m_object.bss = bss; + m_object.symtab = symtab; +#ifdef LC_SYMSEG + m_object.symseg = symseg; +#endif + mywrite((char *) &m_object, 1, sizeof m_object, outdesc); + break; + + default: + m_exec.header = header; + m_exec.pagezero.cmd = LC_SEGMENT; + m_exec.pagezero.cmdsize = sizeof (struct segment_command); + strncpy(m_exec.pagezero.segname, SEG_PAGEZERO, sizeof m_exec.pagezero.segname); + m_exec.pagezero.vmaddr = 0; + m_exec.pagezero.vmsize = page_size; + m_exec.pagezero.fileoff = 0; + m_exec.pagezero.filesize = 0; + m_exec.pagezero.maxprot = VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE; + m_exec.pagezero.initprot = 0; + m_exec.pagezero.nsects = 0; + m_exec.pagezero.flags = 0; + m_exec.text_segment.cmd = LC_SEGMENT; + m_exec.text_segment.cmdsize = sizeof (struct segment_command) + sizeof (struct section); + strncpy(m_exec.text_segment.segname, SEG_TEXT, sizeof m_exec.text_segment.segname); + m_exec.text_segment.vmaddr = text_start; + m_exec.text_segment.vmsize = text_size; + m_exec.text_segment.fileoff = output_text_offset; + m_exec.text_segment.filesize = text_size; + m_exec.text_segment.maxprot = VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE; + m_exec.text_segment.initprot = VM_PROT_READ | VM_PROT_EXECUTE; + if (output_style == OUTPUT_WRITABLE_TEXT) + m_exec.text_segment.initprot |= VM_PROT_WRITE; + m_exec.text_segment.nsects = 1; + m_exec.text_segment.flags = 0; + m_exec.text = text; + m_exec.data_segment.cmd = LC_SEGMENT; + m_exec.data_segment.cmdsize = sizeof (struct segment_command) + 2 * sizeof (struct section); + strncpy(m_exec.data_segment.segname, SEG_DATA, sizeof m_exec.data_segment.segname); + m_exec.data_segment.vmaddr = data_start; + m_exec.data_segment.vmsize = data_size + bss_size; + m_exec.data_segment.fileoff = output_data_offset; + m_exec.data_segment.filesize = data_size; + m_exec.data_segment.maxprot = VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE; + m_exec.data_segment.initprot = VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE; + m_exec.data_segment.nsects = 2; + m_exec.data_segment.flags = 0; + m_exec.data = data; + m_exec.bss = bss; + m_exec.unixthread.cmd = LC_UNIXTHREAD; + m_exec.unixthread.cmdsize + = sizeof (struct thread_command) + 2 * sizeof (long int) + sizeof (thread_state); + m_exec.flavor = THREAD_FLAVOR; + m_exec.count = THREAD_COUNT; + m_exec.state.thread_state_entry_field = entry_symbol + ? entry_symbol->value : text_start + text_header_size; + m_exec.symtab = symtab; +#ifdef LC_SYMSEG + m_exec.symseg = symseg; +#endif + mywrite((char *) &m_exec, 1, sizeof m_exec, outdesc); + break; + } +} + +/* Translate a.out style symbols into Mach-O style symbols. */ + +void +generate_mach_o_symbols (syms, nsyms) + struct nlist *syms; + int nsyms; +{ + int i; + + for (i = 0; i < nsyms; ++i) + switch (syms[i].n_type) + { + case N_TEXT: + case N_TEXT | N_EXT: + syms[i].n_type = syms[i].n_type & N_EXT | N_SECT; + syms[i].n_sect = 1; /* text section ordinal */ + break; + case N_DATA: + case N_DATA | N_EXT: + syms[i].n_type = syms[i].n_type & N_EXT | N_SECT; + syms[i].n_sect = 2; /* data section ordinal */ + break; + case N_BSS: + case N_BSS | N_EXT: + syms[i].n_type = syms[i].n_type & N_EXT | N_BSS; + syms[i].n_sect = 3; /* bss section ordinal */ + break; + case N_SLINE: + syms[i].n_type = N_SLINE; + syms[i].n_sect = 1; /* text section ordinal */ + break; + case N_DSLINE: + syms[i].n_type = N_SLINE; + syms[i].n_sect = 2; /* data section ordinal */ + break; + case N_BSLINE: + syms[i].n_type = N_SLINE; + syms[i].n_sect = 3; /* bss section ordinal */ + break; + } +} + +/* Translate a.out style relocation info into Mach-O style relocation + info. */ + +void +generate_mach_o_relocations (reloc, nreloc) + struct relocation_info *reloc; + int nreloc; +{ + int i; + + for (i = 0; i < nreloc; ++i) + if (!RELOC_EXTERN_P (&reloc[i])) + switch (RELOC_TYPE (&reloc[i])) + { + case N_ABS: + case N_ABS | N_EXT: + RELOC_TYPE (&reloc[i]) = R_ABS; + break; + case N_TEXT: + case N_TEXT | N_EXT: + RELOC_TYPE (&reloc[i]) = 1; /* output text section ordinal */ + break; + case N_DATA: + case N_DATA | N_EXT: + RELOC_TYPE (&reloc[i]) = 2; /* output data section ordinal */ + break; + case N_BSS: + case N_BSS | N_EXT: + RELOC_TYPE (&reloc[i]) = 3; /* output bss section ordinal */ + break; + } +} + +#endif + +/* The following functions are simple switches according to the + output style. */ + +/* Compute text_start and text_header_size as appropriate for the + output format. */ + +void +initialize_text_start () +{ +#ifdef A_OUT + if (output_file_type == IS_A_OUT) + { + initialize_a_out_text_start (); + return; + } +#endif +#ifdef MACH_O + if (output_file_type == IS_MACH_O) + { + initialize_mach_o_text_start (); + return; + } +#endif + fatal ("unknown output file type (enum file_type)", (char *) 0); +} + +/* Initialize data_start as appropriate to the output format, once text_size + is known. */ + +void +initialize_data_start () +{ +#ifdef A_OUT + if (output_file_type == IS_A_OUT) + { + initialize_a_out_data_start (); + return; + } +#endif +#ifdef MACH_O + if (output_file_type == IS_MACH_O) + { + initialize_mach_o_data_start (); + return; + } +#endif + fatal ("unknown output file type (enum file_type)", (char *) 0); +} + +/* Compute offsets of the various sections within the output file. */ + +void +compute_section_offsets () +{ +#ifdef A_OUT + if (output_file_type == IS_A_OUT) + { + compute_a_out_section_offsets (); + return; + } +#endif +#ifdef MACH_O + if (output_file_type == IS_MACH_O) + { + compute_mach_o_section_offsets (); + return; + } +#endif + fatal ("unknown output file type (enum file_type)", (char *) 0); +} + +/* Compute more section offsets, once the size of the string table + is finalized. */ +void +compute_more_section_offsets () +{ +#ifdef A_OUT + if (output_file_type == IS_A_OUT) + { + compute_more_a_out_section_offsets (); + return; + } +#endif +#ifdef MACH_O + if (output_file_type == IS_MACH_O) + { + compute_more_mach_o_section_offsets (); + return; + } +#endif + fatal ("unknown output file type (enum file_type)", (char *) 0); +} + +/* Write the output file header, once everything is known. */ +void +write_header () +{ +#ifdef A_OUT + if (output_file_type == IS_A_OUT) + { + write_a_out_header (); + return; + } +#endif +#ifdef MACH_O + if (output_file_type == IS_MACH_O) + { + write_mach_o_header (); + return; + } +#endif + fatal ("unknown output file type (enum file_type)", (char *) 0); +} + +/* Write the output file */ + +void +write_output () +{ + struct stat statbuf; + int filemode, mask; + + /* Remove the old file in case it is owned by someone else. + This prevents spurious "not owner" error messages. + Don't check for errors from unlink; we don't really care + whether it worked. + + Note that this means that if the output file is hard linked, + the other names will still have the old contents. This is + the way Unix ld works; I'm going to consider it a feature. */ + (void) unlink (output_filename); + + outdesc = open (output_filename, O_WRONLY | O_CREAT | O_TRUNC, 0666); + if (outdesc < 0) perror_name (output_filename); + + if (fstat (outdesc, &statbuf) < 0) + perror_name (output_filename); + + filemode = statbuf.st_mode; + + chmod (output_filename, filemode & ~0111); + + /* Calculate the offsets of the various pieces of the output file. */ + compute_section_offsets (); + + /* Output the text and data segments, relocating as we go. */ + write_text (); + write_data (); + + /* Output the merged relocation info, if requested with `-r'. */ + if (output_style == OUTPUT_RELOCATABLE) + write_rel (); + + /* Output the symbol table (both globals and locals). */ + write_syms (); + + /* At this point the total size of the symbol table and string table + are finalized. */ + compute_more_section_offsets (); + + /* Copy any GDB symbol segments from input files. */ + write_symsegs (); + + /* Now that everything is known about the output file, write its header. */ + write_header (); + + close (outdesc); + + mask = umask (0); + umask (mask); + + if (chmod (output_filename, filemode | (0111 & ~mask)) == -1) + perror_name (output_filename); +} + +void modify_location (), perform_relocation (), copy_text (), copy_data (); + +/* Relocate the text segment of each input file + and write to the output file. */ + +void +write_text () +{ + if (trace_files) + fprintf (stderr, "Copying and relocating text:\n\n"); + + lseek (outdesc, output_text_offset + text_header_size, 0); + + each_full_file (copy_text, 0); + file_close (); + + if (trace_files) + fprintf (stderr, "\n"); + + padfile (text_pad, outdesc); +} + +/* Read in all of the relocation information */ + +void +read_relocation () +{ + each_full_file (read_file_relocation, 0); +} + +/* Read in the relocation sections of ENTRY if necessary */ + +void +read_file_relocation (entry) + struct file_entry *entry; +{ + register struct relocation_info *reloc; + int desc; + int read_return; + + desc = -1; + if (!entry->textrel) + { + reloc = (struct relocation_info *) xmalloc (entry->text_reloc_size); + desc = file_open (entry); + lseek (desc, entry->starting_offset + entry->text_reloc_offset, L_SET); + if (entry->text_reloc_size != (read_return = read (desc, reloc, entry->text_reloc_size))) + { + fprintf (stderr, "Return from read: %d\n", read_return); + fatal_with_file ("premature eof in text relocation of ", entry); + } + entry->textrel = reloc; + } + + if (!entry->datarel) + { + reloc = (struct relocation_info *) xmalloc (entry->data_reloc_size); + if (desc == -1) desc = file_open (entry); + lseek (desc, entry->starting_offset + entry->data_reloc_offset, L_SET); + if (entry->data_reloc_size != read (desc, reloc, entry->data_reloc_size)) + fatal_with_file ("premature eof in data relocation of ", entry); + entry->datarel = reloc; + } + +#ifdef MACH_O + if (entry->file_type == IS_MACH_O) + { + translate_mach_o_relocation (entry, entry->textrel, + entry->text_reloc_size / sizeof (struct relocation_info)); + translate_mach_o_relocation (entry, entry->datarel, + entry->data_reloc_size / sizeof (struct relocation_info)); + } +#endif +} + +/* Read the text segment contents of ENTRY, relocate them, + and write the result to the output file. + If `-r', save the text relocation for later reuse. */ + +void +copy_text (entry) + struct file_entry *entry; +{ + register char *bytes; + register int desc; + register struct relocation_info *reloc; + + if (trace_files) + prline_file_name (entry, stderr); + + desc = file_open (entry); + + /* Allocate space for the file's text section */ + + bytes = (char *) alloca (entry->text_size); + + /* Deal with relocation information however is appropriate */ + + if (entry->textrel) reloc = entry->textrel; + else if (output_style == OUTPUT_RELOCATABLE) + { + read_file_relocation (entry); + reloc = entry->textrel; + } + else + { + reloc = (struct relocation_info *) alloca (entry->text_reloc_size); + lseek (desc, entry->starting_offset + entry->text_reloc_offset, L_SET); + if (entry->text_reloc_size != read (desc, reloc, entry->text_reloc_size)) + fatal_with_file ("premature eof in text relocation of ", entry); +#ifdef MACH_O + if (entry->file_type == IS_MACH_O) + translate_mach_o_relocation (entry, reloc, + entry->text_reloc_size / sizeof (struct relocation_info)); +#endif + } + + /* Read the text section into core. */ + + lseek (desc, entry->starting_offset + entry->text_offset, L_SET); + if (entry->text_size != read (desc, bytes, entry->text_size)) + fatal_with_file ("premature eof in text section of ", entry); + + /* Relocate the text according to the text relocation. */ + + perform_relocation (bytes, entry->text_start_address - entry->orig_text_address, + entry->text_size, reloc, entry->text_reloc_size, entry); + + /* Write the relocated text to the output file. */ + + mywrite (bytes, 1, entry->text_size, outdesc); +} + +/* Relocate the data segment of each input file + and write to the output file. */ + +void +write_data () +{ + if (trace_files) + fprintf (stderr, "Copying and relocating data:\n\n"); + + lseek (outdesc, output_data_offset, 0); + + each_full_file (copy_data, 0); + file_close (); + + /* Write out the set element vectors. See digest symbols for + description of length of the set vector section. */ + + if (set_vector_count) + mywrite (set_vectors, 2 * set_symbol_count + set_vector_count, + sizeof (unsigned long), outdesc); + + if (trace_files) + fprintf (stderr, "\n"); + + padfile (data_pad, outdesc); +} + +/* Read the data segment contents of ENTRY, relocate them, + and write the result to the output file. + If `-r', save the data relocation for later reuse. + See comments in `copy_text'. */ + +void +copy_data (entry) + struct file_entry *entry; +{ + register struct relocation_info *reloc; + register char *bytes; + register int desc; + + if (trace_files) + prline_file_name (entry, stderr); + + desc = file_open (entry); + + bytes = (char *) alloca (entry->data_size); + + if (entry->datarel) reloc = entry->datarel; + else if (output_style == OUTPUT_RELOCATABLE) /* Will need this again */ + { + read_file_relocation (entry); + reloc = entry->datarel; + } + else + { + reloc = (struct relocation_info *) alloca (entry->data_reloc_size); + lseek (desc, entry->starting_offset + entry->data_reloc_offset, L_SET); + if (entry->data_reloc_size != read (desc, reloc, entry->data_reloc_size)) + fatal_with_file ("premature eof in data relocation of ", entry); +#ifdef MACH_O + if (entry->file_type == IS_MACH_O) + translate_mach_o_relocation (entry, reloc, + entry->data_reloc_size / sizeof (struct relocation_info)); +#endif + } + + lseek (desc, entry->starting_offset + entry->data_offset, L_SET); + if (entry->data_size != read (desc, bytes, entry->data_size)) + fatal_with_file ("premature eof in data section of ", entry); + + perform_relocation (bytes, entry->data_start_address - entry->orig_data_address, + entry->data_size, reloc, entry->data_reloc_size, entry); + + mywrite (bytes, 1, entry->data_size, outdesc); +} + +/* Relocate ENTRY's text or data section contents. + DATA is the address of the contents, in core. + DATA_SIZE is the length of the contents. + PC_RELOCATION is the difference between the address of the contents + in the output file and its address in the input file. + RELOC_INFO is the address of the relocation info, in core. + RELOC_SIZE is its length in bytes. */ +/* This version is about to be severly hacked by Randy. Hope it + works afterwards. */ +void +perform_relocation (data, pc_relocation, data_size, reloc_info, reloc_size, entry) + char *data; + struct relocation_info *reloc_info; + struct file_entry *entry; + int pc_relocation; + int data_size; + int reloc_size; +{ + register struct relocation_info *p = reloc_info; + struct relocation_info *end + = reloc_info + reloc_size / sizeof (struct relocation_info); + int text_relocation = entry->text_start_address - entry->orig_text_address; + int data_relocation = entry->data_start_address - entry->orig_data_address; + int bss_relocation = entry->bss_start_address - entry->orig_bss_address; + + for (; p < end; p++) + { + register int relocation = 0; + register int addr = RELOC_ADDRESS(p); + register unsigned int mask = 0; + + if (addr >= data_size) + fatal_with_file ("relocation address out of range in ", entry); + + if (RELOC_EXTERN_P(p)) + { + int symindex = RELOC_SYMBOL (p) * sizeof (struct nlist); + symbol *sp = ((symbol *) + (((struct nlist *) + (((char *)entry->symbols) + symindex)) + ->n_un.n_name)); + +#ifdef N_INDR + /* Resolve indirection */ + if ((sp->defined & ~N_EXT) == N_INDR) + sp = (symbol *) sp->value; +#endif + + if (symindex >= entry->syms_size) + fatal_with_file ("relocation symbolnum out of range in ", entry); + + /* If the symbol is undefined, leave it at zero. */ + if (! sp->defined) + relocation = 0; + else + relocation = sp->value; + } + else switch (RELOC_TYPE(p)) + { + case N_TEXT: + case N_TEXT | N_EXT: + relocation = text_relocation; + break; + + case N_DATA: + case N_DATA | N_EXT: + relocation = data_relocation; + break; + + case N_BSS: + case N_BSS | N_EXT: + relocation = bss_relocation; + break; + + case N_ABS: + case N_ABS | N_EXT: + /* Don't know why this code would occur, but apparently it does. */ + break; + + default: + fatal_with_file ("nonexternal relocation code invalid in ", entry); + } + + if (RELOC_PCREL_P(p)) + relocation -= pc_relocation; + +#ifdef RELOC_ADD_EXTRA + relocation += RELOC_ADD_EXTRA(p); + if (output_style == OUTPUT_RELOCATABLE) + { + /* If this RELOC_ADD_EXTRA is 0, it means that the + symbol was external and the relocation does not + need a fixup here. */ + if (RELOC_ADD_EXTRA (p)) + { + if (! RELOC_PCREL_P (p)) + RELOC_ADD_EXTRA (p) = relocation; + else + RELOC_ADD_EXTRA (p) -= pc_relocation; + } +#if 0 + if (! RELOC_PCREL_P (p)) + { + if ((int)p->r_type <= RELOC_32 + || RELOC_EXTERN_P (p) == 0) + RELOC_ADD_EXTRA (p) = relocation; + } + else if (RELOC_EXTERN_P (p)) + RELOC_ADD_EXTRA (p) -= pc_relocation; +#endif + continue; + } +#endif + + relocation >>= RELOC_VALUE_RIGHTSHIFT(p); + + /* Unshifted mask for relocation */ + mask = 1 << RELOC_TARGET_BITSIZE(p) - 1; + mask |= mask - 1; + + /* Shift everything up to where it's going to be used */ + relocation <<= RELOC_TARGET_BITPOS(p); + mask <<= RELOC_TARGET_BITPOS(p); + +#ifdef ns32000 + /* This code by Ian Dall for the ns32k displacements */ + { + char * loc = (data + addr); + int bytes = (1 << RELOC_TARGET_SIZE(p)); + void put_num(), put_disp(), put_imm(); + int get_num(), get_disp(), get_imm(); + switch(p->r_disp) + { + case 0: + if (RELOC_MEMORY_SUB_P(p)) + put_imm(loc, relocation - get_imm(loc, bytes), bytes); + else if (RELOC_MEMORY_ADD_P(p)) + put_imm(loc, relocation + get_imm(loc, bytes), bytes); + break; + case 1: + if (RELOC_MEMORY_SUB_P(p)) + put_disp(loc, relocation - get_disp(loc, bytes), bytes); + else if (RELOC_MEMORY_ADD_P(p)) + put_disp(loc, relocation + get_disp(loc, bytes), bytes); + break; + case 2: + if (RELOC_MEMORY_SUB_P(p)) + put_num(loc, relocation - get_num(loc, bytes), bytes); + else if (RELOC_MEMORY_ADD_P(p)) + put_num(loc, relocation + get_num(loc, bytes), bytes); + break; + } + } +#else + switch (RELOC_TARGET_SIZE(p)) + { + case 0: + if (RELOC_MEMORY_SUB_P(p)) + relocation -= *(char *) (data + addr); + else if (RELOC_MEMORY_ADD_P(p)) + relocation += *(char *) (data + addr); + *(char *) (data + addr) &= ~mask; + *(char *) (data + addr) |= relocation & mask; + break; + + case 1: + if (RELOC_MEMORY_SUB_P(p)) + relocation -= *(short *) (data + addr); + else if (RELOC_MEMORY_ADD_P(p)) + relocation += *(short *) (data + addr); + *(short *) (data + addr) &= ~mask; + *(short *) (data + addr) |= relocation & mask; + break; + + case 2: +#ifdef CROSS_LINKER + /* This is necessary if the host has stricter alignment + than the target. Too slow to use all the time. + Also doesn't deal with differing byte-order. */ + { + /* Thing to relocate. */ + long thing; + bcopy (data + addr, &thing, sizeof (thing)); + if (RELOC_MEMORY_SUB_P (p)) + relocation -= thing; + else if (RELOC_MEMORY_ADD_P (p)) + relocation += thing; + thing = (thing & ~mask) | relocation & mask; + bcopy (&thing, data + addr, sizeof (thing)); + } +#else /* not CROSS_LINKER */ + if (RELOC_MEMORY_SUB_P(p)) + relocation -= *(long *) (data + addr); + else if (RELOC_MEMORY_ADD_P(p)) + relocation += *(long *) (data + addr); + *(long *) (data + addr) &= ~mask; + *(long *) (data + addr) |= relocation & mask; +#endif /* not CROSS_LINKER */ + break; + + default: + fatal_with_file ("Unimplemented relocation field length in ", entry); + } +#endif /* MINIX */ + } +} + +/* For OUTPUT_RELOCATABLE only: write out the relocation, + relocating the addresses-to-be-relocated. */ + +void coptxtrel (), copdatrel (); + +void +write_rel () +{ + register int i; + register int count = 0; + + if (trace_files) + fprintf (stderr, "Writing text relocation:\n\n"); + + /* Assign each global symbol a sequence number, giving the order + in which `write_syms' will write it. + This is so we can store the proper symbolnum fields + in relocation entries we write. */ + + for (i = 0; i < TABSIZE; i++) + { + symbol *sp; + for (sp = symtab[i]; sp; sp = sp->link) + if (sp->referenced || sp->defined) + { + sp->def_count = count++; +#ifndef NeXT + /* Leave room for the reference required by N_INDR, if + necessary. */ + if ((sp->defined & ~N_EXT) == N_INDR) + count++; +#endif + } + } + /* Correct, because if (OUTPUT_RELOCATABLE), we will also be writing + whatever indirect blocks we have. */ +#ifndef NeXT + if (count != defined_global_sym_count + + undefined_global_sym_count + global_indirect_count) +#else + if (count != defined_global_sym_count + + undefined_global_sym_count) +#endif + fatal ("internal error"); + + /* Write out the relocations of all files, remembered from copy_text. */ + + lseek (outdesc, output_trel_offset, 0); + each_full_file (coptxtrel, 0); + + if (trace_files) + fprintf (stderr, "\nWriting data relocation:\n\n"); + + lseek (outdesc, output_drel_offset, 0); + each_full_file (copdatrel, 0); + + if (trace_files) + fprintf (stderr, "\n"); +} + +void +coptxtrel (entry) + struct file_entry *entry; +{ + register struct relocation_info *p, *end; + register int reloc = entry->text_start_address - text_start; + + p = entry->textrel; + end = (struct relocation_info *) (entry->text_reloc_size + (char *) p); + while (p < end) + { + RELOC_ADDRESS(p) += reloc; + if (RELOC_EXTERN_P(p)) + { + register int symindex = RELOC_SYMBOL(p) * sizeof (struct nlist); + symbol *symptr = ((symbol *) + (((struct nlist *) + (((char *)entry->symbols) + symindex)) + ->n_un.n_name)); + + if (symindex >= entry->syms_size) + fatal_with_file ("relocation symbolnum out of range in ", entry); + +#ifdef N_INDR + /* Resolve indirection. */ + if ((symptr->defined & ~N_EXT) == N_INDR) + symptr = (symbol *) symptr->value; +#endif + + /* If the symbol is now defined, change the external relocation + to an internal one. */ + + if (symptr->defined) + { + RELOC_EXTERN_P(p) = 0; + RELOC_SYMBOL(p) = (symptr->defined & ~N_EXT); +#ifdef RELOC_ADD_EXTRA + /* If we aren't going to be adding in the value in + memory on the next pass of the loader, then we need + to add it in from the relocation entry. Otherwise + the work we did in this pass is lost. */ + if (!RELOC_MEMORY_ADD_P(p)) + RELOC_ADD_EXTRA (p) += symptr->value; +#endif + } + else + /* Debugger symbols come first, so have to start this + after them. */ +#ifndef NeXT + RELOC_SYMBOL(p) = (symptr->def_count + nsyms + - defined_global_sym_count + - undefined_global_sym_count + - global_indirect_count); +#else + RELOC_SYMBOL(p) = (symptr->def_count + nsyms + - defined_global_sym_count + - undefined_global_sym_count); +#endif + } + p++; + } + +#ifdef MACH_O + if (output_file_type == IS_MACH_O) + generate_mach_o_relocations(entry->textrel, + entry->text_reloc_size / sizeof (struct relocation_info)); +#endif + + mywrite (entry->textrel, 1, entry->text_reloc_size, outdesc); +} + +void +copdatrel (entry) + struct file_entry *entry; +{ + register struct relocation_info *p, *end; + /* Relocate the address of the relocation. + Old address is relative to start of the input file's data section. + New address is relative to start of the output file's data section. + + So the amount we need to relocate it by is the offset of this + input file's data section within the output file's data section. */ + register int reloc = entry->data_start_address - data_start; + + p = entry->datarel; + end = (struct relocation_info *) (entry->data_reloc_size + (char *) p); + while (p < end) + { + RELOC_ADDRESS(p) += reloc; + if (RELOC_EXTERN_P(p)) + { + register int symindex = RELOC_SYMBOL(p) * sizeof (struct nlist); + symbol *symptr = ((symbol *) + (((struct nlist *) + (((char *)entry->symbols) + symindex)) + ->n_un.n_name)); + int symtype; + + if (symindex >= entry->syms_size) + fatal_with_file ("relocation symbolnum out of range in ", entry); + +#ifdef N_INDR + /* Resolve indirection. */ + if ((symptr->defined & ~N_EXT) == N_INDR) + symptr = (symbol *) symptr->value; +#endif + + symtype = symptr->defined & ~N_EXT; + + if (force_common_definition + || symtype == N_DATA || symtype == N_TEXT || symtype == N_ABS) + { + RELOC_EXTERN_P(p) = 0; + RELOC_SYMBOL(p) = symtype; + } + else + /* Debugger symbols come first, so have to start this + after them. */ +#ifndef NeXT + RELOC_SYMBOL(p) + = (((symbol *) + (((struct nlist *) + (((char *)entry->symbols) + symindex)) + ->n_un.n_name)) + ->def_count + + nsyms - defined_global_sym_count + - undefined_global_sym_count + - global_indirect_count); +#else + RELOC_SYMBOL(p) + = (((symbol *) + (((struct nlist *) + (((char *)entry->symbols) + symindex)) + ->n_un.n_name)) + ->def_count + + nsyms - defined_global_sym_count + - undefined_global_sym_count); +#endif + } + p++; + } +#ifdef MACH_O + if (output_file_type == IS_MACH_O) + generate_mach_o_relocations(entry->datarel, + entry->data_reloc_size / sizeof (struct relocation_info)); +#endif + + mywrite (entry->datarel, 1, entry->data_reloc_size, outdesc); +} + +void write_file_syms (); +void write_string_table (); + +/* Total size of string table strings allocated so far, + including strings in `strtab_vector'. */ +int strtab_size; + +/* Vector whose elements are strings to be added to the string table. */ +char **strtab_vector; + +/* Vector whose elements are the lengths of those strings. */ +int *strtab_lens; + +/* Index in `strtab_vector' at which the next string will be stored. */ +int strtab_index; + +/* Add the string NAME to the output file string table. + Record it in `strtab_vector' to be output later. + Return the index within the string table that this string will have. */ + +int +assign_string_table_index (name) + char *name; +{ + register int index = strtab_size; + register int len = strlen (name) + 1; + + strtab_size += len; + strtab_vector[strtab_index] = name; + strtab_lens[strtab_index++] = len; + + return index; +} + +FILE *outstream = (FILE *) 0; + +/* Write the contents of `strtab_vector' into the string table. + This is done once for each file's local&debugger symbols + and once for the global symbols. */ + +void +write_string_table () +{ + register int i; + + lseek (outdesc, output_strs_offset + output_strs_size, 0); + + if (!outstream) + outstream = fdopen (outdesc, "w"); + + for (i = 0; i < strtab_index; i++) + { + fwrite (strtab_vector[i], 1, strtab_lens[i], outstream); + output_strs_size += strtab_lens[i]; + } + + fflush (outstream); + + /* Report I/O error such as disk full. */ + if (ferror (outstream)) + perror_name (output_filename); +} + +/* Write the symbol table and string table of the output file. */ + +void +write_syms () +{ + /* Number of symbols written so far. */ + int syms_written = 0; + register int i; + register symbol *sp; + + /* Buffer big enough for all the global symbols. One + extra struct for each indirect symbol to hold the extra reference + following. */ + struct nlist *buf +#ifndef NeXT + = (struct nlist *) alloca ((defined_global_sym_count + + undefined_global_sym_count + + global_indirect_count) + * sizeof (struct nlist)); +#else + = (struct nlist *) alloca ((defined_global_sym_count + + undefined_global_sym_count) + * sizeof (struct nlist)); +#endif + /* Pointer for storing into BUF. */ + register struct nlist *bufp = buf; + + /* Size of string table includes the bytes that store the size. */ + strtab_size = sizeof strtab_size; + + output_syms_size = 0; + output_strs_size = strtab_size; + + if (strip_symbols == STRIP_ALL) + return; + + /* Write the local symbols defined by the various files. */ + + each_file (write_file_syms, &syms_written); + file_close (); + + /* Now write out the global symbols. */ + + /* Allocate two vectors that record the data to generate the string + table from the global symbols written so far. This must include + extra space for the references following indirect outputs. */ + + strtab_vector = (char **) alloca ((num_hash_tab_syms + + global_indirect_count) * sizeof (char *)); + strtab_lens = (int *) alloca ((num_hash_tab_syms + + global_indirect_count) * sizeof (int)); + strtab_index = 0; + + /* Scan the symbol hash table, bucket by bucket. */ + + for (i = 0; i < TABSIZE; i++) + for (sp = symtab[i]; sp; sp = sp->link) + { + struct nlist nl; + +#ifdef N_SECT + nl.n_sect = 0; +#else + nl.n_other = 0; +#endif + nl.n_desc = 0; + + /* Compute a `struct nlist' for the symbol. */ + + if (sp->defined || sp->referenced) + { + /* common condition needs to be before undefined condition */ + /* because unallocated commons are set undefined in */ + /* digest_symbols */ + if (sp->defined > 1) /* defined with known type */ + { + /* If the target of an indirect symbol has been + defined and we are outputting an executable, + resolve the indirection; it's no longer needed */ + if (output_style != OUTPUT_RELOCATABLE + && ((sp->defined & ~N_EXT) == N_INDR) + && (((symbol *) sp->value)->defined > 1)) + { + symbol *newsp = (symbol *) sp->value; + nl.n_type = newsp->defined; + nl.n_value = newsp->value; + } + else + { + nl.n_type = sp->defined; + if (sp->defined != (N_INDR | N_EXT)) + nl.n_value = sp->value; + else + nl.n_value = 0; + } + } + else if (sp->max_common_size) /* defined as common but not allocated. */ + { + /* happens only with -r and not -d */ + /* write out a common definition */ + nl.n_type = N_UNDF | N_EXT; + nl.n_value = sp->max_common_size; + } + else if (!sp->defined) /* undefined -- legit only if -r */ + { + nl.n_type = N_UNDF | N_EXT; + nl.n_value = 0; + } + else + fatal ("internal error: %s defined in mysterious way", sp->name); + + /* Allocate string table space for the symbol name. */ + + nl.n_un.n_strx = assign_string_table_index (sp->name); + + /* Output to the buffer and count it. */ + + *bufp++ = nl; + syms_written++; + if (nl.n_type == (N_INDR | N_EXT)) +#ifndef NeXT + { + struct nlist xtra_ref; + xtra_ref.n_type == N_EXT | N_UNDF; + xtra_ref.n_un.n_strx + = assign_string_table_index (((symbol *) sp->value)->name); +#ifdef N_SECT + xtra_ref.n_sect = 0; +#else + xtra_ref.n_other = 0; +#endif + xtra_ref.n_desc = 0; + xtra_ref.n_value = 0; + *bufp++ = xtra_ref; + syms_written++; + } +#else + nl.n_value = assign_string_table_index (((symbol *) sp->value)->name); +#endif + } + } + +#ifdef MACH_O + if (output_file_type == IS_MACH_O) + generate_mach_o_symbols(buf, bufp - buf); +#endif + + /* Output the buffer full of `struct nlist's. */ + + lseek (outdesc, output_syms_offset + output_syms_size, 0); + mywrite (buf, sizeof (struct nlist), bufp - buf, outdesc); + output_syms_size += sizeof (struct nlist) * (bufp - buf); + + if (syms_written != nsyms) + fatal ("internal error: wrong number of symbols written into output file", 0); + + /* Now the total string table size is known, so write it into the + first word of the string table. */ + + lseek (outdesc, output_strs_offset, 0); + mywrite (&strtab_size, sizeof (int), 1, outdesc); + + /* Write the strings for the global symbols. */ + + write_string_table (); +} + +/* Write the local and debugger symbols of file ENTRY. + Increment *SYMS_WRITTEN_ADDR for each symbol that is written. */ + +/* Note that we do not combine identical names of local symbols. + dbx or gdb would be confused if we did that. */ + +void +write_file_syms (entry, syms_written_addr) + struct file_entry *entry; + int *syms_written_addr; +{ + register struct nlist *p = entry->symbols; + register struct nlist *end = p + entry->syms_size / sizeof (struct nlist); + + /* Buffer to accumulate all the syms before writing them. + It has one extra slot for the local symbol we generate here. */ + struct nlist *buf + = (struct nlist *) alloca (entry->syms_size + sizeof (struct nlist)); + register struct nlist *bufp = buf; + + /* Upper bound on number of syms to be written here. */ + int max_syms = (entry->syms_size / sizeof (struct nlist)) + 1; + + /* Make tables that record, for each symbol, its name and its name's length. + The elements are filled in by `assign_string_table_index'. */ + + strtab_vector = (char **) alloca (max_syms * sizeof (char *)); + strtab_lens = (int *) alloca (max_syms * sizeof (int)); + strtab_index = 0; + + /* Generate a local symbol for the start of this file's text. */ + +#ifndef OFILE_FN_FLAGGED + if (discard_locals != DISCARD_ALL) +#endif + { + struct nlist nl; + +#ifndef OFILE_FN_FLAGGED + nl.n_type = N_TEXT; +#else + nl.n_type = N_FN | N_EXT; +#endif + nl.n_un.n_strx = assign_string_table_index (entry->local_sym_name); + nl.n_value = entry->text_start_address; + nl.n_desc = 0; +#ifdef N_SECT + nl.n_sect = 0; +#else + nl.n_other = 0; +#endif + *bufp++ = nl; + (*syms_written_addr)++; + entry->local_syms_offset = *syms_written_addr * sizeof (struct nlist); + } + + /* Read the file's string table. */ + + entry->strings = (char *) alloca (entry->strs_size); + read_entry_strings (file_open (entry), entry); + + for (; p < end; p++) + { + register int type = p->n_type; + register int write = 0; + + /* WRITE gets 1 for a non-global symbol that should be written. */ + + + if (SET_ELEMENT_P (type)) /* This occurs even if global. These */ + /* types of symbols are never written */ + /* globally, though they are stored */ + /* globally. */ + write = output_style == OUTPUT_RELOCATABLE; + else if (!(type & (N_STAB | N_EXT))) + /* ordinary local symbol */ + write = ((discard_locals != DISCARD_ALL) + && !(discard_locals == DISCARD_L && + (p->n_un.n_strx + entry->strings)[0] == LPREFIX) + && type != N_WARNING); + else if (!(type & N_EXT)) + /* debugger symbol */ + write = (strip_symbols == STRIP_NONE); + + if (write) + { + /* If this symbol has a name, + allocate space for it in the output string table. */ + + if (p->n_un.n_strx) + p->n_un.n_strx = assign_string_table_index (p->n_un.n_strx + + entry->strings); + + /* Output this symbol to the buffer and count it. */ + + *bufp++ = *p; + (*syms_written_addr)++; + } + } + +#ifdef MACH_O + if (output_file_type == IS_MACH_O) + generate_mach_o_symbols(buf, bufp - buf); +#endif + + /* All the symbols are now in BUF; write them. */ + + lseek (outdesc, output_syms_offset + output_syms_size, 0); + mywrite (buf, sizeof (struct nlist), bufp - buf, outdesc); + output_syms_size += sizeof (struct nlist) * (bufp - buf); + + /* Write the string-table data for the symbols just written, + using the data in vectors `strtab_vector' and `strtab_lens'. */ + + write_string_table (); + entry->strings = 0; /* Since it will dissapear anyway. */ +} + +/* Copy any GDB symbol segments from the input files to the output file. + The contents of the symbol segment is copied without change + except that we store some information into the beginning of it. */ + +void write_file_symseg (); + +void +write_symsegs () +{ + lseek (outdesc, output_symseg_offset, 0); + each_file (write_file_symseg, 0); +} + +void +write_file_symseg (entry) + struct file_entry *entry; +{ + char buffer[4096]; + struct symbol_root root; + int indesc, len, total; + + if (entry->symseg_size == 0) + return; + + output_symseg_size += entry->symseg_size; + + /* This entry has a symbol segment. Read the root of the segment. */ + + indesc = file_open (entry); + lseek (indesc, entry->symseg_offset + entry->starting_offset, 0); + if (sizeof root != read (indesc, &root, sizeof root)) + fatal_with_file ("premature end of file in symbol segment of ", entry); + + /* Store some relocation info into the root. */ + + root.ldsymoff = entry->local_syms_offset; + root.textrel = entry->text_start_address - entry->orig_text_address; + root.datarel = entry->data_start_address - entry->orig_data_address; + root.bssrel = entry->bss_start_address - entry->orig_bss_address; + root.databeg = entry->data_start_address - root.datarel; + root.bssbeg = entry->bss_start_address - root.bssrel; + + /* Write the modified root into the output file. */ + + mywrite (&root, sizeof root, 1, outdesc); + + /* Copy the rest of the symbol segment unchanged. */ + + total = entry->symseg_size - sizeof root; + + while (total > 0) + { + len = read (indesc, buffer, min (sizeof buffer, total)); + + if (len != min (sizeof buffer, total)) + fatal_with_file ("premature end of file in symbol segment of ", entry); + total -= len; + mywrite (buffer, len, 1, outdesc); + } + + file_close (); +} + +/* Define a special symbol (etext, edata, or end). NAME is the + name of the symbol, with a leading underscore (whether or not this + system uses such underscores). TYPE is its type (e.g. N_DATA | N_EXT). + Store a symbol * for the symbol in *SYM if SYM is non-NULL. */ +static void +symbol_define (name, type, sym) + /* const */ char *name; + int type; + symbol **sym; +{ + symbol *thesym; + +#if defined(nounderscore) + /* Skip the leading underscore. */ + name++; +#endif + + thesym = getsym (name); + if (thesym->defined) + { + /* The symbol is defined in some input file. Don't mess with it. */ + if (sym) + *sym = 0; + } + else + { + if (thesym->referenced) + /* The symbol was not defined, and we are defining it now. */ + undefined_global_sym_count--; + thesym->defined = type; + thesym->referenced = 1; + if (sym) + *sym = thesym; + } +} + +/* Create the symbol table entries for `etext', `edata' and `end'. */ + +void +symtab_init () +{ + symbol_define ("_edata", N_DATA | N_EXT, &edata_symbol); + symbol_define ("_etext", N_TEXT | N_EXT, &etext_symbol); + symbol_define ("_end", N_BSS | N_EXT, &end_symbol); + + /* Either _edata or __edata (C names) is OK as far as ANSI is concerned + (see section 4.1.2.1). In general, it is best to use __foo and + not worry about the confusing rules for the _foo namespace. + But HPUX 7.0 uses _edata, so we might as weel be consistent. */ + symbol_define ("__edata", N_DATA | N_EXT, &edata_symbol_alt); + symbol_define ("__etext", N_TEXT | N_EXT, &etext_symbol_alt); + symbol_define ("__end", N_BSS | N_EXT, &end_symbol_alt); + +#ifdef sun + { + symbol *dynamic_symbol; + symbol_define ("__DYNAMIC", N_ABS | N_EXT, &dynamic_symbol); + if (dynamic_symbol) + dynamic_symbol->value = 0; + } +#endif +#ifdef sequent + { + symbol *i387_flt_symbol; + symbol_define ("_387_flt", N_ABS | N_EXT, &i387_flt_symbol); + if (i387_flt_symbol) + i387_flt_symbol->value = 0; + } +#endif +#ifdef NeXT + { + symbol *shlib_init_symbol; + symbol_define ("__shared_library_initialization", N_UNDF | N_EXT, &shlib_init_symbol); + if (shlib_init_symbol) + shlib_init_symbol->max_common_size = sizeof (long int); + } +#endif +} + +/* Compute the hash code for symbol name KEY. */ + +int +hash_string (key) + char *key; +{ + register char *cp; + register int k; + + cp = key; + k = 0; + while (*cp) + k = (((k << 1) + (k >> 14)) ^ (*cp++)) & 0x3fff; + + return k; +} + +/* Get the symbol table entry for the global symbol named KEY. + Create one if there is none. */ + +symbol * +getsym (key) + char *key; +{ + register int hashval; + register symbol *bp; + + /* Determine the proper bucket. */ + + hashval = hash_string (key) % TABSIZE; + + /* Search the bucket. */ + + for (bp = symtab[hashval]; bp; bp = bp->link) + if (! strcmp (key, bp->name)) + return bp; + + /* Nothing was found; create a new symbol table entry. */ + + bp = (symbol *) xmalloc (sizeof (symbol)); + bp->refs = 0; + bp->name = (char *) xmalloc (strlen (key) + 1); + strcpy (bp->name, key); + bp->defined = 0; + bp->referenced = 0; + bp->trace = 0; + bp->value = 0; + bp->max_common_size = 0; + bp->warning = 0; + bp->undef_refs = 0; + bp->multiply_defined = 0; + bp->last_library_ref = 0; + + /* Add the entry to the bucket. */ + + bp->link = symtab[hashval]; + symtab[hashval] = bp; + + ++num_hash_tab_syms; + + return bp; +} + +/* Like `getsym' but return 0 if the symbol is not already known. */ + +symbol * +getsym_soft (key) + char *key; +{ + register int hashval; + register symbol *bp; + + /* Determine which bucket. */ + + hashval = hash_string (key) % TABSIZE; + + /* Search the bucket. */ + + for (bp = symtab[hashval]; bp; bp = bp->link) + if (! strcmp (key, bp->name)) + return bp; + + return 0; +} + +/* Report a usage error. + Like fatal except prints a usage summary. */ + +void +usage (string, arg) + char *string, *arg; +{ + if (string) + { + fprintf (stderr, "%s: ", progname); + fprintf (stderr, string, arg); + fprintf (stderr, "\n"); + } + fprintf (stderr, "\ +Usage: %s [-d] [-dc] [-dp] [-e symbol] [-l lib] [-n] [-noinhibit-exec]\n\ + [-nostdlib] [-o file] [-r] [-s] [-t] [-u symbol] [-x] [-y symbol]\n\ + [-z] [-A file] [-Bstatic] [-D size] [-L libdir] [-M] [-N]\n\ + [-S] [-T[{text,data}] addr] [-V prefix] [-X] [file...]\n", + progname); + exit (1); +} + +/* Report a fatal error. + STRING is a printf format string and ARG is one arg for it. */ + +void +fatal (string, arg) + char *string, *arg; +{ + fprintf (stderr, "%s: ", progname); + fprintf (stderr, string, arg); + fprintf (stderr, "\n"); + exit (1); +} + +/* Report a fatal error. The error message is STRING + followed by the filename of ENTRY. */ + +void +fatal_with_file (string, entry) + char *string; + struct file_entry *entry; +{ + fprintf (stderr, "%s: ", progname); + fprintf (stderr, string); + print_file_name (entry, stderr); + fprintf (stderr, "\n"); + exit (1); +} + +/* Report a fatal error using the message for the last failed system call, + followed by the string NAME. */ + +void +perror_name (name) + char *name; +{ + extern int errno, sys_nerr; + extern char *sys_errlist[]; + char *s; + + if (errno < sys_nerr) + s = concat ("", sys_errlist[errno], " for %s"); + else + s = "cannot open %s"; + fatal (s, name); +} + +/* Report a fatal error using the message for the last failed system call, + followed by the name of file ENTRY. */ + +void +perror_file (entry) + struct file_entry *entry; +{ + extern int errno, sys_nerr; + extern char *sys_errlist[]; + char *s; + + if (errno < sys_nerr) + s = concat ("", sys_errlist[errno], " for "); + else + s = "cannot open "; + fatal_with_file (s, entry); +} + +/* Report a nonfatal error. + STRING is a format for printf, and ARG1 ... ARG3 are args for it. */ + +void +error (string, arg1, arg2, arg3) + char *string, *arg1, *arg2, *arg3; +{ + fprintf (stderr, "%s: ", progname); + fprintf (stderr, string, arg1, arg2, arg3); + fprintf (stderr, "\n"); +} + + +/* Output COUNT*ELTSIZE bytes of data at BUF + to the descriptor DESC. */ + +void +mywrite (buf, count, eltsize, desc) + char *buf; + int count; + int eltsize; + int desc; +{ + register int val; + register int bytes = count * eltsize; + + while (bytes > 0) + { + val = write (desc, buf, bytes); + if (val <= 0) + perror_name (output_filename); + buf += val; + bytes -= val; + } +} + +/* Output PADDING zero-bytes to descriptor OUTDESC. + PADDING may be negative; in that case, do nothing. */ + +void +padfile (padding, outdesc) + int padding; + int outdesc; +{ + register char *buf; + if (padding <= 0) + return; + + buf = (char *) alloca (padding); + bzero (buf, padding); + mywrite (buf, padding, 1, outdesc); +} + +/* Return a newly-allocated string + whose contents concatenate the strings S1, S2, S3. */ + +char * +concat (s1, s2, s3) + char *s1, *s2, *s3; +{ + register int len1 = strlen (s1), len2 = strlen (s2), len3 = strlen (s3); + register char *result = (char *) xmalloc (len1 + len2 + len3 + 1); + + strcpy (result, s1); + strcpy (result + len1, s2); + strcpy (result + len1 + len2, s3); + result[len1 + len2 + len3] = 0; + + return result; +} + +/* Parse the string ARG using scanf format FORMAT, and return the result. + If it does not parse, report fatal error + generating the error message using format string ERROR and ARG as arg. */ + +int +parse (arg, format, error) + char *arg, *format; +{ + int x; + if (1 != sscanf (arg, format, &x)) + fatal (error, arg); + return x; +} + +/* Like malloc but get fatal error if memory is exhausted. */ + +char * +xmalloc (size) + int size; +{ + register char *result = malloc (size); + if (!result) + fatal ("virtual memory exhausted", 0); + return result; +} + +/* Like realloc but get fatal error if memory is exhausted. */ + +char * +xrealloc (ptr, size) + char *ptr; + int size; +{ + register char *result = realloc (ptr, size); + if (!result) + fatal ("virtual memory exhausted", 0); + return result; +} + +#ifdef USG + +void +bzero (p, n) + char *p; +{ + memset (p, 0, n); +} + +void +bcopy (from, to, n) + char *from, *to; +{ + memcpy (to, from, n); +} + +getpagesize () +{ + return (4096); +} + +#endif +#ifdef ns32000 +/* code by Ian Dall for the ns32k */ +/* Just put out the twos complement value with arbitrary alignment could + * just do an assignment if we know we are on a little endian. */ +void put_num(buf,val,n) + char *buf; + long val; + char n; +{ + for (; n > 0; n--) + { + *buf++ = val & 0xff; val >>= 8; + } +} + +int get_num(buf, n) + char *buf; + int n; +{ + int val = 0; + buf += (n - 1); + for (; n > 0; n--) + { + val = val * 256 + (*buf-- & 0xff); + } + return val; +} + +/* Immediate operands are bigendian */ +void put_imm(buf,val,n) + char *buf; + long val; + char n; +{ + int i; + buf += (n - 1); + for (i = n - 1; i >= 0; i--) + { + *buf-- = (val & 0xff); val >>= 8; + } +} + +int get_imm(buf, n) + char *buf; + int n; +{ + int val = 0; + for (; n > 0; n--) + { + val = (val * 256) + (*buf++ & 0xff); + } + return val; +} + +/* This converts an integer "val" to a displacement. The reason for its' + existence is the fact that ns32k uses Huffman coded displacements. + This implies that the bit order is reversed in displacements and + that they are prefixed with a size-tag. + + binary: msb -> lsb 0xxxxxxx byte + 10xxxxxx xxxxxxxx word + 11xxxxxx xxxxxxxx xxxxxxxx xxxxxxxx double word + + This must be taken care of and we do it here! + */ +void put_disp(buf,val,n) + char *buf; + long val; + char n; +{ + switch(n) { + case 1: + if (val < -64 || val > 63) + fprintf(stderr,"Byte displacement %d, out of range.\n", val); + val&=0x7f; +#ifdef SHOW_NUM + printf("%x ",val & 0xff); +#endif + *buf++=val; + break; + case 2: + if (val < -8192 || val > 8191) + fprintf(stderr,"Word displacement %d, out of range.\n", val); + val&=0x3fff; + val|=0x8000; +#ifdef SHOW_NUM + printf("%x ",val>>8 & 0xff); +#endif + *buf++=(val>>8); +#ifdef SHOW_NUM + printf("%x ",val & 0xff); +#endif + *buf++=val; + break; + case 4: + if (val < -0x1f000000 || val >= 0x20000000) + /* if (val < -0x20000000 || val >= 0x20000000) */ + fprintf(stderr,"Double word displacement %d, out of range\n", val); + val|=0xc0000000; +#ifdef SHOW_NUM + printf("%x ",val>>24 & 0xff); +#endif + *buf++=(val>>24); +#ifdef SHOW_NUM + printf("%x ",val>>16 & 0xff); +#endif + *buf++=(val>>16); +#ifdef SHOW_NUM + printf("%x ",val>>8 & 0xff); +#endif + *buf++=(val>>8); +#ifdef SHOW_NUM + printf("%x ",val & 0xff); +#endif + *buf++=val; + break; + default: + error("Internal logic error"); + } +} + +int sign_extend (value, bits) + int value, bits; +{ + value = value & ((1 << bits) - 1); + return (value & (1 << (bits-1)) + ? value | (~((1 << bits) - 1)) + : value); +} + +int get_disp (buffer, n) + char *buffer; + int n; +{ + int Ivalue; + + Ivalue = *buffer++ & 0xff; + if (n == 0) + if (Ivalue & 0x80) + if (Ivalue & 0x40) + n = 4; + else + n = 2; + else + n = 1; + switch (n) + { + case 1: + Ivalue = sign_extend (Ivalue, 7); + break; + case 2: + Ivalue = sign_extend (Ivalue, 6); + Ivalue = (Ivalue << 8) | (0xff & *buffer); + break; + case 4: + Ivalue = sign_extend (Ivalue, 6); + Ivalue = (Ivalue << 8) | (0xff & *buffer++); + Ivalue = (Ivalue << 8) | (0xff & *buffer++); + Ivalue = (Ivalue << 8) | (0xff & *buffer); + break; + default: + fprintf(stderr, "get_disp: invalid argument\n"); + } + return Ivalue; +} +#endif + +#if defined(sun) && defined(sparc) +int +getpagesize () +{ + return 8192; +} +#endif diff --git a/binutils-1.9/libconvert b/binutils-1.9/libconvert new file mode 100755 index 0000000..4b3bda9 --- /dev/null +++ b/binutils-1.9/libconvert @@ -0,0 +1,46 @@ +#! /bin/sh + +if [ $# != 2 ] +then + echo 'usage: libconvert from.a to.a' + exit 1 +fi + +fromlib=$1 +tolib=$2 + +# +# Convert coff libc to a coff-encapsulated libc +# suitable for linking with the GNU linker. +# +# Extract all members of /lib/libc.a (using coff ar). +# Convert each using robotussin. +# Create new libc (using gnu ar) with members in the same order as coff libc. + +# set -e makes this script exit if any command gets an error +set -e + +case $fromlib in +/*) rel_fromlib=$fromlib ;; +*) rel_fromlib=../$fromlib ;; +esac + +case $tolib in +/*) rel_tolib=$tolib ;; +*) rel_tolib=../$tolib ;; +esac + +rm -rf libconvert-tmp +mkdir libconvert-tmp +cd libconvert-tmp +/bin/ar x $rel_fromlib +for i in * +do + echo $i + ../robotussin $i x + mv x $i +done +rm -f $rel_tolib +../ar rs $rel_tolib `/bin/ar t $rel_fromlib` +cd .. +rm -rf libconvert-tmp diff --git a/binutils-1.9/nm.c b/binutils-1.9/nm.c new file mode 100644 index 0000000..6ba3b66 --- /dev/null +++ b/binutils-1.9/nm.c @@ -0,0 +1,1204 @@ +/* Describe symbol table of a rel file. + Copyright (C) 1986, 1988, 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 <stdio.h> +#include <ar.h> +#include <sys/types.h> +#include <sys/file.h> +#include "getopt.h" + +#if !defined(A_OUT) && !defined(MACH_O) +#define A_OUT +#endif + +#ifdef A_OUT +#ifdef COFF_ENCAPSULATE +#include "a.out.encap.h" +#else +/* On native BSD systems, use the system's own a.out.h. */ +#include <a.out.h> +#endif +#endif + +#ifdef MACH_O +#ifndef A_OUT +#include <nlist.h> +#ifndef N_TEXT +#define N_TEXT 4 +#define N_DATA 6 +#define N_BSS 8 +#endif +#ifndef N_FN +#define N_FN 15 +#endif +#endif +#include <sys/loader.h> +#endif + +/* Always use the GNU version of debugging symbol type codes, if possible. */ +#include "stab.h" + +/* Struct or union for header of object file. */ + +#ifdef USG +#include <string.h> +/* You might need to compile with -I/usr/include/sys if your fcntl.h + isn't in /usr/include (which is where it should be according to POSIX). */ +#include <fcntl.h> +#else +#include <strings.h> +#endif + +/* Alloca definitions and includes... */ + +/* If compiled with GNU C, use the built-in alloca */ +#ifdef __GNUC__ +#define alloca __builtin_alloca +#else +/* If on a sun 4, make sure to include the right definition. */ +#if defined(sun) && defined(sparc) +#include "alloca.h" +#else +char *alloca (); +#endif +#endif + +char *malloc (), *realloc (); + +char *xmalloc (), *xrealloc (); + +/* C++ demangler stuff. */ +char *cplus_demangle (); + +/* Print NAME on STREAM, demangling if necessary. */ +void +fprint_name (stream, name) + FILE *stream; + char *name; +{ + char *demangled = cplus_demangle (name); + if (demangled == NULL) + fputs (name, stream); + else + { + fputs (demangled, stream); + free (demangled); + } +} + +/* Special global symbol types understood by GNU LD. */ + +/* The following type indicates the definition of a symbol as being + an indirect reference to another symbol. The other symbol + appears as an undefined reference, immediately following this symbol. + + Indirection is asymmetrical. The other symbol's value will be used + to satisfy requests for the indirect symbol, but not vice versa. + If the other symbol does not have a definition, libraries will + be searched to find a definition. */ +#ifndef N_INDR +#define N_INDR 0xa +#endif + +/* Warning message symbol. The name of this symbol will be printed if + any symbols from its file are required by the linker. */ +#ifndef N_WARNING +#define N_WARNING 0x1e +#endif + + +/* The following symbols refer to set elements. + All the N_SET[ATDB] symbols with the same name form one set. + Space is allocated for the set in the text section, and each set + element's value is stored into one word of the space. + The first word of the space is the length of the set (number of elements). + + The address of the set is made into an N_SETV symbol + whose name is the same as the name of the set. + This symbol acts like a N_DATA global symbol + in that it can satisfy undefined external references. */ + +#ifndef N_SETA +#define N_SETA 0x14 /* Absolute set element symbol */ +#endif /* This is input to LD, in a .o file. */ + +#ifndef N_SETT +#define N_SETT 0x16 /* Text set element symbol */ +#endif /* This is input to LD, in a .o file. */ + +#ifndef N_SETD +#define N_SETD 0x18 /* Data set element symbol */ +#endif /* This is input to LD, in a .o file. */ + +#ifndef N_SETB +#define N_SETB 0x1A /* Bss set element symbol */ +#endif /* This is input to LD, in a .o file. */ + +/* Macros dealing with the set element symbols defined in a.out.h */ +#define SET_ELEMENT_P(x) ((x)>=N_SETA&&(x)<=(N_SETB|N_EXT)) +#define TYPE_OF_SET_ELEMENT(x) ((x)-N_SETA+N_ABS) + +#ifndef N_SETV +#define N_SETV 0x1C /* Pointer to set vector in text area. */ +#endif /* This is output from LD. */ + +#ifndef __GNU_STAB__ + +/* Line number for the data section. This is to be used to describe + the source location of a variable declaration. */ +#ifndef N_DSLINE +#define N_DSLINE (N_SLINE+N_DATA-N_TEXT) +#endif + +/* Line number for the bss section. This is to be used to describe + the source location of a variable declaration. */ +#ifndef N_BSLINE +#define N_BSLINE (N_SLINE+N_BSS-N_TEXT) +#endif + +#endif /* not __GNU_STAB__ */ + +/* Name of this program. */ + +char *program_name; + +/* Number of input files specified. */ + +int number_of_files; + +/* Current file's name. */ + +char *input_name; + +/* Current member's name, or 0 if processing a non-library file. */ + +char *input_member; + +#ifdef MACH_O +/* Section ordinals for N_SECT references. */ +int text_section; +int data_section; +int bss_section; +#endif + +/* Offset within archive of the current member, + if we are processing an archive. */ + +int member_offset; + +/* Command options. */ + +int external_only; /* nonzero means print external symbols only. */ +int sort_numerically; /* sort in numerical order rather than alphabetic */ +int reverse_sort; /* sort in downward (alphabetic or numeric) order. */ +int no_sort; /* don't sort; print symbols in order in the file. */ +int undefined_only; /* print undefined symbols only. */ +int file_on_each_line; /* print file name on each line. */ +int debugger_syms; /* print the debugger-only symbols. */ +int print_symdefs; /* describe the __.SYMDEF data in any archive file specified. */ + +/* The __.SYMDEF member of an archive has the following format: + 1) A longword saying the size of the symdef data that follows + 2) Zero or more struct symdef filling that many bytes + 3) A longword saying how many bytes of strings follow + 4) That many bytes of string data. +*/ + +struct symdef + { + long stringoffset; /* Offset of this symbol's name in the string data */ + long offset; /* Offset in the archive of the header-data for the member that + defines this symbol. */ + }; + +/* Create a table of debugging stab-codes and corresponding names. */ +#ifdef __GNU_STAB__ +#define __define_stab(NAME, CODE, STRING) {NAME, STRING}, +struct {enum __stab_debug_code code; char *string;} stab_names[] + = { +#include "stab.def" + }; +#undef __define_stab +#endif + +char *concat (); +int decode_arg (); +void decode_switch (); +void do_one_file (); +void do_one_rel_file (); +void do_symdef_member (); +void error (); +void error_with_file (); +void fatal (); +void perror_name (); +void print_file_name (); + +void +main (argc, argv) + char **argv; + int argc; +{ + int i; + int c; + int ind; + static struct option long_options[] = + { + {"all", 0, &debugger_syms, 1}, + {"extern-only", 0, &external_only, 1}, + {"numeric-sort", 0, &sort_numerically, 1}, + {"print-file-name", 0, &file_on_each_line,1}, + {"no-sort", 0, &no_sort, 1}, + {"reverse", 0, &reverse_sort, 1}, + {"print-symdefs", 0, &print_symdefs, 1}, + {"undefined-only",0, &undefined_only, 1}, + {NULL, 0, NULL, 0} + }; + + program_name = argv[0]; + + number_of_files = 0; + external_only = 0; + sort_numerically = 0; + reverse_sort = 0; + no_sort = 0; + undefined_only = 0; + file_on_each_line = 0; + debugger_syms = 0; + print_symdefs = 0; + + while ((c = getopt_long (argc, argv, "agnoprsu", long_options, &ind)) != EOF) + switch (c) + { + case 0: + break; + + case 'a': + debugger_syms = 1; + break; + + case 'g': + external_only = 1; + break; + + case 'n': + sort_numerically = 1; + break; + + case 'o': + file_on_each_line = 1; + break; + + case 'p': + no_sort = 1; + break; + + case 'r': + reverse_sort = 1; + break; + + case 's': + print_symdefs = 1; + break; + + case 'u': + undefined_only = 1; + break; + + default: + fprintf (stderr, "\ +Usage: %s [-agnoprsu] [+all] [+extern-only] [+numeric-sort]\n\ + [+print-file-name] [+no-sort] [+reverse] [+print-symdefs]\n\ + [+undefined-only] [file...]\n", program_name); + exit (1); + break; + } + + number_of_files = argc - optind; + + /* Now scan again and print the files. */ + + if (argc == optind) + do_one_file ("a.out"); + else + for (i = optind; i < argc; i++) + do_one_file (argv[i]); + + exit (0); +} + +/* Print the filename of the current file on 'outfile' (a stdio stream). */ + +void +print_file_name (outfile) + FILE *outfile; +{ + fprintf (outfile, "%s", input_name); + if (input_member) + fprintf (outfile, "(%s)", input_member); +} + +/* process one input file */ +void scan_library (); + +void +do_one_file (name) + char *name; +{ + int desc, nchars; + char armag[SARMAG]; + + desc = open (name, O_RDONLY, 0); + + if (desc < 0) + { + perror_name (name); + return; + } + + input_name = name; + input_member = 0; + + nchars = read (desc, armag, SARMAG); + if (nchars == SARMAG && !strncmp(armag, ARMAG, SARMAG)) + scan_library (desc); + else + do_one_rel_file (desc, 0); + + close (desc); +} + +/* Read in the archive data about one member. + SUBFILE_OFFSET is the address within the archive of the start of that data. + + Return the length of the member's contents, which does + not include the archive data about the member. + A pointer to the member's name is stored into *MEMBER_NAME_PTR. + If there are no more valid members, return zero. */ + +int +decode_library_subfile (desc, subfile_offset, member_name_ptr) + int desc; + int subfile_offset; + char **member_name_ptr; +{ + int bytes_read; + int namelen; + int member_length; + char *name; + struct ar_hdr hdr1; + + lseek (desc, subfile_offset, 0); + + bytes_read = read (desc, &hdr1, sizeof hdr1); + if (!bytes_read) + ; /* end of archive */ + + else if (sizeof hdr1 != bytes_read) + error_with_file ("malformed library archive "); + + else if (sscanf (hdr1.ar_size, "%d", &member_length) != 1) + error_with_file ("malformatted header of archive member in "); + + else + { + for (namelen = 0; ; namelen++) + if (hdr1.ar_name[namelen] == 0 || hdr1.ar_name[namelen] == ' ' + /* Some systems use a slash? Strange. */ + || hdr1.ar_name[namelen] == '/') + break; + + name = (char *) xmalloc (namelen+1); + strncpy (name, hdr1.ar_name, namelen); + name[namelen] = 0; + + *member_name_ptr = name; + + return member_length; + } + return 0; /* tell caller to exit loop */ +} + +/* Scan a library and describe each member. */ + +void +scan_library (desc) + int desc; +{ + int this_subfile_offset = SARMAG; + int member_length; + + if (!file_on_each_line) + printf ("\n%s:\n", input_name); + + while (1) + { + member_length + = decode_library_subfile (desc, this_subfile_offset, &input_member); + if (member_length == 0) + break; + + /* Describe every member except the ranlib data if any. */ + + if (strcmp (input_member, "__.SYMDEF")) + do_one_rel_file (desc, this_subfile_offset + sizeof (struct ar_hdr)); + else if (print_symdefs) + do_symdef_member (desc, this_subfile_offset + sizeof (struct ar_hdr), member_length); + + this_subfile_offset += ((member_length + sizeof (struct ar_hdr)) + 1) & -2; + } +} + +/* Read a file's header and fill in various pieces of information. + Return 0 on failure. */ + +int +read_header_info (desc, offset, syms_offset, syms_size, strs_offset, strs_size) + int desc; + long int offset; + long int *syms_offset; + unsigned int *syms_size; + long int *strs_offset; + unsigned int *strs_size; +{ + int len; + +#ifdef A_OUT + { + struct exec hdr; + + lseek (desc, offset, 0); +#ifdef HEADER_SEEK_FD + /* Skip the headers that encapsulate our data in some other format + such as COFF. */ + HEADER_SEEK_FD (desc); +#endif + len = read (desc, (char *) &hdr, sizeof (struct exec)); + if (len == sizeof (struct exec) && !N_BADMAG (hdr)) + { + *syms_offset = N_SYMOFF(hdr); + *syms_size = hdr.a_syms; + *strs_offset = N_STROFF(hdr); + lseek(desc, N_STROFF(hdr) + offset, 0); + if (read (desc, (char *) strs_size, sizeof *strs_size) != sizeof *strs_size) + { + error_with_file ("cannot read string table size in "); + return 0; + } + return 1; + } + } +#endif + +#ifdef MACH_O + { + struct mach_header mach_header; + char *hdrbuf; + struct load_command *load_command; + struct segment_command *segment_command; + struct section *section; + struct symtab_command *symtab_command; + int symtab_seen; + int len, cmd, seg, ordinal; + + symtab_seen = 0; + + lseek (desc, offset, 0); + len = read (desc, (char *) &mach_header, sizeof (struct mach_header)); + if (len == sizeof (struct mach_header) && mach_header.magic == MH_MAGIC) + { + hdrbuf = xmalloc (mach_header.sizeofcmds); + len = read (desc, hdrbuf, mach_header.sizeofcmds); + if (len != mach_header.sizeofcmds) + { + error_with_file ("failure reading Mach-O load commands in "); + return 0; + } + load_command = (struct load_command *) hdrbuf; + ordinal = 1; + for (cmd = 0; cmd < mach_header.ncmds; ++cmd) + { + switch (load_command->cmd) + { + case LC_SEGMENT: + segment_command = (struct segment_command *) load_command; + section = (struct section *) ((char *) (segment_command + 1)); + for (seg = 0; seg < segment_command->nsects; ++seg, ++section, ++ordinal) + { + if (!strncmp(SECT_TEXT, section->sectname, sizeof section->sectname)) + text_section = ordinal; + else if (!strncmp(SECT_DATA, section->sectname, sizeof section->sectname)) + data_section = ordinal; + else if (!strncmp(SECT_BSS, section->sectname, sizeof section->sectname)) + bss_section = ordinal; + } + break; + case LC_SYMTAB: + if (symtab_seen) + error_with_file ("more than one LC_SYMTAB in "); + else + { + symtab_seen = 1; + symtab_command = (struct symtab_command *) load_command; + *syms_offset = symtab_command->symoff; + *syms_size = symtab_command->nsyms * sizeof (struct nlist); + *strs_offset = symtab_command->stroff; + *strs_size = symtab_command->strsize; + } + break; + } + load_command = (struct load_command *) + ((char *) load_command + load_command->cmdsize); + } + free (hdrbuf); + return 1; + } + } +#endif + + return 0; +} + +void print_symbols (); +void print_one_symbol (); +void read_header (); +int alphacompare (); +int valuecompare (); +int filter_symbols (); + +void +do_one_rel_file (desc, offset) + int desc; + int offset; +{ + struct nlist *symbols_and_strings; + int symcount; + int totalsize; + char *strings; + long int syms_offset, strs_offset; + unsigned int syms_size, strs_size; + + if (!read_header_info (desc, offset, &syms_offset, &syms_size, &strs_offset, &strs_size)) + { + error_with_file ("malformed input (not a rel file or archive) in "); + return; + } + + /* Number of symbol entries in the file. */ + symcount = syms_size / sizeof (struct nlist); + + totalsize = strs_size + syms_size; + + /* Allocate space for symbol entries and string table. */ + symbols_and_strings = (struct nlist *) xmalloc (totalsize); + strings = (char *) symbols_and_strings + syms_size; + + /* Read them both in. */ + lseek (desc, syms_offset + offset, 0); + if (syms_size != read (desc, (char *) symbols_and_strings, syms_size)) + { + error_with_file ("premature end of file in symbols of "); + return; + } + lseek (desc, strs_offset + offset, 0); + if (strs_size != read (desc, (char *) strings, strs_size)) + { + error_with_file ("premature end of file in strings of "); + return; + } + + /* Identify this file, if desired. */ + + if (!file_on_each_line && (number_of_files > 1 || input_member)) + printf ("\n%s:\n", input_member ? input_member : input_name); + + /* Discard the symbols we don't want to print; compact the rest down. */ + + symcount = filter_symbols (symbols_and_strings, symcount, strings); + + /* Modify each symbol entry to point directly at the symbol name. + This is so the sort routine does not need to be passed + the value of `strings' separately. */ + + { + struct nlist *p = symbols_and_strings; + struct nlist *end = symbols_and_strings + symcount; + + for (; p < end; p++) + { + /* A zero index means there is no string. */ + if (p->n_un.n_strx != 0) + { + if (p->n_un.n_strx > 0 && p->n_un.n_strx < strs_size) + p->n_un.n_name = strings + p->n_un.n_strx; + else + { + error_with_file ("invalid string table offset in "); + return; + } + } + } + } + + /* Sort the symbols if desired. */ + + if (!no_sort) + qsort (symbols_and_strings, symcount, sizeof (struct nlist), + sort_numerically ? valuecompare : alphacompare); + + /* Print the symbols in the order they are now in. */ + + print_symbols (symbols_and_strings, symcount); + + free (symbols_and_strings); +} + +/* Choose which symbol entries to print; + compact them downward to get rid of the rest. + Return the number of symbols to be printed. */ + +int +filter_symbols (syms, symcount, strings) + struct nlist *syms; + int symcount; + char *strings; +{ + struct nlist *from = syms, *to = syms; + struct nlist *end = syms + symcount; + + while (from < end) + { + int keep = 0; + + /* undefined sym or common sym */ + if (from->n_type == N_EXT) keep = !undefined_only || !from->n_value; + /* global defined sym */ + else if (from->n_type & N_EXT) keep = !undefined_only; + /* debugger sym: normally don't print */ + else if (from->n_type & ~(N_TYPE | N_EXT)) keep = debugger_syms; + /* local sym */ + else keep = !external_only && !undefined_only; + + if (keep) + *to++ = *from; + from++; + } + + return to - syms; +} + +/* Comparison functions for sorting symbols. */ + +int +alphacompare (sym1, sym2) + struct nlist *sym1, *sym2; +{ + if (reverse_sort) + { + if (!sym2->n_un.n_name) + { + if (sym1->n_un.n_name) return -1; + else return 0; + } + if (!sym1->n_un.n_name) return 1; + return strcmp (sym2->n_un.n_name, sym1->n_un.n_name); + } + else + { + if (!sym1->n_un.n_name) + { + if (sym2->n_un.n_name) return -1; + else return 0; + } + if (!sym2->n_un.n_name) return 1; + return strcmp (sym1->n_un.n_name, sym2->n_un.n_name); + } +} + + +int +valuecompare (sym1, sym2) + struct nlist *sym1, *sym2; +{ + if (reverse_sort) + return sym2->n_value - sym1->n_value; + else + return sym1->n_value - sym2->n_value; +} + +void +print_symbols (syms, symcount) + struct nlist *syms; + int symcount; +{ + int i; + + for (i = 0; i < symcount; i++) + print_one_symbol (&syms[i]); +} + +void +print_one_symbol (sym) + struct nlist *sym; +{ + if (file_on_each_line) + { + print_file_name (stdout); + printf (":"); + } + + if (undefined_only) + { + if (sym->n_type == N_EXT && !sym->n_value) + { + fprint_name (stdout, sym->n_un.n_name); + printf ("\n"); + } + return; + } + + if (sym->n_type & ~N_EXT || sym->n_value) + printf ("%08x ", sym->n_value); + else printf (" "); + + switch (sym->n_type) + { + case N_EXT: + if (sym->n_value) printf ("C"); + else printf ("U"); + break; + + case 0: + if (sym->n_value) printf ("c"); + else printf ("u"); + break; + + case N_ABS | N_EXT: + printf ("A"); + break; + + case N_ABS: + printf ("a"); + break; + + case N_TEXT | N_EXT: + printf ("T"); + break; + + case N_TEXT: + printf ("t"); + break; + + case N_DATA | N_EXT: + printf ("D"); + break; + + case N_DATA: + printf ("d"); + break; + + case N_BSS | N_EXT: + printf ("B"); + break; + + case N_BSS: + printf ("b"); + break; + + case N_SETV | N_EXT: + printf ("V"); + break; + + case N_SETV: + printf ("v"); + break; + + case N_SETA | N_EXT: + printf ("L"); + break; + + case N_SETA: + printf ("l"); + break; + + case N_SETT | N_EXT: + printf ("X"); + break; + + case N_SETT: + printf ("x"); + break; + + case N_SETD | N_EXT: + printf ("Z"); + break; + + case N_SETD: + printf ("z"); + break; + + case N_SETB | N_EXT: + printf ("S"); + break; + + case N_SETB: + printf ("s"); + break; + + case N_INDR | N_EXT: + printf ("I"); + break; + + case N_INDR: + printf ("i"); + break; + + case N_WARNING | N_EXT: + printf ("W"); + break; + + case N_WARNING: + printf ("w"); + break; + +#ifdef N_SECT + case N_SECT: + if (sym->n_sect == text_section) + printf ("t"); + else if (sym->n_sect == data_section) + printf ("d"); + else if (sym->n_sect == bss_section) + printf ("b"); + else + printf ("%d", sym->n_sect); + break; + + case N_SECT | N_EXT: + if (sym->n_sect == text_section) + printf ("T"); + else if (sym->n_sect == data_section) + printf ("D"); + else if (sym->n_sect == bss_section) + printf ("B"); + else + printf ("%d", sym->n_sect); + break; +#endif + + default: + { + char *s; + int i; +#ifdef __GNU_STAB__ + s = ""; + for (i = sizeof (stab_names) / sizeof (stab_names[0]) - 1; + i >= 0; i--) + { + if (stab_names[i].code + == (enum __stab_debug_code) sym->n_type) + { + s = stab_names[i].string; + break; + } + } +#else /* not __GNU_STAB__ */ + switch (sym->n_type) + { + case N_GSYM: + s = "GSYM"; + break; + case N_FNAME: + s = "FNAME"; + break; + case N_FUN: + s = "FUN"; + break; + case N_STSYM: + s = "STSYM"; + break; + case N_LCSYM: + s = "LCSYM"; + break; + case N_RSYM: + s = "RSYM"; + break; + case N_SLINE: + s = "SLINE"; + break; + case N_DSLINE: + s = "DSLINE"; + break; + case N_BSLINE: + s = "BSLINE"; + break; + case N_SSYM: + s = "SSYM"; + break; + case N_SO: + s = "SO"; + break; + case N_LSYM: + s = "LSYM"; + break; + case N_SOL: + s = "SOL"; + break; + case N_PSYM: + s = "PSYM"; + break; + case N_ENTRY: + s = "ENTRY"; + break; + case N_LBRAC: + s = "LBRAC"; + break; + case N_RBRAC: + s = "RBRAC"; + break; + case N_BCOMM: + s = "BCOMM"; + break; + case N_ECOMM: + s = "ECOMM"; + break; + case N_ECOML: + s = "ECOML"; + break; + case N_LENG: + s = "LENG"; + break; + default: + s = ""; + } +#endif /* not __GNU_STAB__ */ + /* %x treats them as unsigned anyway, so if we didn't cast + them we'd get 0xffffffff for all 1's instead of 0xff + or 0xffff. */ + printf ("- %02x %04x %5s", +#ifdef N_SECT + (unsigned char) sym->n_sect, +#else + (unsigned char) sym->n_other, +#endif + (unsigned short) sym->n_desc, s); + } + } + + printf (" "); + + if (sym->n_un.n_name) + fprint_name (stdout, sym->n_un.n_name); + + printf ("\n"); +} + +void +do_symdef_member (desc, offset, member_length) + int desc; + int offset; + int member_length; +{ + int symdef_size; + int nsymdefs; + struct symdef *symdefs; + int stringsize; + char *strings; + int i; + char *member_name; + int member_offset; + + /* read the string-table-length out of the file */ + + lseek (desc, offset, 0); + if (sizeof symdef_size != read (desc, &symdef_size, sizeof symdef_size)) + { + error_with_file ("premature eof in "); + return; + } + + if (symdef_size < 0) + { + error_with_file ("invalid size value in "); + return; + } + + nsymdefs = symdef_size / sizeof (struct symdef); + symdefs = (struct symdef *) alloca (symdef_size); + if (symdef_size != read (desc, symdefs, symdef_size)) + { + error_with_file ("premature eof in "); + return; + } + + if (stringsize < 0) + { + error_with_file ("invalid size value in "); + return; + } + + if (sizeof stringsize != read (desc, &stringsize, sizeof stringsize)) + { + error_with_file ("premature eof in "); + return; + } + + strings = (char *) alloca (stringsize); + if (stringsize != read (desc, strings, stringsize)) + { + error_with_file ("premature eof in "); + return; + } + + if (stringsize + symdef_size + sizeof stringsize + sizeof symdef_size != member_length) + { + error_with_file ("size of data isn't what the data calls for in "); + return; + } + + if (!file_on_each_line && (number_of_files > 1 || input_member)) + printf ("\n%s:\n", input_member ? input_member : input_name); + + member_offset = -1; + for (i = 0; i < nsymdefs; i++) + { + if (symdefs[i].stringoffset < 0 || symdefs[i].stringoffset >= stringsize) + { + error_with_file ("invalid entry in "); + return; + } + if (member_offset != symdefs[i].offset) + { + member_offset = symdefs[i].offset; + decode_library_subfile (desc, member_offset, &member_name); + } + if (file_on_each_line) + { + print_file_name (stdout); + printf (":"); + } + printf ("%s in %s\n", symdefs[i].stringoffset + strings, member_name); + } +} + +/* Report a fatal error. + STRING is a printf format string and ARG is one arg for it. */ + +void +fatal (string, arg) + char *string, *arg; +{ + fprintf (stderr, "%s: ", program_name); + fprintf (stderr, string, arg); + fprintf (stderr, "\n"); + exit (1); +} + +/* Report a nonfatal error. + STRING is a printf format string and ARG is one arg for it. */ + +void +error (string, arg) + char *string, *arg; +{ + fprintf (stderr, "%s: ", program_name); + fprintf (stderr, string, arg); + fprintf (stderr, "\n"); +} + +/* Report a nonfatal error. + STRING is printed, followed by the current file name. */ + +void +error_with_file (string) + char *string; +{ + fprintf (stderr, "%s: ", program_name); + fprintf (stderr, string); + print_file_name (stderr); + fprintf (stderr, "\n"); +} + +/* Report a fatal error using the message for the last failed system call, + followed by the string NAME. */ + +void +perror_name (name) + char *name; +{ + extern int errno, sys_nerr; + extern char *sys_errlist[]; + char *s; + + if (errno < sys_nerr) + s = concat ("", sys_errlist[errno], " for %s"); + else + s = "cannot open %s"; + error (s, name); +} + +/* Like malloc but get fatal error if memory is exhausted. */ + +char * +xmalloc (size) + unsigned size; +{ + char *result = malloc (size); + + if (!result) + fatal ("virtual memory exhausted", 0); + return result; +} + +/* Like realloc but get fatal error if out of memory. */ + +char * +xrealloc (p, size) + char *p; + unsigned size; +{ + char *result = realloc(p, size); + + if (!result) + fatal ("virtual memory exhausted", 0); + return result; +} + +/* Return a newly-allocated string + whose contents concatenate those of S1, S2, S3. */ + +char * +concat (s1, s2, s3) + char *s1, *s2, *s3; +{ + int len1 = strlen (s1), len2 = strlen (s2), len3 = strlen (s3); + char *result = (char *) xmalloc (len1 + len2 + len3 + 1); + + strcpy (result, s1); + strcpy (result + len1, s2); + strcpy (result + len1 + len2, s3); + result[len1 + len2 + len3] = 0; + + return result; +} + +#ifdef USG + +getpagesize () +{ + return (4096); +} + +#endif diff --git a/binutils-1.9/objdump.c b/binutils-1.9/objdump.c new file mode 100644 index 0000000..94c5303 --- /dev/null +++ b/binutils-1.9/objdump.c @@ -0,0 +1,418 @@ +/* objdump -- print information about an object file. + Copyright (C) 1988, 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 <stdio.h> +#include <errno.h> +extern int errno; +#include "getopt.h" + +#ifndef COFF_ENCAPSULATE +#include <a.out.h> +/* Tell a.out.gnu.h not to try to redefine some things. */ +#define __STRUCT_EXEC_OVERRIDE__ +#define N_NLIST_DECLARED +#define N_RELOCATION_INFO_DECLARED +#define N_MAGIC(exec) ((exec).a_magic) +#include "a.out.gnu.h" +#else +#include "a.out.encap.h" +#endif + +char *malloc (); + +char *xmalloc (); +void dump_file (); +void dump_header (); +void dump_nstuff (); +void dump_reloc (); +void dump_reloc1 (); +void dump_sym (); +void error (); +void usage (); + +/* Symbol table. */ +struct nlist *symtbl; + +/* Number of elements in `symtbl'. */ +int nsyms; + +/* String table. */ +char *strtbl; + +/* Number of elements in `strtbl'. */ +int strsize; + +/* Name this program was run with. */ +char *program_name; + +/* Name of object file currently being processed. */ +char *filename; + +void +read_symbols (execp, f) + struct exec *execp; + FILE *f; +{ + int i; + struct nlist *sp; + + if (symtbl) + return; + nsyms = execp->a_syms / sizeof (struct nlist); + if (nsyms == 0) + return; + + symtbl = (struct nlist *) xmalloc (nsyms * sizeof (struct nlist)); + + fseek (f, N_STROFF (*execp), 0); + if (fread ((char *) &strsize, sizeof strsize, 1, f) != 1) + error (1, errno, "%s: cannot read string table size", filename); + strtbl = xmalloc (strsize); + fseek (f, N_STROFF (*execp), 0); + if (fread (strtbl, 1, strsize, f) != strsize) + error (1, errno, "%s: cannot read string table", filename); + + fseek (f, N_SYMOFF (*execp), 0); + if (fread ((char *) symtbl, sizeof (struct nlist), nsyms, f) != nsyms) + error (1, errno, "%s: cannot read symbol table", filename); + + for (i = 0, sp = symtbl; i < nsyms; i++, sp++) + { + if (sp->n_un.n_strx == 0) + sp->n_un.n_name = ""; + else if (sp->n_un.n_strx < 0 || sp->n_un.n_strx > strsize) + sp->n_un.n_name = "<bad string table index>"; + else + sp->n_un.n_name = strtbl + sp->n_un.n_strx; + } +} + +void +free_symbols () +{ + if (symtbl) + free (symtbl); + symtbl = NULL; + if (strtbl) + free (strtbl); + strtbl = NULL; +} + +/* If nonzero, print header information. */ +int print_header = 0; + +/* If nonzero, print N_* information. */ +int print_nstuff = 0; + +/* If nonzero, print relocation information. */ +int print_relocation = 0; + +/* If nonzero, print symbol information. */ +int print_symbols = 0; + +/* Size of a page. Required by N_DATADDR in a.out.gnu.h [VAX]. */ +int page_size; + +void +main (argc, argv) + int argc; + char **argv; +{ + int c; + int ind; + int option_given = 0; + static struct option long_options[] = + { + {"symbols", 0, &print_symbols, 1}, + {"relocation", 0, &print_relocation, 1}, + {"nstuff", 0, &print_nstuff, 1}, + {"header", 0, &print_header, 1}, + {NULL, 0, NULL, 0} + }; + + page_size = getpagesize (); + + program_name = argv[0]; + + while ((c = getopt_long (argc, argv, "hnrt", long_options, &ind)) + != EOF) + { + option_given = 1; + switch (c) + { + case 0: + break; /* we've been given a long option */ + case 't': + print_symbols = 1; + break; + case 'r': + print_relocation = 1; + break; + case 'n': + print_nstuff = 1; + break; + case 'h': + print_header = 1; + break; + default: + usage (); + } + } + + if (option_given == 0 || optind == argc) + usage (); + + while (optind < argc) + dump_file (argv[optind++]); + + exit (0); +} + +void +dump_file (name) + char *name; +{ + FILE *f; + struct exec exec; + + printf ("%s:\n", name); + f = fopen (name, "r"); + if (f == NULL) + { + error (0, errno, "%s", name); + return; + } + filename = name; + +#ifdef HEADER_SEEK + HEADER_SEEK (f); +#endif + if (fread ((char *) &exec, sizeof exec, 1, f) != 1) + { + error (0, errno, "%s: cannot read header", filename); + return; + } + + if (N_BADMAG (exec)) + { + error (0, 0, "%s: Not an object file", filename); + return; + } + + if (print_header) + dump_header (&exec); + + if (print_nstuff) + dump_nstuff (&exec); + + if (print_symbols) + dump_sym (&exec, f); + + if (print_relocation) + dump_reloc (&exec, f); + + free_symbols (); +} + +void +dump_header (execp) + struct exec *execp; +{ + int x; + +#if defined (__GNU_EXEC_MACROS__) && !defined (__STRUCT_EXEC_OVERRIDE__) + printf ("magic: 0x%x (%o)", N_MAGIC (*execp), N_MAGIC (*execp)); + printf ("machine type: %d", N_MACHTYPE (*execp)); + printf ("flags: 0x%x", N_FLAGS (*execp)); +#else /* non-gnu struct exec. */ + printf ("magic: 0x%x (%o) ", execp->a_magic, execp->a_magic); +#endif /* non-gnu struct exec. */ + printf ("text 0x%x ", execp->a_text); + printf ("data 0x%x ", execp->a_data); + printf ("bss 0x%x\n", execp->a_bss); + printf ("nsyms %d", execp->a_syms / sizeof (struct nlist)); + x = execp->a_syms % sizeof (struct nlist); + if (x) + printf (" (+ %d bytes)", x); + printf (" entry 0x%x ", execp->a_entry); + printf ("trsize 0x%x ", execp->a_trsize); + printf ("drsize 0x%x\n", execp->a_drsize); +} + +void +dump_nstuff (execp) + struct exec *execp; +{ + printf ("N_BADMAG %d\n", N_BADMAG (*execp)); + printf ("N_TXTOFF 0x%x\n", N_TXTOFF (*execp)); + printf ("N_SYMOFF 0x%x\n", N_SYMOFF (*execp)); + printf ("N_STROFF 0x%x\n", N_STROFF (*execp)); + printf ("N_TXTADDR 0x%x\n", N_TXTADDR (*execp)); + printf ("N_DATADDR 0x%x\n", N_DATADDR (*execp)); +} + +void +dump_sym (execp, f) + struct exec *execp; + FILE *f; +{ + int i; + struct nlist *sp; + + read_symbols (execp, f); + if (nsyms == 0) + { + printf ("no symbols\n"); + return; + } + + printf ("%3s: %4s %5s %4s %8s\n", + "#", "type", "other", "desc", "val"); + for (i = 0, sp = symtbl; i < nsyms; i++, sp++) + { + printf ("%3d: %4x %5x %4x %8x %s\n", + i, + sp->n_type & 0xff, + sp->n_other & 0xff, + sp->n_desc & 0xffff, + sp->n_value, + sp->n_un.n_name); + } +} + +void +dump_reloc (execp, f) + struct exec *execp; + FILE *f; +{ + read_symbols (execp, f); + if (execp->a_trsize) + { + printf ("text reloc\n"); + dump_reloc1 (execp, f, N_TRELOFF (*execp), execp->a_trsize); + } + if (execp->a_drsize) + { + printf ("data reloc\n"); + dump_reloc1 (execp, f, N_DRELOFF (*execp), execp->a_drsize); + } +} + +void +dump_reloc1 (execp, f, off, size) + struct exec *execp; + FILE *f; + int off; + int size; +{ +#if !defined(sun) || !defined(sparc) + int nreloc; + struct relocation_info reloc; + int i; + + nreloc = size / sizeof (struct relocation_info); + + printf ("%3s: %3s %8s %4s\n", "#", "len", "adr", "sym"); + fseek (f, off, 0); + for (i = 0; i < nreloc; i++) + { + if (fread ((char *) &reloc, sizeof reloc, 1, f) != 1) + { + error (0, errno, "%s: cannot read relocation information", + filename); + return; + } + printf ("%3d: %3d %8x ", i, 1 << reloc.r_length, + reloc.r_address); + + if (reloc.r_extern) + { + printf ("%4d ", reloc.r_symbolnum); + if (reloc.r_symbolnum < nsyms) + printf ("%s ", symtbl[reloc.r_symbolnum].n_un.n_name); + } + else + { + printf (" "); + switch (reloc.r_symbolnum & ~N_EXT) + { + case N_TEXT: + printf (".text "); + break; + case N_DATA: + printf (".data "); + break; + case N_BSS: + printf (".bss "); + break; + case N_ABS: + printf (".abs "); + break; + default: + printf ("base %x ", reloc.r_symbolnum); + break; + } + } + if (reloc.r_pcrel) + printf ("PCREL "); +#if 0 + if (reloc.r_pad) + printf ("PAD %x ", reloc.r_pad); +#endif + printf ("\n"); + } +#endif +} + +/* Allocate N bytes of memory dynamically, with error checking. */ + +char * +xmalloc (n) + unsigned n; +{ + char *p; + + p = malloc (n); + if (p == 0) + error (1, 0, "virtual memory exhausted"); + return p; +} + +#ifdef USG +int +getpagesize () +{ + return 4096; +} +#endif + +#if defined(sun) && defined(sparc) +int +getpagesize () +{ + return 8192; +} +#endif + +void +usage () +{ + fprintf (stderr, "\ +Usage: %s [-hnrt] [+header] [+nstuff] [+relocation] [+symbols] objfile...\n", + program_name); + exit (1); +} diff --git a/binutils-1.9/ranlib.c b/binutils-1.9/ranlib.c new file mode 100644 index 0000000..91e72d1 --- /dev/null +++ b/binutils-1.9/ranlib.c @@ -0,0 +1,241 @@ +/* Dummy ranlib program for GNU. + All it does is `ar rs LIBRARY' for each library specified. + + Copyright (C) 1989, 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 <ar.h> +#include <sys/types.h> +#include <sys/file.h> +#include <stdio.h> +#include <fcntl.h> +#include "getopt.h" +#include "signame.h" + +#ifndef L_SET +#define L_SET 0 +#define L_INCR 1 +#endif + +#ifndef X_OK +#define X_OK 1 +#endif + +#ifdef USG +#define bzero(s, n) (memset ((s), 0, (n))) +#define gettimeofday(TV,TZ) (time (TV)) +#define seconds(TV) TV +long time(); +#else +#define seconds(TV) TV.tv_sec +#include <sys/time.h> +#endif + +#ifdef USG +#define vfork fork +#endif + +void touch_symdefs (); +void usage (); + +/* The makefile generates a -D switch to define AR_PROG + as the location of the GNU `ar' program. */ +char *prog = AR_PROG; + +/* The first place we look for `ar', before trying `prog'. */ +char *prog_pref = "/usr/local/gnubin/ar"; + +/* The name this program was run with. */ +char *program_name; + +void +main (argc, argv) + int argc; + char **argv; +{ + static int touch = 0, verbose = 0; + static char short_opts[] = "tv"; + static struct option long_opts[] = + { + { "touch", 0, &touch, 0 }, + { "verbose", 0, &verbose, 0 }, + { 0, 0, 0, 0 } + }; + register int c; + char *args; + int longind; + char *ar_program; + + program_name = argv[0]; + + while ((c = getopt_long (argc, argv, short_opts, long_opts, &longind)) + != EOF) + switch (c) + { + case 't': + touch = 1; + break; + + case 'v': + verbose = 1; + break; + + default: + usage (); + } + + if (optind == argc) + usage (); + + if (access (prog_pref, X_OK) == 0) + ar_program = prog_pref; + else + ar_program = prog; + + args = verbose ? "rsv" : "rs"; + + if (touch) + touch_symdefs (argc - optind, argv + optind); + else + { + register int i; + for (i = optind; i < argc; ++i) + { + int pid; + + if (verbose) + printf ("%s %s %s\n", ar_program, args, argv[i]); + + fflush (stdout); + fflush (stderr); + + pid = vfork (); + if (pid < 0) + { + fprintf (stderr, "%s: can not ", program_name); + perror ("vfork"); + } + else if (pid == 0) + { + /* Child side. */ + execl (ar_program, ar_program, args, argv[i], 0); + fprintf (stderr, "%s: can not run ", program_name); + perror (ar_program); + exit (1); + } + else + { + /* Parent side. */ + int status; + if (wait (&status) != pid) + { + fprintf (stderr, "%s: interrupted during ", program_name); + perror ("wait"); + } + if ((status & 0x7f) != 0) + { + psignal (status & 0x7f, ar_program); + exit (1); + } + else if (((status & 0xff00) >> 8) != 0) + exit ((status & 0xff00) >> 8); + } + } + } + + exit (0); +} + +/* "touch" the archive files listed in LARGV, containing LARGC elements. + Find the first symdef member in them and update the date to the + current one. */ + +void +touch_symdefs (largc, largv) + int largc; + char **largv; +{ +#ifdef USG + long tv; +#else + struct timeval tv; + struct timezone tz; +#endif + struct ar_hdr archive_header; + int i, rr; + + gettimeofday (&tv, &tz); + + while (largc--) + { + int fd = open (*largv, O_RDWR); + + if (fd < 0) + { + fprintf (stderr, "%s: can not open `%s' for ", program_name, *largv); + perror ("read/write"); + largv++; + continue; + } + + lseek (fd, SARMAG, L_SET); + + rr = read (fd, &archive_header, sizeof (archive_header)); + + /* In the general case this loop would be sped up by buffering, + but in almost all cases the symdef will be the first member, so + I'm not going to bother. */ + + while (rr == sizeof (archive_header)) + { + if (!strncmp ("__.SYMDEF", archive_header.ar_name, + sizeof ("__.SYMDEF") - 1)) + { + bzero ((char *) archive_header.ar_date, + sizeof (archive_header.ar_date)); + + sprintf (archive_header.ar_date, "%d", seconds (tv)); + + for (i = 0; i < sizeof archive_header.ar_date; i++) + if (archive_header.ar_date[i] == '\0') + archive_header.ar_date[i] = ' '; + + lseek (fd, - rr, L_INCR); + write (fd, &archive_header, sizeof (archive_header)); + close (fd); + break; + } + + lseek (fd, atoi (archive_header.ar_size), L_INCR); + rr = read (fd, &archive_header, sizeof (archive_header)); + } + + if (rr != sizeof (archive_header)) + /* We reached the end of the file. */ + fprintf (stderr, "%s: can not find `__.SYMDEF' member in `%s'\n", + program_name, *largv); + + largv++; + } +} + +void +usage () +{ + fprintf (stderr, "Usage: %s [-tv] [+touch] [+verbose] archive...\n", + program_name); + exit (1); +} diff --git a/binutils-1.9/ranlib.h b/binutils-1.9/ranlib.h new file mode 100644 index 0000000..936ba34 --- /dev/null +++ b/binutils-1.9/ranlib.h @@ -0,0 +1,17 @@ +/* ranlib.h 4.1 83/05/03 */ + +/* + * Structure of the __.SYMDEF table of contents for an archive. + * __.SYMDEF begins with a word giving the number of ranlib structures + * which immediately follow, and then continues with a string + * table consisting of a word giving the number of bytes of strings + * which follow and then the strings themselves. + * The ran_strx fields index the string table whose first byte is numbered 0. + */ +struct ranlib { + union { + off_t ran_strx; /* string table index of */ + char *ran_name; /* symbol defined by */ + } ran_un; + off_t ran_off; /* library member at this offset */ +}; diff --git a/binutils-1.9/robotussin.c b/binutils-1.9/robotussin.c new file mode 100644 index 0000000..061dc41 --- /dev/null +++ b/binutils-1.9/robotussin.c @@ -0,0 +1,579 @@ +/* Convert COFF-format object file to BSD format. + Used for converting the system libraries so GNU ld can link them. + Copyright (C) 1988 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. */ + +/* Written by Jeff Lewis. + + BUGS: + + Should do more to verify that the input COFF file meets our + expectations. + + On machines where the structure of the COFF data in the file does + not match the structure of the COFF data declared (when, for + example sizeof (struct filhdr) != FILHSZ), this program will fail. + (Don't ask me why this is ever allowed to come about). Accessor + functions/ macros that painstakingly extract the data out of the + file and stuff it in the memory struct should be written to fix + this on such machines. + + CAVEATS: + + This program cannot claim correctness, however, it does appear to + work on my fairly vanilla Sys5r2 machine. Someone with the time + and a fine tooth comb (not to mention some documentation on COFF) + should correct this! */ + +#ifndef COFF_ENCAPSULATE +#define COFF_ENCAPSULATE +#endif + +/* Customization for particular machines. */ + +#ifdef i386 +#define INPUT_MAGIC I386MAGIC +#else /* not i386 */ +#if defined (m68k) || defined (mc68000) +#define INPUT_MAGIC MC68MAGIC +#endif +#endif /* not i386 */ + +#include <stdio.h> +#include <varargs.h> +#include <fcntl.h> + +#include "a.out.encap.h" +#define N_ABSOLUTE N_ABS /* N_ABS will be redefined in syms.h */ +#undef N_ABS + +#include <filehdr.h> +#include <aouthdr.h> +#include <scnhdr.h> +#include <syms.h> +#include <reloc.h> +/* Because of struct alignment on dwords sizeof (struct syment) is different + than the syments stored in the file. Therefore, we must kludge: */ +#define sizeof_syment (SYMESZ) +#define sizeof_reloc (RELSZ) +#define sizeof_section (SCNHSZ) +#define sizeof_coff_header (FILHSZ) +/* Without the following, compiling this is a pain on a BSD4.3 Vax. + Though that might not seem useful, the compatibility of byte order + between a Vax and a 386 can come in handy for cross-debugging. */ +#ifdef VPRINTF_MISSING +#define vfprintf(fp, format, ap) _doprnt (format, ap, fp) +#endif + +extern long lseek (); +extern void exit (); +extern char *memcpy (); +extern int errno; + +void error (), sys_error (); +static void reloc_segment (); +char *mem_alloc (); + +int fd_in, fd_out; /* input and output file descriptors */ + +struct filehdr coff_header; /* file header from the input file */ +struct exec bsd_header; /* file header for the output file */ + +struct syment *coff_sym_listp; /* list of symbols from the input */ +int *symbol_map; /* mapping of input symbol #'s to + output symbol numbers */ +char *text_and_data; /* space for text & data section data */ +char *relocations; /* space for output reloc entries */ +int verbose_flag; /* flag for debugging */ + +struct scnhdr coff_text_header; /* COFF text section header */ +struct scnhdr coff_data_header; /* COFF data section header */ +struct scnhdr coff_bss_header; /* COFF bss section header */ +int text_sect_num; /* COFF section # for text */ +int data_sect_num; /* COFF section # for data */ +int bss_sect_num; /* COFF section # for bss */ + +char *program_name; /* Name this program was run with. */ + +void +main (argc, argv) + int argc; + char **argv; +{ + int i, j; + char *coff_string_table, *bsd_string_table; + register char *pc, *pc2; + int string_table_len; + int symbol_count; + struct scnhdr section; + struct nlist name; + + program_name = argv[0]; + + if (argc < 3) + error ("Usage: %s COFF-file BSD-file", argv[0]); + if (argc > 3) + verbose_flag = 1; + + fd_in = open (argv[1], O_RDONLY); + if (fd_in < 0) + sys_error ("cannot open %s", argv[1]); + + fd_out = open (argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0666); + if (fd_out < 0) + sys_error ("cannot open %s", argv[2]); + + /* Read in the file header and all section headers searching + for text, data and bss. We note the section #'s of these + sections for use when examining symbols. */ + + if (read (fd_in, &coff_header, sizeof_coff_header) != sizeof_coff_header) + error ("cannot read file header"); + + if (coff_header.f_magic != INPUT_MAGIC) + error ("bad magic number in coff file\n"); + + lseek (fd_in, sizeof_coff_header + coff_header.f_opthdr, 0); + + for (i = 1; i <= coff_header.f_nscns; ++i) + { + if (read (fd_in, §ion, sizeof_section) != sizeof_section) + error ("cannot read section header #%d", i); + if (strcmp (section.s_name, _TEXT) == 0) + { + text_sect_num = i; + memcpy (&coff_text_header, §ion, sizeof section); + } + else if (strcmp (section.s_name, _DATA) == 0) + { + data_sect_num = i; + memcpy (&coff_data_header, §ion, sizeof section); + } + else if (strcmp (section.s_name, _BSS) == 0) + { + bss_sect_num = i; + memcpy (&coff_bss_header, §ion, sizeof section); + } + } + +#ifdef sun386 +/* The purpose of the following kludge is to cope with the discrepancy + between coff_data_header.s_vaddr and coff_text_header.s_size; in a BSD + object file, they are assumed equal. It should be ok to use + coff_data_header.s_paddr, since it has no other apparent use. */ + coff_data_header.s_paddr = coff_text_header.s_size; + coff_bss_header.s_paddr = coff_data_header.s_size + coff_text_header.s_size; +#endif + + /* Pass1 thru the symbol table - count usable symbols and map + old symbol #'s into new ones (as used by relocation + info). We're only interested in keeping the kinds of symbols + we'd expect to find in a BSD library object file: no debug + symbols, file names, section definition symbols, etc. + Section definition symbols are referenced by reloc entries + in the COFF file, so we note their position with a negative + symbol number indicating the section. -1 is used to flag + symbols we're not interested in, yielding an unexpected error + if we find any reloc entries referencing them. */ + + coff_sym_listp = + (struct syment *) mem_alloc (coff_header.f_nsyms * sizeof (struct syment)); + symbol_map = (int *) mem_alloc (coff_header.f_nsyms * sizeof *symbol_map); + if (lseek (fd_in, coff_header.f_symptr, 0) < 0L) + sys_error ("cannot seek to COFF symbols"); + for (i = 0; i < coff_header.f_nsyms; ++i) + { + if (read (fd_in, coff_sym_listp + i, sizeof_syment) != sizeof_syment) + error ("cannot read COFF symbols"); + } + symbol_count = 0; + for (i = 0; i < coff_header.f_nsyms; ++i) + { + if (coff_sym_listp[i].n_scnum != N_DEBUG + && coff_sym_listp[i].n_name[0] != '.' + && coff_sym_listp[i].n_scnum != -1) + { + if (verbose_flag) + printf ("map %d to %d\n", i, symbol_count); + symbol_map[i] = symbol_count++; +#ifdef sun386 + if (coff_sym_listp[i].n_scnum == data_sect_num) + coff_sym_listp[i].n_value -= + coff_data_header.s_vaddr - coff_data_header.s_paddr; + else if (coff_sym_listp[i].n_scnum == bss_sect_num) + coff_sym_listp[i].n_value -= + coff_bss_header.s_vaddr - coff_bss_header.s_paddr; +#endif + } + else + { + if (coff_sym_listp[i].n_sclass == C_STAT) + { + if (strcmp (coff_sym_listp[i].n_name, _TEXT) == 0) + symbol_map[i] = -N_TEXT; + else if (strcmp (coff_sym_listp[i].n_name, _DATA) == 0) + symbol_map[i] = -N_DATA; + else if (strcmp (coff_sym_listp[i].n_name, _BSS) == 0) + symbol_map[i] = -N_BSS; + else + symbol_map[i] = -1; + } + else + { + symbol_map[i] = -1; + } + } + /* skip auxillary entries */ + j = coff_sym_listp[i].n_numaux; + if (j != 0) + { + if (j < 0) + error ("invalid numaux"); + if (j != 1) + fprintf (stderr, "unlikely numaux value\n"); + while (--j >= 0) + ++i; + } + } + + /* Now we know enough to write the output file header. */ + + N_SET_MAGIC (bsd_header, OMAGIC); + bsd_header.a_text = coff_text_header.s_size; + bsd_header.a_data = coff_data_header.s_size; + bsd_header.a_bss = coff_bss_header.s_size; + bsd_header.a_syms = symbol_count * sizeof (struct nlist); + bsd_header.a_entry = 0; + bsd_header.a_trsize = coff_text_header.s_nreloc * sizeof (struct relocation_info); + bsd_header.a_drsize = coff_data_header.s_nreloc * sizeof (struct relocation_info); + if (write (fd_out, &bsd_header, sizeof bsd_header) != sizeof bsd_header) + sys_error ("cannot write BSD header"); + + /* Read in and save text and data sections - some data in + these sections may need to be altered due to relocations. */ + + text_and_data = (char *) mem_alloc (coff_text_header.s_size + coff_data_header.s_size); + if (lseek (fd_in, coff_text_header.s_scnptr, 0) < 0L) + sys_error ("cannot seek to text section"); + if (read (fd_in, text_and_data, coff_text_header.s_size) != coff_text_header.s_size) + error ("cannot read text section"); + if (lseek (fd_in, coff_data_header.s_scnptr, 0) < 0L) + sys_error ("cannot seek to data section"); + if (read (fd_in, text_and_data + coff_text_header.s_size, coff_data_header.s_size) != coff_data_header.s_size) + error ("cannot read data section"); + + /* Convert the relocation entries and do any text or data + modifications necessary. */ + + relocations = (char *) mem_alloc (bsd_header.a_trsize + bsd_header.a_drsize); + reloc_segment (&coff_text_header, relocations); + reloc_segment (&coff_data_header, relocations + bsd_header.a_trsize); + + if (write (fd_out, text_and_data, coff_text_header.s_size + coff_data_header.s_size) + != coff_text_header.s_size + coff_data_header.s_size) + sys_error ("cannot write text and data sections"); + /* ZZ - are there any alignment considerations?? */ + if ((coff_text_header.s_size & 1) || (coff_data_header.s_size & 1)) + fprintf (stderr, "non-aligned text or data section\n"); + if (write (fd_out, relocations, bsd_header.a_trsize + bsd_header.a_drsize) + != bsd_header.a_trsize + bsd_header.a_drsize) + sys_error ("cannot write relocation entries"); + + /* Second pass thru the symbol table. + a COFF symbol entry may contain up to 8 chars of symbol name + in the entry itself - symbol names > 8 go into the string table, + whereas the BSD entry puts all symbol names into the string + table. */ + + if (lseek (fd_in, coff_header.f_symptr + coff_header.f_nsyms * sizeof_syment, 0) < 0L) + error ("cannot seek to string table"); + + i = read (fd_in, &string_table_len, sizeof string_table_len); + if (i == sizeof string_table_len) + { + coff_string_table = mem_alloc (string_table_len); + string_table_len -= sizeof string_table_len; + i = read (fd_in, coff_string_table + sizeof string_table_len, string_table_len); + if (i < 0) + error ("cannot read string table"); + if (i != string_table_len) + error ("truncated string table - expected %d, got %d", + string_table_len, i); + } + else + { + string_table_len = 0; + } + bsd_string_table = mem_alloc (string_table_len + coff_header.f_nsyms * (SYMNMLEN + 1)); + pc = bsd_string_table + sizeof string_table_len; + for (i = 0; i < coff_header.f_nsyms; ++i) + { + if (coff_sym_listp[i].n_scnum != N_DEBUG + && coff_sym_listp[i].n_name[0] != '.' + && coff_sym_listp[i].n_scnum != -1) + { + if (coff_sym_listp[i].n_zeroes == 0) + { + j = pc - bsd_string_table; +#ifndef nounderscore + if (coff_sym_listp[i].n_sclass == C_EXT + || coff_sym_listp[i].n_sclass == C_STAT) + *pc++ = '_'; +#endif + pc2 = coff_string_table + coff_sym_listp[i].n_offset; + while (*pc++ = *pc2++) + /* null */ ; + name.n_un.n_strx = j; + } + else + { + pc2 = &coff_sym_listp[i].n_name[0]; + j = pc - bsd_string_table; +#ifndef nounderscore + if (coff_sym_listp[i].n_sclass == C_EXT + || coff_sym_listp[i].n_sclass == C_STAT) + *pc++ = '_'; +#endif + { + int x; + for (x = 0; x < SYMNMLEN; x++) + { + if (*pc2 == 0) + break; + *pc++ = *pc2++; + } + *pc++ = 0; + } + name.n_un.n_strx = j; + } + switch (coff_sym_listp[i].n_scnum) + { + case N_ABS: + name.n_type = N_ABSOLUTE; + break; + case N_UNDEF: + name.n_type = N_UNDF; + break; + default: + if (coff_sym_listp[i].n_scnum == text_sect_num) + name.n_type = N_TEXT; + else if (coff_sym_listp[i].n_scnum == data_sect_num) + name.n_type = N_DATA; + else if (coff_sym_listp[i].n_scnum == bss_sect_num) + name.n_type = N_BSS; + break; + } + if (coff_sym_listp[i].n_sclass == C_EXT) + name.n_type |= N_EXT; + name.n_other = 0; + name.n_desc = 0; + name.n_value = coff_sym_listp[i].n_value; + + if (write (fd_out, &name, sizeof name) != sizeof name) + sys_error ("cannot write symbol"); + } + /* skip auxillary entries */ + j = coff_sym_listp[i].n_numaux; + if (j != 0) + { + while (--j >= 0) + ++i; + } + } + i = *((int *) bsd_string_table) = pc - bsd_string_table; + if (write (fd_out, bsd_string_table, i) != i) + error ("cannot write string table"); + + close (fd_in); + close (fd_out); + exit (0); +} + +/* Convert the relocation entries and do any text or data + modifications necessary. */ + +static void +reloc_segment (section_headerp, reloc_infop) + struct scnhdr *section_headerp; + struct relocation_info *reloc_infop; +{ + struct reloc coff_reloc; + int i; + + if (lseek (fd_in, section_headerp->s_relptr, 0) < 0L) + error ("cannot seek to relocation entries"); + for (i = 0; i < section_headerp->s_nreloc; ++i) + { + if (read (fd_in, &coff_reloc, sizeof_reloc) != sizeof_reloc) + error ("cannot read relocation entry"); +#ifdef sun386 + coff_reloc.r_vaddr -= + section_headerp->s_vaddr - section_headerp->s_paddr; +#endif + if (verbose_flag) + printf ("vaddr = 0x%x, symndx = %d\n", coff_reloc.r_vaddr, coff_reloc.r_symndx); + /* The reloc references a symbol declared common, thus the + value of the symbol holds its size (in bytes). In COFF, + apparently this info is also put into the binary - + BSD doesn't like this, so we subtract it out. */ + if (coff_sym_listp[coff_reloc.r_symndx].n_scnum == N_UNDEF) + { + if (coff_sym_listp[coff_reloc.r_symndx].n_value != 0) + { + if (verbose_flag) + printf ("adjust common 0x%x (%d)\n", + coff_sym_listp[coff_reloc.r_symndx].n_value, + coff_sym_listp[coff_reloc.r_symndx].n_value); + switch (coff_reloc.r_type) + { + case R_RELBYTE: + *((char *) (text_and_data + coff_reloc.r_vaddr)) + -= coff_sym_listp[coff_reloc.r_symndx].n_value; + break; + case R_RELWORD: + *((short *) (text_and_data + coff_reloc.r_vaddr)) + -= coff_sym_listp[coff_reloc.r_symndx].n_value; + break; + case R_RELLONG: + case R_DIR32: /* these are the only two that really show up */ + case R_PCRLONG: + *((int *) (text_and_data + coff_reloc.r_vaddr)) + -= coff_sym_listp[coff_reloc.r_symndx].n_value; + break; + default: + error ("unknown relocation type 0%o", coff_reloc.r_type); + } + } + } + /* >= 0 means its an extern - value is the output symbol #. + < 0 means its an intern - value is N_TEXT, N_DATA or N_BSS. */ + if (symbol_map[coff_reloc.r_symndx] >= 0) + { + reloc_infop->r_symbolnum = symbol_map[coff_reloc.r_symndx]; + reloc_infop->r_extern = 1; + } + else + { + if (symbol_map[coff_reloc.r_symndx] == -1) + error ("Oops! possible bug - reloc reference to ignored symbol"); +#ifdef sun386 + { + int offset=0; + switch (-symbol_map[coff_reloc.r_symndx]) + { + case N_DATA: + offset = coff_data_header.s_vaddr - coff_data_header.s_paddr; + break; + case N_BSS: + offset = coff_bss_header.s_vaddr - coff_bss_header.s_paddr; + break; + } + switch (coff_reloc.r_type) + { + case R_RELBYTE: + *((char *) (text_and_data + coff_reloc.r_vaddr)) -= offset; + break; + case R_RELWORD: + *((short *) (text_and_data + coff_reloc.r_vaddr)) -= offset; + break; + case R_RELLONG: + case R_DIR32: + case R_PCRLONG: + *((int *) (text_and_data + coff_reloc.r_vaddr)) -= offset; + break; + default: + error ("unknown relocation type 0%o", coff_reloc.r_type); + } + } +#endif + reloc_infop->r_symbolnum = -symbol_map[coff_reloc.r_symndx]; + reloc_infop->r_extern = 0; + } + /* COFF address includes the section address - BSD doesn't, so + subtract it out. */ +#ifdef sun386 + coff_reloc.r_vaddr += + section_headerp->s_vaddr - section_headerp->s_paddr; +#endif + reloc_infop->r_address = coff_reloc.r_vaddr - section_headerp->s_vaddr; + switch (coff_reloc.r_type) + { + case R_PCRLONG: + reloc_infop->r_pcrel = 1; + reloc_infop->r_length = 2; /* 4 bytes */ + break; + case R_DIR32: + case R_RELLONG: + reloc_infop->r_pcrel = 0; + reloc_infop->r_length = 2; + break; + default: + error ("cannot handle coff reloction type 0%o", coff_reloc.r_type); + } + + if (verbose_flag) + printf ("reloc: addr = 0x%x, synum = %d\n", + reloc_infop->r_address, reloc_infop->r_symbolnum); + reloc_infop->r_pad = 0; + ++reloc_infop; + } +} + +void +error (format, va_alist) + char *format; + va_dcl +{ + va_list args; + + va_start (args); + fprintf (stderr, "%s: ", program_name); + vfprintf (stderr, format, args); + putc ('\n', stderr); + va_end (args); + exit (1); +} + +extern char *sys_errlist[]; +extern int errno; + +void +sys_error (format, va_alist) + char *format; + va_dcl +{ + va_list args; + + va_start (args); + fprintf (stderr, "%s: ", program_name); + vfprintf (stderr, format, args); + fprintf (stderr, ": %s\n", sys_errlist[errno]); + va_end (args); + exit (1); +} + +extern char *malloc (); + +char * +mem_alloc (size) + int size; +{ + char *pc; + + if ((pc = malloc (size)) == NULL) + error ("memory exhausted!"); + return pc; +} diff --git a/binutils-1.9/signame.c b/binutils-1.9/signame.c new file mode 100644 index 0000000..d62fead --- /dev/null +++ b/binutils-1.9/signame.c @@ -0,0 +1,248 @@ +/* Convert between signal names and numbers. + Copyright (C) 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; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <stdio.h> +#include <signal.h> +#include "signame.h" + +#ifdef __STDC__ +#define CONST const +#else +#define CONST +#endif + +#ifdef SYS_SIGLIST_MISSING +/* There is too much variation in Sys V signal numbers and names, so + we must initialize them at runtime. */ + +static CONST char undoc[] = "unknown signal"; + +CONST char *sys_siglist[NSIG]; +#endif /* SYS_SIGLIST_MISSING */ + +/* Table of abbreviations for signals. Note: A given number can + appear more than once with different abbreviations. */ +typedef struct + { + int number; + CONST char *abbrev; + } num_abbrev; +static num_abbrev sig_table[NSIG*2]; +/* Number of elements of sig_table used. */ +static int sig_table_nelts = 0; + +/* Enter signal number NUMBER into the tables with ABBREV and NAME. */ +static void +init_sig (number, abbrev, name) + int number; + CONST char *abbrev; + CONST char *name; +{ +#ifdef SYS_SIGLIST_MISSING + sys_siglist[number] = name; +#endif + sig_table[sig_table_nelts].number = number; + sig_table[sig_table_nelts++].abbrev = abbrev; +} + +static void +init_sigs () +{ +#ifdef SYS_SIGLIST_MISSING + int i; + /* Initialize signal names. */ + for (i = 0; i < NSIG; i++) + sys_siglist[i] = undoc; +#endif /* SYS_SIGLIST_MISSING */ + + /* Initialize signal names. */ +#if defined (SIGHUP) + init_sig (SIGHUP, "HUP", "Hangup"); +#endif +#if defined (SIGINT) + init_sig (SIGINT, "INT", "Interrupt"); +#endif +#if defined (SIGQUIT) + init_sig (SIGQUIT, "QUIT", "Quit"); +#endif +#if defined (SIGILL) + init_sig (SIGILL, "ILL", "Illegal Instruction"); +#endif +#if defined (SIGTRAP) + init_sig (SIGTRAP, "TRAP", "Trace/breakpoint trap"); +#endif + /* If SIGIOT == SIGABRT, we want to print it as SIGABRT because + SIGABRT is in ANSI and POSIX.1 and SIGIOT isn't. */ +#if defined (SIGABRT) + init_sig (SIGABRT, "ABRT", "Aborted"); +#endif +#if defined (SIGIOT) + init_sig (SIGIOT, "IOT", "IOT trap"); +#endif +#if defined (SIGEMT) + init_sig (SIGEMT, "EMT", "EMT trap"); +#endif +#if defined (SIGFPE) + init_sig (SIGFPE, "FPE", "Floating point exception"); +#endif +#if defined (SIGKILL) + init_sig (SIGKILL, "KILL", "Killed"); +#endif +#if defined (SIGBUS) + init_sig (SIGBUS, "BUS", "Bus error"); +#endif +#if defined (SIGSEGV) + init_sig (SIGSEGV, "SEGV", "Segmentation fault"); +#endif +#if defined (SIGSYS) + init_sig (SIGSYS, "SYS", "Bad system call"); +#endif +#if defined (SIGPIPE) + init_sig (SIGPIPE, "PIPE", "Broken pipe"); +#endif +#if defined (SIGALRM) + init_sig (SIGALRM, "ALRM", "Alarm clock"); +#endif +#if defined (SIGTERM) + init_sig (SIGTERM, "TERM", "Terminated"); +#endif +#if defined (SIGUSR1) + init_sig (SIGUSR1, "USR1", "User defined signal 1"); +#endif +#if defined (SIGUSR2) + init_sig (SIGUSR2, "USR2", "User defined signal 2"); +#endif + /* If SIGCLD == SIGCHLD, we want to print it as SIGCHLD because that + is what is in POSIX.1. */ +#if defined (SIGCHLD) + init_sig (SIGCHLD, "CHLD", "Child exited"); +#endif +#if defined (SIGCLD) + init_sig (SIGCLD, "CLD", "Child exited"); +#endif +#if defined (SIGPWR) + init_sig (SIGPWR, "PWR", "Power failure"); +#endif +#if defined (SIGTSTP) + init_sig (SIGTSTP, "TSTP", "Stopped"); +#endif +#if defined (SIGTTIN) + init_sig (SIGTTIN, "TTIN", "Stopped (tty input)"); +#endif +#if defined (SIGTTOU) + init_sig (SIGTTOU, "TTOU", "Stopped (tty output)"); +#endif +#if defined (SIGSTOP) + init_sig (SIGSTOP, "STOP", "Stopped (signal)"); +#endif +#if defined (SIGXCPU) + init_sig (SIGXCPU, "XCPU", "CPU time limit exceeded"); +#endif +#if defined (SIGXFSZ) + init_sig (SIGXFSZ, "XFSZ", "File size limit exceeded"); +#endif +#if defined (SIGVTALRM) + init_sig (SIGVTALRM, "VTALRM", "Virtual timer expired"); +#endif +#if defined (SIGPROF) + init_sig (SIGPROF, "PROF", "Profiling timer expired"); +#endif +#if defined (SIGWINCH) + /* "Window size changed" might be more accurate, but even if that + is all that it means now, perhaps in the future it will be + extended to cover other kinds of window changes. */ + init_sig (SIGWINCH, "WINCH", "Window changed"); +#endif +#if defined (SIGCONT) + init_sig (SIGCONT, "CONT", "Continued"); +#endif +#if defined (SIGURG) + init_sig (SIGURG, "URG", "Urgent I/O condition"); +#endif +#if defined (SIGIO) + /* "I/O pending" has also been suggested. A disadvantage is + that signal only happens when the process has + asked for it, not everytime I/O is pending. Another disadvantage + is the confusion from giving it a different name than under Unix. */ + init_sig (SIGIO, "IO", "I/O possible"); +#endif +#if defined (SIGWIND) + init_sig (SIGWIND, "WIND", "SIGWIND"); +#endif +#if defined (SIGPHONE) + init_sig (SIGPHONE, "PHONE", "SIGPHONE"); +#endif +#if defined (SIGPOLL) + init_sig (SIGPOLL, "POLL", "I/O possible"); +#endif +#if defined (SIGLOST) + init_sig (SIGLOST, "LOST", "Resource lost"); +#endif +} + +/* Return the abbreviation for signal NUMBER. */ +char * +sig_abbrev (number) + int number; +{ + int i; + + if (sig_table_nelts == 0) + init_sigs (); + + for (i = 0; i < sig_table_nelts; i++) + if (sig_table[i].number == number) + return (char *)sig_table[i].abbrev; + return NULL; +} + +/* Return the signal number for an ABBREV, or -1 if there is no + signal by that name. */ +int +sig_number (abbrev) + CONST char *abbrev; +{ + int i; + + if (sig_table_nelts == 0) + init_sigs (); + + /* Skip over "SIG" if present. */ + if (abbrev[0] == 'S' && abbrev[1] == 'I' && abbrev[2] == 'G') + abbrev += 3; + + for (i = 0; i < sig_table_nelts; i++) + if (abbrev[0] == sig_table[i].abbrev[0] + && strcmp (abbrev, sig_table[i].abbrev) == 0) + return sig_table[i].number; + return -1; +} + +#if defined (SYS_SIGLIST_MISSING) +/* Print to standard error the name of SIGNAL, preceded by MESSAGE and + a colon, and followed by a newline. */ +void +psignal (signal, message) + int signal; + CONST char *message; +{ + if (signal <= 0 || signal >= NSIG) + fprintf (stderr, "%s: unknown signal", message); + else + fprintf (stderr, "%s: %s\n", message, sys_siglist[signal]); +} +#endif diff --git a/binutils-1.9/signame.h b/binutils-1.9/signame.h new file mode 100644 index 0000000..d4dc211 --- /dev/null +++ b/binutils-1.9/signame.h @@ -0,0 +1,42 @@ +/* Convert between signal names and numbers. + Copyright (C) 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; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#ifdef __STDC__ +/* Return the abbreviation (e.g. ABRT, FPE, etc.) for signal NUMBER. + Do not return this as a const char *. The caller might want to + assign it to a char *. */ +char *sig_abbrev (int number); + +/* Return the signal number for an ABBREV, or -1 if there is no + signal by that name. */ +int sig_number (const char *abbrev); + +/* Print to standard error the name of SIGNAL, preceded by MESSAGE and + a colon, and followed by a newline. */ +void psignal (int signal, const char *message); + +/* Names for signals from 0 to NSIG-1. */ +extern const char *sys_siglist[]; + +#else + +char *sig_abbrev (); +int sig_number (); +void psignal (); +extern char *sys_siglist[]; + +#endif diff --git a/binutils-1.9/size.c b/binutils-1.9/size.c new file mode 100644 index 0000000..8c000f7 --- /dev/null +++ b/binutils-1.9/size.c @@ -0,0 +1,400 @@ +/* Size of rel file utility (`size') for GNU. + Copyright (C) 1986 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 <stdio.h> +#include <ar.h> +#include <sys/types.h> +#include <sys/file.h> + +#if !defined(A_OUT) && !defined(MACH_O) +#define A_OUT +#endif + +#ifdef A_OUT +#ifdef COFF_ENCAPSULATE +#include "a.out.encap.h" +#else +/* On native BSD systems, use the system's own a.out.h. */ +#include <a.out.h> +#endif +#endif + +#ifdef MACH_O +#include <sys/loader.h> +#endif + +#ifdef USG +#include <fcntl.h> +#include <string.h> +#else +#include <strings.h> +#endif + +/* Number of input file names specified. */ +int number_of_files; + +/* Current file's name */ +char *input_name; + +/* Current member's name, or 0 if processing a non-library file. */ +char *input_member; + +/* Offset within archive of the current member, + if we are processing an archive. */ +int member_offset; + +/* The name this program was run with. */ +char *program_name; + +char *malloc (); + +void do_one_file (); +void do_one_rel_file (); +void error_with_file (); +void fatal (); +void perror_name (); +void print_file_name (); +char *xmalloc (); +char *concat (); + +void +main (argc, argv) + char **argv; + int argc; +{ + int i; + + program_name = argv[0]; + + number_of_files = argc - 1; + + printf ("text\tdata\tbss\tdec\thex\n"); + + /* Now scan and describe the files. */ + + if (number_of_files == 0) + do_one_file ("a.out"); + else + for (i = 1; i < argc; i++) + do_one_file (argv[i]); + exit (0); +} + +/* Print the filename of the current file on 'outfile' (a stdio stream). */ + +void +print_file_name (outfile) + FILE *outfile; +{ + fprintf (outfile, "%s", input_name); + if (input_member) + fprintf (outfile, "(%s)", input_member); +} + +/* process one input file */ +void scan_library (); + +void +do_one_file (name) + char *name; +{ + int desc; + char armag[SARMAG]; + + desc = open (name, O_RDONLY, 0); + + if (desc < 0) + { + perror_name (name); + return; + } + + input_name = name; + input_member = 0; + + if (SARMAG != read (desc, armag, SARMAG) || strncmp (armag, ARMAG, SARMAG)) + do_one_rel_file (desc, 0L); + else + scan_library (desc); + + close (desc); +} + +/* Read in the archive data about one member. + SUBFILE_OFFSET is the address within the archive of the start of that data. + + Return the length of the member's contents, which does + not include the archive data about the member. + If there are no more valid members, return zero. */ + +int +decode_library_subfile (desc, subfile_offset) + int desc; + int subfile_offset; +{ + int bytes_read; + int namelen; + int member_length; + char *name; + struct ar_hdr hdr1; + + lseek (desc, subfile_offset, 0); + + bytes_read = read (desc, &hdr1, sizeof hdr1); + if (!bytes_read) + ; /* end of archive */ + + else if (sizeof hdr1 != bytes_read) + error_with_file ("malformed library archive"); + + else if (sscanf (hdr1.ar_size, "%d", &member_length) != 1) + error_with_file ("malformatted header of archive member"); + + else + { + for (namelen = 0; + hdr1.ar_name[namelen] != 0 + && hdr1.ar_name[namelen] != ' ' + && hdr1.ar_name[namelen] != '/'; + namelen++) + ; + name = (char *) xmalloc (namelen+1); + strncpy (name, hdr1.ar_name, namelen); + *(name + namelen) = 0; + + input_member = name; + + return member_length; + } + return 0; /* tell caller to exit loop */ +} + +/* Scan a library and describe each member. */ + +void +scan_library (desc) + int desc; +{ + int this_subfile_offset = SARMAG; + int member_length; + + while (member_length = decode_library_subfile (desc, this_subfile_offset)) + { + /* describe every member except the ranlib data if any */ + if (strcmp (input_member, "__.SYMDEF")) + do_one_rel_file (desc, this_subfile_offset + sizeof (struct ar_hdr)); + + this_subfile_offset += ((member_length + sizeof (struct ar_hdr)) + 1) & -2; + } +} + +/* Read a file's header and fill in various pieces of information. + Return 0 on failure, 1 on success. */ + +int +read_header_info (desc, offset, tsize, dsize, bsize) + int desc; + long int offset; + unsigned int *tsize; + unsigned int *dsize; + unsigned int *bsize; +{ + int len; + +#ifdef A_OUT + { + struct exec hdr; + + lseek (desc, offset, 0); +#ifdef HEADER_SEEK_FD + /* Skip the headers that encapsulate our data in some other format + such as COFF. */ + HEADER_SEEK_FD (desc); +#endif + len = read (desc, (char *) &hdr, sizeof (struct exec)); + if (len == sizeof (struct exec) && !N_BADMAG (hdr)) + { + *tsize = hdr.a_text; + *dsize = hdr.a_data; + *bsize = hdr.a_bss; + return 1; + } + } +#endif + +#ifdef MACH_O + { + struct mach_header mach_header; + char *hdrbuf; + struct load_command *load_command; + struct segment_command *segment_command; + struct section *section; + int len, cmd, seg; + + lseek (desc, offset, 0); + len = read (desc, (char *) &mach_header, sizeof (struct mach_header)); + if (len == sizeof (struct mach_header) && mach_header.magic == MH_MAGIC) + { + hdrbuf = xmalloc (mach_header.sizeofcmds); + len = read (desc, hdrbuf, mach_header.sizeofcmds); + if (len != mach_header.sizeofcmds) + { + error_with_file ("failure reading Mach-O load commands"); + return 0; + } + load_command = (struct load_command *) hdrbuf; + for (cmd = 0; cmd < mach_header.ncmds; ++cmd) + { + if (load_command->cmd == LC_SEGMENT) + { + segment_command = (struct segment_command *) load_command; + section = (struct section *) ((char *) (segment_command + 1)); + for (seg = 0; seg < segment_command->nsects; ++seg, ++section) + { + if (!strncmp(SECT_TEXT, section->sectname, sizeof section->sectname)) + *tsize = section->size; + else if (!strncmp(SECT_DATA, section->sectname, sizeof section->sectname)) + *dsize = section->size; + else if (!strncmp(SECT_BSS, section->sectname, sizeof section->sectname)) + *bsize = section->size; + } + } + load_command = (struct load_command *) + ((char *) load_command + load_command->cmdsize); + } + free (hdrbuf); + return 1; + } + } +#endif + + return 0; +} + +void +do_one_rel_file (desc, offset) + int desc; + long int offset; +{ + unsigned int tsize, dsize, bsize, total; + + if (read_header_info (desc, offset, &tsize, &dsize, &bsize)) + { + total = tsize + dsize + bsize; + printf ("%u\t%u\t%u\t%u\t%x", tsize, dsize, bsize, total, total); + } + else + { + error_with_file ("malformed input file (not rel or archive)"); + return; + } + + if (number_of_files > 1 || input_member) + { + printf ("\t"); + print_file_name (stdout); + } + printf ("\n"); +} + +/* Report a fatal error. + STRING is a printf format string and ARG is one arg for it. */ + +void +fatal (string, arg) + char *string, *arg; +{ + fprintf (stderr, "%s: ", program_name); + fprintf (stderr, string, arg); + fprintf (stderr, "\n"); + exit (1); +} + +/* Report a nonfatal error. + STRING is a printf format string and ARG is one arg for it. */ + +void +error (string, arg) + char *string, *arg; +{ + fprintf (stderr, "%s: ", program_name); + fprintf (stderr, string, arg); + fprintf (stderr, "\n"); +} + +/* Report a nonfatal error. + STRING is printed, followed by the current file name. */ + +void +error_with_file (string) + char *string; +{ + fprintf (stderr, "%s: ", program_name); + print_file_name (stderr); + fprintf (stderr, ": "); + fprintf (stderr, string); + fprintf (stderr, "\n"); +} + +/* Report an error using the message for the last failed system call, + followed by the string NAME. */ + +void +perror_name (name) + char *name; +{ + extern int errno, sys_nerr; + extern char *sys_errlist[]; + char *s; + + if (errno < sys_nerr) + s = concat (name, ": ", sys_errlist[errno]); + else + s = concat (name, ": ", "unknown error"); + error (s, name); +} + +/* Like malloc but get fatal error if memory is exhausted. */ + +char * +xmalloc (size) + unsigned size; +{ + char *result = malloc (size); + + if (!result) + fatal ("virtual memory exhausted", 0); + return result; +} + +/* Return a newly-allocated string + whose contents concatenate those of S1, S2, S3. */ + +char * +concat (s1, s2, s3) + char *s1, *s2, *s3; +{ + int len1 = strlen (s1), len2 = strlen (s2), len3 = strlen (s3); + char *result = (char *) xmalloc (len1 + len2 + len3 + 1); + + strcpy (result, s1); + strcpy (result + len1, s2); + strcpy (result + len1 + len2, s3); + result[len1 + len2 + len3] = 0; + + return result; +} diff --git a/binutils-1.9/stab.def b/binutils-1.9/stab.def new file mode 100644 index 0000000..b81cda4 --- /dev/null +++ b/binutils-1.9/stab.def @@ -0,0 +1,115 @@ +/* Table of DBX symbol codes for the GNU system. + Copyright (C) 1988 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. */ + +/* Global variable. Only the name is significant. + To find the address, look in the corresponding external symbol. */ +__define_stab (N_GSYM, 0x20, "GSYM") + +/* Function name for BSD Fortran. Only the name is significant. + To find the address, look in the corresponding external symbol. */ +__define_stab (N_FNAME, 0x22, "FNAME") + +/* Function name or text-segment variable for C. Value is its address. + Desc is supposedly starting line number, but GCC doesn't set it + and DBX seems not to miss it. */ +__define_stab (N_FUN, 0x24, "FUN") + +/* Data-segment variable with internal linkage. Value is its address. */ +__define_stab (N_STSYM, 0x26, "STSYM") + +/* BSS-segment variable with internal linkage. Value is its address. */ +__define_stab (N_LCSYM, 0x28, "LCSYM") + +/* Name of main routine. Only the name is significant. + This is not used in C. */ +__define_stab (N_MAIN, 0x2a, "MAIN") + +/* Register variable. Value is number of register. */ +__define_stab (N_RSYM, 0x40, "RSYM") + +/* Structure or union element. Value is offset in the structure. */ +__define_stab (N_SSYM, 0x60, "SSYM") + +/* Parameter variable. Value is offset from argument pointer. + (On most machines the argument pointer is the same as the frame pointer. */ +__define_stab (N_PSYM, 0xa0, "PSYM") + +/* Automatic variable in the stack. Value is offset from frame pointer. + Also used for type descriptions. */ +__define_stab (N_LSYM, 0x80, "LSYM") + +/* Alternate entry point. Value is its address. */ +__define_stab (N_ENTRY, 0xa4, "ENTRY") + +/* Name of main source file. + Value is starting text address of the compilation. */ +__define_stab (N_SO, 0x64, "SO") + +/* Name of sub-source file. + Value is starting text address of the compilation. */ +__define_stab (N_SOL, 0x84, "SOL") + +/* Line number in text segment. Desc is the line number; + value is corresponding address. */ +__define_stab (N_SLINE, 0x44, "SLINE") +/* Similar, for data segment. */ +__define_stab (N_DSLINE, 0x46, "DSLINE") +/* Similar, for bss segment. */ +__define_stab (N_BSLINE, 0x48, "BSLINE") + +/* Beginning of an include file. Only Sun uses this. + In an object file, only the name is significant. + The Sun linker puts data into some of the other fields. */ +__define_stab (N_BINCL, 0x82, "BINCL") +/* End of an include file. No name. + These two act as brackets around the file's output. + In an object file, there is no significant data in this entry. + The Sun linker puts data into some of the fields. */ +__define_stab (N_EINCL, 0xa2, "EINCL") +/* Place holder for deleted include file. + This appears only in output from the Sun linker. */ +__define_stab (N_EXCL, 0xc2, "EXCL") + +/* Beginning of lexical block. + The desc is the nesting level in lexical blocks. + The value is the address of the start of the text for the block. + The variables declared inside the block *precede* the N_LBRAC symbol. */ +__define_stab (N_LBRAC, 0xc0, "LBRAC") +/* End of a lexical block. Desc matches the N_LBRAC's desc. + The value is the address of the end of the text for the block. */ +__define_stab (N_RBRAC, 0xe0, "RBRAC") + +/* Begin named common block. Only the name is significant. */ +__define_stab (N_BCOMM, 0xe2, "BCOMM") +/* Begin named common block. Only the name is significant + (and it should match the N_BCOMM). */ +__define_stab (N_ECOMM, 0xe4, "ECOMM") +/* End common (local name): value is address. + I'm not sure how this is used. */ +__define_stab (N_ECOML, 0xe8, "ECOML") +/* Second symbol entry containing a length-value for the preceding entry. + The value is the length. */ +__define_stab (N_LENG, 0xfe, "LENG") + +/* Global symbol in Pascal. + Supposedly the value is its line number; I'm skeptical. */ +__define_stab (N_PC, 0x30, "PC") + +/* Modula-2 compilation unit. Can someone say what info it contains? */ +__define_stab (N_M2C, 0x42, "M2C") +/* Modula-2 scope information. Can someone say what info it contains? */ +__define_stab (N_SCOPE, 0xc4, "SCOPE") diff --git a/binutils-1.9/stab.h b/binutils-1.9/stab.h new file mode 100644 index 0000000..80bd594 --- /dev/null +++ b/binutils-1.9/stab.h @@ -0,0 +1,17 @@ +#ifndef __GNU_STAB__ + +/* Indicate the GNU stab.h is in use. */ + +#define __GNU_STAB__ + +#define __define_stab(NAME, CODE, STRING) NAME=CODE, + +enum __stab_debug_code +{ +#include "stab.def" +LAST_UNUSED_STAB_CODE +}; + +#undef __define_stab + +#endif /* __GNU_STAB_ */ diff --git a/binutils-1.9/strip.c b/binutils-1.9/strip.c new file mode 100644 index 0000000..6008382 --- /dev/null +++ b/binutils-1.9/strip.c @@ -0,0 +1,915 @@ +/* strip certain symbols from a rel file. + Copyright (C) 1986, 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 <stdio.h> +#include <sys/types.h> +#include <sys/file.h> +#include <sys/stat.h> +#include <signal.h> +#include <errno.h> +extern int errno; +#include "getopt.h" + +#ifdef USG +#include <fcntl.h> +#include <string.h> +#else +#include <strings.h> +#endif + +#if !defined(A_OUT) && !defined(MACH_O) +#define A_OUT +#endif + +#ifdef A_OUT +#ifdef COFF_ENCAPSULATE +#include "a.out.encap.h" +#else +/* On native BSD systems, use the system's own a.out.h. */ +#include <a.out.h> +#endif +#endif + +#ifdef MACH_O +#ifndef A_OUT +#include <nlist.h> +#include <reloc.h> +#endif +#include <sys/loader.h> +#endif + +#ifdef nounderscore +#define LPREFIX '.' +#else +#define LPREFIX 'L' +#endif + +#if defined (sun) && defined (sparc) +/* On the sparc, the name of the relocation info structure is + different (on SunOS4, "struct relocation_info" does not exist). + The meaning of the r_index field is the same as r_symbolnum + in normal relocation_info's for external symbols. Fortunately, + we only use the field for external symbols. */ +typedef struct reloc_info_sparc *relocation_info_ptr; +#define RELOCATION_INFO_SYMBOL_NUM(ri) (ri)->r_index +#else /* not Sun and sparc. */ +typedef struct relocation_info *relocation_info_ptr; +#define RELOCATION_INFO_SYMBOL_NUM(ri) (ri)->r_symbolnum +#endif /* not Sun and sparc. */ + +/* If BSD or HP-UX, we can use `ftruncate' and `rename'. */ + +#if !defined(USG) || defined(hpux) +#define HAVE_FTRUNCATE +#define HAVE_RENAME +#endif /* !USG || hpux */ + +/* Number of nlist entries that are for local symbols. */ +int local_sym_count; + +/* Number of nlist entries that are for local symbols + whose names don't start with L. */ +int non_L_local_sym_count; + +/* Number of nlist entries for debugger info. */ +int debugger_sym_count; + +/* Number of global symbols referenced or defined. */ +int global_sym_count; + +/* Total number of symbols to be preserved in the current file. */ +int nsyms; + +/* Number of files specified in the command line. */ +int number_of_files; + +/* Kinds of files understood. */ +enum file_type { IS_UNKNOWN, IS_A_OUT, IS_MACH_O }; + +/* Each specified file has a file_entry structure for it. + These are contained in the vector which file_table points to. */ + +struct file_entry +{ + char *filename; + enum file_type filetype; /* what kind of file it is */ + + /* Things obtained from the file's header. */ + long int trel_offset; /* offset to text relocation */ + unsigned int trel_size; /* size of text relocation */ + long int drel_offset; /* offset to data relocation */ + unsigned int drel_size; /* size of data relocation */ + long int syms_offset; /* offset to the symbol table */ + unsigned int syms_size; /* size of the symbol table */ + long int strs_offset; /* offset to the string table */ + unsigned int strs_size; /* size of the string table */ + + int ss_size; /* size, in bytes, of symbols_and_strings data */ + struct nlist *symbols_and_strings; + + /* offset of the symtab_command in a mach-O file's header */ + long int symtab_cmd_offset; +}; + +struct file_entry *file_table; + +/* Descriptor on which current file is open. */ +int input_desc; + +/* Stream for writing that file using stdio. */ +FILE *outstream; + +enum strip_action +{ + strip_undef, + strip_all, /* strip all symbols */ + strip_debug /* strip all debugger symbols */ +}; + +/* Which symbols to remove. */ +enum strip_action strip_symbols; + +enum locals_action +{ + locals_undef, + locals_start_L, /* discard locals starting with L */ + locals_all /* discard all locals */ +}; + +/* Which local symbols to remove. */ +enum locals_action discard_locals; + +/* The name this program was run with. */ +char *program_name; + +char *malloc (); + +char *concat (); +char *xmalloc (); +int file_open (); +int read_entry_symbols (); +int read_file_symbols (); +int read_header (); +void count_file_symbols (); +void error (); +void file_close (); +void rewrite_file_symbols (); +void strip_file (); +void usage (); + +struct option long_options[] = +{ + {"strip-all", 0, 0, 's'}, + {"strip-debug", 0, 0, 'S'}, + {"discard-all", 0, 0, 'x'}, + {"discard-locals", 0, 0, 'X'}, + {0, 0, 0, 0} +}; + +void +main (argc, argv) + char **argv; + int argc; +{ + int c; + int ind; + int i; + struct file_entry *p; + + program_name = argv[0]; + + strip_symbols = strip_undef; /* default is to strip everything. */ + discard_locals = locals_undef; + + while ((c = getopt_long (argc, argv, "gsSxX", long_options, &ind)) != EOF) + { + switch (c) + { + case 0 : + break; + case 's': + strip_symbols = strip_all; + break; + case 'g': + case 'S': + strip_symbols = strip_debug; + break; + case 'x': + discard_locals = locals_all; + break; + case 'X': + discard_locals = locals_start_L; + break; + default: + usage (); + } + } + + /* Default is to strip all symbols. */ + if (strip_symbols == strip_undef && discard_locals == locals_undef) + strip_symbols = strip_all; + + number_of_files = argc - optind; + + if (!number_of_files) + usage (); + + p = file_table + = (struct file_entry *) xmalloc (number_of_files * sizeof (struct file_entry)); + + /* Now fill in file_table */ + + for (i = 0; i < number_of_files; i++) + { + p->filename = argv[i + optind]; + p->filetype = IS_UNKNOWN; + p->trel_offset = p->trel_size = p->drel_offset = p->drel_size = 0; + p->syms_offset = p->syms_size = p->strs_offset = p->strs_size = 0; + p->symbols_and_strings = 0; + p->symtab_cmd_offset = 0; + p++; + } + + for (i = 0; i < number_of_files; i++) + strip_file (&file_table[i]); + exit (0); +} + +int delayed_signal; + +void +delay_signal (signo) + int signo; +{ + delayed_signal = signo; + signal (signo, delay_signal); +} + +/* process one input file */ + +void +strip_file (entry) + struct file_entry *entry; +{ + int val; + int sigint_handled = 0; + int sighup_handled = 0; + int sigterm_handled = 0; + + local_sym_count = 0; + non_L_local_sym_count = 0; + debugger_sym_count = 0; + global_sym_count = 0; + + val = file_open (entry); + if (val < 0) + return; + + if (strip_symbols != strip_all) + /* Read in the existing symbols unless we are discarding everything. */ + { + if (read_file_symbols (entry) < 0) + return; + } + + /* Effectively defer handling of asynchronous kill signals. */ + delayed_signal = 0; + if (signal (SIGINT, SIG_IGN) != SIG_IGN) + sigint_handled = 1, signal (SIGINT, delay_signal); + if (signal (SIGHUP, SIG_IGN) != SIG_IGN) + sighup_handled = 1, signal (SIGHUP, delay_signal); + if (signal (SIGTERM, SIG_IGN) != SIG_IGN) + sigterm_handled = 1, signal (SIGTERM, delay_signal); + + /* Change the file. */ + + rewrite_file_symbols (entry); + if (strip_symbols != strip_all) + free (entry->symbols_and_strings); + + file_close (); + + /* Effectively undefer handling. */ + if (sigint_handled) + signal (SIGINT, SIG_DFL); + if (sighup_handled) + signal (SIGHUP, SIG_DFL); + if (sigterm_handled) + signal (SIGTERM, SIG_DFL); + + /* Handle any signal that came in while they were deferred. */ + if (delayed_signal) + kill (getpid (), delayed_signal); +} + +/** Convenient functions for operating on one or all files being processed. */ + +/* Close the file that is now open. */ + +void +file_close () +{ + if (input_desc != -1) + close (input_desc); + input_desc = -1; +} + +/* Open the file specified by 'entry', and return a descriptor, + or -1 if the file cannot be opened or is not in rel format. + The descriptor is also saved in input_desc. */ + +int +file_open (entry) + struct file_entry *entry; +{ + int desc; + + desc = open (entry->filename, O_RDWR, 0); + + if (desc > 0) + { + input_desc = desc; + if (read_header (desc, entry) < 0) + { + close (desc); + return -1; + } + return desc; + } + + error (0, errno, "%s", entry->filename); + return -1; +} + +/* Validate file ENTRY and read its symbol and string sections into core. + Return 0 if ok, -1 if error. */ + +int +read_file_symbols (entry) + struct file_entry *entry; +{ + if (read_entry_symbols (input_desc, entry) < 0) + { + file_close (); + return -1; + } + count_file_symbols (entry); + return 0; +} + +/* Read a file's header and fill in various fields of a file's entry. + Return -1 on failure, 0 if successful. */ + +int +read_header (desc, entry) + int desc; + struct file_entry *entry; +{ + int len; + +#ifdef A_OUT + { + struct exec hdr; + + lseek (desc, 0, 0); +#ifdef HEADER_SEEK_FD + /* Skip the headers that encapsulate our data in some other format + such as COFF. */ + HEADER_SEEK_FD (desc); +#endif + len = read (desc, (char *) &hdr, sizeof (struct exec)); + if (len == sizeof (struct exec) && !N_BADMAG (hdr)) + { + entry->filetype = IS_A_OUT; +#ifdef N_TRELOFF + entry->trel_offset = N_TRELOFF (hdr); +#else +#ifdef N_DATOFF + entry->trel_offset = N_DATOFF (hdr) + hdr.a_data; +#else + entry->trel_offset = N_TXTOFF (hdr) + hdr.a_text + hdr.a_data; +#endif +#endif + entry->trel_size = hdr.a_trsize; +#ifdef N_DRELOFF + entry->drel_offset = N_DRELOFF (hdr); +#else + entry->drel_offset = entry->trel_offset + entry->trel_size; +#endif + entry->drel_size = hdr.a_drsize; + entry->syms_offset = N_SYMOFF(hdr); + entry->syms_size = hdr.a_syms; + entry->strs_offset = N_STROFF(hdr); + lseek(desc, entry->strs_offset, 0); + if (read (desc, (char *) &entry->strs_size, sizeof entry->strs_size) + != sizeof entry->strs_size) + { + error (0, errno, "%s: cannot read string table size", + entry->filename); + return -1; + } + return 0; + } + } +#endif + +#ifdef MACH_O + { + struct mach_header mach_header; + char *hdrbuf; + struct load_command *load_command; + struct segment_command *segment_command; + struct section *section; + struct symtab_command *symtab_command; + int symtab_seen; + int len, cmd, seg; + + symtab_seen = 0; + + lseek (desc, 0L, 0); + len = read (desc, (char *) &mach_header, sizeof (struct mach_header)); + if (len == sizeof (struct mach_header) && mach_header.magic == MH_MAGIC) + { + entry->filetype = IS_MACH_O; + hdrbuf = xmalloc (mach_header.sizeofcmds); + len = read (desc, hdrbuf, mach_header.sizeofcmds); + if (len != mach_header.sizeofcmds) + { + error (0, errno, "%s: cannot read Mach-O load commands", + entry->filename); + return -1; + } + load_command = (struct load_command *) hdrbuf; + for (cmd = 0; cmd < mach_header.ncmds; ++cmd) + { + switch (load_command->cmd) + { + case LC_SEGMENT: + segment_command = (struct segment_command *) load_command; + section = (struct section *) ((char *) (segment_command + 1)); + for (seg = 0; seg < segment_command->nsects; ++seg, ++section) + { + if (!strncmp(SECT_TEXT, section->sectname, sizeof section->sectname)) + { + entry->trel_offset = section->reloff; + entry->trel_size = section->nreloc * sizeof (struct relocation_info); + } + else if (!strncmp(SECT_DATA, section->sectname, sizeof section->sectname)) + { + entry->drel_offset = section->reloff; + entry->drel_size = section->nreloc * sizeof (struct relocation_info); + } + } + break; + case LC_SYMTAB: + if (symtab_seen) + error (0, 0, "%s: more than one LC_SYMTAB", entry->filename); + else + { + symtab_seen = 1; + symtab_command = (struct symtab_command *) load_command; + entry->syms_offset = symtab_command->symoff; + entry->syms_size = symtab_command->nsyms * sizeof (struct nlist); + entry->strs_offset = symtab_command->stroff; + entry->strs_size = symtab_command->strsize; + entry->symtab_cmd_offset = (char *) load_command - hdrbuf + + sizeof (struct mach_header); + } + break; + } + load_command = (struct load_command *) + ((char *) load_command + load_command->cmdsize); + } + + free (hdrbuf); + + if (!symtab_seen) + { + error (0, 0, "%s: no symbol table", entry->filename); + return -1; + } + + return 0; + } + } +#endif + + error (0, 0, "%s: not an executable or object file", entry->filename); + return -1; +} + +/* Read the symbols and strings of file ENTRY into core. + Assume it is already open, on descriptor DESC. + Return -1 on failure, 0 if successful. */ + +int +read_entry_symbols (desc, entry) + struct file_entry *entry; + int desc; +{ + entry->ss_size = entry->syms_size + entry->strs_size; + entry->symbols_and_strings = (struct nlist *) xmalloc (entry->ss_size); + + lseek (desc, entry->syms_offset, 0); + if (entry->ss_size != read (desc, entry->symbols_and_strings, entry->ss_size)) + { + error (0, errno, "%s: premature end of file in symbols/strings", + entry->filename); + return -1; + } + return 0; +} + +/* Count the number of symbols of various categories in the file of ENTRY. */ + +void +count_file_symbols (entry) + struct file_entry *entry; +{ + struct nlist *p, *end = entry->symbols_and_strings + entry->syms_size / sizeof (struct nlist); + char *name_base = entry->syms_size + (char *) entry->symbols_and_strings; + + for (p = entry->symbols_and_strings; p < end; p++) + if (p->n_type & N_EXT) + global_sym_count++; + else if (p->n_un.n_strx && !(p->n_type & (N_STAB | N_EXT))) + { + if ((p->n_un.n_strx + name_base)[0] != LPREFIX) + non_L_local_sym_count++; + local_sym_count++; + } + else + debugger_sym_count++; +} + +void modify_relocation (); +void write_file_syms (); + +/* Total size of string table strings allocated so far */ +int strtab_size; + +/* Vector whose elements are the strings to go in the string table */ +char **strtab_vector; + +/* Index in strtab_vector at which the next string will be stored */ +int strtab_index; + +int sym_written_count; + +int +assign_string_table_index (name) + char *name; +{ + int index = strtab_size; + + strtab_size += strlen (name) + 1; + strtab_vector[strtab_index++] = name; + + return index; +} + +void +rewrite_file_symbols (entry) + struct file_entry *entry; +{ + int i; + struct nlist *newsyms; + + /* Calculate number of symbols to be preserved. */ + + if (strip_symbols == strip_all) + nsyms = 0; + else + { + nsyms = global_sym_count; + if (discard_locals == locals_start_L) + nsyms += non_L_local_sym_count; + else if (discard_locals == locals_undef) + nsyms += local_sym_count; + } + + if (strip_symbols == strip_undef) + nsyms += debugger_sym_count; + + strtab_vector = (char **) xmalloc (nsyms * sizeof (char *)); + strtab_index = 0; + + strtab_size = 4; + + /* Accumulate in 'newsyms' the symbol table to be written. */ + + newsyms = (struct nlist *) xmalloc (nsyms * sizeof (struct nlist)); + + sym_written_count = 0; + + if (strip_symbols != strip_all) + /* Write into newsyms the symbols we want to keep. */ + write_file_syms (entry, newsyms); + + if (sym_written_count != nsyms) + { + fprintf (stderr, "written = %d, expected = %d\n", + sym_written_count, nsyms); + abort (); + } + + /* Modify the symbol-numbers in the relocation in the file, + to preserve its meaning */ + modify_relocation (input_desc, entry); + +#ifndef HAVE_FTRUNCATE + { + int size = entry->syms_offset, mode; + int old_desc; + char *renamed; + char *copy_buffer = (char *)xmalloc (size); + struct stat statbuf; + + renamed = (char *) xmalloc(strlen(entry->filename) + 3); + strcpy(renamed, entry->filename); + { + char *file_p, *renamed_p; + file_p = strrchr(entry->filename, '/'); + file_p = file_p ? ++file_p : entry->filename; + renamed_p = renamed + (file_p - entry->filename); + *renamed_p++ = '~'; + while (*renamed_p++ = *file_p++) + ; + *renamed_p++ = '~'; + *renamed_p = '\0'; + } + + lseek (input_desc, 0, 0); + if (read (input_desc, copy_buffer, size) != size) + { + error (0, errno, "%s: cannot read up to symbol table", + entry->filename); + return; + } + mode = fstat (input_desc, &statbuf) ? 0666 : statbuf.st_mode; + if (rename (entry->filename, renamed)) + { + error (0, errno, "%s", entry->filename); + return; + } + old_desc = input_desc; + input_desc = open (entry->filename, O_RDWR | O_CREAT | O_TRUNC, mode); + if (input_desc < 0) + { + error (0, errno, "%s", entry->filename); + return; + } + if (write (input_desc, copy_buffer, size) != size) + error (0, errno, "%s", entry->filename); + if (unlink (renamed)) + error (0, errno, "%s", renamed); + if (close (old_desc)) + error (0, errno, "%s", renamed); + free (copy_buffer); + free (renamed); + } +#endif /* not HAVE_FTRUNCATE */ + + /* Now write contents of NEWSYMS into the file. */ + + lseek (input_desc, entry->syms_offset, 0); + write (input_desc, newsyms, nsyms * sizeof (struct nlist)); + free (newsyms); + + /* Now write the string table. */ + + { + char *strvec = (char *) xmalloc (strtab_size); + char *p; + + *((long *) strvec) = strtab_size; + + p = strvec + sizeof (long); + + for (i = 0; i < strtab_index; i++) + { + int len = strlen (strtab_vector[i]); + strcpy (p, strtab_vector[i]); + *(p+len) = 0; + p += len + 1; + } + + write (input_desc, strvec, strtab_size); + free (strvec); + } + + /* Adjust file to be smaller */ + +#ifdef HAVE_FTRUNCATE + if (ftruncate (input_desc, lseek (input_desc, 0L, 1)) < 0) + error (0, errno, "%s", entry->filename); +#endif + + /* Write new symbol table size into file header. */ + +#ifdef A_OUT + if (entry->filetype == IS_A_OUT) + { + struct exec hdr; + + lseek (input_desc, 0L, 0); +#ifdef HEADER_SEEK_FD + HEADER_SEEK_FD (input_desc); +#endif + read (input_desc, (char *) &hdr, sizeof hdr); + hdr.a_syms = nsyms * sizeof (struct nlist); + lseek (input_desc, -(long) sizeof hdr, 1); + write (input_desc, (char *) &hdr, sizeof hdr); + } +#endif + +#ifdef MACH_O + if (entry->filetype == IS_MACH_O) + { + struct symtab_command cmd; + + lseek (input_desc, entry->symtab_cmd_offset, 0); + read (input_desc, (char *) &cmd, sizeof cmd); + cmd.nsyms = nsyms; + cmd.stroff = cmd.symoff + cmd.nsyms * sizeof (struct nlist); + cmd.strsize = strtab_size; + lseek (input_desc, entry->symtab_cmd_offset, 0); + write (input_desc, (char *) &cmd, sizeof cmd); + } +#endif + + free (strtab_vector); +} + +/* Copy into NEWSYMS the symbol entries to be preserved. + Count them in sym_written_count. + + We record, for each symbol written, its symbol number in the resulting file. + This is so that the relocation can be updated later. + Since the symbol names will not be needed again, + this index goes in the `n_strx' field. + If a symbol is not written, -1 is stored there. */ + +void +write_file_syms (entry, newsyms) + struct file_entry *entry; + struct nlist *newsyms; +{ + struct nlist *p = entry->symbols_and_strings; + struct nlist *end = p + entry->syms_size / sizeof (struct nlist); + char *string_base = (char *) end; /* address of start of file's string table */ + struct nlist *outp = newsyms; + + for (; p < end; p++) + { + int write; + + if (p->n_type & N_EXT) + write = 1; + else if (p->n_un.n_strx && !(p->n_type & (N_STAB | N_EXT))) + /* ordinary local symbol */ + write = (discard_locals != locals_all) + && !(discard_locals == locals_start_L && + (p->n_un.n_strx + string_base)[0] == LPREFIX); + else + /* debugger symbol */ + write = (strip_symbols == strip_undef); + + if (write) + { + if (p->n_un.n_strx) + p->n_un.n_strx = assign_string_table_index (p->n_un.n_strx + string_base); + + *outp++ = *p; + + p->n_un.n_strx = sym_written_count++; + } + else p->n_un.n_strx = -1; + } +} + +/* Read in ENTRY's relocation, alter the symbolnums in it, + and write it out again. */ + +void +modify_relocation (desc, entry) + int desc; + struct file_entry *entry; +{ + relocation_info_ptr reloc, p, end; + int size; + struct nlist *sym_base = (struct nlist *) entry->symbols_and_strings; + int losing = 0; + long int offsets[2]; + unsigned int sizes[2]; + int i; + + offsets[0] = entry->trel_offset; + sizes[0] = entry->trel_size; + offsets[1] = entry->drel_offset; + sizes[1] = entry->drel_size; + + for (i = 0; i < 2; ++i) + { + size = sizes[i]; + reloc = (relocation_info_ptr) xmalloc (size); + lseek (desc, offsets[i], 0); + read (desc, reloc, size); + + p = reloc; + end = (relocation_info_ptr) (size + (char *) reloc); + while (p < end) + { + if (p->r_extern) + { + int newnum = (sym_base == 0 ? -1 + :((sym_base + RELOCATION_INFO_SYMBOL_NUM(p)) + -> n_un.n_strx)); + if (newnum < 0) + { + if (losing == 0) + error (0, 0, "%s: warning: file is now unlinkable", + entry->filename); + losing = 1; + } + RELOCATION_INFO_SYMBOL_NUM(p) = newnum; + } + p++; + } + + lseek (desc, offsets[i], 0); + write (desc, reloc, size); + free ((char *) reloc); + } +} + +/* Return a newly-allocated string whose contents + concatenate those of S1, S2, S3. */ + +char * +concat (s1, s2, s3) + char *s1, *s2, *s3; +{ + int len1 = strlen (s1), len2 = strlen (s2), len3 = strlen (s3); + char *result = (char *) xmalloc (len1 + len2 + len3 + 1); + + strcpy (result, s1); + strcpy (result + len1, s2); + strcpy (result + len1 + len2, s3); + *(result + len1 + len2 + len3) = 0; + + return result; +} + +/* Like malloc but get fatal error if memory is exhausted. */ + +char * +xmalloc (size) + unsigned size; +{ + /* Some implementations of malloc get unhappy if size==0. + * Given that the code sometimes "wants" a 0-length array, + * it seems cleaner to put a work-around here + * than clutter up the code logic in various other places. */ + char *result = malloc (size==0 ? 1 : size); + + if (!result) + error (1, 0, "virtual memory exhausted"); + return result; +} + +#ifndef HAVE_RENAME +int +rename (from, to) + char *from, *to; +{ + unlink (to); + if (link (from, to) < 0 || unlink (from) < 0) + return -1; + else + return 0; +} +#endif + +void +usage () +{ + fprintf (stderr, "\ +Usage: %s [-gsxSX] [+strip-all] [+strip-debug] [+discard-all]\n\ + [+discard-locals] file...\n", program_name); + exit (1); +} diff --git a/binutils-1.9/symseg.h b/binutils-1.9/symseg.h new file mode 100644 index 0000000..43a8123 --- /dev/null +++ b/binutils-1.9/symseg.h @@ -0,0 +1,511 @@ +/* GDB symbol table format definitions. + Copyright (C) 1986, 1989 Free Software Foundation, Inc. + Hacked by Michael Tiemann (tiemann@mcc.com) + +This file is part of GDB. + +GDB 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. + +GDB 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 GDB; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* Format of GDB symbol table data. + There is one symbol segment for each source file or + independant compilation. These segments are simply concatenated + to form the GDB symbol table. A zero word where the beginning + of a segment is expected indicates there are no more segments. + +Format of a symbol segment: + + The symbol segment begins with a word containing 1 + if it is in the format described here. Other formats may + be designed, with other code numbers. + + The segment contains many objects which point at each other. + The pointers are offsets in bytes from the beginning of the segment. + Thus, each segment can be loaded into core and its pointers relocated + to make valid in-core pointers. + + All the data objects in the segment can be found indirectly from + one of them, the root object, of type `struct symbol_root'. + It appears at the beginning of the segment. + + The total size of the segment, in bytes, appears as the `length' + field of this object. This size includes the size of the + root object. + + All the object data types are defined here to contain pointer types + appropriate for in-core use on a relocated symbol segment. + Casts to and from type int are required for working with + unrelocated symbol segments such as are found in the file. + + The ldsymaddr word is filled in by the loader to contain + the offset (in bytes) within the ld symbol table + of the first nonglobal symbol from this compilation. + This makes it possible to match those symbols + (which contain line number information) reliably with + the segment they go with. + + Core addresses within the program that appear in the symbol segment + are not relocated by the loader. They are inserted by the assembler + and apply to addresses as output by the assembler, so GDB must + relocate them when it loads the symbol segment. It gets the information + on how to relocate from the textrel, datarel, bssrel, databeg and bssbeg + words of the root object. + + The words textrel, datarel and bssrel + are filled in by ld with the amounts to relocate within-the-file + text, data and bss addresses by; databeg and bssbeg can be + used to tell which kind of relocation an address needs. */ + +enum language {language_c}; + +struct symbol_root +{ + int format; /* Data format version */ + int length; /* # bytes in this symbol segment */ + int ldsymoff; /* Offset in ld symtab of this file's syms */ + int textrel; /* Relocation for text addresses */ + int datarel; /* Relocation for data addresses */ + int bssrel; /* Relocation for bss addresses */ + char *filename; /* Name of main source file compiled */ + char *filedir; /* Name of directory it was reached from */ + struct blockvector *blockvector; /* Vector of all symbol-naming blocks */ + struct typevector *typevector; /* Vector of all data types */ + enum language language; /* Code identifying the language used */ + char *version; /* Version info. Not fully specified */ + char *compilation; /* Compilation info. Not fully specified */ + int databeg; /* Address within the file of data start */ + int bssbeg; /* Address within the file of bss start */ + struct sourcevector *sourcevector; /* Vector of line-number info */ +}; + +/* All data types of symbols in the compiled program + are represented by `struct type' objects. + All of these objects are pointed to by the typevector. + The type vector may have empty slots that contain zero. */ + +struct typevector +{ + int length; /* Number of types described */ + struct type *type[1]; +}; + +/* Different kinds of data types are distinguished by the `code' field. */ + +enum type_code +{ + TYPE_CODE_UNDEF, /* Not used; catches errors */ + TYPE_CODE_PTR, /* Pointer type */ + TYPE_CODE_ARRAY, /* Array type, lower bound zero */ + TYPE_CODE_STRUCT, /* C struct or Pascal record */ + TYPE_CODE_UNION, /* C union or Pascal variant part */ + TYPE_CODE_ENUM, /* Enumeration type */ + TYPE_CODE_FUNC, /* Function type */ + TYPE_CODE_INT, /* Integer type */ + TYPE_CODE_FLT, /* Floating type */ + TYPE_CODE_VOID, /* Void type (values zero length) */ + TYPE_CODE_SET, /* Pascal sets */ + TYPE_CODE_RANGE, /* Range (integers within spec'd bounds) */ + TYPE_CODE_PASCAL_ARRAY, /* Array with explicit type of index */ + + /* C++ */ + TYPE_CODE_MEMBER, /* Member type */ + TYPE_CODE_METHOD, /* Method type */ + TYPE_CODE_REF, /* C++ Reference types */ +}; + +/* This appears in a type's flags word for an unsigned integer type. */ +#define TYPE_FLAG_UNSIGNED 1 +/* This appears in a type's flags word + if it is a (pointer to a|function returning a)* built in scalar type. + These types are never freed. */ +#define TYPE_FLAG_PERM 4 +/* This appears in a type's flags word if it is a stub type (eg. if + someone referenced a type that wasn't definined in a source file + via (struct sir_not_appearing_in_this_film *)). */ +#define TYPE_FLAG_STUB 8 +/* Set when a class has a constructor defined */ +#define TYPE_FLAG_HAS_CONSTRUCTOR 256 +/* Set when a class has a destructor defined */ +#define TYPE_FLAG_HAS_DESTRUCTOR 512 +/* Indicates that this type is a public baseclass of another class, + i.e. that all its public methods are available in the derived + class. */ +#define TYPE_FLAG_VIA_PUBLIC 1024 +/* Indicates that this type is a virtual baseclass of another class, + i.e. that if this class is inherited more than once by another + class, only one set of member variables will be included. */ +#define TYPE_FLAG_VIA_VIRTUAL 2048 + +struct type +{ + /* Code for kind of type */ + enum type_code code; + /* Name of this type, or zero if none. + This is used for printing only. + Type names specified as input are defined by symbols. */ + char *name; + /* Length in bytes of storage for a value of this type */ + int length; + /* For a pointer type, describes the type of object pointed to. + For an array type, describes the type of the elements. + For a function or method type, describes the type of the value. + For a range type, describes the type of the full range. + Unused otherwise. */ + struct type *target_type; + /* Type that is a pointer to this type. + Zero if no such pointer-to type is known yet. + The debugger may add the address of such a type + if it has to construct one later. */ + struct type *pointer_type; + /* C++: also need a reference type. */ + struct type *reference_type; + struct type **arg_types; + + /* Type that is a function returning this type. + Zero if no such function type is known here. + The debugger may add the address of such a type + if it has to construct one later. */ + struct type *function_type; + +/* Handling of pointers to members: + TYPE_MAIN_VARIANT is used for pointer and pointer + to member types. Normally it the value of the address of its + containing type. However, for pointers to members, we must be + able to allocate pointer to member types and look them up + from some place of reference. + NEXT_VARIANT is the next element in the chain. */ + struct type *main_variant, *next_variant; + + /* Flags about this type. */ + short flags; + /* Number of fields described for this type */ + short nfields; + /* For structure and union types, a description of each field. + For set and pascal array types, there is one "field", + whose type is the domain type of the set or array. + For range types, there are two "fields", + the minimum and maximum values (both inclusive). + For enum types, each possible value is described by one "field". + + Using a pointer to a separate array of fields + allows all types to have the same size, which is useful + because we can allocate the space for a type before + we know what to put in it. */ + struct field + { + /* Position of this field, counting in bits from start of + containing structure. For a function type, this is the + position in the argument list of this argument. + For a range bound or enum value, this is the value itself. */ + int bitpos; + /* Size of this field, in bits, or zero if not packed. + For an unpacked field, the field's type's length + says how many bytes the field occupies. */ + int bitsize; + /* In a struct or enum type, type of this field. + In a function type, type of this argument. + In an array type, the domain-type of the array. */ + struct type *type; + /* Name of field, value or argument. + Zero for range bounds and array domains. */ + char *name; + } *fields; + + /* C++ */ + int *private_field_bits; + int *protected_field_bits; + + /* Number of methods described for this type */ + short nfn_fields; + /* Number of base classes this type derives from. */ + short n_baseclasses; + + /* Number of methods described for this type plus all the + methods that it derives from. */ + int nfn_fields_total; + + /* For classes, structures, and unions, a description of each field, + which consists of an overloaded name, followed by the types of + arguments that the method expects, and then the name after it + has been renamed to make it distinct. */ + struct fn_fieldlist + { + /* The overloaded name. */ + char *name; + /* The number of methods with this name. */ + int length; + /* The list of methods. */ + struct fn_field + { +#if 0 + /* The overloaded name */ + char *name; +#endif + /* The return value of the method */ + struct type *type; + /* The argument list */ + struct type **args; + /* The name after it has been processed */ + char *physname; + /* If this is a virtual function, the offset into the vtbl-1, + else 0. */ + int voffset; + } *fn_fields; + + int *private_fn_field_bits; + int *protected_fn_field_bits; + + } *fn_fieldlists; + + unsigned char via_protected; + unsigned char via_public; + + /* For types with virtual functions, VPTR_BASETYPE is the base class which + defined the virtual function table pointer. VPTR_FIELDNO is + the field number of that pointer in the structure. + + For types that are pointer to member types, VPTR_BASETYPE + ifs the type that this pointer is a member of. + + Unused otherwise. */ + struct type *vptr_basetype; + + int vptr_fieldno; + + /* If this type has a base class, put it here. + If this type is a pointer type, the chain of member pointer + types goes here. + Unused otherwise. + + Contrary to all maxims of C style and common sense, the baseclasses + are indexed from 1 to N_BASECLASSES rather than 0 to N_BASECLASSES-1 + (i.e. BASECLASSES points to one *before* the first element of + the array). */ + struct type **baseclasses; +}; + +/* All of the name-scope contours of the program + are represented by `struct block' objects. + All of these objects are pointed to by the blockvector. + + Each block represents one name scope. + Each lexical context has its own block. + + The first two blocks in the blockvector are special. + The first one contains all the symbols defined in this compilation + whose scope is the entire program linked together. + The second one contains all the symbols whose scope is the + entire compilation excluding other separate compilations. + In C, these correspond to global symbols and static symbols. + + Each block records a range of core addresses for the code that + is in the scope of the block. The first two special blocks + give, for the range of code, the entire range of code produced + by the compilation that the symbol segment belongs to. + + The blocks appear in the blockvector + in order of increasing starting-address, + and, within that, in order of decreasing ending-address. + + This implies that within the body of one function + the blocks appear in the order of a depth-first tree walk. */ + +struct blockvector +{ + /* Number of blocks in the list. */ + int nblocks; + /* The blocks themselves. */ + struct block *block[1]; +}; + +struct block +{ + /* Addresses in the executable code that are in this block. + Note: in an unrelocated symbol segment in a file, + these are always zero. They can be filled in from the + N_LBRAC and N_RBRAC symbols in the loader symbol table. */ + int startaddr, endaddr; + /* The symbol that names this block, + if the block is the body of a function; + otherwise, zero. + Note: In an unrelocated symbol segment in an object file, + this field may be zero even when the block has a name. + That is because the block is output before the name + (since the name resides in a higher block). + Since the symbol does point to the block (as its value), + it is possible to find the block and set its name properly. */ + struct symbol *function; + /* The `struct block' for the containing block, or 0 if none. */ + /* Note that in an unrelocated symbol segment in an object file + this pointer may be zero when the correct value should be + the second special block (for symbols whose scope is one compilation). + This is because the compiler ouptuts the special blocks at the + very end, after the other blocks. */ + struct block *superblock; + /* A flag indicating whether or not the fucntion corresponding + to this block was compiled with gcc or not. If there is no + function corresponding to this block, this meaning of this flag + is undefined. (In practice it will be 1 if the block was created + while processing a file compiled with gcc and 0 when not). */ + unsigned char gcc_compile_flag; + /* Number of local symbols. */ + int nsyms; + /* The symbols. */ + struct symbol *sym[1]; +}; + +/* Represent one symbol name; a variable, constant, function or typedef. */ + +/* Different name spaces for symbols. Looking up a symbol specifies + a namespace and ignores symbol definitions in other name spaces. + + VAR_NAMESPACE is the usual namespace. + In C, this contains variables, function names, typedef names + and enum type values. + + STRUCT_NAMESPACE is used in C to hold struct, union and enum type names. + Thus, if `struct foo' is used in a C program, + it produces a symbol named `foo' in the STRUCT_NAMESPACE. + + LABEL_NAMESPACE may be used for names of labels (for gotos); + currently it is not used and labels are not recorded at all. */ + +/* For a non-global symbol allocated statically, + the correct core address cannot be determined by the compiler. + The compiler puts an index number into the symbol's value field. + This index number can be matched with the "desc" field of + an entry in the loader symbol table. */ + +enum namespace +{ + UNDEF_NAMESPACE, VAR_NAMESPACE, STRUCT_NAMESPACE, LABEL_NAMESPACE, +}; + +/* An address-class says where to find the value of the symbol in core. */ + +enum address_class +{ + LOC_UNDEF, /* Not used; catches errors */ + LOC_CONST, /* Value is constant int */ + LOC_STATIC, /* Value is at fixed address */ + LOC_REGISTER, /* Value is in register */ + LOC_ARG, /* Value is at spec'd position in arglist */ + LOC_REF_ARG, /* Value address is at spec'd position in */ + /* arglist. */ + LOC_REGPARM, /* Value is at spec'd position in register window */ + LOC_LOCAL, /* Value is at spec'd pos in stack frame */ + LOC_TYPEDEF, /* Value not used; definition in SYMBOL_TYPE + Symbols in the namespace STRUCT_NAMESPACE + all have this class. */ + LOC_LABEL, /* Value is address in the code */ + LOC_BLOCK, /* Value is address of a `struct block'. + Function names have this class. */ + LOC_EXTERNAL, /* Value is at address not in this compilation. + This is used for .comm symbols + and for extern symbols within functions. + Inside GDB, this is changed to LOC_STATIC once the + real address is obtained from a loader symbol. */ + LOC_CONST_BYTES /* Value is a constant byte-sequence. */ +}; + +struct symbol +{ + /* Symbol name */ + char *name; + /* Name space code. */ + enum namespace namespace; + /* Address class */ + enum address_class class; + /* Data type of value */ + struct type *type; + /* constant value, or address if static, or register number, + or offset in arguments, or offset in stack frame. */ + union + { + long value; + struct block *block; /* for LOC_BLOCK */ + char *bytes; /* for LOC_CONST_BYTES */ + } + value; +}; + +struct partial_symbol +{ + /* Symbol name */ + char *name; + /* Name space code. */ + enum namespace namespace; + /* Address class (for info_symbols) */ + enum address_class class; + /* Value (only used for static functions currently). Done this + way so that we can use the struct symbol macros. + Note that the address of a function is SYMBOL_VALUE (pst) + in a partial symbol table, but BLOCK_START (SYMBOL_BLOCK_VALUE (st)) + in a symbol table. */ + union + { + long value; + } + value; +}; + +/* + * Vectors of all partial symbols read in from file; actually declared + * and used in dbxread.c. + */ +extern struct psymbol_allocation_list { + struct partial_symbol *list, *next; + int size; +} global_psymbols, static_psymbols; + + +/* Source-file information. + This describes the relation between source files and line numbers + and addresses in the program text. */ + +struct sourcevector +{ + int length; /* Number of source files described */ + struct source *source[1]; /* Descriptions of the files */ +}; + +/* Each item represents a line-->pc (or the reverse) mapping. This is + somewhat more wasteful of space than one might wish, but since only + the files which are actually debugged are read in to core, we don't + waste much space. + + Each item used to be an int; either minus a line number, or a + program counter. If it represents a line number, that is the line + described by the next program counter value. If it is positive, it + is the program counter at which the code for the next line starts. */ + +struct linetable_entry +{ + int line; + CORE_ADDR pc; +}; + +struct linetable +{ + int nitems; + struct linetable_entry item[1]; +}; + +/* All the information on one source file. */ + +struct source +{ + char *name; /* Name of file */ + struct linetable contents; +}; diff --git a/linux-0.01.tar.gz b/linux-0.01.tar.gz Binary files differnew file mode 100644 index 0000000..ed6029b --- /dev/null +++ b/linux-0.01.tar.gz diff --git a/linux/Makefile b/linux/Makefile new file mode 100644 index 0000000..d9d9fab --- /dev/null +++ b/linux/Makefile @@ -0,0 +1,96 @@ +# +# Makefile for linux. +# If you don't have '-mstring-insns' in your gcc (and nobody but me has :-) +# remove them from the CFLAGS defines. +# + +AS86 =as -0 -a +CC86 =cc -0 +LD86 =ld -0 + +AS =gas +LD =gld +LDFLAGS =-s -x -M +CC =gcc +CFLAGS =-Wall -O -fstrength-reduce -fomit-frame-pointer -fcombine-regs +CPP =gcc -E -nostdinc -Iinclude + +ARCHIVES=kernel/kernel.o mm/mm.o fs/fs.o +LIBS =lib/lib.a + +.c.s: + $(CC) $(CFLAGS) \ + -nostdinc -Iinclude -S -o $*.s $< +.s.o: + $(AS) -c -o $*.o $< +.c.o: + $(CC) $(CFLAGS) \ + -nostdinc -Iinclude -c -o $*.o $< + +all: Image + +Image: boot/boot tools/system tools/build + tools/build boot/boot tools/system > Image + sync + +tools/build: tools/build.c + $(CC) $(CFLAGS) \ + -o tools/build tools/build.c + chmem +65000 tools/build + +boot/head.o: boot/head.s + +tools/system: boot/head.o init/main.o \ + $(ARCHIVES) $(LIBS) + $(LD) $(LDFLAGS) boot/head.o init/main.o \ + $(ARCHIVES) \ + $(LIBS) \ + -o tools/system > System.map + +kernel/kernel.o: + (cd kernel; make) + +mm/mm.o: + (cd mm; make) + +fs/fs.o: + (cd fs; make) + +lib/lib.a: + (cd lib; make) + +boot/boot: boot/boot.s tools/system + (echo -n "SYSSIZE = (";ls -l tools/system | grep system \ + | cut -c25-31 | tr '\012' ' '; echo "+ 15 ) / 16") > tmp.s + cat boot/boot.s >> tmp.s + $(AS86) -o boot/boot.o tmp.s + rm -f tmp.s + $(LD86) -s -o boot/boot boot/boot.o + +clean: + rm -f Image System.map tmp_make boot/boot core + rm -f init/*.o boot/*.o tools/system tools/build + (cd mm;make clean) + (cd fs;make clean) + (cd kernel;make clean) + (cd lib;make clean) + +backup: clean + (cd .. ; tar cf - linux | compress16 - > backup.Z) + sync + +dep: + sed '/\#\#\# Dependencies/q' < Makefile > tmp_make + (for i in init/*.c;do echo -n "init/";$(CPP) -M $$i;done) >> tmp_make + cp tmp_make Makefile + (cd fs; make dep) + (cd kernel; make dep) + (cd mm; make dep) + +### Dependencies: +init/main.o : init/main.c include/unistd.h include/sys/stat.h \ + include/sys/types.h include/sys/times.h include/sys/utsname.h \ + include/utime.h include/time.h include/linux/tty.h include/termios.h \ + include/linux/sched.h include/linux/head.h include/linux/fs.h \ + include/linux/mm.h include/asm/system.h include/asm/io.h include/stddef.h \ + include/stdarg.h include/fcntl.h diff --git a/linux/boot/boot.s b/linux/boot/boot.s new file mode 100644 index 0000000..e19bbd2 --- /dev/null +++ b/linux/boot/boot.s @@ -0,0 +1,329 @@ +| +| boot.s +| +| boot.s is loaded at 0x7c00 by the bios-startup routines, and moves itself +| out of the way to address 0x90000, and jumps there. +| +| It then loads the system at 0x10000, using BIOS interrupts. Thereafter +| it disables all interrupts, moves the system down to 0x0000, changes +| to protected mode, and calls the start of system. System then must +| RE-initialize the protected mode in it's own tables, and enable +| interrupts as needed. +| +| NOTE! currently system is at most 8*65536 bytes long. This should be no +| problem, even in the future. I want to keep it simple. This 512 kB +| kernel size should be enough - in fact more would mean we'd have to move +| not just these start-up routines, but also do something about the cache- +| memory (block IO devices). The area left over in the lower 640 kB is meant +| for these. No other memory is assumed to be "physical", ie all memory +| over 1Mb is demand-paging. All addresses under 1Mb are guaranteed to match +| their physical addresses. +| +| NOTE1 abouve is no longer valid in it's entirety. cache-memory is allocated +| above the 1Mb mark as well as below. Otherwise it is mainly correct. +| +| NOTE 2! The boot disk type must be set at compile-time, by setting +| the following equ. Having the boot-up procedure hunt for the right +| disk type is severe brain-damage. +| The loader has been made as simple as possible (had to, to get it +| in 512 bytes with the code to move to protected mode), and continuos +| read errors will result in a unbreakable loop. Reboot by hand. It +| loads pretty fast by getting whole sectors at a time whenever possible. + +| 1.44Mb disks: +sectors = 18 +| 1.2Mb disks: +| sectors = 15 +| 720kB disks: +| sectors = 9 + +.globl begtext, begdata, begbss, endtext, enddata, endbss +.text +begtext: +.data +begdata: +.bss +begbss: +.text + +BOOTSEG = 0x07c0 +INITSEG = 0x9000 +SYSSEG = 0x1000 | system loaded at 0x10000 (65536). +ENDSEG = SYSSEG + SYSSIZE + +entry start +start: + mov ax,#BOOTSEG + mov ds,ax + mov ax,#INITSEG + mov es,ax + mov cx,#256 + sub si,si + sub di,di + rep + movw + jmpi go,INITSEG +go: mov ax,cs + mov ds,ax + mov es,ax + mov ss,ax + mov sp,#0x400 | arbitrary value >>512 + + mov ah,#0x03 | read cursor pos + xor bh,bh + int 0x10 + + mov cx,#24 + mov bx,#0x0007 | page 0, attribute 7 (normal) + mov bp,#msg1 + mov ax,#0x1301 | write string, move cursor + int 0x10 + +| ok, we've written the message, now +| we want to load the system (at 0x10000) + + mov ax,#SYSSEG + mov es,ax | segment of 0x010000 + call read_it + call kill_motor + +| if the read went well we get current cursor position ans save it for +| posterity. + + mov ah,#0x03 | read cursor pos + xor bh,bh + int 0x10 | save it in known place, con_init fetches + mov [510],dx | it from 0x90510. + +| now we want to move to protected mode ... + + cli | no interrupts allowed ! + +| first we move the system to it's rightful place + + mov ax,#0x0000 + cld | 'direction'=0, movs moves forward +do_move: + mov es,ax | destination segment + add ax,#0x1000 + cmp ax,#0x9000 + jz end_move + mov ds,ax | source segment + sub di,di + sub si,si + mov cx,#0x8000 + rep + movsw + j do_move + +| then we load the segment descriptors + +end_move: + + mov ax,cs | right, forgot this at first. didn't work :-) + mov ds,ax + lidt idt_48 | load idt with 0,0 + lgdt gdt_48 | load gdt with whatever appropriate + +| that was painless, now we enable A20 + + call empty_8042 + mov al,#0xD1 | command write + out #0x64,al + call empty_8042 + mov al,#0xDF | A20 on + out #0x60,al + call empty_8042 + +| well, that went ok, I hope. Now we have to reprogram the interrupts :-( +| we put them right after the intel-reserved hardware interrupts, at +| int 0x20-0x2F. There they won't mess up anything. Sadly IBM really +| messed this up with the original PC, and they haven't been able to +| rectify it afterwards. Thus the bios puts interrupts at 0x08-0x0f, +| which is used for the internal hardware interrupts as well. We just +| have to reprogram the 8259's, and it isn't fun. + + mov al,#0x11 | initialization sequence + out #0x20,al | send it to 8259A-1 + .word 0x00eb,0x00eb | jmp $+2, jmp $+2 + out #0xA0,al | and to 8259A-2 + .word 0x00eb,0x00eb + mov al,#0x20 | start of hardware int's (0x20) + out #0x21,al + .word 0x00eb,0x00eb + mov al,#0x28 | start of hardware int's 2 (0x28) + out #0xA1,al + .word 0x00eb,0x00eb + mov al,#0x04 | 8259-1 is master + out #0x21,al + .word 0x00eb,0x00eb + mov al,#0x02 | 8259-2 is slave + out #0xA1,al + .word 0x00eb,0x00eb + mov al,#0x01 | 8086 mode for both + out #0x21,al + .word 0x00eb,0x00eb + out #0xA1,al + .word 0x00eb,0x00eb + mov al,#0xFF | mask off all interrupts for now + out #0x21,al + .word 0x00eb,0x00eb + out #0xA1,al + +| well, that certainly wasn't fun :-(. Hopefully it works, and we don't +| need no steenking BIOS anyway (except for the initial loading :-). +| The BIOS-routine wants lots of unnecessary data, and it's less +| "interesting" anyway. This is how REAL programmers do it. +| +| Well, now's the time to actually move into protected mode. To make +| things as simple as possible, we do no register set-up or anything, +| we let the gnu-compiled 32-bit programs do that. We just jump to +| absolute address 0x00000, in 32-bit protected mode. + + mov ax,#0x0001 | protected mode (PE) bit + lmsw ax | This is it! + jmpi 0,8 | jmp offset 0 of segment 8 (cs) + +| This routine checks that the keyboard command queue is empty +| No timeout is used - if this hangs there is something wrong with +| the machine, and we probably couldn't proceed anyway. +empty_8042: + .word 0x00eb,0x00eb + in al,#0x64 | 8042 status port + test al,#2 | is input buffer full? + jnz empty_8042 | yes - loop + ret + +| This routine loads the system at address 0x10000, making sure +| no 64kB boundaries are crossed. We try to load it as fast as +| possible, loading whole tracks whenever we can. +| +| in: es - starting address segment (normally 0x1000) +| +| This routine has to be recompiled to fit another drive type, +| just change the "sectors" variable at the start of the file +| (originally 18, for a 1.44Mb drive) +| +sread: .word 1 | sectors read of current track +head: .word 0 | current head +track: .word 0 | current track +read_it: + mov ax,es + test ax,#0x0fff +die: jne die | es must be at 64kB boundary + xor bx,bx | bx is starting address within segment +rp_read: + mov ax,es + cmp ax,#ENDSEG | have we loaded all yet? + jb ok1_read + ret +ok1_read: + mov ax,#sectors + sub ax,sread + mov cx,ax + shl cx,#9 + add cx,bx + jnc ok2_read + je ok2_read + xor ax,ax + sub ax,bx + shr ax,#9 +ok2_read: + call read_track + mov cx,ax + add ax,sread + cmp ax,#sectors + jne ok3_read + mov ax,#1 + sub ax,head + jne ok4_read + inc track +ok4_read: + mov head,ax + xor ax,ax +ok3_read: + mov sread,ax + shl cx,#9 + add bx,cx + jnc rp_read + mov ax,es + add ax,#0x1000 + mov es,ax + xor bx,bx + jmp rp_read + +read_track: + push ax + push bx + push cx + push dx + mov dx,track + mov cx,sread + inc cx + mov ch,dl + mov dx,head + mov dh,dl + mov dl,#0 + and dx,#0x0100 + mov ah,#2 + int 0x13 + jc bad_rt + pop dx + pop cx + pop bx + pop ax + ret +bad_rt: mov ax,#0 + mov dx,#0 + int 0x13 + pop dx + pop cx + pop bx + pop ax + jmp read_track + +/* + * This procedure turns off the floppy drive motor, so + * that we enter the kernel in a known state, and + * don't have to worry about it later. + */ +kill_motor: + push dx + mov dx,#0x3f2 + mov al,#0 + outb + pop dx + ret + +gdt: + .word 0,0,0,0 | dummy + + .word 0x07FF | 8Mb - limit=2047 (2048*4096=8Mb) + .word 0x0000 | base address=0 + .word 0x9A00 | code read/exec + .word 0x00C0 | granularity=4096, 386 + + .word 0x07FF | 8Mb - limit=2047 (2048*4096=8Mb) + .word 0x0000 | base address=0 + .word 0x9200 | data read/write + .word 0x00C0 | granularity=4096, 386 + +idt_48: + .word 0 | idt limit=0 + .word 0,0 | idt base=0L + +gdt_48: + .word 0x800 | gdt limit=2048, 256 GDT entries + .word gdt,0x9 | gdt base = 0X9xxxx + +msg1: + .byte 13,10 + .ascii "Loading system ..." + .byte 13,10,13,10 + +.text +endtext: +.data +enddata: +.bss +endbss: diff --git a/linux/boot/head.s b/linux/boot/head.s new file mode 100644 index 0000000..c008ba8 --- /dev/null +++ b/linux/boot/head.s @@ -0,0 +1,175 @@ +/* + * head.s contains the 32-bit startup code. + * + * NOTE!!! Startup happens at absolute address 0x00000000, which is also where + * the page directory will exist. The startup code will be overwritten by + * the page directory. + */ +.text +.globl _idt,_gdt,_pg_dir +_pg_dir: +startup_32: + movl $0x10,%eax + mov %ax,%ds + mov %ax,%es + mov %ax,%fs + mov %ax,%gs + lss _stack_start,%esp + call setup_idt + call setup_gdt + movl $0x10,%eax # reload all the segment registers + mov %ax,%ds # after changing gdt. CS was already + mov %ax,%es # reloaded in 'setup_gdt' + mov %ax,%fs + mov %ax,%gs + lss _stack_start,%esp + xorl %eax,%eax +1: incl %eax # check that A20 really IS enabled + movl %eax,0x000000 + cmpl %eax,0x100000 + je 1b + movl %cr0,%eax # check math chip + andl $0x80000011,%eax # Save PG,ET,PE + testl $0x10,%eax + jne 1f # ET is set - 387 is present + orl $4,%eax # else set emulate bit +1: movl %eax,%cr0 + jmp after_page_tables + +/* + * setup_idt + * + * sets up a idt with 256 entries pointing to + * ignore_int, interrupt gates. It then loads + * idt. Everything that wants to install itself + * in the idt-table may do so themselves. Interrupts + * are enabled elsewhere, when we can be relatively + * sure everything is ok. This routine will be over- + * written by the page tables. + */ +setup_idt: + lea ignore_int,%edx + movl $0x00080000,%eax + movw %dx,%ax /* selector = 0x0008 = cs */ + movw $0x8E00,%dx /* interrupt gate - dpl=0, present */ + + lea _idt,%edi + mov $256,%ecx +rp_sidt: + movl %eax,(%edi) + movl %edx,4(%edi) + addl $8,%edi + dec %ecx + jne rp_sidt + lidt idt_descr + ret + +/* + * setup_gdt + * + * This routines sets up a new gdt and loads it. + * Only two entries are currently built, the same + * ones that were built in init.s. The routine + * is VERY complicated at two whole lines, so this + * rather long comment is certainly needed :-). + * This routine will beoverwritten by the page tables. + */ +setup_gdt: + lgdt gdt_descr + ret + +.org 0x1000 +pg0: + +.org 0x2000 +pg1: + +.org 0x3000 +pg2: # This is not used yet, but if you + # want to expand past 8 Mb, you'll have + # to use it. + +.org 0x4000 +after_page_tables: + pushl $0 # These are the parameters to main :-) + pushl $0 + pushl $0 + pushl $L6 # return address for main, if it decides to. + pushl $_main + jmp setup_paging +L6: + jmp L6 # main should never return here, but + # just in case, we know what happens. + +/* This is the default interrupt "handler" :-) */ +.align 2 +ignore_int: + incb 0xb8000+160 # put something on the screen + movb $2,0xb8000+161 # so that we know something + iret # happened + + +/* + * Setup_paging + * + * This routine sets up paging by setting the page bit + * in cr0. The page tables are set up, identity-mapping + * the first 8MB. The pager assumes that no illegal + * addresses are produced (ie >4Mb on a 4Mb machine). + * + * NOTE! Although all physical memory should be identity + * mapped by this routine, only the kernel page functions + * use the >1Mb addresses directly. All "normal" functions + * use just the lower 1Mb, or the local data space, which + * will be mapped to some other place - mm keeps track of + * that. + * + * For those with more memory than 8 Mb - tough luck. I've + * not got it, why should you :-) The source is here. Change + * it. (Seriously - it shouldn't be too difficult. Mostly + * change some constants etc. I left it at 8Mb, as my machine + * even cannot be extended past that (ok, but it was cheap :-) + * I've tried to show which constants to change by having + * some kind of marker at them (search for "8Mb"), but I + * won't guarantee that's all :-( ) + */ +.align 2 +setup_paging: + movl $1024*3,%ecx + xorl %eax,%eax + xorl %edi,%edi /* pg_dir is at 0x000 */ + cld;rep;stosl + movl $pg0+7,_pg_dir /* set present bit/user r/w */ + movl $pg1+7,_pg_dir+4 /* --------- " " --------- */ + movl $pg1+4092,%edi + movl $0x7ff007,%eax /* 8Mb - 4096 + 7 (r/w user,p) */ + std +1: stosl /* fill pages backwards - more efficient :-) */ + subl $0x1000,%eax + jge 1b + xorl %eax,%eax /* pg_dir is at 0x0000 */ + movl %eax,%cr3 /* cr3 - page directory start */ + movl %cr0,%eax + orl $0x80000000,%eax + movl %eax,%cr0 /* set paging (PG) bit */ + ret /* this also flushes prefetch-queue */ + +.align 2 +.word 0 +idt_descr: + .word 256*8-1 # idt contains 256 entries + .long _idt +.align 2 +.word 0 +gdt_descr: + .word 256*8-1 # so does gdt (not that that's any + .long _gdt # magic number, but it works for me :^) + + .align 3 +_idt: .fill 256,8,0 # idt is uninitialized + +_gdt: .quad 0x0000000000000000 /* NULL descriptor */ + .quad 0x00c09a00000007ff /* 8Mb */ + .quad 0x00c09200000007ff /* 8Mb */ + .quad 0x0000000000000000 /* TEMPORARY - don't use */ + .fill 252,8,0 /* space for LDT's and TSS's etc */ diff --git a/linux/fs/Makefile b/linux/fs/Makefile new file mode 100644 index 0000000..24e7e4c --- /dev/null +++ b/linux/fs/Makefile @@ -0,0 +1,95 @@ +AR =gar +AS =gas +CC =gcc +LD =gld +CFLAGS =-Wall -O -fstrength-reduce -fcombine-regs -fomit-frame-pointer \ + -mstring-insns -nostdinc -I../include +CPP =gcc -E -nostdinc -I../include + +.c.s: + $(CC) $(CFLAGS) \ + -S -o $*.s $< +.c.o: + $(CC) $(CFLAGS) \ + -c -o $*.o $< +.s.o: + $(AS) -o $*.o $< + +OBJS= open.o read_write.o inode.o file_table.o buffer.o super.o \ + block_dev.o char_dev.o file_dev.o stat.o exec.o pipe.o namei.o \ + bitmap.o fcntl.o ioctl.o tty_ioctl.o truncate.o + +fs.o: $(OBJS) + $(LD) -r -o fs.o $(OBJS) + +clean: + rm -f core *.o *.a tmp_make + for i in *.c;do rm -f `basename $$i .c`.s;done + +dep: + sed '/\#\#\# Dependencies/q' < Makefile > tmp_make + (for i in *.c;do $(CPP) -M $$i;done) >> tmp_make + cp tmp_make Makefile + +### Dependencies: +bitmap.o : bitmap.c ../include/string.h ../include/linux/sched.h \ + ../include/linux/head.h ../include/linux/fs.h ../include/sys/types.h \ + ../include/linux/mm.h ../include/linux/kernel.h +block_dev.o : block_dev.c ../include/errno.h ../include/linux/fs.h \ + ../include/sys/types.h ../include/linux/kernel.h ../include/asm/segment.h +buffer.o : buffer.c ../include/linux/config.h ../include/linux/sched.h \ + ../include/linux/head.h ../include/linux/fs.h ../include/sys/types.h \ + ../include/linux/mm.h ../include/linux/kernel.h ../include/asm/system.h +char_dev.o : char_dev.c ../include/errno.h ../include/linux/sched.h \ + ../include/linux/head.h ../include/linux/fs.h ../include/sys/types.h \ + ../include/linux/mm.h ../include/linux/kernel.h +exec.o : exec.c ../include/errno.h ../include/sys/stat.h \ + ../include/sys/types.h ../include/a.out.h ../include/linux/fs.h \ + ../include/linux/sched.h ../include/linux/head.h ../include/linux/mm.h \ + ../include/linux/kernel.h ../include/asm/segment.h +fcntl.o : fcntl.c ../include/string.h ../include/errno.h \ + ../include/linux/sched.h ../include/linux/head.h ../include/linux/fs.h \ + ../include/sys/types.h ../include/linux/mm.h ../include/linux/kernel.h \ + ../include/asm/segment.h ../include/fcntl.h ../include/sys/stat.h +file_dev.o : file_dev.c ../include/errno.h ../include/fcntl.h \ + ../include/sys/types.h ../include/linux/sched.h ../include/linux/head.h \ + ../include/linux/fs.h ../include/linux/mm.h ../include/linux/kernel.h \ + ../include/asm/segment.h +file_table.o : file_table.c ../include/linux/fs.h ../include/sys/types.h +inode.o : inode.c ../include/string.h ../include/linux/sched.h \ + ../include/linux/head.h ../include/linux/fs.h ../include/sys/types.h \ + ../include/linux/mm.h ../include/linux/kernel.h ../include/asm/system.h +ioctl.o : ioctl.c ../include/string.h ../include/errno.h \ + ../include/sys/stat.h ../include/sys/types.h ../include/linux/sched.h \ + ../include/linux/head.h ../include/linux/fs.h ../include/linux/mm.h +namei.o : namei.c ../include/linux/sched.h ../include/linux/head.h \ + ../include/linux/fs.h ../include/sys/types.h ../include/linux/mm.h \ + ../include/linux/kernel.h ../include/asm/segment.h ../include/string.h \ + ../include/fcntl.h ../include/errno.h ../include/const.h \ + ../include/sys/stat.h +open.o : open.c ../include/string.h ../include/errno.h ../include/fcntl.h \ + ../include/sys/types.h ../include/utime.h ../include/sys/stat.h \ + ../include/linux/sched.h ../include/linux/head.h ../include/linux/fs.h \ + ../include/linux/mm.h ../include/linux/tty.h ../include/termios.h \ + ../include/linux/kernel.h ../include/asm/segment.h +pipe.o : pipe.c ../include/signal.h ../include/sys/types.h \ + ../include/linux/sched.h ../include/linux/head.h ../include/linux/fs.h \ + ../include/linux/mm.h ../include/asm/segment.h +read_write.o : read_write.c ../include/sys/stat.h ../include/sys/types.h \ + ../include/errno.h ../include/linux/kernel.h ../include/linux/sched.h \ + ../include/linux/head.h ../include/linux/fs.h ../include/linux/mm.h \ + ../include/asm/segment.h +stat.o : stat.c ../include/errno.h ../include/sys/stat.h \ + ../include/sys/types.h ../include/linux/fs.h ../include/linux/sched.h \ + ../include/linux/head.h ../include/linux/mm.h ../include/linux/kernel.h \ + ../include/asm/segment.h +super.o : super.c ../include/linux/config.h ../include/linux/sched.h \ + ../include/linux/head.h ../include/linux/fs.h ../include/sys/types.h \ + ../include/linux/mm.h ../include/linux/kernel.h +truncate.o : truncate.c ../include/linux/sched.h ../include/linux/head.h \ + ../include/linux/fs.h ../include/sys/types.h ../include/linux/mm.h \ + ../include/sys/stat.h +tty_ioctl.o : tty_ioctl.c ../include/errno.h ../include/termios.h \ + ../include/linux/sched.h ../include/linux/head.h ../include/linux/fs.h \ + ../include/sys/types.h ../include/linux/mm.h ../include/linux/kernel.h \ + ../include/linux/tty.h ../include/asm/segment.h ../include/asm/system.h diff --git a/linux/fs/bitmap.c b/linux/fs/bitmap.c new file mode 100644 index 0000000..49bb81b --- /dev/null +++ b/linux/fs/bitmap.c @@ -0,0 +1,158 @@ +/* bitmap.c contains the code that handles the inode and block bitmaps */ +#include <string.h> + +#include <linux/sched.h> +#include <linux/kernel.h> + +#define clear_block(addr) \ +__asm__("cld\n\t" \ + "rep\n\t" \ + "stosl" \ + ::"a" (0),"c" (BLOCK_SIZE/4),"D" ((long) (addr)):"cx","di") + +#define set_bit(nr,addr) ({\ +register int res __asm__("ax"); \ +__asm__("btsl %2,%3\n\tsetb %%al":"=a" (res):"0" (0),"r" (nr),"m" (*(addr))); \ +res;}) + +#define clear_bit(nr,addr) ({\ +register int res __asm__("ax"); \ +__asm__("btrl %2,%3\n\tsetnb %%al":"=a" (res):"0" (0),"r" (nr),"m" (*(addr))); \ +res;}) + +#define find_first_zero(addr) ({ \ +int __res; \ +__asm__("cld\n" \ + "1:\tlodsl\n\t" \ + "notl %%eax\n\t" \ + "bsfl %%eax,%%edx\n\t" \ + "je 2f\n\t" \ + "addl %%edx,%%ecx\n\t" \ + "jmp 3f\n" \ + "2:\taddl $32,%%ecx\n\t" \ + "cmpl $8192,%%ecx\n\t" \ + "jl 1b\n" \ + "3:" \ + :"=c" (__res):"c" (0),"S" (addr):"ax","dx","si"); \ +__res;}) + +void free_block(int dev, int block) +{ + struct super_block * sb; + struct buffer_head * bh; + + if (!(sb = get_super(dev))) + panic("trying to free block on nonexistent device"); + if (block < sb->s_firstdatazone || block >= sb->s_nzones) + panic("trying to free block not in datazone"); + bh = get_hash_table(dev,block); + if (bh) { + if (bh->b_count != 1) { + printk("trying to free block (%04x:%d), count=%d\n", + dev,block,bh->b_count); + return; + } + bh->b_dirt=0; + bh->b_uptodate=0; + brelse(bh); + } + block -= sb->s_firstdatazone - 1 ; + if (clear_bit(block&8191,sb->s_zmap[block/8192]->b_data)) { + printk("block (%04x:%d) ",dev,block+sb->s_firstdatazone-1); + panic("free_block: bit already cleared"); + } + sb->s_zmap[block/8192]->b_dirt = 1; +} + +int new_block(int dev) +{ + struct buffer_head * bh; + struct super_block * sb; + int i,j; + + if (!(sb = get_super(dev))) + panic("trying to get new block from nonexistant device"); + j = 8192; + for (i=0 ; i<8 ; i++) + if (bh=sb->s_zmap[i]) + if ((j=find_first_zero(bh->b_data))<8192) + break; + if (i>=8 || !bh || j>=8192) + return 0; + if (set_bit(j,bh->b_data)) + panic("new_block: bit already set"); + bh->b_dirt = 1; + j += i*8192 + sb->s_firstdatazone-1; + if (j >= sb->s_nzones) + return 0; + if (!(bh=getblk(dev,j))) + panic("new_block: cannot get block"); + if (bh->b_count != 1) + panic("new block: count is != 1"); + clear_block(bh->b_data); + bh->b_uptodate = 1; + bh->b_dirt = 1; + brelse(bh); + return j; +} + +void free_inode(struct m_inode * inode) +{ + struct super_block * sb; + struct buffer_head * bh; + + if (!inode) + return; + if (!inode->i_dev) { + memset(inode,0,sizeof(*inode)); + return; + } + if (inode->i_count>1) { + printk("trying to free inode with count=%d\n",inode->i_count); + panic("free_inode"); + } + if (inode->i_nlinks) + panic("trying to free inode with links"); + if (!(sb = get_super(inode->i_dev))) + panic("trying to free inode on nonexistent device"); + if (inode->i_num < 1 || inode->i_num > sb->s_ninodes) + panic("trying to free inode 0 or nonexistant inode"); + if (!(bh=sb->s_imap[inode->i_num>>13])) + panic("nonexistent imap in superblock"); + if (clear_bit(inode->i_num&8191,bh->b_data)) + panic("free_inode: bit already cleared"); + bh->b_dirt = 1; + memset(inode,0,sizeof(*inode)); +} + +struct m_inode * new_inode(int dev) +{ + struct m_inode * inode; + struct super_block * sb; + struct buffer_head * bh; + int i,j; + + if (!(inode=get_empty_inode())) + return NULL; + if (!(sb = get_super(dev))) + panic("new_inode with unknown device"); + j = 8192; + for (i=0 ; i<8 ; i++) + if (bh=sb->s_imap[i]) + if ((j=find_first_zero(bh->b_data))<8192) + break; + if (!bh || j >= 8192 || j+i*8192 > sb->s_ninodes) { + iput(inode); + return NULL; + } + if (set_bit(j,bh->b_data)) + panic("new_inode: bit already set"); + bh->b_dirt = 1; + inode->i_count=1; + inode->i_nlinks=1; + inode->i_dev=dev; + inode->i_dirt=1; + inode->i_num = j + i*8192; + inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; + return inode; +} diff --git a/linux/fs/block_dev.c b/linux/fs/block_dev.c new file mode 100644 index 0000000..7bbae6a --- /dev/null +++ b/linux/fs/block_dev.c @@ -0,0 +1,86 @@ +#include <errno.h> + +#include <linux/fs.h> +#include <linux/kernel.h> +#include <asm/segment.h> + +#define NR_BLK_DEV ((sizeof (rd_blk))/(sizeof (rd_blk[0]))) + +int block_write(int dev, long * pos, char * buf, int count) +{ + int block = *pos / BLOCK_SIZE; + int offset = *pos % BLOCK_SIZE; + int chars; + int written = 0; + struct buffer_head * bh; + register char * p; + + while (count>0) { + bh = bread(dev,block); + if (!bh) + return written?written:-EIO; + chars = (count<BLOCK_SIZE) ? count : BLOCK_SIZE; + p = offset + bh->b_data; + offset = 0; + block++; + *pos += chars; + written += chars; + count -= chars; + while (chars-->0) + *(p++) = get_fs_byte(buf++); + bh->b_dirt = 1; + brelse(bh); + } + return written; +} + +int block_read(int dev, unsigned long * pos, char * buf, int count) +{ + int block = *pos / BLOCK_SIZE; + int offset = *pos % BLOCK_SIZE; + int chars; + int read = 0; + struct buffer_head * bh; + register char * p; + + while (count>0) { + bh = bread(dev,block); + if (!bh) + return read?read:-EIO; + chars = (count<BLOCK_SIZE) ? count : BLOCK_SIZE; + p = offset + bh->b_data; + offset = 0; + block++; + *pos += chars; + read += chars; + count -= chars; + while (chars-->0) + put_fs_byte(*(p++),buf++); + bh->b_dirt = 1; + brelse(bh); + } + return read; +} + +extern void rw_hd(int rw, struct buffer_head * bh); + +typedef void (*blk_fn)(int rw, struct buffer_head * bh); + +static blk_fn rd_blk[]={ + NULL, /* nodev */ + NULL, /* dev mem */ + NULL, /* dev fd */ + rw_hd, /* dev hd */ + NULL, /* dev ttyx */ + NULL, /* dev tty */ + NULL}; /* dev lp */ + +void ll_rw_block(int rw, struct buffer_head * bh) +{ + blk_fn blk_addr; + unsigned int major; + + if ((major=MAJOR(bh->b_dev)) >= NR_BLK_DEV || !(blk_addr=rd_blk[major])) + panic("Trying to read nonexistent block-device"); + blk_addr(rw, bh); +} diff --git a/linux/fs/buffer.c b/linux/fs/buffer.c new file mode 100644 index 0000000..312ab17 --- /dev/null +++ b/linux/fs/buffer.c @@ -0,0 +1,254 @@ +/* + * 'buffer.c' implements the buffer-cache functions. Race-conditions have + * been avoided by NEVER letting a interrupt change a buffer (except for the + * data, of course), but instead letting the caller do it. NOTE! As interrupts + * can wake up a caller, some cli-sti sequences are needed to check for + * sleep-on-calls. These should be extremely quick, though (I hope). + */ + +#include <linux/config.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <asm/system.h> + +#if (BUFFER_END & 0xfff) +#error "Bad BUFFER_END value" +#endif + +#if (BUFFER_END > 0xA0000 && BUFFER_END <= 0x100000) +#error "Bad BUFFER_END value" +#endif + +extern int end; +struct buffer_head * start_buffer = (struct buffer_head *) &end; +struct buffer_head * hash_table[NR_HASH]; +static struct buffer_head * free_list; +static struct task_struct * buffer_wait = NULL; +int NR_BUFFERS = 0; + +static inline void wait_on_buffer(struct buffer_head * bh) +{ + cli(); + while (bh->b_lock) + sleep_on(&bh->b_wait); + sti(); +} + +int sys_sync(void) +{ + int i; + struct buffer_head * bh; + + sync_inodes(); /* write out inodes into buffers */ + bh = start_buffer; + for (i=0 ; i<NR_BUFFERS ; i++,bh++) { + wait_on_buffer(bh); + if (bh->b_dirt) + ll_rw_block(WRITE,bh); + } + return 0; +} + +static int sync_dev(int dev) +{ + int i; + struct buffer_head * bh; + + bh = start_buffer; + for (i=0 ; i<NR_BUFFERS ; i++,bh++) { + if (bh->b_dev != dev) + continue; + wait_on_buffer(bh); + if (bh->b_dirt) + ll_rw_block(WRITE,bh); + } + return 0; +} + +#define _hashfn(dev,block) (((unsigned)(dev^block))%NR_HASH) +#define hash(dev,block) hash_table[_hashfn(dev,block)] + +static inline void remove_from_queues(struct buffer_head * bh) +{ +/* remove from hash-queue */ + if (bh->b_next) + bh->b_next->b_prev = bh->b_prev; + if (bh->b_prev) + bh->b_prev->b_next = bh->b_next; + if (hash(bh->b_dev,bh->b_blocknr) == bh) + hash(bh->b_dev,bh->b_blocknr) = bh->b_next; +/* remove from free list */ + if (!(bh->b_prev_free) || !(bh->b_next_free)) + panic("Free block list corrupted"); + bh->b_prev_free->b_next_free = bh->b_next_free; + bh->b_next_free->b_prev_free = bh->b_prev_free; + if (free_list == bh) + free_list = bh->b_next_free; +} + +static inline void insert_into_queues(struct buffer_head * bh) +{ +/* put at end of free list */ + bh->b_next_free = free_list; + bh->b_prev_free = free_list->b_prev_free; + free_list->b_prev_free->b_next_free = bh; + free_list->b_prev_free = bh; +/* put the buffer in new hash-queue if it has a device */ + bh->b_prev = NULL; + bh->b_next = NULL; + if (!bh->b_dev) + return; + bh->b_next = hash(bh->b_dev,bh->b_blocknr); + hash(bh->b_dev,bh->b_blocknr) = bh; + bh->b_next->b_prev = bh; +} + +static struct buffer_head * find_buffer(int dev, int block) +{ + struct buffer_head * tmp; + + for (tmp = hash(dev,block) ; tmp != NULL ; tmp = tmp->b_next) + if (tmp->b_dev==dev && tmp->b_blocknr==block) + return tmp; + return NULL; +} + +/* + * Why like this, I hear you say... The reason is race-conditions. + * As we don't lock buffers (unless we are readint them, that is), + * something might happen to it while we sleep (ie a read-error + * will force it bad). This shouldn't really happen currently, but + * the code is ready. + */ +struct buffer_head * get_hash_table(int dev, int block) +{ + struct buffer_head * bh; + +repeat: + if (!(bh=find_buffer(dev,block))) + return NULL; + bh->b_count++; + wait_on_buffer(bh); + if (bh->b_dev != dev || bh->b_blocknr != block) { + brelse(bh); + goto repeat; + } + return bh; +} + +/* + * Ok, this is getblk, and it isn't very clear, again to hinder + * race-conditions. Most of the code is seldom used, (ie repeating), + * so it should be much more efficient than it looks. + */ +struct buffer_head * getblk(int dev,int block) +{ + struct buffer_head * tmp; + +repeat: + if (tmp=get_hash_table(dev,block)) + return tmp; + tmp = free_list; + do { + if (!tmp->b_count) { + wait_on_buffer(tmp); /* we still have to wait */ + if (!tmp->b_count) /* on it, it might be dirty */ + break; + } + tmp = tmp->b_next_free; + } while (tmp != free_list || (tmp=NULL)); + /* Kids, don't try THIS at home ^^^^^. Magic */ + if (!tmp) { + printk("Sleeping on free buffer .."); + sleep_on(&buffer_wait); + printk("ok\n"); + goto repeat; + } + tmp->b_count++; + remove_from_queues(tmp); +/* + * Now, when we know nobody can get to this node (as it's removed from the + * free list), we write it out. We can sleep here without fear of race- + * conditions. + */ + if (tmp->b_dirt) + sync_dev(tmp->b_dev); +/* update buffer contents */ + tmp->b_dev=dev; + tmp->b_blocknr=block; + tmp->b_dirt=0; + tmp->b_uptodate=0; +/* NOTE!! While we possibly slept in sync_dev(), somebody else might have + * added "this" block already, so check for that. Thank God for goto's. + */ + if (find_buffer(dev,block)) { + tmp->b_dev=0; /* ok, someone else has beaten us */ + tmp->b_blocknr=0; /* to it - free this block and */ + tmp->b_count=0; /* try again */ + insert_into_queues(tmp); + goto repeat; + } +/* and then insert into correct position */ + insert_into_queues(tmp); + return tmp; +} + +void brelse(struct buffer_head * buf) +{ + if (!buf) + return; + wait_on_buffer(buf); + if (!(buf->b_count--)) + panic("Trying to free free buffer"); + wake_up(&buffer_wait); +} + +/* + * bread() reads a specified block and returns the buffer that contains + * it. It returns NULL if the block was unreadable. + */ +struct buffer_head * bread(int dev,int block) +{ + struct buffer_head * bh; + + if (!(bh=getblk(dev,block))) + panic("bread: getblk returned NULL\n"); + if (bh->b_uptodate) + return bh; + ll_rw_block(READ,bh); + if (bh->b_uptodate) + return bh; + brelse(bh); + return (NULL); +} + +void buffer_init(void) +{ + struct buffer_head * h = start_buffer; + void * b = (void *) BUFFER_END; + int i; + + while ( (b -= BLOCK_SIZE) >= ((void *) (h+1)) ) { + h->b_dev = 0; + h->b_dirt = 0; + h->b_count = 0; + h->b_lock = 0; + h->b_uptodate = 0; + h->b_wait = NULL; + h->b_next = NULL; + h->b_prev = NULL; + h->b_data = (char *) b; + h->b_prev_free = h-1; + h->b_next_free = h+1; + h++; + NR_BUFFERS++; + if (b == (void *) 0x100000) + b = (void *) 0xA0000; + } + h--; + free_list = start_buffer; + free_list->b_prev_free = h; + h->b_next_free = free_list; + for (i=0;i<NR_HASH;i++) + hash_table[i]=NULL; +} diff --git a/linux/fs/char_dev.c b/linux/fs/char_dev.c new file mode 100644 index 0000000..e974242 --- /dev/null +++ b/linux/fs/char_dev.c @@ -0,0 +1,50 @@ +#include <errno.h> + +#include <linux/sched.h> +#include <linux/kernel.h> + +extern int tty_read(unsigned minor,char * buf,int count); +extern int tty_write(unsigned minor,char * buf,int count); + +static int rw_ttyx(int rw,unsigned minor,char * buf,int count); +static int rw_tty(int rw,unsigned minor,char * buf,int count); + +typedef (*crw_ptr)(int rw,unsigned minor,char * buf,int count); + +#define NRDEVS ((sizeof (crw_table))/(sizeof (crw_ptr))) + +static crw_ptr crw_table[]={ + NULL, /* nodev */ + NULL, /* /dev/mem */ + NULL, /* /dev/fd */ + NULL, /* /dev/hd */ + rw_ttyx, /* /dev/ttyx */ + rw_tty, /* /dev/tty */ + NULL, /* /dev/lp */ + NULL}; /* unnamed pipes */ + +static int rw_ttyx(int rw,unsigned minor,char * buf,int count) +{ + return ((rw==READ)?tty_read(minor,buf,count): + tty_write(minor,buf,count)); +} + +static int rw_tty(int rw,unsigned minor,char * buf,int count) +{ + if (current->tty<0) + return -EPERM; + return rw_ttyx(rw,current->tty,buf,count); +} + +int rw_char(int rw,int dev, char * buf, int count) +{ + crw_ptr call_addr; + + if (MAJOR(dev)>=NRDEVS) + panic("rw_char: dev>NRDEV"); + if (!(call_addr=crw_table[MAJOR(dev)])) { + printk("dev: %04x\n",dev); + panic("Trying to r/w from/to nonexistent character device"); + } + return call_addr(rw,MINOR(dev),buf,count); +} diff --git a/linux/fs/exec.c b/linux/fs/exec.c new file mode 100644 index 0000000..e9d5058 --- /dev/null +++ b/linux/fs/exec.c @@ -0,0 +1,306 @@ +#include <errno.h> +#include <sys/stat.h> +#include <a.out.h> + +#include <linux/fs.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <asm/segment.h> + +extern int sys_exit(int exit_code); +extern int sys_close(int fd); + +/* + * MAX_ARG_PAGES defines the number of pages allocated for arguments + * and envelope for the new program. 32 should suffice, this gives + * a maximum env+arg of 128kB ! + */ +#define MAX_ARG_PAGES 32 + +#define cp_block(from,to) \ +__asm__("pushl $0x10\n\t" \ + "pushl $0x17\n\t" \ + "pop %%es\n\t" \ + "cld\n\t" \ + "rep\n\t" \ + "movsl\n\t" \ + "pop %%es" \ + ::"c" (BLOCK_SIZE/4),"S" (from),"D" (to) \ + :"cx","di","si") + +/* + * read_head() reads blocks 1-6 (not 0). Block 0 has already been + * read for header information. + */ +int read_head(struct m_inode * inode,int blocks) +{ + struct buffer_head * bh; + int count; + + if (blocks>6) + blocks=6; + for(count = 0 ; count<blocks ; count++) { + if (!inode->i_zone[count+1]) + continue; + if (!(bh=bread(inode->i_dev,inode->i_zone[count+1]))) + return -1; + cp_block(bh->b_data,count*BLOCK_SIZE); + brelse(bh); + } + return 0; +} + +int read_ind(int dev,int ind,long size,unsigned long offset) +{ + struct buffer_head * ih, * bh; + unsigned short * table,block; + + if (size<=0) + panic("size<=0 in read_ind"); + if (size>512*BLOCK_SIZE) + size=512*BLOCK_SIZE; + if (!ind) + return 0; + if (!(ih=bread(dev,ind))) + return -1; + table = (unsigned short *) ih->b_data; + while (size>0) { + if (block=*(table++)) + if (!(bh=bread(dev,block))) { + brelse(ih); + return -1; + } else { + cp_block(bh->b_data,offset); + brelse(bh); + } + size -= BLOCK_SIZE; + offset += BLOCK_SIZE; + } + brelse(ih); + return 0; +} + +/* + * read_area() reads an area into %fs:mem. + */ +int read_area(struct m_inode * inode,long size) +{ + struct buffer_head * dind; + unsigned short * table; + int i,count; + + if ((i=read_head(inode,(size+BLOCK_SIZE-1)/BLOCK_SIZE)) || + (size -= BLOCK_SIZE*6)<=0) + return i; + if ((i=read_ind(inode->i_dev,inode->i_zone[7],size,BLOCK_SIZE*6)) || + (size -= BLOCK_SIZE*512)<=0) + return i; + if (!(i=inode->i_zone[8])) + return 0; + if (!(dind = bread(inode->i_dev,i))) + return -1; + table = (unsigned short *) dind->b_data; + for(count=0 ; count<512 ; count++) + if ((i=read_ind(inode->i_dev,*(table++),size, + BLOCK_SIZE*(518+count))) || (size -= BLOCK_SIZE*512)<=0) + return i; + panic("Impossibly long executable"); +} + +/* + * create_tables() parses the env- and arg-strings in new user + * memory and creates the pointer tables from them, and puts their + * addresses on the "stack", returning the new stack pointer value. + */ +static unsigned long * create_tables(char * p,int argc,int envc) +{ + unsigned long *argv,*envp; + unsigned long * sp; + + sp = (unsigned long *) (0xfffffffc & (unsigned long) p); + sp -= envc+1; + envp = sp; + sp -= argc+1; + argv = sp; + put_fs_long((unsigned long)envp,--sp); + put_fs_long((unsigned long)argv,--sp); + put_fs_long((unsigned long)argc,--sp); + while (argc-->0) { + put_fs_long((unsigned long) p,argv++); + while (get_fs_byte(p++)) /* nothing */ ; + } + put_fs_long(0,argv); + while (envc-->0) { + put_fs_long((unsigned long) p,envp++); + while (get_fs_byte(p++)) /* nothing */ ; + } + put_fs_long(0,envp); + return sp; +} + +/* + * count() counts the number of arguments/envelopes + */ +static int count(char ** argv) +{ + int i=0; + char ** tmp; + + if (tmp = argv) + while (get_fs_long((unsigned long *) (tmp++))) + i++; + + return i; +} + +/* + * 'copy_string()' copies argument/envelope strings from user + * memory to free pages in kernel mem. These are in a format ready + * to be put directly into the top of new user memory. + */ +static unsigned long copy_strings(int argc,char ** argv,unsigned long *page, + unsigned long p) +{ + int len,i; + char *tmp; + + while (argc-- > 0) { + if (!(tmp = (char *)get_fs_long(((unsigned long *) argv)+argc))) + panic("argc is wrong"); + len=0; /* remember zero-padding */ + do { + len++; + } while (get_fs_byte(tmp++)); + if (p-len < 0) /* this shouldn't happen - 128kB */ + return 0; + i = ((unsigned) (p-len)) >> 12; + while (i<MAX_ARG_PAGES && !page[i]) { + if (!(page[i]=get_free_page())) + return 0; + i++; + } + do { + --p; + if (!page[p/PAGE_SIZE]) + panic("nonexistent page in exec.c"); + ((char *) page[p/PAGE_SIZE])[p%PAGE_SIZE] = + get_fs_byte(--tmp); + } while (--len); + } + return p; +} + +static unsigned long change_ldt(unsigned long text_size,unsigned long * page) +{ + unsigned long code_limit,data_limit,code_base,data_base; + int i; + + code_limit = text_size+PAGE_SIZE -1; + code_limit &= 0xFFFFF000; + data_limit = 0x4000000; + code_base = get_base(current->ldt[1]); + data_base = code_base; + set_base(current->ldt[1],code_base); + set_limit(current->ldt[1],code_limit); + set_base(current->ldt[2],data_base); + set_limit(current->ldt[2],data_limit); +/* make sure fs points to the NEW data segment */ + __asm__("pushl $0x17\n\tpop %%fs"::); + data_base += data_limit; + for (i=MAX_ARG_PAGES-1 ; i>=0 ; i--) { + data_base -= PAGE_SIZE; + if (page[i]) + put_page(page[i],data_base); + } + return data_limit; +} + +/* + * 'do_execve()' executes a new program. + */ +int do_execve(unsigned long * eip,long tmp,char * filename, + char ** argv, char ** envp) +{ + struct m_inode * inode; + struct buffer_head * bh; + struct exec ex; + unsigned long page[MAX_ARG_PAGES]; + int i,argc,envc; + unsigned long p; + + if ((0xffff & eip[1]) != 0x000f) + panic("execve called from supervisor mode"); + for (i=0 ; i<MAX_ARG_PAGES ; i++) /* clear page-table */ + page[i]=0; + if (!(inode=namei(filename))) /* get executables inode */ + return -ENOENT; + if (!S_ISREG(inode->i_mode)) { /* must be regular file */ + iput(inode); + return -EACCES; + } + i = inode->i_mode; + if (current->uid && current->euid) { + if (current->euid == inode->i_uid) + i >>= 6; + else if (current->egid == inode->i_gid) + i >>= 3; + } else if (i & 0111) + i=1; + if (!(i & 1)) { + iput(inode); + return -ENOEXEC; + } + if (!(bh = bread(inode->i_dev,inode->i_zone[0]))) { + iput(inode); + return -EACCES; + } + ex = *((struct exec *) bh->b_data); /* read exec-header */ + brelse(bh); + if (N_MAGIC(ex) != ZMAGIC || ex.a_trsize || ex.a_drsize || + ex.a_text+ex.a_data+ex.a_bss>0x3000000 || + inode->i_size < ex.a_text+ex.a_data+ex.a_syms+N_TXTOFF(ex)) { + iput(inode); + return -ENOEXEC; + } + if (N_TXTOFF(ex) != BLOCK_SIZE) + panic("N_TXTOFF != BLOCK_SIZE. See a.out.h."); + argc = count(argv); + envc = count(envp); + p = copy_strings(envc,envp,page,PAGE_SIZE*MAX_ARG_PAGES-4); + p = copy_strings(argc,argv,page,p); + if (!p) { + for (i=0 ; i<MAX_ARG_PAGES ; i++) + free_page(page[i]); + iput(inode); + return -1; + } +/* OK, This is the point of no return */ + for (i=0 ; i<32 ; i++) + current->sig_fn[i] = NULL; + for (i=0 ; i<NR_OPEN ; i++) + if ((current->close_on_exec>>i)&1) + sys_close(i); + current->close_on_exec = 0; + free_page_tables(get_base(current->ldt[1]),get_limit(0x0f)); + free_page_tables(get_base(current->ldt[2]),get_limit(0x17)); + if (last_task_used_math == current) + last_task_used_math = NULL; + current->used_math = 0; + p += change_ldt(ex.a_text,page)-MAX_ARG_PAGES*PAGE_SIZE; + p = (unsigned long) create_tables((char *)p,argc,envc); + current->brk = ex.a_bss + + (current->end_data = ex.a_data + + (current->end_code = ex.a_text)); + current->start_stack = p & 0xfffff000; + i = read_area(inode,ex.a_text+ex.a_data); + iput(inode); + if (i<0) + sys_exit(-1); + i = ex.a_text+ex.a_data; + while (i&0xfff) + put_fs_byte(0,(char *) (i++)); + eip[0] = ex.a_entry; /* eip, magic happens :-) */ + eip[3] = p; /* stack pointer */ + return 0; +} diff --git a/linux/fs/fcntl.c b/linux/fs/fcntl.c new file mode 100644 index 0000000..169f376 --- /dev/null +++ b/linux/fs/fcntl.c @@ -0,0 +1,69 @@ +#include <string.h> +#include <errno.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <asm/segment.h> + +#include <fcntl.h> +#include <sys/stat.h> + +extern int sys_close(int fd); + +static int dupfd(unsigned int fd, unsigned int arg) +{ + if (fd >= NR_OPEN || !current->filp[fd]) + return -EBADF; + if (arg >= NR_OPEN) + return -EINVAL; + while (arg < NR_OPEN) + if (current->filp[arg]) + arg++; + else + break; + if (arg >= NR_OPEN) + return -EMFILE; + current->close_on_exec &= ~(1<<arg); + (current->filp[arg] = current->filp[fd])->f_count++; + return arg; +} + +int sys_dup2(unsigned int oldfd, unsigned int newfd) +{ + sys_close(newfd); + return dupfd(oldfd,newfd); +} + +int sys_dup(unsigned int fildes) +{ + return dupfd(fildes,0); +} + +int sys_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + struct file * filp; + + if (fd >= NR_OPEN || !(filp = current->filp[fd])) + return -EBADF; + switch (cmd) { + case F_DUPFD: + return dupfd(fd,arg); + case F_GETFD: + return (current->close_on_exec>>fd)&1; + case F_SETFD: + if (arg&1) + current->close_on_exec |= (1<<fd); + else + current->close_on_exec &= ~(1<<fd); + return 0; + case F_GETFL: + return filp->f_flags; + case F_SETFL: + filp->f_flags &= ~(O_APPEND | O_NONBLOCK); + filp->f_flags |= arg & (O_APPEND | O_NONBLOCK); + return 0; + case F_GETLK: case F_SETLK: case F_SETLKW: + return -1; + default: + return -1; + } +} diff --git a/linux/fs/file_dev.c b/linux/fs/file_dev.c new file mode 100644 index 0000000..da5d014 --- /dev/null +++ b/linux/fs/file_dev.c @@ -0,0 +1,84 @@ +#include <errno.h> +#include <fcntl.h> + +#include <linux/sched.h> +#include <linux/kernel.h> +#include <asm/segment.h> + +#define MIN(a,b) (((a)<(b))?(a):(b)) +#define MAX(a,b) (((a)>(b))?(a):(b)) + +int file_read(struct m_inode * inode, struct file * filp, char * buf, int count) +{ + int left,chars,nr; + struct buffer_head * bh; + + if ((left=count)<=0) + return 0; + while (left) { + if (nr = bmap(inode,(filp->f_pos)/BLOCK_SIZE)) { + if (!(bh=bread(inode->i_dev,nr))) + break; + } else + bh = NULL; + nr = filp->f_pos % BLOCK_SIZE; + chars = MIN( BLOCK_SIZE-nr , left ); + filp->f_pos += chars; + left -= chars; + if (bh) { + char * p = nr + bh->b_data; + while (chars-->0) + put_fs_byte(*(p++),buf++); + brelse(bh); + } else { + while (chars-->0) + put_fs_byte(0,buf++); + } + } + inode->i_atime = CURRENT_TIME; + return (count-left)?(count-left):-ERROR; +} + +int file_write(struct m_inode * inode, struct file * filp, char * buf, int count) +{ + off_t pos; + int block,c; + struct buffer_head * bh; + char * p; + int i=0; + +/* + * ok, append may not work when many processes are writing at the same time + * but so what. That way leads to madness anyway. + */ + if (filp->f_flags & O_APPEND) + pos = inode->i_size; + else + pos = filp->f_pos; + while (i<count) { + if (!(block = create_block(inode,pos/BLOCK_SIZE))) + break; + if (!(bh=bread(inode->i_dev,block))) + break; + c = pos % BLOCK_SIZE; + p = c + bh->b_data; + bh->b_dirt = 1; + c = BLOCK_SIZE-c; + if (c > count-i) c = count-i; + pos += c; + if (pos > inode->i_size) { + inode->i_size = pos; + inode->i_dirt = 1; + } + i += c; + while (c-->0) + *(p++) = get_fs_byte(buf++); + brelse(bh); + } + inode->i_mtime = CURRENT_TIME; + if (!(filp->f_flags & O_APPEND)) { + filp->f_pos = pos; + inode->i_ctime = CURRENT_TIME; + } + return (i?i:-1); +} diff --git a/linux/fs/file_table.c b/linux/fs/file_table.c new file mode 100644 index 0000000..ab7cebf --- /dev/null +++ b/linux/fs/file_table.c @@ -0,0 +1,3 @@ +#include <linux/fs.h> + +struct file file_table[NR_FILE]; diff --git a/linux/fs/inode.c b/linux/fs/inode.c new file mode 100644 index 0000000..d06ff90 --- /dev/null +++ b/linux/fs/inode.c @@ -0,0 +1,288 @@ +#include <string.h> + +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <asm/system.h> + +struct m_inode inode_table[NR_INODE]={{0,},}; + +static void read_inode(struct m_inode * inode); +static void write_inode(struct m_inode * inode); + +static inline void wait_on_inode(struct m_inode * inode) +{ + cli(); + while (inode->i_lock) + sleep_on(&inode->i_wait); + sti(); +} + +static inline void lock_inode(struct m_inode * inode) +{ + cli(); + while (inode->i_lock) + sleep_on(&inode->i_wait); + inode->i_lock=1; + sti(); +} + +static inline void unlock_inode(struct m_inode * inode) +{ + inode->i_lock=0; + wake_up(&inode->i_wait); +} + +void sync_inodes(void) +{ + int i; + struct m_inode * inode; + + inode = 0+inode_table; + for(i=0 ; i<NR_INODE ; i++,inode++) { + wait_on_inode(inode); + if (inode->i_dirt && !inode->i_pipe) + write_inode(inode); + } +} + +static int _bmap(struct m_inode * inode,int block,int create) +{ + struct buffer_head * bh; + int i; + + if (block<0) + panic("_bmap: block<0"); + if (block >= 7+512+512*512) + panic("_bmap: block>big"); + if (block<7) { + if (create && !inode->i_zone[block]) + if (inode->i_zone[block]=new_block(inode->i_dev)) { + inode->i_ctime=CURRENT_TIME; + inode->i_dirt=1; + } + return inode->i_zone[block]; + } + block -= 7; + if (block<512) { + if (create && !inode->i_zone[7]) + if (inode->i_zone[7]=new_block(inode->i_dev)) { + inode->i_dirt=1; + inode->i_ctime=CURRENT_TIME; + } + if (!inode->i_zone[7]) + return 0; + if (!(bh = bread(inode->i_dev,inode->i_zone[7]))) + return 0; + i = ((unsigned short *) (bh->b_data))[block]; + if (create && !i) + if (i=new_block(inode->i_dev)) { + ((unsigned short *) (bh->b_data))[block]=i; + bh->b_dirt=1; + } + brelse(bh); + return i; + } + block -= 512; + if (create && !inode->i_zone[8]) + if (inode->i_zone[8]=new_block(inode->i_dev)) { + inode->i_dirt=1; + inode->i_ctime=CURRENT_TIME; + } + if (!inode->i_zone[8]) + return 0; + if (!(bh=bread(inode->i_dev,inode->i_zone[8]))) + return 0; + i = ((unsigned short *)bh->b_data)[block>>9]; + if (create && !i) + if (i=new_block(inode->i_dev)) { + ((unsigned short *) (bh->b_data))[block>>9]=i; + bh->b_dirt=1; + } + brelse(bh); + if (!i) + return 0; + if (!(bh=bread(inode->i_dev,i))) + return 0; + i = ((unsigned short *)bh->b_data)[block&511]; + if (create && !i) + if (i=new_block(inode->i_dev)) { + ((unsigned short *) (bh->b_data))[block&511]=i; + bh->b_dirt=1; + } + brelse(bh); + return i; +} + +int bmap(struct m_inode * inode,int block) +{ + return _bmap(inode,block,0); +} + +int create_block(struct m_inode * inode, int block) +{ + return _bmap(inode,block,1); +} + +void iput(struct m_inode * inode) +{ + if (!inode) + return; + wait_on_inode(inode); + if (!inode->i_count) + panic("iput: trying to free free inode"); + if (inode->i_pipe) { + wake_up(&inode->i_wait); + if (--inode->i_count) + return; + free_page(inode->i_size); + inode->i_count=0; + inode->i_dirt=0; + inode->i_pipe=0; + return; + } + if (!inode->i_dev || inode->i_count>1) { + inode->i_count--; + return; + } +repeat: + if (!inode->i_nlinks) { + truncate(inode); + free_inode(inode); + return; + } + if (inode->i_dirt) { + write_inode(inode); /* we can sleep - so do again */ + wait_on_inode(inode); + goto repeat; + } + inode->i_count--; + return; +} + +static volatile int last_allocated_inode = 0; + +struct m_inode * get_empty_inode(void) +{ + struct m_inode * inode; + int inr; + + while (1) { + inode = NULL; + inr = last_allocated_inode; + do { + if (!inode_table[inr].i_count) { + inode = inr + inode_table; + break; + } + inr++; + if (inr>=NR_INODE) + inr=0; + } while (inr != last_allocated_inode); + if (!inode) { + for (inr=0 ; inr<NR_INODE ; inr++) + printk("%04x: %6d\t",inode_table[inr].i_dev, + inode_table[inr].i_num); + panic("No free inodes in mem"); + } + last_allocated_inode = inr; + wait_on_inode(inode); + while (inode->i_dirt) { + write_inode(inode); + wait_on_inode(inode); + } + if (!inode->i_count) + break; + } + memset(inode,0,sizeof(*inode)); + inode->i_count = 1; + return inode; +} + +struct m_inode * get_pipe_inode(void) +{ + struct m_inode * inode; + + if (!(inode = get_empty_inode())) + return NULL; + if (!(inode->i_size=get_free_page())) { + inode->i_count = 0; + return NULL; + } + inode->i_count = 2; /* sum of readers/writers */ + PIPE_HEAD(*inode) = PIPE_TAIL(*inode) = 0; + inode->i_pipe = 1; + return inode; +} + +struct m_inode * iget(int dev,int nr) +{ + struct m_inode * inode, * empty; + + if (!dev) + panic("iget with dev==0"); + empty = get_empty_inode(); + inode = inode_table; + while (inode < NR_INODE+inode_table) { + if (inode->i_dev != dev || inode->i_num != nr) { + inode++; + continue; + } + wait_on_inode(inode); + if (inode->i_dev != dev || inode->i_num != nr) { + inode = inode_table; + continue; + } + inode->i_count++; + if (empty) + iput(empty); + return inode; + } + if (!empty) + return (NULL); + inode=empty; + inode->i_dev = dev; + inode->i_num = nr; + read_inode(inode); + return inode; +} + +static void read_inode(struct m_inode * inode) +{ + struct super_block * sb; + struct buffer_head * bh; + int block; + + lock_inode(inode); + sb=get_super(inode->i_dev); + block = 2 + sb->s_imap_blocks + sb->s_zmap_blocks + + (inode->i_num-1)/INODES_PER_BLOCK; + if (!(bh=bread(inode->i_dev,block))) + panic("unable to read i-node block"); + *(struct d_inode *)inode = + ((struct d_inode *)bh->b_data) + [(inode->i_num-1)%INODES_PER_BLOCK]; + brelse(bh); + unlock_inode(inode); +} + +static void write_inode(struct m_inode * inode) +{ + struct super_block * sb; + struct buffer_head * bh; + int block; + + lock_inode(inode); + sb=get_super(inode->i_dev); + block = 2 + sb->s_imap_blocks + sb->s_zmap_blocks + + (inode->i_num-1)/INODES_PER_BLOCK; + if (!(bh=bread(inode->i_dev,block))) + panic("unable to read i-node block"); + ((struct d_inode *)bh->b_data) + [(inode->i_num-1)%INODES_PER_BLOCK] = + *(struct d_inode *)inode; + bh->b_dirt=1; + inode->i_dirt=0; + brelse(bh); + unlock_inode(inode); +} diff --git a/linux/fs/ioctl.c b/linux/fs/ioctl.c new file mode 100644 index 0000000..131561b --- /dev/null +++ b/linux/fs/ioctl.c @@ -0,0 +1,40 @@ +#include <string.h> +#include <errno.h> +#include <sys/stat.h> + +#include <linux/sched.h> + +extern int tty_ioctl(int dev, int cmd, int arg); + +typedef int (*ioctl_ptr)(int dev,int cmd,int arg); + +#define NRDEVS ((sizeof (ioctl_table))/(sizeof (ioctl_ptr))) + +static ioctl_ptr ioctl_table[]={ + NULL, /* nodev */ + NULL, /* /dev/mem */ + NULL, /* /dev/fd */ + NULL, /* /dev/hd */ + tty_ioctl, /* /dev/ttyx */ + tty_ioctl, /* /dev/tty */ + NULL, /* /dev/lp */ + NULL}; /* named pipes */ + + +int sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + struct file * filp; + int dev,mode; + + if (fd >= NR_OPEN || !(filp = current->filp[fd])) + return -EBADF; + mode=filp->f_inode->i_mode; + if (!S_ISCHR(mode) && !S_ISBLK(mode)) + return -EINVAL; + dev = filp->f_inode->i_zone[0]; + if (MAJOR(dev) >= NRDEVS) + panic("unknown device for ioctl"); + if (!ioctl_table[MAJOR(dev)]) + return -ENOTTY; + return ioctl_table[MAJOR(dev)](dev,cmd,arg); +} diff --git a/linux/fs/namei.c b/linux/fs/namei.c new file mode 100644 index 0000000..600737b --- /dev/null +++ b/linux/fs/namei.c @@ -0,0 +1,678 @@ +#include <linux/sched.h> +#include <linux/kernel.h> +#include <asm/segment.h> + +#include <string.h> +#include <fcntl.h> +#include <errno.h> +#include <const.h> +#include <sys/stat.h> + +#define ACC_MODE(x) ("\004\002\006\377"[(x)&O_ACCMODE]) + +/* + * comment out this line if you want names > NAME_LEN chars to be + * truncated. Else they will be disallowed. + */ +/* #define NO_TRUNCATE */ + +#define MAY_EXEC 1 +#define MAY_WRITE 2 +#define MAY_READ 4 + +/* + * permission() + * + * is used to check for read/write/execute permissions on a file. + * I don't know if we should look at just the euid or both euid and + * uid, but that should be easily changed. + */ +static int permission(struct m_inode * inode,int mask) +{ + int mode = inode->i_mode; + +/* special case: not even root can read/write a deleted file */ + if (inode->i_dev && !inode->i_nlinks) + return 0; + if (!(current->uid && current->euid)) + mode=0777; + else if (current->uid==inode->i_uid || current->euid==inode->i_uid) + mode >>= 6; + else if (current->gid==inode->i_gid || current->egid==inode->i_gid) + mode >>= 3; + return mode & mask & 0007; +} + +/* + * ok, we cannot use strncmp, as the name is not in our data space. + * Thus we'll have to use match. No big problem. Match also makes + * some sanity tests. + * + * NOTE! unlike strncmp, match returns 1 for success, 0 for failure. + */ +static int match(int len,const char * name,struct dir_entry * de) +{ + register int same __asm__("ax"); + + if (!de || !de->inode || len > NAME_LEN) + return 0; + if (len < NAME_LEN && de->name[len]) + return 0; + __asm__("cld\n\t" + "fs ; repe ; cmpsb\n\t" + "setz %%al" + :"=a" (same) + :"0" (0),"S" ((long) name),"D" ((long) de->name),"c" (len) + :"cx","di","si"); + return same; +} + +/* + * find_entry() + * + * finds and entry in the specified directory with the wanted name. It + * returns the cache buffer in which the entry was found, and the entry + * itself (as a parameter - res_dir). It does NOT read the inode of the + * entry - you'll have to do that yourself if you want to. + */ +static struct buffer_head * find_entry(struct m_inode * dir, + const char * name, int namelen, struct dir_entry ** res_dir) +{ + int entries; + int block,i; + struct buffer_head * bh; + struct dir_entry * de; + +#ifdef NO_TRUNCATE + if (namelen > NAME_LEN) + return NULL; +#else + if (namelen > NAME_LEN) + namelen = NAME_LEN; +#endif + entries = dir->i_size / (sizeof (struct dir_entry)); + *res_dir = NULL; + if (!namelen) + return NULL; + if (!(block = dir->i_zone[0])) + return NULL; + if (!(bh = bread(dir->i_dev,block))) + return NULL; + i = 0; + de = (struct dir_entry *) bh->b_data; + while (i < entries) { + if ((char *)de >= BLOCK_SIZE+bh->b_data) { + brelse(bh); + bh = NULL; + if (!(block = bmap(dir,i/DIR_ENTRIES_PER_BLOCK)) || + !(bh = bread(dir->i_dev,block))) { + i += DIR_ENTRIES_PER_BLOCK; + continue; + } + de = (struct dir_entry *) bh->b_data; + } + if (match(namelen,name,de)) { + *res_dir = de; + return bh; + } + de++; + i++; + } + brelse(bh); + return NULL; +} + +/* + * add_entry() + * + * adds a file entry to the specified directory, using the same + * semantics as find_entry(). It returns NULL if it failed. + * + * NOTE!! The inode part of 'de' is left at 0 - which means you + * may not sleep between calling this and putting something into + * the entry, as someone else might have used it while you slept. + */ +static struct buffer_head * add_entry(struct m_inode * dir, + const char * name, int namelen, struct dir_entry ** res_dir) +{ + int block,i; + struct buffer_head * bh; + struct dir_entry * de; + + *res_dir = NULL; +#ifdef NO_TRUNCATE + if (namelen > NAME_LEN) + return NULL; +#else + if (namelen > NAME_LEN) + namelen = NAME_LEN; +#endif + if (!namelen) + return NULL; + if (!(block = dir->i_zone[0])) + return NULL; + if (!(bh = bread(dir->i_dev,block))) + return NULL; + i = 0; + de = (struct dir_entry *) bh->b_data; + while (1) { + if ((char *)de >= BLOCK_SIZE+bh->b_data) { + brelse(bh); + bh = NULL; + block = create_block(dir,i/DIR_ENTRIES_PER_BLOCK); + if (!block) + return NULL; + if (!(bh = bread(dir->i_dev,block))) { + i += DIR_ENTRIES_PER_BLOCK; + continue; + } + de = (struct dir_entry *) bh->b_data; + } + if (i*sizeof(struct dir_entry) >= dir->i_size) { + de->inode=0; + dir->i_size = (i+1)*sizeof(struct dir_entry); + dir->i_dirt = 1; + dir->i_ctime = CURRENT_TIME; + } + if (!de->inode) { + dir->i_mtime = CURRENT_TIME; + for (i=0; i < NAME_LEN ; i++) + de->name[i]=(i<namelen)?get_fs_byte(name+i):0; + bh->b_dirt = 1; + *res_dir = de; + return bh; + } + de++; + i++; + } + brelse(bh); + return NULL; +} + +/* + * get_dir() + * + * Getdir traverses the pathname until it hits the topmost directory. + * It returns NULL on failure. + */ +static struct m_inode * get_dir(const char * pathname) +{ + char c; + const char * thisname; + struct m_inode * inode; + struct buffer_head * bh; + int namelen,inr,idev; + struct dir_entry * de; + + if (!current->root || !current->root->i_count) + panic("No root inode"); + if (!current->pwd || !current->pwd->i_count) + panic("No cwd inode"); + if ((c=get_fs_byte(pathname))=='/') { + inode = current->root; + pathname++; + } else if (c) + inode = current->pwd; + else + return NULL; /* empty name is bad */ + inode->i_count++; + while (1) { + thisname = pathname; + if (!S_ISDIR(inode->i_mode) || !permission(inode,MAY_EXEC)) { + iput(inode); + return NULL; + } + for(namelen=0;(c=get_fs_byte(pathname++))&&(c!='/');namelen++) + /* nothing */ ; + if (!c) + return inode; + if (!(bh = find_entry(inode,thisname,namelen,&de))) { + iput(inode); + return NULL; + } + inr = de->inode; + idev = inode->i_dev; + brelse(bh); + iput(inode); + if (!(inode = iget(idev,inr))) + return NULL; + } +} + +/* + * dir_namei() + * + * dir_namei() returns the inode of the directory of the + * specified name, and the name within that directory. + */ +static struct m_inode * dir_namei(const char * pathname, + int * namelen, const char ** name) +{ + char c; + const char * basename; + struct m_inode * dir; + + if (!(dir = get_dir(pathname))) + return NULL; + basename = pathname; + while (c=get_fs_byte(pathname++)) + if (c=='/') + basename=pathname; + *namelen = pathname-basename-1; + *name = basename; + return dir; +} + +/* + * namei() + * + * is used by most simple commands to get the inode of a specified name. + * Open, link etc use their own routines, but this is enough for things + * like 'chmod' etc. + */ +struct m_inode * namei(const char * pathname) +{ + const char * basename; + int inr,dev,namelen; + struct m_inode * dir; + struct buffer_head * bh; + struct dir_entry * de; + + if (!(dir = dir_namei(pathname,&namelen,&basename))) + return NULL; + if (!namelen) /* special case: '/usr/' etc */ + return dir; + bh = find_entry(dir,basename,namelen,&de); + if (!bh) { + iput(dir); + return NULL; + } + inr = de->inode; + dev = dir->i_dev; + brelse(bh); + iput(dir); + dir=iget(dev,inr); + if (dir) { + dir->i_atime=CURRENT_TIME; + dir->i_dirt=1; + } + return dir; +} + +/* + * open_namei() + * + * namei for open - this is in fact almost the whole open-routine. + */ +int open_namei(const char * pathname, int flag, int mode, + struct m_inode ** res_inode) +{ + const char * basename; + int inr,dev,namelen; + struct m_inode * dir, *inode; + struct buffer_head * bh; + struct dir_entry * de; + + if ((flag & O_TRUNC) && !(flag & O_ACCMODE)) + flag |= O_WRONLY; + mode &= 0777 & ~current->umask; + mode |= I_REGULAR; + if (!(dir = dir_namei(pathname,&namelen,&basename))) + return -ENOENT; + if (!namelen) { /* special case: '/usr/' etc */ + if (!(flag & (O_ACCMODE|O_CREAT|O_TRUNC))) { + *res_inode=dir; + return 0; + } + iput(dir); + return -EISDIR; + } + bh = find_entry(dir,basename,namelen,&de); + if (!bh) { + if (!(flag & O_CREAT)) { + iput(dir); + return -ENOENT; + } + if (!permission(dir,MAY_WRITE)) { + iput(dir); + return -EACCES; + } + inode = new_inode(dir->i_dev); + if (!inode) { + iput(dir); + return -ENOSPC; + } + inode->i_mode = mode; + inode->i_dirt = 1; + bh = add_entry(dir,basename,namelen,&de); + if (!bh) { + inode->i_nlinks--; + iput(inode); + iput(dir); + return -ENOSPC; + } + de->inode = inode->i_num; + bh->b_dirt = 1; + brelse(bh); + iput(dir); + *res_inode = inode; + return 0; + } + inr = de->inode; + dev = dir->i_dev; + brelse(bh); + iput(dir); + if (flag & O_EXCL) + return -EEXIST; + if (!(inode=iget(dev,inr))) + return -EACCES; + if ((S_ISDIR(inode->i_mode) && (flag & O_ACCMODE)) || + permission(inode,ACC_MODE(flag))!=ACC_MODE(flag)) { + iput(inode); + return -EPERM; + } + inode->i_atime = CURRENT_TIME; + if (flag & O_TRUNC) + truncate(inode); + *res_inode = inode; + return 0; +} + +int sys_mkdir(const char * pathname, int mode) +{ + const char * basename; + int namelen; + struct m_inode * dir, * inode; + struct buffer_head * bh, *dir_block; + struct dir_entry * de; + + if (current->euid && current->uid) + return -EPERM; + if (!(dir = dir_namei(pathname,&namelen,&basename))) + return -ENOENT; + if (!namelen) { + iput(dir); + return -ENOENT; + } + if (!permission(dir,MAY_WRITE)) { + iput(dir); + return -EPERM; + } + bh = find_entry(dir,basename,namelen,&de); + if (bh) { + brelse(bh); + iput(dir); + return -EEXIST; + } + inode = new_inode(dir->i_dev); + if (!inode) { + iput(dir); + return -ENOSPC; + } + inode->i_size = 32; + inode->i_dirt = 1; + inode->i_mtime = inode->i_atime = CURRENT_TIME; + if (!(inode->i_zone[0]=new_block(inode->i_dev))) { + iput(dir); + inode->i_nlinks--; + iput(inode); + return -ENOSPC; + } + inode->i_dirt = 1; + if (!(dir_block=bread(inode->i_dev,inode->i_zone[0]))) { + iput(dir); + free_block(inode->i_dev,inode->i_zone[0]); + inode->i_nlinks--; + iput(inode); + return -ERROR; + } + de = (struct dir_entry *) dir_block->b_data; + de->inode=inode->i_num; + strcpy(de->name,"."); + de++; + de->inode = dir->i_num; + strcpy(de->name,".."); + inode->i_nlinks = 2; + dir_block->b_dirt = 1; + brelse(dir_block); + inode->i_mode = I_DIRECTORY | (mode & 0777 & ~current->umask); + inode->i_dirt = 1; + bh = add_entry(dir,basename,namelen,&de); + if (!bh) { + iput(dir); + free_block(inode->i_dev,inode->i_zone[0]); + inode->i_nlinks=0; + iput(inode); + return -ENOSPC; + } + de->inode = inode->i_num; + bh->b_dirt = 1; + dir->i_nlinks++; + dir->i_dirt = 1; + iput(dir); + iput(inode); + brelse(bh); + return 0; +} + +/* + * routine to check that the specified directory is empty (for rmdir) + */ +static int empty_dir(struct m_inode * inode) +{ + int nr,block; + int len; + struct buffer_head * bh; + struct dir_entry * de; + + len = inode->i_size / sizeof (struct dir_entry); + if (len<2 || !inode->i_zone[0] || + !(bh=bread(inode->i_dev,inode->i_zone[0]))) { + printk("warning - bad directory on dev %04x\n",inode->i_dev); + return 0; + } + de = (struct dir_entry *) bh->b_data; + if (de[0].inode != inode->i_num || !de[1].inode || + strcmp(".",de[0].name) || strcmp("..",de[1].name)) { + printk("warning - bad directory on dev %04x\n",inode->i_dev); + return 0; + } + nr = 2; + de += 2; + while (nr<len) { + if ((void *) de >= (void *) (bh->b_data+BLOCK_SIZE)) { + brelse(bh); + block=bmap(inode,nr/DIR_ENTRIES_PER_BLOCK); + if (!block) { + nr += DIR_ENTRIES_PER_BLOCK; + continue; + } + if (!(bh=bread(inode->i_dev,block))) + return 0; + de = (struct dir_entry *) bh->b_data; + } + if (de->inode) { + brelse(bh); + return 0; + } + de++; + nr++; + } + brelse(bh); + return 1; +} + +int sys_rmdir(const char * name) +{ + const char * basename; + int namelen; + struct m_inode * dir, * inode; + struct buffer_head * bh; + struct dir_entry * de; + + if (current->euid && current->uid) + return -EPERM; + if (!(dir = dir_namei(name,&namelen,&basename))) + return -ENOENT; + if (!namelen) { + iput(dir); + return -ENOENT; + } + bh = find_entry(dir,basename,namelen,&de); + if (!bh) { + iput(dir); + return -ENOENT; + } + if (!permission(dir,MAY_WRITE)) { + iput(dir); + brelse(bh); + return -EPERM; + } + if (!(inode = iget(dir->i_dev, de->inode))) { + iput(dir); + brelse(bh); + return -EPERM; + } + if (inode == dir) { /* we may not delete ".", but "../dir" is ok */ + iput(inode); + iput(dir); + brelse(bh); + return -EPERM; + } + if (!S_ISDIR(inode->i_mode)) { + iput(inode); + iput(dir); + brelse(bh); + return -ENOTDIR; + } + if (!empty_dir(inode)) { + iput(inode); + iput(dir); + brelse(bh); + return -ENOTEMPTY; + } + if (inode->i_nlinks != 2) + printk("empty directory has nlink!=2 (%d)",inode->i_nlinks); + de->inode = 0; + bh->b_dirt = 1; + brelse(bh); + inode->i_nlinks=0; + inode->i_dirt=1; + dir->i_nlinks--; + dir->i_ctime = dir->i_mtime = CURRENT_TIME; + dir->i_dirt=1; + iput(dir); + iput(inode); + return 0; +} + +int sys_unlink(const char * name) +{ + const char * basename; + int namelen; + struct m_inode * dir, * inode; + struct buffer_head * bh; + struct dir_entry * de; + + if (!(dir = dir_namei(name,&namelen,&basename))) + return -ENOENT; + if (!namelen) { + iput(dir); + return -ENOENT; + } + if (!permission(dir,MAY_WRITE)) { + iput(dir); + return -EPERM; + } + bh = find_entry(dir,basename,namelen,&de); + if (!bh) { + iput(dir); + return -ENOENT; + } + inode = iget(dir->i_dev, de->inode); + if (!inode) { + printk("iget failed in delete (%04x:%d)",dir->i_dev,de->inode); + iput(dir); + brelse(bh); + return -ENOENT; + } + if (!S_ISREG(inode->i_mode)) { + iput(inode); + iput(dir); + brelse(bh); + return -EPERM; + } + if (!inode->i_nlinks) { + printk("Deleting nonexistent file (%04x:%d), %d\n", + inode->i_dev,inode->i_num,inode->i_nlinks); + inode->i_nlinks=1; + } + de->inode = 0; + bh->b_dirt = 1; + brelse(bh); + inode->i_nlinks--; + inode->i_dirt = 1; + inode->i_ctime = CURRENT_TIME; + iput(inode); + iput(dir); + return 0; +} + +int sys_link(const char * oldname, const char * newname) +{ + struct dir_entry * de; + struct m_inode * oldinode, * dir; + struct buffer_head * bh; + const char * basename; + int namelen; + + oldinode=namei(oldname); + if (!oldinode) + return -ENOENT; + if (!S_ISREG(oldinode->i_mode)) { + iput(oldinode); + return -EPERM; + } + dir = dir_namei(newname,&namelen,&basename); + if (!dir) { + iput(oldinode); + return -EACCES; + } + if (!namelen) { + iput(oldinode); + iput(dir); + return -EPERM; + } + if (dir->i_dev != oldinode->i_dev) { + iput(dir); + iput(oldinode); + return -EXDEV; + } + if (!permission(dir,MAY_WRITE)) { + iput(dir); + iput(oldinode); + return -EACCES; + } + bh = find_entry(dir,basename,namelen,&de); + if (bh) { + brelse(bh); + iput(dir); + iput(oldinode); + return -EEXIST; + } + bh = add_entry(dir,basename,namelen,&de); + if (!bh) { + iput(dir); + iput(oldinode); + return -ENOSPC; + } + de->inode = oldinode->i_num; + bh->b_dirt = 1; + brelse(bh); + iput(dir); + oldinode->i_nlinks++; + oldinode->i_ctime = CURRENT_TIME; + oldinode->i_dirt = 1; + iput(oldinode); + return 0; +} diff --git a/linux/fs/open.c b/linux/fs/open.c new file mode 100644 index 0000000..6918690 --- /dev/null +++ b/linux/fs/open.c @@ -0,0 +1,188 @@ +#include <string.h> +#include <errno.h> +#include <fcntl.h> +#include <sys/types.h> +#include <utime.h> +#include <sys/stat.h> + +#include <linux/sched.h> +#include <linux/tty.h> +#include <linux/kernel.h> +#include <asm/segment.h> + +int sys_utime(char * filename, struct utimbuf * times) +{ + struct m_inode * inode; + long actime,modtime; + + if (!(inode=namei(filename))) + return -ENOENT; + if (times) { + actime = get_fs_long((unsigned long *) ×->actime); + modtime = get_fs_long((unsigned long *) ×->modtime); + } else + actime = modtime = CURRENT_TIME; + inode->i_atime = actime; + inode->i_mtime = modtime; + inode->i_dirt = 1; + iput(inode); + return 0; +} + +int sys_access(const char * filename,int mode) +{ + struct m_inode * inode; + int res; + + mode &= 0007; + if (!(inode=namei(filename))) + return -EACCES; + res = inode->i_mode & 0777; + iput(inode); + if (!(current->euid && current->uid)) + if (res & 0111) + res = 0777; + else + res = 0666; + if (current->euid == inode->i_uid) + res >>= 6; + else if (current->egid == inode->i_gid) + res >>= 6; + if ((res & 0007 & mode) == mode) + return 0; + return -EACCES; +} + +int sys_chdir(const char * filename) +{ + struct m_inode * inode; + + if (!(inode = namei(filename))) + return -ENOENT; + if (!S_ISDIR(inode->i_mode)) { + iput(inode); + return -ENOTDIR; + } + iput(current->pwd); + current->pwd = inode; + return (0); +} + +int sys_chroot(const char * filename) +{ + struct m_inode * inode; + + if (!(inode=namei(filename))) + return -ENOENT; + if (!S_ISDIR(inode->i_mode)) { + iput(inode); + return -ENOTDIR; + } + iput(current->root); + current->root = inode; + return (0); +} + +int sys_chmod(const char * filename,int mode) +{ + struct m_inode * inode; + + if (!(inode=namei(filename))) + return -ENOENT; + if (current->uid && current->euid) + if (current->uid!=inode->i_uid && current->euid!=inode->i_uid) { + iput(inode); + return -EACCES; + } else + mode = (mode & 0777) | (inode->i_mode & 07000); + inode->i_mode = (mode & 07777) | (inode->i_mode & ~07777); + inode->i_dirt = 1; + iput(inode); + return 0; +} + +int sys_chown(const char * filename,int uid,int gid) +{ + struct m_inode * inode; + + if (!(inode=namei(filename))) + return -ENOENT; + if (current->uid && current->euid) { + iput(inode); + return -EACCES; + } + inode->i_uid=uid; + inode->i_gid=gid; + inode->i_dirt=1; + iput(inode); + return 0; +} + +int sys_open(const char * filename,int flag,int mode) +{ + struct m_inode * inode; + struct file * f; + int i,fd; + + mode &= 0777 & ~current->umask; + for(fd=0 ; fd<NR_OPEN ; fd++) + if (!current->filp[fd]) + break; + if (fd>=NR_OPEN) + return -EINVAL; + current->close_on_exec &= ~(1<<fd); + f=0+file_table; + for (i=0 ; i<NR_FILE ; i++,f++) + if (!f->f_count) break; + if (i>=NR_FILE) + return -EINVAL; + (current->filp[fd]=f)->f_count++; + if ((i=open_namei(filename,flag,mode,&inode))<0) { + current->filp[fd]=NULL; + f->f_count=0; + return i; + } +/* ttys are somewhat special (ttyxx major==4, tty major==5) */ + if (S_ISCHR(inode->i_mode)) + if (MAJOR(inode->i_zone[0])==4) { + if (current->leader && current->tty<0) { + current->tty = MINOR(inode->i_zone[0]); + tty_table[current->tty].pgrp = current->pgrp; + } + } else if (MAJOR(inode->i_zone[0])==5) + if (current->tty<0) { + iput(inode); + current->filp[fd]=NULL; + f->f_count=0; + return -EPERM; + } + f->f_mode = inode->i_mode; + f->f_flags = flag; + f->f_count = 1; + f->f_inode = inode; + f->f_pos = 0; + return (fd); +} + +int sys_creat(const char * pathname, int mode) +{ + return sys_open(pathname, O_CREAT | O_TRUNC, mode); +} + +int sys_close(unsigned int fd) +{ + struct file * filp; + + if (fd >= NR_OPEN) + return -EINVAL; + current->close_on_exec &= ~(1<<fd); + if (!(filp = current->filp[fd])) + return -EINVAL; + current->filp[fd] = NULL; + if (filp->f_count == 0) + panic("Close: file count is 0"); + if (--filp->f_count) + return (0); + iput(filp->f_inode); + return (0); +} diff --git a/linux/fs/pipe.c b/linux/fs/pipe.c new file mode 100644 index 0000000..7e03e13 --- /dev/null +++ b/linux/fs/pipe.c @@ -0,0 +1,92 @@ +#include <signal.h> + +#include <linux/sched.h> +#include <linux/mm.h> /* for get_free_page */ +#include <asm/segment.h> + +int read_pipe(struct m_inode * inode, char * buf, int count) +{ + char * b=buf; + + while (PIPE_EMPTY(*inode)) { + wake_up(&inode->i_wait); + if (inode->i_count != 2) /* are there any writers left? */ + return 0; + sleep_on(&inode->i_wait); + } + while (count>0 && !(PIPE_EMPTY(*inode))) { + count --; + put_fs_byte(((char *)inode->i_size)[PIPE_TAIL(*inode)],b++); + INC_PIPE( PIPE_TAIL(*inode) ); + } + wake_up(&inode->i_wait); + return b-buf; +} + +int write_pipe(struct m_inode * inode, char * buf, int count) +{ + char * b=buf; + + wake_up(&inode->i_wait); + if (inode->i_count != 2) { /* no readers */ + current->signal |= (1<<(SIGPIPE-1)); + return -1; + } + while (count-->0) { + while (PIPE_FULL(*inode)) { + wake_up(&inode->i_wait); + if (inode->i_count != 2) { + current->signal |= (1<<(SIGPIPE-1)); + return b-buf; + } + sleep_on(&inode->i_wait); + } + ((char *)inode->i_size)[PIPE_HEAD(*inode)] = get_fs_byte(b++); + INC_PIPE( PIPE_HEAD(*inode) ); + wake_up(&inode->i_wait); + } + wake_up(&inode->i_wait); + return b-buf; +} + +int sys_pipe(unsigned long * fildes) +{ + struct m_inode * inode; + struct file * f[2]; + int fd[2]; + int i,j; + + j=0; + for(i=0;j<2 && i<NR_FILE;i++) + if (!file_table[i].f_count) + (f[j++]=i+file_table)->f_count++; + if (j==1) + f[0]->f_count=0; + if (j<2) + return -1; + j=0; + for(i=0;j<2 && i<NR_OPEN;i++) + if (!current->filp[i]) { + current->filp[ fd[j]=i ] = f[j]; + j++; + } + if (j==1) + current->filp[fd[0]]=NULL; + if (j<2) { + f[0]->f_count=f[1]->f_count=0; + return -1; + } + if (!(inode=get_pipe_inode())) { + current->filp[fd[0]] = + current->filp[fd[1]] = NULL; + f[0]->f_count = f[1]->f_count = 0; + return -1; + } + f[0]->f_inode = f[1]->f_inode = inode; + f[0]->f_pos = f[1]->f_pos = 0; + f[0]->f_mode = 1; /* read */ + f[1]->f_mode = 2; /* write */ + put_fs_long(fd[0],0+fildes); + put_fs_long(fd[1],1+fildes); + return 0; +} diff --git a/linux/fs/read_write.c b/linux/fs/read_write.c new file mode 100644 index 0000000..93faac2 --- /dev/null +++ b/linux/fs/read_write.c @@ -0,0 +1,97 @@ +#include <sys/stat.h> +#include <errno.h> +#include <sys/types.h> + +#include <linux/kernel.h> +#include <linux/sched.h> +#include <asm/segment.h> + +extern int rw_char(int rw,int dev, char * buf, int count); +extern int read_pipe(struct m_inode * inode, char * buf, int count); +extern int write_pipe(struct m_inode * inode, char * buf, int count); +extern int block_read(int dev, off_t * pos, char * buf, int count); +extern int block_write(int dev, off_t * pos, char * buf, int count); +extern int file_read(struct m_inode * inode, struct file * filp, + char * buf, int count); +extern int file_write(struct m_inode * inode, struct file * filp, + char * buf, int count); + +int sys_lseek(unsigned int fd,off_t offset, int origin) +{ + struct file * file; + int tmp; + + if (fd >= NR_OPEN || !(file=current->filp[fd]) || !(file->f_inode) + || !IS_BLOCKDEV(MAJOR(file->f_inode->i_dev))) + return -EBADF; + if (file->f_inode->i_pipe) + return -ESPIPE; + switch (origin) { + case 0: + if (offset<0) return -EINVAL; + file->f_pos=offset; + break; + case 1: + if (file->f_pos+offset<0) return -EINVAL; + file->f_pos += offset; + break; + case 2: + if ((tmp=file->f_inode->i_size+offset) < 0) + return -EINVAL; + file->f_pos = tmp; + break; + default: + return -EINVAL; + } + return file->f_pos; +} + +int sys_read(unsigned int fd,char * buf,int count) +{ + struct file * file; + struct m_inode * inode; + + if (fd>=NR_OPEN || count<0 || !(file=current->filp[fd])) + return -EINVAL; + if (!count) + return 0; + verify_area(buf,count); + inode = file->f_inode; + if (inode->i_pipe) + return (file->f_mode&1)?read_pipe(inode,buf,count):-1; + if (S_ISCHR(inode->i_mode)) + return rw_char(READ,inode->i_zone[0],buf,count); + if (S_ISBLK(inode->i_mode)) + return block_read(inode->i_zone[0],&file->f_pos,buf,count); + if (S_ISDIR(inode->i_mode) || S_ISREG(inode->i_mode)) { + if (count+file->f_pos > inode->i_size) + count = inode->i_size - file->f_pos; + if (count<=0) + return 0; + return file_read(inode,file,buf,count); + } + printk("(Read)inode->i_mode=%06o\n\r",inode->i_mode); + return -EINVAL; +} + +int sys_write(unsigned int fd,char * buf,int count) +{ + struct file * file; + struct m_inode * inode; + + if (fd>=NR_OPEN || count <0 || !(file=current->filp[fd])) + return -EINVAL; + if (!count) + return 0; + inode=file->f_inode; + if (inode->i_pipe) + return (file->f_mode&2)?write_pipe(inode,buf,count):-1; + if (S_ISCHR(inode->i_mode)) + return rw_char(WRITE,inode->i_zone[0],buf,count); + if (S_ISBLK(inode->i_mode)) + return block_write(inode->i_zone[0],&file->f_pos,buf,count); + if (S_ISREG(inode->i_mode)) + return file_write(inode,file,buf,count); + printk("(Write)inode->i_mode=%06o\n\r",inode->i_mode); + return -EINVAL; +} diff --git a/linux/fs/stat.c b/linux/fs/stat.c new file mode 100644 index 0000000..4bec71d --- /dev/null +++ b/linux/fs/stat.c @@ -0,0 +1,51 @@ +#include <errno.h> +#include <sys/stat.h> + +#include <linux/fs.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <asm/segment.h> + +static int cp_stat(struct m_inode * inode, struct stat * statbuf) +{ + struct stat tmp; + int i; + + verify_area(statbuf,sizeof (* statbuf)); + tmp.st_dev = inode->i_dev; + tmp.st_ino = inode->i_num; + tmp.st_mode = inode->i_mode; + tmp.st_nlink = inode->i_nlinks; + tmp.st_uid = inode->i_uid; + tmp.st_gid = inode->i_gid; + tmp.st_rdev = inode->i_zone[0]; + tmp.st_size = inode->i_size; + tmp.st_atime = inode->i_atime; + tmp.st_mtime = inode->i_mtime; + tmp.st_ctime = inode->i_ctime; + for (i=0 ; i<sizeof (tmp) ; i++) + put_fs_byte(((char *) &tmp)[i],&((char *) statbuf)[i]); + return (0); +} + +int sys_stat(char * filename, struct stat * statbuf) +{ + int i; + struct m_inode * inode; + + if (!(inode=namei(filename))) + return -ENOENT; + i=cp_stat(inode,statbuf); + iput(inode); + return i; +} + +int sys_fstat(unsigned int fd, struct stat * statbuf) +{ + struct file * f; + struct m_inode * inode; + + if (fd >= NR_OPEN || !(f=current->filp[fd]) || !(inode=f->f_inode)) + return -ENOENT; + return cp_stat(inode,statbuf); +} diff --git a/linux/fs/super.c b/linux/fs/super.c new file mode 100644 index 0000000..d832289 --- /dev/null +++ b/linux/fs/super.c @@ -0,0 +1,102 @@ +/* + * super.c contains code to handle the super-block tables. + */ +#include <linux/config.h> +#include <linux/sched.h> +#include <linux/kernel.h> + +/* set_bit uses setb, as gas doesn't recognize setc */ +#define set_bit(bitnr,addr) ({ \ +register int __res __asm__("ax"); \ +__asm__("bt %2,%3;setb %%al":"=a" (__res):"a" (0),"r" (bitnr),"m" (*(addr))); \ +__res; }) + +struct super_block super_block[NR_SUPER]; + +struct super_block * do_mount(int dev) +{ + struct super_block * p; + struct buffer_head * bh; + int i,block; + + for(p = &super_block[0] ; p < &super_block[NR_SUPER] ; p++ ) + if (!(p->s_dev)) + break; + p->s_dev = -1; /* mark it in use */ + if (p >= &super_block[NR_SUPER]) + return NULL; + if (!(bh = bread(dev,1))) + return NULL; + *p = *((struct super_block *) bh->b_data); + brelse(bh); + if (p->s_magic != SUPER_MAGIC) { + p->s_dev = 0; + return NULL; + } + for (i=0;i<I_MAP_SLOTS;i++) + p->s_imap[i] = NULL; + for (i=0;i<Z_MAP_SLOTS;i++) + p->s_zmap[i] = NULL; + block=2; + for (i=0 ; i < p->s_imap_blocks ; i++) + if (p->s_imap[i]=bread(dev,block)) + block++; + else + break; + for (i=0 ; i < p->s_zmap_blocks ; i++) + if (p->s_zmap[i]=bread(dev,block)) + block++; + else + break; + if (block != 2+p->s_imap_blocks+p->s_zmap_blocks) { + for(i=0;i<I_MAP_SLOTS;i++) + brelse(p->s_imap[i]); + for(i=0;i<Z_MAP_SLOTS;i++) + brelse(p->s_zmap[i]); + p->s_dev=0; + return NULL; + } + p->s_imap[0]->b_data[0] |= 1; + p->s_zmap[0]->b_data[0] |= 1; + p->s_dev = dev; + p->s_isup = NULL; + p->s_imount = NULL; + p->s_time = 0; + p->s_rd_only = 0; + p->s_dirt = 0; + return p; +} + +void mount_root(void) +{ + int i,free; + struct super_block * p; + struct m_inode * mi; + + if (32 != sizeof (struct d_inode)) + panic("bad i-node size"); + for(i=0;i<NR_FILE;i++) + file_table[i].f_count=0; + for(p = &super_block[0] ; p < &super_block[NR_SUPER] ; p++) + p->s_dev = 0; + if (!(p=do_mount(ROOT_DEV))) + panic("Unable to mount root"); + if (!(mi=iget(ROOT_DEV,1))) + panic("Unable to read root i-node"); + mi->i_count += 3 ; /* NOTE! it is logically used 4 times, not 1 */ + p->s_isup = p->s_imount = mi; + current->pwd = mi; + current->root = mi; + free=0; + i=p->s_nzones; + while (-- i >= 0) + if (!set_bit(i&8191,p->s_zmap[i>>13]->b_data)) + free++; + printk("%d/%d free blocks\n\r",free,p->s_nzones); + free=0; + i=p->s_ninodes+1; + while (-- i >= 0) + if (!set_bit(i&8191,p->s_imap[i>>13]->b_data)) + free++; + printk("%d/%d free inodes\n\r",free,p->s_ninodes); +} diff --git a/linux/fs/truncate.c b/linux/fs/truncate.c new file mode 100644 index 0000000..fd82db8 --- /dev/null +++ b/linux/fs/truncate.c @@ -0,0 +1,59 @@ +#include <linux/sched.h> + +#include <sys/stat.h> + +static void free_ind(int dev,int block) +{ + struct buffer_head * bh; + unsigned short * p; + int i; + + if (!block) + return; + if (bh=bread(dev,block)) { + p = (unsigned short *) bh->b_data; + for (i=0;i<512;i++,p++) + if (*p) + free_block(dev,*p); + brelse(bh); + } + free_block(dev,block); +} + +static void free_dind(int dev,int block) +{ + struct buffer_head * bh; + unsigned short * p; + int i; + + if (!block) + return; + if (bh=bread(dev,block)) { + p = (unsigned short *) bh->b_data; + for (i=0;i<512;i++,p++) + if (*p) + free_ind(dev,*p); + brelse(bh); + } + free_block(dev,block); +} + +void truncate(struct m_inode * inode) +{ + int i; + + if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode))) + return; + for (i=0;i<7;i++) + if (inode->i_zone[i]) { + free_block(inode->i_dev,inode->i_zone[i]); + inode->i_zone[i]=0; + } + free_ind(inode->i_dev,inode->i_zone[7]); + free_dind(inode->i_dev,inode->i_zone[8]); + inode->i_zone[7] = inode->i_zone[8] = 0; + inode->i_size = 0; + inode->i_dirt = 1; + inode->i_mtime = inode->i_ctime = CURRENT_TIME; +} + diff --git a/linux/fs/tty_ioctl.c b/linux/fs/tty_ioctl.c new file mode 100644 index 0000000..b4d9bf7 --- /dev/null +++ b/linux/fs/tty_ioctl.c @@ -0,0 +1,166 @@ +#include <errno.h> +#include <termios.h> + +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/tty.h> + +#include <asm/segment.h> +#include <asm/system.h> + +static void flush(struct tty_queue * queue) +{ + cli(); + queue->head = queue->tail; + sti(); +} + +static void wait_until_sent(struct tty_struct * tty) +{ + /* do nothing - not implemented */ +} + +static void send_break(struct tty_struct * tty) +{ + /* do nothing - not implemented */ +} + +static int get_termios(struct tty_struct * tty, struct termios * termios) +{ + int i; + + verify_area(termios, sizeof (*termios)); + for (i=0 ; i< (sizeof (*termios)) ; i++) + put_fs_byte( ((char *)&tty->termios)[i] , i+(char *)termios ); + return 0; +} + +static int set_termios(struct tty_struct * tty, struct termios * termios) +{ + int i; + + for (i=0 ; i< (sizeof (*termios)) ; i++) + ((char *)&tty->termios)[i]=get_fs_byte(i+(char *)termios); + return 0; +} + +static int get_termio(struct tty_struct * tty, struct termio * termio) +{ + int i; + struct termio tmp_termio; + + verify_area(termio, sizeof (*termio)); + tmp_termio.c_iflag = tty->termios.c_iflag; + tmp_termio.c_oflag = tty->termios.c_oflag; + tmp_termio.c_cflag = tty->termios.c_cflag; + tmp_termio.c_lflag = tty->termios.c_lflag; + tmp_termio.c_line = tty->termios.c_line; + for(i=0 ; i < NCC ; i++) + tmp_termio.c_cc[i] = tty->termios.c_cc[i]; + for (i=0 ; i< (sizeof (*termio)) ; i++) + put_fs_byte( ((char *)&tmp_termio)[i] , i+(char *)termio ); + return 0; +} + +static int set_termio(struct tty_struct * tty, struct termio * termio) +{ + int i; + struct termio tmp_termio; + + for (i=0 ; i< (sizeof (*termio)) ; i++) + ((char *)&tmp_termio)[i]=get_fs_byte(i+(char *)termio); + *(unsigned short *)&tty->termios.c_iflag = tmp_termio.c_iflag; + *(unsigned short *)&tty->termios.c_oflag = tmp_termio.c_oflag; + *(unsigned short *)&tty->termios.c_cflag = tmp_termio.c_cflag; + *(unsigned short *)&tty->termios.c_lflag = tmp_termio.c_lflag; + tty->termios.c_line = tmp_termio.c_line; + for(i=0 ; i < NCC ; i++) + tty->termios.c_cc[i] = tmp_termio.c_cc[i]; + return 0; +} + +int tty_ioctl(int dev, int cmd, int arg) +{ + struct tty_struct * tty; + if (MAJOR(dev) == 5) { + dev=current->tty; + if (dev<0) + panic("tty_ioctl: dev<0"); + } else + dev=MINOR(dev); + tty = dev + tty_table; + switch (cmd) { + case TCGETS: + return get_termios(tty,(struct termios *) arg); + case TCSETSF: + flush(&tty->read_q); /* fallthrough */ + case TCSETSW: + wait_until_sent(tty); /* fallthrough */ + case TCSETS: + return set_termios(tty,(struct termios *) arg); + case TCGETA: + return get_termio(tty,(struct termio *) arg); + case TCSETAF: + flush(&tty->read_q); /* fallthrough */ + case TCSETAW: + wait_until_sent(tty); /* fallthrough */ + case TCSETA: + return set_termio(tty,(struct termio *) arg); + case TCSBRK: + if (!arg) { + wait_until_sent(tty); + send_break(tty); + } + return 0; + case TCXONC: + return -EINVAL; /* not implemented */ + case TCFLSH: + if (arg==0) + flush(&tty->read_q); + else if (arg==1) + flush(&tty->write_q); + else if (arg==2) { + flush(&tty->read_q); + flush(&tty->write_q); + } else + return -EINVAL; + return 0; + case TIOCEXCL: + return -EINVAL; /* not implemented */ + case TIOCNXCL: + return -EINVAL; /* not implemented */ + case TIOCSCTTY: + return -EINVAL; /* set controlling term NI */ + case TIOCGPGRP: + verify_area((void *) arg,4); + put_fs_long(tty->pgrp,(unsigned long *) arg); + return 0; + case TIOCSPGRP: + tty->pgrp=get_fs_long((unsigned long *) arg); + return 0; + case TIOCOUTQ: + verify_area((void *) arg,4); + put_fs_long(CHARS(tty->write_q),(unsigned long *) arg); + return 0; + case TIOCSTI: + return -EINVAL; /* not implemented */ + case TIOCGWINSZ: + return -EINVAL; /* not implemented */ + case TIOCSWINSZ: + return -EINVAL; /* not implemented */ + case TIOCMGET: + return -EINVAL; /* not implemented */ + case TIOCMBIS: + return -EINVAL; /* not implemented */ + case TIOCMBIC: + return -EINVAL; /* not implemented */ + case TIOCMSET: + return -EINVAL; /* not implemented */ + case TIOCGSOFTCAR: + return -EINVAL; /* not implemented */ + case TIOCSSOFTCAR: + return -EINVAL; /* not implemented */ + default: + return -EINVAL; + } +} diff --git a/linux/include/a.out.h b/linux/include/a.out.h new file mode 100644 index 0000000..3e67974 --- /dev/null +++ b/linux/include/a.out.h @@ -0,0 +1,220 @@ +#ifndef _A_OUT_H +#define _A_OUT_H + +#define __GNU_EXEC_MACROS__ + +struct exec { + unsigned long a_magic; /* Use macros N_MAGIC, etc for access */ + unsigned a_text; /* length of text, in bytes */ + unsigned a_data; /* length of data, in bytes */ + unsigned a_bss; /* length of uninitialized data area for file, in bytes */ + unsigned a_syms; /* length of symbol table data in file, in bytes */ + unsigned a_entry; /* start address */ + unsigned a_trsize; /* length of relocation info for text, in bytes */ + unsigned a_drsize; /* length of relocation info for data, in bytes */ +}; + +#ifndef N_MAGIC +#define N_MAGIC(exec) ((exec).a_magic) +#endif + +#ifndef OMAGIC +/* Code indicating object file or impure executable. */ +#define OMAGIC 0407 +/* Code indicating pure executable. */ +#define NMAGIC 0410 +/* Code indicating demand-paged executable. */ +#define ZMAGIC 0413 +#endif /* not OMAGIC */ + +#ifndef N_BADMAG +#define N_BADMAG(x) \ + (N_MAGIC(x) != OMAGIC && N_MAGIC(x) != NMAGIC \ + && N_MAGIC(x) != ZMAGIC) +#endif + +#define _N_BADMAG(x) \ + (N_MAGIC(x) != OMAGIC && N_MAGIC(x) != NMAGIC \ + && N_MAGIC(x) != ZMAGIC) + +#define _N_HDROFF(x) (SEGMENT_SIZE - sizeof (struct exec)) + +#ifndef N_TXTOFF +#define N_TXTOFF(x) \ + (N_MAGIC(x) == ZMAGIC ? _N_HDROFF((x)) + sizeof (struct exec) : sizeof (struct exec)) +#endif + +#ifndef N_DATOFF +#define N_DATOFF(x) (N_TXTOFF(x) + (x).a_text) +#endif + +#ifndef N_TRELOFF +#define N_TRELOFF(x) (N_DATOFF(x) + (x).a_data) +#endif + +#ifndef N_DRELOFF +#define N_DRELOFF(x) (N_TRELOFF(x) + (x).a_trsize) +#endif + +#ifndef N_SYMOFF +#define N_SYMOFF(x) (N_DRELOFF(x) + (x).a_drsize) +#endif + +#ifndef N_STROFF +#define N_STROFF(x) (N_SYMOFF(x) + (x).a_syms) +#endif + +/* Address of text segment in memory after it is loaded. */ +#ifndef N_TXTADDR +#define N_TXTADDR(x) 0 +#endif + +/* Address of data segment in memory after it is loaded. + Note that it is up to you to define SEGMENT_SIZE + on machines not listed here. */ +#if defined(vax) || defined(hp300) || defined(pyr) +#define SEGMENT_SIZE PAGE_SIZE +#endif +#ifdef hp300 +#define PAGE_SIZE 4096 +#endif +#ifdef sony +#define SEGMENT_SIZE 0x2000 +#endif /* Sony. */ +#ifdef is68k +#define SEGMENT_SIZE 0x20000 +#endif +#if defined(m68k) && defined(PORTAR) +#define PAGE_SIZE 0x400 +#define SEGMENT_SIZE PAGE_SIZE +#endif + +#define PAGE_SIZE 4096 +#define SEGMENT_SIZE 1024 + +#define _N_SEGMENT_ROUND(x) (((x) + SEGMENT_SIZE - 1) & ~(SEGMENT_SIZE - 1)) + +#define _N_TXTENDADDR(x) (N_TXTADDR(x)+(x).a_text) + +#ifndef N_DATADDR +#define N_DATADDR(x) \ + (N_MAGIC(x)==OMAGIC? (_N_TXTENDADDR(x)) \ + : (_N_SEGMENT_ROUND (_N_TXTENDADDR(x)))) +#endif + +/* Address of bss segment in memory after it is loaded. */ +#ifndef N_BSSADDR +#define N_BSSADDR(x) (N_DATADDR(x) + (x).a_data) +#endif + +#ifndef N_NLIST_DECLARED +struct nlist { + union { + char *n_name; + struct nlist *n_next; + long n_strx; + } n_un; + unsigned char n_type; + char n_other; + short n_desc; + unsigned long n_value; +}; +#endif + +#ifndef N_UNDF +#define N_UNDF 0 +#endif +#ifndef N_ABS +#define N_ABS 2 +#endif +#ifndef N_TEXT +#define N_TEXT 4 +#endif +#ifndef N_DATA +#define N_DATA 6 +#endif +#ifndef N_BSS +#define N_BSS 8 +#endif +#ifndef N_COMM +#define N_COMM 18 +#endif +#ifndef N_FN +#define N_FN 15 +#endif + +#ifndef N_EXT +#define N_EXT 1 +#endif +#ifndef N_TYPE +#define N_TYPE 036 +#endif +#ifndef N_STAB +#define N_STAB 0340 +#endif + +/* The following type indicates the definition of a symbol as being + an indirect reference to another symbol. The other symbol + appears as an undefined reference, immediately following this symbol. + + Indirection is asymmetrical. The other symbol's value will be used + to satisfy requests for the indirect symbol, but not vice versa. + If the other symbol does not have a definition, libraries will + be searched to find a definition. */ +#define N_INDR 0xa + +/* The following symbols refer to set elements. + All the N_SET[ATDB] symbols with the same name form one set. + Space is allocated for the set in the text section, and each set + element's value is stored into one word of the space. + The first word of the space is the length of the set (number of elements). + + The address of the set is made into an N_SETV symbol + whose name is the same as the name of the set. + This symbol acts like a N_DATA global symbol + in that it can satisfy undefined external references. */ + +/* These appear as input to LD, in a .o file. */ +#define N_SETA 0x14 /* Absolute set element symbol */ +#define N_SETT 0x16 /* Text set element symbol */ +#define N_SETD 0x18 /* Data set element symbol */ +#define N_SETB 0x1A /* Bss set element symbol */ + +/* This is output from LD. */ +#define N_SETV 0x1C /* Pointer to set vector in data area. */ + +#ifndef N_RELOCATION_INFO_DECLARED + +/* This structure describes a single relocation to be performed. + The text-relocation section of the file is a vector of these structures, + all of which apply to the text section. + Likewise, the data-relocation section applies to the data section. */ + +struct relocation_info +{ + /* Address (within segment) to be relocated. */ + int r_address; + /* The meaning of r_symbolnum depends on r_extern. */ + unsigned int r_symbolnum:24; + /* Nonzero means value is a pc-relative offset + and it should be relocated for changes in its own address + as well as for changes in the symbol or section specified. */ + unsigned int r_pcrel:1; + /* Length (as exponent of 2) of the field to be relocated. + Thus, a value of 2 indicates 1<<2 bytes. */ + unsigned int r_length:2; + /* 1 => relocate with value of symbol. + r_symbolnum is the index of the symbol + in file's the symbol table. + 0 => relocate with the address of a segment. + r_symbolnum is N_TEXT, N_DATA, N_BSS or N_ABS + (the N_EXT bit may be set also, but signifies nothing). */ + unsigned int r_extern:1; + /* Four bits that aren't used, but when writing an object file + it is desirable to clear them. */ + unsigned int r_pad:4; +}; +#endif /* no N_RELOCATION_INFO_DECLARED. */ + + +#endif /* __A_OUT_GNU_H__ */ diff --git a/linux/include/asm/io.h b/linux/include/asm/io.h new file mode 100644 index 0000000..d5cc42a --- /dev/null +++ b/linux/include/asm/io.h @@ -0,0 +1,24 @@ +#define outb(value,port) \ +__asm__ ("outb %%al,%%dx"::"a" (value),"d" (port)) + + +#define inb(port) ({ \ +unsigned char _v; \ +__asm__ volatile ("inb %%dx,%%al":"=a" (_v):"d" (port)); \ +_v; \ +}) + +#define outb_p(value,port) \ +__asm__ ("outb %%al,%%dx\n" \ + "\tjmp 1f\n" \ + "1:\tjmp 1f\n" \ + "1:"::"a" (value),"d" (port)) + +#define inb_p(port) ({ \ +unsigned char _v; \ +__asm__ volatile ("inb %%dx,%%al\n" \ + "\tjmp 1f\n" \ + "1:\tjmp 1f\n" \ + "1:":"=a" (_v):"d" (port)); \ +_v; \ +}) diff --git a/linux/include/asm/memory.h b/linux/include/asm/memory.h new file mode 100644 index 0000000..4b0a98e --- /dev/null +++ b/linux/include/asm/memory.h @@ -0,0 +1,14 @@ +/* + * NOTE!!! memcpy(dest,src,n) assumes ds=es=normal data segment. This + * goes for all kernel functions (ds=es=kernel space, fs=local data, + * gs=null), as well as for all well-behaving user programs (ds=es= + * user data space). This is NOT a bug, as any user program that changes + * es deserves to die if it isn't careful. + */ +#define memcpy(dest,src,n) ({ \ +void * _res = dest; \ +__asm__ ("cld;rep;movsb" \ + ::"D" ((long)(_res)),"S" ((long)(src)),"c" ((long) (n)) \ + :"di","si","cx"); \ +_res; \ +}) diff --git a/linux/include/asm/segment.h b/linux/include/asm/segment.h new file mode 100644 index 0000000..1a53b08 --- /dev/null +++ b/linux/include/asm/segment.h @@ -0,0 +1,38 @@ +extern inline unsigned char get_fs_byte(const char * addr) +{ + unsigned register char _v; + + __asm__ ("movb %%fs:%1,%0":"=r" (_v):"m" (*addr)); + return _v; +} + +extern inline unsigned short get_fs_word(const unsigned short *addr) +{ + unsigned short _v; + + __asm__ ("movw %%fs:%1,%0":"=r" (_v):"m" (*addr)); + return _v; +} + +extern inline unsigned long get_fs_long(const unsigned long *addr) +{ + unsigned long _v; + + __asm__ ("movl %%fs:%1,%0":"=r" (_v):"m" (*addr)); \ + return _v; +} + +extern inline void put_fs_byte(char val,char *addr) +{ +__asm__ ("movb %0,%%fs:%1"::"r" (val),"m" (*addr)); +} + +extern inline void put_fs_word(short val,short * addr) +{ +__asm__ ("movw %0,%%fs:%1"::"r" (val),"m" (*addr)); +} + +extern inline void put_fs_long(unsigned long val,unsigned long * addr) +{ +__asm__ ("movl %0,%%fs:%1"::"r" (val),"m" (*addr)); +} diff --git a/linux/include/asm/system.h b/linux/include/asm/system.h new file mode 100644 index 0000000..0b5a21d --- /dev/null +++ b/linux/include/asm/system.h @@ -0,0 +1,66 @@ +#define move_to_user_mode() \ +__asm__ ("movl %%esp,%%eax\n\t" \ + "pushl $0x17\n\t" \ + "pushl %%eax\n\t" \ + "pushfl\n\t" \ + "pushl $0x0f\n\t" \ + "pushl $1f\n\t" \ + "iret\n" \ + "1:\tmovl $0x17,%%eax\n\t" \ + "movw %%ax,%%ds\n\t" \ + "movw %%ax,%%es\n\t" \ + "movw %%ax,%%fs\n\t" \ + "movw %%ax,%%gs" \ + :::"ax") + +#define sti() __asm__ ("sti"::) +#define cli() __asm__ ("cli"::) +#define nop() __asm__ ("nop"::) + +#define iret() __asm__ ("iret"::) + +#define _set_gate(gate_addr,type,dpl,addr) \ +__asm__ ("movw %%dx,%%ax\n\t" \ + "movw %0,%%dx\n\t" \ + "movl %%eax,%1\n\t" \ + "movl %%edx,%2" \ + : \ + : "i" ((short) (0x8000+(dpl<<13)+(type<<8))), \ + "o" (*((char *) (gate_addr))), \ + "o" (*(4+(char *) (gate_addr))), \ + "d" ((char *) (addr)),"a" (0x00080000)) + +#define set_intr_gate(n,addr) \ + _set_gate(&idt[n],14,0,addr) + +#define set_trap_gate(n,addr) \ + _set_gate(&idt[n],15,0,addr) + +#define set_system_gate(n,addr) \ + _set_gate(&idt[n],15,3,addr) + +#define _set_seg_desc(gate_addr,type,dpl,base,limit) {\ + *(gate_addr) = ((base) & 0xff000000) | \ + (((base) & 0x00ff0000)>>16) | \ + ((limit) & 0xf0000) | \ + ((dpl)<<13) | \ + (0x00408000) | \ + ((type)<<8); \ + *((gate_addr)+1) = (((base) & 0x0000ffff)<<16) | \ + ((limit) & 0x0ffff); } + +#define _set_tssldt_desc(n,addr,type) \ +__asm__ ("movw $104,%1\n\t" \ + "movw %%ax,%2\n\t" \ + "rorl $16,%%eax\n\t" \ + "movb %%al,%3\n\t" \ + "movb $" type ",%4\n\t" \ + "movb $0x00,%5\n\t" \ + "movb %%ah,%6\n\t" \ + "rorl $16,%%eax" \ + ::"a" (addr), "m" (*(n)), "m" (*(n+2)), "m" (*(n+4)), \ + "m" (*(n+5)), "m" (*(n+6)), "m" (*(n+7)) \ + ) + +#define set_tss_desc(n,addr) _set_tssldt_desc(((char *) (n)),addr,"0x89") +#define set_ldt_desc(n,addr) _set_tssldt_desc(((char *) (n)),addr,"0x82") diff --git a/linux/include/const.h b/linux/include/const.h new file mode 100644 index 0000000..7828e61 --- /dev/null +++ b/linux/include/const.h @@ -0,0 +1,15 @@ +#ifndef _CONST_H +#define _CONST_H + +#define BUFFER_END 0x200000 + +#define I_TYPE 0170000 +#define I_DIRECTORY 0040000 +#define I_REGULAR 0100000 +#define I_BLOCK_SPECIAL 0060000 +#define I_CHAR_SPECIAL 0020000 +#define I_NAMED_PIPE 0010000 +#define I_SET_UID_BIT 0004000 +#define I_SET_GID_BIT 0002000 + +#endif diff --git a/linux/include/ctype.h b/linux/include/ctype.h new file mode 100644 index 0000000..4043d6e --- /dev/null +++ b/linux/include/ctype.h @@ -0,0 +1,34 @@ +#ifndef _CTYPE_H +#define _CTYPE_H + +#define _U 0x01 /* upper */ +#define _L 0x02 /* lower */ +#define _D 0x04 /* digit */ +#define _C 0x08 /* cntrl */ +#define _P 0x10 /* punct */ +#define _S 0x20 /* white space (space/lf/tab) */ +#define _X 0x40 /* hex digit */ +#define _SP 0x80 /* hard space (0x20) */ + +extern unsigned char _ctype[]; +extern char _ctmp; + +#define isalnum(c) ((_ctype+1)[c]&(_U|_L|_D)) +#define isalpha(c) ((_ctype+1)[c]&(_U|_L)) +#define iscntrl(c) ((_ctype+1)[c]&(_C)) +#define isdigit(c) ((_ctype+1)[c]&(_D)) +#define isgraph(c) ((_ctype+1)[c]&(_P|_U|_L|_D)) +#define islower(c) ((_ctype+1)[c]&(_L)) +#define isprint(c) ((_ctype+1)[c]&(_P|_U|_L|_D|_SP)) +#define ispunct(c) ((_ctype+1)[c]&(_P)) +#define isspace(c) ((_ctype+1)[c]&(_S)) +#define isupper(c) ((_ctype+1)[c]&(_U)) +#define isxdigit(c) ((_ctype+1)[c]&(_D|_X)) + +#define isascii(c) (((unsigned) c)<=0x7f) +#define toascii(c) (((unsigned) c)&0x7f) + +#define tolower(c) (_ctmp=c,isupper(_ctmp)?_ctmp+('a'+'A'):_ctmp) +#define toupper(c) (_ctmp=c,islower(_ctmp)?_ctmp+('A'-'a'):_ctmp) + +#endif diff --git a/linux/include/errno.h b/linux/include/errno.h new file mode 100644 index 0000000..c282f69 --- /dev/null +++ b/linux/include/errno.h @@ -0,0 +1,60 @@ +#ifndef _ERRNO_H +#define _ERRNO_H + +/* + * ok, as I hadn't got any other source of information about + * possible error numbers, I was forced to use the same numbers + * as minix. + * Hopefully these are posix or something. I wouldn't know (and posix + * isn't telling me - they want $$$ for their f***ing standard). + * + * We don't use the _SIGN cludge of minix, so kernel returns must + * see to the sign by themselves. + * + * NOTE! Remember to change strerror() if you change this file! + */ + +extern int errno; + +#define ERROR 99 +#define EPERM 1 +#define ENOENT 2 +#define ESRCH 3 +#define EINTR 4 +#define EIO 5 +#define ENXIO 6 +#define E2BIG 7 +#define ENOEXEC 8 +#define EBADF 9 +#define ECHILD 10 +#define EAGAIN 11 +#define ENOMEM 12 +#define EACCES 13 +#define EFAULT 14 +#define ENOTBLK 15 +#define EBUSY 16 +#define EEXIST 17 +#define EXDEV 18 +#define ENODEV 19 +#define ENOTDIR 20 +#define EISDIR 21 +#define EINVAL 22 +#define ENFILE 23 +#define EMFILE 24 +#define ENOTTY 25 +#define ETXTBSY 26 +#define EFBIG 27 +#define ENOSPC 28 +#define ESPIPE 29 +#define EROFS 30 +#define EMLINK 31 +#define EPIPE 32 +#define EDOM 33 +#define ERANGE 34 +#define EDEADLK 35 +#define ENAMETOOLONG 36 +#define ENOLCK 37 +#define ENOSYS 38 +#define ENOTEMPTY 39 + +#endif diff --git a/linux/include/fcntl.h b/linux/include/fcntl.h new file mode 100644 index 0000000..a5bf9af --- /dev/null +++ b/linux/include/fcntl.h @@ -0,0 +1,55 @@ +#ifndef _FCNTL_H +#define _FCNTL_H + +#include <sys/types.h> + +/* open/fcntl - NOCTTY, NDELAY isn't implemented yet */ +#define O_ACCMODE 00003 +#define O_RDONLY 00 +#define O_WRONLY 01 +#define O_RDWR 02 +#define O_CREAT 00100 /* not fcntl */ +#define O_EXCL 00200 /* not fcntl */ +#define O_NOCTTY 00400 /* not fcntl */ +#define O_TRUNC 01000 /* not fcntl */ +#define O_APPEND 02000 +#define O_NONBLOCK 04000 /* not fcntl */ +#define O_NDELAY O_NONBLOCK + +/* Defines for fcntl-commands. Note that currently + * locking isn't supported, and other things aren't really + * tested. + */ +#define F_DUPFD 0 /* dup */ +#define F_GETFD 1 /* get f_flags */ +#define F_SETFD 2 /* set f_flags */ +#define F_GETFL 3 /* more flags (cloexec) */ +#define F_SETFL 4 +#define F_GETLK 5 /* not implemented */ +#define F_SETLK 6 +#define F_SETLKW 7 + +/* for F_[GET|SET]FL */ +#define FD_CLOEXEC 1 /* actually anything with low bit set goes */ + +/* Ok, these are locking features, and aren't implemented at any + * level. POSIX wants them. + */ +#define F_RDLCK 0 +#define F_WRLCK 1 +#define F_UNLCK 2 + +/* Once again - not implemented, but ... */ +struct flock { + short l_type; + short l_whence; + off_t l_start; + off_t l_len; + pid_t l_pid; +}; + +extern int creat(const char * filename,mode_t mode); +extern int fcntl(int fildes,int cmd, ...); +extern int open(const char * filename, int flags, ...); + +#endif diff --git a/linux/include/linux/config.h b/linux/include/linux/config.h new file mode 100644 index 0000000..c84cacc --- /dev/null +++ b/linux/include/linux/config.h @@ -0,0 +1,53 @@ +#ifndef _CONFIG_H +#define _CONFIG_H + +/* #define LASU_HD */ +#define LINUS_HD + +/* + * Amount of ram memory (in bytes, 640k-1M not discounted). Currently 8Mb. + * Don't make this bigger without making sure that there are enough page + * directory entries (boot/head.s) + */ +#if defined(LINUS_HD) +#define HIGH_MEMORY (0x800000) +#elif defined(LASU_HD) +#define HIGH_MEMORY (0x400000) +#else +#error "must define hd" +#endif + +/* End of buffer memory. Must be 0xA0000, or > 0x100000, 4096-byte aligned */ +#if (HIGH_MEMORY>=0x600000) +#define BUFFER_END 0x200000 +#else +#define BUFFER_END 0xA0000 +#endif + +/* Root device at bootup. */ +#if defined(LINUS_HD) +#define ROOT_DEV 0x306 +#elif defined(LASU_HD) +#define ROOT_DEV 0x302 +#else +#error "must define HD" +#endif + +/* + * HD type. If 2, put 2 structures with a comma. If just 1, put + * only 1 struct. The structs are { HEAD, SECTOR, TRACKS, WPCOM, LZONE, CTL } + * + * NOTE. CTL is supposed to be 0 for drives with less than 8 heads, and + * 8 if heads >= 8. Don't know why, and I haven't tested it on a drive with + * more than 8 heads, but that is what the bios-listings seem to imply. I + * just love not having a manual. + */ +#if defined(LASU_HD) +#define HD_TYPE { 7,35,915,65536,920,0 } +#elif defined(LINUS_HD) +#define HD_TYPE { 5,17,980,300,980,0 },{ 5,17,980,300,980,0 } +#else +#error "must define a hard-disk type" +#endif + +#endif diff --git a/linux/include/linux/fs.h b/linux/include/linux/fs.h new file mode 100644 index 0000000..e5db892 --- /dev/null +++ b/linux/include/linux/fs.h @@ -0,0 +1,185 @@ +/* + * This file has definitions for some important file table + * structures etc. + */ + +#ifndef _FS_H +#define _FS_H + +#include <sys/types.h> + +/* devices are as follows: (same as minix, so we can use the minix + * file system. These are major numbers.) + * + * 0 - unused (nodev) + * 1 - /dev/mem + * 2 - /dev/fd + * 3 - /dev/hd + * 4 - /dev/ttyx + * 5 - /dev/tty + * 6 - /dev/lp + * 7 - unnamed pipes + */ + +#define IS_BLOCKDEV(x) ((x)==2 || (x)==3) + +#define READ 0 +#define WRITE 1 + +void buffer_init(void); + +#define MAJOR(a) (((unsigned)(a))>>8) +#define MINOR(a) ((a)&0xff) + +#define NAME_LEN 14 + +#define I_MAP_SLOTS 8 +#define Z_MAP_SLOTS 8 +#define SUPER_MAGIC 0x137F + +#define NR_OPEN 20 +#define NR_INODE 32 +#define NR_FILE 64 +#define NR_SUPER 8 +#define NR_HASH 307 +#define NR_BUFFERS nr_buffers +#define BLOCK_SIZE 1024 +#ifndef NULL +#define NULL ((void *) 0) +#endif + +#define INODES_PER_BLOCK ((BLOCK_SIZE)/(sizeof (struct d_inode))) +#define DIR_ENTRIES_PER_BLOCK ((BLOCK_SIZE)/(sizeof (struct dir_entry))) + +typedef char buffer_block[BLOCK_SIZE]; + +struct buffer_head { + char * b_data; /* pointer to data block (1024 bytes) */ + unsigned short b_dev; /* device (0 = free) */ + unsigned short b_blocknr; /* block number */ + unsigned char b_uptodate; + unsigned char b_dirt; /* 0-clean,1-dirty */ + unsigned char b_count; /* users using this block */ + unsigned char b_lock; /* 0 - ok, 1 -locked */ + struct task_struct * b_wait; + struct buffer_head * b_prev; + struct buffer_head * b_next; + struct buffer_head * b_prev_free; + struct buffer_head * b_next_free; +}; + +struct d_inode { + unsigned short i_mode; + unsigned short i_uid; + unsigned long i_size; + unsigned long i_time; + unsigned char i_gid; + unsigned char i_nlinks; + unsigned short i_zone[9]; +}; + +struct m_inode { + unsigned short i_mode; + unsigned short i_uid; + unsigned long i_size; + unsigned long i_mtime; + unsigned char i_gid; + unsigned char i_nlinks; + unsigned short i_zone[9]; +/* these are in memory also */ + struct task_struct * i_wait; + unsigned long i_atime; + unsigned long i_ctime; + unsigned short i_dev; + unsigned short i_num; + unsigned short i_count; + unsigned char i_lock; + unsigned char i_dirt; + unsigned char i_pipe; + unsigned char i_mount; + unsigned char i_seek; + unsigned char i_update; +}; + +#define PIPE_HEAD(inode) (((long *)((inode).i_zone))[0]) +#define PIPE_TAIL(inode) (((long *)((inode).i_zone))[1]) +#define PIPE_SIZE(inode) ((PIPE_HEAD(inode)-PIPE_TAIL(inode))&(PAGE_SIZE-1)) +#define PIPE_EMPTY(inode) (PIPE_HEAD(inode)==PIPE_TAIL(inode)) +#define PIPE_FULL(inode) (PIPE_SIZE(inode)==(PAGE_SIZE-1)) +#define INC_PIPE(head) \ +__asm__("incl %0\n\tandl $4095,%0"::"m" (head)) + +struct file { + unsigned short f_mode; + unsigned short f_flags; + unsigned short f_count; + struct m_inode * f_inode; + off_t f_pos; +}; + +struct super_block { + unsigned short s_ninodes; + unsigned short s_nzones; + unsigned short s_imap_blocks; + unsigned short s_zmap_blocks; + unsigned short s_firstdatazone; + unsigned short s_log_zone_size; + unsigned long s_max_size; + unsigned short s_magic; +/* These are only in memory */ + struct buffer_head * s_imap[8]; + struct buffer_head * s_zmap[8]; + unsigned short s_dev; + struct m_inode * s_isup; + struct m_inode * s_imount; + unsigned long s_time; + unsigned char s_rd_only; + unsigned char s_dirt; +}; + +struct dir_entry { + unsigned short inode; + char name[NAME_LEN]; +}; + +extern struct m_inode inode_table[NR_INODE]; +extern struct file file_table[NR_FILE]; +extern struct super_block super_block[NR_SUPER]; +extern struct buffer_head * start_buffer; +extern int nr_buffers; + +extern void truncate(struct m_inode * inode); +extern void sync_inodes(void); +extern void wait_on(struct m_inode * inode); +extern int bmap(struct m_inode * inode,int block); +extern int create_block(struct m_inode * inode,int block); +extern struct m_inode * namei(const char * pathname); +extern int open_namei(const char * pathname, int flag, int mode, + struct m_inode ** res_inode); +extern void iput(struct m_inode * inode); +extern struct m_inode * iget(int dev,int nr); +extern struct m_inode * get_empty_inode(void); +extern struct m_inode * get_pipe_inode(void); +extern struct buffer_head * get_hash_table(int dev, int block); +extern struct buffer_head * getblk(int dev, int block); +extern void ll_rw_block(int rw, struct buffer_head * bh); +extern void brelse(struct buffer_head * buf); +extern struct buffer_head * bread(int dev,int block); +extern int new_block(int dev); +extern void free_block(int dev, int block); +extern struct m_inode * new_inode(int dev); +extern void free_inode(struct m_inode * inode); + +extern void mount_root(void); + +extern inline struct super_block * get_super(int dev) +{ + struct super_block * s; + + for(s = 0+super_block;s < NR_SUPER+super_block; s++) + if (s->s_dev == dev) + return s; + return NULL; +} + +#endif diff --git a/linux/include/linux/hdreg.h b/linux/include/linux/hdreg.h new file mode 100644 index 0000000..04ebf87 --- /dev/null +++ b/linux/include/linux/hdreg.h @@ -0,0 +1,99 @@ +/* + * This file contains some defines for the AT-hd-controller. + * Various sources. Check out some definitions (see comments with + * a ques). + */ +#ifndef _HDREG_H +#define _HDREG_H + +/* currently supports only 1 hd, put type here */ +#define HARD_DISK_TYPE 17 + +/* + * Ok, hard-disk-type is currently hardcoded. Not beatiful, + * but easier. We don't use BIOS for anything else, why should + * we get HD-type from it? Get these values from Reference Guide. + */ + +#if HARD_DISK_TYPE == 17 +#define _CYL 977 +#define _HEAD 5 +#define __WPCOM 300 +#define _LZONE 977 +#define _SECT 17 +#define _CTL 0 +#elif HARD_DISK_TYPE == 18 +#define _CYL 977 +#define _HEAD 7 +#define __WPCOM (-1) +#define _LZONE 977 +#define _SECT 17 +#define _CTL 0 +#else +#error Define HARD_DISK_TYPE and parameters, add your own entries as well +#endif + +/* Controller wants just wp-com/4 */ +#if __WPCOM >= 0 +#define _WPCOM ((__WPCOM)>>2) +#else +#define _WPCOM __WPCOM +#endif + +/* Hd controller regs. Ref: IBM AT Bios-listing */ +#define HD_DATA 0x1f0 /* _CTL when writing */ +#define HD_ERROR 0x1f1 /* see err-bits */ +#define HD_NSECTOR 0x1f2 /* nr of sectors to read/write */ +#define HD_SECTOR 0x1f3 /* starting sector */ +#define HD_LCYL 0x1f4 /* starting cylinder */ +#define HD_HCYL 0x1f5 /* high byte of starting cyl */ +#define HD_CURRENT 0x1f6 /* 101dhhhh , d=drive, hhhh=head */ +#define HD_STATUS 0x1f7 /* see status-bits */ +#define HD_PRECOMP HD_ERROR /* same io address, read=error, write=precomp */ +#define HD_COMMAND HD_STATUS /* same io address, read=status, write=cmd */ + +#define HD_CMD 0x3f6 + +/* Bits of HD_STATUS */ +#define ERR_STAT 0x01 +#define INDEX_STAT 0x02 +#define ECC_STAT 0x04 /* Corrected error */ +#define DRQ_STAT 0x08 +#define SEEK_STAT 0x10 +#define WRERR_STAT 0x20 +#define READY_STAT 0x40 +#define BUSY_STAT 0x80 + +/* Values for HD_COMMAND */ +#define WIN_RESTORE 0x10 +#define WIN_READ 0x20 +#define WIN_WRITE 0x30 +#define WIN_VERIFY 0x40 +#define WIN_FORMAT 0x50 +#define WIN_INIT 0x60 +#define WIN_SEEK 0x70 +#define WIN_DIAGNOSE 0x90 +#define WIN_SPECIFY 0x91 + +/* Bits for HD_ERROR */ +#define MARK_ERR 0x01 /* Bad address mark ? */ +#define TRK0_ERR 0x02 /* couldn't find track 0 */ +#define ABRT_ERR 0x04 /* ? */ +#define ID_ERR 0x10 /* ? */ +#define ECC_ERR 0x40 /* ? */ +#define BBD_ERR 0x80 /* ? */ + +struct partition { + unsigned char boot_ind; /* 0x80 - active (unused) */ + unsigned char head; /* ? */ + unsigned char sector; /* ? */ + unsigned char cyl; /* ? */ + unsigned char sys_ind; /* ? */ + unsigned char end_head; /* ? */ + unsigned char end_sector; /* ? */ + unsigned char end_cyl; /* ? */ + unsigned int start_sect; /* starting sector counting from 0 */ + unsigned int nr_sects; /* nr of sectors in partition */ +}; + +#endif diff --git a/linux/include/linux/head.h b/linux/include/linux/head.h new file mode 100644 index 0000000..db3dda2 --- /dev/null +++ b/linux/include/linux/head.h @@ -0,0 +1,20 @@ +#ifndef _HEAD_H +#define _HEAD_H + +typedef struct desc_struct { + unsigned long a,b; +} desc_table[256]; + +extern unsigned long pg_dir[1024]; +extern desc_table idt,gdt; + +#define GDT_NUL 0 +#define GDT_CODE 1 +#define GDT_DATA 2 +#define GDT_TMP 3 + +#define LDT_NUL 0 +#define LDT_CODE 1 +#define LDT_DATA 2 + +#endif diff --git a/linux/include/linux/kernel.h b/linux/include/linux/kernel.h new file mode 100644 index 0000000..9e533a7 --- /dev/null +++ b/linux/include/linux/kernel.h @@ -0,0 +1,8 @@ +/* + * 'kernel.h' contains some often-used function prototypes etc + */ +void verify_area(void * addr,int count); +volatile void panic(const char * str); +int printf(const char * fmt, ...); +int printk(const char * fmt, ...); +int tty_write(unsigned ch,char * buf,int count); diff --git a/linux/include/linux/mm.h b/linux/include/linux/mm.h new file mode 100644 index 0000000..5a160f3 --- /dev/null +++ b/linux/include/linux/mm.h @@ -0,0 +1,10 @@ +#ifndef _MM_H +#define _MM_H + +#define PAGE_SIZE 4096 + +extern unsigned long get_free_page(void); +extern unsigned long put_page(unsigned long page,unsigned long address); +extern void free_page(unsigned long addr); + +#endif diff --git a/linux/include/linux/sched.h b/linux/include/linux/sched.h new file mode 100644 index 0000000..bf6b639 --- /dev/null +++ b/linux/include/linux/sched.h @@ -0,0 +1,230 @@ +#ifndef _SCHED_H +#define _SCHED_H + +#define NR_TASKS 64 +#define HZ 100 + +#define FIRST_TASK task[0] +#define LAST_TASK task[NR_TASKS-1] + +#include <linux/head.h> +#include <linux/fs.h> +#include <linux/mm.h> + +#if (NR_OPEN > 32) +#error "Currently the close-on-exec-flags are in one word, max 32 files/proc" +#endif + +#define TASK_RUNNING 0 +#define TASK_INTERRUPTIBLE 1 +#define TASK_UNINTERRUPTIBLE 2 +#define TASK_ZOMBIE 3 +#define TASK_STOPPED 4 + +#ifndef NULL +#define NULL ((void *) 0) +#endif + +extern int copy_page_tables(unsigned long from, unsigned long to, long size); +extern int free_page_tables(unsigned long from, long size); + +extern void sched_init(void); +extern void schedule(void); +extern void trap_init(void); +extern void panic(const char * str); +extern int tty_write(unsigned minor,char * buf,int count); + +typedef int (*fn_ptr)(); + +struct i387_struct { + long cwd; + long swd; + long twd; + long fip; + long fcs; + long foo; + long fos; + long st_space[20]; /* 8*10 bytes for each FP-reg = 80 bytes */ +}; + +struct tss_struct { + long back_link; /* 16 high bits zero */ + long esp0; + long ss0; /* 16 high bits zero */ + long esp1; + long ss1; /* 16 high bits zero */ + long esp2; + long ss2; /* 16 high bits zero */ + long cr3; + long eip; + long eflags; + long eax,ecx,edx,ebx; + long esp; + long ebp; + long esi; + long edi; + long es; /* 16 high bits zero */ + long cs; /* 16 high bits zero */ + long ss; /* 16 high bits zero */ + long ds; /* 16 high bits zero */ + long fs; /* 16 high bits zero */ + long gs; /* 16 high bits zero */ + long ldt; /* 16 high bits zero */ + long trace_bitmap; /* bits: trace 0, bitmap 16-31 */ + struct i387_struct i387; +}; + +struct task_struct { +/* these are hardcoded - don't touch */ + long state; /* -1 unrunnable, 0 runnable, >0 stopped */ + long counter; + long priority; + long signal; + fn_ptr sig_restorer; + fn_ptr sig_fn[32]; +/* various fields */ + int exit_code; + unsigned long end_code,end_data,brk,start_stack; + long pid,father,pgrp,session,leader; + unsigned short uid,euid,suid; + unsigned short gid,egid,sgid; + long alarm; + long utime,stime,cutime,cstime,start_time; + unsigned short used_math; +/* file system info */ + int tty; /* -1 if no tty, so it must be signed */ + unsigned short umask; + struct m_inode * pwd; + struct m_inode * root; + unsigned long close_on_exec; + struct file * filp[NR_OPEN]; +/* ldt for this task 0 - zero 1 - cs 2 - ds&ss */ + struct desc_struct ldt[3]; +/* tss for this task */ + struct tss_struct tss; +}; + +/* + * INIT_TASK is used to set up the first task table, touch at + * your own risk!. Base=0, limit=0x9ffff (=640kB) + */ +#define INIT_TASK \ +/* state etc */ { 0,15,15, \ +/* signals */ 0,NULL,{(fn_ptr) 0,}, \ +/* ec,brk... */ 0,0,0,0,0, \ +/* pid etc.. */ 0,-1,0,0,0, \ +/* uid etc */ 0,0,0,0,0,0, \ +/* alarm */ 0,0,0,0,0,0, \ +/* math */ 0, \ +/* fs info */ -1,0133,NULL,NULL,0, \ +/* filp */ {NULL,}, \ + { \ + {0,0}, \ +/* ldt */ {0x9f,0xc0fa00}, \ + {0x9f,0xc0f200}, \ + }, \ +/*tss*/ {0,PAGE_SIZE+(long)&init_task,0x10,0,0,0,0,(long)&pg_dir,\ + 0,0,0,0,0,0,0,0, \ + 0,0,0x17,0x17,0x17,0x17,0x17,0x17, \ + _LDT(0),0x80000000, \ + {} \ + }, \ +} + +extern struct task_struct *task[NR_TASKS]; +extern struct task_struct *last_task_used_math; +extern struct task_struct *current; +extern long volatile jiffies; +extern long startup_time; + +#define CURRENT_TIME (startup_time+jiffies/HZ) + +extern void sleep_on(struct task_struct ** p); +extern void interruptible_sleep_on(struct task_struct ** p); +extern void wake_up(struct task_struct ** p); + +/* + * Entry into gdt where to find first TSS. 0-nul, 1-cs, 2-ds, 3-syscall + * 4-TSS0, 5-LDT0, 6-TSS1 etc ... + */ +#define FIRST_TSS_ENTRY 4 +#define FIRST_LDT_ENTRY (FIRST_TSS_ENTRY+1) +#define _TSS(n) ((((unsigned long) n)<<4)+(FIRST_TSS_ENTRY<<3)) +#define _LDT(n) ((((unsigned long) n)<<4)+(FIRST_LDT_ENTRY<<3)) +#define ltr(n) __asm__("ltr %%ax"::"a" (_TSS(n))) +#define lldt(n) __asm__("lldt %%ax"::"a" (_LDT(n))) +#define str(n) \ +__asm__("str %%ax\n\t" \ + "subl %2,%%eax\n\t" \ + "shrl $4,%%eax" \ + :"=a" (n) \ + :"a" (0),"i" (FIRST_TSS_ENTRY<<3)) +/* + * switch_to(n) should switch tasks to task nr n, first + * checking that n isn't the current task, in which case it does nothing. + * This also clears the TS-flag if the task we switched to has used + * tha math co-processor latest. + */ +#define switch_to(n) {\ +struct {long a,b;} __tmp; \ +__asm__("cmpl %%ecx,_current\n\t" \ + "je 1f\n\t" \ + "xchgl %%ecx,_current\n\t" \ + "movw %%dx,%1\n\t" \ + "ljmp %0\n\t" \ + "cmpl %%ecx,%2\n\t" \ + "jne 1f\n\t" \ + "clts\n" \ + "1:" \ + ::"m" (*&__tmp.a),"m" (*&__tmp.b), \ + "m" (last_task_used_math),"d" _TSS(n),"c" ((long) task[n])); \ +} + +#define PAGE_ALIGN(n) (((n)+0xfff)&0xfffff000) + +#define _set_base(addr,base) \ +__asm__("movw %%dx,%0\n\t" \ + "rorl $16,%%edx\n\t" \ + "movb %%dl,%1\n\t" \ + "movb %%dh,%2" \ + ::"m" (*((addr)+2)), \ + "m" (*((addr)+4)), \ + "m" (*((addr)+7)), \ + "d" (base) \ + :"dx") + +#define _set_limit(addr,limit) \ +__asm__("movw %%dx,%0\n\t" \ + "rorl $16,%%edx\n\t" \ + "movb %1,%%dh\n\t" \ + "andb $0xf0,%%dh\n\t" \ + "orb %%dh,%%dl\n\t" \ + "movb %%dl,%1" \ + ::"m" (*(addr)), \ + "m" (*((addr)+6)), \ + "d" (limit) \ + :"dx") + +#define set_base(ldt,base) _set_base( ((char *)&(ldt)) , base ) +#define set_limit(ldt,limit) _set_limit( ((char *)&(ldt)) , (limit-1)>>12 ) + +#define _get_base(addr) ({\ +unsigned long __base; \ +__asm__("movb %3,%%dh\n\t" \ + "movb %2,%%dl\n\t" \ + "shll $16,%%edx\n\t" \ + "movw %1,%%dx" \ + :"=d" (__base) \ + :"m" (*((addr)+2)), \ + "m" (*((addr)+4)), \ + "m" (*((addr)+7))); \ +__base;}) + +#define get_base(ldt) _get_base( ((char *)&(ldt)) ) + +#define get_limit(segment) ({ \ +unsigned long __limit; \ +__asm__("lsll %1,%0\n\tincl %0":"=r" (__limit):"r" (segment)); \ +__limit;}) + +#endif diff --git a/linux/include/linux/sys.h b/linux/include/linux/sys.h new file mode 100644 index 0000000..acdcc95 --- /dev/null +++ b/linux/include/linux/sys.h @@ -0,0 +1,80 @@ +extern int sys_setup(); +extern int sys_exit(); +extern int sys_fork(); +extern int sys_read(); +extern int sys_write(); +extern int sys_open(); +extern int sys_close(); +extern int sys_waitpid(); +extern int sys_creat(); +extern int sys_link(); +extern int sys_unlink(); +extern int sys_execve(); +extern int sys_chdir(); +extern int sys_time(); +extern int sys_mknod(); +extern int sys_chmod(); +extern int sys_chown(); +extern int sys_break(); +extern int sys_stat(); +extern int sys_lseek(); +extern int sys_getpid(); +extern int sys_mount(); +extern int sys_umount(); +extern int sys_setuid(); +extern int sys_getuid(); +extern int sys_stime(); +extern int sys_ptrace(); +extern int sys_alarm(); +extern int sys_fstat(); +extern int sys_pause(); +extern int sys_utime(); +extern int sys_stty(); +extern int sys_gtty(); +extern int sys_access(); +extern int sys_nice(); +extern int sys_ftime(); +extern int sys_sync(); +extern int sys_kill(); +extern int sys_rename(); +extern int sys_mkdir(); +extern int sys_rmdir(); +extern int sys_dup(); +extern int sys_pipe(); +extern int sys_times(); +extern int sys_prof(); +extern int sys_brk(); +extern int sys_setgid(); +extern int sys_getgid(); +extern int sys_signal(); +extern int sys_geteuid(); +extern int sys_getegid(); +extern int sys_acct(); +extern int sys_phys(); +extern int sys_lock(); +extern int sys_ioctl(); +extern int sys_fcntl(); +extern int sys_mpx(); +extern int sys_setpgid(); +extern int sys_ulimit(); +extern int sys_uname(); +extern int sys_umask(); +extern int sys_chroot(); +extern int sys_ustat(); +extern int sys_dup2(); +extern int sys_getppid(); +extern int sys_getpgrp(); +extern int sys_setsid(); + +fn_ptr sys_call_table[] = { sys_setup, sys_exit, sys_fork, sys_read, +sys_write, sys_open, sys_close, sys_waitpid, sys_creat, sys_link, +sys_unlink, sys_execve, sys_chdir, sys_time, sys_mknod, sys_chmod, +sys_chown, sys_break, sys_stat, sys_lseek, sys_getpid, sys_mount, +sys_umount, sys_setuid, sys_getuid, sys_stime, sys_ptrace, sys_alarm, +sys_fstat, sys_pause, sys_utime, sys_stty, sys_gtty, sys_access, +sys_nice, sys_ftime, sys_sync, sys_kill, sys_rename, sys_mkdir, +sys_rmdir, sys_dup, sys_pipe, sys_times, sys_prof, sys_brk, sys_setgid, +sys_getgid, sys_signal, sys_geteuid, sys_getegid, sys_acct, sys_phys, +sys_lock, sys_ioctl, sys_fcntl, sys_mpx, sys_setpgid, sys_ulimit, +sys_uname, sys_umask, sys_chroot, sys_ustat, sys_dup2, sys_getppid, +sys_getpgrp,sys_setsid}; diff --git a/linux/include/linux/tty.h b/linux/include/linux/tty.h new file mode 100644 index 0000000..1d103e0 --- /dev/null +++ b/linux/include/linux/tty.h @@ -0,0 +1,74 @@ +/* + * 'tty.h' defines some structures used by tty_io.c and some defines. + * + * NOTE! Don't touch this without checking that nothing in rs_io.s or + * con_io.s breaks. Some constants are hardwired into the system (mainly + * offsets into 'tty_queue' + */ + +#ifndef _TTY_H +#define _TTY_H + +#include <termios.h> + +#define TTY_BUF_SIZE 1024 + +struct tty_queue { + unsigned long data; + unsigned long head; + unsigned long tail; + struct task_struct * proc_list; + char buf[TTY_BUF_SIZE]; +}; + +#define INC(a) ((a) = ((a)+1) & (TTY_BUF_SIZE-1)) +#define DEC(a) ((a) = ((a)-1) & (TTY_BUF_SIZE-1)) +#define EMPTY(a) ((a).head == (a).tail) +#define LEFT(a) (((a).tail-(a).head-1)&(TTY_BUF_SIZE-1)) +#define LAST(a) ((a).buf[(TTY_BUF_SIZE-1)&((a).head-1)]) +#define FULL(a) (!LEFT(a)) +#define CHARS(a) (((a).head-(a).tail)&(TTY_BUF_SIZE-1)) +#define GETCH(queue,c) \ +(void)({c=(queue).buf[(queue).tail];INC((queue).tail);}) +#define PUTCH(c,queue) \ +(void)({(queue).buf[(queue).head]=(c);INC((queue).head);}) + +#define EOF_CHAR(tty) ((tty)->termios.c_cc[VEOF]) +#define INTR_CHAR(tty) ((tty)->termios.c_cc[VINTR]) +#define STOP_CHAR(tty) ((tty)->termios.c_cc[VSTOP]) +#define START_CHAR(tty) ((tty)->termios.c_cc[VSTART]) +#define ERASE_CHAR(tty) ((tty)->termios.c_cc[VERASE]) + +struct tty_struct { + struct termios termios; + int pgrp; + int stopped; + void (*write)(struct tty_struct * tty); + struct tty_queue read_q; + struct tty_queue write_q; + struct tty_queue secondary; + }; + +extern struct tty_struct tty_table[]; + +/* intr=^C quit=^| erase=del kill=^U + eof=^D vtime=\0 vmin=\1 sxtc=\0 + start=^Q stop=^S susp=^Y eol=\0 + reprint=^R discard=^U werase=^W lnext=^V + eol2=\0 +*/ +#define INIT_C_CC "\003\034\177\025\004\0\1\0\021\023\031\0\022\017\027\026\0" + +void rs_init(void); +void con_init(void); +void tty_init(void); + +int tty_read(unsigned c, char * buf, int n); +int tty_write(unsigned c, char * buf, int n); + +void rs_write(struct tty_struct * tty); +void con_write(struct tty_struct * tty); + +void copy_to_cooked(struct tty_struct * tty); + +#endif diff --git a/linux/include/signal.h b/linux/include/signal.h new file mode 100644 index 0000000..b895813 --- /dev/null +++ b/linux/include/signal.h @@ -0,0 +1,65 @@ +#ifndef _SIGNAL_H +#define _SIGNAL_H + +#include <sys/types.h> + +typedef int sig_atomic_t; +typedef unsigned int sigset_t; /* 32 bits */ + +#define _NSIG 32 +#define NSIG _NSIG + +#define SIGHUP 1 +#define SIGINT 2 +#define SIGQUIT 3 +#define SIGILL 4 +#define SIGTRAP 5 +#define SIGABRT 6 +#define SIGIOT 6 +#define SIGUNUSED 7 +#define SIGFPE 8 +#define SIGKILL 9 +#define SIGUSR1 10 +#define SIGSEGV 11 +#define SIGUSR2 12 +#define SIGPIPE 13 +#define SIGALRM 14 +#define SIGTERM 15 +#define SIGSTKFLT 16 +#define SIGCHLD 17 +#define SIGCONT 18 +#define SIGSTOP 19 +#define SIGTSTP 20 +#define SIGTTIN 21 +#define SIGTTOU 22 + +/* Ok, I haven't implemented sigactions, but trying to keep headers POSIX */ +#define SA_NOCLDSTOP 1 + +#define SIG_BLOCK 0 /* for blocking signals */ +#define SIG_UNBLOCK 1 /* for unblocking signals */ +#define SIG_SETMASK 2 /* for setting the signal mask */ + +#define SIG_DFL ((void (*)(int))0) /* default signal handling */ +#define SIG_IGN ((void (*)(int))1) /* ignore signal */ + +struct sigaction { + void (*sa_handler)(int); + sigset_t sa_mask; + int sa_flags; +}; + +void (*signal(int _sig, void (*_func)(int)))(int); +int raise(int sig); +int kill(pid_t pid, int sig); +int sigaddset(sigset_t *mask, int signo); +int sigdelset(sigset_t *mask, int signo); +int sigemptyset(sigset_t *mask); +int sigfillset(sigset_t *mask); +int sigismember(sigset_t *mask, int signo); /* 1 - is, 0 - not, -1 error */ +int sigpending(sigset_t *set); +int sigprocmask(int how, sigset_t *set, sigset_t *oldset); +int sigsuspend(sigset_t *sigmask); +int sigaction(int sig, struct sigaction *act, struct sigaction *oldact); + +#endif /* _SIGNAL_H */ diff --git a/linux/include/stdarg.h b/linux/include/stdarg.h new file mode 100755 index 0000000..fd79ec0 --- /dev/null +++ b/linux/include/stdarg.h @@ -0,0 +1,28 @@ +#ifndef _STDARG_H +#define _STDARG_H + +typedef char *va_list; + +/* Amount of space required in an argument list for an arg of type TYPE. + TYPE may alternatively be an expression whose type is used. */ + +#define __va_rounded_size(TYPE) \ + (((sizeof (TYPE) + sizeof (int) - 1) / sizeof (int)) * sizeof (int)) + +#ifndef __sparc__ +#define va_start(AP, LASTARG) \ + (AP = ((char *) &(LASTARG) + __va_rounded_size (LASTARG))) +#else +#define va_start(AP, LASTARG) \ + (__builtin_saveregs (), \ + AP = ((char *) &(LASTARG) + __va_rounded_size (LASTARG))) +#endif + +void va_end (va_list); /* Defined in gnulib */ +#define va_end(AP) + +#define va_arg(AP, TYPE) \ + (AP += __va_rounded_size (TYPE), \ + *((TYPE *) (AP - __va_rounded_size (TYPE)))) + +#endif /* _STDARG_H */ diff --git a/linux/include/stddef.h b/linux/include/stddef.h new file mode 100644 index 0000000..97f72ff --- /dev/null +++ b/linux/include/stddef.h @@ -0,0 +1,19 @@ +#ifndef _STDDEF_H +#define _STDDEF_H + +#ifndef _PTRDIFF_T +#define _PTRDIFF_T +typedef long ptrdiff_t; +#endif + +#ifndef _SIZE_T +#define _SIZE_T +typedef unsigned long size_t; +#endif + +#undef NULL +#define NULL ((void *)0) + +#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) + +#endif diff --git a/linux/include/string.h b/linux/include/string.h new file mode 100644 index 0000000..05a6d9c --- /dev/null +++ b/linux/include/string.h @@ -0,0 +1,405 @@ +#ifndef _STRING_H_ +#define _STRING_H_ + +#ifndef NULL +#define NULL ((void *) 0) +#endif + +#ifndef _SIZE_T +#define _SIZE_T +typedef unsigned int size_t; +#endif + +extern char * strerror(int errno); + +/* + * This string-include defines all string functions as inline + * functions. Use gcc. It also assumes ds=es=data space, this should be + * normal. Most of the string-functions are rather heavily hand-optimized, + * see especially strtok,strstr,str[c]spn. They should work, but are not + * very easy to understand. Everything is done entirely within the register + * set, making the functions fast and clean. String instructions have been + * used through-out, making for "slightly" unclear code :-) + * + * (C) 1991 Linus Torvalds + */ + +extern inline char * strcpy(char * dest,const char *src) +{ +__asm__("cld\n" + "1:\tlodsb\n\t" + "stosb\n\t" + "testb %%al,%%al\n\t" + "jne 1b" + ::"S" (src),"D" (dest):"si","di","ax"); +return dest; +} + +extern inline char * strncpy(char * dest,const char *src,int count) +{ +__asm__("cld\n" + "1:\tdecl %2\n\t" + "js 2f\n\t" + "lodsb\n\t" + "stosb\n\t" + "testb %%al,%%al\n\t" + "jne 1b\n\t" + "rep\n\t" + "stosb\n" + "2:" + ::"S" (src),"D" (dest),"c" (count):"si","di","ax","cx"); +return dest; +} + +extern inline char * strcat(char * dest,const char * src) +{ +__asm__("cld\n\t" + "repne\n\t" + "scasb\n\t" + "decl %1\n" + "1:\tlodsb\n\t" + "stosb\n\t" + "testb %%al,%%al\n\t" + "jne 1b" + ::"S" (src),"D" (dest),"a" (0),"c" (0xffffffff):"si","di","ax","cx"); +return dest; +} + +extern inline char * strncat(char * dest,const char * src,int count) +{ +__asm__("cld\n\t" + "repne\n\t" + "scasb\n\t" + "decl %1\n\t" + "movl %4,%3\n" + "1:\tdecl %3\n\t" + "js 2f\n\t" + "lodsb\n\t" + "stosb\n\t" + "testb %%al,%%al\n\t" + "jne 1b\n" + "2:\txorl %2,%2\n\t" + "stosb" + ::"S" (src),"D" (dest),"a" (0),"c" (0xffffffff),"g" (count) + :"si","di","ax","cx"); +return dest; +} + +extern inline int strcmp(const char * cs,const char * ct) +{ +register int __res __asm__("ax"); +__asm__("cld\n" + "1:\tlodsb\n\t" + "scasb\n\t" + "jne 2f\n\t" + "testb %%al,%%al\n\t" + "jne 1b\n\t" + "xorl %%eax,%%eax\n\t" + "jmp 3f\n" + "2:\tmovl $1,%%eax\n\t" + "jl 3f\n\t" + "negl %%eax\n" + "3:" + :"=a" (__res):"D" (cs),"S" (ct):"si","di"); +return __res; +} + +extern inline int strncmp(const char * cs,const char * ct,int count) +{ +register int __res __asm__("ax"); +__asm__("cld\n" + "1:\tdecl %3\n\t" + "js 2f\n\t" + "lodsb\n\t" + "scasb\n\t" + "jne 3f\n\t" + "testb %%al,%%al\n\t" + "jne 1b\n" + "2:\txorl %%eax,%%eax\n\t" + "jmp 4f\n" + "3:\tmovl $1,%%eax\n\t" + "jl 4f\n\t" + "negl %%eax\n" + "4:" + :"=a" (__res):"D" (cs),"S" (ct),"c" (count):"si","di","cx"); +return __res; +} + +extern inline char * strchr(const char * s,char c) +{ +register char * __res __asm__("ax"); +__asm__("cld\n\t" + "movb %%al,%%ah\n" + "1:\tlodsb\n\t" + "cmpb %%ah,%%al\n\t" + "je 2f\n\t" + "testb %%al,%%al\n\t" + "jne 1b\n\t" + "movl $1,%1\n" + "2:\tmovl %1,%0\n\t" + "decl %0" + :"=a" (__res):"S" (s),"0" (c):"si"); +return __res; +} + +extern inline char * strrchr(const char * s,char c) +{ +register char * __res __asm__("dx"); +__asm__("cld\n\t" + "movb %%al,%%ah\n" + "1:\tlodsb\n\t" + "cmpb %%ah,%%al\n\t" + "jne 2f\n\t" + "movl %%esi,%0\n\t" + "decl %0\n" + "2:\ttestb %%al,%%al\n\t" + "jne 1b" + :"=d" (__res):"0" (0),"S" (s),"a" (c):"ax","si"); +return __res; +} + +extern inline int strspn(const char * cs, const char * ct) +{ +register char * __res __asm__("si"); +__asm__("cld\n\t" + "movl %4,%%edi\n\t" + "repne\n\t" + "scasb\n\t" + "notl %%ecx\n\t" + "decl %%ecx\n\t" + "movl %%ecx,%%edx\n" + "1:\tlodsb\n\t" + "testb %%al,%%al\n\t" + "je 2f\n\t" + "movl %4,%%edi\n\t" + "movl %%edx,%%ecx\n\t" + "repne\n\t" + "scasb\n\t" + "je 1b\n" + "2:\tdecl %0" + :"=S" (__res):"a" (0),"c" (0xffffffff),"0" (cs),"g" (ct) + :"ax","cx","dx","di"); +return __res-cs; +} + +extern inline int strcspn(const char * cs, const char * ct) +{ +register char * __res __asm__("si"); +__asm__("cld\n\t" + "movl %4,%%edi\n\t" + "repne\n\t" + "scasb\n\t" + "notl %%ecx\n\t" + "decl %%ecx\n\t" + "movl %%ecx,%%edx\n" + "1:\tlodsb\n\t" + "testb %%al,%%al\n\t" + "je 2f\n\t" + "movl %4,%%edi\n\t" + "movl %%edx,%%ecx\n\t" + "repne\n\t" + "scasb\n\t" + "jne 1b\n" + "2:\tdecl %0" + :"=S" (__res):"a" (0),"c" (0xffffffff),"0" (cs),"g" (ct) + :"ax","cx","dx","di"); +return __res-cs; +} + +extern inline char * strpbrk(const char * cs,const char * ct) +{ +register char * __res __asm__("si"); +__asm__("cld\n\t" + "movl %4,%%edi\n\t" + "repne\n\t" + "scasb\n\t" + "notl %%ecx\n\t" + "decl %%ecx\n\t" + "movl %%ecx,%%edx\n" + "1:\tlodsb\n\t" + "testb %%al,%%al\n\t" + "je 2f\n\t" + "movl %4,%%edi\n\t" + "movl %%edx,%%ecx\n\t" + "repne\n\t" + "scasb\n\t" + "jne 1b\n\t" + "decl %0\n\t" + "jmp 3f\n" + "2:\txorl %0,%0\n" + "3:" + :"=S" (__res):"a" (0),"c" (0xffffffff),"0" (cs),"g" (ct) + :"ax","cx","dx","di"); +return __res; +} + +extern inline char * strstr(const char * cs,const char * ct) +{ +register char * __res __asm__("ax"); +__asm__("cld\n\t" \ + "movl %4,%%edi\n\t" + "repne\n\t" + "scasb\n\t" + "notl %%ecx\n\t" + "decl %%ecx\n\t" /* NOTE! This also sets Z if searchstring='' */ + "movl %%ecx,%%edx\n" + "1:\tmovl %4,%%edi\n\t" + "movl %%esi,%%eax\n\t" + "movl %%edx,%%ecx\n\t" + "repe\n\t" + "cmpsb\n\t" + "je 2f\n\t" /* also works for empty string, see above */ + "xchgl %%eax,%%esi\n\t" + "incl %%esi\n\t" + "cmpb $0,-1(%%eax)\n\t" + "jne 1b\n\t" + "xorl %%eax,%%eax\n\t" + "2:" + :"=a" (__res):"0" (0),"c" (0xffffffff),"S" (cs),"g" (ct) + :"cx","dx","di","si"); +return __res; +} + +extern inline int strlen(const char * s) +{ +register int __res __asm__("cx"); +__asm__("cld\n\t" + "repne\n\t" + "scasb\n\t" + "notl %0\n\t" + "decl %0" + :"=c" (__res):"D" (s),"a" (0),"0" (0xffffffff):"di"); +return __res; +} + +extern char * ___strtok; + +extern inline char * strtok(char * s,const char * ct) +{ +register char * __res __asm__("si"); +__asm__("testl %1,%1\n\t" + "jne 1f\n\t" + "testl %0,%0\n\t" + "je 8f\n\t" + "movl %0,%1\n" + "1:\txorl %0,%0\n\t" + "movl $-1,%%ecx\n\t" + "xorl %%eax,%%eax\n\t" + "cld\n\t" + "movl %4,%%edi\n\t" + "repne\n\t" + "scasb\n\t" + "notl %%ecx\n\t" + "decl %%ecx\n\t" + "je 7f\n\t" /* empty delimeter-string */ + "movl %%ecx,%%edx\n" + "2:\tlodsb\n\t" + "testb %%al,%%al\n\t" + "je 7f\n\t" + "movl %4,%%edi\n\t" + "movl %%edx,%%ecx\n\t" + "repne\n\t" + "scasb\n\t" + "je 2b\n\t" + "decl %1\n\t" + "cmpb $0,(%1)\n\t" + "je 7f\n\t" + "movl %1,%0\n" + "3:\tlodsb\n\t" + "testb %%al,%%al\n\t" + "je 5f\n\t" + "movl %4,%%edi\n\t" + "movl %%edx,%%ecx\n\t" + "repne\n\t" + "scasb\n\t" + "jne 3b\n\t" + "decl %1\n\t" + "cmpb $0,(%1)\n\t" + "je 5f\n\t" + "movb $0,(%1)\n\t" + "incl %1\n\t" + "jmp 6f\n" + "5:\txorl %1,%1\n" + "6:\tcmpb $0,(%0)\n\t" + "jne 7f\n\t" + "xorl %0,%0\n" + "7:\ttestl %0,%0\n\t" + "jne 8f\n\t" + "movl %0,%1\n" + "8:" + :"=b" (__res),"=S" (___strtok) + :"0" (___strtok),"1" (s),"g" (ct) + :"ax","cx","dx","di"); +return __res; +} + +extern inline void * memcpy(void * dest,const void * src, int n) +{ +__asm__("cld\n\t" + "rep\n\t" + "movsb" + ::"c" (n),"S" (src),"D" (dest) + :"cx","si","di"); +return dest; +} + +extern inline void * memmove(void * dest,const void * src, int n) +{ +if (dest<src) +__asm__("cld\n\t" + "rep\n\t" + "movsb" + ::"c" (n),"S" (src),"D" (dest) + :"cx","si","di"); +else +__asm__("std\n\t" + "rep\n\t" + "movsb" + ::"c" (n),"S" (src+n-1),"D" (dest+n-1) + :"cx","si","di"); +return dest; +} + +extern inline int memcmp(const void * cs,const void * ct,int count) +{ +register int __res __asm__("ax"); +__asm__("cld\n\t" + "repe\n\t" + "cmpsb\n\t" + "je 1f\n\t" + "movl $1,%%eax\n\t" + "jl 1f\n\t" + "negl %%eax\n" + "1:" + :"=a" (__res):"0" (0),"D" (cs),"S" (ct),"c" (count) + :"si","di","cx"); +return __res; +} + +extern inline void * memchr(const void * cs,char c,int count) +{ +register void * __res __asm__("di"); +if (!count) + return NULL; +__asm__("cld\n\t" + "repne\n\t" + "scasb\n\t" + "je 1f\n\t" + "movl $1,%0\n" + "1:\tdecl %0" + :"=D" (__res):"a" (c),"D" (cs),"c" (count) + :"cx"); +return __res; +} + +extern inline void * memset(void * s,char c,int count) +{ +__asm__("cld\n\t" + "rep\n\t" + "stosb" + ::"a" (c),"D" (s),"c" (count) + :"cx","di"); +return s; +} + +#endif diff --git a/linux/include/sys/stat.h b/linux/include/sys/stat.h new file mode 100644 index 0000000..41c3840 --- /dev/null +++ b/linux/include/sys/stat.h @@ -0,0 +1,58 @@ +#ifndef _SYS_STAT_H +#define _SYS_STAT_H + +#include <sys/types.h> + +struct stat { + dev_t st_dev; + ino_t st_ino; + umode_t st_mode; + nlink_t st_nlink; + uid_t st_uid; + gid_t st_gid; + dev_t st_rdev; + off_t st_size; + time_t st_atime; + time_t st_mtime; + time_t st_ctime; +}; + +#define S_IFMT 00170000 +#define S_IFREG 0100000 +#define S_IFBLK 0060000 +#define S_IFDIR 0040000 +#define S_IFCHR 0020000 +#define S_IFIFO 0010000 +#define S_ISUID 0004000 +#define S_ISGID 0002000 +#define S_ISVTX 0001000 + +#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) +#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) +#define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR) +#define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK) +#define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO) + +#define S_IRWXU 00700 +#define S_IRUSR 00400 +#define S_IWUSR 00200 +#define S_IXUSR 00100 + +#define S_IRWXG 00070 +#define S_IRGRP 00040 +#define S_IWGRP 00020 +#define S_IXGRP 00010 + +#define S_IRWXO 00007 +#define S_IROTH 00004 +#define S_IWOTH 00002 +#define S_IXOTH 00001 + +extern int chmod(const char *_path, mode_t mode); +extern int fstat(int fildes, struct stat *stat_buf); +extern int mkdir(const char *_path, mode_t mode); +extern int mkfifo(const char *_path, mode_t mode); +extern int stat(const char *filename, struct stat *stat_buf); +extern mode_t umask(mode_t mask); + +#endif diff --git a/linux/include/sys/times.h b/linux/include/sys/times.h new file mode 100644 index 0000000..68d5bfb --- /dev/null +++ b/linux/include/sys/times.h @@ -0,0 +1,15 @@ +#ifndef _TIMES_H +#define _TIMES_H + +#include <sys/types.h> + +struct tms { + time_t tms_utime; + time_t tms_stime; + time_t tms_cutime; + time_t tms_cstime; +}; + +extern time_t times(struct tms * tp); + +#endif diff --git a/linux/include/sys/types.h b/linux/include/sys/types.h new file mode 100644 index 0000000..557aa31 --- /dev/null +++ b/linux/include/sys/types.h @@ -0,0 +1,46 @@ +#ifndef _SYS_TYPES_H +#define _SYS_TYPES_H + +#ifndef _SIZE_T +#define _SIZE_T +typedef unsigned int size_t; +#endif + +#ifndef _TIME_T +#define _TIME_T +typedef long time_t; +#endif + +#ifndef _PTRDIFF_T +#define _PTRDIFF_T +typedef long ptrdiff_t; +#endif + +#ifndef NULL +#define NULL ((void *) 0) +#endif + +typedef int pid_t; +typedef unsigned short uid_t; +typedef unsigned char gid_t; +typedef unsigned short dev_t; +typedef unsigned short ino_t; +typedef unsigned short mode_t; +typedef unsigned short umode_t; +typedef unsigned char nlink_t; +typedef int daddr_t; +typedef long off_t; +typedef unsigned char u_char; +typedef unsigned short ushort; + +typedef struct { int quot,rem; } div_t; +typedef struct { long quot,rem; } ldiv_t; + +struct ustat { + daddr_t f_tfree; + ino_t f_tinode; + char f_fname[6]; + char f_fpack[6]; +}; + +#endif diff --git a/linux/include/sys/utsname.h b/linux/include/sys/utsname.h new file mode 100644 index 0000000..0a1c5a0 --- /dev/null +++ b/linux/include/sys/utsname.h @@ -0,0 +1,16 @@ +#ifndef _SYS_UTSNAME_H +#define _SYS_UTSNAME_H + +#include <sys/types.h> + +struct utsname { + char sysname[9]; + char nodename[9]; + char release[9]; + char version[9]; + char machine[9]; +}; + +extern int uname(struct utsname * utsbuf); + +#endif diff --git a/linux/include/sys/wait.h b/linux/include/sys/wait.h new file mode 100644 index 0000000..53190c2 --- /dev/null +++ b/linux/include/sys/wait.h @@ -0,0 +1,23 @@ +#ifndef _SYS_WAIT_H +#define _SYS_WAIT_H + +#include <sys/types.h> + +#define _LOW(v) ( (v) & 0377) +#define _HIGH(v) ( ((v) >> 8) & 0377) + +/* options for waitpid, WUNTRACED not supported */ +#define WNOHANG 1 +#define WUNTRACED 2 + +#define WIFEXITED(s) (!((s)&0xFF) +#define WIFSTOPPED(s) (((s)&0xFF)==0x7F) +#define WEXITSTATUS(s) (((s)>>8)&0xFF) +#define WTERMSIG(s) ((s)&0x7F) +#define WSTOPSIG(s) (((s)>>8)&0xFF) +#define WIFSIGNALED(s) (((unsigned int)(s)-1 & 0xFFFF) < 0xFF) + +pid_t wait(int *stat_loc); +pid_t waitpid(pid_t pid, int *stat_loc, int options); + +#endif diff --git a/linux/include/termios.h b/linux/include/termios.h new file mode 100644 index 0000000..f707674 --- /dev/null +++ b/linux/include/termios.h @@ -0,0 +1,222 @@ +#ifndef _TERMIOS_H +#define _TERMIOS_H + +#define TTY_BUF_SIZE 1024 + +/* 0x54 is just a magic number to make these relatively uniqe ('T') */ + +#define TCGETS 0x5401 +#define TCSETS 0x5402 +#define TCSETSW 0x5403 +#define TCSETSF 0x5404 +#define TCGETA 0x5405 +#define TCSETA 0x5406 +#define TCSETAW 0x5407 +#define TCSETAF 0x5408 +#define TCSBRK 0x5409 +#define TCXONC 0x540A +#define TCFLSH 0x540B +#define TIOCEXCL 0x540C +#define TIOCNXCL 0x540D +#define TIOCSCTTY 0x540E +#define TIOCGPGRP 0x540F +#define TIOCSPGRP 0x5410 +#define TIOCOUTQ 0x5411 +#define TIOCSTI 0x5412 +#define TIOCGWINSZ 0x5413 +#define TIOCSWINSZ 0x5414 +#define TIOCMGET 0x5415 +#define TIOCMBIS 0x5416 +#define TIOCMBIC 0x5417 +#define TIOCMSET 0x5418 +#define TIOCGSOFTCAR 0x5419 +#define TIOCSSOFTCAR 0x541A + +struct winsize { + unsigned short ws_row; + unsigned short ws_col; + unsigned short ws_xpixel; + unsigned short ws_ypixel; +}; + +#define NCC 8 +struct termio { + unsigned short c_iflag; /* input mode flags */ + unsigned short c_oflag; /* output mode flags */ + unsigned short c_cflag; /* control mode flags */ + unsigned short c_lflag; /* local mode flags */ + unsigned char c_line; /* line discipline */ + unsigned char c_cc[NCC]; /* control characters */ +}; + +#define NCCS 17 +struct termios { + unsigned long c_iflag; /* input mode flags */ + unsigned long c_oflag; /* output mode flags */ + unsigned long c_cflag; /* control mode flags */ + unsigned long c_lflag; /* local mode flags */ + unsigned char c_line; /* line discipline */ + unsigned char c_cc[NCCS]; /* control characters */ +}; + +/* c_cc characters */ +#define VINTR 0 +#define VQUIT 1 +#define VERASE 2 +#define VKILL 3 +#define VEOF 4 +#define VTIME 5 +#define VMIN 6 +#define VSWTC 7 +#define VSTART 8 +#define VSTOP 9 +#define VSUSP 10 +#define VEOL 11 +#define VREPRINT 12 +#define VDISCARD 13 +#define VWERASE 14 +#define VLNEXT 15 +#define VEOL2 16 + +/* c_iflag bits */ +#define IGNBRK 0000001 +#define BRKINT 0000002 +#define IGNPAR 0000004 +#define PARMRK 0000010 +#define INPCK 0000020 +#define ISTRIP 0000040 +#define INLCR 0000100 +#define IGNCR 0000200 +#define ICRNL 0000400 +#define IUCLC 0001000 +#define IXON 0002000 +#define IXANY 0004000 +#define IXOFF 0010000 +#define IMAXBEL 0020000 + +/* c_oflag bits */ +#define OPOST 0000001 +#define OLCUC 0000002 +#define ONLCR 0000004 +#define OCRNL 0000010 +#define ONOCR 0000020 +#define ONLRET 0000040 +#define OFILL 0000100 +#define OFDEL 0000200 +#define NLDLY 0000400 +#define NL0 0000000 +#define NL1 0000400 +#define CRDLY 0003000 +#define CR0 0000000 +#define CR1 0001000 +#define CR2 0002000 +#define CR3 0003000 +#define TABDLY 0014000 +#define TAB0 0000000 +#define TAB1 0004000 +#define TAB2 0010000 +#define TAB3 0014000 +#define XTABS 0014000 +#define BSDLY 0020000 +#define BS0 0000000 +#define BS1 0020000 +#define VTDLY 0040000 +#define VT0 0000000 +#define VT1 0040000 +#define FFDLY 0040000 +#define FF0 0000000 +#define FF1 0040000 + +/* c_cflag bit meaning */ +#define CBAUD 0000017 +#define B0 0000000 /* hang up */ +#define B50 0000001 +#define B75 0000002 +#define B110 0000003 +#define B134 0000004 +#define B150 0000005 +#define B200 0000006 +#define B300 0000007 +#define B600 0000010 +#define B1200 0000011 +#define B1800 0000012 +#define B2400 0000013 +#define B4800 0000014 +#define B9600 0000015 +#define B19200 0000016 +#define B38400 0000017 +#define CSIZE 0000060 +#define CS5 0000000 +#define CS6 0000020 +#define CS7 0000040 +#define CS8 0000060 +#define CSTOPB 0000100 +#define CREAD 0000200 +#define CPARENB 0000400 +#define CPARODD 0001000 +#define HUPCL 0002000 +#define CLOCAL 0004000 +#define CIBAUD 03600000 /* input baud rate (not used) */ +#define CRTSCTS 020000000000 /* flow control */ + +/* c_lflag bits */ +#define ISIG 0000001 +#define ICANON 0000002 +#define XCASE 0000004 +#define ECHO 0000010 +#define ECHOE 0000020 +#define ECHOK 0000040 +#define ECHONL 0000100 +#define NOFLSH 0000200 +#define TOSTOP 0000400 +#define ECHOCTL 0001000 +#define ECHOPRT 0002000 +#define ECHOKE 0004000 +#define FLUSHO 0010000 +#define PENDIN 0040000 +#define IEXTEN 0100000 + +/* modem lines */ +#define TIOCM_LE 0x001 +#define TIOCM_DTR 0x002 +#define TIOCM_RTS 0x004 +#define TIOCM_ST 0x008 +#define TIOCM_SR 0x010 +#define TIOCM_CTS 0x020 +#define TIOCM_CAR 0x040 +#define TIOCM_RNG 0x080 +#define TIOCM_DSR 0x100 +#define TIOCM_CD TIOCM_CAR +#define TIOCM_RI TIOCM_RNG + +/* tcflow() and TCXONC use these */ +#define TCOOFF 0 +#define TCOON 1 +#define TCIOFF 2 +#define TCION 3 + +/* tcflush() and TCFLSH use these */ +#define TCIFLUSH 0 +#define TCOFLUSH 1 +#define TCIOFLUSH 2 + +/* tcsetattr uses these */ +#define TCSANOW 0 +#define TCSADRAIN 1 +#define TCSAFLUSH 2 + +typedef int speed_t; + +extern speed_t cfgetispeed(struct termios *termios_p); +extern speed_t cfgetospeed(struct termios *termios_p); +extern int cfsetispeed(struct termios *termios_p, speed_t speed); +extern int cfsetospeed(struct termios *termios_p, speed_t speed); +extern int tcdrain(int fildes); +extern int tcflow(int fildes, int action); +extern int tcflush(int fildes, int queue_selector); +extern int tcgetattr(int fildes, struct termios *termios_p); +extern int tcsendbreak(int fildes, int duration); +extern int tcsetattr(int fildes, int optional_actions, + struct termios *termios_p); + +#endif diff --git a/linux/include/time.h b/linux/include/time.h new file mode 100644 index 0000000..d0a765d --- /dev/null +++ b/linux/include/time.h @@ -0,0 +1,42 @@ +#ifndef _TIME_H +#define _TIME_H + +#ifndef _TIME_T +#define _TIME_T +typedef long time_t; +#endif + +#ifndef _SIZE_T +#define _SIZE_T +typedef unsigned int size_t; +#endif + +#define CLOCKS_PER_SEC 100 + +typedef long clock_t; + +struct tm { + int tm_sec; + int tm_min; + int tm_hour; + int tm_mday; + int tm_mon; + int tm_year; + int tm_wday; + int tm_yday; + int tm_isdst; +}; + +clock_t clock(void); +time_t time(time_t * tp); +double difftime(time_t time2, time_t time1); +time_t mktime(struct tm * tp); + +char * asctime(const struct tm * tp); +char * ctime(const time_t * tp); +struct tm * gmtime(const time_t *tp); +struct tm *localtime(const time_t * tp); +size_t strftime(char * s, size_t smax, const char * fmt, const struct tm * tp); +void tzset(void); + +#endif diff --git a/linux/include/unistd.h b/linux/include/unistd.h new file mode 100644 index 0000000..eb10771 --- /dev/null +++ b/linux/include/unistd.h @@ -0,0 +1,247 @@ +#ifndef _UNISTD_H +#define _UNISTD_H + +/* ok, this may be a joke, but I'm working on it */ +#define _POSIX_VERSION 198808L + +#define _POSIX_CHOWN_RESTRICTED /* only root can do a chown (I think..) */ +/* #define _POSIX_NO_TRUNC*/ /* pathname truncation (but see in kernel) */ +#define _POSIX_VDISABLE '\0' /* character to disable things like ^C */ +/*#define _POSIX_SAVED_IDS */ /* we'll get to this yet */ +/*#define _POSIX_JOB_CONTROL */ /* we aren't there quite yet. Soon hopefully */ + +#define STDIN_FILENO 0 +#define STDOUT_FILENO 1 +#define STDERR_FILENO 2 + +#ifndef NULL +#define NULL ((void *)0) +#endif + +/* access */ +#define F_OK 0 +#define X_OK 1 +#define W_OK 2 +#define R_OK 4 + +/* lseek */ +#define SEEK_SET 0 +#define SEEK_CUR 1 +#define SEEK_END 2 + +/* _SC stands for System Configuration. We don't use them much */ +#define _SC_ARG_MAX 1 +#define _SC_CHILD_MAX 2 +#define _SC_CLOCKS_PER_SEC 3 +#define _SC_NGROUPS_MAX 4 +#define _SC_OPEN_MAX 5 +#define _SC_JOB_CONTROL 6 +#define _SC_SAVED_IDS 7 +#define _SC_VERSION 8 + +/* more (possibly) configurable things - now pathnames */ +#define _PC_LINK_MAX 1 +#define _PC_MAX_CANON 2 +#define _PC_MAX_INPUT 3 +#define _PC_NAME_MAX 4 +#define _PC_PATH_MAX 5 +#define _PC_PIPE_BUF 6 +#define _PC_NO_TRUNC 7 +#define _PC_VDISABLE 8 +#define _PC_CHOWN_RESTRICTED 9 + +#include <sys/stat.h> +#include <sys/times.h> +#include <sys/utsname.h> +#include <utime.h> + +#ifdef __LIBRARY__ + +#define __NR_setup 0 /* used only by init, to get system going */ +#define __NR_exit 1 +#define __NR_fork 2 +#define __NR_read 3 +#define __NR_write 4 +#define __NR_open 5 +#define __NR_close 6 +#define __NR_waitpid 7 +#define __NR_creat 8 +#define __NR_link 9 +#define __NR_unlink 10 +#define __NR_execve 11 +#define __NR_chdir 12 +#define __NR_time 13 +#define __NR_mknod 14 +#define __NR_chmod 15 +#define __NR_chown 16 +#define __NR_break 17 +#define __NR_stat 18 +#define __NR_lseek 19 +#define __NR_getpid 20 +#define __NR_mount 21 +#define __NR_umount 22 +#define __NR_setuid 23 +#define __NR_getuid 24 +#define __NR_stime 25 +#define __NR_ptrace 26 +#define __NR_alarm 27 +#define __NR_fstat 28 +#define __NR_pause 29 +#define __NR_utime 30 +#define __NR_stty 31 +#define __NR_gtty 32 +#define __NR_access 33 +#define __NR_nice 34 +#define __NR_ftime 35 +#define __NR_sync 36 +#define __NR_kill 37 +#define __NR_rename 38 +#define __NR_mkdir 39 +#define __NR_rmdir 40 +#define __NR_dup 41 +#define __NR_pipe 42 +#define __NR_times 43 +#define __NR_prof 44 +#define __NR_brk 45 +#define __NR_setgid 46 +#define __NR_getgid 47 +#define __NR_signal 48 +#define __NR_geteuid 49 +#define __NR_getegid 50 +#define __NR_acct 51 +#define __NR_phys 52 +#define __NR_lock 53 +#define __NR_ioctl 54 +#define __NR_fcntl 55 +#define __NR_mpx 56 +#define __NR_setpgid 57 +#define __NR_ulimit 58 +#define __NR_uname 59 +#define __NR_umask 60 +#define __NR_chroot 61 +#define __NR_ustat 62 +#define __NR_dup2 63 +#define __NR_getppid 64 +#define __NR_getpgrp 65 +#define __NR_setsid 66 + +#define _syscall0(type,name) \ +type name(void) \ +{ \ +type __res; \ +__asm__ volatile ("int $0x80" \ + : "=a" (__res) \ + : "0" (__NR_##name)); \ +if (__res >= 0) \ + return __res; \ +errno = -__res; \ +return -1; \ +} + +#define _syscall1(type,name,atype,a) \ +type name(atype a) \ +{ \ +type __res; \ +__asm__ volatile ("int $0x80" \ + : "=a" (__res) \ + : "0" (__NR_##name),"b" (a)); \ +if (__res >= 0) \ + return __res; \ +errno = -__res; \ +return -1; \ +} + +#define _syscall2(type,name,atype,a,btype,b) \ +type name(atype a,btype b) \ +{ \ +type __res; \ +__asm__ volatile ("int $0x80" \ + : "=a" (__res) \ + : "0" (__NR_##name),"b" (a),"c" (b)); \ +if (__res >= 0) \ + return __res; \ +errno = -__res; \ +return -1; \ +} + +#define _syscall3(type,name,atype,a,btype,b,ctype,c) \ +type name(atype a,btype b,ctype c) \ +{ \ +type __res; \ +__asm__ volatile ("int $0x80" \ + : "=a" (__res) \ + : "0" (__NR_##name),"b" (a),"c" (b),"d" (c)); \ +if (__res<0) \ + errno=-__res , __res = -1; \ +return __res;\ +} + +#endif /* __LIBRARY__ */ + +extern int errno; + +int access(const char * filename, mode_t mode); +int acct(const char * filename); +int alarm(int sec); +int brk(void * end_data_segment); +void * sbrk(ptrdiff_t increment); +int chdir(const char * filename); +int chmod(const char * filename, mode_t mode); +int chown(const char * filename, uid_t owner, gid_t group); +int chroot(const char * filename); +int close(int fildes); +int creat(const char * filename, mode_t mode); +int dup(int fildes); +int execve(const char * filename, char ** argv, char ** envp); +int execv(const char * pathname, char ** argv); +int execvp(const char * file, char ** argv); +int execl(const char * pathname, char * arg0, ...); +int execlp(const char * file, char * arg0, ...); +int execle(const char * pathname, char * arg0, ...); +volatile void exit(int status); +volatile void _exit(int status); +int fcntl(int fildes, int cmd, ...); +int fork(void); +int getpid(void); +int getuid(void); +int geteuid(void); +int getgid(void); +int getegid(void); +int ioctl(int fildes, int cmd, ...); +int kill(pid_t pid, int signal); +int link(const char * filename1, const char * filename2); +int lseek(int fildes, off_t offset, int origin); +int mknod(const char * filename, mode_t mode, dev_t dev); +int mount(const char * specialfile, const char * dir, int rwflag); +int nice(int val); +int open(const char * filename, int flag, ...); +int pause(void); +int pipe(int * fildes); +int read(int fildes, char * buf, off_t count); +int setpgrp(void); +int setpgid(pid_t pid,pid_t pgid); +int setuid(uid_t uid); +int setgid(gid_t gid); +void (*signal(int sig, void (*fn)(int)))(int); +int stat(const char * filename, struct stat * stat_buf); +int fstat(int fildes, struct stat * stat_buf); +int stime(time_t * tptr); +int sync(void); +time_t time(time_t * tloc); +time_t times(struct tms * tbuf); +int ulimit(int cmd, long limit); +mode_t umask(mode_t mask); +int umount(const char * specialfile); +int uname(struct utsname * name); +int unlink(const char * filename); +int ustat(dev_t dev, struct ustat * ubuf); +int utime(const char * filename, struct utimbuf * times); +pid_t waitpid(pid_t pid,int * wait_stat,int options); +pid_t wait(int * wait_stat); +int write(int fildes, const char * buf, off_t count); +int dup2(int oldfd, int newfd); +int getppid(void); +pid_t getpgrp(void); +pid_t setsid(void); + +#endif diff --git a/linux/include/utime.h b/linux/include/utime.h new file mode 100644 index 0000000..83f07c7 --- /dev/null +++ b/linux/include/utime.h @@ -0,0 +1,13 @@ +#ifndef _UTIME_H +#define _UTIME_H + +#include <sys/types.h> /* I know - shouldn't do this, but .. */ + +struct utimbuf { + time_t actime; + time_t modtime; +}; + +extern int utime(const char *filename, struct utimbuf *times); + +#endif diff --git a/linux/init/main.c b/linux/init/main.c new file mode 100644 index 0000000..1192119 --- /dev/null +++ b/linux/init/main.c @@ -0,0 +1,147 @@ +#define __LIBRARY__ +#include <unistd.h> +#include <time.h> + +/* + * we need this inline - forking from kernel space will result + * in NO COPY ON WRITE (!!!), until an execve is executed. This + * is no problem, but for the stack. This is handled by not letting + * main() use the stack at all after fork(). Thus, no function + * calls - which means inline code for fork too, as otherwise we + * would use the stack upon exit from 'fork()'. + * + * Actually only pause and fork are needed inline, so that there + * won't be any messing with the stack from main(), but we define + * some others too. + */ +static inline _syscall0(int,fork) +static inline _syscall0(int,pause) +static inline _syscall0(int,setup) +static inline _syscall0(int,sync) + +#include <linux/tty.h> +#include <linux/sched.h> +#include <linux/head.h> +#include <asm/system.h> +#include <asm/io.h> + +#include <stddef.h> +#include <stdarg.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/types.h> + +#include <linux/fs.h> + +static char printbuf[1024]; + +extern int vsprintf(); +extern void init(void); +extern void hd_init(void); +extern long kernel_mktime(struct tm * tm); +extern long startup_time; + +/* + * Yeah, yeah, it's ugly, but I cannot find how to do this correctly + * and this seems to work. I anybody has more info on the real-time + * clock I'd be interested. Most of this was trial and error, and some + * bios-listing reading. Urghh. + */ + +#define CMOS_READ(addr) ({ \ +outb_p(0x80|addr,0x70); \ +inb_p(0x71); \ +}) + +#define BCD_TO_BIN(val) ((val)=((val)&15) + ((val)>>4)*10) + +static void time_init(void) +{ + struct tm time; + + do { + time.tm_sec = CMOS_READ(0); + time.tm_min = CMOS_READ(2); + time.tm_hour = CMOS_READ(4); + time.tm_mday = CMOS_READ(7); + time.tm_mon = CMOS_READ(8)-1; + time.tm_year = CMOS_READ(9); + } while (time.tm_sec != CMOS_READ(0)); + BCD_TO_BIN(time.tm_sec); + BCD_TO_BIN(time.tm_min); + BCD_TO_BIN(time.tm_hour); + BCD_TO_BIN(time.tm_mday); + BCD_TO_BIN(time.tm_mon); + BCD_TO_BIN(time.tm_year); + startup_time = kernel_mktime(&time); +} + +void main(void) /* This really IS void, no error here. */ +{ /* The startup routine assumes (well, ...) this */ +/* + * Interrupts are still disabled. Do necessary setups, then + * enable them + */ + time_init(); + tty_init(); + trap_init(); + sched_init(); + buffer_init(); + hd_init(); + sti(); + move_to_user_mode(); + if (!fork()) { /* we count on this going ok */ + init(); + } +/* + * NOTE!! For any other task 'pause()' would mean we have to get a + * signal to awaken, but task0 is the sole exception (see 'schedule()') + * as task 0 gets activated at every idle moment (when no other tasks + * can run). For task0 'pause()' just means we go check if some other + * task can run, and if not we return here. + */ + for(;;) pause(); +} + +static int printf(const char *fmt, ...) +{ + va_list args; + int i; + + va_start(args, fmt); + write(1,printbuf,i=vsprintf(printbuf, fmt, args)); + va_end(args); + return i; +} + +static char * argv[] = { "-",NULL }; +static char * envp[] = { "HOME=/usr/root", NULL }; + +void init(void) +{ + int i,j; + + setup(); + if (!fork()) + _exit(execve("/bin/update",NULL,NULL)); + (void) open("/dev/tty0",O_RDWR,0); + (void) dup(0); + (void) dup(0); + printf("%d buffers = %d bytes buffer space\n\r",NR_BUFFERS, + NR_BUFFERS*BLOCK_SIZE); + printf(" Ok.\n\r"); + if ((i=fork())<0) + printf("Fork failed in init\r\n"); + else if (!i) { + close(0);close(1);close(2); + setsid(); + (void) open("/dev/tty0",O_RDWR,0); + (void) dup(0); + (void) dup(0); + _exit(execve("/bin/sh",argv,envp)); + } + j=wait(&i); + printf("child %d died with code %04x\n",j,i); + sync(); + _exit(0); /* NOTE! _exit, not exit() */ +} diff --git a/linux/kernel/Makefile b/linux/kernel/Makefile new file mode 100644 index 0000000..23fe7dd --- /dev/null +++ b/linux/kernel/Makefile @@ -0,0 +1,90 @@ +# +# Makefile for the FREAX-kernel. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# + +AR =gar +AS =gas +LD =gld +LDFLAGS =-s -x +CC =gcc +CFLAGS =-Wall -O -fstrength-reduce -fomit-frame-pointer -fcombine-regs \ + -finline-functions -mstring-insns -nostdinc -I../include +CPP =gcc -E -nostdinc -I../include + +.c.s: + $(CC) $(CFLAGS) \ + -S -o $*.s $< +.s.o: + $(AS) -c -o $*.o $< +.c.o: + $(CC) $(CFLAGS) \ + -c -o $*.o $< + +OBJS = sched.o system_call.o traps.o asm.o fork.o \ + panic.o printk.o vsprintf.o tty_io.o console.o \ + keyboard.o rs_io.o hd.o sys.o exit.o serial.o \ + mktime.o + +kernel.o: $(OBJS) + $(LD) -r -o kernel.o $(OBJS) + sync + +clean: + rm -f core *.o *.a tmp_make + for i in *.c;do rm -f `basename $$i .c`.s;done + +dep: + sed '/\#\#\# Dependencies/q' < Makefile > tmp_make + (for i in *.c;do echo -n `echo $$i | sed 's,\.c,\.s,'`" "; \ + $(CPP) -M $$i;done) >> tmp_make + cp tmp_make Makefile + +### Dependencies: +console.s console.o : console.c ../include/linux/sched.h ../include/linux/head.h \ + ../include/linux/fs.h ../include/sys/types.h ../include/linux/mm.h \ + ../include/linux/tty.h ../include/termios.h ../include/asm/io.h \ + ../include/asm/system.h +exit.s exit.o : exit.c ../include/errno.h ../include/signal.h \ + ../include/sys/types.h ../include/sys/wait.h ../include/linux/sched.h \ + ../include/linux/head.h ../include/linux/fs.h ../include/linux/mm.h \ + ../include/linux/kernel.h ../include/linux/tty.h ../include/termios.h \ + ../include/asm/segment.h +fork.s fork.o : fork.c ../include/errno.h ../include/linux/sched.h \ + ../include/linux/head.h ../include/linux/fs.h ../include/sys/types.h \ + ../include/linux/mm.h ../include/linux/kernel.h ../include/asm/segment.h \ + ../include/asm/system.h +hd.s hd.o : hd.c ../include/linux/config.h ../include/linux/sched.h \ + ../include/linux/head.h ../include/linux/fs.h ../include/sys/types.h \ + ../include/linux/mm.h ../include/linux/kernel.h ../include/linux/hdreg.h \ + ../include/asm/system.h ../include/asm/io.h ../include/asm/segment.h +mktime.s mktime.o : mktime.c ../include/time.h +panic.s panic.o : panic.c ../include/linux/kernel.h +printk.s printk.o : printk.c ../include/stdarg.h ../include/stddef.h \ + ../include/linux/kernel.h +sched.s sched.o : sched.c ../include/linux/sched.h ../include/linux/head.h \ + ../include/linux/fs.h ../include/sys/types.h ../include/linux/mm.h \ + ../include/linux/kernel.h ../include/signal.h ../include/linux/sys.h \ + ../include/asm/system.h ../include/asm/io.h ../include/asm/segment.h +serial.s serial.o : serial.c ../include/linux/tty.h ../include/termios.h \ + ../include/linux/sched.h ../include/linux/head.h ../include/linux/fs.h \ + ../include/sys/types.h ../include/linux/mm.h ../include/asm/system.h \ + ../include/asm/io.h +sys.s sys.o : sys.c ../include/errno.h ../include/linux/sched.h \ + ../include/linux/head.h ../include/linux/fs.h ../include/sys/types.h \ + ../include/linux/mm.h ../include/linux/tty.h ../include/termios.h \ + ../include/linux/kernel.h ../include/asm/segment.h ../include/sys/times.h \ + ../include/sys/utsname.h +traps.s traps.o : traps.c ../include/string.h ../include/linux/head.h \ + ../include/linux/sched.h ../include/linux/fs.h ../include/sys/types.h \ + ../include/linux/mm.h ../include/linux/kernel.h ../include/asm/system.h \ + ../include/asm/segment.h +tty_io.s tty_io.o : tty_io.c ../include/ctype.h ../include/errno.h \ + ../include/signal.h ../include/sys/types.h ../include/linux/sched.h \ + ../include/linux/head.h ../include/linux/fs.h ../include/linux/mm.h \ + ../include/linux/tty.h ../include/termios.h ../include/asm/segment.h \ + ../include/asm/system.h +vsprintf.s vsprintf.o : vsprintf.c ../include/stdarg.h ../include/string.h diff --git a/linux/kernel/asm.s b/linux/kernel/asm.s new file mode 100644 index 0000000..6fe1981 --- /dev/null +++ b/linux/kernel/asm.s @@ -0,0 +1,157 @@ +/* + * asm.s contains the low-level code for most hardware faults. + * page_exception is handled by the mm, so that isn't here. This + * file also handles (hopefully) fpu-exceptions due to TS-bit, as + * the fpu must be properly saved/resored. This hasn't been tested. + */ + +.globl _divide_error,_debug,_nmi,_int3,_overflow,_bounds,_invalid_op +.globl _device_not_available,_double_fault,_coprocessor_segment_overrun +.globl _invalid_TSS,_segment_not_present,_stack_segment +.globl _general_protection,_coprocessor_error,_reserved + +_divide_error: + pushl $_do_divide_error +no_error_code: + xchgl %eax,(%esp) + pushl %ebx + pushl %ecx + pushl %edx + pushl %edi + pushl %esi + pushl %ebp + push %ds + push %es + push %fs + pushl $0 # "error code" + lea 44(%esp),%edx + pushl %edx + movl $0x10,%edx + mov %dx,%ds + mov %dx,%es + mov %dx,%fs + call *%eax + addl $8,%esp + pop %fs + pop %es + pop %ds + popl %ebp + popl %esi + popl %edi + popl %edx + popl %ecx + popl %ebx + popl %eax + iret + +_debug: + pushl $_do_int3 # _do_debug + jmp no_error_code + +_nmi: + pushl $_do_nmi + jmp no_error_code + +_int3: + pushl $_do_int3 + jmp no_error_code + +_overflow: + pushl $_do_overflow + jmp no_error_code + +_bounds: + pushl $_do_bounds + jmp no_error_code + +_invalid_op: + pushl $_do_invalid_op + jmp no_error_code + +math_emulate: + popl %eax + pushl $_do_device_not_available + jmp no_error_code +_device_not_available: + pushl %eax + movl %cr0,%eax + bt $2,%eax # EM (math emulation bit) + jc math_emulate + clts # clear TS so that we can use math + movl _current,%eax + cmpl _last_task_used_math,%eax + je 1f # shouldn't happen really ... + pushl %ecx + pushl %edx + push %ds + movl $0x10,%eax + mov %ax,%ds + call _math_state_restore + pop %ds + popl %edx + popl %ecx +1: popl %eax + iret + +_coprocessor_segment_overrun: + pushl $_do_coprocessor_segment_overrun + jmp no_error_code + +_reserved: + pushl $_do_reserved + jmp no_error_code + +_coprocessor_error: + pushl $_do_coprocessor_error + jmp no_error_code + +_double_fault: + pushl $_do_double_fault +error_code: + xchgl %eax,4(%esp) # error code <-> %eax + xchgl %ebx,(%esp) # &function <-> %ebx + pushl %ecx + pushl %edx + pushl %edi + pushl %esi + pushl %ebp + push %ds + push %es + push %fs + pushl %eax # error code + lea 44(%esp),%eax # offset + pushl %eax + movl $0x10,%eax + mov %ax,%ds + mov %ax,%es + mov %ax,%fs + call *%ebx + addl $8,%esp + pop %fs + pop %es + pop %ds + popl %ebp + popl %esi + popl %edi + popl %edx + popl %ecx + popl %ebx + popl %eax + iret + +_invalid_TSS: + pushl $_do_invalid_TSS + jmp error_code + +_segment_not_present: + pushl $_do_segment_not_present + jmp error_code + +_stack_segment: + pushl $_do_stack_segment + jmp error_code + +_general_protection: + pushl $_do_general_protection + jmp error_code + diff --git a/linux/kernel/console.c b/linux/kernel/console.c new file mode 100644 index 0000000..9e00b31 --- /dev/null +++ b/linux/kernel/console.c @@ -0,0 +1,550 @@ +/* + * console.c + * + * This module implements the console io functions + * 'void con_init(void)' + * 'void con_write(struct tty_queue * queue)' + * Hopefully this will be a rather complete VT102 implementation. + * + */ + +/* + * NOTE!!! We sometimes disable and enable interrupts for a short while + * (to put a word in video IO), but this will work even for keyboard + * interrupts. We know interrupts aren't enabled when getting a keyboard + * interrupt, as we use trap-gates. Hopefully all is well. + */ + +#include <linux/sched.h> +#include <linux/tty.h> +#include <asm/io.h> +#include <asm/system.h> + +#define SCREEN_START 0xb8000 +#define SCREEN_END 0xc0000 +#define LINES 25 +#define COLUMNS 80 +#define NPAR 16 + +extern void keyboard_interrupt(void); + +static unsigned long origin=SCREEN_START; +static unsigned long scr_end=SCREEN_START+LINES*COLUMNS*2; +static unsigned long pos; +static unsigned long x,y; +static unsigned long top=0,bottom=LINES; +static unsigned long lines=LINES,columns=COLUMNS; +static unsigned long state=0; +static unsigned long npar,par[NPAR]; +static unsigned long ques=0; +static unsigned char attr=0x07; + +/* + * this is what the terminal answers to a ESC-Z or csi0c + * query (= vt100 response). + */ +#define RESPONSE "\033[?1;2c" + +static inline void gotoxy(unsigned int new_x,unsigned int new_y) +{ + if (new_x>=columns || new_y>=lines) + return; + x=new_x; + y=new_y; + pos=origin+((y*columns+x)<<1); +} + +static inline void set_origin(void) +{ + cli(); + outb_p(12,0x3d4); + outb_p(0xff&((origin-SCREEN_START)>>9),0x3d5); + outb_p(13,0x3d4); + outb_p(0xff&((origin-SCREEN_START)>>1),0x3d5); + sti(); +} + +static void scrup(void) +{ + if (!top && bottom==lines) { + origin += columns<<1; + pos += columns<<1; + scr_end += columns<<1; + if (scr_end>SCREEN_END) { + __asm__("cld\n\t" + "rep\n\t" + "movsl\n\t" + "movl _columns,%1\n\t" + "rep\n\t" + "stosw" + ::"a" (0x0720), + "c" ((lines-1)*columns>>1), + "D" (SCREEN_START), + "S" (origin) + :"cx","di","si"); + scr_end -= origin-SCREEN_START; + pos -= origin-SCREEN_START; + origin = SCREEN_START; + } else { + __asm__("cld\n\t" + "rep\n\t" + "stosl" + ::"a" (0x07200720), + "c" (columns>>1), + "D" (scr_end-(columns<<1)) + :"cx","di"); + } + set_origin(); + } else { + __asm__("cld\n\t" + "rep\n\t" + "movsl\n\t" + "movl _columns,%%ecx\n\t" + "rep\n\t" + "stosw" + ::"a" (0x0720), + "c" ((bottom-top-1)*columns>>1), + "D" (origin+(columns<<1)*top), + "S" (origin+(columns<<1)*(top+1)) + :"cx","di","si"); + } +} + +static void scrdown(void) +{ + __asm__("std\n\t" + "rep\n\t" + "movsl\n\t" + "addl $2,%%edi\n\t" /* %edi has been decremented by 4 */ + "movl _columns,%%ecx\n\t" + "rep\n\t" + "stosw" + ::"a" (0x0720), + "c" ((bottom-top-1)*columns>>1), + "D" (origin+(columns<<1)*bottom-4), + "S" (origin+(columns<<1)*(bottom-1)-4) + :"ax","cx","di","si"); +} + +static void lf(void) +{ + if (y+1<bottom) { + y++; + pos += columns<<1; + return; + } + scrup(); +} + +static void ri(void) +{ + if (y>top) { + y--; + pos -= columns<<1; + return; + } + scrdown(); +} + +static void cr(void) +{ + pos -= x<<1; + x=0; +} + +static void del(void) +{ + if (x) { + pos -= 2; + x--; + *(unsigned short *)pos = 0x0720; + } +} + +static void csi_J(int par) +{ + long count __asm__("cx"); + long start __asm__("di"); + + switch (par) { + case 0: /* erase from cursor to end of display */ + count = (scr_end-pos)>>1; + start = pos; + break; + case 1: /* erase from start to cursor */ + count = (pos-origin)>>1; + start = origin; + break; + case 2: /* erase whole display */ + count = columns*lines; + start = origin; + break; + default: + return; + } + __asm__("cld\n\t" + "rep\n\t" + "stosw\n\t" + ::"c" (count), + "D" (start),"a" (0x0720) + :"cx","di"); +} + +static void csi_K(int par) +{ + long count __asm__("cx"); + long start __asm__("di"); + + switch (par) { + case 0: /* erase from cursor to end of line */ + if (x>=columns) + return; + count = columns-x; + start = pos; + break; + case 1: /* erase from start of line to cursor */ + start = pos - (x<<1); + count = (x<columns)?x:columns; + break; + case 2: /* erase whole line */ + start = pos - (x<<1); + count = columns; + break; + default: + return; + } + __asm__("cld\n\t" + "rep\n\t" + "stosw\n\t" + ::"c" (count), + "D" (start),"a" (0x0720) + :"cx","di"); +} + +void csi_m(void) +{ + int i; + + for (i=0;i<=npar;i++) + switch (par[i]) { + case 0:attr=0x07;break; + case 1:attr=0x0f;break; + case 4:attr=0x0f;break; + case 7:attr=0x70;break; + case 27:attr=0x07;break; + } +} + +static inline void set_cursor(void) +{ + cli(); + outb_p(14,0x3d4); + outb_p(0xff&((pos-SCREEN_START)>>9),0x3d5); + outb_p(15,0x3d4); + outb_p(0xff&((pos-SCREEN_START)>>1),0x3d5); + sti(); +} + +static void respond(struct tty_struct * tty) +{ + char * p = RESPONSE; + + cli(); + while (*p) { + PUTCH(*p,tty->read_q); + p++; + } + sti(); + copy_to_cooked(tty); +} + +static void insert_char(void) +{ + int i=x; + unsigned short tmp,old=0x0720; + unsigned short * p = (unsigned short *) pos; + + while (i++<columns) { + tmp=*p; + *p=old; + old=tmp; + p++; + } +} + +static void insert_line(void) +{ + int oldtop,oldbottom; + + oldtop=top; + oldbottom=bottom; + top=y; + bottom=lines; + scrdown(); + top=oldtop; + bottom=oldbottom; +} + +static void delete_char(void) +{ + int i; + unsigned short * p = (unsigned short *) pos; + + if (x>=columns) + return; + i = x; + while (++i < columns) { + *p = *(p+1); + p++; + } + *p=0x0720; +} + +static void delete_line(void) +{ + int oldtop,oldbottom; + + oldtop=top; + oldbottom=bottom; + top=y; + bottom=lines; + scrup(); + top=oldtop; + bottom=oldbottom; +} + +static void csi_at(int nr) +{ + if (nr>columns) + nr=columns; + else if (!nr) + nr=1; + while (nr--) + insert_char(); +} + +static void csi_L(int nr) +{ + if (nr>lines) + nr=lines; + else if (!nr) + nr=1; + while (nr--) + insert_line(); +} + +static void csi_P(int nr) +{ + if (nr>columns) + nr=columns; + else if (!nr) + nr=1; + while (nr--) + delete_char(); +} + +static void csi_M(int nr) +{ + if (nr>lines) + nr=lines; + else if (!nr) + nr=1; + while (nr--) + delete_line(); +} + +static int saved_x=0; +static int saved_y=0; + +static void save_cur(void) +{ + saved_x=x; + saved_y=y; +} + +static void restore_cur(void) +{ + x=saved_x; + y=saved_y; + pos=origin+((y*columns+x)<<1); +} + +void con_write(struct tty_struct * tty) +{ + int nr; + char c; + + nr = CHARS(tty->write_q); + while (nr--) { + GETCH(tty->write_q,c); + switch(state) { + case 0: + if (c>31 && c<127) { + if (x>=columns) { + x -= columns; + pos -= columns<<1; + lf(); + } + __asm__("movb _attr,%%ah\n\t" + "movw %%ax,%1\n\t" + ::"a" (c),"m" (*(short *)pos) + :"ax"); + pos += 2; + x++; + } else if (c==27) + state=1; + else if (c==10 || c==11 || c==12) + lf(); + else if (c==13) + cr(); + else if (c==ERASE_CHAR(tty)) + del(); + else if (c==8) { + if (x) { + x--; + pos -= 2; + } + } else if (c==9) { + c=8-(x&7); + x += c; + pos += c<<1; + if (x>columns) { + x -= columns; + pos -= columns<<1; + lf(); + } + c=9; + } + break; + case 1: + state=0; + if (c=='[') + state=2; + else if (c=='E') + gotoxy(0,y+1); + else if (c=='M') + ri(); + else if (c=='D') + lf(); + else if (c=='Z') + respond(tty); + else if (x=='7') + save_cur(); + else if (x=='8') + restore_cur(); + break; + case 2: + for(npar=0;npar<NPAR;npar++) + par[npar]=0; + npar=0; + state=3; + if (ques=(c=='?')) + break; + case 3: + if (c==';' && npar<NPAR-1) { + npar++; + break; + } else if (c>='0' && c<='9') { + par[npar]=10*par[npar]+c-'0'; + break; + } else state=4; + case 4: + state=0; + switch(c) { + case 'G': case '`': + if (par[0]) par[0]--; + gotoxy(par[0],y); + break; + case 'A': + if (!par[0]) par[0]++; + gotoxy(x,y-par[0]); + break; + case 'B': case 'e': + if (!par[0]) par[0]++; + gotoxy(x,y+par[0]); + break; + case 'C': case 'a': + if (!par[0]) par[0]++; + gotoxy(x+par[0],y); + break; + case 'D': + if (!par[0]) par[0]++; + gotoxy(x-par[0],y); + break; + case 'E': + if (!par[0]) par[0]++; + gotoxy(0,y+par[0]); + break; + case 'F': + if (!par[0]) par[0]++; + gotoxy(0,y-par[0]); + break; + case 'd': + if (par[0]) par[0]--; + gotoxy(x,par[0]); + break; + case 'H': case 'f': + if (par[0]) par[0]--; + if (par[1]) par[1]--; + gotoxy(par[1],par[0]); + break; + case 'J': + csi_J(par[0]); + break; + case 'K': + csi_K(par[0]); + break; + case 'L': + csi_L(par[0]); + break; + case 'M': + csi_M(par[0]); + break; + case 'P': + csi_P(par[0]); + break; + case '@': + csi_at(par[0]); + break; + case 'm': + csi_m(); + break; + case 'r': + if (par[0]) par[0]--; + if (!par[1]) par[1]=lines; + if (par[0] < par[1] && + par[1] <= lines) { + top=par[0]; + bottom=par[1]; + } + break; + case 's': + save_cur(); + break; + case 'u': + restore_cur(); + break; + } + } + } + set_cursor(); +} + +/* + * void con_init(void); + * + * This routine initalizes console interrupts, and does nothing + * else. If you want the screen to clear, call tty_write with + * the appropriate escape-sequece. + */ +void con_init(void) +{ + register unsigned char a; + + gotoxy(*(unsigned char *)(0x90000+510),*(unsigned char *)(0x90000+511)); + set_trap_gate(0x21,&keyboard_interrupt); + outb_p(inb_p(0x21)&0xfd,0x21); + a=inb_p(0x61); + outb_p(a|0x80,0x61); + outb(a,0x61); +} diff --git a/linux/kernel/exit.c b/linux/kernel/exit.c new file mode 100644 index 0000000..3402c33 --- /dev/null +++ b/linux/kernel/exit.c @@ -0,0 +1,135 @@ +#include <errno.h> +#include <signal.h> +#include <sys/wait.h> + +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/tty.h> +#include <asm/segment.h> + +int sys_pause(void); +int sys_close(int fd); + +void release(struct task_struct * p) +{ + int i; + + if (!p) + return; + for (i=1 ; i<NR_TASKS ; i++) + if (task[i]==p) { + task[i]=NULL; + free_page((long)p); + schedule(); + return; + } + panic("trying to release non-existent task"); +} + +static inline void send_sig(long sig,struct task_struct * p,int priv) +{ + if (!p || sig<1 || sig>32) + return; + if (priv || + current->uid==p->uid || + current->euid==p->uid || + current->uid==p->euid || + current->euid==p->euid) + p->signal |= (1<<(sig-1)); +} + +void do_kill(long pid,long sig,int priv) +{ + struct task_struct **p = NR_TASKS + task; + + if (!pid) while (--p > &FIRST_TASK) { + if (*p && (*p)->pgrp == current->pid) + send_sig(sig,*p,priv); + } else if (pid>0) while (--p > &FIRST_TASK) { + if (*p && (*p)->pid == pid) + send_sig(sig,*p,priv); + } else if (pid == -1) while (--p > &FIRST_TASK) + send_sig(sig,*p,priv); + else while (--p > &FIRST_TASK) + if (*p && (*p)->pgrp == -pid) + send_sig(sig,*p,priv); +} + +int sys_kill(int pid,int sig) +{ + do_kill(pid,sig,!(current->uid || current->euid)); + return 0; +} + +int do_exit(long code) +{ + int i; + + free_page_tables(get_base(current->ldt[1]),get_limit(0x0f)); + free_page_tables(get_base(current->ldt[2]),get_limit(0x17)); + for (i=0 ; i<NR_TASKS ; i++) + if (task[i] && task[i]->father == current->pid) + task[i]->father = 0; + for (i=0 ; i<NR_OPEN ; i++) + if (current->filp[i]) + sys_close(i); + iput(current->pwd); + current->pwd=NULL; + iput(current->root); + current->root=NULL; + if (current->leader && current->tty >= 0) + tty_table[current->tty].pgrp = 0; + if (last_task_used_math == current) + last_task_used_math = NULL; + if (current->father) { + current->state = TASK_ZOMBIE; + do_kill(current->father,SIGCHLD,1); + current->exit_code = code; + } else + release(current); + schedule(); + return (-1); /* just to suppress warnings */ +} + +int sys_exit(int error_code) +{ + return do_exit((error_code&0xff)<<8); +} + +int sys_waitpid(pid_t pid,int * stat_addr, int options) +{ + int flag=0; + struct task_struct ** p; + + verify_area(stat_addr,4); +repeat: + for(p = &LAST_TASK ; p > &FIRST_TASK ; --p) + if (*p && *p != current && + (pid==-1 || (*p)->pid==pid || + (pid==0 && (*p)->pgrp==current->pgrp) || + (pid<0 && (*p)->pgrp==-pid))) + if ((*p)->father == current->pid) { + flag=1; + if ((*p)->state==TASK_ZOMBIE) { + put_fs_long((*p)->exit_code, + (unsigned long *) stat_addr); + current->cutime += (*p)->utime; + current->cstime += (*p)->stime; + flag = (*p)->pid; + release(*p); + return flag; + } + } + if (flag) { + if (options & WNOHANG) + return 0; + sys_pause(); + if (!(current->signal &= ~(1<<(SIGCHLD-1)))) + goto repeat; + else + return -EINTR; + } + return -ECHILD; +} + + diff --git a/linux/kernel/fork.c b/linux/kernel/fork.c new file mode 100644 index 0000000..70f9ddd --- /dev/null +++ b/linux/kernel/fork.c @@ -0,0 +1,136 @@ +/* + * 'fork.c' contains the help-routines for the 'fork' system call + * (see also system_call.s), and some misc functions ('verify_area'). + * Fork is rather simple, once you get the hang of it, but the memory + * management can be a bitch. See 'mm/mm.c': 'copy_page_tables()' + */ +#include <errno.h> + +#include <linux/sched.h> +#include <linux/kernel.h> +#include <asm/segment.h> +#include <asm/system.h> + +extern void write_verify(unsigned long address); + +long last_pid=0; + +void verify_area(void * addr,int size) +{ + unsigned long start; + + start = (unsigned long) addr; + size += start & 0xfff; + start &= 0xfffff000; + start += get_base(current->ldt[2]); + while (size>0) { + size -= 4096; + write_verify(start); + start += 4096; + } +} + +int copy_mem(int nr,struct task_struct * p) +{ + unsigned long old_data_base,new_data_base,data_limit; + unsigned long old_code_base,new_code_base,code_limit; + + code_limit=get_limit(0x0f); + data_limit=get_limit(0x17); + old_code_base = get_base(current->ldt[1]); + old_data_base = get_base(current->ldt[2]); + if (old_data_base != old_code_base) + panic("We don't support separate I&D"); + if (data_limit < code_limit) + panic("Bad data_limit"); + new_data_base = new_code_base = nr * 0x4000000; + set_base(p->ldt[1],new_code_base); + set_base(p->ldt[2],new_data_base); + if (copy_page_tables(old_data_base,new_data_base,data_limit)) { + free_page_tables(new_data_base,data_limit); + return -ENOMEM; + } + return 0; +} + +/* + * Ok, this is the main fork-routine. It copies the system process + * information (task[nr]) and sets up the necessary registers. It + * also copies the data segment in it's entirety. + */ +int copy_process(int nr,long ebp,long edi,long esi,long gs,long none, + long ebx,long ecx,long edx, + long fs,long es,long ds, + long eip,long cs,long eflags,long esp,long ss) +{ + struct task_struct *p; + int i; + struct file *f; + + p = (struct task_struct *) get_free_page(); + if (!p) + return -EAGAIN; + *p = *current; /* NOTE! this doesn't copy the supervisor stack */ + p->state = TASK_RUNNING; + p->pid = last_pid; + p->father = current->pid; + p->counter = p->priority; + p->signal = 0; + p->alarm = 0; + p->leader = 0; /* process leadership doesn't inherit */ + p->utime = p->stime = 0; + p->cutime = p->cstime = 0; + p->start_time = jiffies; + p->tss.back_link = 0; + p->tss.esp0 = PAGE_SIZE + (long) p; + p->tss.ss0 = 0x10; + p->tss.eip = eip; + p->tss.eflags = eflags; + p->tss.eax = 0; + p->tss.ecx = ecx; + p->tss.edx = edx; + p->tss.ebx = ebx; + p->tss.esp = esp; + p->tss.ebp = ebp; + p->tss.esi = esi; + p->tss.edi = edi; + p->tss.es = es & 0xffff; + p->tss.cs = cs & 0xffff; + p->tss.ss = ss & 0xffff; + p->tss.ds = ds & 0xffff; + p->tss.fs = fs & 0xffff; + p->tss.gs = gs & 0xffff; + p->tss.ldt = _LDT(nr); + p->tss.trace_bitmap = 0x80000000; + if (last_task_used_math == current) + __asm__("fnsave %0"::"m" (p->tss.i387)); + if (copy_mem(nr,p)) { + free_page((long) p); + return -EAGAIN; + } + for (i=0; i<NR_OPEN;i++) + if (f=p->filp[i]) + f->f_count++; + if (current->pwd) + current->pwd->i_count++; + if (current->root) + current->root->i_count++; + set_tss_desc(gdt+(nr<<1)+FIRST_TSS_ENTRY,&(p->tss)); + set_ldt_desc(gdt+(nr<<1)+FIRST_LDT_ENTRY,&(p->ldt)); + task[nr] = p; /* do this last, just in case */ + return last_pid; +} + +int find_empty_process(void) +{ + int i; + + repeat: + if ((++last_pid)<0) last_pid=1; + for(i=0 ; i<NR_TASKS ; i++) + if (task[i] && task[i]->pid == last_pid) goto repeat; + for(i=1 ; i<NR_TASKS ; i++) + if (!task[i]) + return i; + return -EAGAIN; +} diff --git a/linux/kernel/hd.c b/linux/kernel/hd.c new file mode 100644 index 0000000..d3e6140 --- /dev/null +++ b/linux/kernel/hd.c @@ -0,0 +1,413 @@ +#include <linux/config.h> +#include <linux/sched.h> +#include <linux/fs.h> +#include <linux/kernel.h> +#include <linux/hdreg.h> +#include <asm/system.h> +#include <asm/io.h> +#include <asm/segment.h> + +/* + * This code handles all hd-interrupts, and read/write requests to + * the hard-disk. It is relatively straigthforward (not obvious maybe, + * but interrupts never are), while still being efficient, and never + * disabling interrupts (except to overcome possible race-condition). + * The elevator block-seek algorithm doesn't need to disable interrupts + * due to clever programming. + */ + +/* Max read/write errors/sector */ +#define MAX_ERRORS 5 +#define MAX_HD 2 +#define NR_REQUEST 32 + +/* + * This struct defines the HD's and their types. + * Currently defined for CP3044's, ie a modified + * type 17. + */ +static struct hd_i_struct{ + int head,sect,cyl,wpcom,lzone,ctl; + } hd_info[]= { HD_TYPE }; + +#define NR_HD ((sizeof (hd_info))/(sizeof (struct hd_i_struct))) + +static struct hd_struct { + long start_sect; + long nr_sects; +} hd[5*MAX_HD]={{0,0},}; + +static struct hd_request { + int hd; /* -1 if no request */ + int nsector; + int sector; + int head; + int cyl; + int cmd; + int errors; + struct buffer_head * bh; + struct hd_request * next; +} request[NR_REQUEST]; + +#define IN_ORDER(s1,s2) \ +((s1)->hd<(s2)->hd || (s1)->hd==(s2)->hd && \ +((s1)->cyl<(s2)->cyl || (s1)->cyl==(s2)->cyl && \ +((s1)->head<(s2)->head || (s1)->head==(s2)->head && \ +((s1)->sector<(s2)->sector)))) + +static struct hd_request * this_request = NULL; + +static int sorting=0; + +static void do_request(void); +static void reset_controller(void); +static void rw_abs_hd(int rw,unsigned int nr,unsigned int sec,unsigned int head, + unsigned int cyl,struct buffer_head * bh); +void hd_init(void); + +#define port_read(port,buf,nr) \ +__asm__("cld;rep;insw"::"d" (port),"D" (buf),"c" (nr):"cx","di") + +#define port_write(port,buf,nr) \ +__asm__("cld;rep;outsw"::"d" (port),"S" (buf),"c" (nr):"cx","si") + +extern void hd_interrupt(void); + +static struct task_struct * wait_for_request=NULL; + +static inline void lock_buffer(struct buffer_head * bh) +{ + if (bh->b_lock) + printk("hd.c: buffer multiply locked\n"); + bh->b_lock=1; +} + +static inline void unlock_buffer(struct buffer_head * bh) +{ + if (!bh->b_lock) + printk("hd.c: free buffer being unlocked\n"); + bh->b_lock=0; + wake_up(&bh->b_wait); +} + +static inline void wait_on_buffer(struct buffer_head * bh) +{ + cli(); + while (bh->b_lock) + sleep_on(&bh->b_wait); + sti(); +} + +void rw_hd(int rw, struct buffer_head * bh) +{ + unsigned int block,dev; + unsigned int sec,head,cyl; + + block = bh->b_blocknr << 1; + dev = MINOR(bh->b_dev); + if (dev >= 5*NR_HD || block+2 > hd[dev].nr_sects) + return; + block += hd[dev].start_sect; + dev /= 5; + __asm__("divl %4":"=a" (block),"=d" (sec):"0" (block),"1" (0), + "r" (hd_info[dev].sect)); + __asm__("divl %4":"=a" (cyl),"=d" (head):"0" (block),"1" (0), + "r" (hd_info[dev].head)); + rw_abs_hd(rw,dev,sec+1,head,cyl,bh); +} + +/* This may be used only once, enforced by 'static int callable' */ +int sys_setup(void) +{ + static int callable = 1; + int i,drive; + struct partition *p; + + if (!callable) + return -1; + callable = 0; + for (drive=0 ; drive<NR_HD ; drive++) { + rw_abs_hd(READ,drive,1,0,0,(struct buffer_head *) start_buffer); + if (!start_buffer->b_uptodate) { + printk("Unable to read partition table of drive %d\n\r", + drive); + panic(""); + } + if (start_buffer->b_data[510] != 0x55 || (unsigned char) + start_buffer->b_data[511] != 0xAA) { + printk("Bad partition table on drive %d\n\r",drive); + panic(""); + } + p = 0x1BE + (void *)start_buffer->b_data; + for (i=1;i<5;i++,p++) { + hd[i+5*drive].start_sect = p->start_sect; + hd[i+5*drive].nr_sects = p->nr_sects; + } + } + printk("Partition table%s ok.\n\r",(NR_HD>1)?"s":""); + mount_root(); + return (0); +} + +/* + * This is the pointer to a routine to be executed at every hd-interrupt. + * Interesting way of doing things, but should be rather practical. + */ +void (*do_hd)(void) = NULL; + +static int controller_ready(void) +{ + int retries=1000; + + while (--retries && (inb(HD_STATUS)&0xc0)!=0x40); + return (retries); +} + +static int win_result(void) +{ + int i=inb(HD_STATUS); + + if ((i & (BUSY_STAT | READY_STAT | WRERR_STAT | SEEK_STAT | ERR_STAT)) + == (READY_STAT | SEEK_STAT)) + return(0); /* ok */ + if (i&1) i=inb(HD_ERROR); + return (1); +} + +static void hd_out(unsigned int drive,unsigned int nsect,unsigned int sect, + unsigned int head,unsigned int cyl,unsigned int cmd, + void (*intr_addr)(void)) +{ + register int port asm("dx"); + + if (drive>1 || head>15) + panic("Trying to write bad sector"); + if (!controller_ready()) + panic("HD controller not ready"); + do_hd = intr_addr; + outb(_CTL,HD_CMD); + port=HD_DATA; + outb_p(_WPCOM,++port); + outb_p(nsect,++port); + outb_p(sect,++port); + outb_p(cyl,++port); + outb_p(cyl>>8,++port); + outb_p(0xA0|(drive<<4)|head,++port); + outb(cmd,++port); +} + +static int drive_busy(void) +{ + unsigned int i; + + for (i = 0; i < 100000; i++) + if (READY_STAT == (inb(HD_STATUS) & (BUSY_STAT | READY_STAT))) + break; + i = inb(HD_STATUS); + i &= BUSY_STAT | READY_STAT | SEEK_STAT; + if (i == READY_STAT | SEEK_STAT) + return(0); + printk("HD controller times out\n\r"); + return(1); +} + +static void reset_controller(void) +{ + int i; + + outb(4,HD_CMD); + for(i = 0; i < 1000; i++) nop(); + outb(0,HD_CMD); + for(i = 0; i < 10000 && drive_busy(); i++) /* nothing */; + if (drive_busy()) + printk("HD-controller still busy\n\r"); + if((i = inb(ERR_STAT)) != 1) + printk("HD-controller reset failed: %02x\n\r",i); +} + +static void reset_hd(int nr) +{ + reset_controller(); + hd_out(nr,_SECT,_SECT,_HEAD-1,_CYL,WIN_SPECIFY,&do_request); +} + +void unexpected_hd_interrupt(void) +{ + panic("Unexpected HD interrupt\n\r"); +} + +static void bad_rw_intr(void) +{ + int i = this_request->hd; + + if (this_request->errors++ >= MAX_ERRORS) { + this_request->bh->b_uptodate = 0; + unlock_buffer(this_request->bh); + wake_up(&wait_for_request); + this_request->hd = -1; + this_request=this_request->next; + } + reset_hd(i); +} + +static void read_intr(void) +{ + if (win_result()) { + bad_rw_intr(); + return; + } + port_read(HD_DATA,this_request->bh->b_data+ + 512*(this_request->nsector&1),256); + this_request->errors = 0; + if (--this_request->nsector) + return; + this_request->bh->b_uptodate = 1; + this_request->bh->b_dirt = 0; + wake_up(&wait_for_request); + unlock_buffer(this_request->bh); + this_request->hd = -1; + this_request=this_request->next; + do_request(); +} + +static void write_intr(void) +{ + if (win_result()) { + bad_rw_intr(); + return; + } + if (--this_request->nsector) { + port_write(HD_DATA,this_request->bh->b_data+512,256); + return; + } + this_request->bh->b_uptodate = 1; + this_request->bh->b_dirt = 0; + wake_up(&wait_for_request); + unlock_buffer(this_request->bh); + this_request->hd = -1; + this_request=this_request->next; + do_request(); +} + +static void do_request(void) +{ + int i,r; + + if (sorting) + return; + if (!this_request) { + do_hd=NULL; + return; + } + if (this_request->cmd == WIN_WRITE) { + hd_out(this_request->hd,this_request->nsector,this_request-> + sector,this_request->head,this_request->cyl, + this_request->cmd,&write_intr); + for(i=0 ; i<3000 && !(r=inb_p(HD_STATUS)&DRQ_STAT) ; i++) + /* nothing */ ; + if (!r) { + reset_hd(this_request->hd); + return; + } + port_write(HD_DATA,this_request->bh->b_data+ + 512*(this_request->nsector&1),256); + } else if (this_request->cmd == WIN_READ) { + hd_out(this_request->hd,this_request->nsector,this_request-> + sector,this_request->head,this_request->cyl, + this_request->cmd,&read_intr); + } else + panic("unknown hd-command"); +} + +/* + * add-request adds a request to the linked list. + * It sets the 'sorting'-variable when doing something + * that interrupts shouldn't touch. + */ +static void add_request(struct hd_request * req) +{ + struct hd_request * tmp; + + if (req->nsector != 2) + panic("nsector!=2 not implemented"); +/* + * Not to mess up the linked lists, we never touch the two first + * entries (not this_request, as it is used by current interrups, + * and not this_request->next, as it can be assigned to this_request). + * This is not too high a price to pay for the ability of not + * disabling interrupts. + */ + sorting=1; + if (!(tmp=this_request)) + this_request=req; + else { + if (!(tmp->next)) + tmp->next=req; + else { + tmp=tmp->next; + for ( ; tmp->next ; tmp=tmp->next) + if ((IN_ORDER(tmp,req) || + !IN_ORDER(tmp,tmp->next)) && + IN_ORDER(req,tmp->next)) + break; + req->next=tmp->next; + tmp->next=req; + } + } + sorting=0; +/* + * NOTE! As a result of sorting, the interrupts may have died down, + * as they aren't redone due to locking with sorting=1. They might + * also never have started, if this is the first request in the queue, + * so we restart them if necessary. + */ + if (!do_hd) + do_request(); +} + +void rw_abs_hd(int rw,unsigned int nr,unsigned int sec,unsigned int head, + unsigned int cyl,struct buffer_head * bh) +{ + struct hd_request * req; + + if (rw!=READ && rw!=WRITE) + panic("Bad hd command, must be R/W"); + lock_buffer(bh); +repeat: + for (req=0+request ; req<NR_REQUEST+request ; req++) + if (req->hd<0) + break; + if (req==NR_REQUEST+request) { + sleep_on(&wait_for_request); + goto repeat; + } + req->hd=nr; + req->nsector=2; + req->sector=sec; + req->head=head; + req->cyl=cyl; + req->cmd = ((rw==READ)?WIN_READ:WIN_WRITE); + req->bh=bh; + req->errors=0; + req->next=NULL; + add_request(req); + wait_on_buffer(bh); +} + +void hd_init(void) +{ + int i; + + for (i=0 ; i<NR_REQUEST ; i++) { + request[i].hd = -1; + request[i].next = NULL; + } + for (i=0 ; i<NR_HD ; i++) { + hd[i*5].start_sect = 0; + hd[i*5].nr_sects = hd_info[i].head* + hd_info[i].sect*hd_info[i].cyl; + } + set_trap_gate(0x2E,&hd_interrupt); + outb_p(inb_p(0x21)&0xfb,0x21); + outb(inb_p(0xA1)&0xbf,0xA1); +} diff --git a/linux/kernel/keyboard.s b/linux/kernel/keyboard.s new file mode 100644 index 0000000..ba54be5 --- /dev/null +++ b/linux/kernel/keyboard.s @@ -0,0 +1,409 @@ +/* + * keyboard.s + */ + +.text +.globl _keyboard_interrupt + +/* + * these are for the keyboard read functions + */ +size = 1024 /* must be a power of two ! And MUST be the same + as in tty_io.c !!!! */ +head = 4 +tail = 8 +proc_list = 12 +buf = 16 + +mode: .byte 0 /* caps, alt, ctrl and shift mode */ +leds: .byte 2 /* num-lock, caps, scroll-lock mode (nom-lock on) */ +e0: .byte 0 + +/* + * con_int is the real interrupt routine that reads the + * keyboard scan-code and converts it into the appropriate + * ascii character(s). + */ +_keyboard_interrupt: + pushl %eax + pushl %ebx + pushl %ecx + pushl %edx + push %ds + push %es + movl $0x10,%eax + mov %ax,%ds + mov %ax,%es + xorl %al,%al /* %eax is scan code */ + inb $0x60,%al + cmpb $0xe0,%al + je set_e0 + cmpb $0xe1,%al + je set_e1 + call key_table(,%eax,4) + movb $0,e0 +e0_e1: inb $0x61,%al + jmp 1f +1: jmp 1f +1: orb $0x80,%al + jmp 1f +1: jmp 1f +1: outb %al,$0x61 + jmp 1f +1: jmp 1f +1: andb $0x7F,%al + outb %al,$0x61 + movb $0x20,%al + outb %al,$0x20 + pushl $0 + call _do_tty_interrupt + addl $4,%esp + pop %es + pop %ds + popl %edx + popl %ecx + popl %ebx + popl %eax + iret +set_e0: movb $1,e0 + jmp e0_e1 +set_e1: movb $2,e0 + jmp e0_e1 + +/* + * This routine fills the buffer with max 8 bytes, taken from + * %ebx:%eax. (%edx is high). The bytes are written in the + * order %al,%ah,%eal,%eah,%bl,%bh ... until %eax is zero. + */ +put_queue: + pushl %ecx + pushl %edx + movl _table_list,%edx # read-queue for console + movl head(%edx),%ecx +1: movb %al,buf(%edx,%ecx) + incl %ecx + andl $size-1,%ecx + cmpl tail(%edx),%ecx # buffer full - discard everything + je 3f + shrdl $8,%ebx,%eax + je 2f + shrl $8,%ebx + jmp 1b +2: movl %ecx,head(%edx) + movl proc_list(%edx),%ecx + testl %ecx,%ecx + je 3f + movl $0,(%ecx) +3: popl %edx + popl %ecx + ret + +ctrl: movb $0x04,%al + jmp 1f +alt: movb $0x10,%al +1: cmpb $0,e0 + je 2f + addb %al,%al +2: orb %al,mode + ret +unctrl: movb $0x04,%al + jmp 1f +unalt: movb $0x10,%al +1: cmpb $0,e0 + je 2f + addb %al,%al +2: notb %al + andb %al,mode + ret + +lshift: + orb $0x01,mode + ret +unlshift: + andb $0xfe,mode + ret +rshift: + orb $0x02,mode + ret +unrshift: + andb $0xfd,mode + ret + +caps: testb $0x80,mode + jne 1f + xorb $4,leds + xorb $0x40,mode + orb $0x80,mode +set_leds: + call kb_wait + movb $0xed,%al /* set leds command */ + outb %al,$0x60 + call kb_wait + movb leds,%al + outb %al,$0x60 + ret +uncaps: andb $0x7f,mode + ret +scroll: + xorb $1,leds + jmp set_leds +num: xorb $2,leds + jmp set_leds + +/* + * curosr-key/numeric keypad cursor keys are handled here. + * checking for numeric keypad etc. + */ +cursor: + subb $0x47,%al + jb 1f + cmpb $12,%al + ja 1f + jne cur2 /* check for ctrl-alt-del */ + testb $0x0c,mode + je cur2 + testb $0x30,mode + jne reboot +cur2: cmpb $0x01,e0 /* e0 forces cursor movement */ + je cur + testb $0x02,leds /* not num-lock forces cursor */ + je cur + testb $0x03,mode /* shift forces cursor */ + jne cur + xorl %ebx,%ebx + movb num_table(%eax),%al + jmp put_queue +1: ret + +cur: movb cur_table(%eax),%al + cmpb $'9,%al + ja ok_cur + movb $'~,%ah +ok_cur: shll $16,%eax + movw $0x5b1b,%ax + xorl %ebx,%ebx + jmp put_queue + +num_table: + .ascii "789 456 1230," +cur_table: + .ascii "HA5 DGC YB623" + +/* + * this routine handles function keys + */ +func: + subb $0x3B,%al + jb end_func + cmpb $9,%al + jbe ok_func + subb $18,%al + cmpb $10,%al + jb end_func + cmpb $11,%al + ja end_func +ok_func: + cmpl $4,%ecx /* check that there is enough room */ + jl end_func + movl func_table(,%eax,4),%eax + xorl %ebx,%ebx + jmp put_queue +end_func: + ret + +/* + * function keys send F1:'esc [ [ A' F2:'esc [ [ B' etc. + */ +func_table: + .long 0x415b5b1b,0x425b5b1b,0x435b5b1b,0x445b5b1b + .long 0x455b5b1b,0x465b5b1b,0x475b5b1b,0x485b5b1b + .long 0x495b5b1b,0x4a5b5b1b,0x4b5b5b1b,0x4c5b5b1b + +key_map: + .byte 0,27 + .ascii "1234567890+'" + .byte 127,9 + .ascii "qwertyuiop}" + .byte 0,10,0 + .ascii "asdfghjkl|{" + .byte 0,0 + .ascii "'zxcvbnm,.-" + .byte 0,'*,0,32 /* 36-39 */ + .fill 16,1,0 /* 3A-49 */ + .byte '-,0,0,0,'+ /* 4A-4E */ + .byte 0,0,0,0,0,0,0 /* 4F-55 */ + .byte '< + .fill 10,1,0 + +shift_map: + .byte 0,27 + .ascii "!\"#$%&/()=?`" + .byte 127,9 + .ascii "QWERTYUIOP]^" + .byte 10,0 + .ascii "ASDFGHJKL\\[" + .byte 0,0 + .ascii "*ZXCVBNM;:_" + .byte 0,'*,0,32 /* 36-39 */ + .fill 16,1,0 /* 3A-49 */ + .byte '-,0,0,0,'+ /* 4A-4E */ + .byte 0,0,0,0,0,0,0 /* 4F-55 */ + .byte '> + .fill 10,1,0 + +alt_map: + .byte 0,0 + .ascii "\0@\0$\0\0{[]}\\\0" + .byte 0,0 + .byte 0,0,0,0,0,0,0,0,0,0,0 + .byte '~,10,0 + .byte 0,0,0,0,0,0,0,0,0,0,0 + .byte 0,0 + .byte 0,0,0,0,0,0,0,0,0,0,0 + .byte 0,0,0,0 /* 36-39 */ + .fill 16,1,0 /* 3A-49 */ + .byte 0,0,0,0,0 /* 4A-4E */ + .byte 0,0,0,0,0,0,0 /* 4F-55 */ + .byte '| + .fill 10,1,0 + +/* + * do_self handles "normal" keys, ie keys that don't change meaning + * and which have just one character returns. + */ +do_self: + lea alt_map,%ebx + testb $0x20,mode /* alt-gr */ + jne 1f + lea shift_map,%ebx + testb $0x03,mode + jne 1f + lea key_map,%ebx +1: movb (%ebx,%eax),%al + orb %al,%al + je none + testb $0x4c,mode /* ctrl or caps */ + je 2f + cmpb $'a,%al + jb 2f + cmpb $'z,%al + ja 2f + subb $32,%al +2: testb $0x0c,mode /* ctrl */ + je 3f + cmpb $64,%al + jb 3f + cmpb $64+32,%al + jae 3f + subb $64,%al +3: testb $0x10,mode /* left alt */ + je 4f + orb $0x80,%al +4: andl $0xff,%eax + xorl %ebx,%ebx + call put_queue +none: ret + +/* + * minus has a routine of it's own, as a 'E0h' before + * the scan code for minus means that the numeric keypad + * slash was pushed. + */ +minus: cmpb $1,e0 + jne do_self + movl $'/,%eax + xorl %ebx,%ebx + jmp put_queue + +/* + * This table decides which routine to call when a scan-code has been + * gotten. Most routines just call do_self, or none, depending if + * they are make or break. + */ +key_table: + .long none,do_self,do_self,do_self /* 00-03 s0 esc 1 2 */ + .long do_self,do_self,do_self,do_self /* 04-07 3 4 5 6 */ + .long do_self,do_self,do_self,do_self /* 08-0B 7 8 9 0 */ + .long do_self,do_self,do_self,do_self /* 0C-0F + ' bs tab */ + .long do_self,do_self,do_self,do_self /* 10-13 q w e r */ + .long do_self,do_self,do_self,do_self /* 14-17 t y u i */ + .long do_self,do_self,do_self,do_self /* 18-1B o p } ^ */ + .long do_self,ctrl,do_self,do_self /* 1C-1F enter ctrl a s */ + .long do_self,do_self,do_self,do_self /* 20-23 d f g h */ + .long do_self,do_self,do_self,do_self /* 24-27 j k l | */ + .long do_self,do_self,lshift,do_self /* 28-2B { para lshift , */ + .long do_self,do_self,do_self,do_self /* 2C-2F z x c v */ + .long do_self,do_self,do_self,do_self /* 30-33 b n m , */ + .long do_self,minus,rshift,do_self /* 34-37 . - rshift * */ + .long alt,do_self,caps,func /* 38-3B alt sp caps f1 */ + .long func,func,func,func /* 3C-3F f2 f3 f4 f5 */ + .long func,func,func,func /* 40-43 f6 f7 f8 f9 */ + .long func,num,scroll,cursor /* 44-47 f10 num scr home */ + .long cursor,cursor,do_self,cursor /* 48-4B up pgup - left */ + .long cursor,cursor,do_self,cursor /* 4C-4F n5 right + end */ + .long cursor,cursor,cursor,cursor /* 50-53 dn pgdn ins del */ + .long none,none,do_self,func /* 54-57 sysreq ? < f11 */ + .long func,none,none,none /* 58-5B f12 ? ? ? */ + .long none,none,none,none /* 5C-5F ? ? ? ? */ + .long none,none,none,none /* 60-63 ? ? ? ? */ + .long none,none,none,none /* 64-67 ? ? ? ? */ + .long none,none,none,none /* 68-6B ? ? ? ? */ + .long none,none,none,none /* 6C-6F ? ? ? ? */ + .long none,none,none,none /* 70-73 ? ? ? ? */ + .long none,none,none,none /* 74-77 ? ? ? ? */ + .long none,none,none,none /* 78-7B ? ? ? ? */ + .long none,none,none,none /* 7C-7F ? ? ? ? */ + .long none,none,none,none /* 80-83 ? br br br */ + .long none,none,none,none /* 84-87 br br br br */ + .long none,none,none,none /* 88-8B br br br br */ + .long none,none,none,none /* 8C-8F br br br br */ + .long none,none,none,none /* 90-93 br br br br */ + .long none,none,none,none /* 94-97 br br br br */ + .long none,none,none,none /* 98-9B br br br br */ + .long none,unctrl,none,none /* 9C-9F br unctrl br br */ + .long none,none,none,none /* A0-A3 br br br br */ + .long none,none,none,none /* A4-A7 br br br br */ + .long none,none,unlshift,none /* A8-AB br br unlshift br */ + .long none,none,none,none /* AC-AF br br br br */ + .long none,none,none,none /* B0-B3 br br br br */ + .long none,none,unrshift,none /* B4-B7 br br unrshift br */ + .long unalt,none,uncaps,none /* B8-BB unalt br uncaps br */ + .long none,none,none,none /* BC-BF br br br br */ + .long none,none,none,none /* C0-C3 br br br br */ + .long none,none,none,none /* C4-C7 br br br br */ + .long none,none,none,none /* C8-CB br br br br */ + .long none,none,none,none /* CC-CF br br br br */ + .long none,none,none,none /* D0-D3 br br br br */ + .long none,none,none,none /* D4-D7 br br br br */ + .long none,none,none,none /* D8-DB br ? ? ? */ + .long none,none,none,none /* DC-DF ? ? ? ? */ + .long none,none,none,none /* E0-E3 e0 e1 ? ? */ + .long none,none,none,none /* E4-E7 ? ? ? ? */ + .long none,none,none,none /* E8-EB ? ? ? ? */ + .long none,none,none,none /* EC-EF ? ? ? ? */ + .long none,none,none,none /* F0-F3 ? ? ? ? */ + .long none,none,none,none /* F4-F7 ? ? ? ? */ + .long none,none,none,none /* F8-FB ? ? ? ? */ + .long none,none,none,none /* FC-FF ? ? ? ? */ + +/* + * kb_wait waits for the keyboard controller buffer to empty. + * there is no timeout - if the buffer doesn't empty, we hang. + */ +kb_wait: + pushl %eax +1: inb $0x64,%al + testb $0x02,%al + jne 1b + popl %eax + ret +/* + * This routine reboots the machine by asking the keyboard + * controller to pulse the reset-line low. + */ +reboot: + call kb_wait + movw $0x1234,0x472 /* don't do memory check */ + movb $0xfc,%al /* pulse reset and A20 low */ + outb %al,$0x64 +die: jmp die diff --git a/linux/kernel/mktime.c b/linux/kernel/mktime.c new file mode 100644 index 0000000..3ba79be --- /dev/null +++ b/linux/kernel/mktime.c @@ -0,0 +1,52 @@ +#include <time.h> + +/* + * This isn't the library routine, it is only used in the kernel. + * as such, we don't care about years<1970 etc, but assume everything + * is ok. Similarly, TZ etc is happily ignored. We just do everything + * as easily as possible. Let's find something public for the library + * routines (although I think minix times is public). + */ +/* + * PS. I hate whoever though up the year 1970 - couldn't they have gotten + * a leap-year instead? I also hate Gregorius, pope or no. I'm grumpy. + */ +#define MINUTE 60 +#define HOUR (60*MINUTE) +#define DAY (24*HOUR) +#define YEAR (365*DAY) + +/* interestingly, we assume leap-years */ +static int month[12] = { + 0, + DAY*(31), + DAY*(31+29), + DAY*(31+29+31), + DAY*(31+29+31+30), + DAY*(31+29+31+30+31), + DAY*(31+29+31+30+31+30), + DAY*(31+29+31+30+31+30+31), + DAY*(31+29+31+30+31+30+31+31), + DAY*(31+29+31+30+31+30+31+31+30), + DAY*(31+29+31+30+31+30+31+31+30+31), + DAY*(31+29+31+30+31+30+31+31+30+31+30) +}; + +long kernel_mktime(struct tm * tm) +{ + long res; + int year; + + year = tm->tm_year - 70; +/* magic offsets (y+1) needed to get leapyears right.*/ + res = YEAR*year + DAY*((year+1)/4); + res += month[tm->tm_mon]; +/* and (y+2) here. If it wasn't a leap-year, we have to adjust */ + if (tm->tm_mon>1 && ((year+2)%4)) + res -= DAY; + res += DAY*(tm->tm_mday-1); + res += HOUR*tm->tm_hour; + res += MINUTE*tm->tm_min; + res += tm->tm_sec; + return res; +} diff --git a/linux/kernel/panic.c b/linux/kernel/panic.c new file mode 100644 index 0000000..feab0cc --- /dev/null +++ b/linux/kernel/panic.c @@ -0,0 +1,11 @@ +/* + * This function is used through-out the kernel (includeinh mm and fs) + * to indicate a major problem. + */ +#include <linux/kernel.h> + +volatile void panic(const char * s) +{ + printk("Kernel panic: %s\n\r",s); + for(;;); +} diff --git a/linux/kernel/printk.c b/linux/kernel/printk.c new file mode 100644 index 0000000..7a70dc3 --- /dev/null +++ b/linux/kernel/printk.c @@ -0,0 +1,33 @@ +/* + * When in kernel-mode, we cannot use printf, as fs is liable to + * point to 'interesting' things. Make a printf with fs-saving, and + * all is well. + */ +#include <stdarg.h> +#include <stddef.h> + +#include <linux/kernel.h> + +static char buf[1024]; + +int printk(const char *fmt, ...) +{ + va_list args; + int i; + + va_start(args, fmt); + i=vsprintf(buf,fmt,args); + va_end(args); + __asm__("push %%fs\n\t" + "push %%ds\n\t" + "pop %%fs\n\t" + "pushl %0\n\t" + "pushl $_buf\n\t" + "pushl $0\n\t" + "call _tty_write\n\t" + "addl $8,%%esp\n\t" + "popl %0\n\t" + "pop %%fs" + ::"r" (i):"ax","cx","dx"); + return i; +} diff --git a/linux/kernel/rs_io.s b/linux/kernel/rs_io.s new file mode 100644 index 0000000..62f075f --- /dev/null +++ b/linux/kernel/rs_io.s @@ -0,0 +1,141 @@ +/* + * rs_io.s + * + * This module implements the rs232 io interrupts. + */ + +.text +.globl _rs1_interrupt,_rs2_interrupt + +size = 1024 /* must be power of two ! + and must match the value + in tty_io.c!!! */ + +/* these are the offsets into the read/write buffer structures */ +rs_addr = 0 +head = 4 +tail = 8 +proc_list = 12 +buf = 16 + +startup = 256 /* chars left in write queue when we restart it */ + +/* + * These are the actual interrupt routines. They look where + * the interrupt is coming from, and take appropriate action. + */ +.align 2 +_rs1_interrupt: + pushl $_table_list+8 + jmp rs_int +.align 2 +_rs2_interrupt: + pushl $_table_list+16 +rs_int: + pushl %edx + pushl %ecx + pushl %ebx + pushl %eax + push %es + push %ds /* as this is an interrupt, we cannot */ + pushl $0x10 /* know that bs is ok. Load it */ + pop %ds + pushl $0x10 + pop %es + movl 24(%esp),%edx + movl (%edx),%edx + movl rs_addr(%edx),%edx + addl $2,%edx /* interrupt ident. reg */ +rep_int: + xorl %eax,%eax + inb %dx,%al + testb $1,%al + jne end + cmpb $6,%al /* this shouldn't happen, but ... */ + ja end + movl 24(%esp),%ecx + pushl %edx + subl $2,%edx + call jmp_table(,%eax,2) /* NOTE! not *4, bit0 is 0 already */ + popl %edx + jmp rep_int +end: movb $0x20,%al + outb %al,$0x20 /* EOI */ + pop %ds + pop %es + popl %eax + popl %ebx + popl %ecx + popl %edx + addl $4,%esp # jump over _table_list entry + iret + +jmp_table: + .long modem_status,write_char,read_char,line_status + +.align 2 +modem_status: + addl $6,%edx /* clear intr by reading modem status reg */ + inb %dx,%al + ret + +.align 2 +line_status: + addl $5,%edx /* clear intr by reading line status reg. */ + inb %dx,%al + ret + +.align 2 +read_char: + inb %dx,%al + movl %ecx,%edx + subl $_table_list,%edx + shrl $3,%edx + movl (%ecx),%ecx # read-queue + movl head(%ecx),%ebx + movb %al,buf(%ecx,%ebx) + incl %ebx + andl $size-1,%ebx + cmpl tail(%ecx),%ebx + je 1f + movl %ebx,head(%ecx) + pushl %edx + call _do_tty_interrupt + addl $4,%esp +1: ret + +.align 2 +write_char: + movl 4(%ecx),%ecx # write-queue + movl head(%ecx),%ebx + subl tail(%ecx),%ebx + andl $size-1,%ebx # nr chars in queue + je write_buffer_empty + cmpl $startup,%ebx + ja 1f + movl proc_list(%ecx),%ebx # wake up sleeping process + testl %ebx,%ebx # is there any? + je 1f + movl $0,(%ebx) +1: movl tail(%ecx),%ebx + movb buf(%ecx,%ebx),%al + outb %al,%dx + incl %ebx + andl $size-1,%ebx + movl %ebx,tail(%ecx) + cmpl head(%ecx),%ebx + je write_buffer_empty + ret +.align 2 +write_buffer_empty: + movl proc_list(%ecx),%ebx # wake up sleeping process + testl %ebx,%ebx # is there any? + je 1f + movl $0,(%ebx) +1: incl %edx + inb %dx,%al + jmp 1f +1: jmp 1f +1: andb $0xd,%al /* disable transmit interrupt */ + outb %al,%dx + ret diff --git a/linux/kernel/sched.c b/linux/kernel/sched.c new file mode 100644 index 0000000..03399fa --- /dev/null +++ b/linux/kernel/sched.c @@ -0,0 +1,254 @@ +/* + * 'sched.c' is the main kernel file. It contains scheduling primitives + * (sleep_on, wakeup, schedule etc) as well as a number of simple system + * call functions (type getpid(), which just extracts a field from + * current-task + */ +#include <linux/sched.h> +#include <linux/kernel.h> +#include <signal.h> +#include <linux/sys.h> +#include <asm/system.h> +#include <asm/io.h> +#include <asm/segment.h> + +#define LATCH (1193180/HZ) + +extern void mem_use(void); + +extern int timer_interrupt(void); +extern int system_call(void); + +union task_union { + struct task_struct task; + char stack[PAGE_SIZE]; +}; + +static union task_union init_task = {INIT_TASK,}; + +long volatile jiffies=0; +long startup_time=0; +struct task_struct *current = &(init_task.task), *last_task_used_math = NULL; + +struct task_struct * task[NR_TASKS] = {&(init_task.task), }; + +long user_stack [ PAGE_SIZE>>2 ] ; + +struct { + long * a; + short b; + } stack_start = { & user_stack [PAGE_SIZE>>2] , 0x10 }; +/* + * 'math_state_restore()' saves the current math information in the + * old math state array, and gets the new ones from the current task + */ +void math_state_restore() +{ + if (last_task_used_math) + __asm__("fnsave %0"::"m" (last_task_used_math->tss.i387)); + if (current->used_math) + __asm__("frstor %0"::"m" (current->tss.i387)); + else { + __asm__("fninit"::); + current->used_math=1; + } + last_task_used_math=current; +} + +/* + * 'schedule()' is the scheduler function. This is GOOD CODE! There + * probably won't be any reason to change this, as it should work well + * in all circumstances (ie gives IO-bound processes good response etc). + * The one thing you might take a look at is the signal-handler code here. + * + * NOTE!! Task 0 is the 'idle' task, which gets called when no other + * tasks can run. It can not be killed, and it cannot sleep. The 'state' + * information in task[0] is never used. + */ +void schedule(void) +{ + int i,next,c; + struct task_struct ** p; + +/* check alarm, wake up any interruptible tasks that have got a signal */ + + for(p = &LAST_TASK ; p > &FIRST_TASK ; --p) + if (*p) { + if ((*p)->alarm && (*p)->alarm < jiffies) { + (*p)->signal |= (1<<(SIGALRM-1)); + (*p)->alarm = 0; + } + if ((*p)->signal && (*p)->state==TASK_INTERRUPTIBLE) + (*p)->state=TASK_RUNNING; + } + +/* this is the scheduler proper: */ + + while (1) { + c = -1; + next = 0; + i = NR_TASKS; + p = &task[NR_TASKS]; + while (--i) { + if (!*--p) + continue; + if ((*p)->state == TASK_RUNNING && (*p)->counter > c) + c = (*p)->counter, next = i; + } + if (c) break; + for(p = &LAST_TASK ; p > &FIRST_TASK ; --p) + if (*p) + (*p)->counter = ((*p)->counter >> 1) + + (*p)->priority; + } + switch_to(next); +} + +int sys_pause(void) +{ + current->state = TASK_INTERRUPTIBLE; + schedule(); + return 0; +} + +void sleep_on(struct task_struct **p) +{ + struct task_struct *tmp; + + if (!p) + return; + if (current == &(init_task.task)) + panic("task[0] trying to sleep"); + tmp = *p; + *p = current; + current->state = TASK_UNINTERRUPTIBLE; + schedule(); + if (tmp) + tmp->state=0; +} + +void interruptible_sleep_on(struct task_struct **p) +{ + struct task_struct *tmp; + + if (!p) + return; + if (current == &(init_task.task)) + panic("task[0] trying to sleep"); + tmp=*p; + *p=current; +repeat: current->state = TASK_INTERRUPTIBLE; + schedule(); + if (*p && *p != current) { + (**p).state=0; + goto repeat; + } + *p=NULL; + if (tmp) + tmp->state=0; +} + +void wake_up(struct task_struct **p) +{ + if (p && *p) { + (**p).state=0; + *p=NULL; + } +} + +void do_timer(long cpl) +{ + if (cpl) + current->utime++; + else + current->stime++; + if ((--current->counter)>0) return; + current->counter=0; + if (!cpl) return; + schedule(); +} + +int sys_alarm(long seconds) +{ + current->alarm = (seconds>0)?(jiffies+HZ*seconds):0; + return seconds; +} + +int sys_getpid(void) +{ + return current->pid; +} + +int sys_getppid(void) +{ + return current->father; +} + +int sys_getuid(void) +{ + return current->uid; +} + +int sys_geteuid(void) +{ + return current->euid; +} + +int sys_getgid(void) +{ + return current->gid; +} + +int sys_getegid(void) +{ + return current->egid; +} + +int sys_nice(long increment) +{ + if (current->priority-increment>0) + current->priority -= increment; + return 0; +} + +int sys_signal(long signal,long addr,long restorer) +{ + long i; + + switch (signal) { + case SIGHUP: case SIGINT: case SIGQUIT: case SIGILL: + case SIGTRAP: case SIGABRT: case SIGFPE: case SIGUSR1: + case SIGSEGV: case SIGUSR2: case SIGPIPE: case SIGALRM: + case SIGCHLD: + i=(long) current->sig_fn[signal-1]; + current->sig_fn[signal-1] = (fn_ptr) addr; + current->sig_restorer = (fn_ptr) restorer; + return i; + default: return -1; + } +} + +void sched_init(void) +{ + int i; + struct desc_struct * p; + + set_tss_desc(gdt+FIRST_TSS_ENTRY,&(init_task.task.tss)); + set_ldt_desc(gdt+FIRST_LDT_ENTRY,&(init_task.task.ldt)); + p = gdt+2+FIRST_TSS_ENTRY; + for(i=1;i<NR_TASKS;i++) { + task[i] = NULL; + p->a=p->b=0; + p++; + p->a=p->b=0; + p++; + } + ltr(0); + lldt(0); + outb_p(0x36,0x43); /* binary, mode 3, LSB/MSB, ch 0 */ + outb_p(LATCH & 0xff , 0x40); /* LSB */ + outb(LATCH >> 8 , 0x40); /* MSB */ + set_intr_gate(0x20,&timer_interrupt); + outb(inb_p(0x21)&~0x01,0x21); + set_system_gate(0x80,&system_call); +} diff --git a/linux/kernel/serial.c b/linux/kernel/serial.c new file mode 100644 index 0000000..f542513 --- /dev/null +++ b/linux/kernel/serial.c @@ -0,0 +1,53 @@ +/* + * serial.c + * + * This module implements the rs232 io functions + * void rs_write(struct tty_struct * queue); + * void rs_init(void); + * and all interrupts pertaining to serial IO. + */ + +#include <linux/tty.h> +#include <linux/sched.h> +#include <asm/system.h> +#include <asm/io.h> + +#define WAKEUP_CHARS (TTY_BUF_SIZE/4) + +extern void rs1_interrupt(void); +extern void rs2_interrupt(void); + +static void init(int port) +{ + outb_p(0x80,port+3); /* set DLAB of line control reg */ + outb_p(0x30,port); /* LS of divisor (48 -> 2400 bps */ + outb_p(0x00,port+1); /* MS of divisor */ + outb_p(0x03,port+3); /* reset DLAB */ + outb_p(0x0b,port+4); /* set DTR,RTS, OUT_2 */ + outb_p(0x0d,port+1); /* enable all intrs but writes */ + (void)inb(port); /* read data port to reset things (?) */ +} + +void rs_init(void) +{ + set_intr_gate(0x24,rs1_interrupt); + set_intr_gate(0x23,rs2_interrupt); + init(tty_table[1].read_q.data); + init(tty_table[2].read_q.data); + outb(inb_p(0x21)&0xE7,0x21); +} + +/* + * This routine gets called when tty_write has put something into + * the write_queue. It must check wheter the queue is empty, and + * set the interrupt register accordingly + * + * void _rs_write(struct tty_struct * tty); + */ +void rs_write(struct tty_struct * tty) +{ + cli(); + if (!EMPTY(tty->write_q)) + outb(inb_p(tty->write_q.data+1)|0x02,tty->write_q.data+1); + sti(); +} diff --git a/linux/kernel/sys.c b/linux/kernel/sys.c new file mode 100644 index 0000000..f18ee7e --- /dev/null +++ b/linux/kernel/sys.c @@ -0,0 +1,216 @@ +#include <errno.h> + +#include <linux/sched.h> +#include <linux/tty.h> +#include <linux/kernel.h> +#include <asm/segment.h> +#include <sys/times.h> +#include <sys/utsname.h> + +int sys_ftime() +{ + return -ENOSYS; +} + +int sys_mknod() +{ + return -ENOSYS; +} + +int sys_break() +{ + return -ENOSYS; +} + +int sys_mount() +{ + return -ENOSYS; +} + +int sys_umount() +{ + return -ENOSYS; +} + +int sys_ustat(int dev,struct ustat * ubuf) +{ + return -1; +} + +int sys_ptrace() +{ + return -ENOSYS; +} + +int sys_stty() +{ + return -ENOSYS; +} + +int sys_gtty() +{ + return -ENOSYS; +} + +int sys_rename() +{ + return -ENOSYS; +} + +int sys_prof() +{ + return -ENOSYS; +} + +int sys_setgid(int gid) +{ + if (current->euid && current->uid) + if (current->gid==gid || current->sgid==gid) + current->egid=gid; + else + return -EPERM; + else + current->gid=current->egid=gid; + return 0; +} + +int sys_acct() +{ + return -ENOSYS; +} + +int sys_phys() +{ + return -ENOSYS; +} + +int sys_lock() +{ + return -ENOSYS; +} + +int sys_mpx() +{ + return -ENOSYS; +} + +int sys_ulimit() +{ + return -ENOSYS; +} + +int sys_time(long * tloc) +{ + int i; + + i = CURRENT_TIME; + if (tloc) { + verify_area(tloc,4); + put_fs_long(i,(unsigned long *)tloc); + } + return i; +} + +int sys_setuid(int uid) +{ + if (current->euid && current->uid) + if (uid==current->uid || current->suid==current->uid) + current->euid=uid; + else + return -EPERM; + else + current->euid=current->uid=uid; + return 0; +} + +int sys_stime(long * tptr) +{ + if (current->euid && current->uid) + return -1; + startup_time = get_fs_long((unsigned long *)tptr) - jiffies/HZ; + return 0; +} + +int sys_times(struct tms * tbuf) +{ + if (!tbuf) + return jiffies; + verify_area(tbuf,sizeof *tbuf); + put_fs_long(current->utime,(unsigned long *)&tbuf->tms_utime); + put_fs_long(current->stime,(unsigned long *)&tbuf->tms_stime); + put_fs_long(current->cutime,(unsigned long *)&tbuf->tms_cutime); + put_fs_long(current->cstime,(unsigned long *)&tbuf->tms_cstime); + return jiffies; +} + +int sys_brk(unsigned long end_data_seg) +{ + if (end_data_seg >= current->end_code && + end_data_seg < current->start_stack - 16384) + current->brk = end_data_seg; + return current->brk; +} + +/* + * This needs some heave checking ... + * I just haven't get the stomach for it. I also don't fully + * understand sessions/pgrp etc. Let somebody who does explain it. + */ +int sys_setpgid(int pid, int pgid) +{ + int i; + + if (!pid) + pid = current->pid; + if (!pgid) + pgid = pid; + for (i=0 ; i<NR_TASKS ; i++) + if (task[i] && task[i]->pid==pid) { + if (task[i]->leader) + return -EPERM; + if (task[i]->session != current->session) + return -EPERM; + task[i]->pgrp = pgid; + return 0; + } + return -ESRCH; +} + +int sys_getpgrp(void) +{ + return current->pgrp; +} + +int sys_setsid(void) +{ + if (current->uid && current->euid) + return -EPERM; + if (current->leader) + return -EPERM; + current->leader = 1; + current->session = current->pgrp = current->pid; + current->tty = -1; + return current->pgrp; +} + +int sys_uname(struct utsname * name) +{ + static struct utsname thisname = { + "linux .0","nodename","release ","version ","machine " + }; + int i; + + if (!name) return -1; + verify_area(name,sizeof *name); + for(i=0;i<sizeof *name;i++) + put_fs_byte(((char *) &thisname)[i],i+(char *) name); + return (0); +} + +int sys_umask(int mask) +{ + int old = current->umask; + + current->umask = mask & 0777; + return (old); +} diff --git a/linux/kernel/system_call.s b/linux/kernel/system_call.s new file mode 100644 index 0000000..df4f072 --- /dev/null +++ b/linux/kernel/system_call.s @@ -0,0 +1,219 @@ +/* + * system_call.s contains the system-call low-level handling routines. + * This also contains the timer-interrupt handler, as some of the code is + * the same. The hd-interrupt is also here. + * + * NOTE: This code handles signal-recognition, which happens every time + * after a timer-interrupt and after each system call. Ordinary interrupts + * don't handle signal-recognition, as that would clutter them up totally + * unnecessarily. + * + * Stack layout in 'ret_from_system_call': + * + * 0(%esp) - %eax + * 4(%esp) - %ebx + * 8(%esp) - %ecx + * C(%esp) - %edx + * 10(%esp) - %fs + * 14(%esp) - %es + * 18(%esp) - %ds + * 1C(%esp) - %eip + * 20(%esp) - %cs + * 24(%esp) - %eflags + * 28(%esp) - %oldesp + * 2C(%esp) - %oldss + */ + +SIG_CHLD = 17 +EAX = 0x00 +EBX = 0x04 +ECX = 0x08 +EDX = 0x0C +FS = 0x10 +ES = 0x14 +DS = 0x18 +EIP = 0x1C +CS = 0x20 +EFLAGS = 0x24 +OLDESP = 0x28 +OLDSS = 0x2C + +state = 0 # these are offsets into the task-struct. +counter = 4 +priority = 8 +signal = 12 +restorer = 16 # address of info-restorer +sig_fn = 20 # table of 32 signal addresses + +nr_system_calls = 67 + +.globl _system_call,_sys_fork,_timer_interrupt,_hd_interrupt,_sys_execve + +.align 2 +bad_sys_call: + movl $-1,%eax + iret +.align 2 +reschedule: + pushl $ret_from_sys_call + jmp _schedule +.align 2 +_system_call: + cmpl $nr_system_calls-1,%eax + ja bad_sys_call + push %ds + push %es + push %fs + pushl %edx + pushl %ecx # push %ebx,%ecx,%edx as parameters + pushl %ebx # to the system call + movl $0x10,%edx # set up ds,es to kernel space + mov %dx,%ds + mov %dx,%es + movl $0x17,%edx # fs points to local data space + mov %dx,%fs + call _sys_call_table(,%eax,4) + pushl %eax + movl _current,%eax + cmpl $0,state(%eax) # state + jne reschedule + cmpl $0,counter(%eax) # counter + je reschedule +ret_from_sys_call: + movl _current,%eax # task[0] cannot have signals + cmpl _task,%eax + je 3f + movl CS(%esp),%ebx # was old code segment supervisor + testl $3,%ebx # mode? If so - don't check signals + je 3f + cmpw $0x17,OLDSS(%esp) # was stack segment = 0x17 ? + jne 3f +2: movl signal(%eax),%ebx # signals (bitmap, 32 signals) + bsfl %ebx,%ecx # %ecx is signal nr, return if none + je 3f + btrl %ecx,%ebx # clear it + movl %ebx,signal(%eax) + movl sig_fn(%eax,%ecx,4),%ebx # %ebx is signal handler address + cmpl $1,%ebx + jb default_signal # 0 is default signal handler - exit + je 2b # 1 is ignore - find next signal + movl $0,sig_fn(%eax,%ecx,4) # reset signal handler address + incl %ecx + xchgl %ebx,EIP(%esp) # put new return address on stack + subl $28,OLDESP(%esp) + movl OLDESP(%esp),%edx # push old return address on stack + pushl %eax # but first check that it's ok. + pushl %ecx + pushl $28 + pushl %edx + call _verify_area + popl %edx + addl $4,%esp + popl %ecx + popl %eax + movl restorer(%eax),%eax + movl %eax,%fs:(%edx) # flag/reg restorer + movl %ecx,%fs:4(%edx) # signal nr + movl EAX(%esp),%eax + movl %eax,%fs:8(%edx) # old eax + movl ECX(%esp),%eax + movl %eax,%fs:12(%edx) # old ecx + movl EDX(%esp),%eax + movl %eax,%fs:16(%edx) # old edx + movl EFLAGS(%esp),%eax + movl %eax,%fs:20(%edx) # old eflags + movl %ebx,%fs:24(%edx) # old return addr +3: popl %eax + popl %ebx + popl %ecx + popl %edx + pop %fs + pop %es + pop %ds + iret + +default_signal: + incl %ecx + cmpl $SIG_CHLD,%ecx + je 2b + pushl %ecx + call _do_exit # remember to set bit 7 when dumping core + addl $4,%esp + jmp 3b + +.align 2 +_timer_interrupt: + push %ds # save ds,es and put kernel data space + push %es # into them. %fs is used by _system_call + push %fs + pushl %edx # we save %eax,%ecx,%edx as gcc doesn't + pushl %ecx # save those across function calls. %ebx + pushl %ebx # is saved as we use that in ret_sys_call + pushl %eax + movl $0x10,%eax + mov %ax,%ds + mov %ax,%es + movl $0x17,%eax + mov %ax,%fs + incl _jiffies + movb $0x20,%al # EOI to interrupt controller #1 + outb %al,$0x20 + movl CS(%esp),%eax + andl $3,%eax # %eax is CPL (0 or 3, 0=supervisor) + pushl %eax + call _do_timer # 'do_timer(long CPL)' does everything from + addl $4,%esp # task switching to accounting ... + jmp ret_from_sys_call + +.align 2 +_sys_execve: + lea EIP(%esp),%eax + pushl %eax + call _do_execve + addl $4,%esp + ret + +.align 2 +_sys_fork: + call _find_empty_process + testl %eax,%eax + js 1f + push %gs + pushl %esi + pushl %edi + pushl %ebp + pushl %eax + call _copy_process + addl $20,%esp +1: ret + +_hd_interrupt: + pushl %eax + pushl %ecx + pushl %edx + push %ds + push %es + push %fs + movl $0x10,%eax + mov %ax,%ds + mov %ax,%es + movl $0x17,%eax + mov %ax,%fs + movb $0x20,%al + outb %al,$0x20 # EOI to interrupt controller #1 + jmp 1f # give port chance to breathe +1: jmp 1f +1: outb %al,$0xA0 # same to controller #2 + movl _do_hd,%eax + testl %eax,%eax + jne 1f + movl $_unexpected_hd_interrupt,%eax +1: call *%eax # "interesting" way of handling intr. + pop %fs + pop %es + pop %ds + popl %edx + popl %ecx + popl %eax + iret + diff --git a/linux/kernel/traps.c b/linux/kernel/traps.c new file mode 100644 index 0000000..b6e8bdb --- /dev/null +++ b/linux/kernel/traps.c @@ -0,0 +1,199 @@ +/* + * 'Traps.c' handles hardware traps and faults after we have saved some + * state in 'asm.s'. Currently mostly a debugging-aid, will be extended + * to mainly kill the offending process (probably by giving it a signal, + * but possibly by killing it outright if necessary). + */ +#include <string.h> + +#include <linux/head.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <asm/system.h> +#include <asm/segment.h> + +#define get_seg_byte(seg,addr) ({ \ +register char __res; \ +__asm__("push %%fs;mov %%ax,%%fs;movb %%fs:%2,%%al;pop %%fs" \ + :"=a" (__res):"0" (seg),"m" (*(addr))); \ +__res;}) + +#define get_seg_long(seg,addr) ({ \ +register unsigned long __res; \ +__asm__("push %%fs;mov %%ax,%%fs;movl %%fs:%2,%%eax;pop %%fs" \ + :"=a" (__res):"0" (seg),"m" (*(addr))); \ +__res;}) + +#define _fs() ({ \ +register unsigned short __res; \ +__asm__("mov %%fs,%%ax":"=a" (__res):); \ +__res;}) + +int do_exit(long code); + +void page_exception(void); + +void divide_error(void); +void debug(void); +void nmi(void); +void int3(void); +void overflow(void); +void bounds(void); +void invalid_op(void); +void device_not_available(void); +void double_fault(void); +void coprocessor_segment_overrun(void); +void invalid_TSS(void); +void segment_not_present(void); +void stack_segment(void); +void general_protection(void); +void page_fault(void); +void coprocessor_error(void); +void reserved(void); + +static void die(char * str,long esp_ptr,long nr) +{ + long * esp = (long *) esp_ptr; + int i; + + printk("%s: %04x\n\r",str,nr&0xffff); + printk("EIP:\t%04x:%p\nEFLAGS:\t%p\nESP:\t%04x:%p\n", + esp[1],esp[0],esp[2],esp[4],esp[3]); + printk("fs: %04x\n",_fs()); + printk("base: %p, limit: %p\n",get_base(current->ldt[1]),get_limit(0x17)); + if (esp[4] == 0x17) { + printk("Stack: "); + for (i=0;i<4;i++) + printk("%p ",get_seg_long(0x17,i+(long *)esp[3])); + printk("\n"); + } + str(i); + printk("Pid: %d, process nr: %d\n\r",current->pid,0xffff & i); + for(i=0;i<10;i++) + printk("%02x ",0xff & get_seg_byte(esp[1],(i+(char *)esp[0]))); + printk("\n\r"); + do_exit(11); /* play segment exception */ +} + +void do_double_fault(long esp, long error_code) +{ + die("double fault",esp,error_code); +} + +void do_general_protection(long esp, long error_code) +{ + die("general protection",esp,error_code); +} + +void do_divide_error(long esp, long error_code) +{ + die("divide error",esp,error_code); +} + +void do_int3(long * esp, long error_code, + long fs,long es,long ds, + long ebp,long esi,long edi, + long edx,long ecx,long ebx,long eax) +{ + int tr; + + __asm__("str %%ax":"=a" (tr):"0" (0)); + printk("eax\t\tebx\t\tecx\t\tedx\n\r%8x\t%8x\t%8x\t%8x\n\r", + eax,ebx,ecx,edx); + printk("esi\t\tedi\t\tebp\t\tesp\n\r%8x\t%8x\t%8x\t%8x\n\r", + esi,edi,ebp,(long) esp); + printk("\n\rds\tes\tfs\ttr\n\r%4x\t%4x\t%4x\t%4x\n\r", + ds,es,fs,tr); + printk("EIP: %8x CS: %4x EFLAGS: %8x\n\r",esp[0],esp[1],esp[2]); +} + +void do_nmi(long esp, long error_code) +{ + die("nmi",esp,error_code); +} + +void do_debug(long esp, long error_code) +{ + die("debug",esp,error_code); +} + +void do_overflow(long esp, long error_code) +{ + die("overflow",esp,error_code); +} + +void do_bounds(long esp, long error_code) +{ + die("bounds",esp,error_code); +} + +void do_invalid_op(long esp, long error_code) +{ + die("invalid operand",esp,error_code); +} + +void do_device_not_available(long esp, long error_code) +{ + die("device not available",esp,error_code); +} + +void do_coprocessor_segment_overrun(long esp, long error_code) +{ + die("coprocessor segment overrun",esp,error_code); +} + +void do_invalid_TSS(long esp,long error_code) +{ + die("invalid TSS",esp,error_code); +} + +void do_segment_not_present(long esp,long error_code) +{ + die("segment not present",esp,error_code); +} + +void do_stack_segment(long esp,long error_code) +{ + die("stack segment",esp,error_code); +} + +void do_coprocessor_error(long esp, long error_code) +{ + die("coprocessor error",esp,error_code); +} + +void do_reserved(long esp, long error_code) +{ + die("reserved (15,17-31) error",esp,error_code); +} + +void trap_init(void) +{ + int i; + + set_trap_gate(0,÷_error); + set_trap_gate(1,&debug); + set_trap_gate(2,&nmi); + set_system_gate(3,&int3); /* int3-5 can be called from all */ + set_system_gate(4,&overflow); + set_system_gate(5,&bounds); + set_trap_gate(6,&invalid_op); + set_trap_gate(7,&device_not_available); + set_trap_gate(8,&double_fault); + set_trap_gate(9,&coprocessor_segment_overrun); + set_trap_gate(10,&invalid_TSS); + set_trap_gate(11,&segment_not_present); + set_trap_gate(12,&stack_segment); + set_trap_gate(13,&general_protection); + set_trap_gate(14,&page_fault); + set_trap_gate(15,&reserved); + set_trap_gate(16,&coprocessor_error); + for (i=17;i<32;i++) + set_trap_gate(i,&reserved); +/* __asm__("movl $0x3ff000,%%eax\n\t" + "movl %%eax,%%db0\n\t" + "movl $0x000d0303,%%eax\n\t" + "movl %%eax,%%db7" + :::"ax");*/ +} + diff --git a/linux/kernel/tty_io.c b/linux/kernel/tty_io.c new file mode 100644 index 0000000..68a390c --- /dev/null +++ b/linux/kernel/tty_io.c @@ -0,0 +1,306 @@ +/* + * 'tty_io.c' gives an orthogonal feeling to tty's, be they consoles + * or rs-channels. It also implements echoing, cooked mode etc (well, + * not currently, but ...) + */ +#include <ctype.h> +#include <errno.h> +#include <signal.h> + +#define ALRMMASK (1<<(SIGALRM-1)) + +#include <linux/sched.h> +#include <linux/tty.h> +#include <asm/segment.h> +#include <asm/system.h> + +#define _L_FLAG(tty,f) ((tty)->termios.c_lflag & f) +#define _I_FLAG(tty,f) ((tty)->termios.c_iflag & f) +#define _O_FLAG(tty,f) ((tty)->termios.c_oflag & f) + +#define L_CANON(tty) _L_FLAG((tty),ICANON) +#define L_ISIG(tty) _L_FLAG((tty),ISIG) +#define L_ECHO(tty) _L_FLAG((tty),ECHO) +#define L_ECHOE(tty) _L_FLAG((tty),ECHOE) +#define L_ECHOK(tty) _L_FLAG((tty),ECHOK) +#define L_ECHOCTL(tty) _L_FLAG((tty),ECHOCTL) +#define L_ECHOKE(tty) _L_FLAG((tty),ECHOKE) + +#define I_UCLC(tty) _I_FLAG((tty),IUCLC) +#define I_NLCR(tty) _I_FLAG((tty),INLCR) +#define I_CRNL(tty) _I_FLAG((tty),ICRNL) +#define I_NOCR(tty) _I_FLAG((tty),IGNCR) + +#define O_POST(tty) _O_FLAG((tty),OPOST) +#define O_NLCR(tty) _O_FLAG((tty),ONLCR) +#define O_CRNL(tty) _O_FLAG((tty),OCRNL) +#define O_NLRET(tty) _O_FLAG((tty),ONLRET) +#define O_LCUC(tty) _O_FLAG((tty),OLCUC) + +struct tty_struct tty_table[] = { + { + {0, + OPOST|ONLCR, /* change outgoing NL to CRNL */ + 0, + ICANON | ECHO | ECHOCTL | ECHOKE, + 0, /* console termio */ + INIT_C_CC}, + 0, /* initial pgrp */ + 0, /* initial stopped */ + con_write, + {0,0,0,0,""}, /* console read-queue */ + {0,0,0,0,""}, /* console write-queue */ + {0,0,0,0,""} /* console secondary queue */ + },{ + {0, /*IGNCR*/ + OPOST | ONLRET, /* change outgoing NL to CR */ + B2400 | CS8, + 0, + 0, + INIT_C_CC}, + 0, + 0, + rs_write, + {0x3f8,0,0,0,""}, /* rs 1 */ + {0x3f8,0,0,0,""}, + {0,0,0,0,""} + },{ + {0, /*IGNCR*/ + OPOST | ONLRET, /* change outgoing NL to CR */ + B2400 | CS8, + 0, + 0, + INIT_C_CC}, + 0, + 0, + rs_write, + {0x2f8,0,0,0,""}, /* rs 2 */ + {0x2f8,0,0,0,""}, + {0,0,0,0,""} + } +}; + +/* + * these are the tables used by the machine code handlers. + * you can implement pseudo-tty's or something by changing + * them. Currently not done. + */ +struct tty_queue * table_list[]={ + &tty_table[0].read_q, &tty_table[0].write_q, + &tty_table[1].read_q, &tty_table[1].write_q, + &tty_table[2].read_q, &tty_table[2].write_q + }; + +void tty_init(void) +{ + rs_init(); + con_init(); +} + +void tty_intr(struct tty_struct * tty, int signal) +{ + int i; + + if (tty->pgrp <= 0) + return; + for (i=0;i<NR_TASKS;i++) + if (task[i] && task[i]->pgrp==tty->pgrp) + task[i]->signal |= 1<<(signal-1); +} + +static void sleep_if_empty(struct tty_queue * queue) +{ + cli(); + while (!current->signal && EMPTY(*queue)) + interruptible_sleep_on(&queue->proc_list); + sti(); +} + +static void sleep_if_full(struct tty_queue * queue) +{ + if (!FULL(*queue)) + return; + cli(); + while (!current->signal && LEFT(*queue)<128) + interruptible_sleep_on(&queue->proc_list); + sti(); +} + +void copy_to_cooked(struct tty_struct * tty) +{ + signed char c; + + while (!EMPTY(tty->read_q) && !FULL(tty->secondary)) { + GETCH(tty->read_q,c); + if (c==13) + if (I_CRNL(tty)) + c=10; + else if (I_NOCR(tty)) + continue; + else ; + else if (c==10 && I_NLCR(tty)) + c=13; + if (I_UCLC(tty)) + c=tolower(c); + if (L_CANON(tty)) { + if (c==ERASE_CHAR(tty)) { + if (EMPTY(tty->secondary) || + (c=LAST(tty->secondary))==10 || + c==EOF_CHAR(tty)) + continue; + if (L_ECHO(tty)) { + if (c<32) + PUTCH(127,tty->write_q); + PUTCH(127,tty->write_q); + tty->write(tty); + } + DEC(tty->secondary.head); + continue; + } + if (c==STOP_CHAR(tty)) { + tty->stopped=1; + continue; + } + if (c==START_CHAR(tty)) { + tty->stopped=0; + continue; + } + } + if (!L_ISIG(tty)) { + if (c==INTR_CHAR(tty)) { + tty_intr(tty,SIGINT); + continue; + } + } + if (c==10 || c==EOF_CHAR(tty)) + tty->secondary.data++; + if (L_ECHO(tty)) { + if (c==10) { + PUTCH(10,tty->write_q); + PUTCH(13,tty->write_q); + } else if (c<32) { + if (L_ECHOCTL(tty)) { + PUTCH('^',tty->write_q); + PUTCH(c+64,tty->write_q); + } + } else + PUTCH(c,tty->write_q); + tty->write(tty); + } + PUTCH(c,tty->secondary); + } + wake_up(&tty->secondary.proc_list); +} + +int tty_read(unsigned channel, char * buf, int nr) +{ + struct tty_struct * tty; + char c, * b=buf; + int minimum,time,flag=0; + long oldalarm; + + if (channel>2 || nr<0) return -1; + tty = &tty_table[channel]; + oldalarm = current->alarm; + time = (unsigned) 10*tty->termios.c_cc[VTIME]; + minimum = (unsigned) tty->termios.c_cc[VMIN]; + if (time && !minimum) { + minimum=1; + if (flag=(!oldalarm || time+jiffies<oldalarm)) + current->alarm = time+jiffies; + } + if (minimum>nr) + minimum=nr; + while (nr>0) { + if (flag && (current->signal & ALRMMASK)) { + current->signal &= ~ALRMMASK; + break; + } + if (current->signal) + break; + if (EMPTY(tty->secondary) || (L_CANON(tty) && + !tty->secondary.data && LEFT(tty->secondary)>20)) { + sleep_if_empty(&tty->secondary); + continue; + } + do { + GETCH(tty->secondary,c); + if (c==EOF_CHAR(tty) || c==10) + tty->secondary.data--; + if (c==EOF_CHAR(tty) && L_CANON(tty)) + return (b-buf); + else { + put_fs_byte(c,b++); + if (!--nr) + break; + } + } while (nr>0 && !EMPTY(tty->secondary)); + if (time && !L_CANON(tty)) + if (flag=(!oldalarm || time+jiffies<oldalarm)) + current->alarm = time+jiffies; + else + current->alarm = oldalarm; + if (L_CANON(tty)) { + if (b-buf) + break; + } else if (b-buf >= minimum) + break; + } + current->alarm = oldalarm; + if (current->signal && !(b-buf)) + return -EINTR; + return (b-buf); +} + +int tty_write(unsigned channel, char * buf, int nr) +{ + static cr_flag=0; + struct tty_struct * tty; + char c, *b=buf; + + if (channel>2 || nr<0) return -1; + tty = channel + tty_table; + while (nr>0) { + sleep_if_full(&tty->write_q); + if (current->signal) + break; + while (nr>0 && !FULL(tty->write_q)) { + c=get_fs_byte(b); + if (O_POST(tty)) { + if (c=='\r' && O_CRNL(tty)) + c='\n'; + else if (c=='\n' && O_NLRET(tty)) + c='\r'; + if (c=='\n' && !cr_flag && O_NLCR(tty)) { + cr_flag = 1; + PUTCH(13,tty->write_q); + continue; + } + if (O_LCUC(tty)) + c=toupper(c); + } + b++; nr--; + cr_flag = 0; + PUTCH(c,tty->write_q); + } + tty->write(tty); + if (nr>0) + schedule(); + } + return (b-buf); +} + +/* + * Jeh, sometimes I really like the 386. + * This routine is called from an interrupt, + * and there should be absolutely no problem + * with sleeping even in an interrupt (I hope). + * Of course, if somebody proves me wrong, I'll + * hate intel for all time :-). We'll have to + * be careful and see to reinstating the interrupt + * chips before calling this, though. + */ +void do_tty_interrupt(int tty) +{ + copy_to_cooked(tty_table+tty); +} diff --git a/linux/kernel/vsprintf.c b/linux/kernel/vsprintf.c new file mode 100644 index 0000000..69c0578 --- /dev/null +++ b/linux/kernel/vsprintf.c @@ -0,0 +1,227 @@ +/* vsprintf.c -- Lars Wirzenius & Linus Torvalds. */ +/* + * Wirzenius wrote this portably, Torvalds fucked it up :-) + */ + +#include <stdarg.h> +#include <string.h> + +/* we use this so that we can do without the ctype library */ +#define is_digit(c) ((c) >= '0' && (c) <= '9') + +static int skip_atoi(const char **s) +{ + int i=0; + + while (is_digit(**s)) + i = i*10 + *((*s)++) - '0'; + return i; +} + +#define ZEROPAD 1 /* pad with zero */ +#define SIGN 2 /* unsigned/signed long */ +#define PLUS 4 /* show plus */ +#define SPACE 8 /* space if plus */ +#define LEFT 16 /* left justified */ +#define SPECIAL 32 /* 0x */ +#define SMALL 64 /* use 'abcdef' instead of 'ABCDEF' */ + +#define do_div(n,base) ({ \ +int __res; \ +__asm__("divl %4":"=a" (n),"=d" (__res):"0" (n),"1" (0),"r" (base)); \ +__res; }) + +static char * number(char * str, int num, int base, int size, int precision + ,int type) +{ + char c,sign,tmp[36]; + const char *digits="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + int i; + + if (type&SMALL) digits="0123456789abcdefghijklmnopqrstuvwxyz"; + if (type&LEFT) type &= ~ZEROPAD; + if (base<2 || base>36) + return 0; + c = (type & ZEROPAD) ? '0' : ' ' ; + if (type&SIGN && num<0) { + sign='-'; + num = -num; + } else + sign=(type&PLUS) ? '+' : ((type&SPACE) ? ' ' : 0); + if (sign) size--; + if (type&SPECIAL) + if (base==16) size -= 2; + else if (base==8) size--; + i=0; + if (num==0) + tmp[i++]='0'; + else while (num!=0) + tmp[i++]=digits[do_div(num,base)]; + if (i>precision) precision=i; + size -= precision; + if (!(type&(ZEROPAD+LEFT))) + while(size-->0) + *str++ = ' '; + if (sign) + *str++ = sign; + if (type&SPECIAL) + if (base==8) + *str++ = '0'; + else if (base==16) { + *str++ = '0'; + *str++ = digits[33]; + } + if (!(type&LEFT)) + while(size-->0) + *str++ = c; + while(i<precision--) + *str++ = '0'; + while(i-->0) + *str++ = tmp[i]; + while(size-->0) + *str++ = ' '; + return str; +} + +int vsprintf(char *buf, const char *fmt, va_list args) +{ + int len; + int i; + char * str; + char *s; + int *ip; + + int flags; /* flags to number() */ + + int field_width; /* width of output field */ + int precision; /* min. # of digits for integers; max + number of chars for from string */ + int qualifier; /* 'h', 'l', or 'L' for integer fields */ + + for (str=buf ; *fmt ; ++fmt) { + if (*fmt != '%') { + *str++ = *fmt; + continue; + } + + /* process flags */ + flags = 0; + repeat: + ++fmt; /* this also skips first '%' */ + switch (*fmt) { + case '-': flags |= LEFT; goto repeat; + case '+': flags |= PLUS; goto repeat; + case ' ': flags |= SPACE; goto repeat; + case '#': flags |= SPECIAL; goto repeat; + case '0': flags |= ZEROPAD; goto repeat; + } + + /* get field width */ + field_width = -1; + if (is_digit(*fmt)) + field_width = skip_atoi(&fmt); + else if (*fmt == '*') { + /* it's the next argument */ + field_width = va_arg(args, int); + if (field_width < 0) { + field_width = -field_width; + flags |= LEFT; + } + } + + /* get the precision */ + precision = -1; + if (*fmt == '.') { + ++fmt; + if (is_digit(*fmt)) + precision = skip_atoi(&fmt); + else if (*fmt == '*') { + /* it's the next argument */ + precision = va_arg(args, int); + } + if (precision < 0) + precision = 0; + } + + /* get the conversion qualifier */ + qualifier = -1; + if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L') { + qualifier = *fmt; + ++fmt; + } + + switch (*fmt) { + case 'c': + if (!(flags & LEFT)) + while (--field_width > 0) + *str++ = ' '; + *str++ = (unsigned char) va_arg(args, int); + while (--field_width > 0) + *str++ = ' '; + break; + + case 's': + s = va_arg(args, char *); + len = strlen(s); + if (precision < 0) + precision = len; + else if (len > precision) + len = precision; + + if (!(flags & LEFT)) + while (len < field_width--) + *str++ = ' '; + for (i = 0; i < len; ++i) + *str++ = *s++; + while (len < field_width--) + *str++ = ' '; + break; + + case 'o': + str = number(str, va_arg(args, unsigned long), 8, + field_width, precision, flags); + break; + + case 'p': + if (field_width == -1) { + field_width = 8; + flags |= ZEROPAD; + } + str = number(str, + (unsigned long) va_arg(args, void *), 16, + field_width, precision, flags); + break; + + case 'x': + flags |= SMALL; + case 'X': + str = number(str, va_arg(args, unsigned long), 16, + field_width, precision, flags); + break; + + case 'd': + case 'i': + flags |= SIGN; + case 'u': + str = number(str, va_arg(args, unsigned long), 10, + field_width, precision, flags); + break; + + case 'n': + ip = va_arg(args, int *); + *ip = (str - buf); + break; + + default: + if (*fmt != '%') + *str++ = '%'; + if (*fmt) + *str++ = *fmt; + else + --fmt; + break; + } + } + *str = '\0'; + return str-buf; +} diff --git a/linux/lib/Makefile b/linux/lib/Makefile new file mode 100644 index 0000000..a06698d --- /dev/null +++ b/linux/lib/Makefile @@ -0,0 +1,44 @@ +# +# Makefile for some libs needed in the kernel. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# + +AR =gar +AS =gas +LD =gld +LDFLAGS =-s -x +CC =gcc +CFLAGS =-Wall -O -fstrength-reduce -fomit-frame-pointer -fcombine-regs \ + -finline-functions -mstring-insns -nostdinc -I../include +CPP =gcc -E -nostdinc -I../include + +.c.s: + $(CC) $(CFLAGS) \ + -S -o $*.s $< +.s.o: + $(AS) -c -o $*.o $< +.c.o: + $(CC) $(CFLAGS) \ + -c -o $*.o $< + +OBJS = ctype.o _exit.o open.o close.o errno.o write.o dup.o setsid.o \ + execve.o wait.o string.o + +lib.a: $(OBJS) + $(AR) rcs lib.a $(OBJS) + sync + +clean: + rm -f core *.o *.a tmp_make + for i in *.c;do rm -f `basename $$i .c`.s;done + +dep: + sed '/\#\#\# Dependencies/q' < Makefile > tmp_make + (for i in *.c;do echo -n `echo $$i | sed 's,\.c,\.s,'`" "; \ + $(CPP) -M $$i;done) >> tmp_make + cp tmp_make Makefile + +### Dependencies: diff --git a/linux/lib/_exit.c b/linux/lib/_exit.c new file mode 100644 index 0000000..44a74e1 --- /dev/null +++ b/linux/lib/_exit.c @@ -0,0 +1,7 @@ +#define __LIBRARY__ +#include <unistd.h> + +volatile void _exit(int exit_code) +{ + __asm__("int $0x80"::"a" (__NR_exit),"b" (exit_code)); +} diff --git a/linux/lib/close.c b/linux/lib/close.c new file mode 100644 index 0000000..182d7eb --- /dev/null +++ b/linux/lib/close.c @@ -0,0 +1,4 @@ +#define __LIBRARY__ +#include <unistd.h> + +_syscall1(int,close,int,fd) diff --git a/linux/lib/ctype.c b/linux/lib/ctype.c new file mode 100644 index 0000000..bf58aac --- /dev/null +++ b/linux/lib/ctype.c @@ -0,0 +1,29 @@ +#include <ctype.h> + +char _ctmp; +unsigned char _ctype[] = {0x00, /* EOF */ +_C,_C,_C,_C,_C,_C,_C,_C, /* 0-7 */ +_C,_C|_S,_C|_S,_C|_S,_C|_S,_C|_S,_C,_C, /* 8-15 */ +_C,_C,_C,_C,_C,_C,_C,_C, /* 16-23 */ +_C,_C,_C,_C,_C,_C,_C,_C, /* 24-31 */ +_S|_SP,_P,_P,_P,_P,_P,_P,_P, /* 32-39 */ +_P,_P,_P,_P,_P,_P,_P,_P, /* 40-47 */ +_D,_D,_D,_D,_D,_D,_D,_D, /* 48-55 */ +_D,_D,_P,_P,_P,_P,_P,_P, /* 56-63 */ +_P,_U|_X,_U|_X,_U|_X,_U|_X,_U|_X,_U|_X,_U, /* 64-71 */ +_U,_U,_U,_U,_U,_U,_U,_U, /* 72-79 */ +_U,_U,_U,_U,_U,_U,_U,_U, /* 80-87 */ +_U,_U,_U,_P,_P,_P,_P,_P, /* 88-95 */ +_P,_L|_X,_L|_X,_L|_X,_L|_X,_L|_X,_L|_X,_L, /* 96-103 */ +_L,_L,_L,_L,_L,_L,_L,_L, /* 104-111 */ +_L,_L,_L,_L,_L,_L,_L,_L, /* 112-119 */ +_L,_L,_L,_P,_P,_P,_P,_C, /* 120-127 */ +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 128-143 */ +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 144-159 */ +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 160-175 */ +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 176-191 */ +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 192-207 */ +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 208-223 */ +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 224-239 */ +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; /* 240-255 */ + diff --git a/linux/lib/dup.c b/linux/lib/dup.c new file mode 100644 index 0000000..03bd5d0 --- /dev/null +++ b/linux/lib/dup.c @@ -0,0 +1,4 @@ +#define __LIBRARY__ +#include <unistd.h> + +_syscall1(int,dup,int,fd) diff --git a/linux/lib/errno.c b/linux/lib/errno.c new file mode 100644 index 0000000..6e7bb62 --- /dev/null +++ b/linux/lib/errno.c @@ -0,0 +1 @@ +int errno; diff --git a/linux/lib/execve.c b/linux/lib/execve.c new file mode 100644 index 0000000..03772e3 --- /dev/null +++ b/linux/lib/execve.c @@ -0,0 +1,4 @@ +#define __LIBRARY__ +#include <unistd.h> + +_syscall3(int,execve,const char *,file,char **,argv,char **,envp) diff --git a/linux/lib/open.c b/linux/lib/open.c new file mode 100644 index 0000000..057039c --- /dev/null +++ b/linux/lib/open.c @@ -0,0 +1,19 @@ +#define __LIBRARY__ +#include <unistd.h> +#include <stdarg.h> + +int open(const char * filename, int flag, ...) +{ + register int res; + va_list arg; + + va_start(arg,flag); + __asm__("int $0x80" + :"=a" (res) + :"0" (__NR_open),"b" (filename),"c" (flag), + "d" (va_arg(arg,int))); + if (res>=0) + return res; + errno = -res; + return -1; +} diff --git a/linux/lib/setsid.c b/linux/lib/setsid.c new file mode 100644 index 0000000..730abf0 --- /dev/null +++ b/linux/lib/setsid.c @@ -0,0 +1,4 @@ +#define __LIBRARY__ +#include <unistd.h> + +_syscall0(pid_t,setsid) diff --git a/linux/lib/string.c b/linux/lib/string.c new file mode 100644 index 0000000..f6befd9 --- /dev/null +++ b/linux/lib/string.c @@ -0,0 +1,8 @@ +#ifndef __GNUC__ +#error I want gcc! +#endif + +#define extern +#define inline +#define __LIBRARY__ +#include <string.h> diff --git a/linux/lib/wait.c b/linux/lib/wait.c new file mode 100644 index 0000000..a14555c --- /dev/null +++ b/linux/lib/wait.c @@ -0,0 +1,10 @@ +#define __LIBRARY__ +#include <unistd.h> +#include <sys/wait.h> + +_syscall3(pid_t,waitpid,pid_t,pid,int *,wait_stat,int,options) + +pid_t wait(int * wait_stat) +{ + return waitpid(-1,wait_stat,0); +} diff --git a/linux/lib/write.c b/linux/lib/write.c new file mode 100644 index 0000000..2613f17 --- /dev/null +++ b/linux/lib/write.c @@ -0,0 +1,4 @@ +#define __LIBRARY__ +#include <unistd.h> + +_syscall3(int,write,int,fd,const char *,buf,off_t,count) diff --git a/linux/mm/Makefile b/linux/mm/Makefile new file mode 100644 index 0000000..cee1f09 --- /dev/null +++ b/linux/mm/Makefile @@ -0,0 +1,37 @@ +CC =gcc +CFLAGS =-O -Wall -fstrength-reduce -fcombine-regs -fomit-frame-pointer \ + -finline-functions -nostdinc -I../include +AS =gas +AR =gar +LD =gld +CPP =gcc -E -nostdinc -I../include + +.c.o: + $(CC) $(CFLAGS) \ + -c -o $*.o $< +.s.o: + $(AS) -o $*.o $< +.c.s: + $(CC) $(CFLAGS) \ + -S -o $*.s $< + +OBJS = memory.o page.o + +all: mm.o + +mm.o: $(OBJS) + $(LD) -r -o mm.o $(OBJS) + +clean: + rm -f core *.o *.a tmp_make + for i in *.c;do rm -f `basename $$i .c`.s;done + +dep: + sed '/\#\#\# Dependencies/q' < Makefile > tmp_make + (for i in *.c;do $(CPP) -M $$i;done) >> tmp_make + cp tmp_make Makefile + +### Dependencies: +memory.o : memory.c ../include/signal.h ../include/sys/types.h \ + ../include/linux/config.h ../include/linux/head.h ../include/linux/kernel.h \ + ../include/asm/system.h diff --git a/linux/mm/memory.c b/linux/mm/memory.c new file mode 100644 index 0000000..7cdcfb6 --- /dev/null +++ b/linux/mm/memory.c @@ -0,0 +1,264 @@ +#include <signal.h> + +#include <linux/config.h> +#include <linux/head.h> +#include <linux/kernel.h> +#include <asm/system.h> + +int do_exit(long code); + +#define invalidate() \ +__asm__("movl %%eax,%%cr3"::"a" (0)) + +#if (BUFFER_END < 0x100000) +#define LOW_MEM 0x100000 +#else +#define LOW_MEM BUFFER_END +#endif + +/* these are not to be changed - thay are calculated from the above */ +#define PAGING_MEMORY (HIGH_MEMORY - LOW_MEM) +#define PAGING_PAGES (PAGING_MEMORY/4096) +#define MAP_NR(addr) (((addr)-LOW_MEM)>>12) + +#if (PAGING_PAGES < 10) +#error "Won't work" +#endif + +#define copy_page(from,to) \ +__asm__("cld ; rep ; movsl"::"S" (from),"D" (to),"c" (1024):"cx","di","si") + +static unsigned short mem_map [ PAGING_PAGES ] = {0,}; + +/* + * Get physical address of first (actually last :-) free page, and mark it + * used. If no free pages left, return 0. + */ +unsigned long get_free_page(void) +{ +register unsigned long __res asm("ax"); + +__asm__("std ; repne ; scasw\n\t" + "jne 1f\n\t" + "movw $1,2(%%edi)\n\t" + "sall $12,%%ecx\n\t" + "movl %%ecx,%%edx\n\t" + "addl %2,%%edx\n\t" + "movl $1024,%%ecx\n\t" + "leal 4092(%%edx),%%edi\n\t" + "rep ; stosl\n\t" + "movl %%edx,%%eax\n" + "1:" + :"=a" (__res) + :"0" (0),"i" (LOW_MEM),"c" (PAGING_PAGES), + "D" (mem_map+PAGING_PAGES-1) + :"di","cx","dx"); +return __res; +} + +/* + * Free a page of memory at physical address 'addr'. Used by + * 'free_page_tables()' + */ +void free_page(unsigned long addr) +{ + if (addr<LOW_MEM) return; + if (addr>HIGH_MEMORY) + panic("trying to free nonexistent page"); + addr -= LOW_MEM; + addr >>= 12; + if (mem_map[addr]--) return; + mem_map[addr]=0; + panic("trying to free free page"); +} + +/* + * This function frees a continuos block of page tables, as needed + * by 'exit()'. As does copy_page_tables(), this handles only 4Mb blocks. + */ +int free_page_tables(unsigned long from,unsigned long size) +{ + unsigned long *pg_table; + unsigned long * dir, nr; + + if (from & 0x3fffff) + panic("free_page_tables called with wrong alignment"); + if (!from) + panic("Trying to free up swapper memory space"); + size = (size + 0x3fffff) >> 22; + dir = (unsigned long *) ((from>>20) & 0xffc); /* _pg_dir = 0 */ + for ( ; size-->0 ; dir++) { + if (!(1 & *dir)) + continue; + pg_table = (unsigned long *) (0xfffff000 & *dir); + for (nr=0 ; nr<1024 ; nr++) { + if (1 & *pg_table) + free_page(0xfffff000 & *pg_table); + *pg_table = 0; + pg_table++; + } + free_page(0xfffff000 & *dir); + *dir = 0; + } + invalidate(); + return 0; +} + +/* + * Well, here is one of the most complicated functions in mm. It + * copies a range of linerar addresses by copying only the pages. + * Let's hope this is bug-free, 'cause this one I don't want to debug :-) + * + * Note! We don't copy just any chunks of memory - addresses have to + * be divisible by 4Mb (one page-directory entry), as this makes the + * function easier. It's used only by fork anyway. + * + * NOTE 2!! When from==0 we are copying kernel space for the first + * fork(). Then we DONT want to copy a full page-directory entry, as + * that would lead to some serious memory waste - we just copy the + * first 160 pages - 640kB. Even that is more than we need, but it + * doesn't take any more memory - we don't copy-on-write in the low + * 1 Mb-range, so the pages can be shared with the kernel. Thus the + * special case for nr=xxxx. + */ +int copy_page_tables(unsigned long from,unsigned long to,long size) +{ + unsigned long * from_page_table; + unsigned long * to_page_table; + unsigned long this_page; + unsigned long * from_dir, * to_dir; + unsigned long nr; + + if ((from&0x3fffff) || (to&0x3fffff)) + panic("copy_page_tables called with wrong alignment"); + from_dir = (unsigned long *) ((from>>20) & 0xffc); /* _pg_dir = 0 */ + to_dir = (unsigned long *) ((to>>20) & 0xffc); + size = ((unsigned) (size+0x3fffff)) >> 22; + for( ; size-->0 ; from_dir++,to_dir++) { + if (1 & *to_dir) + panic("copy_page_tables: already exist"); + if (!(1 & *from_dir)) + continue; + from_page_table = (unsigned long *) (0xfffff000 & *from_dir); + if (!(to_page_table = (unsigned long *) get_free_page())) + return -1; /* Out of memory, see freeing */ + *to_dir = ((unsigned long) to_page_table) | 7; + nr = (from==0)?0xA0:1024; + for ( ; nr-- > 0 ; from_page_table++,to_page_table++) { + this_page = *from_page_table; + if (!(1 & this_page)) + continue; + this_page &= ~2; + *to_page_table = this_page; + if (this_page > LOW_MEM) { + *from_page_table = this_page; + this_page -= LOW_MEM; + this_page >>= 12; + mem_map[this_page]++; + } + } + } + invalidate(); + return 0; +} + +/* + * This function puts a page in memory at the wanted address. + * It returns the physical address of the page gotten, 0 if + * out of memory (either when trying to access page-table or + * page.) + */ +unsigned long put_page(unsigned long page,unsigned long address) +{ + unsigned long tmp, *page_table; + +/* NOTE !!! This uses the fact that _pg_dir=0 */ + + if (page < LOW_MEM || page > HIGH_MEMORY) + printk("Trying to put page %p at %p\n",page,address); + if (mem_map[(page-LOW_MEM)>>12] != 1) + printk("mem_map disagrees with %p at %p\n",page,address); + page_table = (unsigned long *) ((address>>20) & 0xffc); + if ((*page_table)&1) + page_table = (unsigned long *) (0xfffff000 & *page_table); + else { + if (!(tmp=get_free_page())) + return 0; + *page_table = tmp|7; + page_table = (unsigned long *) tmp; + } + page_table[(address>>12) & 0x3ff] = page | 7; + return page; +} + +void un_wp_page(unsigned long * table_entry) +{ + unsigned long old_page,new_page; + + old_page = 0xfffff000 & *table_entry; + if (old_page >= LOW_MEM && mem_map[MAP_NR(old_page)]==1) { + *table_entry |= 2; + return; + } + if (!(new_page=get_free_page())) + do_exit(SIGSEGV); + if (old_page >= LOW_MEM) + mem_map[MAP_NR(old_page)]--; + *table_entry = new_page | 7; + copy_page(old_page,new_page); +} + +/* + * This routine handles present pages, when users try to write + * to a shared page. It is done by copying the page to a new address + * and decrementing the shared-page counter for the old page. + */ +void do_wp_page(unsigned long error_code,unsigned long address) +{ + un_wp_page((unsigned long *) + (((address>>10) & 0xffc) + (0xfffff000 & + *((unsigned long *) ((address>>20) &0xffc))))); + +} + +void write_verify(unsigned long address) +{ + unsigned long page; + + if (!( (page = *((unsigned long *) ((address>>20) & 0xffc)) )&1)) + return; + page &= 0xfffff000; + page += ((address>>10) & 0xffc); + if ((3 & *(unsigned long *) page) == 1) /* non-writeable, present */ + un_wp_page((unsigned long *) page); + return; +} + +void do_no_page(unsigned long error_code,unsigned long address) +{ + unsigned long tmp; + + if (tmp=get_free_page()) + if (put_page(tmp,address)) + return; + do_exit(SIGSEGV); +} + +void calc_mem(void) +{ + int i,j,k,free=0; + long * pg_tbl; + + for(i=0 ; i<PAGING_PAGES ; i++) + if (!mem_map[i]) free++; + printk("%d pages free (of %d)\n\r",free,PAGING_PAGES); + for(i=2 ; i<1024 ; i++) { + if (1&pg_dir[i]) { + pg_tbl=(long *) (0xfffff000 & pg_dir[i]); + for(j=k=0 ; j<1024 ; j++) + if (pg_tbl[j]&1) + k++; + printk("Pg-dir[%d] uses %d pages\n",i,k); + } + } +} diff --git a/linux/mm/page.s b/linux/mm/page.s new file mode 100644 index 0000000..27488c2 --- /dev/null +++ b/linux/mm/page.s @@ -0,0 +1,34 @@ +/* + * page.s contains the low-level page-exception code. + * the real work is done in mm.c + */ + +.globl _page_fault + +_page_fault: + xchgl %eax,(%esp) + pushl %ecx + pushl %edx + push %ds + push %es + push %fs + movl $0x10,%edx + mov %dx,%ds + mov %dx,%es + mov %dx,%fs + movl %cr2,%edx + pushl %edx + pushl %eax + testl $1,%eax + jne 1f + call _do_no_page + jmp 2f +1: call _do_wp_page +2: addl $8,%esp + pop %fs + pop %es + pop %ds + popl %edx + popl %ecx + popl %eax + iret diff --git a/linux/tools/build.c b/linux/tools/build.c new file mode 100644 index 0000000..6afe58c --- /dev/null +++ b/linux/tools/build.c @@ -0,0 +1,68 @@ +#include <stdio.h> /* fprintf */ +#include <stdlib.h> /* contains exit */ +#include <sys/types.h> /* unistd.h needs this */ +#include <unistd.h> /* contains read/write */ +#include <fcntl.h> + +#define MINIX_HEADER 32 +#define GCC_HEADER 1024 + +void die(char * str) +{ + fprintf(stderr,"%s\n",str); + exit(1); +} + +void usage(void) +{ + die("Usage: build boot system [> image]"); +} + +int main(int argc, char ** argv) +{ + int i,c,id; + char buf[1024]; + + if (argc != 3) + usage(); + for (i=0;i<sizeof buf; i++) buf[i]=0; + if ((id=open(argv[1],O_RDONLY,0))<0) + die("Unable to open 'boot'"); + if (read(id,buf,MINIX_HEADER) != MINIX_HEADER) + die("Unable to read header of 'boot'"); + if (((long *) buf)[0]!=0x04100301) + die("Non-Minix header of 'boot'"); + if (((long *) buf)[1]!=MINIX_HEADER) + die("Non-Minix header of 'boot'"); + if (((long *) buf)[3]!=0) + die("Illegal data segment in 'boot'"); + if (((long *) buf)[4]!=0) + die("Illegal bss in 'boot'"); + if (((long *) buf)[5] != 0) + die("Non-Minix header of 'boot'"); + if (((long *) buf)[7] != 0) + die("Illegal symbol table in 'boot'"); + i=read(id,buf,sizeof buf); + fprintf(stderr,"Boot sector %d bytes.\n",i); + if (i>510) + die("Boot block may not exceed 510 bytes"); + buf[510]=0x55; + buf[511]=0xAA; + i=write(1,buf,512); + if (i!=512) + die("Write call failed"); + close (id); + + if ((id=open(argv[2],O_RDONLY,0))<0) + die("Unable to open 'system'"); + if (read(id,buf,GCC_HEADER) != GCC_HEADER) + die("Unable to read header of 'system'"); + if (((long *) buf)[5] != 0) + die("Non-GCC header of 'system'"); + for (i=0 ; (c=read(id,buf,sizeof buf))>0 ; i+=c ) + if (write(1,buf,c)!=c) + die("Write call failed"); + close(id); + fprintf(stderr,"System %d bytes.\n",i); + return(0); +} |
