I'm trying to build a fortran shared library to be inserted into another code and am trying to understand what happens to module level parameters and where they are stored in the library.
A minimal example like this:
MODULE test
IMPLICIT NONE
public
integer,parameter :: my_x=1
integer :: my_y
contains
subroutine my_func(a)
integer, intent(in) :: a
my_y=my_x*a
end subroutine my_func
END MODULE
And making a shared library out of this:
gfortran -shared -fPIC testShared.f90 -o libtest.so
Then running
nm libtest.so
Outputs:
0000000000201018 B __bss_start
0000000000201018 b completed.6938
w __cxa_finalize##GLIBC_2.2.5
00000000000005e0 t deregister_tm_clones
0000000000000670 t __do_global_dtors_aux
0000000000200de8 t __do_global_dtors_aux_fini_array_entry
0000000000200df8 d __dso_handle
0000000000200e00 d _DYNAMIC
0000000000201018 D _edata
0000000000201020 B _end
00000000000006fc T _fini
00000000000006b0 t frame_dummy
0000000000200de0 t __frame_dummy_init_array_entry
0000000000000788 r FRAME_END
0000000000201000 d _GLOBAL_OFFSET_TABLE_
w __gmon_start__
0000000000000708 r __GNU_EH_FRAME_HDR
0000000000000598 T _init
w _ITM_deregisterTMCloneTable
w _ITM_registerTMCloneTable
0000000000200df0 d JCR_END
0000000000200df0 d JCR_LIST
w _Jv_RegisterClasses
0000000000000620 t register_tm_clones
00000000000006e0 T __test_MOD_my_func
000000000020101c B __test_MOD_my_y
0000000000201018 d TMC_END
I've also tried:
gfortran -shared -fPIC -ggdb -O0 testShared.f90 -o libtest.so
To the same effect.
We can see that there is (suitable mangled with underscores) my_func and my_y but no my_x. I would assume the compiler must still store my_x in the library somewhere in case another function linked later wants to use it, even if its compiled out of the function my_func, but i can't tell where that might be.
So is my assumption wrong and my_x is gone or is it stored somehow and i'm just not using nm correctly to be able to see it?
Related
I have some legacy code that passes around a pointer to an internal data structure that the library uses to manipulate and check state variables. Thinking about refactoring this code, I became curious if creating a bunch of classes that are meant to only be used internally in my library would cause security concerns. Could others link in their own implementations of these methods? Say I have a Config class that reads an encrypted license and sets settings correctly. Would it be possible to manipulate this class somehow?
Here's some code I wrote to try initial feasibility
shared.cpp
#include "shared.h"
Config::Config() : max_clients()
{
}
Config::~Config()
{
}
int Config::getMaxClients() const
{
return max_clients;
}
void Config::setMaxClients(int num)
{
max_clients = num;
}
I compiled this into a shared library with
g++ -c shared.cpp -fpic
g++ -shared -o libshared.so shared.o
Once compiled we can see what other people can see about our Config class in our library.
$ nm -C libshared.so
0000000000201030 B __bss_start
0000000000201030 b completed.7696
w __cxa_finalize
0000000000000780 t deregister_tm_clones
0000000000000810 t __do_global_dtors_aux
0000000000200e20 t __do_global_dtors_aux_fini_array_entry
0000000000201028 d __dso_handle
0000000000200e58 d _DYNAMIC
0000000000201030 D _edata
0000000000201038 B _end
00000000000008f8 T _fini
0000000000000850 t frame_dummy
0000000000200e18 t __frame_dummy_init_array_entry
0000000000000a50 r __FRAME_END__
0000000000201000 d _GLOBAL_OFFSET_TABLE_
w __gmon_start__
0000000000000910 r __GNU_EH_FRAME_HDR
0000000000000720 T _init
w _ITM_deregisterTMCloneTable
w _ITM_registerTMCloneTable
00000000000007c0 t register_tm_clones
0000000000201030 d __TMC_END__
U operator delete(void*, unsigned long)
00000000000008de T Config::setMaxClients(int)
000000000000085a T Config::Config()
000000000000085a T Config::Config()
00000000000008a0 T Config::~Config()
0000000000000882 T Config::~Config()
0000000000000882 T Config::~Config()
00000000000008cc T Config::getMaxClients() const
0000000000200e48 V typeinfo for Config
0000000000000908 V typeinfo name for Config
0000000000200e28 V vtable for Config
U vtable for __cxxabiv1::__class_type_info
There's probably a lot more analysis I don't know about here.
mal.cpp
With this information we could guess a bit about return types on setMaxClients and create our own implementation.
#include <iostream>
class Config {
public:
Config() {}
void setMaxClients(int num);
};
void Config::setMaxClients(int num)
{
std::cout << "do something bad" << std::endl;
}
I compiled this file with
g++ -c mal.cpp -fpic
g++ -shared -o libmal.so mal.o
main.cpp
class Config {
public:
Config();
virtual ~Config();
int getMaxClients();
void setMaxClients(int num);
};
int main()
{
Config config;
config.setMaxClients(4);
return 0;
}
Finally, I compiled main.cpp with linking my mal library first then shared like so
g++ main.cpp -lmal -lshared
This produced a binary which when run I get
$ LD_LIBRARY_PATH=. ./a.out
do something bad
*** stack smashing detected ***: <unknown> terminated
[1] 7842 abort (core dumped) LD_LIBRARY_PATH=. ./a.out
Yes it crashed but I'm not more experienced to know where to go next. Is this not a security concern because you wouldn't actually be able to change any of the classes fields since you don't know the member variables? Or is there a concern here that requires best practices in C++?
Your replacement class should ideally be the same size as the original class. The config in main.cpp has a virtual dtor, whereas the one in mal.cpp does not. This will cause the latter to be 8bytes larger (assuming 64bit OS), and will invariably lead to a problem on destruction of the class.
If this is a real concern, a simple next step is to perform a checksum on the dll, and validate that at runtime (quit if checksum does not match). It wont help a great deal for someone who really cares though. If you know enough about asm, you can usually patch a binary exe with some no-ops until your check returns true.
I have not yet dealt with shared libraries in C++, and am having some trouble. I want to create a shared library and then have a C function pick up on that library. So here is my shared library file:
extern int nothing();
//sym.cpp
int nothing() {
return 0;
}
Below is my dlopen/dlsym script:
//symtest.c
#include <stdio.h>
#include <dlfcn.h>
int main(){
void *handle;
handle = dlopen("/path/to/lib/sym.so",RTLD_NOW);
int (*onload)(void *, void **, int);
onload = (int (*)(void *, void **, int))(unsigned long) dlsym(handle,"nothing");
if(onload==NULL) {
printf("NULL");
}
return 0;
}
Compile and ran as following:
$ g++ -shared -fPIC -o sym.so sym.cpp
$ gcc symtest.c -ldl -o symtest
$ ./symtest
NULL
Why am I getting NULL? I am pretty sure this symbol is getting exported, at least by observing the output of the following commands.
nm:
$ nm -CD sym.so | grep " T "
0000000000000670 T nothing()
000000000000067c T _fini
0000000000000518 T _init
objdump:
$ objdump -T sym.so
sym.so: file format elf64-x86-64
DYNAMIC SYMBOL TABLE:
0000000000000518 l d .init 0000000000000000 .init
0000000000000000 w D *UND* 0000000000000000 __gmon_start__
0000000000000000 w D *UND* 0000000000000000 _Jv_RegisterClasses
0000000000000000 w D *UND* 0000000000000000 _ITM_deregisterTMCloneTable
0000000000000000 w D *UND* 0000000000000000 _ITM_registerTMCloneTable
0000000000000000 w DF *UND* 0000000000000000 GLIBC_2.2.5 __cxa_finalize
0000000000200970 g D .bss 0000000000000000 Base _end
0000000000200968 g D .got.plt 0000000000000000 Base _edata
0000000000200968 g D .bss 0000000000000000 Base __bss_start
0000000000000518 g DF .init 0000000000000000 Base _init
000000000000067c g DF .fini 0000000000000000 Base _fini
0000000000000670 g DF .text 000000000000000b Base _Z7nothingv
There is a bigger picture here (Creating C++ Redis Module - "does not export RedisModule_OnLoad() symbol") but I looked through some Redis source code to produce a minimalistic example. Anyone have any idea what I am doing wrong here?
As requested, nm without -C option:
$ nm -D sym.so
w _ITM_deregisterTMCloneTable
w _ITM_registerTMCloneTable
w _Jv_RegisterClasses
0000000000000670 T _Z7nothingv
0000000000200968 B __bss_start
w __cxa_finalize
w __gmon_start__
0000000000200968 D _edata
0000000000200970 B _end
000000000000067c T _fini
0000000000000518 T _init
C++ has function overloading so there can be multiple functions with the same name and just different parameters.
Since object files store only names, C++ applies what is called name mangling. It adds extra symbols representing email parameters the name to differentiate between different versions.
When using dlsym, one must use the mangled name to get at the function address.
Now since name mangling is platform specific it is often better to use C linkage (no name mangling)
This can be done with the extern "C" declaration.
I find the class name cannot be hidden in the shared library if this class is polymorphic. For example,
// example.cpp
#include <stdio.h>
#include <string.h>
// #define virtual
class Base
{
public:
virtual const char* whatiam()
{
return "Papa";
}
};
class Child : public Base
{
public:
virtual const char* whatiam()
{
return "Son";
}
};
const char* whatiam(Base* obj)
{
return obj->whatiam();
}
__attribute__((visibility("default"))) const char* TheAPI(int n)
{
static char buf[64];
Child t;
sprintf(buf, "I'm %s.", whatiam(&t));
return buf;
}
I build a share library on Linux with gcc like this
$ g++ -fPIC -shared -fvisibility=hidden ../example.cpp -o libexample.so
$ strip -R .comment -R .note libexample.so
Then I open the libexample.so as a normal file in Emacs and search, the class name Base and Child will be find out.
And if I uncomments the statement // #define virtual to be #define virtual, that is to say make Base and Child without virtual methods, I find the class name Base and Child will not be find out in the shared library.
Dose the class name be stored in the class vtable by the compiler? Or some other reasons caused this problem?
I find the class name cannot be hidden in the shared library if this class is polymorphic.
Not clear what kind of hiding you refer to.
From linker symbol visibility perspective all names with internal linkage are hidden. Classes do not have linkage at all, functions and variables do:
$ nm -C libexample.so
nm: libexample.so: no symbols
$ nm -D -C libexample.so
0000000000201030 B __bss_start
w __cxa_finalize
0000000000201030 D _edata
00000000002010a0 B _end
0000000000000944 T _fini
w __gmon_start__
0000000000000728 T _init
w _ITM_deregisterTMCloneTable
w _ITM_registerTMCloneTable
w _Jv_RegisterClasses
U sprintf
0000000000000899 T TheAPI(int)
U vtable for __cxxabiv1::__class_type_info
U vtable for __cxxabiv1::__si_class_type_info
$ strings libexample.so | c++filt
__gmon_start__
_init
_fini
_ITM_deregisterTMCloneTable
_ITM_registerTMCloneTable
__cxa_finalize
_Jv_RegisterClasses
TheAPI(int)
sprintf
vtable for __cxxabiv1::__si_class_type_info
vtable for __cxxabiv1::__class_type_info
libstdc++.so.6
libm.so.6
libgcc_s.so.1
libc.so.6
_edata
__bss_start
_end
CXXABI_1.3
GLIBC_2.2.5
fffff.
Papa
I'm %s.
5Child
4Base
;*3$"
Those strings 5Child and 4Base are typeinfo returned by typeid():
typeinfo name for Child:
.string "5Child"
.hidden typeinfo for Child
.weak typeinfo for Child
.section .data.rel.ro._ZTI5Child,"awG",#progbits,typeinfo for Child,comdat
.align 16
.type typeinfo for Child, #object
.size typeinfo for Child, 24
typeinfo name for Base:
.string "4Base"
.hidden typeinfo for Base
.weak typeinfo for Base
.section .data.rel.ro._ZTI4Base,"awG",#progbits,typeinfo for Base,comdat
.align 16
.type typeinfo for Base, #object
.size typeinfo for Base, 16
You can disable typeinfo with -fno-rtti compiler switch:
-fno-rtti
Disable generation of information about every class with virtual
functions for use by the C++ run-time type identification features
(dynamic_cast and typeid). If you don't use those parts of the
language, you can save some space by using this flag. Note that
exception handling uses the same information, but G++ generates it
as needed. The dynamic_cast operator can still be used for casts
that do not require run-time type information, i.e. casts to "void
*" or to unambiguous base classes.
I'm trying to use the adobe xmp library in an iOS application but I'm getting link errors. I have the appropriate headers and libraries in my path, but I'm getting link errors. I double-checked to make sure the headers and library are on my path. I checked the mangled names of the methods, but they aren't in the library (I checked using the nm command). What am I doing wrong?
Library Header:
#if defined ( TXMP_STRING_TYPE )
#include "TXMPMeta.hpp"
#include "TXMPIterator.hpp"
#include "TXMPUtils.hpp"
typedef class TXMPMeta <TXMP_STRING_TYPE> SXMPMeta; // For client convenience.
typedef class TXMPIterator <TXMP_STRING_TYPE> SXMPIterator;
typedef class TXMPUtils <TXMP_STRING_TYPE> SXMPUtils;
.mm file:
#include <string>
using namespace std;
#define IOS_ENV
#define TXMP_STRING_TYPE string
#import "XMP.hpp"
void DoStuff()
{
SXMPMeta meta;
string returnValue;
meta.SetProperty ( kXMP_NS_PDF, "test", "{ formId: {guid} }" );
meta.DumpObject(DumpToString, &returnValue);
}
Link Errors:
(null): "TXMPMeta<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >::DumpObject(int (*)(void*, char const*, unsigned int), void*) const", referenced from:
(null): "TXMPMeta<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >::TXMPMeta()", referenced from:
(null): "TXMPMeta<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >::SetProperty(char const*, char const*, char const*, unsigned int)", referenced from:
(null): "TXMPMeta<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >::~TXMPMeta()", referenced from:
(null): Linker command failed with exit code 1 (use -v to see invocation)
Basically what's happened is that you only have the definitions in the headers
if I say
template<class T> T something(T); somewhere, that tells the compiler "trust me bro, it exists, leave it to the linker"
and it adds the symbol to the object file as if it did exist. Because it can see the prototype it knows how much stack space, what type it returns and such, so it just sets it up so the linker can just come along and put the address of the function in.
BUT in your case there is no address. You /MUST/ have the template definition (not just declaration) in the same file so the compiler can create one (with weak linkage) so here it's assumed they exist, but no where does it actually stamp out this class from a template, so the linker doesn't find it, hence the error.
Will fluff out my answer now, hope this helps.
Addendum 1:
template<class T> void output(T&);
int main(int,char**) {
int x = 5;
output(x);
return 0;
}
This will compile but NOT link.
Output:
if ! g++ -Isrc -Wall -Wextra -O3 -std=c++11 -g -gdwarf-2 -Wno-write-strings -MM src/main.cpp >> build/main.o.d ; then rm build/main.o.d ; exit 1 ; fi
g++ -Wall -Wextra -O3 -std=c++11 -g -gdwarf-2 -Wno-write-strings -Isrc -c src/main.cpp -o build/main.o
g++ build/main.o -o a.out
build/main.o: In function `main':
(my home)/src/main.cpp:13: undefined reference to `void output<int>(int&)'
collect2: error: ld returned 1 exit status
make: *** [a.out] Error 1
(I hijacked an open projet for this hence the names)
As you can see the compile command works fine (the one that ends in -o build/main.o) because we tell it "look this function exists"
So in the object file it says to the linker (in some "name managled form" to keep the templates) "put the location in memory of void output(int&); here" the linker can't find it.
Compiles and links
#include <iostream>
template<class T> void output(T&);
int main(int,char**) {
int x = 5;
output(x);
return 0;
}
template<class T> void output(T& what) {
std::cout<<what<<"\n";
std::cout.flush();
}
Notice line 2, we tell it "there exists a function, a template in T called output, that returns nothing and takes a T reference", that means it can use it in the main function (remember when it's parsing the main function it hasn't seen the definition of output yet, it has just been told it exists), the linker then fixes that. 'though modern compilers are much much smarter (because we have more ram :) ) and rape the structure of your code, link-time-optimisation does this even more, but this is how it used to work, and how it can be considered to work these days.
Output:
make all
if ! g++ -Isrc -Wall -Wextra -O3 -std=c++11 -g -gdwarf-2 -Wno-write-strings -MM src/main.cpp >> build/main.o.d ; then rm build/main.o.d ; exit 1 ; fi
g++ -Wall -Wextra -O3 -std=c++11 -g -gdwarf-2 -Wno-write-strings -Isrc -c src/main.cpp -o build/main.o
g++ build/main.o -o a.out
As you can see it compiled fine and linked fine.
Multiple files without include as proof of this
main.cpp
#include <iostream>
int TrustMeCompilerIExist();
int main(int,char**) {
std::cout<<TrustMeCompilerIExist();
std::cout.flush();
return 0;
}
proof.cpp
int TrustMeCompilerIExist() {
return 5;
}
Compile and link
make all
if ! g++ -Isrc -Wall -Wextra -O3 -std=c++11 -g -gdwarf-2 -Wno-write-strings -MM src/main.cpp >> build/main.o.d ; then rm build/main.o.d ; exit 1 ; fi
g++ -Wall -Wextra -O3 -std=c++11 -g -gdwarf-2 -Wno-write-strings -Isrc -c src/main.cpp -o build/main.o
if ! g++ -Isrc -Wall -Wextra -O3 -std=c++11 -g -gdwarf-2 -Wno-write-strings -MM src/proof.cpp >> build/proof.o.d ; then rm build/proof.o.d ; exit 1 ; fi
g++ -Wall -Wextra -O3 -std=c++11 -g -gdwarf-2 -Wno-write-strings -Isrc -c src/proof.cpp -o build/proof.o
g++ build/main.o build/proof.o -o a.out
(Outputs 5)
Remember #include LITERALLY dumps a file where it says "#include" (+ some other macros that adjust line numbers) this is called a translation unit. Rather than using a header file to contain "int TrustMeCompilerIExist();" which declares that the function exists (but the compiler again doesn't know where it is, the code inside of it, just that it exists) I repeated myself.
Lets look at proof.o
command
objdump proof.o -t
output
proof.o: file format elf64-x86-64
SYMBOL TABLE:
0000000000000000 l df *ABS* 0000000000000000 proof.cpp
0000000000000000 l d .text 0000000000000000 .text
0000000000000000 l d .data 0000000000000000 .data
0000000000000000 l d .bss 0000000000000000 .bss
0000000000000000 l d .debug_info 0000000000000000 .debug_info
0000000000000000 l d .debug_abbrev 0000000000000000 .debug_abbrev
0000000000000000 l d .debug_aranges 0000000000000000 .debug_aranges
0000000000000000 l d .debug_line 0000000000000000 .debug_line
0000000000000000 l d .debug_str 0000000000000000 .debug_str
0000000000000000 l d .note.GNU-stack 0000000000000000 .note.GNU-stack
0000000000000000 l d .eh_frame 0000000000000000 .eh_frame
0000000000000000 l d .comment 0000000000000000 .comment
0000000000000000 g F .text 0000000000000006 _Z21TrustMeCompilerIExistv
Right at the bottom there, there's a function, at offset 6 into the file, with debugging information, (the g is global though) you can see it's called _Z (this is why _ is reserved for some things, I forget what exactly... but it's to do with this) and Z is "integer", 21 is the name length, and after the name, the v is "void" the return type.
The zeros at the start btw are the section number, remember binaries can be HUGE.
Disassembly
running:
objdump proof.o -S gives
proof.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <_Z21TrustMeCompilerIExistv>:
int TrustMeCompilerIExist() {
return 5;
}
0: b8 05 00 00 00 mov $0x5,%eax
5: c3 retq
Because I have -g you can see it put the code that the assembly relates to (it makes more sense with bigger functions, it shows you what the following instructions until the next code block actually do) that wouldn't normally be there.
main.o
Here's the symbol table, obtained the same way as the above:
objdump main.o -t
main.o: file format elf64-x86-64
SYMBOL TABLE:
0000000000000000 l df *ABS* 0000000000000000 main.cpp
0000000000000000 l d .text 0000000000000000 .text
0000000000000000 l d .data 0000000000000000 .data
0000000000000000 l d .bss 0000000000000000 .bss
0000000000000000 l d .text.startup 0000000000000000 .text.startup
0000000000000030 l F .text.startup 0000000000000026 _GLOBAL__sub_I_main
0000000000000000 l O .bss 0000000000000001 _ZStL8__ioinit
0000000000000000 l d .init_array 0000000000000000 .init_array
0000000000000000 l d .debug_info 0000000000000000 .debug_info
0000000000000000 l d .debug_abbrev 0000000000000000 .debug_abbrev
0000000000000000 l d .debug_loc 0000000000000000 .debug_loc
0000000000000000 l d .debug_aranges 0000000000000000 .debug_aranges
0000000000000000 l d .debug_ranges 0000000000000000 .debug_ranges
0000000000000000 l d .debug_line 0000000000000000 .debug_line
0000000000000000 l d .debug_str 0000000000000000 .debug_str
0000000000000000 l d .note.GNU-stack 0000000000000000 .note.GNU-stack
0000000000000000 l d .eh_frame 0000000000000000 .eh_frame
0000000000000000 l d .comment 0000000000000000 .comment
0000000000000000 g F .text.startup 0000000000000026 main
0000000000000000 *UND* 0000000000000000 _Z21TrustMeCompilerIExistv
0000000000000000 *UND* 0000000000000000 _ZSt4cout
0000000000000000 *UND* 0000000000000000 _ZNSolsEi
0000000000000000 *UND* 0000000000000000 _ZNSo5flushEv
0000000000000000 *UND* 0000000000000000 _ZNSt8ios_base4InitC1Ev
0000000000000000 *UND* 0000000000000000 .hidden __dso_handle
0000000000000000 *UND* 0000000000000000 _ZNSt8ios_base4InitD1Ev
0000000000000000 *UND* 0000000000000000 __cxa_atexit
See how it says undefined, that's because it doesn't know where it is, it just knows it exists (along with the standard lib stuff, which the linker will find itself)
In closing
USE HEADER GUARDS and with templates put #include file.cpp at the bottom BEFORE the closing header guard. that way you can include header files as usual :)
The answer to your question is present in ever sample that comes with XMP SDK Toolkit.Clients must compile XMP.incl_cpp to ensure that all client-side glue code is generated. Do this by including it in exactly one of your source files.
For your ready reference I am pasting below a more detailed explanation present in section Template classes and accessing the API of XMPProgrammersGuide.pdf that comes with XMP SDK Toolkit
Template classes and accessing the API
The full client API is defined and documented in the TXMP*.hpp header files. The TXMP* classes are C++ template classes that must be instantiated with a string class such as std::string, which is used to return text strings for property values, serialized XMP, and so on. To allow your code to access the entire XMP API you must:
Provide a string class such as std::string to instantiate the template classes.
Provide access to XMPCore and XMPFiles by including the necessary defines and headers. To do this, add the necessary define and includes directives to your source code so that all necessary code is incorporated into the build:
#include <string>
#define XMP_INCLUDE_XMPFILES 1 //if using XMPFiles
#define TXMP_STRING_TYPE std::string
#include "XMP.hpp"
The SDK provides complete reference documentation for the template classes, but the templates must be instantiated for use. You can read the header files (TXMPMeta.hpp and so on) for information, but do not include them directly in your code. There is one overall header file, XMP.hpp, which is the only one that C++ clients should include using the #include directive. Read the instructions in this file for instantiating the template classes. When you have done this, the API is available through the concrete classes named SXMP*; that is, SXMPMeta, SXMPUtils, SXMPIterator, and SXMPFiles. This document refers to the SXMP* classes, which you can instantiate and which provide static functions.
Clients must compile XMP.incl_cpp to ensure that all client-side glue code is generated. Do this by including it in exactly one of your source files.
Read XMP_Const.h for detailed information about types and constants for namespace URIs and option flags.
I have the problem mentioned. I create an object inside a static lib, it is there when I run nm on the static lib, but when i link the lib to a binary and run nm it has disappeared. I know this problem has been asked before, but I could not find any answers. It is important for me to be able to retain that variable in the binary because we are trying to implement version checks for static libs in a binary. Please help. Thank you. Example code follows:
test.h
#ifndef TEST_H
#define TEST_H
class Test
{
public:
Test();
};
extern Test* gpTest;
#endif
test.cpp
#include "test.h"
Test::Test()
{
gpTest = this;
}
Test test;
main.cpp
#include "test.h"
#include <iostream>
using namespace std;
Test* gpTest = NULL;
int main()
{
return 0;
}
BUILD
g++ -c test.cpp -o test.o
ar cr test.a test.o
g++ main.cpp -o app -L/home/duminda/intest/test.a
nm -C test.a
0000000000000054 t global constructors keyed to _ZN4TestC2Ev
000000000000002b t __static_initialization_and_destruction_0(int, int)
0000000000000016 T Test::Test()
0000000000000000 T Test::Test()
U __gxx_personality_v0
U gpTest
0000000000000000 B test
nm -C app
0000000000600e10 d _DYNAMIC
0000000000600fe8 d _GLOBAL_OFFSET_TABLE_
0000000000400754 t global constructors keyed to gpTest
0000000000400858 R _IO_stdin_used
w _Jv_RegisterClasses
0000000000400717 t __static_initialization_and_destruction_0(int, int)
U std::ios_base::Init::Init()##GLIBCXX_3.4
U std::ios_base::Init::~Init()##GLIBCXX_3.4
0000000000601050 b std::__ioinit
0000000000600df0 d __CTOR_END__
0000000000600de0 d __CTOR_LIST__
0000000000600e00 D __DTOR_END__
0000000000600df8 d __DTOR_LIST__
0000000000400968 r __FRAME_END__
0000000000600e08 d __JCR_END__
0000000000600e08 d __JCR_LIST__
0000000000601038 A __bss_start
U __cxa_atexit##GLIBC_2.2.5
0000000000601028 D __data_start
0000000000400810 t __do_global_ctors_aux
0000000000400670 t __do_global_dtors_aux
0000000000601030 D __dso_handle
w __gmon_start__
U __gxx_personality_v0##CXXABI_1.3
0000000000600ddc d __init_array_end
0000000000600ddc d __init_array_start
0000000000400770 T __libc_csu_fini
0000000000400780 T __libc_csu_init
U __libc_start_main##GLIBC_2.2.5
0000000000601038 A _edata
0000000000601058 A _end
0000000000400848 T _fini
00000000004005a0 T _init
0000000000400620 T _start
000000000040064c t call_gmon_start
0000000000601038 b completed.6096
0000000000601028 W data_start
0000000000601040 b dtor_idx.6098
00000000004006e0 t frame_dummy
0000000000601048 B gpTest
000000000040070c T main
If nothing in the program references an object-file from a library, then there is no reason for the linker to include that object-file in the executable. A linker is designed to pick only those parts from a library that are needed to resolve open dependencies.
When using static linking, the only options are:
Don't use libraries, but pass all the relevant object files explicitly to the linker
Make sure at least one of the object files from the application depends on the relevant object-file from the library.
Perhaps dynamic linking can be of assistance here, but I don't know for sure.
optimizing compiler/linker - you are not using gpTest in main() so it is not added to the code. If you place a reference/use inside the main() function it will reappear.
I found what I was looking for :D. It seems that linker flag --whole-archive is there to make sure all symbols in static lib regardless of whether they are accessed by app or not get into app. Though I still have trouble getting this to work, I guess I am on the right path.