【问题标题】:gcc compiling .c with .s file - .bss confusion (bug?)gcc 用 .s 文件编译 .c - .bss 混淆(错误?)
【发布时间】:2014-06-16 02:36:20
【问题描述】:

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

在 .comm 和 .lcomm 缓冲区之间,汇编文件 foo.s 使用接近最大空间 gas 让我可以在该段中分配(foo 计算 long long 的素数分解)。使用汇编文件 bar.s 处理 i/o 等,一切都可以正常编译和链接(并且快速),并且运行良好。

当我使用 C 文件 bar.c 处理 i/o 时,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,在github.com/RalfMBecker/Euler 处理 i/o testFactor.s 的文件(C 文件分配两个本地(自动)整数数组,每个大小为 10,声明一个 long long n (也是本地的),并根据.s文件中的签名调用getFactor(),然后打印因子(如上所述,使用较小尺寸时一切正常)。我没有将C文件放在github上,因为它真的没什么特殊 - main() 中可能有 10 行;所有变量都是本地/堆栈。
  • 我想知道 C 运行时是否在启动时将 0 显式写入整个 .bss 部分,因此最终会破坏笔记本上的分页?当您不使用任何程序集时,您可能会避免运行时对 .bss 的初始化。
  • @MichaelBurr:是的,我同意。必须进行这样的事情 - 不一定是显式初始化,但可能会将内存段标记为不可访问 - 显然,对于试图编译的编译器也是如此。随着文件大小后来缩小到预期的大小,暂时发生了一些事情,这使得编译原本完全合法的代码(据我所知......)无法编译。不是真正的错误(如果是的话),而是在边缘情况下造成不必要的不​​便。
  • @gnometorule:哦 - 我没有意识到这是关于在编译/链接时(以及在运行时?)发生的问题。我只是想,也许 C 运行时正在从 .bss start 到 .bss end 的每个位置写入零。

标签: c linux gcc assembly x86


【解决方案1】:

我已经在 Debian 上使用 gcc 4.7.2 复制了您的问题。它不适合我,但确实需要相当长的时间(10 秒)。

似乎链接器在处理过程中实际上分配并归零了“comm”内存。如果机器有足够的内存限制(就像你的那样),即使最终的可执行文件很小,也会导致链接器破坏交换。对我来说,内存分配约为 2.3Gb。

我尝试了几种变体(.space、.zero、.org),它们似乎都产生了相同的效果。

使用最新版本的编译器 (4.9) 不再发生这种情况。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多