Pages

Wednesday, February 4, 2026

RiscV Assembly Got/PLT index Calculation

I searched many online document about RiscV PLT table and how the relocation works. Eventually I got my own version working. This is my notes in case one day I will forget. 

Based on the PLT/GOT RISC V code, the lazy binding code is like the following two tables. The first one is the PLT[0] code stub where the _dl_runtime_resolver code is being invoked. The t3 stores the _dl_runtime_resolver address and the jr t3 is to call that function. That function will requires two parameters: t0 holds the link map address, and t1 holds the function (e.g. printf) offset in the .got.plt. In the code, t2 is used as temporary register.

The summary is below

·       t3 is the _dl_runtime_resolver function address stored in GOT

·       t0 is the link map address in GOT

·       t1 is the function (e.g. printf) offset in .got.plt

·       t2 is temporary register

The hdr_size is the PLT[0] size which is two 16 bytes (32 bytes) size section. It has 8 instructions. In the following code, PTRSIZE = 4 if 32bits and PTRSIZE = 8 if 64bits

Table 1 PLT[0] Code Stub

1:  auipc  t2, %pcrel_hi(.got.plt)

    sub    t1, t1, t3               # shifted .got.plt offset + hdr size + 12

    l[w|d] t3, %pcrel_lo(1b)(t2)    # _dl_runtime_resolve

    addi   t1, t1, -(hdr size + 12) # shifted .got.plt offset, hdr_size is PLT0 size (32 bytes)

    addi   t0, t2, %pcrel_lo(1b)    # &.got.plt

    srli   t1, t1, log2(16/PTRSIZE) # .got.plt offset

    l[w|d] t0, PTRSIZE(t0)          # link map

    jr     t3

 

And the above code is invoked by the following code. The following code is a function (e.g. printf) stub in plt section.

Table 2 PLT[N] Code Stub

1:  auipc   t3, %pcrel_hi(function@.got.plt)

    l[w|d]  t3, %pcrel_lo(1b)(t3)

    jalr    t1, t3

    nop

 

How to get offset in t1 register

The t1 is used to compute the got.plt offset from the plt code stub.

When the code called from Table 2 PLT[N] Code Stub to Table 1 PLT[0] Code Stub. The first time the Table 2 PLT[N] Code Stub is called, the function@.got.plt is points to PLT[0]. The PLT[0]’s job is to update the function@.got.plt to actually function address.

Table 2 PLT[N] Code Stub set t1 and t3 to the following value

·       t1 = &nop = &PLT[N] + 12,

o   12 is from 3 instruction and each instruction is 4 bytes

·       t3 = &PLT[0]

In the Table 1 PLT[0] Code Stub, the t1 was computed

t1 = [t1 – t3 – (hdr_size + 12)] >>   

     = [&PLT[N] + 12 - &PLT[0] – hdr_size – 12] >>  

= [&PLT[N] - &PLT[0] – hdr_size] >>   (where hdr_size is the PLT[0] size, it is 32bytes, refer to the diagram below)

= [(N-1)*16] >>  

=  =  

= (N-1)PTRSIZE

 

 

N starts from 1 because PLT[0] is reserved for dynamic resolver. So the 1st function is at 0 and 2nd function is at PTRSIZE. The PTRSIZE is the address data size.

·       If 32bit address, the PTRSIZE is 4, which is 4 bytes equals 32 bits.

·       If 64bit address, the PTRSIZE is 8, which is 8 bytes equals 64 bits.

Please note the GOT.PLT has 1st and 2nd element to be reserved value. The 1st element is PLT[0] address and the 2nd element is link map address.

Link map into t0

The link map is a reserve entry in got.plt section. The link_map is a data structure used internally by the Linux dynamic linker (ld.so) to keep track of all shared libraries (shared objects, .so files) loaded into a running process.

Executable (.got.plt)

├── [0] => address of resolver entry (__dl_runtime_resolve)

├── [1] => pointer to link_map (used by resolver)

├── [2] => address of function (e.g., printf)

 

Please note this is not the end. If you want to call printf, you will need to implement the gnu version table and hash algorithm. I tried both old hash algorithm and new hash algorithm, both works. My environemnt is QEMU and StarFive2 Ubuntu Linux. 

Tuesday, January 13, 2026

Rust Macro

Rust macro is an interesting topic and it provides a way to generate code on the fly. One of the problem for Rust programmer is the code is not easy to understand, especially for compiler and low level programming. 

The code is located at ttliu2000/rust_macro: Rust macro project to make rust programming easier. which making my assembler / linker project code simpler. My background is C# and I will keep everything private and use accessor methods to access the code, which is heavy boilerplate code. Using the macro make the code more readable. 

Thanks to ChatGPT's help, I quickly start the project and get the feature in. So if you start to create macro, ask GPT first. 

Thursday, January 1, 2026

Tao's Fragments: RiscV Linux lib ELF Generation

Today is Jan 1st, 2026. And I will be back with what is meaningful. Years ago, I decide to start a new journey. After working on Rust, Linux OS, ELF file format, RiscV chip/ABI, and assembler and linker, and started C language compiler. Let's see how compiler tech and latest AI can change how technology can make programmer's life easier. 

I feel I can start blog again and share some fragments of my work and ready for open source. 

I've done the ELF executable with dynamic link support, it means that I can use my assembler+linker to generate ELF binary to invoke printf in libc. I've to say the RISCV's PLT/GOT RELA is really a headache, which can be topic for another fragemnt of mine. 😉

Today I finished the .so file generation and it can generate a library can be linked with gcc. Now I know why F# choose to support library after executable is done. And I know why F# library has its own printf. But anyway. 

The .so ELF generation is simple, my test .s file is listed below. Please note that i added my own pseudo instruction for RiscV assembly language. 

.data
.extern printf

tt_fmt: .string "%d\n"

const_float_or_string_value_104:
.string "Array index access test, expected value 42 and actual value: %d\n"

.text 
    .globl  add2               
    .type   add2, @function
add2:
    add     a0, a0, a1
    ret
    .size   add2, .-add2

    exit 42

The .extern will trigger dynamic symbol structure generation. The key here is the add2 function related instruction which is in bold font. 

  • globl add2 is to add "add2" to the dynamic symbol (.dynsym) table.
  • add2 shows the starting point of add2 function and it will be used to compute the size of the add2 function. 
  • .size show the add2's size is equal to current (the small dot) minus add2. 
After these info being added to dynamic symbol table, the readelf -a shows the following:



the riscv64 gcc link can link this shared library with c code and command is listed below:

#include <stdio.h>

int add2(int a, int b);

int main() {
    printf("%d\n", add2(20, 22));
    return 0;



and the exeuction result shows 42 which is 20 + 22 from add2(20, 22). 



Thanks to ChatGPT's which helps me to generate Linux command. Being a long time windows guy, I am still struggle to use correct Linux language, but let's see if this can change when I add more feature.

Friday, December 19, 2025

Compiler AST & Mermaid

I'm interested in RISC compiler for a while. One of the challenge of a compiler is to process AST. Being a visual person, I am interested in seeing the structure in an image. So I generate the mermaid state diagram and can visualize the tree. 





Sunday, October 18, 2020

SelfNote: Enable Python in Visual Studio Interactive

This note is to document how to configure Python interactive window in Visual Studio (not VS Code)

Reference a local library

When I added a new .py file in the project, I cannot add it to the python interact window. The reason is the default path for python interact is under "program files". The following command is to change the current working folder to a new folder:

import os
os.chdir(r"c:\mycode\xyz\")

Make sure the string is started with "r". 

Reload a library

After change a library, the new content is not loaded to the python interactive session by default. The following command is to reload the library into the session.

import myModule #import my module
 
#the following is to reload myModule

import importlib

importlib.reload(myModule )

Please note: myModule does not need quote or double quote 

 

Friday, August 7, 2020

Matrix Multiplication & Graph Reachable

I have been struggling to find a good way to represent graph structure. There are number of graph library where Node and Edge are defined. I found that matrix can be used to represent a graph. 

The row and column of the matrix represent the node in a graph. The element in the matrix is connection between two nodes. In a graph, this connection is called Edge. The following diagram represents a graph with 3 nodes and 3 edges. 

 
In a matrix representation, it can be written as a matrix. Row and column are named A, B, and C. If there is an edge between two nodes, the corresponding cell is set to 1. For example, edge AB shows 1 in cell (A, B) in the grid. We can denote this matrix as M


Matrix "multiplication" to itself can reveal if a certain node can reach the other nodes in two steps. The "multiplication" operation is different from the linear algebra matrix multiplication.  

K = M ⊗ M

 Each element b in N can be calculated by the following formula, where N is the number of the nodes (or the column number of the matrix M).

Two nodes i and j. If there j is reachable from i, either of the following two conditions are met:
  • there is a direct edge between i and j 
  • there is a node p which is reachable from i and p can reach j
Written as math language in the matrix M
  • M(i, j) = 1
  • 彐p, M(i, p) = 1 and M(p, j) = 1
Repeat the "multiplication" operation logN times, it can calculate any node in a graph can be reachable from other nodes. 

Finally, I can move away from a complicated node/edge definition into a formal math world. Hopefully the existing linear algebra can help me down the road. 






Wednesday, February 19, 2020

SelfNote: VSCode setup

Recently I started to use the Azure cloud machine to do my coding work. I can still do my coding in my laptop using full Visual Studio but an Azure VM with VSCode will be much convenient. Low tech but works. :)

The following VSCode setup script helps me set up a new machine:

  1. open PowerShell 
  2. copy extension names to the clipboard (yes, Ctrl+C)
  3. run get-clipboard | % { code --install-extension $_ }



DavidAnson.vscode-markdownlint
eamodio.gitlens
ms-azuretools.vscode-docker
ms-python.python
ms-vscode-remote.remote-wsl
ms-vscode.cpptools
ms-vscode.powershell
npclaudiu.vscode-gn
yzhang.markdown-all-in-one