2012-10-16 20 views
14

aşağıda sorunu gösteren bir örnek verilmiştir, derlerken bana taşınma hataları veriyor bazı kodlar vardır: gfortran -O3 -g -o test test.f ile bu derleniyorgfortran: mcmodel = medium tam olarak ne yapar?

program main 
    common/baz/a,b,c 
    real a,b,c 
    b = 0.0 
    call foo() 
    print*, b 
    end 

    subroutine foo() 
    common/baz/a,b,c 
    real a,b,c 

    integer, parameter :: nx = 450 
    integer, parameter :: ny = 144 
    integer, parameter :: nz = 144 
    integer, parameter :: nf = 23*3 
    real :: bar(nf,nx*ny*nz) 

    !real, allocatable,dimension(:,:) :: bar 
    !allocate(bar(nf,nx*ny*nz)) 

    bar = 1.0 
    b = bar(12,32*138*42) 

    return 
    end 

, aşağıdaki hatayı alıyorum:

relocation truncated to fit: R_X86_64_PC32 against symbol `baz_' defined in COMMON section in /tmp/ccIkj6tt.o 

Ama gfortran -O3 -mcmodel=medium -g -o test test.f kullanırsam çalışır. Ayrıca, diziyi ayrılabilir yapıp altprogramda ayırırsam çalışacağını unutmayın.

Sorumu tam olarak ne -mcmodel=medium yapar? Ben

cevap

27

derleyici yığında statik ayırma yerine, otomatik ayırma oluşturur oldukça büyüktür. Statik diziler, COMMON adlı sözde bir tahsisat oluşturan .comm derleme yönergesiyle oluşturulur. Bu bölümdeki semboller toplanır, aynı adlandırılmış semboller birleştirilir (istenen en büyük boyuta eşit büyüklükte bir sembol isteğine indirgenir) ve daha sonra dinlenme, çoğu çalıştırılabilir formattaki BSS (başlatılmamış veri) bölümüne eşlenir. ELF çalıştırılabilirleri ile .bss bölümü, veri segmentinde, yığının veri parçası kısmından hemen önce yer alır (veri bölümünde yer almayan anonim bellek eşlemeleri tarafından yönetilen başka bir yığın parçası vardır).

small bellek modeliyle 32 bit adresleme yönergeleri, x86_64 üzerindeki simgeler adreslemek için kullanılır. Bu, kodu daha küçük ve daha hızlı hale getirir. small bellek modeli kullanan bazı montaj çıkışı:

movl $bar.1535, %ebx <---- Instruction length saving 
... 
movl %eax, baz_+4(%rip) <---- Problem!! 
... 
.local bar.1535 
.comm bar.1535,2575411200,32 
... 
.comm baz_,12,16 

Bu içine bar.1535 sembolün değerini (bu değer sembol konumu adresine eşittir) koymak için (5 bayt uzunluğunda) 32 bit hareket talimatı kullanır RBX kaydının alt 32 biti (üst 32 biti sıfırlanır). bar.1535 sembolünün kendisi .comm direktifi kullanılarak tahsis edilmiştir. baz COMMON bloğu için bellek daha sonra ayrılır. bar.1535 çok büyük olduğu için, baz_, .bss bölümünün başlangıcından itibaren 2 GiB'den fazla biter. Bu, RIP olmayan 32bit (imzalı) ofset EAX değerinin EAX değerinin taşınması gereken b değişkenini adreslemek için kullanıldığından ikinci movl yönergesinde bir sorun teşkil etmektedir. Bu sadece bağlantı sırasında tespit edilir. Montajcının kendisinin uygun ofseti bilmediğinden, yönerge göstergesinin (RIP) değerinin ne olacağını bilmediğinden (kodun yüklendiği mutlak sanal adrese bağlıdır ve bu, linker tarafından belirlenir) Sadece 0 bir ofset koyar ve daha sonra R_X86_64_PC32 türünde bir relokasyon isteği oluşturur. Bağlayıcıya, gerçek ofset değeriyle 0 değerini yamaları talimatını verir. Ancak, bu, offset değeri, işaretli 32 bitlik bir tam sayı içine sığmayacağı ve bu nedenle kurtarıldığı için bunu yapamaz.yer şeylerde medium bellek modeli ile

şuna benzer:

movabsq $bar.1535, %r10 
... 
movl %eax, baz_+4(%rip) 
... 
.local bar.1535 
.largecomm  bar.1535,2575411200,32 
... 
.comm baz_,12,16 

64 bit acil hareket talimatı (10 bayt uzunluğunda) adresini temsil 64 bit değeri koymak için kullanılır İlk bar.1535 kayıt R10 içine. bar.1535 sembolünün hafızası .largecomm direktifi kullanılarak tahsis edilir ve dolayısıyla ELF'nin .lbss bölümünde sonlanır. .lbss, ilk 2 GiB'ye sığmayan sembolleri saklamak için kullanılır (ve bu nedenle 32-bit yönergeler veya RIP'ye göre adresleme kullanılarak adreslenmemelidir), daha küçük şeyler .bss'a gider (baz_ hala .comm ve .largecomm değil). .lbss bölümü, ELF linker komut dosyasında .bss bölümünden sonra yerleştirildiğinden, baz_, 32 bit RIP ile ilgili adresleme kullanılarak erişilemez duruma gelmez.

Tüm adresleme modları System V ABI: AMD64 Architecture Processor Supplement'da açıklanmaktadır. Ağır bir teknik okumadır ancak 64 bit kodun çoğu x86_64 Unix üzerinde nasıl çalıştığını anlamak isteyen herkes için okumalıdır.

movl $2575411200, %edi 
... 
call malloc 
movq %rax, %rdi 

Bu temelde RDI = malloc(2575411200) olup: bir ALLOCATABLE dizi yerine kullanılır

, gfortran (büyük olasılıkla tahsisi büyük boyutlu verilen anonim bellek haritası olarak uygulanır) yığın bellek ayırır. Daha sonra bar unsurları itibaren RDI depolanan değerin pozitif teknikleri kullanarak erişilir: bar başlangıcından itibaren en fazla 2 GiB olan yerler için

movl 51190040(%rdi), %eax 
movl %eax, baz_+4(%rip) 

, daha ayrıntılı bir yöntemi kullanılır. Örneğin. hiçbir şey dinamik ayırma yapılmış olacağını adresi hakkında varsayılır beri

; Some computations that leave the offset in RAX 
movl (%rdi,%rax), %eax 
movl %eax, baz_+4(%rip) 

Bu kod

bellek modeli etkilenmez: b = bar(12,144*144*450) gfortran yaydığı uygulamaktır. Ayrıca, dizi etrafından geçmediğinden, hiçbir tanımlayıcı oluşturulmuyor. Bir varsayılan şekilli diziyi alan ve buna bar geçiren başka bir işlev eklerseniz, bar için bir tanımlayıcı otomatik bir değişken olarak oluşturulur (yani foo yığınında). boo beyan bir arayüze sahip benim örnek durumda call boo(bar) içinde (

movl $bar.1580, %edi 
... 
; RAX still holds the address of the allocated memory as returned by malloc 
; Computations, computations 
movl -232(%rax,%rdx,4), %eax 
movl %eax, baz_+4(%rip) 

ilk hareket, bir işlev çağrısı argüman hazırlar: dizi SAVE özelliği olan statik yapılırsa, açıklayıcısı .bss bölümünde yerleştirilir varsayılan şekilli bir dizi alarak). bar dizi tanımlayıcısının adresini EDI içine taşır. Bu 32-bitlik bir hamledir, bu yüzden tanımlayıcının ilk 2 GiB'de olması beklenir. Nitekim, böyle hem small ve medium bellek modellerinde .bss ayrılır:

.local bar.1580 
.comm bar.1580,72,32 
+1

Bu çok güzel bir açıklama. Teşekkürler. Bu bana, bu şeylerin bir demetine (aradığım şey) daha çok bakmaya başlamak için iyi bir başlangıç ​​sağlar. – mgilson

+0

@mgilson, sadece cevabın şeyiyle ben de bar' başka bir alt tanımlayıcı tarafından geçirildiğinde 'olanlara açıklamalar ekledik. –

8

Hayır, (sizin bar gibi) büyük statik diziler sınırı ise aşabilir ... kod (allocatable diziler ile bir ve olmadan bir) iki sürümü daha fazla veya daha az eşdeğer olduğunu sanmıştım -mcmodel=medium kullanmayın. Ancak, tahsis edilenler elbette daha iyi. Allocatables için sadece dizi tanımlayıcı, tüm dizi değil 2 GB'ye sığmalıdır. GCC referans kaynaktan

: bar yana

-mcmodel=small 
Generate code for the small code model: the program and its symbols must be linked in the lower 2 GB of the address space. Pointers are 64 bits. Programs can be statically or dynamically linked. This is the default code model. 
-mcmodel=kernel 
Generate code for the kernel code model. The kernel runs in the negative 2 GB of the address space. This model has to be used for Linux kernel code. 
-mcmodel=medium 
Generate code for the medium model: The program is linked in the lower 2 GB of the address space but symbols can be located anywhere in the address space. Programs can be statically or dynamically linked, but building of shared libraries are not supported with the medium model. 
-mcmodel=large 
Generate code for the large model: This model makes no assumptions about addresses and sizes of sections. Currently GCC does not implement this model. 
+0

belki soru "statik dizide" ve "allocatable dizisi" arasındaki fark nedir edilir herhalde?Benim izlenimim Eğer yazarken – mgilson

+0

Sadece cevabını düzenlenmiş (ben hakkında çok fazla bilgiye sahip olmayan şeylerden bahsediyorum itiraf etmelidir rağmen) her iki durumda da yığınından tahsis edileceğini oldu. Allocatables'in bir tanımlayıcısı vardır (ek verilerle işaretçi) ve sadece bu 2 GB'ye sığmalıdır. Statik dizi, tüm statik değişkenlerde olduğu gibi statik segmentte tamamen bulunur. –

+0

(Belki sadece statik segmentinde tanımlayıcı bir işaretçi yok, ama bu farkı değişmez.) –