Why this static function has no debug symbol? - gdb

(gdb) l main
...
4614 if (do_daemonize)
4615 save_pid(getpid(), pid_file);
(gdb) l save_pid
Function "save_pid" not defined.
and there's its definition in source file:
static void save_pid(const pid_t pid, const char *pid_file) {
FILE *fp;
...
}
save_pid and main are in the same source file,but only main has debug symbol,why??
UPDATE
Another test case with a really simple static function:
#include <stdio.h>
static int test()
{
return 0;
}
int main(void)
{
//int i = 6;
printf("%f",6.4);
return 0;
}
gcc -Wall -g test.c test
But the symbol test is there!

If the function is simple enough and its address is never used, being static it may have been inlined and then discarded (since there is no possibility of it being called from elsewhere).

Related

why are RTLD_DEEPBIND and RTLD_LOCAL not preventing collision of static class member symbol

I am trying to write a simple plugin system for an application and would like to prevent plugins from stomping on each others symbols, however RTLD_DEEPBIND and RTLD_LOCAL don't seem to be enough when it comes to static class members when they happen to have the same name in different plugins.
I wrote a stripped down example to show what I mean.
I compiled and ran it like this:
g++ -c dumb-plugin.cpp -std=c++17 -fPIC
gcc -shared dumb-plugin.o -o dumb1.plugin
cp dumb1.plugin dumb2.plugin
g++ main.cpp -ldl -o main
./main
And the content of the output file for the second plugin showed that it reused the the class from the first plugin.
How can I avoid this?
EDIT: I compiled the plugin with clang(not main just the plugin) and it worked despite all of the RTLD_DEEPBIND stuff being in main.cpp which was still compiled with g++. It didn't work when the plugin was compiled with gcc 10.3 or 11.1 even when I tried -Bsymbolic. Is this a bug?
If I run readelf on the DSO compiled/linked with clang i see these 2 lines:
21: 00000000000040b0 4 OBJECT UNIQUE DEFAULT 26 _ZN9DumbClass7co[...]_ZN9DumbClass7co[...]
25: 00000000000040b0 4 OBJECT UNIQUE DEFAULT 26 _ZN9DumbClass7co[...]
and with gcc i get:
20: 00000000000040a8 4 OBJECT WEAK DEFAULT 24 _ZN9DumbClass7co[...]
27: 00000000000040a8 4 OBJECT WEAK DEFAULT 24 _ZN9DumbClass7co[...]
with WEAK instead of UNIQUE under the BIND column.
dumb-plugin.cpp:
#include <dlfcn.h>
#include <cstdio>
#include <string>
int global_counter = 0;
static int static_global_counter = 0;
std::string replace_slashes(const char * str) {
std::string s;
for (const char* c = str; *c != '\0'; c++)
s += (*c == '/')?
'#' : *c;
return s;
}
void foo() {}
class DumbClass {
public:
static inline int counter = 0;
};
extern "C" void plugin_func() {
static int static_local_counter = 0;
Dl_info info;
dladdr((void*)foo, &info);
std::string path = "plugin_func() from: " + replace_slashes(info.dli_fname);
auto fp = std::fopen(path.c_str(), "w");
fprintf(fp, "static local counter: %d\n", static_local_counter++);
fprintf(fp, "DumbClass::counter: %d\n", DumbClass::counter++);
fprintf(fp, "global counter: %d\n", global_counter++);
fprintf(fp, "static global counter: %d\n", static_global_counter++);
std::fclose(fp);
}
main.cpp:
#include <dlfcn.h>
#include <iostream>
#include <unistd.h>
#include <string.h>
int main () {
char path1[512], path2[512];
getcwd(path1, 512);
strcat(path1, "/dumb1.plugin");
getcwd(path2, 512);
strcat(path2, "/dumb2.plugin");
auto h1 = dlopen(path1, RTLD_NOW | RTLD_LOCAL | RTLD_DEEPBIND);
auto h2 = dlopen(path2, RTLD_NOW | RTLD_LOCAL | RTLD_DEEPBIND);
auto func = (void(*)()) dlsym(h1, "plugin_func");
func();
func = (void(*)()) dlsym(h2, "plugin_func");
func();
}
gcc implements static inline data members (and also static data members of class templates, inline or not, and static variables in inline functions, and perhaps other things as well) as global unique symbols (a GNU extension to the ELF format). There is only one such symbol with a given name per process, by design.
clang implements such things as normal weak symbols. These will not collide when RTLD_LOCAL and RTLD_DEEPBIND are used.
There are several ways to avoid collisions, but all of them require plugin writers to take an action. The best way IMO is to use hidden symbol visibility by default, only opening symbols that are meant to be dlsymd.

Where is the syntax with class as a parameter in main needed?

This question states that main can be implementation defined with some restrictions.
So, I wrote the following C++ code to try out the following signature of main:
main.h
class MyClass {
private:
int i;
public:
MyClass();
inline int geti() {
return i;
}
inline void seti(int i) {
this->i = i;
}
~MyClass();
};
MyClass::MyClass() {
this->i = 2;
}
MyClass::~MyClass() {
}
main.c++
#include <iostream>
#include "main.h"
int main(MyClass myClass) {
std::cout << myClass.geti() << std::endl;
return 0;
}
Which Gives the following results:
The command g++ -o main main.c++ -O3 compiles successfully with warnings:
main.c++:5:5: warning: first argument of ‘int main(MyClass)’ should be ‘int’ [-Wmain]
5 | int main(MyClass myClass) {
| ^~~~
main.c++:5:5: warning: ‘int main(MyClass)’ takes only zero or two arguments [-Wmain]
The command clang++ -o main main.c++ -std=c++14 gives the error:
main.c++:5:5: error: first parameter of 'main' (argument count) must be of type 'int'
int main(MyClass myClass) {
^
1 error generated.
the main file generated by g++ gives SIGSEGV (why though?)
So, if main can be implementation defined, why does clang give an error while g++ generated file give SIGSEGV?
I also went further and created a different code so that I will be able to pass a MyClass object to main.c++ as follows:
#include <iostream>
#include "main.h"
#include <unistd.h>
int main() {
MyClass myClass;
execve("./main",myClass,NULL);
return 0;
}
However, as execve takes the second parameter to be a char* const *, it does not compile. How do I pass the myClass object to the main file generated by g++?
The command g++ -o main main.c++ -O3 compiles successfully with warnings
This is not successful compilation. You should always use -Werror. If you fail to do so and then decide to ignore the warning and proceed with running the program, it's your own responsibility. You better know full well what you are doing. See this for more information.
the main file generated by g++ gives SIGSEGV (why though?)
The compiler has warned you. It is in your best interest to listen to it. If things go boom, chances are, that's because you have ignored warnings.
why does clang give an error while g++ generated file give SIGSEGV?
The program is not a valid C++ program. There is no meaningful difference between a warning and an error.
How do I pass the myClass object to the main file generated by g++?
You cannot. main must have a form equivalent to one of these two:
int main()
int main(int argc, char* argv[])
(Optional reading in italics) Other forms of main are implementation-defined. This means your implementation needs to support them in a documented way. Unless you have read documentation for your implementation and found that it supports the form of main you want, there's no way to do that.
Other than having an implementation-defined main, the only way a program can get hold of an object of a class type is by constructing that object.
You are close. You have identified your primary issue attempting to pass as a parameter to main() -- that won't work. The declaration for main() is defined by the standard and you are limited to passing string values (nul-terminated character arrays... C-Strings) in as arguments.
In your case you need to create an instance of your class within main(), e.g.
#include <iostream>
#include "main.h"
int main() {
MyClass myClass;
std::cout << myClass.geti() << std::endl;
return 0;
}
Your main.h header has a variable shadowing problem where at line 10:
inline void seti(int i) {
int i shadows a prior declaration at line 3, e.g. int i; (though the consequence would be unlikely to matter). Just replace the variable name in the second declaration with j (or whatever you like). Your code will compile without warning, e.g.
class MyClass {
private:
int i;
public:
MyClass();
inline int geti() {
return i;
}
inline void seti(int j) {
this->i = j;
}
~MyClass();
};
MyClass::MyClass() {
this->i = 2;
}
MyClass::~MyClass() {
}
Example Use/Output
$ ./bin/main
2
You can also call your seti() function to update the private variable in your class, e.g.
myClass.seti(5);
std::cout << myClass.geti() << std::endl;
Which would now output 5.
Let me know if you have further questions.

Global variable's constructor has not been invoked while modify program’s entrypoint

I want to change the program's entrypoint from main to self-defined function, but it seems that the global variable's constructor cannot be invoked in this case.
code like this:
#include <stdin.h>
#include <stdlib.h>
const char my_interp[] __attribute__((section(".interp"))) = "/lib64/ld-linux-x86-64.so.2";
class A {
public:
A() {
printf("construct A\n");
}
};
A a;
int main() {
printf("main start\n");
}
extern "C" {
int smain() {
printf("smain start\n");
exit(0);
}
}
The compile command like this:
g++ -g -Wl,-e,smain -o smain smain.cc
Compiling with -e option gives a replacement for _start routine, not main.
When you use an alternative for start, you will lose initializations which would be a part of the _start routine.
In this particular case, you lose the constructor call for A because a is global.
You cannot use command-line arguments as well.

system() function not called from LD_PRELOAD'ed library

I am trying to use LD_PRELOAD on linux to wrap calls to system function to add some preprocessing to the argument. Here's my system.cpp:
#define _GNU_SOURCE
#include <dlfcn.h>
#include <string>
#include <iostream>
typedef int (*orig_system_type)(const char *command);
int system(const char *command)
{
std::string new_cmd = std::string("set -f;") + command;
// next line is for debuggin only
std::cout << new_cmd << std::endl;
orig_system_type orig_system;
orig_system = (orig_system_type)dlsym(RTLD_NEXT,"system");
return orig_system(new_cmd.c_str());
}
I build it with
g++ -shared -fPIC -ldl -o libsystem.so system.cpp
which produces the .so object. I then run my program with
$ LD_PRELOAD=/path/to/libsystem.so ./myprogram
I do not get any errors - but seemingly my system function is not called. Running with LD_DEBUG=libs, I can see that my .so is being loaded, however my system function is not being called and the one from the standard library is called instead.
What do I need to change in code/build to get it to work?
You need
extern "C" int system ...
because it is called by a C function. The C++ version has its name mangled so it is not recognizable.
You might also want to consider saving the "orig_system" pointer so that you avoid calling dlsym every time. You can do this in a constructor/init function, so you would have something like
extern "C" {
typedef int (*orig_system_type)(const char *command);
static orig_system_type orig_system;
static void myInit() __attribute__((constructor));
void myInit()
{
orig_system = (orig_system_type)dlsym(RTLD_NEXT,"system");
}
int system(const char *command)
{
std::string new_cmd = std::string("set -f;") + command;
// next line is for debuggin only
std::cout << new_cmd << std::endl;
return orig_system(new_cmd.c_str());
}
}
(this code isn't tested, but I have used this technique in the past).
An alternative would be to use GNU ld's --wrap option.
If you compile your shared lib with
-Wl,--wrap system
then in your code you write
extern "C" {
void* __real_system(const char* command);
void* __wrap_system(const char* command)
{
std::string new_cmd = std::string("set -f;") + command;
// next line is for debuggin only
std::cout << new_cmd << std::endl;
return __real_system(new_cmd.c_str());
}
}
(Note that I've never used this).
The code should work perfectly fine. Assuming the driver program is something like this:
#include <cstdlib>
int main() {
system("find -name *.cpp");
}
Then env LD_PRELOAD=$PWD/libsystem.so ./a.out gives me this output:
set -f;find -name *.cpp
./system.cpp
./test.cpp
Which shows that not only is your debug statement appearing, but that glob is disabled for that command.

global const variable definition - access through extern in c++

I read some answers about this topic, but I am still not sure:
In C++ a global const variable definition is automatically static. However I can access it from another cpp-file through extern:
// module.cpp
const int i = 0;
and
// main.cpp
extern const int i;
int main ()
{
if (i > 10)
return 0;
else
return 1;
}
Why is this possible (accessing an object with internal linkage from another module)? Normally I should be forced to define i as extern const int i = 0 in module.cpp, to have an explicitely global but non-static const, or?
In comparison, this is not possible:
// module.cpp
static int i = 0;
and
// main.cpp
extern int i;
int main ()
{
i = 10; // but read-only access like (i > 10) would be possible!
return 0;
}
So is the answer that: yes, you can access internal linked objects from other modules, but only for read (so always with const)?
Edit:
Sorry, but I made a mistake: in my original code I just tried an expression without effect (in both examples):
extern const int i; // or extern int i for second example
int main ()
{
i>10;
return 0;
}
I thought, that it behaves the same, as if program flow or data was dependent of this expression, but in fact it does not! The compiler seems to just cut out this effectless expression, so that the linker does not see it! So all is alright: in the first example i must be indeed defined extern const int i = 0 in module.cpp, and in the second example i cannot be accessed at all (unless in effectless expression). Compiler is VC++2010.
Edit2:
However, now I don't understand why this is possible:
// module.cpp
extern const int i = 0;
and
// main.cpp
int i = 99;
int main ()
{
bool b = i>10;
return 0;
}
i have both external linkage. But no error. When in module.cpp I define int i = 0, then error (multiple symbols). Why?
As for me it looks like a compiler bug. Constant variable i is not defined It is only declared in main.cpp. As for variable i in module.cpp then it has internal linkage and shall not be accessible outside the module.
Relative to your addition to the original post then the compiler has nothing common with this situation. It is linker that checks whether there are duplicate external symbols. I think it decided that if one variable has qualifier const and other has no it then there are two different variables. I think that it is implementation defined whether the linker will issue an error or not. Moreover it can have some options that could control the behaviour of the linker in such situations.