New FAMILUG

The PyMiers

Friday, 2 December 2022

Vài khoảng trắng không xóa đi lịch sử

Người dùng Ubuntu có một bí kíp để khiến cho câu lệnh vừa gõ trong bash không hiện trong lệnh "history": thêm dấu space trước khi gõ lệnh:

$ docker run -it ubuntu:22.04                                                                                 [0]
root@19b65c7de3f6:/# echo you see me
you see me
root@19b65c7de3f6:/#  echo you do not see my password is hunter42
you do not see my password is hunter42
root@19b65c7de3f6:/# history
    1  echo you see me
    2  history
root@19b65c7de3f6:/# echo $HISTCONTROL
ignoredups:ignorespace

Bí kíp này ... chỉ có ở trên Ubuntu bash.

golden bridge

Photo by Ling Tang on Unsplash

HISTCONTROL

Trong bash, biến môi trường HISTCONTROL dùng để config shell sẽ lưu cái gì vào "history". Mặc định trên Ubuntu từ xưa tới nay có giá trị:

ignoredups:ignorespace

Nhưng điều này không đúng trên Debian cũng như nhiều hệ điều hành khác như MacOS, cũng không đúng trên zsh (HIST_IGNORE_SPACE).

$ docker run -it debian:bullseye-slim                                                                         [0]
root@5a7ecd7dd69e:/#  echo you see mypassword
you see mypassword
root@5a7ecd7dd69e:/# history
    1   echo you see mypassword
    2  history

Vậy nên nếu đang dùng server khác Ubuntu mà hồn nhiên gõ mật khẩu vào câu lệnh, bắt đầu bằng dấu space, thì vẫn lưu trong history như thường. Pwned!

HISTCONTROL

A colon-separated list of values controlling how commands are saved on the history list. If the list of values includes ‘ignorespace’, lines which begin with a space character are not saved in the history list. A value of ‘ignoredups’ causes lines which match the previous history entry to not be saved. A value of ‘ignoreboth’ is shorthand for ‘ignorespace’ and ‘ignoredups’. A value of ‘erasedups’ causes all previous lines matching the current line to be removed from the history list before that line is saved. Any value not in the above list is ignored. If HISTCONTROL is unset, or does not include a valid value, all lines read by the shell parser are saved on the history list, subject to the value of HISTIGNORE. The second and subsequent lines of a multi-line compound command are not tested, and are added to the history regardless of the value of HISTCONTROL.

https://www.gnu.org/software/bash/manual/bash.html

Kết luận

Không gõ password trong câu lệnh dưới mọi hình thức!

Tham khảo

Happy ~enter~ leak password in commandline.

Hết.

HVN at http://pymi.vn and https://www.familug.org.

Ủng hộ tác giả 🍺

Tuesday, 29 November 2022

Rust Cargo release build is not the fastest

By default, cargo build will build a debug binary at ./target/debug/filename, which is helpful for development/debug but without many binary optimizations, thus, runs slow.

When build with cargo build --release, it will build an optimized binary, which is ready for production:

--release                    Build artifacts in release mode, with optimizations

But it still not the fastest it can be.

Cargo profiles

Cargo has 4 builtin profiles: dev, release, test, and bench https://doc.rust-lang.org/cargo/reference/profiles.html which pre-defined build options. User can customize them in Cargo.toml, e.g:

[profile.dev]
opt-level = 1
debug = true

https://doc.rust-lang.org/cargo/reference/profiles.html#lto

Default profile.release sets lto = false.

[profile.release]
opt-level = 3
debug = false
split-debuginfo = '...'  # Platform-specific.
debug-assertions = false
overflow-checks = false
lto = false
panic = 'unwind'
incremental = false
codegen-units = 16
rpath = false

Set lto = true or lto = "fat" will

Performs "fat" LTO which attempts to perform optimizations across all crates within the dependency graph.

which makes build much slower, but the binary often faster 10-20% compare to lto = false. User may try lto = "thin" for faster build but the output still good comparable to lto = "fat"

"thin": Performs "thin" LTO. This is similar to "fat", but takes substantially less time to run while still achieving performance gains similar to "fat".

Reference

Happy building!

Monday, 28 November 2022

Học Rust với gdb

Dùng Rust viết ví dụ để học gdb.

gdb là gì

$ whatis gdb
gdb (1)              - The GNU Debugger

GDB là 1 debugger trên dòng lệnh, mặc dù ít biết tới nhưng nó có cả giao diện TUI - giống như đồ họa trên text. gdb là debugger lừng danh luôn dùng để debug code C. Ngoài C, gdb hỗ trợ nhiều ngôn ngữ bao gồm cả Rust/Go...

What Languages does GDB Support?

GDB supports the following languages (in alphabetical order):

    Ada
    Assembly
    C
    C++
    D
    Fortran
    Go
    Objective-C
    OpenCL
    Modula-2
    Pascal
    Rust

gdb hỗ trợ các ngôn ngữ compile thành binary, còn Python có sẵn pdb cũng tương tự.

Cài gdb trên Ubuntu 20.04

$ sudo apt install -y gdb

Cài Rust trên Ubuntu 20.04

$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

theo https://www.rust-lang.org/tools/install

Tạo chương trình Rust đơn giản với stack và heap

Tạo project mới:

$ cargo new gdbplay
     Created binary (application) `gdbplay` package

Sửa nội dung file gdbplay/src/main.rs như sau:

fn main() {
    println!("Hello, world!");
    stack_only(1);
    stack_and_heap(2);
}

fn stack_only(_x: i32) {
    let a = 5;
    println!("{}", a);
}

fn stack_and_heap(_y: i32) {
    let b = 7;
    let p = Box::new(9);
    println!("{} {}", b, p);
}

Debug rust với gdb

Các câu lệnh cơ bản của gdb trong bài:

  • list: in source code ra màn hình
  • b N: đặt breakpoint tại dòng N, debugger sẽ dừng lại khi chạy tới dòng N
  • start: bắt đầu chạy chương trình
  • s: step into - chui vào function
  • c: continue - chạy tới breakpoint tiếp theo.
  • n: next - chạy tới dòng tiếp theo
  • bt: hiển thị backtrace - tất cả các "stackframe"
  • info locals: in ra các biến local
  • info args: in ra các argument của function hiện tại.

help all để xem các câu lệnh khác.

Các câu lệnh gõ vào ở dòng bắt đầu với (gdb)

$ cargo build
   Compiling gdbplay v0.1.0 (/home/hvn/me/familug.github.io/content/gdbplay)
    Finished dev [unoptimized + debuginfo] target(s) in 0.21s
$ gdb target/debug/gdbplay
GNU gdb (Ubuntu 9.2-0ubuntu1~20.04.1) 9.2
...
Reading symbols from target/debug/gdbplay...
warning: Missing auto-load script at offset 0 in section .debug_gdb_scripts
of file /home/hvn/me/familug.github.io/content/gdbplay/target/debug/gdbplay.
...
(gdb) list
1   fn main() {
2       println!("Hello, world!");
3       stack_only(1);
4       stack_and_heap(2);
5   }
6
7   fn stack_only(_x: i32) {
8       let a = 5;
9       println!("{}", a);
10  }
(gdb) list
11
12  fn stack_and_heap(_y: i32) {
13      let b = 7;
14      let p = Box::new(9);
15      println!("{} {}", b, p);
16
17  }
(gdb) b 7
Breakpoint 1 at 0x9508: file src/main.rs, line 8.
(gdb) b 9
Breakpoint 2 at 0x9510: file src/main.rs, line 9.
(gdb) start
Temporary breakpoint 3 at 0x94a4: file src/main.rs, line 2.
Starting program: /home/hvn/me/familug.github.io/content/gdbplay/target/debug/gdbplay
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".

Temporary breakpoint 3, gdbplay::main () at src/main.rs:2
2       println!("Hello, world!");
(gdb) n
Hello, world!
3       stack_only(1);
(gdb) s

Breakpoint 1, gdbplay::stack_only (_x=1) at src/main.rs:8
8       let a = 5;
(gdb) n

Breakpoint 2, gdbplay::stack_only (_x=1) at src/main.rs:9
9       println!("{}", a);
(gdb) bt
#0  gdbplay::stack_only (_x=1) at src/main.rs:9
#1  0x000055555555d4e3 in gdbplay::main () at src/main.rs:3
(gdb) info locals
a = 5
(gdb) info args
_x = 1

các biến a hay _x nằm trên stack. Các biến trên stack sẽ tự động được dọn dẹp sau khi hết scope (ở đây là function).

Giờ đổi sang chế độ "TUI", đồ họa trên dòng lệnh, gõ layout next

gdb_tui

p là một "smart pointer" trỏ tới địa chỉ bộ nhớ 0x55555555a3ad0 trên heap. Nếu p là một pointer thông thường như trên C, nó sẽ không tự biến mất khi function kết thúc, và dẫn tới "memleak", lập trình viên phải "free" nó trên code, thì với smart pointer, giá trị 9 mà p trỏ tới sẽ được dọn dẹp khi hết scope (ở đây là function).

x ten_bien để "examine" hay hiển thị giá trị của biến. x /d ten_bien sẽ hiển thị ở dạng số nguyên. Xem thêm tại "help x".

n - next tiếp chạy tới cuối sẽ thấy:

(gdb) n
[Inferior 1 (process 37385) exited normally]
(gdb) n
The program is not being run.

Kết luận

gdb viết tắt các câu lệnh, nhưng khi đã hiểu thì không còn khó. Happy debugging.

Hết.

HVN at http://pymi.vn and https://www.familug.org.

Ủng hộ tác giả 🍺

Monday, 7 November 2022

Cái giá của 2 giờ đầu - hơn 60k

2h

Khi đi làm, giữa muôn vàn công nghệ phức tạp xung quanh, một người làm đủ "cứng" có thể ứng biến dễ dàng:

  • muốn tạo 1 máy ảo và cấu hình mạng dùng terraform? copy file .tf đã tồn tại và sửa một chút cho phù hợp yêu cầu.
  • muốn deploy 1 service với Salt/Ansible? copy các file yaml/role có sẵn rồi sửa một chút cho phù hợp với yêu cầu.
  • muốn deploy 1 service trên Kubernetes? copy file .yaml có sẵn trong "mono repo" của công ty rồi sửa lại chút chút cho phù hợp yêu cầu.
  • muốn thêm 1 endpoint cho website Django/flask? copy function đã có trong cùng file và sửa lại một chút cho hợp với yêu cầu.
  • muốn thêm 1 alert trên Prometheus/Alertmanager? copy vài dòng trong file yaml cấu hình rồi sửa lại một chút cho phù hợp yêu cầu. ...

Mất bao lâu để "làm chủ", "master" các công nghệ bôi đậm nói trên? Không hề ít thời gian và công sức! Vậy làm sao mọi lập trình viên/devops ngày nay đều yêu cầu phải biết cơ bản những thứ này? làm sao họ làm được? đi đường tắt: copy/paste.

Copy/paste trap

Khác với các sinh viên, có cả năm để học 1 môn, hay các "junior" có cả tháng tìm hiểu và "học việc", thì những người làm lâu năm được kỳ vọng nắm được những thứ trên trong vài ngày/tuần, và khi copy/paste + 1 chút search có thể hoàn thành công việc rồi lại chuyển qua thứ khác, khiến họ dễ mắc phải bẫy "YAML/TF engineer" - hiểu nôm na và biết copy/paste.

Local knowledge

Nếu làm công ty nhỏ, 1 người có thời gian xây dựng cả hệ thống từ đầu, làm những công việc như:

  • tạo 1 project từ đầu "manage.py startproject"
  • setup CI/CD, config GitLab/Jenkins, etc...
  • cài đặt terraform + terraform enterprise/S3 state management
  • tạo cloud Virtual Network/VPC, chia subnet, config route...
  • setup Kubernetes cluster, config ServiceAccount/Role, ...
  • ...

Thì ở các công ty lớn, mỗi người chỉ nắm 1 phần nhỏ, với các hệ thống đã dựng sẵn, và nếu không có kinh nghiệm từ trước, thì họ chỉ có kinh nghiệm "từ ấy".

Rocket 2 giờ

Giải pháp để tránh bẫy hiểu biết cục bộ hay "get shit done" này là dành 2 giờ đầu (hay 4, hay nhiều hơn, trong phạm vi có thể) để hiểu từng công nghệ, tự setup theo tutorial của công nghệ ấy. Việc đầu tư chút thời gian này sẽ có giá trị rất cao về sau... đặc biệt là trong các cuộc phỏng vấn.

Chuyện này chẳng có gì mới, bài học kinh điển của những người "tiết kiệm thời gian", không đọc kỹ document, để rồi mất hàng tháng/tuần/ngày để debug về sau.

Kết luận

Giành vài giờ tự setup từ đầu theo tutorial trước khi dùng 1 công nghệ sẽ mang lại lợi ích lớn về sau.

Hết.

HVN at http://pymi.vn and https://www.familug.org.

Ủng hộ tác giả 🍺

Sunday, 30 October 2022

GitLab CE tự host không như GitLab.com

Một đoạn code đơn giản, đã chạy ít nhất 5 năm trên gitlab.com, khi mang về gitlab CE tự host, dùng cùng gitlab CI runner, chạy CICD job lại fail.

$ git diff --name-only origin/master...HEAD
...
fatal: ambiguous argument 'origin/master...HEAD': unknown revision or path not in the working tree.
Use '--' to separate paths from revisions, like this:
'git <command> [<revision>...] -- [<file>...]'
...

Không có origin/master, kể cả master cũng không có.

img

Photo by Abdulaziz Alfawzan on Unsplash

Hóa ra bên trên output của GitLab CI job có 1 dòng

Fetching changes with git depth set to 20...

Config trên GitLab > CICD Settings của project, mặc định sau khi cài GitLab CE 15.4.2, giá trị Git shallow clone là 20.

Trên GitLab.com, giá trị này mặc định là không set (blank).

gitlab

The number of changes to fetch from GitLab when cloning a repository. Lower values can speed up pipeline execution. Set to 0 or blank to fetch all branches and tags for each job

Kết luận

Code chạy 5 năm, trên cùng 1 chỗ, vẫn fail, vì config từ web thay đổi.

Hết.

Friday, 21 October 2022

Tạm biệt VirtualBox, chào Debian trên libvirt thêm lần nữa

Có 3 giải pháp tạo máy ảo phổ biến: vmware, virtualbox và kvm (linux only).

Bắt đầu với Virtualbox từ khi dùng Windows, đi làm dùng Ubuntu chuyển qua kvm vì các dòng lệnh & công việc (openstack) dùng hàng ngày, rồi lại quay lại VirtualBox khi đó là lựa chọn duy nhất trên MacOs. Ngày nay, lại quay lại với kvm: vì open-source.

Mặc dùng VirtualBox là open-source, một số tính năng trong extension-pack lại chỉ của riêng Oracle và chỉ được dùng cho mục đích cá nhân, tức không thể dùng trên máy công ty/trong công việc. Nếu công ty đủ lớn, lỡ dùng trong công việc có thể bị kiện, đặc biệt là khi làm công ty nước ngoài.

Trên MacOS ngày nay có thể dùng UTM.

Cài đặt libvirt

virt-manager là chương trình giao diện đồ họa.

sudo apt install libvirt-daemon libvirt-clients virt-manager

Thêm user hiện tại vào group libvirt:

sudo usermod -a -G libvirt $(whoami)

Tải debian vagrant box

Vào https://app.vagrantup.com/debian/boxes/bullseye64

tải file libvirt, ví dụ:

https://app.vagrantup.com/debian/boxes/bullseye64/versions/11.20220912.1/providers/libvirt.box

Giải nén:

tar xf libvirt.box

sẽ thấy có file libvirt.img.

Bật virt-manager > File menu > New Virtual Machine > Import from disk > chọn file libvirt.img > next next next.

virt-manager

Bật máy lên, đăng nhập bằng user root password vagrant.

root@bullseye:~# cat /etc/os-release
PRETTY_NAME="Debian GNU/Linux 11 (bullseye)"
NAME="Debian GNU/Linux"
VERSION_ID="11"
VERSION="11 (bullseye)"
VERSION_CODENAME=bullseye
ID=debian
HOME_URL="https://www.debian.org/"
SUPPORT_URL="https://www.debian.org/support"
BUG_REPORT_URL="https://bugs.debian.org/"

Happy hacking!

Monday, 17 October 2022

[AWS][SQS] Queue service dễ dùng, mà toàn trap

Trong ba vạn sáu ngàn chín trăm service của AWS cung cấp, mỗi cái một kiểu, có this có that. EC2, ALB, RDS, S3, SQS, Lambda là những cái tên "cơ bản" nhất, cũng là phổ biến nhất.

Đặc điểm chung là dễ dùng, đắt, và cũng dễ dính trap!!!

Let's go.

SQS là viết tắt của chữ Simple Queue Service, một dịch vụ cloud khá tương đương với các phần mềm message queue dưới đất như:

  • RabbitMQ
  • ActiveMQ
  • IronMQ

itatrap

Photo by Nick Fewings on Unsplash

Nếu bạn chưa nghe tên cả 3, thì nó giống như 1 cái ống nước, một đầu gửi thư, đầu kia nhận thư, nếu không ai nhận thì nó nằm trong cái ống.

Vì chỉ đơn giản có vậy, nên sau khi tạo 1 cái queue, thì dùng nó chỉ có 3 thao tác:

  • gửi message
  • nhận message
  • xóa message

message có thể là 1 đoạn text format JSON.

PS: bài viết không yêu cầu code Python, chỉ dùng minh họa.

Sử dụng Python lib boto3, mọi chuyện đơn giản đúng như nói. Ví dụ lấy từ tutorial của boto3 https://boto3.amazonaws.com/v1/documentation/api/latest/guide/sqs.html

Tạo SQS queue

sqs = boto3.resource('sqs')

# Create the queue. This returns an SQS.Queue instance
queue = sqs.create_queue(QueueName='test', Attributes={'DelaySeconds': '5'})

# You can now access identifiers and attributes
print(queue.url)
print(queue.attributes.get('DelaySeconds'))

Gửi SQS message

sqs = boto3.resource('sqs')

# Get the queue
queue = sqs.get_queue_by_name(QueueName='test')

# Create a new message
response = queue.send_messages(Entries=[
    {
        'Id': '1',
        'MessageBody': 'world'
    },
    {
        'Id': '2',
        'MessageBody': 'boto3',
        'MessageAttributes': {
            'Author': {
                'StringValue': 'Daniel',
                'DataType': 'String'
            }
        }
    }
])

Nhận message rồi xử lý và xóa

Một mô hình đơn giản đó là có 1 đầu gửi lệnh tới SQS queue, đầu kia sẽ nhận lệnh và xử lý.

Nhận:

# Get the service resource
sqs = boto3.resource('sqs')

# Get the queue
queue = sqs.get_queue_by_name(QueueName='test')

while True:
# Process messages by printing out body and optional author name
    for message in queue.receive_messages(MessageAttributeNames=['Author']):
        # Get the custom author message attribute if it was set
        author_text = ''
        if message.message_attributes is not None:
            author_name = message.message_attributes.get('Author').get('StringValue')
            if author_name:
                author_text = ' ({0})'.format(author_name)

        # Print out the body and author (if set)
        print('Hello, {0}!{1}'.format(message.body, author_text))

        # Let the queue know that the message is processed
        message.delete()

Hết tutorial, quá dễ, quá đơn giản!!!

Traps

Hãy thử nghĩ các vẫn đề có thể xảy ra liên quan đến SQS ở đoạn code trên trước khi đọc tiếp, đây chỉ nói về SQS, không nói về code Python.

Trap 1: delete() chưa chắc đã delete message

Ở đây không nói về lỗi network. Đó có lẽ là điều cuối cùng bạn nghĩ tới, khi function delete đôi khi sẽ không delete

Điều này có ghi rõ 2 trường hợp có thể xảy ra chuyện này, và tất nhiên chỉ ai nhìn delete() mà không tin nó sẽ delete mới đọc doc:

Trường hợp 1 là khi dùng SQS standard queue.

For standard queues, it is possible to receive a message even after you delete it. This might happen on rare occasions if one of the servers which stores a copy of the message is unavailable when you send the request to delete the message. The copy remains on the server and might be returned to you during a subsequent receive request. You should ensure that your application is idempotent, so that receiving a message more than once does not cause issues.

Yep, SQS có 2 loại queue, 1 là standard queue (cũ) 2 là FIFO queue mới ra đời vào 2016, sau 12 năm tồn tại của SQS với vô số "bí mật". Standard queue có thể xảy ra trường hợp delete xong mà vẫn còn message.

Nếu ai từng học về cấu trúc dữ liệu trong lập trình, kiểu queue có nghĩa là phải FIFO (first-in first-out), thì queue của AWS có 2 loại là 1 không FIFO và 1 FIFO.

Trường hợp thứ 2, tinh vi hơn:

The ReceiptHandle is associated with a specific instance of receiving a message. If you receive a message more than once, the ReceiptHandle is different each time you receive a message. When you use the DeleteMessage action, you must provide the most recently received ReceiptHandle for the message (otherwise, the request succeeds, but the message might not be deleted).

Tức nếu có

  • C1 nhận messageA xử lý,
  • rồi C2 nhận messageA xử lý
  • C1 delete message -> return success, nhưng không thực sự xóa
  • C2 delete message -> nếu không có thằng nào khác (C3, C4...) thì mới xóa.

C1 và C2 có thể nhận cùng 1 message, nếu 1 hệ thống có nhiều worker cùng hoạt động, cùng gọi SQS, thì ban đầu chỉ C1 nhận được messageA, nhưng sau visibility timeout mặc định 30s, C2 cũng sẽ nhìn thấy messageA. Chỉ với 1 message có visibility timeout 30s, và 2 worker xử lý mỗi message sau 50s, bạn đã tạo được 1 vòng lặp gần vô hạn!

img

Trap2

receive_messages kể cả không nhận được message nào, cũng mất phí. Nhìn chung thì cứ gọi API của AWS là tính phí rồi, nên cái này không đến nỗi quá bất ngờ. Nhưng khi chứng kiến các backend engineer viết app "scale"/concurrency/parallel, gọi hàng triệu call mỗi 10 giây, số tiền bạn phải trả AWS 1 tháng đủ để trả lương kỹ sư ấy cả năm.

$0.4/1_000_000 call => 86400/10 * 0.4 == $3_456/ngày.

Trap N

Ôi dào, đấy là do không chịu đọc doc của từng function thôi.

Okie bạn ơi, còn 31 cái warning 56 cái note trong tài liệu này, chúc vui! https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/sqs.html

Còn trang tutorial thân thiện, thì không có gì. https://boto3.amazonaws.com/v1/documentation/api/latest/guide/sqs.html

Kết luận

AWS dễ, mà trap everywhere, và mỗi bài học phải được trả bằng tiền, rất nhiều tiền!

Có trong tay full các chứng chỉ cloud của 1 nhà cung cấp cloud hàng đầu (không phải AWS), tôi không tin có chứng chỉ nào dạy cho bạn những điều này.

Đó là việc của "best practice", của các chuyên gia tư vấn sẽ tới thăm bạn và xin cục tiền.

PS: Nếu lỗi nào có được đào tạo khi luyện thi các chứng chỉ của AWS, các chuyên gia AWS full chứng chỉ vui lòng PM mình để update vào đây, tạo động lực thi chứng chỉ cho quần chúng.

Happy crying!

Hết.

HVN at http://pymi.vn and https://www.familug.org.

Ủng hộ tác giả 🍺

Sunday, 16 October 2022

Hello world dùng x64 assembly

Assembly (asm) ở Việt Nam được gọi là "hợp ngữ cũng có thể được gọi là mã máy tượng trưng", là một ngôn ngữ lập trình cấp thấp, thường tương ứng 1 lệnh với 1 lệnh của CPU. Lập trình assembly ngày nay không còn phổ biến như trước khi có C.

C/Zig/Rust... được dùng để viết code "low level", nhưng khi cần tốc độ tối đa, lập trình nhúng, viết driver, hay thực hiện reverse engineering, binary exploitation, asm là ngôn ngữ được ưa chuộng.

cpu

Photo by Olivier Collet on Unsplash

  • Code Rust ---compile -> binary
  • Code C ---compile -> binary
  • Code ASM ---compile -> binary

Loạt bài viết này giới thiệu vài chương trình assembly đơn giản, cách compile, chạy, các tool dùng để tìm hiểu file binary.

Assembly đơn giản (giới hạn không nhiều lệnh, luật lệ rõ ràng), nhưng không dễ (mất rất nhiều bước để làm một việc, rất thủ công).

Hello world

Là một chương trình in ra màn hình dòng chữ "hello world".

Code Python:

print("Hello,world")

Code C:

#include <stdio.h>
int main(int argc, char *argv[])
{
    puts("Hello, world\n");
    return 0;
}

Code asm trong file hello.s - copy từ wikipedia https://en.wikipedia.org/wiki/GNU_Assembler#Example_program

.global _start

.text
_start:
    mov  $4, %eax   # 4 (code for "write" syscall) -> EAX register
    mov  $1, %ebx   # 1 (file descriptor for stdout) -> EBX (1st argument to syscall)
    mov  $message, %ecx # address of message string -> ECX (2nd argument)
    mov  $len, %edx # len (32 bit address) -> EDX (3rd arg)
    int   $0x80      # interrupt with location 0x80 (128), which invokes the kernel's system call procedure

    mov  $1, %eax   # 1 ("exit") -> EAX
    mov  $0, %ebx   # 0 (with success) -> EBX
    int  $0x80      # see previous
.data
message:
    .ascii  "Hello, PyMivn!\n" # inline ascii string
    len = 15

Compile dùng gcc

$ gcc -c hello.s
$ ls -l
-rw-rw-r--  1 hvn hvn  904 Oct 16 14:11 hello.o
-rw-rw-r--  1 hvn hvn  598 Oct 16 14:11 hello.s
$ file hello.o
hello.o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), not stripped

gcc -c compile source code hello.s sẽ sinh ra object file hello.o. GCC sẽ gọi GNU Assembler (gas - lệnh là as) để thực hiện compile. Ngoài as, trên Linux còn phổ biến nasm. Trên Windows phổ biến MASM

ld - linker

man ld

ld combines a number of object and archive files, relocates their data and ties up symbol references. Usually the last step in compiling a program is to run ld.

ld thực hiện link (các) file object, ở đây chỉ có 1 file hello.o, thành file binary cuối cùng chạy đuợc, có tên mặc định là a.out.

$ ld hello.o
$ ls -l
-rwxrwxr-x  1 hvn hvn 8920 Oct 16 14:13 a.out
$ file a.out
a.out: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, not stripped
$ ./a.out
Hello, PyMivn!

ELF file

file binary chạy được tên a.out trên Linux có format ELF.

$ whatis elf
elf (5)              - format of Executable and Linking Format (ELF) files

Trên MacOS sử dụng format Mach-O, trên Windows sử dụng format Portable Executable (PE).

https://en.wikipedia.org/wiki/Comparison_of_executable_file_formats.

Chi tiết code asm

AT&T và Intel syntax

asm có hai nhánh syntax chính, là AT&T và Intel. Intel phổ biến trên Windows, AT&T phổ biến trên Unix/Linux. Xem chi tiết so sánh tại https://en.wikipedia.org/wiki/AT%26T_syntax#Syntax

hello.s

Code asm trong bài này chứa 3 section. .global, .data.text, sử dụng AT&T syntax.

.global makes the symbol visible to ld

.global sẽ khai báo function nào được chạy, ở đây là _start (thực chất là export symbol _start cho linker nhìn thấy)

.global _start

.data còn gọi là data segment chứa các global variable & static variable.

.data
message:
    .ascii  "Hello, PyMivn!\n" # inline ascii string
    len = 15

.text chứa code của chương trình. Function _start

.text
_start:
    mov  $4, %eax   # 4 (code for "write" syscall) -> EAX register
    mov  $1, %ebx   # 1 (file descriptor for stdout) -> EBX (1st argument to syscall)
    mov  $message, %ecx # address of message string -> ECX (2nd argument)
    mov  $len, %edx # len (32 bit address) -> EDX (3rd arg)
    int   $0x80      # interrupt with location 0x80 (128), which invokes the kernel's system call procedure

    mov  $1, %eax   # 1 ("exit") -> EAX
    mov  $0, %ebx   # 0 (with success) -> EBX
    int  $0x80      # see previous

%eax %ebx %ecx %edx là 4 register (thanh ghi) trong CPU, để chứa các giá trị.

mov $4, %eax

tương đương với viết code C eax = 4. int $0x80 thực hiện interrupt tại địa chỉ 0x80, tức thực hiện gọi syscall. Dòng int $0x80 có thể viết thành syscall.

Về cơ bản đoạn code trên thực hiện gọi 2 syscall số 4-write và 1-exit

Code giả:

syscall(4, 1, message, len)
syscall(1, 0)

hay

write(stdout, message, len)
exit(0)

Chạy với lệnh strace để xem các syscall đã được dùng:

$ strace ./a.out > /dev/null
execve("./a.out", ["./a.out"], 0x7ffd32dba4c0 /* 58 vars */) = 0
strace: [ Process PID=327701 runs in 32 bit mode. ]
write(1, "Hello, PyMivn!\n", 15)        = 15
exit(0)                                 = ?
+++ exited with 0 +++

Phiên bản dùng Intel syntax:

.intel_syntax noprefix
.global _start

.text
_start:
    mov eax, 0x4
    mov ebx, 0x1
    lea ecx, [message]
    mov edx, 0xf
    int 0x80
    mov eax, 0x1
    mov ebx, 0x0
    int 0x80

.data
message:
    .ascii  "Hello, PyMivn!\n"

Hex view xxd

xxd in ra mã hex của từng byte.

$ whatis xxd
xxd (1)              - make a hexdump or do the reverse.
$ xxd a.out | grep -v '0000 0000 0000 0000 0000 0000 0000 0000'
00000000: 7f45 4c46 0201 0100 0000 0000 0000 0000  .ELF............
00000010: 0200 3e00 0100 0000 0010 4000 0000 0000  ..>.......@.....
00000020: 4000 0000 0000 0000 5821 0000 0000 0000  @.......X!......
00000030: 0000 0000 4000 3800 0300 4000 0600 0500  ....@.8...@.....
00000040: 0100 0000 0400 0000 0000 0000 0000 0000  ................
00000050: 0000 4000 0000 0000 0000 4000 0000 0000  ..@.......@.....
00000060: e800 0000 0000 0000 e800 0000 0000 0000  ................
00000070: 0010 0000 0000 0000 0100 0000 0500 0000  ................
00000080: 0010 0000 0000 0000 0010 4000 0000 0000  ..........@.....
00000090: 0010 4000 0000 0000 2200 0000 0000 0000  ..@.....".......
000000a0: 2200 0000 0000 0000 0010 0000 0000 0000  "...............
000000b0: 0100 0000 0600 0000 0020 0000 0000 0000  ......... ......
000000c0: 0020 4000 0000 0000 0020 4000 0000 0000  . @...... @.....
000000d0: 0f00 0000 0000 0000 0f00 0000 0000 0000  ................
000000e0: 0010 0000 0000 0000 0000 0000 0000 0000  ................
00001000: b804 0000 00bb 0100 0000 b900 2040 00ba  ............ @..
00001010: 0f00 0000 cd80 b801 0000 00bb 0000 0000  ................
00001020: cd80 0000 0000 0000 0000 0000 0000 0000  ................
00002000: 4865 6c6c 6f2c 2050 794d 6976 6e21 0a00  Hello, PyMivn!..
00002020: 0000 0000 0000 0000 0000 0000 0300 0100  ................
00002030: 0010 4000 0000 0000 0000 0000 0000 0000  ..@.............
00002040: 0000 0000 0300 0200 0020 4000 0000 0000  ......... @.....
00002050: 0000 0000 0000 0000 0100 0000 0400 f1ff  ................
00002070: 0900 0000 0000 0200 0020 4000 0000 0000  ......... @.....
00002080: 0000 0000 0000 0000 1100 0000 0000 f1ff  ................
00002090: 0f00 0000 0000 0000 0000 0000 0000 0000  ................
000020a0: 1a00 0000 1000 0100 0010 4000 0000 0000  ..........@.....
000020b0: 0000 0000 0000 0000 1500 0000 1000 0200  ................
000020c0: 0f20 4000 0000 0000 0000 0000 0000 0000  . @.............
000020d0: 2100 0000 1000 0200 0f20 4000 0000 0000  !........ @.....
000020e0: 0000 0000 0000 0000 2800 0000 1000 0200  ........(.......
000020f0: 1020 4000 0000 0000 0000 0000 0000 0000  . @.............
00002100: 0068 656c 6c6f 2e6f 006d 6573 7361 6765  .hello.o.message
00002110: 006c 656e 005f 5f62 7373 5f73 7461 7274  .len.__bss_start
00002120: 005f 6564 6174 6100 5f65 6e64 0000 2e73  ._edata._end...s
00002130: 796d 7461 6200 2e73 7472 7461 6200 2e73  ymtab..strtab..s
00002140: 6873 7472 7461 6200 2e74 6578 7400 2e64  hstrtab..text..d
00002150: 6174 6100 0000 0000 0000 0000 0000 0000  ata.............
00002190: 0000 0000 0000 0000 1b00 0000 0100 0000  ................
000021a0: 0600 0000 0000 0000 0010 4000 0000 0000  ..........@.....
000021b0: 0010 0000 0000 0000 2200 0000 0000 0000  ........".......
000021c0: 0000 0000 0000 0000 0100 0000 0000 0000  ................
000021d0: 0000 0000 0000 0000 2100 0000 0100 0000  ........!.......
000021e0: 0300 0000 0000 0000 0020 4000 0000 0000  ......... @.....
000021f0: 0020 0000 0000 0000 0f00 0000 0000 0000  . ..............
00002200: 0000 0000 0000 0000 0100 0000 0000 0000  ................
00002210: 0000 0000 0000 0000 0100 0000 0200 0000  ................
00002230: 1020 0000 0000 0000 f000 0000 0000 0000  . ..............
00002240: 0400 0000 0600 0000 0800 0000 0000 0000  ................
00002250: 1800 0000 0000 0000 0900 0000 0300 0000  ................
00002270: 0021 0000 0000 0000 2d00 0000 0000 0000  .!......-.......
00002280: 0000 0000 0000 0000 0100 0000 0000 0000  ................
00002290: 0000 0000 0000 0000 1100 0000 0300 0000  ................
000022b0: 2d21 0000 0000 0000 2700 0000 0000 0000  -!......'.......
000022c0: 0000 0000 0000 0000 0100 0000 0000 0000  ................
000022d0: 0000 0000 0000 0000                      ........

Bài viết thực hiện trên Ubuntu 20.04.

Tham khảo

Free books:

Kết luận

Viết helloworld.asm không quá khó.

Happy hacking!

Thursday, 6 October 2022

Đọc một quyển sách 400 nghìn tốn bao nhiêu?

  • Sách là một người bạn
  • Sách là trí tuệ
  • Sách là một hình thức giải trí như film
  • ... blah blah blah ...

Đối với mỗi người sách là một thứ khác nhau. Nếu mua một cuốn sách giá 400.000 VND, thì đọc nó tốn bao nhiêu?

Câu trả lời không thực sự dễ dàng.

img

Photo by 🇸🇮 Janko Ferlič on Unsplash

Lấy vị dụ cuốn 1Q84 của Haruki Murakami bản tiếng Anh dày 1328, với tốc độ đọc 3 phút 1 trang, thì mất

>>> 1328*3
3984  phút
>>> 1328*3 / 60
66.4 giờ

66 tiếng (nếu mỗi ngày dành 1 tiếng đọc thì sau 2 tháng sẽ đọc xong, 1 năm đọc được 6 quyển).

Một tháng, 1 người lao động văn phòng đi làm ~168 tiếng.

Với mức lương trung bình 2021 của 1 lập trình viên Python có ~ 5 năm kinh nghiệm ở VN là ~1000 USD, 23 triệu/tháng. Thì thời gian để đọc hết cuốn sách này trị giá:

>>> usd_rate = 24000
>>> 66/168 * 1000 * usd_rate
9428571.428571427

Tức khoảng 9.5 triệu VND tại thời điểm hiện tại.

Như vậy, trừ khi thời gian là miễn phí, việc mua 1 cuốn sách chỉ tốn một phần nhỏ số tiền bỏ ra, nhưng thời gian để đọc nó có thể tốn gấp hơn 20 lần.

Vậy nên hãy chắc chắn đọc được những thứ đáng giá. Đừng nghĩ đọc sách là rẻ (hơn ra rạp ăn bỏng ngô và xem film và...).

Vài cuốn sách đắt giá theo độ dày

Kết luận

Khi mua một cuốn sách, bạn sẽ phải trả thêm một chi phí không nhỏ để đọc nó.

Tham khảo

https://www.raptitude.com/2022/01/everything-must-be-paid-for-twice/

Hết.

Tuesday, 4 October 2022

Tạm biệt Ubuntu, chào Debian

Ubuntu là distro đầu tiên đưa mình vào thế giới Linux, thế giới không Windows, với phiên bản 8.04. Nó cũng đi cùng luôn sự nghiệp khi đa phần server đều dùng Ubuntu.

https://pymi.vn ra đời năm 2015, chạy trên Ubuntu 14.04, sau 3 lần upgrade

  • sang 16.04, thay toàn bộ Upstart init file bằng systemd
  • sang 18.04, thay Python2 bằng Python3

thì lần này, mình quyết định chuyển sang dùng Debian.

debian

Debian không mới, nó là distro mà chính Ubuntu dựa trên, chỉ có một cộng đồng rất lớn, không có công ty đứng sau.

Lý do vì ở phía server, ngày nay không có gì chỉ chạy trên Ubuntu mà ko chạy trên Debian cả.

Debian cũng không có câu chuyện đáng sợ về motd luôn gọi về server của Ubuntu để chạy quảng cáo. Mà nếu để ý, các docker file chính thức đều dùng Debian, điển hình như Python bullseye

Việc sử dụng server debian thì gần như không khác gì Ubuntu, cấu trúc thư mục vẫn vậy, apt vẫn vậy.

Không phải toàn màu hồng

  • năm 2021, đã từng thử cài debian trên desktop, mà lần bật lên đầu tiên bị lỗi window server (lightdm), nghỉ. Nếu còn trẻ, rảnh, thì có lẽ sẽ ngồi tìm hiểu tại sao, rồi fix, nhưng khi chỉ muốn có cái máy chạy được thì lại quay về Ubuntu 20.04.
  • vì một lý do nào đó, tải file vagrant box về dùng không đăng nhập được bằng tài khoản mặc định (vagrant:vagrant)? -> UPDATE: solved -> user root pass vagrant.

Tài liệu

Debian Admin handbook https://www.debian.org/doc/manuals/debian-handbook/

Kết luận

Dùng Debian cho server là một điều hoàn toàn hợp lý.

Happy hacking!

Friday, 27 May 2022

Cài đặt và chạy máy ảo ArchLinux trên VirtualBox 6 trong 10 phút

Nếu vì một lý do gì bạn không dùng vagrant (không muốn cài Ruby chẳng hạn), việc tạo máy ảo Arch trên VirtualBox không có gì phức tạp với Vagrant box.

Thay vì phải lo cài ArchLinux, tải sẵn ngay box build sẵn bởi Vagrant.

Tải ArchLinux vagrant box

Truy cập https://app.vagrantup.com/archlinux/boxes/archlinux tải file dành cho virtualbox

virtualbox Hosted by Vagrant Cloud (465 MB) nhẹ hơn nhiều so với đĩa cài ISO Size: 826.3 MB

Giải nén với tar xvf virtualbox.box sẽ thấy ra file box.ovf, packer-virtualbox.vmdk và một vài file ko quan trọng khác.

Import file OVF

File > Import Appliance > chọn file box.ovf > next next...

Sau đó chọn Settings > Network > Adapter 1 > NAT > Port forwarding, thêm một dòng mới, chọn host port là 1 số từ 2222 đến 22XX, Guest Port là 22.

Port forwarding

File box.ovf này là 1 file XML, có định nghĩa cấu hình của máy sẽ được tạo, trong đó nó sử dụng file packer-virtualbox.vmdk làm ổ cứng dynamic size với kích thước tối đa là 20GB.

Bật máy ảo lên, đăng nhập với vagrant/vagrant, gõ sudo systemctl start sshd để bật SSH server. Tắt máy đi.

SSH vào ArchLinux

Chuột phải chọn máy ảo > Start > Headless start. Chờ vài giây rồi ssh vào máy:

ssh vagrant@127.0.0.1 -p 2222

Nhập password mặc định: vagrant

$ ssh vagrant@localhost -p2222
vagrant@localhost's password:
Last login: Fri May 27 13:50:13 2022 from 10.0.2.2
[vagrant@archlinux ~]$
[vagrant@archlinux ~]$ uname -a
Linux archlinux 5.17.7-arch1-1 #1 SMP PREEMPT Thu, 12 May 2022 18:55:54 +0000 x86_64 GNU/Linux

Cài đặt phần mềm bằng pacman

pacman -Syu # upgrade
pacman -S vim git tmux

Xong.

Kết luận

Thay vì phải lo tải đĩa về cài từ đầu, dùng sẵn box của vagrant rất tiện lợi, tốn ít dung lượng. ArchLinux đi kèm với các phần mềm phiên bản mới nhất, rất thích hợp dùng làm máy ảo để khám phá.

Happy Arching.