【发布时间】: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 运行时正在从
.bssstart 到.bssend 的每个位置写入零。