1

I reading this page.
I follow the answer step by step to test, alse add -fvisibility=hidden to make all symbols hidden, and then I extended the code that is in the answer.

//rectangle.h
#pragma once

#include <memory>

class Rectangle {
    int width, height;
  public:
    void set_values (int,int);
    int area();

};

std::shared_ptr<Rectangle> __attribute__((visibility("default"))) get_rectangle();
//rectangle.cpp
#include "rectangle.h"

int Rectangle::area() {return width*height;}

void Rectangle::set_values (int x, int y) {
  width = x;
  height = y;
}

std::shared_ptr<Rectangle> __attribute__((visibility("default"))) get_rectangle() {
    return std::make_shared<Rectangle>();
}

I build librectangle.so, then I code main.cpp to link librectangle.so

//main.cpp
#include "../rectangle/rectangle.h"

int main() {
    auto rectangle = get_rectangle();
    rectangle->set_values(2, 3);
    rectangle->area();
    return 0;
}

Compilation error

undefined reference to `Rectangle::set_values(int, int)'
undefined reference to `Rectangle::area()'

But, If I change the class member function to a virtual function, it will compile correctly

//changed rectangle.h
#pragma once

#include <memory>

class Rectangle {
    int width, height;
  public:
    virtual void set_values (int,int);
    virtual int area();

};

std::shared_ptr<Rectangle> __attribute__((visibility("default"))) get_rectangle();

Use Usenm -C librectangle.so to compare the normal function class and the virtual function class. The left is the normal function class. the right is the virtual function class.

nm -C librectangle.so | grep Rectangle enter image description here

nm -C librectangle.so | grep area enter image description here

nm -C librectangle.so | grep set_values enter image description here

both demos add -visibility=hidden, the normal functions compile incorrectly, but the virtual functions compile correctly?

5
  • I dont understand. You say the code without virtual functions results in an error while the one with virtual funcitons compiles. How do you conclude from that that -visibility=hidden is invalid for virtual functions? Commented Mar 12, 2024 at 8:23
  • Use nm tool to see what symbols are included in librectangle.so and what are not included. Also provide the compiler flags that you are using. Commented Mar 12, 2024 at 8:27
  • Perhaps I have come to an incorrect conclusion, but what I want to express is why, when both demos add -visibility=hidden, the normal functions compile incorrectly, but the virtual functions compile correctly? Commented Mar 12, 2024 at 9:04
  • I add the um info Commented Mar 12, 2024 at 9:29
  • Note: your nm call doesn't list exported symbols, but all symbols: stackoverflow.com/questions/34732/… Commented Mar 12, 2024 at 9:41

1 Answer 1

2

An library has a list of symbols (that is, functions and global variables) it contains, the exported symbols. An object file has a list of symbols it uses that aren't defined in that object file, the imported symbols.

Hidden visibility on a symbol means it isn't added to the list of exported symbols. Default visibility means it is.

When the linker creates a library/executable from a bunch of object files, it looks at the imported symbols of all the object files and resolves them according to the exported symbols of all object files and libraries.

Let's look at the first example. Your main.o is compiled to assembly roughly to this effect (assuming no callee-saved registers except stack pointer):

main:
  sub sp, 16 ; make space for shared_ptr on stack
  mov arg0, sp ; move address of that space to first argument register
  call get_rectangle
  mov arg0, [esp] ; load pointer to object behind shared_ptr into argument
  mov arg1, 2 ; load arguments
  mov arg2, 3
  call Rectangle::set_values
  mov arg0, [sp] ; load pointer again
  call Rectangle::area
  mov arg0, sp ; load shared_ptr address
  call shared_ptr::~shared_ptr ; destroy shared_ptr, end of scope

As you can see, it references three symbols that are used: get_rectangle, Rectangle::set_values and Rectangle::area. But only get_rectangle has default visibility, so the other two are not listed as exports in librectangle.so, so the linker can't find them. You get errors.

But that's not how virtual calls work. There, the code looks something like this:

main:
  sub sp, 16 ; as before
  mov arg0, sp
  call get_rectangle
  mov arg0, [esp] ; load pointer to object behind shared_ptr
  mov reg0, [arg0] ; load vtable pointer to call virtual function
  mov arg1, 2 ; load arguments
  mov arg2, 3
  call [reg0+0] ; call first entry in vtable, set_values
  mov arg0, [sp] ; load pointer again
  mov reg0, [arg0] ; load vtable pointer again
  call [reg0+8] ; call second entry in vtable, area
  mov arg0, sp ; load shared_ptr address
  call shared_ptr::~shared_ptr ; destroy shared_ptr, end of scope

As you can see, now the assembly only references a single symbol, get_rectangle. The others are only called through the pointers stored in the vtable. The vtable is a global constant stored in the librectangle.so, so it has access to the hidden functions. Also, the vtable itself is a hidden symbol, but it is only referenced by the constructor of Rectangle, which is implicitly defined by the compiler. It is an inline function and so exists in every compilation unit that includes the header, but the only place it is used is in get_rectangle, so there it has access to the vtable symbol.

Note that if you create a local variable of type Rectangle in main you will get a linker error referring to the vtable missing. Also, since the compiler might devirtualize the calls, you would also get errors for these symbols again as it tries the usual direct calls.

Sign up to request clarification or add additional context in comments.

3 Comments

Thank a lot! I test that create a local variable in main. As you said, there was an` undefined reference to 'vtable for Rectangle' `error
Hello, I would like to systematically learn relevant knowledge. Do you have any recommended books or articles?
I'm afraid this is just knowledge I have accumulated over 20 years of programming. I have no particular resources to recommend. I would recommend learning about how C++ compilation works. This book is old but I heard good things about it: amazon.com/Inside-Object-Model-Stanley-Lippman/dp/0201834545

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.