【问题标题】:Cut down the binary size by renaming symbols in C++通过在 C++ 中重命名符号来减少二进制大小
【发布时间】:2018-10-21 20:17:27
【问题描述】:

是否有(自动)解决方案来减少使用模板编译的 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 字节的二进制大小。

【问题讨论】:

  • 好吧,在 posix 上,总是有 strip 命令。您是否使用-g 调试选项进行编译?一般来说,类型和变量的名称不应该出现在编译代码中。

标签: c++ templates gcc embedded


【解决方案1】:

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

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

这正是链接器所做的;它用地址替换符号。为调试而保留的符号可以通过链接器选项或单独的strip 实用程序去除。

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

如果您的代码因使用模板而臃肿,这与符号名称的长度无关。每次使用不同的模板参数实例化模板时,都会创建新代码,因此如果您正在实例化单个模板的许多不同版本,那么看起来少量的代码很容易变得非常大。

【讨论】:

  • 好吧,对于上面的人为示例,您是完全正确的。然而,当使用 RTTI/dynamic_cast 时,实际的类型 name 会在二进制文件中存储为错位文本。我不在乎它是否在二进制文件中从abcdef 替换为a,只要它不与任何其他名称冲突。然而,我不知道让编译器总结这些名称的任何选项。
  • @xryl669 如果内存受到限制,通常应该禁用 RTTI。名称修饰是有目的的,它为重载等编码类型信息。如果你修改它,你会破坏它或限制功能。
  • 是的,我同意。但是,我使用的代码使用dynamic_cast,并且很难更改逻辑。我想我必须添加virtual int getID(),这样我才能禁用 RTTI。我想知道有没有机制可以将 typeid 的 name 符号替换为更小的数字/代码?
猜你喜欢
  • 1970-01-01
  • 2012-02-07
  • 2014-11-18
  • 2021-03-05
  • 1970-01-01
  • 2012-02-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多