~4Dgifts/toolbox/src/swtools/DSOs/forum93 README `!' indicates new or updated as of version 4.2 ! forum93.showcase: updated to now include information on 64 bit This paper refers to the 8 sub-directories containing the case studies code and Makefiles, and should be studied in concert with the "forum93.showcase" file included here as well. Case Studies in Dynamic Shared Objects Jay L. Gischer MIPS Technologies, Inc, In this paper we study several issues related to the use of dynamic shared objects (DSO) as implemented in the IRIX (tm) 5.1 operating system. Each of the cases is based upon a real problem or issue that has occurred during the last year or so. But the names have been changed and the problems simplified for legal and pedagogical reasons. CASE 1: Coordinating the build of multiple shared objects to avoid collisions. CASE 2: Side-by-side comparison of calling overhead for a typical procedure call. Compares DSO overhead to static shared library overhead. All builds are done with cc -c -O. CASE 3: This case looks at how to best write leaf/small routines to tighten the coding. CASE 4: We examine certain loops with calls to very short functions, such as are found in the IRIX GL (tm) graphics library, and see how to reduce the loop overhead for such code. For correctness, it is necessary for the compiler to generate code for function calls which loads the address of the function each time it is called. This is because the address could potentially change during a call. These examples show that by taking the address of the function and calling through a function pointer, the developer can effectively guarantee to the compiler that the address will not change, moving the load of the address out of the loop. CASE 5: Handling platform-dependent configurations. CASE 6: This case shows how shared objects can reduce the redundancy of some link lines because of the different linking semantics of DSO vs. archives. CASE 7: Speeding the development cycle by reducing build/link time. Avoid relinking everything by putting the bulk of code into a shared object and preempting selected portions. CASE 8: Using dlopen(), dlsym(), dlerror() to configure software at runtime. The following example is based on languages but is not the recommended method for internationalization. # CASE 1. # # Coordinating the build of multiple shared objects # to avoid collisions. # # First the naive way. 6 gischer@puget: ls Makefile mymain.c myso1/ myso2/ 7 gischer@puget: more Makefile mymain.c :::::::::::::: Makefile :::::::::::::: RPATH = $(PWD)/myso1:$(PWD)/myso2 default: (cd myso1; make) (cd myso2; make) make mymain mymain: mymain.o cc -Wl,-rpath,$(RPATH) mymain.o myso1/myso1.so myso2/myso2.so -o mymain :::::::::::::: mymain.c :::::::::::::: void foo1(void); void foo2(void); main() { foo1(); foo2(); } 8 gischer@puget: cd myso1 9 gischer@puget: more * :::::::::::::: Makefile :::::::::::::: myso1.so: myso1.o ld -shared myso1.o -o myso1.so :::::::::::::: myso1.c :::::::::::::: #include <stdio.h> void foo1(void) { printf("This is foo1\n"); } :::::::::::::: so_locations :::::::::::::: myso1.so \ :st = .text 0x5ffe0000, 0x00010000:\ :st = .data 0x5fff0000, 0x00010000:\ 10 gischer@puget: cd ../myso2 11 gischer@puget: more * :::::::::::::: Makefile :::::::::::::: myso2.so: myso2.o ld -shared myso2.o -o myso2.so :::::::::::::: myso2.c :::::::::::::: #include <stdio.h> void foo2(void) { printf("This is foo2\n"); } :::::::::::::: so_locations :::::::::::::: myso2.so \ :st = .text 0x5ffe0000, 0x00010000:\ :st = .data 0x5fff0000, 0x00010000:\ # Now we build the libraries and examine them to verify that they # collide # 12 gischer@puget: cd .. 13 gischer@puget: make (cd myso1; make) cc -O -c myso1.c ld -shared myso1.o -o myso1.so (cd myso2; make) `myso2.so' is up to date. make mymain cc -O -c mymain.c cc -Wl,-rpath,/usr/people/gischer/forum/case1/slow/myso1:/usr/people/gischer/forum/case1/slow/myso2 mymain.o myso1/myso1.so myso2/myso2.so -o mymain 14 gischer@puget: elfdump -o myso1/myso1.so ***PROGRAM HEADER*** Type Offset Vaddr Paddr Filesz Memsz Align RWX REGINFO 0x000000c0 0x5ffe00c0 0x5ffe00c0 0x00000018 0x00000018 0x00000004 r-- DYNAMIC 0x00000100 0x5ffe0100 0x5ffe0100 0x00000240 0x00000240 0x00000010 r-- LOAD 0x00000000 0x5ffe0000 0x5ffe0000 0x00001000 0x00001000 0x00010000 r-x LOAD 0x00001000 0x5fff0000 0x5fff0000 0x00001000 0x00001000 0x00010000 rw- 15 gischer@puget: elfdump -o myso2/myso2.so ***PROGRAM HEADER*** Type Offset Vaddr Paddr Filesz Memsz Align RWX REGINFO 0x000000c0 0x5ffe00c0 0x5ffe00c0 0x00000018 0x00000018 0x00000004 r-- DYNAMIC 0x00000100 0x5ffe0100 0x5ffe0100 0x00000240 0x00000240 0x00000010 r-- LOAD 0x00000000 0x5ffe0000 0x5ffe0000 0x00001000 0x00001000 0x00010000 r-x LOAD 0x00001000 0x5fff0000 0x5fff0000 0x00001000 0x00001000 0x00010000 rw- # now we use a central registry file to coordinate. # 6 gischer@puget: ls Makefile myso1/ so_locations.init mymain.c myso2/ 7 gischer@puget: more Makefile mymain.c so_locations.init :::::::::::::: Makefile :::::::::::::: RPATH = $(PWD)/myso1:$(PWD)/myso2 default: so_locations (cd myso1; make) (cd myso2; make) make mymain mymain: mymain.o cc -Wl,-rpath,$(RPATH) mymain.o myso1/myso1.so myso2/myso2.so -o mymain so_locations: cp so_locations.init so_locations :::::::::::::: mymain.c :::::::::::::: void foo1(void); void foo2(void); main() { foo1(); foo2(); } :::::::::::::: so_locations.init :::::::::::::: $start_address=0x40000000 reserved\ :st = $range 0x3f000000,0x01000000: myso1.so\ :st = $range 0x3e000000,0x01000000: myso2.so\ :st = $range 0x3d000000,0x01000000: 8 gischer@puget: cd myso1 9 gischer@puget: more * :::::::::::::: Makefile :::::::::::::: myso1.so: myso1.o ld -shared myso1.o -update_registry ../so_locations -o myso1.so :::::::::::::: myso1.c :::::::::::::: #include <stdio.h> foo1(void) { printf("This is foo1\n"); } 10 gischer@puget: cd ../myso2 11 gischer@puget: more * :::::::::::::: Makefile :::::::::::::: myso2.so: myso2.o ld -shared myso2.o -update_registry ../so_locations -o myso2.so :::::::::::::: myso2.c :::::::::::::: #include <stdio.h> void foo2(void) { printf("This is foo2\n"); } 12 gischer@puget: cd .. 13 gischer@puget: make cp so_locations.init so_locations (cd myso1; make) cc -O -c myso1.c ld -shared myso1.o -update_registry ../so_locations -o myso1.so (cd myso2; make) cc -O -c myso2.c ld -shared myso2.o -update_registry ../so_locations -o myso2.so make mymain cc -O -c mymain.c cc -Wl,-rpath,/usr/people/gischer/forum/case1/fast/myso1:/usr/people/gischer/forum/case1/fast/myso2 mymain.o myso1/myso1.so myso2/myso2.so -o mymain 14 gischer@puget: ls Makefile mymain.c myso1/ so_locations mymain* mymain.o myso2/ so_locations.init 15 gischer@puget: more so_locations $start_address=0x40000000 reserved \ :st = $range 0x3f000000, 0x01000000:\ myso1.so \ :st = $range 0x3e000000, 0x01000000:\ :st = .text 0x3efe0000, 0x00010000:\ :st = .data 0x3eff0000, 0x00010000:\ myso2.so \ :st = $range 0x3d000000, 0x01000000:\ :st = .text 0x3dfe0000, 0x00010000:\ :st = .data 0x3dff0000, 0x00010000:\ # CASE 2 # # Side-by-side comparison of calling overhead for a typical procedure # call. Compares DSO overhead to static shared library overhead. # All builds are done with cc -c -O. :::::::::::::: caller.c :::::::::::::: int callee(int,int); int foo[100]; void caller(int x, int y) { foo[0] = callee(x,y); foo[1] = callee(x+1,y+1); foo[2] = callee(x+2,y+2); } :::::::::::::: callee.c :::::::::::::: int callee(int x, int y) { return (x+2*y); } # Now the disassembly files :::::::::::::: caller.shlib :::::::::::::: caller: [caller.c: 6] 0x0: 27bdffd8 addiu sp,sp,-40 [caller.c: 6] 0x4: afbf0014 sw ra,20(sp) [caller.c: 6] 0x8: afa40028 sw a0,40(sp) [caller.c: 8] 0xc: 0c000000 jal caller [caller.c: 6] 0x10: afa5002c sw a1,44(sp) [caller.c: 9] 0x14: 8fa40028 lw a0,40(sp) [caller.c: 9] 0x18: 8fa5002c lw a1,44(sp) [caller.c: 8] 0x1c: 3c010000 lui at,0 [caller.c: 8] 0x20: ac220000 sw v0,0(at) [caller.c: 9] 0x24: 24840001 addiu a0,a0,1 [caller.c: 9] 0x28: 0c000000 jal caller [caller.c: 9] 0x2c: 24a50001 addiu a1,a1,1 [caller.c: 10] 0x30: 8fa40028 lw a0,40(sp) # These [caller.c: 10] 0x34: 8fa5002c lw a1,44(sp) # instructions [caller.c: 9] 0x38: 3c010000 lui at,0 # all belong [caller.c: 9] 0x3c: ac220004 sw v0,4(at) # to the [caller.c: 10] 0x40: 24840002 addiu a0,a0,2 # same [caller.c: 10] 0x44: 0c000000 jal caller # call. [caller.c: 10] 0x48: 24a50002 addiu a1,a1,2 # [caller.c: 11] 0x4c: 8fbf0014 lw ra,20(sp) [caller.c: 10] 0x50: 3c010000 lui at,0 [caller.c: 10] 0x54: ac220008 sw v0,8(at) [caller.c: 11] 0x58: 03e00008 jr ra [caller.c: 11] 0x5c: 27bd0028 addiu sp,sp,40 :::::::::::::: callee.shlib :::::::::::::: callee: [callee.c: 1] 0x0: 00057040 sll t6,a1,1 [callee.c: 1] 0x4: 03e00008 jr ra [callee.c: 1] 0x8: 01c41021 addu v0,t6,a0 0xc: 00000000 nop # Now the dso code :::::::::::::: caller.dso :::::::::::::: caller: [caller.c: 6] 0x0: 3c1c0000 lui gp,0 [caller.c: 6] 0x4: 279c0000 addiu gp,gp,0 [caller.c: 6] 0x8: 0399e021 addu gp,gp,t9 [caller.c: 6] 0xc: 27bdffe0 addiu sp,sp,-32 [caller.c: 8] 0x10: 8f990000 lw t9,0(gp) [caller.c: 6] 0x14: afbf001c sw ra,28(sp) [caller.c: 6] 0x18: afbc0018 sw gp,24(sp) [caller.c: 6] 0x1c: afa40020 sw a0,32(sp) [caller.c: 8] 0x20: 0320f809 jalr ra,t9 [caller.c: 6] 0x24: afa50024 sw a1,36(sp) [caller.c: 8] 0x28: 8fbc0018 lw gp,24(sp) [caller.c: 9] 0x2c: 8fa40020 lw a0,32(sp) # [caller.c: 9] 0x30: 8fa50024 lw a1,36(sp) # [caller.c: 8] 0x34: 8f810000 lw at,0(gp) [caller.c: 9] 0x38: 8f990000 lw t9,0(gp) # [caller.c: 9] 0x3c: 24840001 addiu a0,a0,1 # [caller.c: 9] 0x40: 24a50001 addiu a1,a1,1 # [caller.c: 9] 0x44: 0320f809 jalr ra,t9 # [caller.c: 8] 0x48: ac220000 sw v0,0(at) [caller.c: 9] 0x4c: 8fbc0018 lw gp,24(sp) # [caller.c: 10] 0x50: 8fa40020 lw a0,32(sp) [caller.c: 10] 0x54: 8fa50024 lw a1,36(sp) [caller.c: 9] 0x58: 8f810000 lw at,0(gp) # [caller.c: 10] 0x5c: 8f990000 lw t9,0(gp) [caller.c: 10] 0x60: 24840002 addiu a0,a0,2 [caller.c: 10] 0x64: 24a50002 addiu a1,a1,2 [caller.c: 10] 0x68: 0320f809 jalr ra,t9 [caller.c: 9] 0x6c: ac220004 sw v0,4(at) # [caller.c: 10] 0x70: 8fbc0018 lw gp,24(sp) [caller.c: 11] 0x74: 8fbf001c lw ra,28(sp) [caller.c: 10] 0x78: 8f810000 lw at,0(gp) [caller.c: 11] 0x7c: 27bd0020 addiu sp,sp,32 [caller.c: 11] 0x80: 03e00008 jr ra [caller.c: 10] 0x84: ac220008 sw v0,8(at) 0x88: 00000000 nop 0x8c: 00000000 nop :::::::::::::: callee.dso :::::::::::::: callee: [callee.c: 1] 0x0: 00057040 sll t6,a1,1 [callee.c: 1] 0x4: 03e00008 jr ra [callee.c: 1] 0x8: 01c41021 addu v0,t6,a0 0xc: 00000000 nop # CASE 3 # # This case looks at how to best write leaf/small routines to tighten # the coding. 5 gischer@puget: cat func.c :::::::::::::: func.c :::::::::::::: extern int x; extern int y; int func(int z) { if (x > 0) { ++y; } return (y+z); } 6 gischer@puget: cc -c -O func.c 7 gischer@puget: dis func.o > func.o.dis 8 gischer@puget: cat func.o.dis :::::::::::::: func.o.dis :::::::::::::: func: [func.c: 4] 0x0: 3c1c0000 lui gp,0 [func.c: 4] 0x4: 279c0000 addiu gp,gp,0 [func.c: 4] 0x8: 0399e021 addu gp,gp,t9 [func.c: 6] 0xc: 8f8e0000 lw t6,0(gp) [func.c: 6] 0x10: 00000000 nop [func.c: 6] 0x14: 8dce0000 lw t6,0(t6) [func.c: 6] 0x18: 00000000 nop [func.c: 6] 0x1c: 19c00007 blez t6,0x3c [func.c: 6] 0x20: 00000000 nop [func.c: 6] 0x24: 8f830000 lw v1,0(gp) [func.c: 7] 0x28: 00000000 nop [func.c: 7] 0x2c: 8c6f0000 lw t7,0(v1) [func.c: 7] 0x30: 00000000 nop [func.c: 7] 0x34: 25f80001 addiu t8,t7,1 [func.c: 7] 0x38: ac780000 sw t8,0(v1) [func.c: 7] 0x3c: 8f830000 lw v1,0(gp) [func.c: 9] 0x40: 00000000 nop [func.c: 9] 0x44: 8c790000 lw t9,0(v1) [func.c: 9] 0x48: 03e00008 jr ra [func.c: 9] 0x4c: 03241021 addu v0,t9,a0 # # Now we try passing parameters # 9 gischer@puget: cat func2.c :::::::::::::: func2.c :::::::::::::: int func(int x, int *yp, int z) { if (x > 0) { ++*yp; } return (*yp+z); } 10 gischer@puget: cc -c -O func2.c 11 gischer@puget: dis func2.o > func2.o.dis 12 gischer@puget: cat func2.o.dis :::::::::::::: func2.o.dis :::::::::::::: func: [func2.c: 3] 0x0: 18800005 blez a0,0x18 [func2.c: 3] 0x4: 00000000 nop [func2.c: 4] 0x8: 8cae0000 lw t6,0(a1) [func2.c: 4] 0xc: 00000000 nop [func2.c: 4] 0x10: 25cf0001 addiu t7,t6,1 [func2.c: 4] 0x14: acaf0000 sw t7,0(a1) [func2.c: 6] 0x18: 8cb80000 lw t8,0(a1) [func2.c: 6] 0x1c: 03e00008 jr ra [func2.c: 6] 0x20: 03061021 addu v0,t8,a2 0x24: 00000000 nop 0x28: 00000000 nop 0x2c: 00000000 nop # # Just for fun, try passing a struct. # 13 gischer@puget: cat func3.c :::::::::::::: func3.c :::::::::::::: struct info { int x; int y; }; int func(struct info *ir, int z) { if (ir->x > 0) { ++ir->y; } return (ir->y+z); } 10 gischer@puget: cc -c -O func3.c 11 gischer@puget: dis func3.o > func3.o.dis 12 gischer@puget: cat func3.o.dis :::::::::::::: func3.o.dis :::::::::::::: func: [func3.c: 8] 0x0: 8c8e0000 lw t6,0(a0) [func3.c: 8] 0x4: 00000000 nop [func3.c: 8] 0x8: 19c00005 blez t6,0x20 [func3.c: 8] 0xc: 00000000 nop [func3.c: 9] 0x10: 8c8f0004 lw t7,4(a0) [func3.c: 9] 0x14: 00000000 nop [func3.c: 9] 0x18: 25f80001 addiu t8,t7,1 [func3.c: 9] 0x1c: ac980004 sw t8,4(a0) [func3.c: 11] 0x20: 8c990004 lw t9,4(a0) [func3.c: 11] 0x24: 03e00008 jr ra [func3.c: 11] 0x28: 03251021 addu v0,t9,a1 0x2c: 00000000 nop # CASE 4 # # We examine certain loops with calls to very short functions, such as # are found in the IRIX GL (tm) graphics library, and see how # to reduce the loop overhead for such code. # # For correctness, it is necessary for the compiler to generate code for # function calls which loads the address of the function each time it is # called. This is because the address could potentially change during # a call. These examples show that by taking the address of the # function and calling through a function pointer, the developer can # effectively guarantee to the compiler that the address will not # change, moving the load of the address out of the loop. 6 gischer@puget: more loop.c :::::::::::::: loop.c :::::::::::::: extern float v3f(float *); float *pts[100]; void loop() { int i; for (i=1; i<100; i++) { v3f(pts[i]); } } 7 gischer@puget: cc -c -O loop.c 8 gischer@puget: dis loop.o > loop.o.dis; cat loop.o.dis :::::::::::::: loop.o.dis :::::::::::::: loop: [loop.c: 5] 0x0: 3c1c0000 lui gp,0 [loop.c: 5] 0x4: 279c0000 addiu gp,gp,0 [loop.c: 5] 0x8: 0399e021 addu gp,gp,t9 [loop.c: 5] 0xc: 27bdffd8 addiu sp,sp,-40 [loop.c: 5] 0x10: afb1001c sw s1,28(sp) [loop.c: 5] 0x14: afb00018 sw s0,24(sp) [loop.c: 7] 0x18: 8f900000 lw s0,0(gp) [loop.c: 7] 0x1c: 8f910000 lw s1,0(gp) [loop.c: 5] 0x20: afbf0024 sw ra,36(sp) [loop.c: 5] 0x24: afbc0020 sw gp,32(sp) [loop.c: 7] 0x28: 26100004 addiu s0,s0,4 [loop.c: 7] 0x2c: 26310190 addiu s1,s1,400 [loop.c: 8] 0x30: 8f990000 lw t9,0(gp) # start loop [loop.c: 8] 0x34: 8e040000 lw a0,0(s0) # [loop.c: 8] 0x38: 0320f809 jalr ra,t9 # [loop.c: 8] 0x3c: 00000000 nop # [loop.c: 8] 0x40: 8fbc0020 lw gp,32(sp) # [loop.c: 8] 0x44: 26100004 addiu s0,s0,4 # [loop.c: 8] 0x48: 1611fff9 bne s0,s1,0x30 # [loop.c: 8] 0x4c: 00000000 nop # end loop [loop.c: 10] 0x50: 8fbf0024 lw ra,36(sp) [loop.c: 10] 0x54: 8fb00018 lw s0,24(sp) [loop.c: 10] 0x58: 8fb1001c lw s1,28(sp) [loop.c: 10] 0x5c: 03e00008 jr ra [loop.c: 10] 0x60: 27bd0028 addiu sp,sp,40 0x64: 00000000 nop 0x68: 00000000 nop 0x6c: 00000000 nop # In loop2.c we try using a function pointer. # This effectively guarantees to the compiler that the name will not # be re-resolved. 9 gischer@puget: cat loop2.c :::::::::::::: loop2.c :::::::::::::: extern float v3f(float *); float *pts[100]; void loop() { int i; float (*vptr)(float *); vptr = v3f; for (i=1; i<100; i++) { (*vptr)(pts[i]); } } 10 gischer@puget: cc -c -O loop2.c 11 gischer@puget: dis loop2.o > loop2.o.dis; cat loop2.o.dis :::::::::::::: loop2.o.dis :::::::::::::: loop: [loop2.c: 5] 0x0: 3c1c0000 lui gp,0 [loop2.c: 5] 0x4: 279c0000 addiu gp,gp,0 [loop2.c: 5] 0x8: 0399e021 addu gp,gp,t9 [loop2.c: 5] 0xc: 27bdffd8 addiu sp,sp,-40 [loop2.c: 5] 0x10: afb2001c sw s2,28(sp) [loop2.c: 5] 0x14: afb00014 sw s0,20(sp) [loop2.c: 10] 0x18: 8f900000 lw s0,0(gp) [loop2.c: 10] 0x1c: 8f920000 lw s2,0(gp) [loop2.c: 5] 0x20: afb10018 sw s1,24(sp) [loop2.c: 10] 0x24: 8f910000 lw s1,0(gp) [loop2.c: 5] 0x28: afbf0024 sw ra,36(sp) [loop2.c: 5] 0x2c: afbc0020 sw gp,32(sp) [loop2.c: 10] 0x30: 26100004 addiu s0,s0,4 [loop2.c: 10] 0x34: 26520190 addiu s2,s2,400 [loop2.c: 11] 0x38: 8e040000 lw a0,0(s0) # start loop [loop2.c: 11] 0x3c: 0220f809 jalr ra,s1 # [loop2.c: 11] 0x40: 0220c825 or t9,s1,zero # [loop2.c: 11] 0x44: 8fbc0020 lw gp,32(sp) # [loop2.c: 11] 0x48: 26100004 addiu s0,s0,4 # [loop2.c: 11] 0x4c: 1612fffa bne s0,s2,0x38 # [loop2.c: 11] 0x50: 00000000 nop # end loop [loop2.c: 13] 0x54: 8fbf0024 lw ra,36(sp) [loop2.c: 13] 0x58: 8fb00014 lw s0,20(sp) [loop2.c: 13] 0x5c: 8fb10018 lw s1,24(sp) [loop2.c: 13] 0x60: 8fb2001c lw s2,28(sp) [loop2.c: 13] 0x64: 03e00008 jr ra [loop2.c: 13] 0x68: 27bd0028 addiu sp,sp,40 0x6c: 00000000 nop # Finally, we hand-unroll the loop. 12 gischer@puget: cat loop3.c :::::::::::::: loop3.c :::::::::::::: extern float v3f(float *); float *pts[100]; void loop() { register int i; register float (*vptr)(float *); vptr = v3f; for (i=1; i<100; i=i+4) { (*vptr)(pts[i]); (*vptr)(pts[i+1]); (*vptr)(pts[i+2]); (*vptr)(pts[i+3]); } } 13 gischer@puget: cc -c -O loop3.c 14 gischer@puget: dis loop3.o > loop3.o.dis; cat loop3.o.dis :::::::::::::: loop3.o.dis :::::::::::::: loop: [loop3.c: 5] 0x0: 3c1c0000 lui gp,0 [loop3.c: 5] 0x4: 279c0000 addiu gp,gp,0 [loop3.c: 5] 0x8: 0399e021 addu gp,gp,t9 [loop3.c: 5] 0xc: 27bdffd8 addiu sp,sp,-40 [loop3.c: 5] 0x10: afb2001c sw s2,28(sp) [loop3.c: 5] 0x14: afb00014 sw s0,20(sp) [loop3.c: 10] 0x18: 8f900000 lw s0,0(gp) [loop3.c: 10] 0x1c: 8f920000 lw s2,0(gp) [loop3.c: 5] 0x20: afb10018 sw s1,24(sp) [loop3.c: 10] 0x24: 8f910000 lw s1,0(gp) [loop3.c: 5] 0x28: afbf0024 sw ra,36(sp) [loop3.c: 5] 0x2c: afbc0020 sw gp,32(sp) [loop3.c: 10] 0x30: 26100004 addiu s0,s0,4 [loop3.c: 10] 0x34: 26520194 addiu s2,s2,404 [loop3.c: 11] 0x38: 8e040000 lw a0,0(s0) # start loop [loop3.c: 11] 0x3c: 0220f809 jalr ra,s1 # [loop3.c: 11] 0x40: 0220c825 or t9,s1 # [loop3.c: 11] 0x44: 8fbc0020 lw gp,32(sp) # [loop3.c: 12] 0x48: 8e040004 lw a0,4(s0) # 5 [loop3.c: 12] 0x4c: 0220f809 jalr ra,s1 # [loop3.c: 12] 0x50: 0220c825 or t9,s1 # [loop3.c: 12] 0x54: 8fbc0020 lw gp,32(sp) # [loop3.c: 13] 0x58: 8e040008 lw a0,8(s0) # [loop3.c: 13] 0x5c: 0220f809 jalr ra,s1 # 10 [loop3.c: 13] 0x60: 0220c825 or t9,s1 # [loop3.c: 13] 0x64: 8fbc0020 lw gp,32(sp) # [loop3.c: 14] 0x68: 8e04000c lw a0,12(s0) # [loop3.c: 14] 0x6c: 0220f809 jalr ra,s1 # [loop3.c: 14] 0x70: 0220c825 or t9,s1 # 15 [loop3.c: 14] 0x74: 8fbc0020 lw gp,32(sp) # [loop3.c: 14] 0x78: 26100010 addiu s0,s0,16 # [loop3.c: 14] 0x7c: 1612ffee bne s0,s2,0x38 # [loop3.c: 14] 0x80: 00000000 nop # end loop [loop3.c: 16] 0x84: 8fbf0024 lw ra,36(sp) [loop3.c: 16] 0x88: 8fb00014 lw s0,20(sp) [loop3.c: 16] 0x8c: 8fb10018 lw s1,24(sp) [loop3.c: 16] 0x90: 8fb2001c lw s2,28(sp) [loop3.c: 16] 0x94: 03e00008 jr ra [loop3.c: 16] 0x98: 27bd0028 addiu sp,sp,40 0x9c: 00000000 nop # CASE 5 # # Handling platform-dependent configurations # :::::::::::::: Makefile :::::::::::::: default: main1 main2 main3 layer1 main1: main.o subdirs cc -o main1 main.o -L./platformA -lZ main2: main.o subdirs cc -o main2 main.o -L./platformA -lZ -no_transitive_link main3: main.o subdirs cc -o main3 main.o -L./platformB -lZ layer1: wrapper.so cc -o layer1 main.o wrapper.so -L./platformA wrapper.so: wrapper.o subdirs ld -shared wrapper.o -L./platformA -lZ \ -no_transitive_link -o wrapper.so subdirs: (cd platformA; make) (cd platformB; make) :::::::::::::: main.c :::::::::::::: #include <stdio.h> main() { printf("Entering Main\n"); z(); printf("Leaving Main\n"); } # # Platform A implementation of libZ.so depends on libZZ.so :::::::::::::: Makefile :::::::::::::: default: libZ.so libZ.so: libZZ.so Z.o ld -shared Z.o libZZ.so -o libZ.so libZZ.so: ZZ.o ld -shared ZZ.o -o libZZ.so clobber: clean rm *.so clean: rm *.o :::::::::::::: Z.c :::::::::::::: #include <stdio.h> extern void zz(void); #pragma weak z = _z void _z() { printf("Entering object Z.so\n"); zz(); printf("Leaving object Z.so\n"); } :::::::::::::: ZZ.c :::::::::::::: #include <stdio.h> void zz() { printf("Entering object ZZ.so\n"); printf("Leaving object ZZ.so\n"); } 407 gischer@puget: make cc -O -c ZZ.c ld -shared ZZ.o -o libZZ.so cc -O -c Z.c ld -shared Z.o libZZ.so -o libZ.so 408 gischer@puget: ls Makefile Z.o ZZ.o libZZ.so Z.c ZZ.c libZ.so so_locations 409 gischer@puget: elfdump -Dl libZ.so ***LIBRARY LIST SECTION*** Name Time-Stamp CheckSum Flags Version .liblist libZZ.so Aug 2 15:13:04 1993 0x798421d1 NONE # ================================================================= # PlatformB has no such dependency and doesn't implement libZZ.so # :::::::::::::: Makefile :::::::::::::: default: libZ.so libZ.so: Z.o ld -shared Z.o -o libZ.so clobber: clean rm *.so clean: rm *.o :::::::::::::: Z.c :::::::::::::: #include <stdio.h> #pragma weak z=_z void _z() { printf("Entering object Z.so\n"); printf("Leaving object Z.so\n"); } 416 gischer@puget: make cc -O -c Z.c ld -shared Z.o -o libZ.so 417 gischer@puget: ls Makefile Z.c Z.o libZ.so so_locations 418 gischer@puget: elfdump -Dl libZ.so # # Now main, built on platformA, will not run correctly on platformB # 426 gischer@puget: make -un main1 cc -O -c main.c (cd platformA; make) (cd platformB; make) cc -o main1 main.o -L./platformA -lZ 427 gischer@puget: setenv LD_LIBRARY_PATH ./platformA 428 gischer@puget: main1 Entering Main Entering object Z.so Entering object ZZ.so Leaving object ZZ.so Leaving object Z.so Leaving Main 429 gischer@puget: setenv LD_LIBRARY_PATH ./platformB 430 gischer@puget: main1 15222:main1: rld: Fatal Error: cannot map libZZ.so under path (./platformB/libZZ.so:/lib/libZZ.so:/usr/lib/libZZ.so:/lib/cmplrs/cc/libZZ.so:/usr/lib/cmplrs/cc/libZZ.so:) # # So we can use -no_transitive_link to fix this. First, when building main:n # 431 gischer@puget: make -un main2 cc -O -c main.c (cd platformA; make) (cd platformB; make) cc -o main2 main.o -L./platformA -lZ -no_transitive_link 432 gischer@puget: setenv LD_LIBRARY_PATH ./platformA 433 gischer@puget: main2 Entering Main Entering object Z.so Entering object ZZ.so Leaving object ZZ.so Leaving object Z.so Leaving Main 434 gischer@puget: setenv LD_LIBRARY_PATH ./platformB 435 gischer@puget: main2 Entering Main Entering object Z.so Leaving object Z.so Leaving Main # # We can duck the issue by building on platformB # This isn't recommended. # 438 gischer@puget: make main3 (cd platformA; make) (cd platformB; make) cc -o main3 main.o -L./platformB -lZ 439 gischer@puget: setenv LD_LIBRARY_PATH ./platformA 440 gischer@puget: main3 Entering Main Entering object Z.so Entering object ZZ.so Leaving object ZZ.so Leaving object Z.so Leaving Main 441 gischer@puget: setenv LD_LIBRARY_PATH ./platformB 442 gischer@puget: main3 Entering Main Entering object Z.so Leaving object Z.so Leaving Main # # Finally, we can hide this in the library by using a wrapper. # This also makes use of weak symbols # :::::::::::::: wrapper.c :::::::::::::: #include <stdio.h> extern void _z(void); void z() { printf("Entering wrapper\n"); _z(); printf("Leaving wrapper\n"); } 443 gischer@puget: make layer1 (cd platformA; make) (cd platformB; make) ld -shared wrapper.o -L./platformA -lZ \ -no_transitive_link -o wrapper.so cc -o layer1 main.o wrapper.so -L./platformA 444 gischer@puget: setenv LD_LIBRARY_PATH :.:./platformA: 445 gischer@puget: layer1 Entering Main Entering wrapper Entering object Z.so Entering object ZZ.so Leaving object ZZ.so Leaving object Z.so Leaving wrapper Leaving Main 446 gischer@puget: setenv LD_LIBRARY_PATH :.:./platformB 447 gischer@puget: layer1 Entering Main Entering wrapper Entering object Z.so Leaving object Z.so Leaving wrapper Leaving Main # # CASE 6 # # This case shows how shared objects can reduce the redundancy of # some link lines because of the different linking semantics of DSO # vs. archives. :::::::::::::: Makefile :::::::::::::: default: test2 test3 libA.a: f1.o f3.o ar r libA.a f1.o f3.o libB.a: f2.o ar r libB.a f2.o libA.so: libA.a ld -shared -all libA.a -o libA.so libB.so: libB.a ld -shared -all libB.a -o libB.so # This build will fail due to an unresolved variable # test1: test.o libA.a libB.a cc test.o libA.a libB.a -o test1 # This build works, showing the need to name the same archive more than # once on a link line. # test2: test.o libA.a libB.a cc test.o libA.a libB.a libA.a -o test2 # Such multiple listings are unnecessary with shared objects, as seen in # the following # test3: test.o libA.so libB.so cc test.o libA.so libB.so -o test3 clean: rm -rf *.o clobber: clean rm -rf *.so *.a test1 test2 test3 :::::::::::::: f1.c :::::::::::::: #include <stdio.h> extern void f2(void); void f1(void) { printf("Entering f1\n"); f2(); printf("Exiting f1\n"); } :::::::::::::: f2.c :::::::::::::: #include <stdio.h> extern void f3(void); void f2(void) { printf("Entering f2\n"); f3(); printf("Leaving f2\n"); } :::::::::::::: f3.c :::::::::::::: #include <stdio.h> void f3(void) { printf("This is f3\n"); } :::::::::::::: test.c :::::::::::::: #include <stdio.h> extern void f1(void); main() { f1(); } # # With archive linking semantics it is sometimes necessary to # mention libraries more than once on the link line. # 455 gischer@puget: make test1 cc -O -c test.c cc -O -c f1.c cc -O -c f3.c ar r libA.a f1.o f3.o ar: Warning: creating libA.a cc -O -c f2.c ar r libB.a f2.o ar: Warning: creating libB.a cc test.o libA.a libB.a -o test1 ld: Unresolved: f3 *** Error code 1 (bu21) make: fatal error. 456 gischer@puget: make test2 cc test.o libA.a libB.a libA.a -o test2 # # Much of this need is eliminated by the use of shared objects # 457 gischer@puget: make test3 ld -shared -all libA.a -o libA.so ld -shared -all libB.a -o libB.so cc test.o libA.so libB.so -o test3 458 gischer@puget: test2 Entering f1 Entering f2 This is f3 Leaving f2 Exiting f1 459 gischer@puget: test3 Entering f1 Entering f2 This is f3 Leaving f2 Exiting f1 # CASE 7 # # Speeding the development cycle by reducing build/link time. # # Avoid relinking everything by putting the bulk of code into a shared # object and preempting selected portions. :::::::::::::: Makefile :::::::::::::: default: main1 main1: bigthing.so main.o cc main.o bigthing.so -Wl,-rpath,`pwd` -o main1 bigthing.so: bigthing.o ld -shared bigthing.o -o bigthing.so main2: bigthing.so newthing.so main.o cc main.o newthing.so bigthing.so -Wl,-rpath,`pwd` -o main2 newthing.so: newthing.o ld -shared newthing.o -o newthing.so :::::::::::::: bigthing.c :::::::::::::: /* This file simulates a giant shared object. In real life * there would be many source files and many object files used * to build the object. */ #include <stdio.h> void func1() { printf("This is the old func1.\n\n"); } void func2() { printf("This is the old func2, which is buggy.\n\n"); } void func3() { printf("This is the old func3, which calls func2.\n"); func2(); } :::::::::::::: main.c :::::::::::::: #include <stdio.h> extern void func1(void); extern void func2(void); extern void func3(void); main() { func1(); func2(); func3(); } # # Build bigthing in nightly build or some such; test. # 466 gischer@puget: make cc -O -c bigthing.c ld -shared bigthing.o -o bigthing.so cc -O -c main.c cc main.o bigthing.so -Wl,-rpath,`pwd` -o main1 467 gischer@puget: main1 This is the old func1. This is the old func2, which is buggy. This is the old func3, which calls func2. This is the old func2, which is buggy. # # As bugs are discovered rebuild small objects and link them in # as shared objects, preempting the functions in the big thing. # :::::::::::::: newthing.c :::::::::::::: #include <stdio.h> void func2() { printf("This is the new func2, which is fixed.\n\n"); } 469 gischer@puget: make main2 cc -O -c newthing.c ld -shared newthing.o -o newthing.so cc main.o newthing.so bigthing.so -Wl,-rpath,`pwd` -o main2 470 gischer@puget: main2 This is the old func1. This is the new func2, which is fixed. This is the old func3, which calls func2. This is the new func2, which is fixed. # # CASE 8 # # Using dlopen(), dlsym(), dlerror() to configure software at runtime. # # The following example is based on languages but is not the # recommended method for internationalization. # :::::::::::::: Makefile :::::::::::::: default: main english.so french.so spanish.so german.so main: main.o cc main.o -ldl -Wl,-rpath,`pwd` -o main english.so: english.o ld -shared english.o -o english.so french.so: french.o ld -shared french.o -o french.so spanish.so: spanish.o ld -shared spanish.o -o spanish.so german.so: german.o ld -shared german.o -o german.so :::::::::::::: main.c :::::::::::::: #include <dlfcn.h> #include <stdio.h> main() { char choice; void *handle; void (*msg)(int); printf("What language would you like your messages in?\n\n"); printf(" 1: English\n\n"); printf(" 2: Spanish\n\n"); printf(" 3: German\n\n"); printf(" 4: French\n\n"); scanf("%c", &choice); switch (choice) { case '1': handle = dlopen("english.so", RTLD_LAZY); break; case '2': handle = dlopen("spanish.so", RTLD_LAZY); break; case '3': handle = dlopen("german.so", RTLD_LAZY); break; case '4': handle = dlopen("french.so", RTLD_LAZY); break; } if (handle == NULL) { fprintf(stderr, "%s\n", dlerror()); exit(1); } msg = dlsym(handle, "message"); if (msg == NULL) { fprintf(stderr, "%s\n", dlerror()); exit(2); } (*msg)(1); (void) getc(stdin); } :::::::::::::: english.c :::::::::::::: #include <stdio.h> void message() { printf("Give Jay a raise!\n"); } :::::::::::::: french.c :::::::::::::: #include <stdio.h> void message() { printf("Donnez une augmentacion a Wilson!\n"); } :::::::::::::: german.c :::::::::::::: #include <stdio.h> void message() { printf("Geben Sie Lilian mehr geld!\n"); } :::::::::::::: spanish.c :::::::::::::: #include <stdio.h> void message() { printf("Da le un aumente de sueldo a Sun.\n"); }
Documentation
Subdirectories