├── .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 |
8 |
9 |
10 |
11 |
12 | [](https://github.com/sthima/libstapsdt)
13 | [](https://travis-ci.org/sthima/libstapsdt)
14 | [](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 |
--------------------------------------------------------------------------------
/docs/_static/usage-workflow.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------