使用gcc编译.c文件和.s文件时遇到.bss混淆(bug)的问题?

3

在IA32架构下,使用Ubuntu 12.04和gcc 4.6.3编译C文件和使用存储在.bss段上的汇编文件时,我遇到了一个问题,其中包括.comm和.lcomm指令。

在.comm和.lcomm缓冲区之间,汇编文件foo.s几乎使用了gas在该段中允许我分配的最大空间(foo计算long longs的质因数)。使用处理输入/输出等操作的汇编文件bar.s,一切都可以成功编译链接(而且速度很快),并且工作正常。

然后我使用C文件bar.c来处理输入/输出,gcc不会终止——至少不会在5分钟内终止。.bss请求接近于我的小笔记本内存,但由于.bss段在编译时不会被初始化,并且它可以与bar.s一起工作,所以我不知道为什么会出现这种情况。

当我减少foo.s中请求的.bss大小时,gcc可以成功编译链接,并且所有内容都可以正常执行。此外,如预期的那样,在每种情况下创建的可执行文件的文件大小都相同。

   gcc bar.c foo.s -Wall      

不依赖于在.bss中所请求的大小(我编译了各种大小,但它们都比原始大小小得多,失败了)。在所有情况下,可执行文件非常小(可能是10k)-实际上,除了原始情况外,全部大小都相同 - 但显然,原始情况无法成功编译并卡住。

这是gcc的错误吗?有没有命令行选项可以防止这种情况发生?或者到底发生了什么?

编辑:根据评论,这里是.bss段分配部分:

# Sieve of Eratosthenes
# Create list of prime numbers smaller than n
#
# Note: - no input error (range) check
#       - n <= 500,000,000 (could be changed) - in assembly
#         compiling it with gcc: trouble. make n <= 50,000,000
# Returns: pointer to array of ints of prime numbers
#          (0 sentinel at end)
#
# Registers: %esi: sentinel value (n+1)
#            %edx: n
#            %ecx: counting variable (2 - n)
#            %ebx: pointer into array of primes
#                  (position next to be added)
#            %eax: inner pointer to A. tmp array
    .section .bss
#   .lcomm tmp_Arr, 2000000008  # 500,000,000 plus sentinel & padding
#   .comm prime_Arr, 500000008 # asymptotically, primes aren't dense
    .lcomm tmp_Arr, 200000008  # 50,000,000 plus sentinel & padding
    .comm prime_Arr, 50000008 # asymptotically, primes aren't dense

    .section .text
    .globl sieve
     .type sieve, @function
sieve:
    pushl %ebp
    movl %esp, %ebp
    movl 8(%ebp), %edx
    pushl %esi
    pushl %ebx  

    # create Eratosthenes tmp array
    movl $0, %ecx
loop_sieve_Tmp_:    
    movl %ecx, tmp_Arr(, %ecx, 4)
    addl $1, %ecx
    cmp %ecx, %edx
    jge loop_sieve_Tmp_

    # initialize registers used in algorithm
    movl $2, %ecx   # outer loop counting var
    movl %ecx, %eax # inner loop counting var
    xor %ebx, %ebx  # pointer to prime array
    movl %edx, %esi
    incl %esi       # sentinel (or placeholder for 'not prime')
 loop_sieve_Outer_:
    movl %ecx, prime_Arr(, %ebx, 4)  # record prime
    incl %ebx
 loop_sieve_Inner_:
    addl %ecx, %eax
    movl %esi, tmp_Arr(, %eax, 4)
    cmp %eax, %edx
    jge loop_sieve_Inner_
 find_Next_:    # find minimum in Erist. tmp array
    addl $1, %ecx
    cmp %ecx, %edx
    jl lbl_sieve_done_
    cmp tmp_Arr(, %ecx, 4), %esi
    je find_Next_

    movl %ecx, %eax
    jmp loop_sieve_Outer_
 lbl_sieve_done_:
    movl $0, prime_Arr(, %ebx, 4)       # sentinel
    movl $prime_Arr, %eax

    popl %ebx
    popl %esi
    movl %ebp, %esp
    popl %ebp
    ret
 # end sieve

@duskwuff:我添加了.bss段分配的部分。质因数分解文件(仍在完善中)是getFactors.s,处理i/o测试Factor.s位于https://github.com/RalfMBecker/Euler(C文件分配两个整数的本地(自动)数组,每个大小为10,声明一个long long n(也是本地的),并按照.s文件中的签名调用getFactor(),然后打印因子(如上所述,当使用较小的大小时,所有工作都正常)。我没有将C文件放在github上,因为它真的不是什么特别的东西 - 可能是main()中的10行代码;所有变量都是本地/堆栈。 - gnometorule
2
我想知道C运行时是否在启动时显式地将0写入整个.bss部分,以便它最终在您的笔记本上翻页?当您仅使用汇编语言时,可能会避免运行时对.bss的初始化。 - Michael Burr
@MichaelBurr:是的,我同意。类似这样的事情一定会发生-不一定是显式初始化,而是可能标记内存段为不可访问-甚至是编译器试图进行编译时也是如此。随着文件大小后来缩小到预期的大小,暂时发生了一些事情,导致本来完全合法的代码(据我所知…)无法编译。这不是一个真正的错误(如果是的话),但是在边缘情况下产生了不必要的不便。 - gnometorule
@MichaelBurr:不,我甚至没有看到运行时:当我使用已注释的大小编译时,问题中的编译命令会挂起(我在几分钟后将其杀死,所以不确定它是否会返回)。当我谈论可执行文件具有“相同大小,无论请求的.bss数量如何”时,那是为了比较较小的请求大小。 - gnometorule
这似乎肯定是gcc的一个bug,使用堆可能是一个解决方法。尝试使用较新版本的gcc,如4.8.2。 - Mark Lakata
显示剩余4条评论
1个回答

2
我已经在Debian上的gcc 4.7.2版本中复制了您的问题。对我而言它并没有挂起,但它确实需要相当长的时间(10秒)。
看起来链接器实际上在处理过程中分配和清零“comm”内存。如果机器的内存限制足够低(似乎你的情况是这样的),这会导致链接器将交换区磁盘频繁读写,即使最终生成的可执行文件相当小。对于我而言,内存分配约为2.3Gb。
我尝试了几个变体(.space,.zero,.org),它们都似乎具有相同的效果。
使用最新版本的编译器(4.9),不再出现这种情况。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接