Tutorial 01: StrLib Basics
Overview
This tutorial covers the fundamental operations of FreshLib's string library (StrLib). You'll learn how to create, manipulate, compare, and search strings using FreshLib's handle-based string system.
Topics Covered
String Structure - Understanding how FreshLib stores strings
Handles vs Pointers - Why FreshLib uses handles
String Creation - Creating new strings
String Duplication - Copying strings
String Pointers - Converting handles to pointers
String Deletion - Proper cleanup
String Concatenation - Joining strings together
String Length - Getting string size
String Comparison - Comparing strings (case-sensitive/insensitive)
String Searching - Finding characters and substrings
Prerequisites
Basic x86 assembly knowledge
Understanding of registers (EAX, EBX, ECX, EDX, ESI, EDI)
Stack frame concepts (push, pop, call, ret)
The FreshLib String Structure
FreshLib strings are more than just character arrays. Each string has:
struc string {
.capacity dd ? ; Total allocated memory (offset -8)
.len dd ? ; Current string length in bytes (offset -4)
label .data byte ; The actual string data (offset 0)
}
Key points:
Strings are null-terminated (like C strings)
Strings are dword-aligned for efficient processing
Capacity can be greater than length (room for growth)
Handles vs Pointers
FreshLib uses a handle system for strings:
Handles (Integer Identifiers)
Stored in the string table
Remain constant during string's lifetime
Used with most StrLib functions
Example:
stdcall StrCat, hString, source
Pointers (Memory Addresses)
Direct memory addresses
Can change during string operations
Required for C library functions
Obtained via
stdcall StrPtr, hString
Why Handles?
; WITHOUT handles (dangerous):
mov eax, string_pointer
stdcall StrCat, eax, " more text" ; eax may be invalid now!
; WITH handles (safe):
stdcall StrNew ; Returns handle in EAX
stdcall StrCat, eax, "text" ; Handle remains valid
stdcall StrCat, eax, " more text" ; Still valid!
Functions in This Tutorial
Initialization and Cleanup
| Function | Purpose |
InitStrings | Initialize string library (automatic in FreshLib) |
FreeStrings | Free all strings (automatic in FreshLib) |
String Creation
| Function | Purpose |
StrNew | Create new empty string |
StrDup | Duplicate a string (copy) |
StrDupMem | Duplicate from memory pointer |
String Access
| Function | Purpose |
StrPtr | Get pointer from handle |
StrLen | Get string length |
StrDel | Delete/free a string |
String Manipulation
| Function | Purpose |
StrCat | Append string to destination |
StrCharCat | Append up to 4 characters |
StrCopy | Copy source to destination |
String Comparison
| Function | Purpose | Return |
StrCompCase | Case-sensitive compare | CF=1 if equal |
StrCompNoCase | Case-insensitive compare | CF=1 if equal |
String Search
| Function | Purpose |
StrCharPos | Find character in string |
StrPos | Find substring in string |
Demo Programs
Demo 01: String Creation
File: demo01_string_create.asm
Demonstrates:
Creating new empty strings with
StrNewDuplicating strings with
StrDupDuplicating from memory with
StrDupMemGetting pointers with
StrPtrDeleting strings with
StrDelKey Concepts:
Always pair
StrNew/StrDupwithStrDelUse
StrPtrto get pointers for C functionsCheck return values in EAX
Demo 02: String Concatenation
File: demo02_string_concat.asm
Demonstrates:
Concatenating strings with
StrCatAppending characters with
StrCharCatCopying strings with
StrCopyGetting string length with
StrLenKey Concepts:
StrCatmodifies destination in placeDestination MUST be a handle (not pointer)
Use
txtmacro for string literals
Demo 03: String Comparison
File: demo03_string_compare.asm
Demonstrates:
Case-sensitive comparison with
StrCompCaseCase-insensitive comparison with
StrCompNoCaseUsing Carry Flag (CF) for comparison results
Proper error handling with
jc/jncKey Concepts:
CF=1 means equal/TRUE for comparisons
CF=0 means not equal/FALSE for comparisons
Use
jc(jump if carry) to check for equalUse
jnc(jump if no carry) to check for not equal
Demo 04: String Search
File: demo04_string_search.asm
Demonstrates:
Finding characters with
StrCharPosFinding substrings with
StrPosHandling NULL results (not found)
Key Concepts:
Return NULL (0) if not found
Check EAX before using result
Pointers vs handles in search results
Common Patterns
Creating and Using a String
stdcall StrNew ; Create new string
mov ebx, eax ; Save handle
stdcall StrCat, ebx, txt "Hello" ; Append text
stdcall StrPtr, ebx ; Get pointer for C functions
; ... use string ...
stdcall StrDel, ebx ; Clean up
Error Handling
stdcall SomeFunction, param
jc .error_handler ; Jump if CF=1 (error)
; Continue on success (CF=0)
String Comparison
stdcall StrCompCase, str1, str2
jc .strings_equal ; CF=1 means equal
; Strings are different
Saving NumToStr Results (FileWriteString Clobbers EAX)
IMPORTANT: FileWriteString clobbers the EAX register! When using NumToStr, you must save the result before calling FileWriteString with other strings.
; WRONG - memory leak!
stdcall NumToStr, eax, ntsDec or ntsUnsigned
stdcall FileWriteString, [STDOUT], "Length: "
stdcall FileWriteString, [STDOUT], eax ; eax is clobbered!
; Memory leaked!
; CORRECT - save and restore
stdcall NumToStr, eax, ntsDec or ntsUnsigned
push eax ; Save NumToStr result
stdcall FileWriteString, [STDOUT], "Length: "
pop eax ; Restore NumToStr result
stdcall FileWriteString, [STDOUT], eax
stdcall StrDel, eax ; Clean up NumToStr result
Why this matters:
NumToStrcreates a new string handle that must be freedFileWriteStringoverwrites EAX with its own return valueForgetting to delete the handle causes a memory leak
Alternative (using a variable):
stdcall NumToStr, eax, ntsDec or ntsUnsigned mov [hResult], eax ; Save in variable stdcall FileWriteString, [STDOUT], "Length: " mov eax, [hResult] ; Restore from variable stdcall FileWriteString, [STDOUT], eax stdcall StrDel, [hResult] ; Clean up
Important Discoveries During Implementation
1. FileWriteString Clobbers EAX
Problem: When using NumToStr followed by FileWriteString, the EAX register (containing the string handle) gets overwritten, causing memory leaks and segmentation faults.
Discovery: FileWriteString modifies the EAX register as part of its normal operation. This means any value stored in EAX before calling FileWriteString will be lost.
Solution: Always save NumToStr results before calling FileWriteString with other strings:
Use
push eax/pop eaxfor temporary savesUse
mov [variable], eaxfor longer-term storageAlways call
StrDelon the saved handle to prevent memory leaksImpact: This pattern affects ALL demos that display numbers. Failing to follow this pattern causes:
Memory leaks (string handles never freed)
Segmentation faults (trying to use garbage data as string handles)
2. Memory Management is Manual
Discovery: Every string created by NumToStr, StrNew, StrDup, StrDupMem, or StrExtract must be explicitly freed with StrDel.
Common Mistakes:
Forgetting to delete
NumToStrresults (especially when they're temporary)Creating strings in error paths and not cleaning them up
Overwriting handle variables before deleting the old handle
Best Practice: Track every string creation and ensure there's a matching
StrDelcall in all code paths (including error handlers).
3. Carry Flag Usage is Inverted for Comparisons
Discovery: String comparison functions use CF=1 for "equal" (TRUE), which is opposite to most error-checking functions.
Two Different Conventions:
Error checking: CF=1 means error, CF=0 means success
Example:
stdcall StrNewfollowed byjc .error
Comparisons: CF=1 means equal/TRUE, CF=0 means not equal/FALSE
Example:
stdcall StrCompCase, s1, s2followed byjc .equalImpact: Using the wrong jump instruction leads to inverted logic.
4. Angle Bracket Syntax is for Constants Only
Discovery: The < > syntax for inline strings only works with compile-time constants, not runtime register values.
Valid:
stdcall FileWriteString, [STDOUT], <"Error!", 13, 10>
Invalid:
stdcall FileWriteString, [STDOUT], <"Position: ", eax, 13, 10> ; WRONG!
Reason: The < > syntax is a preprocessor macro that expands at compile time. Register values only exist at runtime.
5. String Constants Require the text Macro
Discovery: String constants must use the text macro in the iglobal section to ensure proper null termination and alignment.
Correct:
iglobal
cHello text "Hello World"
endg
Wrong (missing null terminator):
iglobal
cHello db "Hello World"
endg
6. Build Order Matters for Dependencies
Discovery: When building demos, the runtime loader ld-musl-i386.so must be present in the same directory as the executable.
Solution: The build script copies the runtime from a reference location before building demos.
Building and Running
cd 01-strlib-basics
./build.sh
This will compile and test all 4 demos.
Next Steps
After completing this tutorial, continue to:
Tutorial 02: String Manipulation (insert, extract, case conversion)
Tutorial 03: Advanced Features (numbers, encoding, patterns)
Reference
FreshLib Source:
~/Documents/fossil/FreshIDE/freshlib/data/strlib.asmAsmBB Usage:
~/Documents/fossil/asmbb/source/*.asm