首页 > 解决方案 > 在两个链接文件中使用强符号和弱符号时的内存覆盖

问题描述

假设我们有一个 64 位 x86 机器,它是 little-endian,因此将一个字的最低有效字节存储在具有最低地址的字节中。假设 64 位 x86 Linux C 编译器的标准对齐规则。

考虑

文件 1:

#include <stdio.h>

struct cs {
    int count;
    unsigned short flags;
};
struct cs gcount;
extern void add_counter( int n );

int main(int c, char *argv[]);

int main(int c, char *argv[]) {
    gcount.flags = 0xe700;
    gcount.count = 1;
    add_counter(42);
    printf("count =%d\n", gcount.count);
    return 0;
}

文件 2:

struct cs {
    unsigned short flags;
    int count;
};

struct cs gcount = {0,0};

void add_counter (int n) {
    gcount.count +=n;
}

如果编译输出是1.

解释:

count 被定义为一个强全局 int,因此第二个文件被初始化为 {0,0},这里的顺序并不重要,因为它只是全零。

每个编译单元定义一个结构/类型,因此第一个文件使用第一个定义写入结构含义

gcount.flags = 0xe700; gcount.count=1;

使记忆看起来像

[e7 00 | 00 00 00 01] 其中(小端)左边是内存的顶部,右边是内存的底部。

(两个字段之间没有填充,因为 short 在最后,但 sizeof 会报告 8B)

调用 add_counter(42) 时,第二个文件将使用 cs 的第二个定义并将内存视为

[e7 00 00 00 | 00 01]

现在两个字段之间有一个 2B 填充,因此对计数的写访问将影响范围

[ e7 00 00 00 | 00 01]

42 是十六进制的 0x2a (2*16 + 10),因此将导致

[ e7 2a 00 00 | 00 01]

将其转换回我们得到的第一个文件的视图

[e7 2a | 00 00 00 01]

因此结果是 1 而不是预期的 43。

现在我确实得到了一般要点,但我有点困惑为什么我们[*e7 2a* 00 00 | 00 01]在添加42=0x2a而不是时得到[*e7 00 00 2a | 00 01]

我期待[*e7 00 00 2a | 00 01],因为我们使用的是小端,意思是,最右边的位是 LSB。所以e7实际上在这里代表最重要的 8 位。

标签: gccmemorylinker

解决方案


尽管我对练习本身发表了贬低的评论,但可以将这个问题解释为一个关于字节排序的更简单的问题。从这个意义上说,问题在于这个断言:

little-endian,意思是,最右边的位是 LSB。

Little-endian 意味着字节从最低有效到最高排序。这个术语是用英语创造的,英语是从左到右书写的,这意味着最左边的字节是小端序中的 LSB。


推荐阅读