Kho tháng 3/2008

Thứ hai, 31 Tháng ba năm 2008 21:53:23 ICT

Ôi... Sợ Em Linux

Không phải Linux nói chung mà là SELinux. Một lần nữa bạn ấy lại có dịp phải chạm mặt với Fedora. Một lần nữa bạn ấy lại phải lầm rầm chửi rủa thằng SELinux. Kệ, cứ chửi theo kiểu mấy thằng dốt hay chửi những gì mình không biết cái đã.

Sau một khoảng thời gian cày cục Apache để cuối cùng nhận ra cái CGI script không chạy vì mình quên cho quyền nó vô /home/pclouds (khốn khổ, đã kiểm tra cả /home lẫn /home/pclouds/www mới đau). Thất vọng tột bực với cái trình độ Ác Min ngày càng bèo nhèo của mình, ấy tiếp tục chỉnh thằng Samba. Khốn khiếp! Sao mình chỉnh mãi nó chẳng cho vào, cứ "Permission denied". Khốn khiếp hơn nữa, đặt ra /tmp thì nó chạy tốt, thử với /var là nó chết tươi.

Ấy đành bất lực nhìn... cái màn hình (không lấy gì làm mát mẻ, nhờ vị trí hẻm cụt, nếu màn hình mình mà mát thì cả phòng nóng theo, chưa kể mấy em âm tính sẽ chuyển sang nóng lạnh). Bỗng nhiên ấy nhớ ra vụ sờ em Linux đặc trưng của FC. Tự nhận mình dốt đặc vụ sờ em sờ anh này, ấy không ngần ngại... gõ ngay system-config-<tab><tab><tab> để rồi nhận ra có một mục sờ soạn dành riêng cho smb. Grrr... còn cái security framework nào bưởi hơn khi chỉ đơn giản làm khó cho người dùng, đến nỗi người dùng chỉ còn lựa chọn... tắt quách nó đi.

Lẽ ra ấy đã định tắt ngay thằng sở khanh dê già đó ngay khi nhận FC, nhưng ấy... mềm lòng. Hic..

Gì thì gì. Chuyện mình dốt cũng không có gì đáng xấu hổ. Miễn sao công nhận mình dốt (vụ này thì quá giỏi). Mai vô coi cấu hình SELinux làm sao. Mai .. sốp.. em.. linux... luôn


Cập nhật 2 lần. Lần cuối: Tue Aug 08 11:22:15+0011 2017

Tác giả: pclouds | Liên kết tĩnh | Linux

Thứ ba, 25 Tháng ba năm 2008 21:04:59 ICT

Sờ Nhiều Mới Phê

Hay Sờ Nữa Em Phê, hay SNMP(Simple Network Management Protocol) là một giao thức được dùng để kiểm tra, theo dõi và cấu hình các thiết bị từ xa. Có thể hình dung nó như Windows Registry, GConf hay /proc, chỉ khác một điều là truy cập từ xa. SNMP có nhiều phiên bản (ít nhất là 1, 2c và 3), bài này chỉ nói về phiên bản 2c.

Vậy SNMP là... thế đó. Dùng SNMP như thế nào? Gói phần mềm "chuẩn" để truy cập một SNMP server (hay thường gọi là SNMP agent) là gói net-snmp. Do dùng gói này cũng khá là cực nên mới có bài viết này.

Trước khi đi vào chi tiết, hãy bàn sơ về các thông số cần thiết để kết nối đến SNMP agent. Ta cần địa chỉ IP của máy (dĩ nhiên), cổng UDP (mặc định 161), community (tuỳ cấu hình, thường thì có một cái community là "public", bài này giả định là community này). Community cũng giống như username, hay cứ coi như thế. Và cuối cùng là biết số phiên bản SNMP, ở đây là 2c. Với bây nhiêu đó tham số, ta sẽ cần truyền vào một lệnh snmp giống như thế này (bỏ qua cổng UDP mặc định):

snmpcmd -v2c -c public IP

Lát nữa "snmpcmd" sẽ được thay bằng các lệnh SNMP khác, nhưng các tham số cơ bản vẫn là thế. Vậy là đã tìm đường tới một máy SNMP, làm sao biết máy đó chứa gì? Có một lệnh sẽ hiển thị tất cả thông tin của một SNMP agent:

snmpwalk -v2c -c public IP

Như tên gọi, lệnh này sẽ lướt qua (walk) và in tất tần tật các thông số có thể thấy. Dĩ nhiên chả ai dại gì dùng lệnh này hơn một lần vì vừa mệt server, vừa chả xem được gì.

Nếu dòm vào đống kết quả snmpwalk, ta sẽ thấy số ơi là số. SNMP đánh địa chỉ một mục thông tin bằng một dãy số, gọi là OID(object identifier), gần tương tự như IP bốn số. Có điều dãy số SNMP dài hơn nhiều, có thể lên đến hơn 10 số. Đã nhắc đến IP, có lẽ đa phần sẽ hình dung đến DNS bởi vì chẳng mấy ai thích việc gõ IP cả. SNMP cũng có một khái niệm tương tự, dùng để ánh xạ một chuỗi ký tự sang OID, gọi là MIB(Management Information Base). Cấu trúc tập tin mô tả MIB khá là phức tạp, nhưng dùng thì không đến nỗi. Một OID thường có thể được ghi bằng một chuỗi các ký tự cách nhau bằng dấu chấm, giống như DNS.

Hãy lấy một ví dụ, OID 1.3.6.1.4.1.4874.2.2.23.1.1 có thể được ánh xạ thành juniFileXferMIB::juniFileXferObjects.juniFileXferTable trong đó juniFileXferMIB tương đương với 1.3.6.1.4.1.4874.2.2.23, juniFileXferObjects là 1 kế tiếp, và juniFileXferTable là 1 cuối cùng. Để ý là ví dụ này chứa một dấu chưa đề cập ::, dùng để diễn tả một MIB module (là juniFileXferMIB). Cấu trúc MIB (hay OID) đưa chia thành cây các MIB module. Mỗi MIB module sẽ chứa một tập các mục thông tin nhỏ. Để hoàn thành ví dụ, MIB hoàn chỉnh không dùng MIB module sẽ là

iso.org.dod.internet.private.enterprises.juniperUni.juniperUniMibs.juniMibs.juniFileXferMIB.juniFileXferObjects.juniFileXferTable

Dài kinh khủng! Đến độ tuy chữ có vẻ dễ hiểu hơn số, chẳng ai muốn gõ một dòng như thế chỉ để tìm hiểu xem bây giờ mấy tính nóng bao nhiêu. Rất may là MIB qui định mọi tên trong MIB đều phải là duy nhất, và do thế ta chỉ cần ghi ngắn gọn juniFileXferTable là cũng đủ để xác định chính xác mục thông tin cần quan tâm.

Giả định ta đã biết tên MIB của mục thông tin cần xem, làm thế nào để xem? Hết sức đơn giản:

snmpget -v2c -c public IP juniFileXferTrapEnabled

Ta cũng có thể dùng snmpwalk để xem tất cả giá trị nằm trong một nhánh OID đang quan tâm. Ví dụ lệnh sau sẽ hiển thị tất cả thông tin nằm trong juniFileXferObjects:

snmpwalk -v2c -c public IP juniFileXferObjects

Vậy để đổi giá trị? Trước hết cần phải biết kiểu dữ liệu của mục cần đổi. Mỗi kiểu dữ liệu được kí hiệu bằng một kí tự, mô tả trong man snmpset. Trong trường hợp của ta, juniFileXferTrapEnabled kiểu interger (chữ i), chấp nhận giá trị 1 là true và 2 là false. Ta sẽ thử tắt nó bằng cách đổi sang 2:

snmpset -v2c -c public IP juniFileXferTrapEnabled i 2

Phần giá trị cũng có thể dùng MIB nếu đã được định nghĩa trước

snmpset -v2c -c public IP juniFileXferDirection.1 i juniFileXferLocalToRemote

SNMP, ngoài các kiểu dữ liệu cơ bản như chuỗi, số... còn hỗ trợ "bảng". Bảng là một tập các giá trị được nhóm theo từng hàng đánh số từ 0. Để xem toàn bảng, dùng

snmptable -v2c -c public IP juniFileXferTable

Để xem thuộc tính juniFileXferTrapEnabled trong bảng thuộc hàng thứ 0, dùng snmpget như sau:

snmpget -v2c -c public IP juniFileXferTrapEnabled.0

Từ lệnh trên, có lẽ cũng không khó hình dung ra lệnh đặt lại giá trị cho juniFileXferTrapEnabled hàng thứ 0. Vậy để tạo một hàng mới? Ta cần phải đặt một giá trị đặc biệt vào một thuộc tính đặc biệt của hàng cần thêm. Trong ví dụ này giá trị đặc biệt là 5 (createAndWait), hàng cần thêm là 1:

snmpset -v2c -c public IP juniFileXferRowStatus.1 i 5

Đấy, SNMP chỉ có thế. Nhưng một loạt thông tin (ví dụ mấy cái juni..., con số 5 ma thuật...) kiếm ở đâu? SNMP lưu tất cả các thông tin này trong các tập tin MIB (đuôi .mib hay .mi2), bạn cần chỉ cho net-snmp biết nơi đặt những tập tin này để net-snmp sử dụng. Các tập tin mib này đi kèm với các thiết bị hỗ trợ SNMP. Ví dụ thư mục /tmp/juniperMibs chứa các tập tin mib kể trên, ta cần phải đặt biến môi trường MIBDIRS (nếu có nhiều thư mục thì cách nhau bằng dấu hai chấm):

export MIBDIRS=/tmp/juniperMibs

Chưa đủ, cần phải chỉ ra MIB module nào cần nạp. Ở trình độ "vọc là chính" thì cũng chả cần quan tâm mấy, cứ đặt ALL, nghĩa là "nạp tuốt"

export MIBS=ALL

MIBDIRSMIBS có thể lần lượt thay bằng tham số -M, -m trong các lệnh snmp kể trên.

Sau khi chôm để dành tiền sắm một cái router Juniper ERX320, cấu hình cái router, chôm cái thư mục juniperMibs và thiết lập biến môi trường xong, những lệnh trên phải chạy. Lệnh snmpset có thể không chạy vì community public thường không có quyền ghi, nhưng đó là chuyện điều chỉnh router.

Bạn đã đủ tự tin để tìm hiểu cái router mới cáu, nhưng không đủ can đảm đọc mấy tập tin mib trong juniperMibs để biết mình có thể xem được những gì? Đừng lo, lệnh sau sẽ trình bày cấu trúc toàn bộ các mib mà net-snmp có thể thấy:

snmptranslate -Tp

Nếu bạn chỉ quan tâm những gì bên trong juniFileXferTable, dùng

snmptranslate -Tp juniFileXferTable

Lần đầu tiên xem thế cũng vui mắt, nhưng nhỡ có lúc muốn tìm lại một mục nào đó, chẳng lẽ lại duyệt từ đầu. Bạn biết regular expression? Tuyệt, hãy dùng lệnh sau để liệt kê những mục khớp với regex của bạn (tốt hơn là .* cái gì đó, ví dụ .*File):

snmptranslate -TB '.*File'

Tốt, bạn đã lấy được juniFileXferTable, nhưng bạn bắt đầu chán ghét MIB và muốn dùng OID thuần tuý, làm sao biết juniFileXferTable tương ứng với OID nào?

snmptranslate -IR -On juniFileXferTable

Hay bạn muốn khoe mẽ (y như cái thằng viết bài này) MIB đầy đủ của juniFileXferTable?

snmptranslate -IR -Of juniFileXferTable

Bạn đã sẵn sàng để đối mặt với MIB, nhằm tìm hiểu sâu hơn về juniFileXferTable? Không cần phải mò mẫm trong đống tập tin mib, dùng:

snmptranslate -IR -Td juniFileXferTable

Hừm. Vậy là bạn đã biết hết những gì cần biết về SNMP rồi đấy. Chỉ còn thiếu mỗi một thứ: SNMP trap. SNMP trap là cách để SNMP agent chủ động thông báo tin tức cho bạn, bạn khỏi nhọc công truy vấn SNMP agent. Để dùng snmptrapd của net-snmp, trước hết cần tạo /etc/snmp/snmptrapd.conf, cấu hình đơn giản sau là đủ cho mục đích thử nghiệm (chỉ thử community public thôi):

authCommunity log public
authCommunity execute public
authCommunity net public

Sau đó chạy snmptrapd để đón "trap"

snmptrapd -f  -Le

Các tham số dành cho snmptrapd phức tạp tí. Nhưng đại loại lệnh trên nói không chạy nền (kiểu daemon) bằng -f và in ra stderr bằng -Le. SNMP trap dùng cổng UDP 162. Bạn có thể kiểm tra cái SNMP trap mới cáu của mình bằng lệnh snmptrap. Lưu ý là mặc định snmptrap sẽ gửi đến cổng 161. Bạn cần xác định rõ cần phải gửi đến cổng 162.

Đến đây xem như bạn đã đạt trình độ "trùm SNMP của những thằng không biết gì về SNMP". Nên tham khảo thêm phần Tutorials nếu muốn đạt mức "siêu trùm SNMP của ..."

Cuối cùng là tiết mục nhảm. SNMP là viết tắt của:

  • Sống nhờ em Phượng
  • Sinh nhật em Phương
  • Sờ nhầm em Phi
  • Sờ nhiều may phước
  • Sờ nhanh mau ph...

Cập nhật 2 lần. Lần cuối: Tue Aug 08 11:22:15+0011 2017

Tác giả: pclouds | Liên kết tĩnh | OSS, Linux

Chủ nhật, 23 Tháng ba năm 2008 00:08:47 ICT

Sửa lỗi biên dịch C++ bằng GCC

(Bài này lẽ ra phải viết tiếng Anh, nhưng tiếng nước ngoài thuộc loại tối kị ở cái hẻm này, nên chơi tiếng Việt luôn!)

Ai cũng biết "làm phần mềm" đơn giản gồm hai giai đoạn: viết và... sửa lỗi (nói văn chương là gây hậu quả để rồi đi khắc phục hậu quả, đời có thể định nghĩa đơn giản là một chuỗi các hậu quả liên tiếp và những hành động xoay quanh). Hai giai đoạn này lặp đi lặp lại liên tục cho đến khi phần mềm "qua đời". Phần viết, có lẽ chẳng cần bàn nhiều. Có vô số sách dạy viết phần mềm, dùng C++... (trong 24 ngày hay 7 ngày gì đó). Riêng phần sửa lỗi thì ít thấy ai dạy, đa phần tự rút kinh nghiệm trong quá trình làm việc.

Nói về sửa lỗi, có thể phân thành hai nhóm lỗi: nhóm lỗi trong lúc biên dịch và nhóm lỗi trong lúc chạy chương trình. Tìm và sửa lỗi xảy ra khi chạy chương trình có thể viết thành tiểu thuyết dài tập với nhiều hồi gây cấn, ở những thời kì khác nhau... nên sẽ không đề cập ở đây. Bài này chỉ đề cập đến lỗi lúc biên dịch.

Do quá trình biên dịch (ở đây chỉ đề cập GCC) bao gồm nhiều giai đoạn và lỗi phát sinh ở mỗi giai đoạn có những đặc điểm riêng, những cách xử lí riêng, nên hiểu biết về các công đoạn biên dịch sẽ giúp việc chẩn đoán và sửa lỗi dễ dàng hơn.

Một chương trình (là một tập hợp các tập tin .cc và .h) khi vào tay GCC, sẽ được:

  1. Tiền xử lý (tất cả các macro sẽ được xử lý và loại bỏ)
  2. Biên dịch thành các tập tin assembly với đuôi .s
  3. Biên dịch thành các tập tin .o
  4. Liên kết các tập tin .o để tạo thành chương trình hoàn chỉnh

(Bước 2 thật sự không cần thiết, thường được gộp thẳng vào bước 3. Tuy nhiên việc nhận ra sự tồn tại của bước 2 sẽ giúp ích nhiều trong quá trình phân tích các lỗi khi chạy)

Bước 1: Tiền xử lý

Bước một đọc, xử lý và loại bỏ hoàn toàn các macro (#ifdef, #define...). Bước này thường hiếm xảy ra lỗi, và lỗi thường dễ đoán. Lỗi trong bước này thường có chữ "macro" hay dấu #. Những lỗi khó sửa thường liên quan đến các macro #if, #else, #endif, #ifdef và đại loại thế. Một số ví dụ:

  • #else without #if
  • #else after #else
  • #elif without #if
  • #elif after #else
  • #endif without #if

Những lỗi này, nói đơn giản là cấu trúc macro if-else bị phá hỏng, thường gặp khi bạn chép một đoạn mã từ tập tin khác sang (do tập tin đó có dùng macro if-else). Đoạn mã bạn chép có thể có #if, nhưng lại thiếu #endif, hoặc ngược lại... Cách sửa duy nhất là lần theo cấu trúc if-else (tìm tất cả các macro if-else trong tập tin) và bảo đảm là cấu trúc này không bị phá vỡ.

Hai dạng lỗi khác liên quan đến bước 1, nhưng không phát sinh lỗi ngay ở bước 1. Dạng đầu là macro có sử dụng nhưng không được xử lý (bung macro). Dạng hai, ngược lại, không định dùng macro, nhưng trình xử lý macro nhận dạng nhầm và coi đó như macro.

Ở dạng đầu, trường hợp thường gặp là sử dụng macro mà quên khai báo macro. Ví dụ, macro abc được khai báo trong tập tin file1.h và được sử dụng trong tập tin file1.cc. Ta chép một hàm từ file1.cc sang file2.cc mà quên #include "file1.h". Hậu quả là abc không được nhìn nhận là một macro trong file2.cc ở giai đoạn tiền xử lý, và do đó được đẩy thẳng qua bước 2/3.

  • Nếu abc được sử dụng giống như một biến, thường ta sẽ gặp error: 'abc' undeclared (first use in this function)
  • Nếu abc được sử dụng như một hàm, ta sẽ thậm chí không gặp lỗi ở bước 2/3 mà chỉ có cảnh báo warning: implicit declaration of function 'abc'. Lỗi chỉ thật sự xảy ra ở bước 4 undefined reference to 'abc'

Xử lý tương đối đơn giản: nếu chắc chắn abc là macro, tìm tập tin khai báo macro đó và, hoặc #include, hoặc chép khai báo đó vào tập tin sử dụng.

Ở dạng sau, lỗi thường cực kì khó hiểu. Nếu đã xem qua dòng gây lỗi mà vẫn không thể hình dung tại sao lại xảy ra lỗi như vậy, nên xem xét kiểm tra trường hợp này. Cách kiểm tra khá đơn giản (trên lí thuyết): nhờ GCC tiền xử lí tập tin, sau đó kiểm tra mã nguồn xem abc có còn nguyên vẹn tại dòng gây lỗi hay không. gcc -E sẽ dừng quá trình biên dịch ngay khi kết thúc bước 1 và xuất kết quả ra màn hình. Tham số thật sự cho gcc sẽ khá phức tạp, phụ thuộc vào từng dự án. Thường ta sẽ theo dõi được dòng biên dịch tập tin file2.cc đại loại như sau:

gcc .... -o file2.o ... -c file2.cc ....

Ta chỉ cần sử dụng lại dòng lệnh đó, thay file2.o bằng một tên tập tin tạm (sẽ chứa kết quả sau khi tiền xử lý) và thay -c bằng -E.

Bước 2/3: Biên dịch

Bước 2/3 (từ này về sau sẽ chỉ đề cập là "bước 3") thật sự là công đoạn vất vả nhất của trình biên dịch: phân tích mã nguồn, chuyển đổi sang mã máy. Lỗi ở giai đoạn này (cũng như bước 1) thường chỉ rõ tập tin .cc hoặc .h nào gây ra lỗi, và lỗi ở dòng nào.

Một số dạng lỗi thường gặp:

  • 'foo' undeclared: kiểm tra xem bạn có thật sự khai báo foo hay chưa (và có thật sự #include nếu khai báo nằm ở tập tin .h). Ngoài ra cân nhắc lỗi ở bước 1.
  • "expected" cái gì đó: thường thông báo lỗi khá rõ ràng, bạn thiếu mất một dấu chấm, dấu ngoặc, hoặc bạn dùng sai kiểu... Trong một số trường hợp (ví dụ như thiếu } hay "), cần tìm ngược lên trên để xác định vị trí thật sự gây ra lỗi
  • ... discard qualifiers: bạn đang dùng biến kiểu const cho những thao tác có khá năng thay đổi nội dung biến (thường là truyền biến cho một hàm yêu cầu kiểu dữ liệu không có const)
  • storage size of 'foo' isn't known: "foo" có lẽ được khai báo dạng forwarding-declaration, nhưng khai báo cấu trúc thực sự của "foo" (ví dụ các trường trong một struct) thì không có. Cần tìm xem đã #include tập tin chứa khai báo thật sự của "foo" hay chưa.
  • no matching function for call to 'FooClass::foo()': bảo đảm hàm foo thật sự được khai báo trong lớp FooClass. Sau đó bảo đảm truyền tham số đúng (bao gồm cả đúng kiểu cho từng tham số, do hàm foo có thể có nhiều phiên bản khác nhau tuỳ theo tham số)
  • cannot call member function 'Foo* Foo::instance() const' without object: bạn đang gọi Foo::instance() theo kiểu hàm static, nhưng lại khai báo đó là một phương thức bình thường (không static).
  • syntax error before...: bảo đảm chấm phẩy, móc ngoặc đầy đủ. Nếu thông báo lỗi nằm ở đoạn khai báo (khai báo hàm, struct...) thì bảo đảm kiểu được đề cập trước before ... đã được khai báo.

Bước 4: Liên kết

Sau bước 3, ta có được các tập tin .o. Những tập tin này chứa mã máy cho những hàm được cài đặt trong các tập tin .cc tương ứng. Do hàm này có thể tham khảo đến hàm/biến khác không nằm cùng tập tin .o, những tham chiếu như vậy sẽ được giải quyết ở bước 4, khi ta gom tất cả các hàm/biến lại với nhau để tạo thành một chương trình hoàn chỉnh.

Điểm nổi bật của lỗi bước 4 là không đề cập dòng phát sinh lỗi (trong trường hợp xấu hơn, không đề cập cả tập tin .cc gây ra lỗi). Điều này thường làm cho việc tìm và sửa lỗi loại này khó khăn hơn các dạng lỗi khác.

Dạng lỗi phổ biến đầu tiên ở bước này là "undefined reference to foo". Nói đơn giản, có vài chỗ cần dùng đến hàm/biến tên foo, nhưng nó thật sự không tồn tại, do nhiều lí do:

  • Dùng nhầm tên. Có thể tên biến/hàm thật sự cần dùng là foo2 chứ không phải foo. Một số khả năng:

    • Trộn lẫn C++ và C. C++ "chắp vá" "foo" thành "fooXYZ" trong khi đoạn code C lại tưởng "foo" chỉ đơn giản là "foo". Giải pháp là dùng extern "C" cho những hàm cần gọi từ C.
    • Cùng tên hàm nhưng khác tham số. Có thể cách đưa tham số làm GCC hiểu nhầm là một hàm trùng tên khác tham số. Cần kiểm tra kiểu biến truyền vào hàm xem có khớp với kiểu biến khai báo hàm hay không.
    • Hàm template dùng kèm với -fno-implicit-templates. Bạn phải thật sự "instantiate" một template trong trường hợp này.
  • Chưa khai báo biến. Các biến "extern" và biến static của lớp chỉ mới là bảo với phần còn lại thế giới "trên đời này có foo", nhưng chưa thực sự cấp phát một vùng nhớ dành cho "foo". Bạn phải khai báo lại foo trong một tập tin .cc (trong trường hợp biến extern, khai báo lại mà không có "extern")

  • Chưa khai báo hàm.

  • Bạn dùng một hàm/biến từ thư viện có sẵn nhưng thật sự thì thư viện đó không hỗ trợ hàm/biến bạn dùng?

Một lỗi khác là "multiple definition of foo". Lỗi này thường xảy ra khi bạn khai báo biến toàn cục trong tập tin .h mà thiếu extern "C". Hoặc chỉ đơn giản là bạn link đến hai bản sao của foo.o

Quay lại lần chót với "undefined reference", trường hợp lẽ ra foo phải tồn tại trong hệ thống. Có cái gì đó sai dẫn đến sự mất tích của foo trong các tập tin .o (và hậu quả là không thể liên kết thành công). Trong trường hợp này, dĩ nhiên ta biết foo phải nằm ở tập tin nào, và giải pháp đơn giản là thử và sai. Tuy nhiên để tránh thời gian liên kết (thường khá dài với những dự án lớn) làm chậm quá trình thử và sai, ta có thể "thử" ngay tập tin .o tương ứng thay vì phải liên kết để kiểm tra lỗi "undefined reference" có xuất hiện hay không. Lệnh nm (đối với trình biên dịch chéo thì nó có thể có một tên khác, ví dụ mingw32-nm hoặc nmppc) có thể được dùng để liệt kê danh sách các symbol có liên quan đến một tập tin .o. Kết quả là một loạt các dòng theo định dạng

[Địa chỉ] Loại Symbol

Loại là một ký tự. Có khá nhiều ký tự, có thể tham khảo tài liệu về nm. Nhưng ký tự ta quan tâm là U (The symbol is undefined). Nếu kết quả kiểm tra foo cho thấy ký hiệu của foo không phảiU, nghĩa là ta đã thành công tạo ra foo. Nói cách khác, ta sẽ không bị lỗi undefined reference liên quan đến foo.

C++

Hầu hết những gì đề cập ở trên có thể áp dụng cho cả C và C++. Phần này sẽ dành để nói thêm về hai điểm khá đặc trưng của C++ so với C: template và cách biến đổi tên của C++ (name mangling)

Trước hết về template. Điểm đầu tiên cần lưu ý, template không phải là code thật sự. Template bảo trình biên dịch biết có thể tạo một loạt các loại cấu trúc dữ liệu hoặc các hàm theo mẫu này, mẫu này, hết. Thời điểm template thật sự biến thành "vật chất" gọi là "template instantiation". Thường đó là khi bạn dùng template (ví dụ list<int> intlist;).

Mặc định GCC sẽ tự động "vật chất hoá" template cho bạn khi bạn dùng nó (trong ví dụ trên, nó sẽ tạo một lớp thực sự tương ứng với list<int>). Trường hợp ngoại lệ là khi bạn truyền tham số -fno-implicit-templates cho GCC, yêu cầu GCC không tự động làm thế. Trường hợp đó, bạn cần có một dòng như thế này trong một tập .cc:

template class list;

Nếu không có dòng này, "máu thịt" cho lớp list<int> không tồn tại, bạn sẽ nhanh chóng đối diện với đống lỗi "undefined reference" cực kì khó hiểu.

Điểm còn lại là cách biến đổi tên của C++. Khi bạn nói Foo::blah(int,int), GCC phải biến đổi chuỗi đó về một dạng đơn giản hơn. Có thể tìm hiểu kĩ hơn về chủ đề này trên wikipedia. Bạn có thể phần nào đoán được cách biến đổi. Tuy nhiên, nếu quá phức tạp, hãy giao công việc cho những người chuyên nghiệp: c++filt (hoặc cppfilt, cxxfilt). Công cụ này, khi nhận tham số là một symbol C++, sẽ biến ra dạng dễ đọc.

Những dòng trên có thể giúp bạn lúc này lúc khác, nhưng không thể giúp bạn trong mọi hoàn cảnh. Hiểu biết về cách công cụ mình sử dụng hoạt động như thế nào sẽ giúp bạn điều khiển công cụ đó tốt hơn. Nên nhớ máy tính rất ngu. Vậy đừng trách nếu nó thông báo quá khó hiểu. Bạn ưu việt hơn mọi cái máy. Hãy chứng tỏ khả năng của mình.


Cập nhật 2 lần. Lần cuối: Tue Aug 08 11:22:15+0011 2017

Tác giả: pclouds | Liên kết tĩnh | OSS, Mánh và mẹo

Thứ ba, 18 Tháng ba năm 2008 22:16:04 ICT

Cực hâm

Hân hoan thay là cảnh thân tù!
Tâm mở rộng và hồn sôi rạo rực
Ta lắng nghe tiếng lòng lăn náo nức
Ở ngoài kia nhốn nháo biết bao nhiêu!

(Chôm và sửa từ thơ Tố Hữu hoàn toàn không vì mục đích bôi nhọ bản gốc)


Cập nhật 3 lần. Lần cuối: Thu Aug 25 17:43:17+0003 2022

Tác giả: pclouds | Liên kết tĩnh | Hâm, Trích dẫn

Thứ hai, 17 Tháng ba năm 2008 21:32:09 ICT

Chạy gdb với autotools

Bạn nào đã "từng trải" việc dùng gdb với những chương trình sử dụng autotools (đúng hơn là libtool) chắc cũng biết nỗi khổ này. Sau khi biên dịch chương trình xong, các tập tin thực thi được cho là chương trình, không phải là chương trình thực sự (theo dạng thức ELF) mà là shell script. Lí do là bởi vì libtool, công cụ đứng sau lưng, cần phải tái liên kết một số thư viện để chương trình sử dụng nhưng chưa cài đặt (như bạn biết, các thư viện chỉ được tìm trong các đường dẫn cố định, thường là /usr/lib, nên nếu chưa cài đặt thư viện, bạn sẽ không thể dùng).

Thì kệ nó, miễn sao chạy được là ngon. Tuy nhiên gdb lại yêu cầu đối tượng cần "kiểm tra/theo dõi" phải là ELF (thường nằmg trong thư mục .libs tương ứng). Và để bạn có thể vượt qua ải "tìm thư viện" để chạy với gdb, cần phải điều chỉnh biến LD_LIBRARY_PATH khá cực nhọc (càng dùng nhiều thư viện càng cực). Thật ra có một cách nhanh hơn, cũng là mục đích của bài viết dài dòng này:

libtool gdb

Cập nhật 2 lần. Lần cuối: Tue Aug 08 11:22:15+0011 2017

Tác giả: pclouds | Liên kết tĩnh | Mánh và mẹo, Linux

Thứ hai, 17 Tháng ba năm 2008 21:03:07 ICT

Chuyện loài Ép Ếch

Nói ngắn gọn là quảng cáo cho bài From BFS to ZFS: Past, Present, and Future of File Systems.

Với những ai chưa đọc. Đây là một câu chuyện lịch sử về tình người, sự chung thuỷ, tính lười nhác và lòng thù hận. Nó cho ta thấy lịch sử luôn để lại nhiều bài học đáng giá mà các thế hệ sau không thể làm ngơ.

Lịch sử luôn tồn tại trong ta, như thể bóng ma CP/M vẫn lẩn khuất đâu đó trong thế kỉ 21 chộn rộn này. Lịch sử cho thấy những quyết định nông nổi sẽ để lại hậu quả dài lâu. Lịch sử cho ta thấy anh em cũng có ngày đánh nhau vì mục đích cá nhân. Lịch sử cho thấy những cuộc cách mạng đột phá, mang dấu ấn "geek", bao giờ cũng bị dập tắt. Nhưng lịch sử cũng ghi nhận những đóng góp đáng kể từ những cuộc cách mạng này, những thành tựu (và cả hậu quả) lưu truyền qua biết bao thế hệ.

Thế giới vẫn cứ phát triển. Nhiều dòng họ "Ép Ếch" danh giá nổi lên, tranh giành quyền lực, rồi tiêu vong. Chỉ mong mơ ước thầm kín (của ai đó) "NTFS tồn tại đến tận 60056 sau công nguyên" sẽ mãi mãi chỉ là ước mơ. Để đời mãi xanh tươi.

Trường Giang cuồn cuộn đổ về Đông
Bao lớp sóng xô bấy lớp anh hùng
Ngoảnh đầu lại nhân tình thế thái
Được mất bại thành bỗng chốc hóa hư không

Biết mấy tịch dương nhuộm hồng sóng nước
Bao kiếp ngư tiều bơi chải theo dòng
Đắm mình với gió Đông
Cất chén rượu nồng thêm thoả chí
Dưới ánh trăng thu càng thắm thiết cuộc trùng phùng
Chuyện xưa, chuyện nay, bại thành, được mất
Được mất, bại thành, bỗng chốc hoá hư không

Cập nhật 3 lần. Lần cuối: Thu Aug 25 17:43:17+0003 2022

Tác giả: pclouds | Liên kết tĩnh | Hâm, Trích dẫn

Thứ sáu, 14 Tháng ba năm 2008 11:28:14 ICT

Geek time: History of Linux Quiz

Một phần cũng nhờ may mắn vì không chắc nhớ chính xác tháng phát hành Linux 0.01. Câu S/390 phải xem mã nguồn :(

Your score: 90 out of 100 (Score 90: You do everything at the command line! Score 100: Can you write the next quiz for us?)

Your answers

  1. Most Linux aficionados know that Linus Torvalds created the Linux kernel. But on what sort of computer did he write it on?

Your answer: c. 386 PC

  1. Linus was driven to write Linux because he couldn't afford the proprietary operating system and the hardware it ran on while studying at the University of Helsinki in Finland. What company made the operating system he couldn't afford?

Your answer: b. Sun Microsystems

  1. The creation of Linux was inspired by what operating system created by Andrew S. Tanenbaum to run on Intel 8086-based machines as a teaching tool?

Your answer: a. MINIX

  1. When was Linux version 0.01 released?

Your answer: d. September, 1991

  1. Richard Stallman is well known as a programmer and the founder of the GNU Project and the Free Software Foundation. In addition to his compiler development, he worked on the creation of which well used application.

Your answer: a. Emacs

  1. The Linux kernel number indicates whether a kernel is stable or in development. Stable kernels are:

Your answer: c. End in an even number

  1. Patrick Volkerding in 1993 created one of the earliest distributions of Linux and probably the oldest still currently maintained. That distribution is known as:

Your answer: b. Slackware

  1. Back in the late 90s, with the dotcom boom in full force, many companies were putting funding into Linux projects. Which large software company, best known for graphic design software had a Linux distribution?

Your answer: d. Corel

  1. With the Linux buzz and the rise in tech stocks a services company was started in late 1998 to provide support to enterprise Linux users. That service company had many well known developers from leading open source software projects including Rasmus Lerdorf (PHP) and Andrew Tridgell (samba, rsync). That company was:

Your answer: d. Open Source Support Network

Incorrect. Correct answer is b. Linuxcare (They're now called Levanta)

  1. Linux runs on many different processors 386, PowerPC, SPARC and Alpha. Does it run on IBM S/390 and Wang VS machines.

Your answer: a. On the S/390 but not the Wang VS. (There is Linux software that will allow you to run Wang VS applications on standard server hardware.)


Cập nhật 2 lần. Lần cuối: Tue Aug 08 11:22:15+0011 2017

Tác giả: pclouds | Liên kết tĩnh | Linux

Thứ năm, 13 Tháng ba năm 2008 20:37:16 ICT

Thằng mất dạy

Tự chửi mình sau khi đọc hơn 100 trang "Kẻ vô luân". Phải nói thêm là vào lúc tự chửi, độ cồn trong cơ thể lớn hơn không.

Nếu thầy Huy đọc những dòng này, cứ coi như chưa đọc gì.

Kẻ vô luân được dịch bởi Bùi Giáng. Người tôi chỉ biết như là một nhà thơ. Đọc hơn 100 trang "Kẻ vô luân" (tác giả André Gide) do Bùi Giáng dịch (ít ra trang đầu nó ghi thế), nhận ra một điều:

  • Hoặc đây không phải là truyện dành cho tôi
  • Hoặc tôi không đủ trình độ đọc truyện này

Điểm đầu tiên đập vào mắt là việc lạm dụng từ Hán Việt. Thằng này, vốn dốt chữ Hán, có cảm tưởng đọc truyện Việt mà như đang cố đọc một tài liệu ngoại ngữ. Nếu thay cả các địa danh trong truyện bằng tên gần tương ứng bằng chữ Hán, thì có lẽ không ai có thể nhận ra đây là truyện gốc Pháp, viết bởi André Gide. Nếu thay nốt lượng ít ỏi từ thuần Việt còn lại, hoàn toàn có thể khẳng địch đây là một bản chuyển ngữ từ tiếng Trung Hoa. Đọc truyện, có cảm giác như đây là truyện Trung Hoa trăm năm trước. Thú thật cảm giác đọc không khác gì đọc Thuỷ hử. Nhưng Thuỷ hử là truyện của trăm năm trước, dịch cách đây hơn chục năm trước. Còn "Kẻ vô luân" in xong và nộp lưu chiểu tháng 8 năm 2007 ?? Hơ... "tặng A Chu A Tỷ"??? Chẳng lẽ truyền hình Việt Nam tràn ngập phim Trung Quốc chưa đủ sao?

Một điều khác gây bực mình hơn là cách chèn các đoạn trích tiếng Pháp trong bản dịch. Tôi chấp nhận các danh từ riêng tiếng Pháp. Có thể tôi hình dung cách đọc không đúng, nhưng tôi vẫn có thể phân biệt các địa danh. Đằng này, chèn ngẫu nhiên vài đoạn văn tiếng Pháp không một lời chú thích, phải chăng đánh đố, hay thử thách trình độ đọc giả? May mắn là tuy dốt tiếng Pháp, tôi vẫn có thể lần mò ra đôi đoạn đã được dịch bên dưới. Nhưng có cần đánh đố đọc giả đến thế không? Nếu đọc giả không biết tiếng Pháp, phải chăng những đoạn trích đó là một cách khoe mẽ, khi đối với đọc giả đó chỉ là những dòng chữ vô nghĩa?

Một điều (có lẽ) ngẫu nhiên khác, là đột nhiên có một địa điểm ở Pháp được đặc cách phiên âm tiếng Việt trong chú thích (Le Morinière). Lẽ nào, sau khi đã tung ra bao từ ngoại quốc, giờ lại phiên dịch? Chính phần chú thích này làm tôi càng mất hứng thú đọc truyện, đồng thời nghĩ đến một chữ: cẩu thả. Tôi có thể chấp nhận một tác phẩm (bất kể tiếng Việt hay tiếng nước ngoài) với một vài sai sót chính tả. Nói cho cùng, người Việt cũng nghèo. Biên tập viên cũng không thể giàu. Nếu đòi hỏi một bản in hoàn hảo, hãy nghĩ đến chuyện trả gấp đôi giá tiền cho một quyển sách mình mua. Nhưng đây lại là một dấu ấn hết sức đặc biệt. Tôi tự hỏi, có lẽ hoặc tôi ngu dốt quá không thể hình dung lí do vì sao một địa danh nước ngoài lại được ưu tiên phiên âm, hoặc đơn giản chỉ là vì đây là một bản dịch cẩu thả?

Càng đọc, lòng kiên nhẫn cạn dần. Có thể đây là một truyện hay. Nhưng xin lỗi, có lẽ tôi không phải là tầng lớp đọc giả mà truyện này (hay đúng hơn, bản dịch này) nhắm đến. Tôi đọc truyện dịch không ít. Nhưng đây là lần đầu tiên tôi tự hỏi, có cần phải dịch truyện khi người đọc không hiểu truyện viết gì?

Hi vọng đến một thời điểm nào đó, tôi có đủ khả năng đọc L’immoraliste để có thể có một đánh giá đúng về truyện này.

TB. Bài viết này đánh dấu sự kiện lần đầu tiên tui xưng "tôi" ở địa điểm "thiêng liêng" này.


Cập nhật 3 lần. Lần cuối: Thu Aug 25 14:40:58+0003 2022

Tác giả: pclouds | Liên kết tĩnh | Hâm, Sách

Chủ nhật, 09 Tháng ba năm 2008 21:36:43 ICT

Con gái và nón bảo hiểm

Qui định bắt buộc đội nón bảo hiểm cũng có cái hay. Đi ngoài đường thấy con gái vừa gỡ nón bảo hiểm vừa chạy xe, trông cứ như... vừa chạy xe vừa cởi áo.

Nhìn phê phê...

Lần sau phải đề nghị áp dụng qui định con gái ra ngoài đường phải che mặt, rồi lại canh chị em phá luật. Hé hé...


Cập nhật 2 lần. Lần cuối: Tue Aug 08 11:22:15+0011 2017

Tác giả: pclouds | Liên kết tĩnh

Chủ nhật, 02 Tháng ba năm 2008 00:53:26 ICT

Way back into love

Cái lợi điểm của pop, ai cũng biết là dễ nghe. Mới nghe lần đầu đã thấy "được". Chả bù với mấy cái album nhạc của mình, nghe cả tháng mới thông. Nhưng cái gì cũng có giá của nó, nghe mau thì chóng quên. Còn mấy cái album của mình thời gian nghe tính theo.. năm. Nghe riết ghiền (hic.. vẫn phải ráng cai Persephone, đến là khổ).

Với sự xuất hiện của "Turning season within" bản không bị "bíp", coi bộ tháng kế sẽ đến lượt Draconian làm mưa làm gió rồi. Chuyện Draconian vượt Nightwish chiếm hạng 4 chỉ còn là vấn đề thời gian. Liệu Draconian có thể đoạt hạng 3 của Blackmore's Night? Hừm.. ai dám bảo không...


Cập nhật 2 lần. Lần cuối: Tue Aug 08 11:22:15+0011 2017

Tác giả: pclouds | Liên kết tĩnh | Nhạc