├── .clang-tidy ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .gitmessage ├── .readthedocs.yml ├── LICENSE ├── Makefile ├── README.md ├── docs ├── Makefile ├── _static │ ├── .gitkeep │ ├── internal-flow.svg │ └── usage-workflow.svg ├── _templates │ └── .gitkeep ├── api │ ├── api.rst │ ├── functions.rst │ └── types.rst ├── conf.py ├── getting-started │ └── getting-started.rst ├── how-it-works │ ├── how-to-use.rst │ ├── index.rst │ └── internals.rst ├── index.rst ├── requirements.txt └── wrappers │ └── wrappers.rst ├── example └── demo.c ├── logo.png ├── src ├── asm │ └── libstapsdt-x86_64.s ├── dynamic-symbols.c ├── dynamic-symbols.h ├── errors.c ├── errors.h ├── hash-table.c ├── hash-table.h ├── libstapsdt.c ├── libstapsdt.h ├── sdtnote.c ├── sdtnote.h ├── section.c ├── section.h ├── shared-lib.c ├── shared-lib.h ├── string-table.c ├── string-table.h ├── util.c └── util.h ├── tests ├── Makefile ├── test-errors.c └── test-memory-leaks.c └── tools └── setup-deb-pkg.py /.clang-tidy: -------------------------------------------------------------------------------- 1 | Checks: 'clang-diagnostic-*,clang-analyzer-*,-clang-analyzer-alpha*,google-*' 2 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: libstapsdt CI 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | test: 11 | runs-on: ${{ matrix.os }} 12 | strategy: 13 | matrix: 14 | os: [ubuntu-18.04, ubuntu-20.04] 15 | cc: [gcc, clang] 16 | 17 | steps: 18 | - uses: actions/checkout@v2 19 | - name: Install dependencies 20 | run: | 21 | sudo apt-get -qq update 22 | sudo apt-get install -y libelf1 libelf-dev nasm valgrind 23 | - run: make test CC=${{ matrix.cc }} 24 | 25 | lint: 26 | runs-on: ubuntu-latest 27 | 28 | steps: 29 | - uses: actions/checkout@v2 30 | - name: Install dependencies 31 | run: | 32 | sudo apt-get -qq update 33 | sudo apt-get install -y clang-tidy 34 | sudo apt-get install -y libelf1 libelf-dev clang-tidy 35 | - run: make lint 36 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Object files 5 | *.o 6 | *.ko 7 | *.obj 8 | *.elf 9 | 10 | # Linker output 11 | *.ilk 12 | *.map 13 | *.exp 14 | 15 | # Precompiled Headers 16 | *.gch 17 | *.pch 18 | 19 | # Libraries 20 | *.lib 21 | *.a 22 | *.la 23 | *.lo 24 | 25 | # Shared objects (inc. Windows DLLs) 26 | *.dll 27 | *.so 28 | *.so.* 29 | *.dylib 30 | 31 | # Executables 32 | *.exe 33 | *.out 34 | *.app 35 | *.i*86 36 | *.x86_64 37 | *.hex 38 | 39 | # Debug files 40 | *.dSYM/ 41 | *.su 42 | *.idb 43 | *.pdb 44 | 45 | # Kernel Module Compile Results 46 | *.mod* 47 | *.cmd 48 | .tmp_versions/ 49 | modules.order 50 | Module.symvers 51 | Mkfile.old 52 | dkms.conf 53 | 54 | out/ 55 | build/ 56 | dist/ 57 | core 58 | 59 | demo 60 | test-memory-leaks 61 | 62 | docs/_build/ 63 | .env/ 64 | .venv/ 65 | -------------------------------------------------------------------------------- /.gitmessage: -------------------------------------------------------------------------------- 1 | module: Some awesome title (max of 50 characters) 2 | 3 | Your message, describing why (and not what) was changed. Reference to 4 | Pull Requests and issues must be placed at the end of the message. 5 | Message's body must be wrapped at 72 characters. 6 | 7 | Ref: https://github.com/mmarchini/libstapsdt/issues/2 8 | Fixex: https://github.com/mmarchini/libstapsdt/issues/1 9 | -------------------------------------------------------------------------------- /.readthedocs.yml: -------------------------------------------------------------------------------- 1 | requirements_file: docs/requirements.txt 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Sthima 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | CC=gcc 3 | CFLAGS= -std=gnu11 4 | LDFLAGS=-lelf -ldl -Wl,-z,noexecstack 5 | VERSION=0.1.0 6 | 7 | PREFIX=/usr 8 | 9 | OBJECTS = $(patsubst src/%.c, build/lib/%.o, $(wildcard src/*.c)) 10 | HEADERS = $(wildcard src/*.h) 11 | 12 | SOLINK = libstapsdt.so 13 | SONAME = libstapsdt.so.0 14 | 15 | all: out/libstapsdt.a out/$(SONAME) 16 | 17 | install: 18 | mkdir -p $(DESTDIR)$(PREFIX)/lib 19 | mkdir -p $(DESTDIR)$(PREFIX)/include 20 | cp out/$(SONAME) $(DESTDIR)$(PREFIX)/lib/$(SONAME) 21 | cp src/libstapsdt.h $(DESTDIR)$(PREFIX)/include/ 22 | ln -s $(DESTDIR)$(PREFIX)/lib/$(SONAME) $(DESTDIR)$(PREFIX)/lib/$(SOLINK) 23 | 24 | uninstall: 25 | rm -f $(DESTDIR)$(PREFIX)/lib/$(SONAME) 26 | rm -f $(DESTDIR)$(PREFIX)/lib/$(SOLINK) 27 | rm -f $(DESTDIR)$(PREFIX)/include/libstapsdt.h 28 | 29 | build/lib/libstapsdt-x86_64.o: src/asm/libstapsdt-x86_64.s 30 | mkdir -p build 31 | $(CC) $(CFLAGS) -fPIC -c $^ -o $@ 32 | 33 | build/lib/%.o: src/%.c $(HEADERS) 34 | mkdir -p build/lib/ 35 | $(CC) $(CFLAGS) -fPIC -c $< -o $@ 36 | 37 | out/libstapsdt.a: $(OBJECTS) build/lib/libstapsdt-x86_64.o 38 | mkdir -p out 39 | ar rcs $@ $^ 40 | 41 | out/$(SONAME): $(OBJECTS) build/lib/libstapsdt-x86_64.o 42 | mkdir -p out 43 | $(CC) $(CFLAGS) -shared -Wl,-soname=$(SONAME) -o $@ $^ $(LDFLAGS) 44 | 45 | demo: all example/demo.c 46 | $(CC) $(CFLAGS) example/demo.c out/libstapsdt.a -o demo -Isrc/ $(LDFLAGS) 47 | 48 | test: all 49 | make -C ./tests/ 50 | 51 | clean: 52 | rm -rf build/* 53 | rm -rf out/* 54 | make clean -C ./tests/ 55 | 56 | lint: 57 | clang-tidy src/*.h src/*.c -- -Isrc/ 58 | 59 | format: 60 | clang-tidy src/*.h src/*.c -fix -- -Isrc/ 61 | 62 | docs: 63 | make -C ./docs/ html 64 | 65 | docs-server: 66 | cd docs/_build/html; python3 -m http.server; 67 | 68 | deb-pkg-setup: 69 | mkdir -p dist/libstapsdt-$(VERSION)/; 70 | git archive HEAD | gzip > dist/libstapsdt-$(VERSION).tar.gz; 71 | tar xvzf dist/libstapsdt-$(VERSION).tar.gz -C dist/libstapsdt-$(VERSION)/; 72 | cd dist/libstapsdt-$(VERSION); dh_make -l -c mit -y -f ../libstapsdt-$(VERSION).tar.gz; 73 | rm -rf dist/libstapsdt-$(VERSION)/debian/*.ex dist/libstapsdt-$(VERSION)/debian/*.EX dist/libstapsdt-$(VERSION)/debian/README.* 74 | 75 | 76 | .PHONY: all clear lint format build-tests docs install uninstall deb-pkg-setup 77 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | libstapsdt 8 | 9 |

10 | 11 | 12 | [![Stability](https://img.shields.io/badge/version-unstable-lightgrey.svg?style=flat-square)](https://github.com/sthima/libstapsdt) 13 | [![Build Status](https://img.shields.io/travis/sthima/libstapsdt/master.svg?style=flat-square)](https://travis-ci.org/sthima/libstapsdt) 14 | [![Read the Docs](https://img.shields.io/readthedocs/libstapsdt.svg?style=flat-square)](https://libstapsdt.readthedocs.org/) 15 | 16 | ------------------------------------------------------------------------------- 17 | 18 | `libstapsdt` is a library which allows creating and firing Systemtap's USDT 19 | probes at runtime. It's inspired on 20 | [chrisa/libusdt](https://github.com/chrisa/libusdt/). The goal of this 21 | library is to add USDT probes functionality to dynamic languages. 22 | 23 | ## Table of Contents 24 | 25 | 26 | 27 | - [Table of Contents](#table-of-contents) 28 | - [How it works](#how-it-works) 29 | - [Dependencies](#dependencies) 30 | - [Ubuntu 16.04](#ubuntu-1604) 31 | - [Install](#install) 32 | - [Demo](#demo) 33 | - [Run tests](#run-tests) 34 | - [Write your own wrapper](#write-your-own-wrapper) 35 | 36 | 37 | 38 | ## How it works 39 | 40 | Systemtap's USDT implementation allows only statically defined probes because 41 | they are set as ELF notes by the compiler. To create probes at runtime, 42 | `libstapsdt` takes advantage of shared libraries: it creates a small library 43 | with an ELF note and links it at runtime. This way, most existing tools will 44 | keep working as expected. 45 | 46 | ## Dependencies 47 | 48 | `libstapsdt` currently requires the following dependencies: 49 | 50 | * libelf (from elfutils) 51 | 52 | ### Ubuntu 16.04 53 | 54 | ```bash 55 | sudo apt install libelf1 libelf-dev 56 | ``` 57 | 58 | ### Fedora 29 59 | 60 | ```bash 61 | sudo dnf install elfutils-libelf-devel 62 | ``` 63 | 64 | ## Install 65 | 66 | To build and install libstapsdt, just run: 67 | 68 | ```bash 69 | make 70 | sudo make install 71 | sudo ldconfig 72 | ``` 73 | 74 | ## Demo 75 | 76 | There's a demo program available. To build it, run: 77 | 78 | ```bash 79 | make demo # Executable will be available at ./demo 80 | ``` 81 | 82 | Usage: 83 | 84 | ```bash 85 | ./demo PROVIDER_NAME PROBE_NAME 86 | ``` 87 | 88 | After running the demo program, it can be instrumented with proper tools. 89 | 90 | Here's an example using [eBPF/bcc](https://github.com/iovisor/bcc) trace tool 91 | (built from source): 92 | 93 | ```bash 94 | sudo /usr/share/bcc/tools/trace -p $(pgrep demo) 'u::PROBE_NAME' 95 | ``` 96 | 97 | ## Run tests 98 | 99 | To run tests, just run the command below. Please be aware that there are only 100 | a few tests for now, but more will be added in the future. 101 | 102 | ```bash 103 | make test 104 | ``` 105 | 106 | # Wrappers 107 | 108 | Here is a list of wrappers for other languages: 109 | 110 | * [Python](https://pypi.org/project/stapsdt/) 111 | * [NodeJS](https://www.npmjs.com/package/usdt) 112 | * [Go](https://github.com/mmcshane/salp) 113 | * [LuaJIT](git.sr.ht/~sm2n/luajit-stapsdt) 114 | 115 | ## Write your own wrapper 116 | 117 | `libstapsdt` is written in C, which makes it very portable to almost any 118 | language. Most dynamic languages provide a way to wrap C code. Feel free to 119 | develop a wrapper in your language of choice. If you do, please let us know to 120 | update our wrappers list! 121 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = python -msphinx 7 | SPHINXPROJ = libstapsdt 8 | SOURCEDIR = . 9 | BUILDDIR = _build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | livehtml: 16 | sphinx-autobuild -b html . $(BUILDDIR)/html 17 | 18 | 19 | .PHONY: help Makefile livehtml 20 | 21 | # Catch-all target: route all unknown targets to Sphinx using the new 22 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 23 | %: Makefile 24 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 25 | -------------------------------------------------------------------------------- /docs/_static/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linux-usdt/libstapsdt/0d53f987b0787362fd9c16a93cdad2c273d809fc/docs/_static/.gitkeep -------------------------------------------------------------------------------- /docs/_static/internal-flow.svg: -------------------------------------------------------------------------------- 1 | 2 |
Provider and Probes registration
Provider and Probes registration
Shared library creation
Shared library creation
Shared library loading
Shared library loading
-------------------------------------------------------------------------------- /docs/_static/usage-workflow.svg: -------------------------------------------------------------------------------- 1 | 2 |
providerInit()
providerInit()
providerAddProbe()
<font face="Hack">providerAddProbe()</font>
providerLoad()
<font face="Hack">providerLoad()</font>
probeFire()
[Not supported by viewer]
probeIsEnabled()
<font face="Hack">probeIsEnabled()</font>
providerUnload()
<font face="Hack">providerUnload()</font>
providerDestroy()
<font face="Hack">providerDestroy()</font>
Provider Unloaded
[Not supported by viewer]
Provider Loaded
[Not supported by viewer]
-------------------------------------------------------------------------------- /docs/_templates/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linux-usdt/libstapsdt/0d53f987b0787362fd9c16a93cdad2c273d809fc/docs/_templates/.gitkeep -------------------------------------------------------------------------------- /docs/api/api.rst: -------------------------------------------------------------------------------- 1 | ### 2 | API 3 | ### 4 | 5 | .. toctree:: 6 | :maxdepth: 2 7 | 8 | types 9 | functions 10 | -------------------------------------------------------------------------------- /docs/api/functions.rst: -------------------------------------------------------------------------------- 1 | ######### 2 | Functions 3 | ######### 4 | 5 | 6 | .. c:function:: SDTProvider_t *providerInit(const char *name) 7 | 8 | This function received a name as argument, creates a :c:type:`SDTProvider_t` 9 | with all attributes correctly initialized and then returns a pointer to it, 10 | or ``NULL`` if there was an error. 11 | 12 | :param string name: The name of this provider 13 | :return: A pointer to the new provider 14 | :rtype: SDTProvider_t* 15 | :type str: const char* 16 | 17 | .. c:function:: SDTProbe_t *providerAddProbe(SDTProvider_t *provider, const char *name, int argCount, ...) 18 | 19 | This function received a :c:type:`SDTProvider_t` created by 20 | :c:func:`providerInit`, a name and any number of :c:type:`ArgType_t` (you 21 | must pass the actual number of arguments as argCount). It will then 22 | create a :c:type:`SDTProbe_t` with all attributes correctly initialized and 23 | register it to the given :c:type:`SDTProvider_t`. The return would be a 24 | pointer to the created :c:type:`SDTProbe_t`, or ``NULL`` if there was an 25 | error. 26 | 27 | :param SDTProvider_t* provider: The provider where this probe will be created 28 | :param string name: The name of this probe 29 | :param int argCount: The number of arguments accepted by this probe 30 | :param SDTArgTypes_t ...: Any number of arguments (number of arguments must 31 | match argCount) 32 | :return: A pointer to the new provider 33 | :rtype: SDTProbe_t* 34 | 35 | .. c:function:: int providerLoad(SDTProvider_t *provider) 36 | 37 | When you created all probes you wanted, you should call this function to load 38 | the provider correctly. The returning value will be ``0`` if everything 39 | went well, or another number otherwise. 40 | 41 | After calling this function, all probes will be efectively 42 | available for tracing, and you shouldn't add new probes or load this 43 | provider again. 44 | 45 | :param SDTProvider_t* provider: The provider to be loaded 46 | :return: A status code (``0`` means success, other numbers indicates error) 47 | :rtype: int 48 | 49 | 50 | .. c:function:: int providerUnload(SDTProvider_t *provider) 51 | 52 | Once you don't want your probes to be available anymore, you can call this 53 | function. This will clean-up everything that :c:func:`providerLoad` did. The 54 | returning value will be ``0`` if everything went well, or another number 55 | otherwise. 56 | 57 | After calling this function all probes will be unavailable for tracing, and 58 | you can add new probes to the provider again. 59 | 60 | :param SDTProvider_t* provider: The provider to be unloaded 61 | :return: A status code (``0`` means success, other numbers indicates error) 62 | :rtype: int 63 | 64 | .. c:function:: void providerDestroy(SDTProvider_t *provider) 65 | 66 | This function frees a :c:type:`SDTProvider_t` from memory, along with all 67 | registered :c:type:`SDTProbe_t` created with :c:func:`providerAddProbe`. 68 | 69 | :param SDTProvider_t* provider: The provider to be freed from memory, along 70 | with it's probes 71 | 72 | .. c:function:: void probeFire(SDTProbe_t *probe, ...) 73 | 74 | This function fires a probe if it's available for tracing (which means it 75 | will only fire the probe if :c:func:`providerLoad` was called before). 76 | 77 | :param SDTProbe_t* probe: The probe to be fired 78 | :param any ...: Any number of arguments (must match the expected 79 | number of arguments for this probe) 80 | 81 | .. c:function:: int probeIsEnabled(SDTProbe_t *probe) 82 | 83 | This function returns ``1`` if the probe is being traced, or ``0`` otherwise. 84 | 85 | :param SDTProbe_t* probe: The probe to be checked 86 | :return: ``1`` if probe is enabled, ``0`` otherwise 87 | :rtype: int 88 | -------------------------------------------------------------------------------- /docs/api/types.rst: -------------------------------------------------------------------------------- 1 | ##### 2 | Types 3 | ##### 4 | 5 | 6 | .. c:type:: struct SDTProvider_t 7 | 8 | Represents a USDT Provider. A USDT Provider is basically a namespace for USDT 9 | probes. Shouldn't be created manually, use :c:func:`providerInit` instead. 10 | 11 | .. c:member:: char *name 12 | 13 | Provider's name 14 | 15 | .. c:member:: SDTProbeList_t *probes 16 | 17 | Linked-list of registered probes 18 | 19 | .. c:member:: SDTError_t errno 20 | 21 | Error code of the last error for this provider 22 | 23 | .. c:member:: char *error 24 | 25 | Error string of the last error for this provider 26 | 27 | .. c:type:: struct SDTProbe_t 28 | 29 | Represents a USDT Probe. A probe is basically a software breakpoint. 30 | Shouldn't be created manually, use :c:func:`providerAddProbe` instead. 31 | 32 | .. c:member:: char *name 33 | 34 | Probe's name 35 | 36 | .. c:member:: ArgType_t argFmt[MAX_ARGUMENTS] 37 | 38 | Array holding all arguments accepted by this probe. 39 | 40 | .. c:member:: struct SDTProvider *provider 41 | 42 | Pointer to this probe's provider 43 | 44 | .. c:member:: int argCount 45 | 46 | Number of accepted arguments 47 | 48 | .. c:type:: enum SDTArgTypes_t 49 | 50 | Represents all accepted arguments defined by Systeptap's SDT probes. 51 | 52 | .. c:member:: noarg 53 | 54 | No argument 55 | 56 | .. c:member:: uint8 57 | 58 | 8 bits unsigned int 59 | 60 | .. c:member:: int8 61 | 62 | 8 bits signed int 63 | 64 | .. c:member:: uint16 65 | 66 | 16 bits unsigned int 67 | 68 | .. c:member:: int16 69 | 70 | 16 bits signed int 71 | 72 | .. c:member:: uint32 73 | 74 | 32 bits unsigned int 75 | 76 | .. c:member:: int32 77 | 78 | 32 bits signed int 79 | 80 | .. c:member:: uint64 81 | 82 | 64 bits unsigned int 83 | 84 | .. c:member:: int64 85 | 86 | 64 bits signed int 87 | 88 | .. c:type:: enum SDTError_t 89 | 90 | Represents all errors thrown by libstapsdt. 91 | 92 | .. c:member:: noError 93 | 94 | This error code means that no error occured so far 95 | 96 | .. c:member:: elfCreationError 97 | 98 | This error code means that we were unable to create an Elf file to store 99 | our probes 100 | 101 | .. c:member:: tmpCreationError 102 | 103 | This error code means that we were unable to open a temporary file at 104 | ``/tmp/``. A common mistake here is having a ``/`` in the provider name, 105 | which will be interpreted by the operating system as a folder. 106 | 107 | .. c:member:: sharedLibraryOpenError 108 | 109 | This error code means that we were unable to open the shared library that we 110 | just created 111 | 112 | .. c:member:: symbolLoadingError 113 | 114 | This error code means that the we were unable to load a symbol from the 115 | shared library we just created 116 | 117 | .. c:member:: sharedLibraryCloseError 118 | 119 | This error code means that we were unable to close the shared library for 120 | this provider 121 | 122 | 123 | .. c:type:: struct SDTProbeList_t 124 | 125 | Represents a linked-list of :c:type:`SDTProbe_t`. Shouldn't be handled 126 | manually, use :c:func:`providerAddProbe` instead. 127 | 128 | .. c:member:: SDTProbe_t probe 129 | .. c:member:: struct SDTProbeList_ *next 130 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # libstapsdt documentation build configuration file, created by 5 | # sphinx-quickstart on Wed Oct 4 13:29:04 2017. 6 | # 7 | # This file is execfile()d with the current directory set to its 8 | # containing dir. 9 | # 10 | # Note that not all possible configuration values are present in this 11 | # autogenerated file. 12 | # 13 | # All configuration values have a default; values that are commented out 14 | # serve to show the default. 15 | 16 | # If extensions (or modules to document with autodoc) are in another directory, 17 | # add these directories to sys.path here. If the directory is relative to the 18 | # documentation root, use os.path.abspath to make it absolute, like shown here. 19 | # 20 | # import os 21 | # import sys 22 | # sys.path.insert(0, os.path.abspath('.')) 23 | 24 | 25 | # -- General configuration ------------------------------------------------ 26 | 27 | # If your documentation needs a minimal Sphinx version, state it here. 28 | # 29 | # needs_sphinx = '1.0' 30 | 31 | # Add any Sphinx extension module names here, as strings. They can be 32 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 33 | # ones. 34 | extensions = [] 35 | 36 | # Add any paths that contain templates here, relative to this directory. 37 | templates_path = ['_templates'] 38 | 39 | # The suffix(es) of source filenames. 40 | # You can specify multiple suffix as a list of string: 41 | # 42 | 43 | source_parsers = { 44 | '.md': 'recommonmark.parser.CommonMarkParser', 45 | } 46 | source_suffix = ['.rst', '.md'] 47 | 48 | # The master toctree document. 49 | master_doc = 'index' 50 | 51 | # General information about the project. 52 | project = 'libstapsdt' 53 | copyright = '2017, Mary Marchini' 54 | author = 'Mary Marchini' 55 | 56 | # The version info for the project you're documenting, acts as replacement for 57 | # |version| and |release|, also used in various other places throughout the 58 | # built documents. 59 | # 60 | # The short X.Y version. 61 | version = '0.1.1' 62 | # The full version, including alpha/beta/rc tags. 63 | release = '0.1.1' 64 | 65 | # The language for content autogenerated by Sphinx. Refer to documentation 66 | # for a list of supported languages. 67 | # 68 | # This is also used if you do content translation via gettext catalogs. 69 | # Usually you set "language" from the command line for these cases. 70 | language = None 71 | 72 | # List of patterns, relative to source directory, that match files and 73 | # directories to ignore when looking for source files. 74 | # This patterns also effect to html_static_path and html_extra_path 75 | exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] 76 | 77 | # The name of the Pygments (syntax highlighting) style to use. 78 | pygments_style = 'sphinx' 79 | 80 | # If true, `todo` and `todoList` produce output, else they produce nothing. 81 | todo_include_todos = False 82 | 83 | 84 | # -- Options for HTML output ---------------------------------------------- 85 | 86 | # The theme to use for HTML and HTML Help pages. See the documentation for 87 | # a list of builtin themes. 88 | # 89 | # html_theme = 'alabaster' 90 | html_theme = "sphinx_rtd_theme" 91 | 92 | # Theme options are theme-specific and customize the look and feel of a theme 93 | # further. For a list of options available for each theme, see the 94 | # documentation. 95 | # 96 | html_theme_options = { 97 | 'navigation_depth': 4, 98 | } 99 | 100 | # Add any paths that contain custom static files (such as style sheets) here, 101 | # relative to this directory. They are copied after the builtin static files, 102 | # so a file named "default.css" will overwrite the builtin "default.css". 103 | html_static_path = ['_static'] 104 | 105 | # Custom sidebar templates, must be a dictionary that maps document names 106 | # to template names. 107 | # 108 | # This is required for the alabaster theme 109 | # refs: http://alabaster.readthedocs.io/en/latest/installation.html#sidebars 110 | html_sidebars = { 111 | '**': [ 112 | 'about.html', 113 | 'navigation.html', 114 | 'relations.html', # needs 'show_related': True theme option to display 115 | 'searchbox.html', 116 | 'donate.html', 117 | ] 118 | } 119 | 120 | 121 | # -- Options for HTMLHelp output ------------------------------------------ 122 | 123 | # Output file base name for HTML help builder. 124 | htmlhelp_basename = 'libstapsdtdoc' 125 | 126 | 127 | # -- Options for LaTeX output --------------------------------------------- 128 | 129 | latex_elements = { 130 | # The paper size ('letterpaper' or 'a4paper'). 131 | # 132 | # 'papersize': 'letterpaper', 133 | 134 | # The font size ('10pt', '11pt' or '12pt'). 135 | # 136 | # 'pointsize': '10pt', 137 | 138 | # Additional stuff for the LaTeX preamble. 139 | # 140 | # 'preamble': '', 141 | 142 | # Latex figure (float) alignment 143 | # 144 | # 'figure_align': 'htbp', 145 | } 146 | 147 | # Grouping the document tree into LaTeX files. List of tuples 148 | # (source start file, target name, title, 149 | # author, documentclass [howto, manual, or own class]). 150 | latex_documents = [ 151 | (master_doc, 'libstapsdt.tex', 'libstapsdt Documentation', 152 | 'Mary Marchini', 'manual'), 153 | ] 154 | 155 | 156 | # -- Options for manual page output --------------------------------------- 157 | 158 | # One entry per manual page. List of tuples 159 | # (source start file, name, description, authors, manual section). 160 | man_pages = [ 161 | (master_doc, 'libstapsdt', 'libstapsdt Documentation', 162 | [author], 1) 163 | ] 164 | 165 | 166 | # -- Options for Texinfo output ------------------------------------------- 167 | 168 | # Grouping the document tree into Texinfo files. List of tuples 169 | # (source start file, target name, title, author, 170 | # dir menu entry, description, category) 171 | texinfo_documents = [ 172 | (master_doc, 'libstapsdt', 'libstapsdt Documentation', 173 | author, 'libstapsdt', 'One line description of project.', 174 | 'Miscellaneous'), 175 | ] 176 | -------------------------------------------------------------------------------- /docs/getting-started/getting-started.rst: -------------------------------------------------------------------------------- 1 | ############### 2 | Getting Started 3 | ############### 4 | 5 | ``libstapsdt`` 6 | 7 | ======== 8 | Packages 9 | ======== 10 | 11 | We currently offer package installation for Ubuntu via PPA. 12 | 13 | Ubuntu via PPA 14 | -------------- 15 | 16 | To install ``libstapsdt`` on Ubuntu, you need to add Sthima's Open Source 17 | Software PPA first, and then you'll be able to install it: 18 | 19 | .. code-block:: bash 20 | 21 | sudo add-apt-repository ppa:sthima/oss 22 | sudo apt-get update 23 | sudo apt-get install libstapsdt0 libstapsdt-dev 24 | 25 | 26 | ================= 27 | Build from source 28 | ================= 29 | 30 | If you need to install libstapsdt in a different distribution or want to use the 31 | latest version, you can build it from source as follow. 32 | 33 | Dependencies 34 | ------------ 35 | 36 | First, you need to install ``libstapsdt`` dependencies. For now, the only 37 | dependency is libelf (from elfutils). 38 | 39 | Ubuntu 40 | ...... 41 | 42 | .. code-block:: bash 43 | 44 | sudo apt install libelf1 libelf-dev 45 | 46 | 47 | Build 48 | ----- 49 | 50 | To build and install libstapsdt, you just need to run: 51 | 52 | .. code-block:: bash 53 | 54 | make 55 | sudo make install 56 | sudo ldconfig 57 | 58 | Now ``libstapsdt`` is installed on your system! 59 | 60 | Demo 61 | ---- 62 | 63 | There's a demo program available. To build it, run: 64 | 65 | .. code-block:: bash 66 | 67 | make demo # Executable will be available at ./demo 68 | 69 | You can then try it by running: 70 | 71 | .. code-block:: bash 72 | 73 | ./demo PROVIDER_NAME PROBE_NAME 74 | 75 | After running the demo program, you can. 76 | 77 | Here's an example using the latest version of 78 | `iovisor/bcc `_'s ``trace`` tool: 79 | 80 | .. code-block:: bash 81 | 82 | sudo /usr/share/bcc/tools/trace -p $(pgrep demo) 'u::PROBE_NAME' 83 | -------------------------------------------------------------------------------- /docs/how-it-works/how-to-use.rst: -------------------------------------------------------------------------------- 1 | #################### 2 | libstapsdt explained 3 | #################### 4 | 5 | 6 | 7 | ``libstapsdt`` has a small API with only 7 functions and one possible flow. To 8 | use ``libstapsdt`` you need to create a provider and register probes to it. A 9 | provider has two possible states: unloaded and loaded. After creating it, it's 10 | state is unloaded. After all probes are registered, you need to load your 11 | provider, effectively allowing tracing tools to access the provider's probes. 12 | 13 | After you finish using those probes, you can unload the provider, freeing all 14 | structures allocated to it during the load step. Only after that you can 15 | destroy the provider, removing all registered probes with it. 16 | 17 | This flow is illustrated in the image below: 18 | 19 | .. image:: ../_static/usage-workflow.svg 20 | 21 | ======= 22 | Example 23 | ======= 24 | 25 | The following example goes through all steps of ``libstapsdt``'s flow: 26 | 27 | .. code-block:: c 28 | 29 | #include 30 | #include 31 | #include 32 | 33 | int main() { 34 | int i=1; 35 | SDTProvider_t *provider; 36 | SDTProbe_t *probe; 37 | 38 | provider = providerInit("myLittleProvider"); 39 | probe = providerAddProbe(provider, "myLittleProbe", 1, uint64); 40 | 41 | if(providerLoad(provider) == -1) { 42 | printf("Something went wrong...\n"); 43 | return -1; 44 | } 45 | 46 | while(i) { 47 | printf("Firing probe...\n"); 48 | if(probeIsEnabled(probe)) { 49 | probeFire(probe, "I'm a runner!"); 50 | printf("Probe fired!\n"); 51 | i = 0; 52 | } 53 | sleep(1); 54 | } 55 | 56 | providerUnload(provider); 57 | providerDestroy(provider); 58 | 59 | return 0; 60 | } 61 | 62 | Let's take a better look at the code. The first lines are used to create a 63 | provider named ``"myLittleProvider"`` (using :c:func:`providerInit`) and then 64 | it registers a probe named ``"myLittleProbe"`` (using 65 | :c:func:`providerAddProbe`). You may have noticed those extra arguments to 66 | :c:func:`providerAddProbe`: they are used to determine how many arguments 67 | the probe will accept when fired, and the type of those parameters (you can 68 | see all available types in :c:type:`SDTArgTypes_t`). 69 | 70 | .. code-block:: c 71 | 72 | SDTProvider_t *provider; 73 | SDTProbe_t *probe; 74 | 75 | provider = providerInit("myLittleProvider"); 76 | probe = providerAddProbe(provider, "myLittleProbe", 1, uint64); 77 | 78 | 79 | After creating our provider and registering our probe, we need to load our 80 | provider (otherwise we won't be able to fire our probes). This is done by 81 | simply calling :c:func:`providerLoad` with the provider as argument. It's 82 | important to handle any errors that may occur to avoid problems in execution 83 | later on. 84 | 85 | 86 | .. code-block:: c 87 | 88 | if(providerLoad(provider) == -1) { 89 | printf("Something went wrong...\n"); 90 | return -1; 91 | } 92 | 93 | 94 | Now we can use :c:func:`probeIsEnabled` and :c:func:`probeFire`. 95 | :c:func:`probeIsEnabled` will only return `True` if the program is being 96 | traced. Therefore, in this example we'll be on an infinite loop until our 97 | program is traced. You can use 98 | `iovisor/bcc `_ ``trace`` tool for this 99 | (``sudo /usr/share/bcc/tools/trace -p PID 'u::myLittleProbe'``). 100 | 101 | After using the trace tool, our probe will be replaced by a breakpoint, and 102 | :c:func:`probeIsEnabled` will return ``True``, firing the probe with 103 | :c:func:`probeFire` inside our if-statement and then stepping out of our loop. 104 | 105 | .. code-block:: c 106 | 107 | while(i) { 108 | printf("Firing probe...\n"); 109 | if(probeIsEnabled(probe)) { 110 | probeFire(probe, "I'm a runner!"); 111 | printf("Probe fired!\n"); 112 | i = 0; 113 | } 114 | sleep(1); 115 | } 116 | 117 | Those last lines of code are used to unload and cleanup our provider. It is 118 | imporant that you run both :c:func:`providerUnload` and 119 | :c:func:`providerDestroy` in this exact order after you don't need the probes 120 | anymore, to avoid memory leaks and Segmentation faults. 121 | 122 | .. code-block:: c 123 | 124 | providerUnload(provider); 125 | providerDestroy(provider); 126 | -------------------------------------------------------------------------------- /docs/how-it-works/index.rst: -------------------------------------------------------------------------------- 1 | ############ 2 | How it works 3 | ############ 4 | 5 | .. toctree:: 6 | :maxdepth: 2 7 | 8 | how-to-use 9 | internals 10 | -------------------------------------------------------------------------------- /docs/how-it-works/internals.rst: -------------------------------------------------------------------------------- 1 | ################ 2 | Behind the Scene 3 | ################ 4 | 5 | .. attention:: 6 | 7 | This page is intended for advanced users, and it assumes working knowledge of 8 | **Elf** files, **shared libraries** and **software instrumentation**. 9 | 10 | ``libstapsdt`` uses Systemtap SDT format to create runtime SDT probes - 11 | therefore the reason for its name. So why write yet another library for SDT 12 | probes instead of using Systemtap? 13 | 14 | ======================= 15 | How Systemtap SDT works 16 | ======================= 17 | 18 | Systemtap uses compiler macros to register its SDT probes, making it impossible 19 | to have probes registered during runtime. An example is shown below, where we 20 | register a probe called `Probe` to a provider called `Provider`. 21 | 22 | .. code-block:: c 23 | 24 | #include 25 | 26 | int main() { 27 | DTRACE_PROBE(Provider, Probe); 28 | return 0; 29 | } 30 | 31 | 32 | The resulting binary from this code will have a new Elf section called 33 | ``.stapsdt.base``, located right after the code (usually being the ``.text`` 34 | section). This base is relevant to help tracing tools to calculate the memory 35 | address of any probe after the binary is loaded into memory. 36 | 37 | .. 38 | explain why tracing tools needs to calculate the address after a binary 39 | is loaded into memory 40 | 41 | It will also have a Elf note, where all probes data (name, address, 42 | semaphores, arguments) will be stored to be read later by any tracing tool. 43 | The compiler will also replace our ``DTRACE_PROBE`` macro with a function call, 44 | and that's where the probe points to, allowing it to easily pass arguments to 45 | the probe. This function is a ``no-op``. 46 | 47 | .. code-block:: python 48 | 49 | Displaying notes found at file offset 0x00001064 with length 0x0000003c: 50 | Owner Data size Description 51 | stapsdt 0x00000028 NT_STAPSDT (SystemTap probe descriptors) 52 | Provider: Provider 53 | Name: Probe 54 | Location: 0x00000000004004da, Base: 0x0000000000400574, Semaphore: 0x0000000000000000 55 | Arguments: 56 | 57 | There's more information about how Systemtap implements their SDT probes 58 | `here `_. 59 | 60 | ================================== 61 | Roses are Red, Violets are Blue... 62 | ================================== 63 | 64 | **And shared libraries are Elf files!** (on most UNIX systems at least) 65 | 66 | Ok, so now we know that Systemtap uses Elf properties to inform tracing tools 67 | about registered probes. We also know that they have a well-defined and rather 68 | simple strucutre. One which can easily be implemented. 69 | 70 | But we can't edit our binary just to add new Elf notes pointing to new probes, 71 | and most Systemtap-SDT-capable tracing tools will only look at the binary and 72 | not at the running process for this information. 73 | 74 | That means we need to generate an Elf file at runtime, add our probes to it, and 75 | then use it in our running process in a way that our tracing tools will find 76 | it, which means... Shared libraries! 77 | 78 | The "secret source" used by ``libstapsdt`` to allow Systemtap SDT probes 79 | registration at runtime is shared libraries. Our public API is rather simple, 80 | but the library has quite some code. Most of this code is used to generate a 81 | shared library from scratch, dynamically adding code to it and registering all 82 | probes as Elf notes. 83 | 84 | .. image:: ../_static/internal-flow.svg 85 | 86 | The shared library is created (with help from ``libelf``) and loaded into memory 87 | (by using ``dlopen()`` when :c:func:`providerLoad` is executed. That's why it's 88 | not possible to add new probes after a provider is loaded. It's also worth 89 | noting that each provider will generate exactly one shared library when loaded, 90 | and providers don't share a shared library. 91 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. libstapsdt documentation master file, created by 2 | sphinx-quickstart on Wed Oct 4 13:29:04 2017. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | ########################################## 7 | libstapsdt - Runtime USDT probes on Linux! 8 | ########################################## 9 | 10 | **libstapsdt** is a library which allows creating and firing Systemtap's USDT 11 | probes at runtime. It's inspired on 12 | `chrisa/libusdt `_. The goal of this 13 | library is to add USDT probes functionality to dynamic languages. 14 | 15 | Motivation 16 | ========== 17 | 18 | Systemtap's USDT implementation allows only statically defined probes because 19 | they are set as ELF notes by the compiler. To create probes at runtime, 20 | `libstapsdt` takes advantage of shared libraries: it creates a small 21 | library with an ELF note and links it at runtime. This way, most existing tools 22 | will keep working as expected. 23 | 24 | 25 | Table of Contents 26 | ================= 27 | 28 | 29 | .. toctree:: 30 | :maxdepth: 1 31 | 32 | getting-started/getting-started 33 | how-it-works/index 34 | wrappers/wrappers 35 | api/api 36 | -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | sphinx >= 1.6.0 2 | -------------------------------------------------------------------------------- /docs/wrappers/wrappers.rst: -------------------------------------------------------------------------------- 1 | ######## 2 | Wrappers 3 | ######## 4 | 5 | Here is a list of wrappers for other languages: 6 | 7 | * `Python `_ 8 | * `NodeJS `_ 9 | * `Go `_ 10 | 11 | Write your own wrapper 12 | ====================== 13 | 14 | ``libstapsdt`` is written in C, which makes it very portable to almost any 15 | language. Most dynamic languages provide a way to wrap C code. Feel free 16 | to develop a wrapper in your language of choice. If you do, please let us know 17 | to update our wrappers list! 18 | -------------------------------------------------------------------------------- /example/demo.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int main( int argc, char *argv[] ) { 7 | SDTProvider_t *provider; 8 | SDTProbe_t **probes; 9 | int probesCount = 0; 10 | unsigned long long i=0; 11 | int j=0; 12 | 13 | if(argc < 3) { 14 | printf("usage: demo PROVIDER PROBE\n"); 15 | return -1; 16 | } 17 | 18 | probesCount = argc - 2; 19 | probes = calloc(sizeof(SDTProvider_t *), probesCount); 20 | 21 | provider = providerInit(argv[1]); 22 | for (int idx = 0; idx < (probesCount); idx++) { 23 | probes[idx] = providerAddProbe(provider, argv[idx + 2], 2, uint64, int64); 24 | } 25 | 26 | if(providerLoad(provider) == -1) { 27 | printf("Something went wrong: %s\n", provider->error); 28 | return -1; 29 | } 30 | 31 | while(1) { 32 | printf("Firing probes...\n"); 33 | for (int idx = 0; idx < probesCount; idx++) { 34 | printf("Firing probe [%d]...\n", idx); 35 | probeFire(probes[idx], i++, j--); 36 | } 37 | printf("Probe fired!\n"); 38 | sleep(3); 39 | } 40 | 41 | return 0; 42 | } 43 | -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linux-usdt/libstapsdt/0d53f987b0787362fd9c16a93cdad2c273d809fc/logo.png -------------------------------------------------------------------------------- /src/asm/libstapsdt-x86_64.s: -------------------------------------------------------------------------------- 1 | .text 2 | 3 | .align 4, 0x90 4 | .globl _funcStart 5 | .globl _funcEnd 6 | 7 | _funcStart: 8 | nop 9 | nop 10 | nop 11 | nop 12 | ret 13 | _funcEnd: 14 | nop 15 | -------------------------------------------------------------------------------- /src/dynamic-symbols.c: -------------------------------------------------------------------------------- 1 | #include "dynamic-symbols.h" 2 | #include "string-table.h" 3 | #include 4 | 5 | DynamicSymbolTable *dynamicSymbolTableInit(StringTable *dynamicString) { 6 | DynamicSymbolTable *dynSymTab = 7 | (DynamicSymbolTable *)calloc(sizeof(DynamicSymbolTable), 1); 8 | 9 | dynSymTab->stringTable = dynamicString; 10 | 11 | dynSymTab->bssStart.string = stringTableAdd(dynamicString, "__bss_start"); 12 | dynSymTab->eData.string = stringTableAdd(dynamicString, "_edata"); 13 | dynSymTab->end.string = stringTableAdd(dynamicString, "_end"); 14 | 15 | dynSymTab->count = 0; 16 | dynSymTab->symbols = NULL; 17 | 18 | return dynSymTab; 19 | } 20 | 21 | DynamicSymbol *dynamicSymbolTableAdd(DynamicSymbolTable *table, 22 | char *symbolName) { 23 | DynamicSymbolList *symbol = (DynamicSymbolList *)calloc(sizeof(DynamicSymbolList), 1); 24 | 25 | symbol->symbol.string = stringTableAdd(table->stringTable, symbolName); 26 | 27 | symbol->next = table->symbols; 28 | table->symbols = symbol; 29 | table->count += 1; 30 | 31 | return &(symbol->symbol); 32 | } 33 | 34 | void dynamicSymbolTableFree(DynamicSymbolTable *table) { 35 | DynamicSymbolList *node=NULL, *next=NULL; 36 | for(node=table->symbols; node!=NULL; node=next) { 37 | next=node->next; 38 | free(node); 39 | } 40 | free(table); 41 | } 42 | -------------------------------------------------------------------------------- /src/dynamic-symbols.h: -------------------------------------------------------------------------------- 1 | #ifndef _DYNAMIC_SYMBOLS_ 2 | #define _DYNAMIC_SYMBOLS_ 3 | 4 | #include "string-table.h" 5 | #include 6 | 7 | typedef struct { 8 | StringTableNode *string; 9 | Elf64_Sym *symbol; 10 | } DynamicSymbol; 11 | 12 | typedef struct DynamicSymbolList_ { 13 | DynamicSymbol symbol; 14 | struct DynamicSymbolList_ *next; 15 | } DynamicSymbolList; 16 | 17 | typedef struct { 18 | StringTable *stringTable; 19 | 20 | DynamicSymbol bssStart; 21 | DynamicSymbol eData; 22 | DynamicSymbol end; 23 | 24 | size_t count; 25 | DynamicSymbolList *symbols; 26 | } DynamicSymbolTable; 27 | 28 | DynamicSymbolTable *dynamicSymbolTableInit(StringTable *dynamicString); 29 | DynamicSymbol *dynamicSymbolTableAdd(DynamicSymbolTable *table, 30 | char *symbolName); 31 | 32 | void dynamicSymbolTableFree(DynamicSymbolTable *table); 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /src/errors.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #define _GNU_SOURCE 4 | #include 5 | #include 6 | 7 | #include "errors.h" 8 | 9 | const char *sdtErrors[] = { 10 | "failed to create Elf shared library for provider '%s'", 11 | "failed to create temporary file", 12 | "failed to open shared library '%s': %s", 13 | "failed to load symbol '%s' for shared library '%s': %s", 14 | "failed to close shared library '%s' for provider '%s': %s", 15 | }; 16 | 17 | void sdtSetError(SDTProvider_t *provider, SDTError_t error, ...) { 18 | va_list argp; 19 | 20 | if(provider->error != NULL) { 21 | free(provider->error); 22 | provider->error = NULL; 23 | provider->errno = noError; 24 | } 25 | 26 | va_start(argp, error); 27 | provider->errno = error; 28 | (void)vasprintf(&provider->error, sdtErrors[error], argp); 29 | va_end(argp); 30 | } 31 | -------------------------------------------------------------------------------- /src/errors.h: -------------------------------------------------------------------------------- 1 | #ifndef _ERRORS_H 2 | #define _ERRORS_H 3 | 4 | #include "libstapsdt.h" 5 | 6 | void sdtSetError(SDTProvider_t *provider, SDTError_t error, ...); 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /src/hash-table.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "dynamic-symbols.h" 3 | 4 | size_t hashTableFromSymbolTable(DynamicSymbolTable *table, uint32_t **hashTable) { 5 | DynamicSymbolList *current; 6 | uint32_t nBuckets = table->count + 3, // + 3, to count bss, end, and eData symbols 7 | nChains = table->count + 3; 8 | size_t hashTableSize = (nBuckets + nChains + 2); 9 | 10 | uint32_t *hashTable_ = (uint32_t *)calloc(sizeof(uint32_t), hashTableSize); 11 | uint32_t *buckets = &(hashTable_[2]); 12 | uint32_t *chains = &(buckets[nBuckets]); 13 | int idx, stringIdx=2; 14 | 15 | hashTable_[0] = nBuckets; 16 | hashTable_[1] = nChains; 17 | 18 | for(current=table->symbols; current!=NULL; current=current->next) { 19 | idx = elf_hash(current->symbol.string->str) % nBuckets; 20 | chains[stringIdx] = buckets[idx]; 21 | buckets[idx] = stringIdx; 22 | stringIdx++; 23 | } 24 | 25 | *hashTable = hashTable_; 26 | return hashTableSize * sizeof(uint32_t); 27 | } 28 | -------------------------------------------------------------------------------- /src/hash-table.h: -------------------------------------------------------------------------------- 1 | #include "dynamic-symbols.h" 2 | 3 | size_t hashTableFromSymbolTable(DynamicSymbolTable *table, uint32_t **hashTable); 4 | -------------------------------------------------------------------------------- /src/libstapsdt.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "dynamic-symbols.h" 14 | #include "sdtnote.h" 15 | #include "section.h" 16 | #include "string-table.h" 17 | #include "shared-lib.h" 18 | #include "util.h" 19 | #include "libstapsdt.h" 20 | #include "errors.h" 21 | 22 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0) 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #ifdef __NR_memfd_create // older glibc may not have this syscall defined 29 | #define HAVE_LIBSTAPSDT_MEMORY_BACKED_FD 30 | // Note that linux must be 3.17 or greater to support this 31 | static inline int memfd_create(const char *name, unsigned int flags) { 32 | return syscall(__NR_memfd_create, name, flags); 33 | } 34 | #endif // #ifdef __NR_memfd_create 35 | #endif // if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0) 36 | 37 | int createSharedLibrary(int fd, SDTProvider_t *provider) { 38 | DynElf *dynElf = dynElfInit(fd); 39 | 40 | for(SDTProbeList_t *node=provider->probes; node != NULL; node = node->next) { 41 | dynElfAddProbe(dynElf, &(node->probe)); 42 | } 43 | 44 | if(dynElfSave(dynElf) == -1) { 45 | sdtSetError(provider, elfCreationError, provider->name); 46 | return -1; 47 | } 48 | 49 | dynElfClose(dynElf); 50 | 51 | return 0; 52 | } 53 | 54 | SDTProvider_t *providerInit(const char *name) { 55 | SDTProvider_t *provider = (SDTProvider_t *) calloc(sizeof(SDTProvider_t), 1); 56 | provider->error = NULL; 57 | provider->errno = noError; 58 | provider->probes = NULL; 59 | provider->_handle = NULL; 60 | provider->_filename = NULL; 61 | 62 | provider->_memfd = -1; 63 | #ifdef HAVE_LIBSTAPSDT_MEMORY_BACKED_FD 64 | provider->_use_memfd = memfd_enabled; 65 | #else 66 | provider->_use_memfd = memfd_disabled; 67 | #endif 68 | 69 | provider->name = (char *) calloc(sizeof(char), strlen(name) + 1); 70 | memcpy(provider->name, name, sizeof(char) * strlen(name) + 1); 71 | 72 | return provider; 73 | } 74 | 75 | int providerUseMemfd(SDTProvider_t *provider, const MemFDOption_t use_memfd) { 76 | #ifdef HAVE_LIBSTAPSDT_MEMORY_BACKED_FD 77 | // Changing use of memfd must be done while provider is not loaded. 78 | if(provider && !provider->_handle) { 79 | provider->_use_memfd = use_memfd; 80 | return 1; 81 | } 82 | #endif 83 | return 0; 84 | } 85 | 86 | SDTProbe_t *providerAddProbe(SDTProvider_t *provider, const char *name, int argCount, ...) { 87 | int i; 88 | va_list vl; 89 | ArgType_t arg; 90 | va_start(vl, argCount); 91 | 92 | SDTProbeList_t *probeList = (SDTProbeList_t *) calloc(sizeof(SDTProbeList_t), 1); 93 | probeList->probe._fire = NULL; 94 | 95 | probeList->probe.name = (char *) calloc(sizeof(char), strlen(name) + 1); 96 | memcpy(probeList->probe.name, name, sizeof(char) * strlen(name) + 1); 97 | 98 | probeList->next = provider->probes; 99 | provider->probes = probeList; 100 | 101 | probeList->probe.argCount = argCount; 102 | 103 | for(i=0; i < argCount; i++) { 104 | arg = va_arg(vl, ArgType_t); 105 | probeList->probe.argFmt[i] = arg; 106 | } 107 | 108 | for(; iprobe.argFmt[i] = noarg; 110 | } 111 | 112 | probeList->probe.provider = provider; 113 | 114 | return &(probeList->probe); 115 | } 116 | 117 | static char *tempElfPath(int *fd, const char *name, const int use_memfd) { 118 | char *filename = NULL; 119 | #ifdef HAVE_LIBSTAPSDT_MEMORY_BACKED_FD 120 | if(use_memfd == memfd_enabled) { 121 | char path_buffer[PATH_MAX]; 122 | snprintf(path_buffer, sizeof(path_buffer), "libstapsdt:%s", name); 123 | 124 | *fd = memfd_create(path_buffer, F_SEAL_SEAL); 125 | if (*fd < 0) { 126 | return NULL; 127 | } 128 | 129 | snprintf(path_buffer, sizeof(path_buffer), "/proc/%d/fd/%d", getpid(), *fd); 130 | filename = calloc(sizeof(char), strlen(path_buffer) + 1); 131 | strncpy(filename, path_buffer, strlen(path_buffer) + 1); 132 | 133 | return filename; 134 | } 135 | #endif 136 | filename = calloc(sizeof(char), strlen("/tmp/-XXXXXX.so") + strlen(name) + 1); 137 | 138 | sprintf(filename, "/tmp/%s-XXXXXX.so", name); 139 | 140 | if ((*fd = mkstemps(filename, 3)) < 0) { 141 | free(filename); 142 | return NULL; 143 | } 144 | return filename; 145 | } 146 | 147 | int providerLoad(SDTProvider_t *provider) { 148 | int fd; 149 | void *fireProbe; 150 | char *error; 151 | 152 | provider->_filename = tempElfPath(&fd, provider->name, provider->_use_memfd); 153 | if(provider->_use_memfd == memfd_enabled) { 154 | provider->_memfd = fd; 155 | } 156 | if (provider->_filename == NULL) { 157 | sdtSetError(provider, tmpCreationError); 158 | return -1; 159 | } 160 | 161 | if(createSharedLibrary(fd, provider) != 0) { 162 | (void)close(fd); 163 | return -1; 164 | } 165 | 166 | if (provider->_memfd == -1) { 167 | (void)close(fd); 168 | } 169 | 170 | provider->_handle = dlopen(provider->_filename, RTLD_LAZY); 171 | if (!provider->_handle) { 172 | sdtSetError(provider, sharedLibraryOpenError, provider->_filename, 173 | dlerror()); 174 | return -1; 175 | } 176 | 177 | for(SDTProbeList_t *node=provider->probes; node != NULL; node = node->next) { 178 | fireProbe = dlsym(provider->_handle, node->probe.name); 179 | 180 | // TODO (mmarchini) handle errors better when a symbol fails to load 181 | if ((error = dlerror()) != NULL) { 182 | sdtSetError(provider, sharedLibraryOpenError, provider->_filename, 183 | node->probe.name, error); 184 | return -1; 185 | } 186 | 187 | node->probe._fire = fireProbe; 188 | } 189 | 190 | 191 | return 0; 192 | } 193 | 194 | int providerUnload(SDTProvider_t *provider) { 195 | if(provider->_handle == NULL) { 196 | return 0; 197 | } 198 | if(dlclose(provider->_handle) != 0) { 199 | sdtSetError(provider, sharedLibraryCloseError, provider->_filename, provider->name, dlerror()); 200 | return -1; 201 | } 202 | provider->_handle = NULL; 203 | 204 | for(SDTProbeList_t *node=provider->probes; node != NULL; node = node->next) { 205 | node->probe._fire = NULL; 206 | } 207 | 208 | if (provider->_memfd > 0) { 209 | (void)close(provider->_memfd); 210 | provider->_memfd = -1; 211 | } else { 212 | unlink(provider->_filename); 213 | } 214 | free(provider->_filename); 215 | 216 | return 0; 217 | } 218 | 219 | void probeFire(SDTProbe_t *probe, ...) { 220 | if(probe->_fire == NULL) { 221 | return; 222 | } 223 | va_list vl; 224 | va_start(vl, probe); 225 | uint64_t arg[6] = {0}; 226 | for(int i=0; i < probe->argCount; i++) { 227 | arg[i] = va_arg(vl, uint64_t); 228 | } 229 | 230 | switch(probe->argCount) { 231 | case 0: 232 | ((void (*)())probe->_fire) (); 233 | return; 234 | case 1: 235 | ((void (*)())probe->_fire) (arg[0]); 236 | return; 237 | case 2: 238 | ((void (*)())probe->_fire) (arg[0], arg[1]); 239 | return; 240 | case 3: 241 | ((void (*)())probe->_fire) (arg[0], arg[1], arg[2]); 242 | return; 243 | case 4: 244 | ((void (*)())probe->_fire) (arg[0], arg[1], arg[2], arg[3]); 245 | return; 246 | case 5: 247 | ((void (*)())probe->_fire) (arg[0], arg[1], arg[2], arg[3], arg[4]); 248 | return; 249 | case 6: 250 | ((void (*)())probe->_fire) (arg[0], arg[1], arg[2], arg[3], arg[4], arg[5]); 251 | return; 252 | default: 253 | ((void (*)())probe->_fire) (); 254 | return; 255 | } 256 | } 257 | 258 | int probeIsEnabled(SDTProbe_t *probe) { 259 | if(probe->_fire == NULL) { 260 | return 0; 261 | } 262 | if(((*(char *)probe->_fire) & 0x90) == 0x90) { 263 | return 0; 264 | } 265 | return 1; 266 | } 267 | 268 | void providerDestroy(SDTProvider_t *provider) { 269 | SDTProbeList_t *node=NULL, *next=NULL; 270 | 271 | for(node=provider->probes; node!=NULL; node=next) { 272 | free(node->probe.name); 273 | next=node->next; 274 | free(node); 275 | } 276 | free(provider->name); 277 | if(provider->error != NULL) { 278 | free(provider->error); 279 | } 280 | free(provider); 281 | } 282 | -------------------------------------------------------------------------------- /src/libstapsdt.h: -------------------------------------------------------------------------------- 1 | #ifndef _LIBSTAPSDT_H 2 | #define _LIBSTAPSDT_H 3 | #define MAX_ARGUMENTS 6 4 | 5 | typedef enum { 6 | noError = -1, 7 | elfCreationError = 0, 8 | tmpCreationError = 1, 9 | sharedLibraryOpenError = 2, 10 | symbolLoadingError = 3, 11 | sharedLibraryCloseError = 4, 12 | } SDTError_t; 13 | 14 | typedef enum { 15 | noarg = 0, 16 | uint8 = 1, 17 | int8 = -1, 18 | uint16 = 2, 19 | int16 = -2, 20 | uint32 = 4, 21 | int32 = -4, 22 | uint64 = 8, 23 | int64 = -8, 24 | } ArgType_t; 25 | 26 | typedef enum { 27 | memfd_disabled = 0, 28 | memfd_enabled = 1, 29 | } MemFDOption_t; 30 | 31 | struct SDTProvider; 32 | 33 | typedef struct SDTProbe { 34 | char *name; 35 | ArgType_t argFmt[MAX_ARGUMENTS]; 36 | void *_fire; 37 | struct SDTProvider *provider; 38 | int argCount; 39 | } SDTProbe_t; 40 | 41 | typedef struct SDTProbeList_ { 42 | SDTProbe_t probe; 43 | struct SDTProbeList_ *next; 44 | } SDTProbeList_t; 45 | 46 | typedef struct SDTProvider { 47 | char *name; 48 | SDTProbeList_t *probes; 49 | SDTError_t errno; 50 | char *error; 51 | 52 | // private 53 | void *_handle; 54 | char *_filename; 55 | int _memfd; 56 | MemFDOption_t _use_memfd; 57 | } SDTProvider_t; 58 | 59 | SDTProvider_t *providerInit(const char *name); 60 | 61 | /* 62 | Linux newer than 3.17 with libc that supports the syscall it will default to 63 | using a memory-backed file descriptor. This behavior can be overridden at 64 | runtime by calling this with use_memfd = memfd_disabled prior after providerInit, and before 65 | providerLoad. 66 | */ 67 | int providerUseMemfd(SDTProvider_t *provider, const MemFDOption_t use_memfd); 68 | 69 | SDTProbe_t *providerAddProbe(SDTProvider_t *provider, const char *name, int argCount, ...); 70 | 71 | int providerLoad(SDTProvider_t *provider); 72 | 73 | int providerUnload(SDTProvider_t *provider); 74 | 75 | void providerDestroy(SDTProvider_t *provider); 76 | 77 | void probeFire(SDTProbe_t *probe, ...); 78 | 79 | int probeIsEnabled(SDTProbe_t *probe); 80 | 81 | #endif 82 | -------------------------------------------------------------------------------- /src/sdtnote.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "sdtnote.h" 6 | #include "util.h" 7 | 8 | 9 | // TODO (mmarchini) add other architectures (this only works for x86_64) 10 | char *regMap(int idx) { 11 | switch (idx) { 12 | case 0: 13 | return "rdi"; 14 | break; 15 | case 1: 16 | return "rsi"; 17 | break; 18 | case 2: 19 | return "rdx"; 20 | break; 21 | case 3: 22 | return "rcx"; 23 | break; 24 | case 4: 25 | return "r8"; 26 | break; 27 | case 5: 28 | return "r9"; 29 | break; 30 | default: 31 | return NULL; 32 | } 33 | } 34 | 35 | size_t sdtNoteSize(SDTNote *sdt) { 36 | size_t size = 0; 37 | size += sizeof(sdt->header); 38 | size += sdt->header.n_namesz; 39 | size += sdt->header.n_descsz; 40 | 41 | size = roundUp(size, 4); 42 | 43 | return size; 44 | } 45 | 46 | SDTNote *sdtNoteInit(SDTProbe_t *probe) { 47 | char buf[100]; 48 | SDTNote *sdt = calloc(sizeof(SDTNote), 1); 49 | size_t descsz = 0, providersz = strlen(probe->provider->name) + 1, 50 | probesz = strlen(probe->name) + 1; 51 | sdt->header.n_type = NT_STAPSDT; 52 | sdt->header.n_namesz = sizeof(NT_STAPSDT_NAME); 53 | 54 | // TODO(mmarchini): should add pad if sizeof(NT_STAPSDT)%4 != 0 55 | sdt->name = calloc(sizeof(NT_STAPSDT_NAME), 1); 56 | strncpy(sdt->name, NT_STAPSDT_NAME, strlen(NT_STAPSDT_NAME) + 1); 57 | 58 | sdt->content.probePC = -1; 59 | descsz += sizeof(sdt->content.probePC); 60 | sdt->content.base_addr = -1; 61 | descsz += sizeof(sdt->content.base_addr); 62 | sdt->content.sem_addr = 0; 63 | descsz += sizeof(sdt->content.sem_addr); 64 | 65 | sdt->content.provider = calloc(providersz, 1); 66 | descsz += providersz; 67 | strncpy(sdt->content.provider, probe->provider->name, providersz); 68 | 69 | sdt->content.probe = calloc(probesz, 1); 70 | descsz += probesz; 71 | strncpy(sdt->content.probe, probe->name, probesz); 72 | 73 | sdt->content.argFmt = calloc(sizeof(char), 1); 74 | sdt->content.argFmt[0] = '\0'; 75 | for(int i=0; i < probe->argCount; i++) { 76 | sprintf(buf, "%d@%%%s", probe->argFmt[i], regMap(i)); 77 | 78 | 79 | if(i==0) { 80 | sdt->content.argFmt = realloc(sdt->content.argFmt, strlen(sdt->content.argFmt) + strlen(buf) + 1); 81 | sprintf(sdt->content.argFmt, "%s", buf); 82 | } else { 83 | sdt->content.argFmt = realloc(sdt->content.argFmt, strlen(sdt->content.argFmt) + strlen(buf) + 2); 84 | sprintf(&(sdt->content.argFmt[strlen(sdt->content.argFmt)]), " %s", buf); 85 | } 86 | } 87 | descsz += strlen(sdt->content.argFmt) + 1; 88 | 89 | sdt->header.n_descsz = descsz; 90 | 91 | return sdt; 92 | } 93 | 94 | int sdtNoteToBuffer(SDTNote *sdt, char *buffer) { 95 | int cur = 0; 96 | size_t sdtSize = sdtNoteSize(sdt); 97 | 98 | // Header 99 | memcpy(&(buffer[cur]), &(sdt->header), sizeof(sdt->header)); 100 | cur += sizeof(sdt->header); 101 | 102 | // Name 103 | memcpy(&(buffer[cur]), sdt->name, sdt->header.n_namesz); 104 | cur += sdt->header.n_namesz; 105 | 106 | // Content 107 | memcpy(&(buffer[cur]), &(sdt->content.probePC), sizeof(sdt->content.probePC)); 108 | cur += sizeof(sdt->content.probePC); 109 | 110 | memcpy(&(buffer[cur]), &(sdt->content.base_addr), 111 | sizeof(sdt->content.base_addr)); 112 | cur += sizeof(sdt->content.base_addr); 113 | 114 | memcpy(&(buffer[cur]), &(sdt->content.sem_addr), 115 | sizeof(sdt->content.sem_addr)); 116 | cur += sizeof(sdt->content.sem_addr); 117 | 118 | memcpy(&(buffer[cur]), sdt->content.provider, 119 | strlen(sdt->content.provider) + 1); 120 | cur += strlen(sdt->content.provider) + 1; 121 | 122 | memcpy(&(buffer[cur]), sdt->content.probe, strlen(sdt->content.probe) + 1); 123 | cur += strlen(sdt->content.probe) + 1; 124 | 125 | memcpy(&(buffer[cur]), sdt->content.argFmt, strlen(sdt->content.argFmt) + 1); 126 | cur += strlen(sdt->content.argFmt) + 1; 127 | 128 | if (cur < sdtSize) { 129 | memset(&(buffer[cur]), 0, sdtSize - cur); 130 | } 131 | 132 | return sdtSize; 133 | } 134 | 135 | void sdtNoteFree(SDTNote *sdtNote) { 136 | free(sdtNote->name); 137 | free(sdtNote->content.provider); 138 | free(sdtNote->content.probe); 139 | free(sdtNote->content.argFmt); 140 | 141 | free(sdtNote); 142 | } 143 | 144 | SDTNoteList_t *sdtNoteListAppend(SDTNoteList_t *list, SDTNote *note) { 145 | SDTNoteList_t *newNode = calloc(sizeof(SDTNoteList_t), 1); 146 | newNode->next = list; 147 | newNode->note = note; 148 | 149 | return newNode; 150 | } 151 | 152 | size_t sdtNoteListSize(SDTNoteList_t *list) { 153 | size_t size = 0; 154 | for(SDTNoteList_t *node=list; node!=NULL; node=node->next) { 155 | size += sdtNoteSize(node->note); 156 | } 157 | 158 | return size; 159 | } 160 | 161 | size_t sdtNoteListToBuffer(SDTNoteList_t *list, char *buffer) { 162 | size_t offset = 0; 163 | for(SDTNoteList_t *node=list; node!=NULL; node=node->next) { 164 | offset += sdtNoteToBuffer(node->note, &(buffer[offset])); 165 | } 166 | return offset; 167 | } 168 | 169 | void sdtNoteListFree(SDTNoteList_t *list) { 170 | SDTNoteList_t *node, *next; 171 | for(node=list; node!=NULL; node=next) { 172 | sdtNoteFree(node->note); 173 | next = node->next; 174 | free(node); 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /src/sdtnote.h: -------------------------------------------------------------------------------- 1 | #ifndef _SDT_NOTE_H 2 | #define _SDT_NOTE_H 3 | 4 | #include 5 | #include "libstapsdt.h" 6 | 7 | #define NT_STAPSDT 3 8 | #define NT_STAPSDT_NAME "stapsdt" 9 | 10 | typedef struct SDTNote_ { 11 | // Header 12 | Elf64_Nhdr header; 13 | // Note name 14 | char *name; 15 | struct { 16 | // Note description 17 | Elf64_Xword probePC; 18 | Elf64_Xword base_addr; 19 | Elf64_Xword sem_addr; 20 | char *provider; // mainer 21 | char *probe; // 22 | char *argFmt; // \0 23 | } content; 24 | unsigned long long textSectionOffset; 25 | } SDTNote; 26 | 27 | typedef struct SDTNoteList_ { 28 | SDTNote *note; 29 | struct SDTNoteList_ *next; 30 | } SDTNoteList_t; 31 | 32 | size_t sdtNoteSize(SDTNote *sdt); 33 | 34 | SDTNote *sdtNoteInit(SDTProbe_t *probe); 35 | 36 | void sdtNoteFree(SDTNote *sdtNote); 37 | 38 | SDTNoteList_t *sdtNoteListAppend(SDTNoteList_t *list, SDTNote *note); 39 | 40 | size_t sdtNoteListSize(SDTNoteList_t *list); 41 | 42 | size_t sdtNoteListToBuffer(SDTNoteList_t *list, char *buffer); 43 | 44 | void sdtNoteListFree(SDTNoteList_t *list); 45 | 46 | #endif 47 | -------------------------------------------------------------------------------- /src/section.c: -------------------------------------------------------------------------------- 1 | #include "section.h" 2 | 3 | Section *sectionInit(Elf *e, StringTable *table, char *name) { 4 | Section *section = calloc(sizeof(Section), 1); 5 | 6 | section->string = stringTableAdd(table, name); 7 | 8 | if ((section->scn = elf_newscn(e)) == NULL) { 9 | free(section); 10 | return NULL; 11 | } 12 | 13 | if ((section->data = elf_newdata(section->scn)) == NULL) { 14 | free(section); 15 | return NULL; 16 | } 17 | 18 | if ((section->shdr = elf64_getshdr(section->scn)) == NULL) { 19 | free(section); 20 | return NULL; 21 | } 22 | 23 | return section; 24 | } 25 | 26 | 27 | void sectionFree(Section *section) { 28 | // Fields are just references, shouldn't be freed here 29 | free(section); 30 | } 31 | -------------------------------------------------------------------------------- /src/section.h: -------------------------------------------------------------------------------- 1 | #ifndef _SECTION_H 2 | #define _SECTION_H 3 | 4 | #include 5 | 6 | #include "string-table.h" 7 | 8 | typedef struct { 9 | Elf_Scn *scn; 10 | Elf64_Shdr *shdr; 11 | Elf_Data *data; 12 | Elf64_Addr offset; 13 | 14 | StringTableNode *string; 15 | } Section; 16 | 17 | Section *sectionInit(Elf *e, StringTable *table, char *name); 18 | 19 | void sectionFree(Section *section); 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /src/shared-lib.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "shared-lib.h" 3 | #include "hash-table.h" 4 | 5 | #define PHDR_ALIGN 0x200000 6 | 7 | void _funcStart(); 8 | void _funcEnd(); 9 | 10 | uint32_t eh_frame[] = {0x0, 0x0}; 11 | 12 | Elf *createElf(int fd) { 13 | Elf *e; 14 | 15 | if (elf_version(EV_CURRENT) == EV_NONE) { 16 | // TODO (mmarchini) error message 17 | return NULL; 18 | } 19 | 20 | if ((e = elf_begin(fd, ELF_C_WRITE, NULL)) == NULL) { 21 | // TODO (mmarchini) error message 22 | return NULL; 23 | } 24 | 25 | return e; 26 | } 27 | 28 | int createElfStringTables(DynElf *dynElf) { 29 | // FIXME (mmarchini) error handling 30 | dynElf->stringTable = stringTableInit(); 31 | dynElf->dynamicString = stringTableInit(); 32 | dynElf->dynamicSymbols = dynamicSymbolTableInit(dynElf->dynamicString); 33 | return 0; 34 | } 35 | 36 | Elf64_Ehdr *createElfHeader(Elf *elf) { 37 | Elf64_Ehdr *ehdr; 38 | if ((ehdr = elf64_newehdr(elf)) == NULL) { 39 | // FIXME (mmarchini) properly free everything 40 | return NULL; 41 | } 42 | 43 | ehdr->e_ident[EI_DATA] = ELFDATA2LSB; 44 | ehdr->e_type = ET_DYN; 45 | ehdr->e_machine = EM_X86_64; 46 | ehdr->e_version = EV_CURRENT; 47 | ehdr->e_flags = 0; 48 | 49 | return ehdr; 50 | } 51 | 52 | int createElfProgramHeaders(DynElf *dynElf) { 53 | if ((dynElf->phdrLoad1 = elf64_newphdr(dynElf->elf, 4)) == NULL) { 54 | // TODO (mmarchini) error message 55 | // FIXME (mmarchini) properly free everything 56 | return -1; 57 | } 58 | 59 | dynElf->phdrDyn = &dynElf->phdrLoad1[2]; 60 | dynElf->phdrLoad2 = &dynElf->phdrLoad1[1]; 61 | dynElf->phdrStack = &dynElf->phdrLoad1[3]; 62 | 63 | return 0; 64 | } 65 | 66 | int createElfSections(DynElf *dynElf) { 67 | // FIXME (mmarchini) error message 68 | dynElf->sections.shStrTab = sectionInit(dynElf->elf, dynElf->stringTable, ".shstrtab"); 69 | dynElf->sections.hash = sectionInit(dynElf->elf, dynElf->stringTable, ".hash"); 70 | dynElf->sections.dynSym = sectionInit(dynElf->elf, dynElf->stringTable, ".dynsym"); 71 | dynElf->sections.dynStr = sectionInit(dynElf->elf, dynElf->stringTable, ".dynstr"); 72 | dynElf->sections.text = sectionInit(dynElf->elf, dynElf->stringTable, ".text"); 73 | dynElf->sections.ehFrame = sectionInit(dynElf->elf, dynElf->stringTable, ".eh_frame"); 74 | dynElf->sections.dynamic = sectionInit(dynElf->elf, dynElf->stringTable, ".dynamic"); 75 | dynElf->sections.sdtBase = sectionInit(dynElf->elf, dynElf->stringTable, ".stapsdt.base"); 76 | dynElf->sections.sdtNote = sectionInit(dynElf->elf, dynElf->stringTable, ".note.stapsdt"); 77 | 78 | return 0; 79 | } 80 | 81 | void *createDynSymData(DynamicSymbolTable *table) { 82 | size_t symbolsCount = (5 + table->count); 83 | int i; 84 | DynamicSymbolList *current; 85 | Elf64_Sym *dynsyms = calloc(sizeof(Elf64_Sym), (5 + table->count)); 86 | 87 | for (i = 0; i < symbolsCount; i++) { 88 | dynsyms[i].st_name = 0; 89 | dynsyms[i].st_info = 0; 90 | dynsyms[i].st_other = 0; 91 | dynsyms[i].st_shndx = 0; 92 | dynsyms[i].st_value = 0; 93 | dynsyms[i].st_size = 0; 94 | } 95 | 96 | dynsyms[1].st_info = ELF64_ST_INFO(STB_LOCAL, STT_SECTION); 97 | 98 | current = table->symbols; 99 | for (i = 0; i < table->count; i++) { 100 | dynsyms[i + 2].st_name = current->symbol.string->index; 101 | dynsyms[i + 2].st_info = ELF64_ST_INFO(STB_GLOBAL, STT_NOTYPE); 102 | current = current->next; 103 | } 104 | i -= 1; 105 | 106 | dynsyms[i + 3].st_name = table->bssStart.string->index; 107 | dynsyms[i + 3].st_info = ELF64_ST_INFO(STB_GLOBAL, STT_NOTYPE); 108 | 109 | dynsyms[i + 4].st_name = table->eData.string->index; 110 | dynsyms[i + 4].st_info = ELF64_ST_INFO(STB_GLOBAL, STT_NOTYPE); 111 | 112 | dynsyms[i + 5].st_name = table->end.string->index; 113 | dynsyms[i + 5].st_info = ELF64_ST_INFO(STB_GLOBAL, STT_NOTYPE); 114 | 115 | return dynsyms; 116 | } 117 | 118 | // TODO(mmarchini): dynamic strings creation 119 | void *createDynamicData() { 120 | Elf64_Dyn *dyns = calloc(sizeof(Elf64_Dyn), 11); 121 | 122 | for (int i = 0; i < 11; i++) { 123 | dyns[i].d_tag = DT_NULL; 124 | } 125 | 126 | dyns[0].d_tag = DT_HASH; 127 | dyns[1].d_tag = DT_STRTAB; 128 | dyns[2].d_tag = DT_SYMTAB; 129 | dyns[3].d_tag = DT_STRSZ; 130 | 131 | return dyns; 132 | } 133 | 134 | DynElf *dynElfInit(int fd) { 135 | DynElf *dynElf = (DynElf *)calloc(sizeof(DynElf), 1); 136 | dynElf->sdtNotes = NULL; 137 | dynElf->sdtNotesCount = 0; 138 | 139 | if(createElfStringTables(dynElf) == -1) { 140 | // TODO (mmarchini) error message 141 | // FIXME (mmarchini) properly free everything 142 | free(dynElf); 143 | return NULL; 144 | } 145 | 146 | dynElf->elf = createElf(fd); 147 | 148 | if ((dynElf->ehdr = createElfHeader(dynElf->elf)) == NULL) { 149 | // TODO (mmarchini) error message 150 | // FIXME (mmarchini) properly free everything 151 | free(dynElf); 152 | return NULL; 153 | } 154 | 155 | if(createElfProgramHeaders(dynElf) == -1) { 156 | // TODO (mmarchini) error message 157 | // FIXME (mmarchini) properly free everything 158 | free(dynElf); 159 | return NULL; 160 | } 161 | 162 | if(createElfSections(dynElf) == -1) { 163 | // TODO (mmarchini) error message 164 | // FIXME (mmarchini) properly free everything 165 | free(dynElf); 166 | return NULL; 167 | } 168 | 169 | return dynElf; 170 | } 171 | 172 | int dynElfAddProbe(DynElf *dynElf, SDTProbe_t *probe) { 173 | dynElf->sdtNotes = sdtNoteListAppend(dynElf->sdtNotes, sdtNoteInit(probe)); 174 | dynamicSymbolTableAdd(dynElf->dynamicSymbols, probe->name); 175 | dynElf->sdtNotesCount++; 176 | 177 | return 0; 178 | } 179 | 180 | size_t prepareTextData(DynElf *dynElf, char **textData) { 181 | size_t funcSize = (unsigned long long)_funcEnd - (unsigned long long)_funcStart; 182 | unsigned long long offset=0; 183 | *textData = calloc(funcSize, dynElf->sdtNotesCount); 184 | 185 | for(SDTNoteList_t *node=dynElf->sdtNotes; node!=NULL; node=node->next) { 186 | node->note->textSectionOffset = offset; 187 | memcpy(&((*textData)[offset]), _funcStart, funcSize); 188 | offset += funcSize; 189 | } 190 | 191 | return offset; 192 | } 193 | 194 | // TODO (mmarchini) refactor (no idea how) 195 | int dynElfSave(DynElf *dynElf) { 196 | Elf64_Sym *dynSymData = createDynSymData(dynElf->dynamicSymbols); 197 | Elf64_Dyn *dynamicData = createDynamicData(); 198 | void *sdtNoteData = calloc(sdtNoteListSize(dynElf->sdtNotes), 1); 199 | void *stringTableData = stringTableToBuffer(dynElf->stringTable), 200 | *dynamicStringData = stringTableToBuffer(dynElf->dynamicString); 201 | void *textData = NULL; 202 | uint32_t *hashTable; 203 | size_t hashTableSize = hashTableFromSymbolTable(dynElf->dynamicSymbols, &hashTable); 204 | int i; 205 | 206 | // ----------------------------------------------------------------------- // 207 | // Section: HASH 208 | 209 | dynElf->sections.hash->data->d_align = 8; 210 | dynElf->sections.hash->data->d_off = 0LL; 211 | dynElf->sections.hash->data->d_buf = hashTable; 212 | dynElf->sections.hash->data->d_type = ELF_T_XWORD; 213 | dynElf->sections.hash->data->d_size = hashTableSize; 214 | dynElf->sections.hash->data->d_version = EV_CURRENT; 215 | 216 | dynElf->sections.hash->shdr->sh_name = dynElf->sections.hash->string->index; 217 | dynElf->sections.hash->shdr->sh_type = SHT_HASH; 218 | dynElf->sections.hash->shdr->sh_flags = SHF_ALLOC; 219 | 220 | // ----------------------------------------------------------------------- // 221 | // Section: Dynsym 222 | 223 | dynElf->sections.dynSym->data->d_align = 8; 224 | dynElf->sections.dynSym->data->d_off = 0LL; 225 | dynElf->sections.dynSym->data->d_buf = dynSymData; 226 | dynElf->sections.dynSym->data->d_type = ELF_T_XWORD; 227 | dynElf->sections.dynSym->data->d_size = sizeof(Elf64_Sym) * ((5 + dynElf->dynamicSymbols->count)); 228 | dynElf->sections.dynSym->data->d_version = EV_CURRENT; 229 | 230 | dynElf->sections.dynSym->shdr->sh_name = dynElf->sections.dynSym->string->index; 231 | dynElf->sections.dynSym->shdr->sh_type = SHT_DYNSYM; 232 | dynElf->sections.dynSym->shdr->sh_flags = SHF_ALLOC; 233 | dynElf->sections.dynSym->shdr->sh_info = 2; // First non local symbol 234 | 235 | dynElf->sections.hash->shdr->sh_link = elf_ndxscn(dynElf->sections.dynSym->scn); 236 | 237 | // ----------------------------------------------------------------------- // 238 | // Section: DYNSTR 239 | 240 | dynElf->sections.dynStr->data->d_align = 1; 241 | dynElf->sections.dynStr->data->d_off = 0LL; 242 | dynElf->sections.dynStr->data->d_buf = dynamicStringData; 243 | 244 | dynElf->sections.dynStr->data->d_type = ELF_T_BYTE; 245 | dynElf->sections.dynStr->data->d_size = dynElf->dynamicString->size; 246 | dynElf->sections.dynStr->data->d_version = EV_CURRENT; 247 | 248 | dynElf->sections.dynStr->shdr->sh_name = dynElf->sections.dynStr->string->index; 249 | dynElf->sections.dynStr->shdr->sh_type = SHT_STRTAB; 250 | dynElf->sections.dynStr->shdr->sh_flags = SHF_ALLOC; 251 | 252 | dynElf->sections.dynSym->shdr->sh_link = elf_ndxscn(dynElf->sections.dynStr->scn); 253 | 254 | // ----------------------------------------------------------------------- // 255 | // Section: TEXT 256 | 257 | dynElf->sections.text->data->d_align = 16; 258 | dynElf->sections.text->data->d_off = 0LL; 259 | dynElf->sections.text->data->d_size = prepareTextData(dynElf, (char **) &textData); 260 | dynElf->sections.text->data->d_buf = textData; 261 | dynElf->sections.text->data->d_type = ELF_T_BYTE; 262 | dynElf->sections.text->data->d_version = EV_CURRENT; 263 | 264 | dynElf->sections.text->shdr->sh_name = dynElf->sections.text->string->index; 265 | dynElf->sections.text->shdr->sh_type = SHT_PROGBITS; 266 | dynElf->sections.text->shdr->sh_flags = SHF_ALLOC | SHF_EXECINSTR; 267 | 268 | // ----------------------------------------------------------------------- // 269 | // Section: SDT BASE 270 | 271 | dynElf->sections.sdtBase->data->d_align = 1; 272 | dynElf->sections.sdtBase->data->d_off = 0LL; 273 | dynElf->sections.sdtBase->data->d_buf = eh_frame; 274 | dynElf->sections.sdtBase->data->d_type = ELF_T_BYTE; 275 | dynElf->sections.sdtBase->data->d_size = 1; 276 | dynElf->sections.sdtBase->data->d_version = EV_CURRENT; 277 | 278 | dynElf->sections.sdtBase->shdr->sh_name = dynElf->sections.sdtBase->string->index; 279 | dynElf->sections.sdtBase->shdr->sh_type = SHT_PROGBITS; 280 | dynElf->sections.sdtBase->shdr->sh_flags = SHF_ALLOC; 281 | 282 | // ----------------------------------------------------------------------- // 283 | // Section: EH_FRAME 284 | 285 | dynElf->sections.ehFrame->data->d_align = 8; 286 | dynElf->sections.ehFrame->data->d_off = 0LL; 287 | dynElf->sections.ehFrame->data->d_buf = eh_frame; 288 | dynElf->sections.ehFrame->data->d_type = ELF_T_BYTE; 289 | dynElf->sections.ehFrame->data->d_size = 0; 290 | dynElf->sections.ehFrame->data->d_version = EV_CURRENT; 291 | 292 | dynElf->sections.ehFrame->shdr->sh_name = dynElf->sections.ehFrame->string->index; 293 | dynElf->sections.ehFrame->shdr->sh_type = SHT_PROGBITS; 294 | dynElf->sections.ehFrame->shdr->sh_flags = SHF_ALLOC; 295 | 296 | // ----------------------------------------------------------------------- // 297 | // Section: DYNAMIC 298 | 299 | dynElf->sections.dynamic->data->d_align = 8; 300 | dynElf->sections.dynamic->data->d_off = 0LL; 301 | dynElf->sections.dynamic->data->d_buf = dynamicData; 302 | dynElf->sections.dynamic->data->d_type = ELF_T_BYTE; 303 | dynElf->sections.dynamic->data->d_size = 11 * sizeof(Elf64_Dyn); 304 | dynElf->sections.dynamic->data->d_version = EV_CURRENT; 305 | 306 | dynElf->sections.dynamic->shdr->sh_name = dynElf->sections.dynamic->string->index; 307 | dynElf->sections.dynamic->shdr->sh_type = SHT_DYNAMIC; 308 | dynElf->sections.dynamic->shdr->sh_flags = SHF_WRITE | SHF_ALLOC; 309 | dynElf->sections.dynamic->shdr->sh_link = elf_ndxscn(dynElf->sections.dynStr->scn); 310 | 311 | // ----------------------------------------------------------------------- // 312 | // Section: SDT_NOTE 313 | 314 | dynElf->sections.sdtNote->data->d_align = 4; 315 | dynElf->sections.sdtNote->data->d_off = 0LL; 316 | dynElf->sections.sdtNote->data->d_buf = sdtNoteData; 317 | dynElf->sections.sdtNote->data->d_type = ELF_T_NHDR; 318 | dynElf->sections.sdtNote->data->d_size = sdtNoteListSize(dynElf->sdtNotes); 319 | dynElf->sections.sdtNote->data->d_version = EV_CURRENT; 320 | 321 | dynElf->sections.sdtNote->shdr->sh_name = dynElf->sections.sdtNote->string->index; 322 | dynElf->sections.sdtNote->shdr->sh_type = SHT_NOTE; 323 | dynElf->sections.sdtNote->shdr->sh_flags = 0; 324 | 325 | // ----------------------------------------------------------------------- // 326 | // Section: SHSTRTAB 327 | 328 | dynElf->sections.shStrTab->data->d_align = 1; 329 | dynElf->sections.shStrTab->data->d_off = 0LL; 330 | dynElf->sections.shStrTab->data->d_buf = stringTableData; 331 | dynElf->sections.shStrTab->data->d_type = ELF_T_BYTE; 332 | dynElf->sections.shStrTab->data->d_size = dynElf->stringTable->size; 333 | dynElf->sections.shStrTab->data->d_version = EV_CURRENT; 334 | 335 | dynElf->sections.shStrTab->shdr->sh_name = dynElf->sections.shStrTab->string->index; 336 | dynElf->sections.shStrTab->shdr->sh_type = SHT_STRTAB; 337 | dynElf->sections.shStrTab->shdr->sh_flags = 0; 338 | 339 | dynElf->ehdr->e_shstrndx = elf_ndxscn(dynElf->sections.shStrTab->scn); 340 | 341 | // ----------------------------------------------------------------------- // 342 | 343 | if (elf_update(dynElf->elf, ELF_C_NULL) < 0) { 344 | return -1; 345 | } 346 | 347 | dynElf->sections.hash->shdr->sh_addr = dynElf->sections.hash->shdr->sh_offset; 348 | dynElf->sections.hash->offset = dynElf->sections.hash->shdr->sh_offset; 349 | 350 | // -- // 351 | 352 | dynElf->sections.dynSym->shdr->sh_addr = dynElf->sections.dynSym->shdr->sh_offset; 353 | dynElf->sections.dynSym->offset = dynElf->sections.dynSym->shdr->sh_offset; 354 | 355 | // -- // 356 | 357 | dynElf->sections.dynStr->shdr->sh_addr = dynElf->sections.dynStr->shdr->sh_offset; 358 | dynElf->sections.dynStr->offset = dynElf->sections.dynStr->shdr->sh_offset; 359 | 360 | // -- // 361 | 362 | dynElf->sections.text->shdr->sh_addr = dynElf->sections.text->shdr->sh_offset; 363 | dynElf->ehdr->e_entry = dynElf->sections.text->shdr->sh_addr; 364 | dynElf->sections.text->offset = dynElf->sections.text->shdr->sh_offset; 365 | 366 | // -- // 367 | 368 | dynElf->sections.sdtBase->shdr->sh_addr = dynElf->sections.sdtBase->shdr->sh_offset; 369 | dynElf->sections.sdtBase->offset = dynElf->sections.sdtBase->shdr->sh_offset; 370 | 371 | // -- // 372 | 373 | dynElf->sections.ehFrame->shdr->sh_addr = dynElf->sections.ehFrame->shdr->sh_offset; 374 | dynElf->sections.ehFrame->offset = dynElf->sections.ehFrame->shdr->sh_offset; 375 | 376 | // -- // 377 | 378 | dynElf->sections.dynamic->shdr->sh_addr = PHDR_ALIGN + dynElf->sections.dynamic->shdr->sh_offset; 379 | dynElf->sections.dynamic->offset = dynElf->sections.dynamic->shdr->sh_offset; 380 | 381 | // -- // 382 | 383 | dynElf->sections.sdtNote->shdr->sh_addr = dynElf->sections.sdtNote->shdr->sh_offset; 384 | dynElf->sections.sdtNote->offset = dynElf->sections.sdtNote->shdr->sh_offset; 385 | 386 | for(SDTNoteList_t *node=dynElf->sdtNotes; node != NULL; node = node->next) { 387 | node->note->content.probePC = dynElf->sections.text->offset + node->note->textSectionOffset; 388 | node->note->content.base_addr = dynElf->sections.sdtBase->offset; 389 | } 390 | sdtNoteListToBuffer(dynElf->sdtNotes, sdtNoteData); 391 | 392 | // -- // 393 | 394 | dynElf->sections.shStrTab->offset = dynElf->sections.shStrTab->shdr->sh_offset; 395 | 396 | // -- // 397 | 398 | // ----------------------------------------------------------------------- // 399 | 400 | if (elf_update(dynElf->elf, ELF_C_NULL) < 0) { 401 | return -1; 402 | } 403 | 404 | // ----------------------------------------------------------------------- // 405 | // Fill PHDRs 406 | 407 | // First LOAD PHDR 408 | 409 | dynElf->phdrLoad1->p_type = PT_LOAD; 410 | dynElf->phdrLoad1->p_flags = PF_X + PF_R; 411 | dynElf->phdrLoad1->p_offset = 0; 412 | dynElf->phdrLoad1->p_vaddr = 0; 413 | dynElf->phdrLoad1->p_paddr = 0; 414 | dynElf->phdrLoad1->p_filesz = dynElf->sections.ehFrame->offset; 415 | dynElf->phdrLoad1->p_memsz = dynElf->sections.ehFrame->offset; 416 | dynElf->phdrLoad1->p_align = PHDR_ALIGN; 417 | 418 | // Second LOAD PHDR 419 | 420 | dynElf->phdrLoad2->p_type = PT_LOAD; 421 | dynElf->phdrLoad2->p_flags = PF_W + PF_R; 422 | dynElf->phdrLoad2->p_offset = dynElf->sections.ehFrame->offset; 423 | dynElf->phdrLoad2->p_vaddr = dynElf->sections.ehFrame->offset + PHDR_ALIGN; 424 | dynElf->phdrLoad2->p_paddr = dynElf->sections.ehFrame->offset + PHDR_ALIGN; 425 | dynElf->phdrLoad2->p_filesz = dynElf->sections.dynamic->data->d_size; 426 | dynElf->phdrLoad2->p_memsz = dynElf->sections.dynamic->data->d_size; 427 | dynElf->phdrLoad2->p_align = PHDR_ALIGN; 428 | 429 | // Dynamic PHDR 430 | 431 | dynElf->phdrDyn->p_type = PT_DYNAMIC; 432 | dynElf->phdrDyn->p_flags = PF_W + PF_R; 433 | dynElf->phdrDyn->p_offset = dynElf->sections.ehFrame->offset; 434 | dynElf->phdrDyn->p_vaddr = dynElf->sections.ehFrame->offset + PHDR_ALIGN; 435 | dynElf->phdrDyn->p_paddr = dynElf->sections.ehFrame->offset + PHDR_ALIGN; 436 | dynElf->phdrDyn->p_filesz = dynElf->sections.dynamic->data->d_size; 437 | dynElf->phdrDyn->p_memsz = dynElf->sections.dynamic->data->d_size; 438 | dynElf->phdrDyn->p_align = 0x8; // XXX magic number? 439 | 440 | // GNU_STACK PHDR 441 | dynElf->phdrStack->p_type = PT_GNU_STACK; 442 | dynElf->phdrStack->p_flags = PF_W + PF_R; 443 | dynElf->phdrStack->p_offset = 0; 444 | dynElf->phdrStack->p_vaddr = 0; 445 | dynElf->phdrStack->p_paddr = 0; 446 | dynElf->phdrStack->p_filesz = 0; 447 | dynElf->phdrStack->p_memsz = 0; 448 | dynElf->phdrStack->p_align = 0x10; 449 | 450 | // Fix offsets DynSym 451 | // ----------------------------------------------------------------------- // 452 | 453 | dynSymData[0].st_value = 0; 454 | 455 | dynSymData[1].st_value = dynElf->sections.text->offset; 456 | dynSymData[1].st_shndx = elf_ndxscn(dynElf->sections.text->scn); 457 | 458 | 459 | i=0; 460 | for (SDTNoteList_t *node = dynElf->sdtNotes; node != NULL; node = node->next) { 461 | dynSymData[i + 2].st_value = dynElf->sections.text->offset + node->note->textSectionOffset; 462 | dynSymData[i + 2].st_shndx = elf_ndxscn(dynElf->sections.text->scn); 463 | i++; 464 | } 465 | i -= 1; 466 | 467 | dynSymData[i + 3].st_value = PHDR_ALIGN + dynElf->sections.shStrTab->offset; 468 | dynSymData[i + 3].st_shndx = elf_ndxscn(dynElf->sections.dynamic->scn); 469 | 470 | dynSymData[i + 4].st_value = PHDR_ALIGN + dynElf->sections.shStrTab->offset; 471 | dynSymData[i + 4].st_shndx = elf_ndxscn(dynElf->sections.dynamic->scn); 472 | 473 | dynSymData[i + 5].st_value = PHDR_ALIGN + dynElf->sections.shStrTab->offset; 474 | dynSymData[i + 5].st_shndx = elf_ndxscn(dynElf->sections.dynamic->scn); 475 | 476 | // Fix offsets Dynamic 477 | // ----------------------------------------------------------------------- // 478 | 479 | dynamicData[0].d_un.d_ptr = dynElf->sections.hash->offset; 480 | dynamicData[1].d_un.d_ptr = dynElf->sections.dynStr->offset; 481 | dynamicData[2].d_un.d_ptr = dynElf->sections.dynSym->offset; 482 | dynamicData[3].d_un.d_val = dynElf->dynamicString->size; 483 | dynamicData[4].d_un.d_val = sizeof(Elf64_Sym); 484 | 485 | // ----------------------------------------------------------------------- // 486 | 487 | elf_flagphdr(dynElf->elf, ELF_C_SET, ELF_F_DIRTY); 488 | 489 | if (elf_update(dynElf->elf, ELF_C_WRITE) < 0) { 490 | return -1; 491 | } 492 | 493 | free(textData); 494 | free(dynSymData); 495 | free(dynamicData); 496 | free(sdtNoteData); 497 | free(stringTableData); 498 | free(dynamicStringData); 499 | free(hashTable); 500 | return 0; 501 | } 502 | 503 | void dynElfSectionsClose(SectionsList *sections) { 504 | if(sections->hash != NULL) { 505 | sectionFree(sections->hash); 506 | } 507 | 508 | if(sections->dynSym != NULL) { 509 | sectionFree(sections->dynSym); 510 | } 511 | 512 | if(sections->dynStr != NULL) { 513 | sectionFree(sections->dynStr); 514 | } 515 | 516 | if(sections->text != NULL) { 517 | sectionFree(sections->text); 518 | } 519 | 520 | if(sections->sdtBase != NULL) { 521 | sectionFree(sections->sdtBase); 522 | } 523 | 524 | if(sections->ehFrame != NULL) { 525 | sectionFree(sections->ehFrame); 526 | } 527 | 528 | if(sections->dynamic != NULL) { 529 | sectionFree(sections->dynamic); 530 | } 531 | 532 | if(sections->sdtNote != NULL) { 533 | sectionFree(sections->sdtNote); 534 | } 535 | 536 | if(sections->shStrTab != NULL) { 537 | sectionFree(sections->shStrTab); 538 | } 539 | } 540 | 541 | 542 | void dynElfClose(DynElf *dynElf) { 543 | if(dynElf->stringTable != NULL) { 544 | stringTableFree(dynElf->stringTable); 545 | } 546 | 547 | if(dynElf->dynamicString != NULL) { 548 | stringTableFree(dynElf->dynamicString); 549 | } 550 | 551 | if(dynElf->dynamicSymbols != NULL) { 552 | dynamicSymbolTableFree(dynElf->dynamicSymbols); 553 | } 554 | 555 | if(dynElf->sdtNotes != NULL) { 556 | sdtNoteListFree(dynElf->sdtNotes); 557 | } 558 | 559 | dynElfSectionsClose(&dynElf->sections); 560 | 561 | (void)elf_end(dynElf->elf); 562 | free(dynElf); 563 | } 564 | -------------------------------------------------------------------------------- /src/shared-lib.h: -------------------------------------------------------------------------------- 1 | #ifndef _SHARED_LIB_H 2 | #define _SHARED_LIB_H 3 | 4 | #include "section.h" 5 | #include "string-table.h" 6 | #include "sdtnote.h" 7 | #include "dynamic-symbols.h" 8 | 9 | typedef struct { 10 | Section 11 | *hash, 12 | *dynSym, 13 | *dynStr, 14 | *text, 15 | *sdtBase, 16 | *ehFrame, 17 | *dynamic, 18 | *sdtNote, 19 | *shStrTab; 20 | } SectionsList; 21 | 22 | typedef struct { 23 | Elf *elf; 24 | Elf64_Ehdr *ehdr; 25 | Elf64_Phdr *phdrLoad1, *phdrLoad2, *phdrDyn, *phdrStack; 26 | 27 | StringTable *stringTable; 28 | StringTable *dynamicString; 29 | 30 | DynamicSymbolTable *dynamicSymbols; 31 | 32 | SDTNoteList_t *sdtNotes; 33 | size_t sdtNotesCount; 34 | 35 | SectionsList sections; 36 | } DynElf; 37 | 38 | DynElf *dynElfInit(); 39 | 40 | int dynElfAddProbe(DynElf *dynElf, SDTProbe_t *probe); 41 | 42 | int dynElfSave(DynElf *dynElf); 43 | 44 | void dynElfClose(DynElf *dynElf); 45 | 46 | #endif 47 | -------------------------------------------------------------------------------- /src/string-table.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "string-table.h" 5 | 6 | StringTable *stringTableInit() { 7 | StringTable *stringTable = (StringTable *)calloc(sizeof(StringTable), 1); 8 | stringTable->count = 1; 9 | stringTable->size = 1; 10 | 11 | stringTable->first = (StringTableNode *)calloc(sizeof(StringTableNode), 1); 12 | 13 | stringTable->first->index = 0; 14 | stringTable->first->size = 1; 15 | stringTable->first->str = (char *)calloc(sizeof(char), 1); 16 | stringTable->first->str[0] = '\0'; 17 | stringTable->first->next = NULL; 18 | 19 | return stringTable; 20 | } 21 | 22 | StringTableNode *stringTableAdd(StringTable *stringTable, char *str) { 23 | StringTableNode *current; 24 | 25 | for (current = stringTable->first; current->next != NULL; 26 | current = current->next) { 27 | } 28 | 29 | current->next = (StringTableNode *)calloc(sizeof(StringTableNode), 1); 30 | current->next->index = current->index + current->size; 31 | 32 | current = current->next; 33 | current->size = strlen(str) + 1; 34 | 35 | current->str = (char *)calloc(current->size, 1); 36 | memcpy(current->str, str, current->size); 37 | current->next = NULL; 38 | 39 | stringTable->count += 1; 40 | stringTable->size += current->size; 41 | 42 | return current; 43 | } 44 | 45 | char *stringTableToBuffer(StringTable *stringTable) { 46 | int offset; 47 | StringTableNode *current; 48 | char *buffer = (char *)calloc(stringTable->size, 1); 49 | 50 | for (current = stringTable->first, offset = 0; current != NULL; 51 | offset += current->size, current = current->next) { 52 | memcpy(&buffer[offset], current->str, current->size); 53 | } 54 | 55 | return buffer; 56 | } 57 | 58 | void stringTableFree(StringTable *table) { 59 | StringTableNode *node=NULL, *next=NULL; 60 | for(node=table->first; node!=NULL; node=next) { 61 | free(node->str); 62 | 63 | next=node->next; 64 | free(node); 65 | } 66 | free(table); 67 | } 68 | -------------------------------------------------------------------------------- /src/string-table.h: -------------------------------------------------------------------------------- 1 | #ifndef _STRING_TABLE_H 2 | #define _STRING_TABLE_H 3 | 4 | #include 5 | #include 6 | 7 | typedef struct StringTableNode_ { 8 | int index; 9 | int size; 10 | char *str; 11 | struct StringTableNode_ *next; 12 | } StringTableNode; 13 | 14 | typedef struct { 15 | int count; 16 | size_t size; 17 | StringTableNode *first; 18 | } StringTable; 19 | 20 | StringTable *stringTableInit(); 21 | 22 | StringTableNode *stringTableAdd(StringTable *stringTable, char *str); 23 | 24 | char *stringTableToBuffer(StringTable *stringTable); 25 | 26 | void stringTableFree(StringTable *stringTable); 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /src/util.c: -------------------------------------------------------------------------------- 1 | int roundUp(int numToRound, int multiple) { 2 | if (multiple == 0) { 3 | return numToRound; 4 | } 5 | 6 | int remainder = numToRound % multiple; 7 | if (remainder == 0) { 8 | return numToRound; 9 | } 10 | 11 | return numToRound + multiple - remainder; 12 | } 13 | -------------------------------------------------------------------------------- /src/util.h: -------------------------------------------------------------------------------- 1 | #ifndef _UTIL_H 2 | #define _UTIL_H 3 | 4 | int roundUp(int numToRound, int multiple); 5 | 6 | #endif 7 | -------------------------------------------------------------------------------- /tests/Makefile: -------------------------------------------------------------------------------- 1 | LDFLAGS=-lelf -ldl 2 | VALGRINDFLAGS= --tool=memcheck --leak-check=full --error-exitcode=1 3 | 4 | all: test-memory-leaks test-errors 5 | 6 | clean: 7 | rm -f test-memory-leaks 8 | 9 | build-memory-leaks: test-memory-leaks.c 10 | $(CC) $< ../out/libstapsdt.a -o test-memory-leaks -I../src/ $(LDFLAGS) 11 | 12 | test-memory-leaks: build-memory-leaks 13 | ./test-memory-leaks 14 | valgrind $(VALGRINDFLAGS) ./test-memory-leaks 15 | 16 | build-errors: test-errors.c 17 | $(CC) $< ../out/libstapsdt.a -o test-errors -I../src/ $(LDFLAGS) 18 | 19 | test-errors: build-errors 20 | ./test-errors 21 | -------------------------------------------------------------------------------- /tests/test-errors.c: -------------------------------------------------------------------------------- 1 | #include "libstapsdt.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0) 7 | #include 8 | #ifdef __NR_memfd_create // older glibc may not have this syscall defined 9 | #define HAVE_LIBSTAPSDT_MEMORY_BACKED_FD 10 | #endif 11 | #endif 12 | 13 | int testElfCreationError() { 14 | // TODO (mmarchini) write test case for elf creation error 15 | return 1; 16 | } 17 | 18 | int testTmpCreationError() { 19 | SDTProvider_t *provider; 20 | SDTError_t errno; 21 | #ifdef HAVE_LIBSTAPSDT_MEMORY_BACKED_FD 22 | char *providerLongName = "test/probe/creation/error/with/a/name/longer/than/" 23 | "249/characters" 24 | "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" 25 | "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" 26 | "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" 27 | "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; 28 | 29 | provider = providerInit(providerLongName); 30 | #else 31 | provider = providerInit("test/probe/creation/error"); 32 | #endif 33 | if(providerLoad(provider) == 0) { 34 | return 0; 35 | } 36 | printf("[testTmpCreationError] Error message: %s\n", provider->error); 37 | errno = provider->errno; 38 | providerDestroy(provider); 39 | return errno == tmpCreationError; 40 | } 41 | 42 | int testSharedLibraryOpenError() { 43 | // TODO (mmarchini) write test case for shared library loading error 44 | return 1; 45 | } 46 | 47 | int testSymbolLoadingError() { 48 | // TODO (mmarchini) write test case for symbol loading error 49 | return 1; 50 | } 51 | 52 | int testSharedLibraryCloseError() { 53 | SDTProvider_t *provider; 54 | SDTError_t errno; 55 | provider = providerInit("test-error"); 56 | providerLoad(provider); 57 | dlclose(provider->_handle); 58 | if(providerUnload(provider) == 0) { 59 | return 0; 60 | } 61 | errno = provider->errno; 62 | printf("[testSharedLibraryCloseError] Error message: %s\n", provider->error); 63 | providerDestroy(provider); 64 | return errno == sharedLibraryCloseError; 65 | } 66 | 67 | int main() { 68 | if (!testElfCreationError()) { 69 | printf("Test case failed: testElfCreationError\n"); 70 | return -1; 71 | } 72 | 73 | if (!testTmpCreationError()) { 74 | printf("Test case failed: testTmpCreationError\n"); 75 | return -2; 76 | } 77 | 78 | if (!testSharedLibraryOpenError()) { 79 | printf("Test case failed: testSharedLibraryOpenError\n"); 80 | return -3; 81 | } 82 | 83 | if (!testSymbolLoadingError()) { 84 | printf("Test case failed: testSymbolLoadingError\n"); 85 | return -4; 86 | } 87 | 88 | if (!testSharedLibraryCloseError()) { 89 | printf("Test case failed: testSharedLibraryCloseError\n"); 90 | return -5; 91 | } 92 | 93 | 94 | return 0; 95 | } 96 | -------------------------------------------------------------------------------- /tests/test-memory-leaks.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main( int argc, char *argv[] ) { 6 | SDTProvider_t *provider; 7 | SDTProbe_t *probe1, *probe2; 8 | 9 | provider = providerInit("testProvider"); 10 | probe1 = providerAddProbe(provider, "testProbe1", 4, int8, uint8, int64, uint64); 11 | probe2 = providerAddProbe(provider, "testProbe2", 2, int8, uint8); 12 | 13 | if(providerLoad(provider) == -1) { 14 | printf("Something went wrong...\n"); 15 | return -1; 16 | } 17 | 18 | probeFire(probe1, 1, 2, 3, 4); 19 | probeFire(probe2, -3, 8); 20 | 21 | providerUnload(provider); 22 | providerDestroy(provider); 23 | 24 | return 0; 25 | } 26 | -------------------------------------------------------------------------------- /tools/setup-deb-pkg.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import os 4 | import re 5 | from os import path 6 | import argparse 7 | import fileinput 8 | 9 | def inline_file_edit(filepath): 10 | return fileinput.FileInput(filepath, inplace=True) 11 | 12 | LIB_SHORT_DESCRIPTION = "Runtime USDT probes for Linux" 13 | LIB_LONG_DESCRIPTION = """ 14 | Library to give Linux runtime USDT probes capability 15 | """.strip() 16 | 17 | HEADERS_SHORT_DESCRIPTION = "Headers for libstapsdt" 18 | HEADERS_LONG_DESCRIPTION = """ 19 | Headers for libstapsdt, a library to give Linux runtime USDT probes capability 20 | """.strip() 21 | 22 | SITE = "https://github.com/sthima/libstapsdt" 23 | 24 | parser = argparse.ArgumentParser() 25 | parser.add_argument("codename", type=str) 26 | parser.add_argument("version", type=str) 27 | args = parser.parse_args() 28 | 29 | BASE = path.join("dist", "libstapsdt-{0}".format(args.version)) 30 | DEBIAN = path.join(BASE, "debian") 31 | 32 | print(args.version) 33 | print(args.codename) 34 | 35 | # Rename 36 | 37 | os.rename(path.join(DEBIAN, "libstapsdt1.install"), path.join(DEBIAN, "libstapsdt0.install")) 38 | os.rename(path.join(DEBIAN, "libstapsdt1.dirs"), path.join(DEBIAN, "libstapsdt0.dirs")) 39 | 40 | # Fix changelog 41 | with inline_file_edit(path.join(DEBIAN, "changelog")) as file_: 42 | for line in file_: 43 | if 'unstable' in line: 44 | line = line.replace('unstable', args.codename) 45 | elif 'Initial release' in line: 46 | line = " * Initial release\n" 47 | print(line, end="") 48 | 49 | 50 | # Fix control 51 | header = True 52 | with inline_file_edit(path.join(DEBIAN, "control")) as file_: 53 | for line in file_: 54 | if line.startswith("#"): 55 | continue 56 | if "3.9.6" in line: 57 | line = line.replace("3.9.6", "3.9.7") 58 | if "upstream URL" in line: 59 | line = line.replace("", SITE) 60 | if "BROKEN" in line: 61 | line = line.replace("BROKEN", "0") 62 | if "debhelper (>=9)" in line: 63 | line = line.replace("\n", ", libelf1, libelf-dev\n") 64 | if "insert up" in line: 65 | if header: 66 | line = line.replace("", HEADERS_SHORT_DESCRIPTION) 67 | else: 68 | line = line.replace("", LIB_SHORT_DESCRIPTION) 69 | if "insert long" in line: 70 | if header: 71 | line = line.replace("", HEADERS_LONG_DESCRIPTION) 72 | header = False 73 | else: 74 | line = line.replace("", LIB_LONG_DESCRIPTION) 75 | print(line, end="") 76 | 77 | # Fix copyright 78 | header = True 79 | COPYRIGHT_LINE = "" 80 | copyright_regex = re.compile("Copyright: [0-9]") 81 | with open(path.join(DEBIAN, "copyright")) as file_: 82 | for line in file_.readlines(): 83 | if copyright_regex.match(line): 84 | COPYRIGHT_LINE = line 85 | 86 | with inline_file_edit(path.join(DEBIAN, "copyright")) as file_: 87 | for line in file_: 88 | if line.startswith("#"): 89 | continue 90 | if "url://example" in line: 91 | line = line.replace("", SITE) 92 | if "" in line: 93 | if "Copyright" in line: 94 | line = COPYRIGHT_LINE 95 | else: 96 | continue 97 | print(line, end="") 98 | 99 | # Fix installs 100 | with open(path.join(DEBIAN, "libstapsdt-dev.links"), "w+") as file_: 101 | file_.writelines(["usr/lib/libstapsdt.so.0 usr/lib/libstapsdt.so"]) 102 | with inline_file_edit(path.join(DEBIAN, "libstapsdt0.install")) as file_: 103 | for line in file_: 104 | print("usr/lib/lib*.so.*") 105 | break 106 | with inline_file_edit(path.join(DEBIAN, "libstapsdt-dev.install")) as file_: 107 | for line in file_: 108 | print("usr/include/*") 109 | break 110 | 111 | # Fix rules 112 | with inline_file_edit(path.join(DEBIAN, "rules")) as file_: 113 | for line in file_: 114 | if "DH_VERBOSE" in line: 115 | print("export DH_VERBOSE=1") 116 | print("export DEB_BUILD_OPTIONS=nocheck") 117 | continue 118 | else: 119 | print(line, end="") 120 | --------------------------------------------------------------------------------