丸子
Lv 4
丸子 asked in 電腦與網際網路程式設計 · 10 years ago

memory corruption

想請教 Jacob, 東邪大及各方好手一個棘手的問題,

小弟要把一隻程式從 Solaris platform 轉移到 Linux, 程式主體是從國外網站下載然後加以改寫. 因為程式主體包含 K&R style code, linux gcc 似乎不接受 old style coding, 所以小弟著實花了一些時間改 code. 可是 compile 時還是遇到一堆問題不知如何解決, 目前進度是用 linux gcc compile可以過, 可是run project時, 遇到 memory corruption. 以下主要兩個問題.

1. linux gcc compile ok. 可是有一些warning message, 我覺得怪怪的.

gcc -g -I../../include -c -o st.o st.c

st.c: In function `st_lookup':

st.c:107: warning: cast from pointer to integer of different size

實際去看相關 code

typedef struct st_table st_table;

struct st_table {

int (*compare)();

int (*hash)();

int num_bins;

int num_entries;

int max_density;

int reorder_flag;

double grow_factor;

st_table_entry **bins;

};

#define do_hash(key, table)\

((table->hash == st_ptrhash) ? ST_PTRHASH((key),(table)->num_bins) :\

(table->hash == st_numhash) ? ST_NUMHASH((key), (table)->num_bins) :\

(*table->hash)((key), (table)->num_bins))

st_lookup(table, key, value)

st_table *table;

register char *key;

char **value;

{

int hash_val;

register st_table_entry *ptr, **last;

hash_val = do_hash(key, table); // line 107,

FIND_ENTRY(table, hash_val, key, ptr, last);

...

}

感覺是 do_hash回傳的 pointer size和 (int)hash_val size不合, 可是不論我怎麼調整 type, 問題還是存在. 不曉得是不是哪裡弄錯了.

2. 嘗試 run project卻產生如下訊息

*** glibc detected *** malloc(): memory corruption: 0x000000000056c910 ***

利用 gdb 下去 trace 發現問題如下

(gdb) where

...

#4 0x0000003cf6c6bc22 in malloc () from /lib64/tls/libc.so.6

#5 0x000000000042f6a2 in lsStart (list=0x56c110) at list.c:378

#6 0x000000000040d318 in vl_create_rangedecl (type=8, range=0x0,

ids=0x56c110, mv=0) at vl_create.c:748

#7 0x0000000000403993 in yyparse () at verilog.y:1320

看起來是 list.c:378 有問題, 可是實際下去看 code,

#define alloc(type) (type *) malloc(sizeof(type))

lsGen lsStart(list)

lsList list; /* List to generate items from */

{

lsDesc *realList = (lsDesc *) list;

lsGenInternal *newGen;

newGen = alloc(lsGenInternal); // line 378

newGen->mainList = realList;

newGen->beforeSpot = NIL(lsElem);

...

}

實在是看不出哪裡有問題, 想到頭都快爆炸了....Orz

還請各方好手幫忙給些意見吧~ >"<

Update:

typedef struct gen_desc { /* Generator Descriptor */

lsDesc *mainList; /* Pointer to list descriptor */

lsElem *beforeSpot; /* Item before the current spot */

lsElem *afterSpot; /* Item after the current spot */

} lsGenInternal;

Update 2:

to 帕拉提斯大大,

1. 您的意思是說把 do_hash()的回傳值改為long long嗎? 我應該怎麼修改阿? = ="

#define ST_NUMCMP(x,y) ((x) != (y))

#define ST_NUMHASH(x,size) (ABS((int)x)%(size))

#define ST_PTRHASH(x,size) ((int)((unsigned)(x)>>2)%size)

看起來回傳值應該都是 int, 由於整個程式碼太長, 檔案也不少, 所以如果大大需要進一步資料,可能要 email給您了.

Update 3:

to EdisonX,

程式中並沒有用到 void* v = alloc(void);

Update 4:

to 帕拉提斯大大,

我嘗試使用"gcc -m32" 的參數去 compile, 反而出現下面的訊息.

/usr/bin/ld: warning: i386 architecture of input file `verilog_yacc.o' is incompatible with i386:x86-64 output

另外我更改 hash_val = (long long)do_hash(...); 似乎沒什麼效果..?不知道我是不是改錯了..= =

Update 5:

do_hash()原型是define macro, 所以我必須去修改整個 macro 嗎? 有點頭痛不知從何下手..~_~

Update 6:

make clean當然是一定會先做, 只是如果問題真是出在-m32 不就等於我要去修整各下載下來的程式主體? >"

Update 7:

我用的linux gcc 版本, 是gcc-3.4.6-11

Update 8:

看起來真的是大工程阿...頭痛...>"<

我看我可能要花很多時間了...謝謝帕拉提斯大大.

2 Answers

Rating
  • 其威
    Lv 7
    10 years ago
    Favorite Answer

    如果有完整的程式碼會比較清楚.

    1.

    看你在 2 中的 backtrace, 你似乎是在 x86_64 系統上編譯執行這隻程式 (backtrace 給出的程式位置是 8byte).

    所以你的 pointer 會是 8byte (64bit), 可是 gcc 預設是 llp64, 也就是 int 跟 long 是 4byte (32bit), 要 long long 才是 8byte (64bit).

    你可以試著將 hash 的回傳值改成 long long (或 #include <stdint.h> 並使用 int64_t), 或是乾脆用 void *.

    最好再有 ST_PATHNAME() 與 ST_NUMHASH() 的定義式.

    2.

    這段程式碼看起來沒有問題, 我想是其他地方造成 memory corruption, 導致他在 malloc() 的時候出問題.

    === 分隔線 ===

    我 google 了你的程式碼, 發現這堆程式碼似乎是這東西:

    http://users.ece.utexas.edu/~adnan/pop-06/glu/

    他是寫個 32bit 系統用的. 我將編譯器改成 "gcc -m32" 表示要產生 x86 程式碼的話, 就不會有任何編譯警告訊息.

    $ make CC="gcc -m32"

    但是我沒有程式可以測, 所以不知道執行時會發生什麼事.

    2011-05-21 02:19:17 補充:

    這題最主要的問題, 應該是:「The C programming language is not as portable as you think.」

    2011-05-21 05:25:14 補充:

    register 是說,在許可的情況下,編譯器應該盡可能將該變數放在 register 中,而不是放在 memory 中。

    而能放到一個 register 裡面的東西,自然只有 int (或是 char, short, long, long long)...

    如果放不進去,他自然就「安全的忽略」這個 hint 了。

    我找不到有關 register「只能」用在 int type 的資料,請先進提出 reference.

    2011-05-21 05:32:10 補充:

    至於 gcc 的 sizeof(void) 會回傳 1, 那是 GCC 自己的 extension.

    http://gcc.gnu.org/onlinedocs/gcc-2.95.3/gcc_4.htm...

    在 gcc 會無聲的通過編譯 (除非有加 -pedantic), 在 g++ 會出警告.

    void 是 incomplete type, 照理說 sizeof 應該 error 才對 - 你無法確知 incomplete type 的長度.

    2011-05-21 06:34:25 補充:

    我想他說「一個單位的值」應該是指 scalar value,pointer 是 scalar value.

    這個「整型的長度」應該是指該平台 register 的長度。例如 linux 採 lp64,所以應該是 64bit:

    sizeof(short): 2

    sizeof(int): 4

    sizeof(long): 8

    sizeof(long long): 8

    sizeof(void *): 8

    2011-05-23 14:34:57 補充:

    你要先 make clean 再 make CC="gcc -m32". 而且你的系統上不一定有 32bit library, 要看發行套件支不支援.

    32-64 bit 的轉換不是光改一個地方就好了, 你必須把所有不相容的地方都修改掉.

    2011-05-23 14:36:49 補充:

    而且你改的方法錯了... 是要改 do_hash() 函數的原型, 不是光在呼叫端改回傳值型態.

    2011-05-24 00:28:32 補充:

    do_hash() 裡面呼叫 *table->hash() 來產生實際的雜湊值, 所以你要修改可設定給 *table->hash() 的所有函式.

    你還必須確認其他程式碼, 檢查是否有把 32bit 資料轉成 64bit 的地方 - 這會是個大工程.

  • 10 years ago

    我也認為帕拉說的 pointer size 有可能是個問題,

    --------

    比較好奇的是這個 (應不是問題點)

    register char *key;

    我不知道早期對於 register 是怎麼制定,但就我所知,

    register 關鍵字不可用於 pointer 、array、non-integer variable,

    所以我覺得奇怪。

    2011-05-21 04:21:19 補充:

    另想先請 丸子 確認一件事

    #define alloc(type) (type *) malloc(sizeof(type))

    程式碼中有沒有用到這種東西

    void* v = alloc(void);

    2011-05-21 04:21:41 補充:

    y C99, 6.5.3.4

    2 The sizeof operator yields the size (in bytes) of its operand,

    which may be an expression or the parenthesized name of a type. The

    size is determined from the type of the operand...(以下略)

    2011-05-21 04:22:04 補充:

    vc 下會 return 0,gcc 下會給出 warnning,並回傳 1,並有下列訊息

    [Warning] invalid application of 'sizeof' to a void type

    剩下的再看其他高人怎麼說。

    2011-05-21 05:54:32 補充:

    >> 我找不到有關 register「只能」用在 int type 的資料,

    >> 請先進提出 reference

    這部份確實為我誤,我誤解原意,

    參考書籍:C語言深度解剖 (應不夠 power)

    register 变量必须是一个单个的值,并且其长度应小于或等于整型的长度。而且register 变量可能不存放在内存中,所以不能用取址运算符“&”

    来获取register 变量的地址。

    2011-05-21 05:57:25 補充:

    至於提醒 sizeof(void) 為 undefined ,乃因該份程式碼之 marco

    #define alloc(type) (type*)malloc(sizeof(type))

    因此而提醒而已

Still have questions? Get your answers by asking now.