通过在C ++中重命名符号来减少二进制大小

时间:2018-10-21 20:17:27

标签: c++ templates gcc embedded

是否有一个(自动)解决方案来减少使用-Os编译模板的C ++代码的大小? 现在,由于模板和名称空间的原因,这些符号已被修饰并且很大。

我知道每个函数后面的asm("symbolName"),但是当您有很多符号时,要写很多东西。

我正在寻找一种自动工具,该工具可以剥离并重命名所有符号(或将它们替换为简单的数字值?)

如果问题不清楚,下面是2个代码的示例(一个面向C,另一个面向C ++):

// C like, using virtual table + next pointer
struct Test
{
    virtual bool run() = 0;
    Test * next;
    Test() : next(nullptr) {}
    virtual ~Test() { delete next; }
};

Test & head = [...];
bool runTests() 
{ 
    Test * t = &head; 
    while (t) { if (!t->run()) return false; t = t->next; }
    return true;
}

对于10个测试,在32位系统上需要:10 *虚拟表大小+ 10 *下一个指针大小+头部指针,可能在内存中为30个指针的大小,但是代码大小(.text)最小。在我的系统上,运行strip -Alx a.out后,我得到的二进制大小为9336字节

与之相对:

template <typename Child, typename Next>
struct Test : public Child, public Next
{
    bool run() 
    { 
        if (!static_cast<Child*>(this)->run()) return false; 
        return Next::run(); 
    }
};

struct End
{
   static bool run() { return true; }
};

// Example follower
volatile bool g = true;
// Example follower
struct Test9 { bool run() { return g; } };
struct Test8 { bool run() { return g; } };
struct Test7 { bool run() { return g; } };
struct Test6 { bool run() { return g; } };
struct Test5 { bool run() { return g; } };
struct Test4 { bool run() { return g; } };
struct Test3 { bool run() { return g; } };
struct Test2 { bool run() { return g; } };
struct Test1 { bool run() { return g; } };

Test<Test1, Test<Test2, Test<Test3, Test<Test4, Test<Test5, Test<Test6, Test<Test7, Test<Test8, Test<Test9, End>>>>>>>>> head;
bool runTests() { return head.run(); }

共享代码:

int main()
{
    printf("%s: %s\n", typeid(head).name(), runTests() ? "passed" : "failed");
    return 0;
}

通常,它不应该包含任何指针,并且是简约的。但是,递归模板代码会创建占用闪存空间的非常大的符号(这些符号不会从二进制中删除,因为必须对其进行标识)。在我的情况下,后者创建的二进制大小被剥离了9804字节。

1 个答案:

答案 0 :(得分:3)

C ++是一种编译语言-符号长度以任何方式都不会对代码大小造成影响。它们保留在目标代码和库中,用于解析链接和调试,但在运行时不占用二进制文件的空间。

  

我正在寻找一种可以剥离并重命名的自动化工具   所有符号(或将它们替换为简单的数字值?)

链接器正是这样做的;它用地址替换符号。保留用于调试的符号可以通过链接器选项或单独的strip实用程序来剥离。

如果目标是裸机非托管环境,则在任何情况下,目标上加载的二进制文件通常不会包含调试符号表;因此,在这种情况下,剥离除阻止您使用符号调试器外没有其他用途。为了进行调试,这些符号仅加载到在开发主机上运行的调试器中,而在目标中不存在。

如果由于使用模板导致代码膨胀,则与符号名称的长度无关。每次使用不同的模板参数实例化模板时,都会创建新代码,因此,当您实例化单个模板的许多不同版本时,似乎很少的代码变得非常大。