ftp.nice.ch/peanuts/GeneralData/Usenet/news/1991/CSNProg-91.tar.gz#/comp-sys-next-programmer/1991/Dec/Shared-libraries-and-MallocDebug

This is Shared-libraries-and-MallocDebug in view mode; [Up]


Date: Sun 24-Dec-1991 23:41:59 From: bediger@nugget.rmnug.org (Bruce Ediger) Subject: Shared libraries and MallocDebug I was curious about shared libraries and what startup code does, so I made an attempt to discover the true meaning and inner workings of crt0.o, the initial startup code that ld links into each and every compiled program. Things I learned: 1. the start() routine is almost certainly not compiled from C code. ld actually sets up the Mach-O executable file header such that the address of the start() function (in /lib/crt0.o) is the entry point of the program. This entry point resides in the "LC_UNIXTHREAD" load command. 2. start() definitely calls _init_shlibs(), conditionally calls mach_init_routine(), _cthread_init_routine() and _objcInit() before calling main(). _init_shlibs() is included in crt0.o object file. The others are all "Absolute" symbols in shared library /usr/shlib/libsys_s.B.shlib . 3. _init_shlibs() is probably compiled from C source code, without a -O option. I can be almost duplicate it by C compiled to assembly. From its name, I assume that it initializes shared libraries. In point of fact, it does things based on the contents of the __fvmlib_init0 section of the __TEXT segment of the user's Mach-O executable. 4. based on a disassembly of shared library (otool -v -t /usr/shlib/libsys_s.B.shlib) there's a branch table living in the __text section of the __TEXT segment of the memory map of the file. Branch table runs from 0x0500205a to 0x05006000. There appear to be some ranges of slots that are open for expansion/addition. 5. functions residing in shared library are compiled in the user program as a bsrl to an address in the branch table of the shared library. At that address is a jmp absolute long to another address in the shared library. Branch table consists of many 6 byte slots: first 2 bytes are "4ef9" (jmpl absolute) followed by a 4 byte address. Based on item 5, there are about 2715 slots. Since the __TEXT segment has "read" and "execute" permissions, this leaves open the question of exactly what the _init_shlibs() function in crt0.o accomplishes. It can't be writing values into the branch table since this would cause a segmentation violation. 6. _init_shlibs() function in crt0.o fills in a table in the __data section of the __DATA segment of the shared library. The table contains addresses of symbols in user's code that are used indirectly by library code. Apparently this table building is actually filling in values of static variables used by functions in the shared libraries. Since all of the values in the table are addresses, I assume that this is a fill-in of pointers. For example, _init_shlibs() puts the (user's data segment) address of NXArgc and NXArgv in the shared library data segment. 7. There appear to be only about 41 variables in /usr/lib/libsys_s.B.shlib that get filled in by _init_shlibs(). Based on the output of nm run on libsys_s.B.shlib they are: 04010620 D __libsys_NXAddRegion 0401064c D __libsys_NXArgc 040105f4 D __libsys_NXArgv 04010608 D __libsys_NXCreateChildZone 04010604 D __libsys_NXCreateZone 0401061c D __libsys_NXDefaultMallocZone 04010624 D __libsys_NXMallocCheck 0401060c D __libsys_NXMergeZone 04010648 D __libsys_NXNameZone 04010628 D __libsys_NXRemoveRegion 04010610 D __libsys_NXZoneCalloc 04010614 D __libsys_NXZoneFromPtr 04010618 D __libsys_NXZonePtrInfo 04010634 D __libsys__NXMallocDumpFrees 04010638 D __libsys__NXMallocDumpZones 040105bc D __libsys__longjmp 04010644 D __libsys__malloc_fork_child 04010640 D __libsys__malloc_fork_parent 0401063c D __libsys__malloc_fork_prepare 040105c4 D __libsys__mh_execute_header 040105b8 D __libsys__setjmp 040105d0 D __libsys_calloc 040105f8 D __libsys_catch_exception_raise 040105c0 D __libsys_environ 040105c8 D __libsys_exit 040105e0 D __libsys_free 04010600 D __libsys_longjmp 040105cc D __libsys_malloc 040105e8 D __libsys_malloc_debug 040105ec D __libsys_malloc_error 04010650 D __libsys_malloc_freezedry 040105f0 D __libsys_malloc_good_size 04010654 D __libsys_malloc_jumpstart 0401062c D __libsys_malloc_singlethreaded 040105e4 D __libsys_malloc_size 040105dc D __libsys_mstats 04010658 d __libsys_pointers_pad 040105d4 D __libsys_realloc 040105fc D __libsys_setjmp 040105d8 D __libsys_valloc 04010630 D __libsys_vfree You can also get the correspondence of user data segment address to shared library data segment address by running "otool -v -s __TEXT __fvmlib_init0" on any executable. It would also appear that there's a security flaw here: a user's program could subsitute the address of one of it's own routines for one of the shared library routines that gets called by derefencing a function pointer. I'm not entirely sure how this would get done, but it smells like it could be a problem. 8. The __fvmlib_init0 section of __TEXT segment is definitely how the MallocDebug replacements for malloc() and relatives takes place. Using otool -v -s __TEXT __fvmlib_init0 on 2 executables produced from the same source, one compiled with MallocDebug, one without illustrates the difference. When compiled with -lMallocDebug, malloc() resides in user's __TEXT segment. When compiled ordinarily, code for malloc() is in shared libraries __TEXT segment.

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Marcel Waldvogel and Netfuture.ch.